IDA 汇编命令分析以及函数调用过程
2017-08-15 19:39
645 查看
dll的文件,入口函数DllEntryPoint:
arg_0 = qword ptr 8:声明arg_0变量等于从地址rbp:8处取出一个四字宽度的值,地址rbp:8指向一个4字(8个字节)的值,且取别名为arg_0,以后要用到时,直接使用别名。C语言相当于*(rbp+8)赋给了arg_0。可参考地址
esp:32位栈指针寄存器(extended stack pointer),该指针指向系统栈最上面栈的顶部。
ebp:32位基址指针寄存器(extended base pointer),该指针指向系统栈(整个系统)中最上面栈帧(一个函数)的底部。
函数栈帧:esp和ebp之间内存空间为当前栈帧,ebp表示当前栈帧底部,esb表示当前栈帧顶部。
eip:指令寄存器(extended instruction pointer),该指针指向下一条待执行的指令地址。
参考地址。
rsp:64位的栈顶指针,相较于rbp处于低地址
rbp:64位的栈底指针,相较于rsp处于高地址
rbx:64位通用寄存器,64位基地址
rsi:用来向函数参数的指针,源
rdi:用来向函数参数的指针,目的
ebx:用来向函数参数的指针
call:将当前相对地址(IP)压入栈中,调用CALL后的子程序。
lea:将源操作数(即存储单元的有效地址偏移地址)传送到目的操作数。(运行时执行)
左边的是目的操作数,保存操作结果,该指令目的操作数只能是8个通用寄存器之一
右边的是源操作数,该指令的源操作数只能是一个存储单元,表达存储单元有多种寻址方式
jae: 大于等于转移
ja:前者大于后者转移,用于无符号比较(jump if above)
jl:条件跳转指令,用于有符号数 小于跳转,jump less。还有类似jg 大于跳转jump grater
xor: 异或,相同为0,不同为1。
xor <op1> <op2>
op1异或op2 后 结果放入op1
xor eax, eax 与 mov eax, 0作用相同,只是所占字节不同而已,前面为2字节,后面为5字节
1.它一般用来判断两个值是否相等
比如:
xor rcx, rsp
判断rcx与rsp是否相等,相等则rcx为0,不等,则为1
2.它一般用来给一个值清0
比如:
xor edx, edx
将edx赋值为0
jz: jump if zero(结果为0,则设置ZF零标志位为1,并跳转)
jnz:jump if not zero
test:逻辑与运算指令,根据结果设置标志寄存器ZF,结果本身不保存。运算结果为0时,ZF零标志置1,否则清0
test ax,bx与test bx,ax效果一样。
它一般用来判断寄存器值是否为0
比如:
test ax, ax
jz short loc_1400012C8
判断ax是否为0,为0,则跳转到子函数loc_1400012C8处执行
cmp:比较指令, cmp的功能相当于减法指令,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器
位来得知比较结果。
比如:
mov ax,8
mov bx,3
cmp ax,bx
执行后:
ax=8,ZF=0,PF=1,SF=0,CF=0,OF=0.
通过cmp指令执行后,相关标志位的值就可以看出比较的结果。
cmp ax,bx的逻辑含义是比较ax,bx中的值。
如果执行后:
ZF=1则AX=BX
ZF=0则AX!=BX
CF=1则AX<BX
CF=0则AX>=BX
CF=0&ZF=0则AX>BX
CF=1|ZF=1则AX<=BX
比如:
mov eax, 10
mov ebx, 10
cmp eax, ebx
相减求出结果,若为0,则ZF置1,反之结果为1,则ZF清0
jz SOME_WHERE ;检查ZF,为1就跳
jnz SOME_WHERE ;检查ZF,为0就跳
db:以表达式的值的字节形式初始化代码空间,只能位于CODE段之内,否则会发生错误。
格式:[标号:] db 表达式
表达式中可包含符号,字符串或表达式等项。各项之间以逗号隔开,字符串用引号括起来。括号内的标号时可选的,如果使用了标号,则标号的值将时表达式表中第一个字节的地址。
retn:段内转移子程序返回。
movzx:零扩展传送
setb:将其后寄存器位置1
neg:求其后寄存器的补码
lock:使CPU产生一个LOCK#信号,确保在多处理器系统或多线程竞争的环境下互斥的使用这个内存地址,当指令执行完毕时,锁定动作消失。
cmpxchg:比较交换指令,第一个操作数先与al/ax/eax比较;若相等则ZF置1,且第二个操作数覆盖第一个操作数。反之,ZF清0,第一个操作数覆盖al/ax/eax。在80486以上的CPU中支持。
.text:004092D9 lock cmpxchg [esi], ecx
jmp short opr:段内直接短跳转转移
因为偏移量需向前或向后跳转,因此它时一个有符号的数值。这种转移格式只允许在-128~+127字节范围内转移。
.text:004092DF jz short loc_4092EC
offset:取偏移地址。(编译时执行)
mov reg,offset xxx 比 lea reg,xxx 的指令长度少一个字节,且快一个时钟,因此,应优先选用 mov offset 指令
栈:汇编内把一段内存空间定义为一个栈,栈总是先进后出。
进栈指令Push:
push reg/mem/seg
sp<---sp-2
ss<---reg/mem/seg
先使堆栈指令sp减2,随后把一个字操作数存入堆栈顶部。其操作单元只能是字,进栈时低字节存放在低地址,高字节存放在高地址,sp相应的向低地址移动两个字节单元。
出栈指令:
pop reg/seg/mem; reg/seg/mem<--ss:[sp],sp<-sp+2
出栈指令把栈顶的一个字传送至指定的目的操作数,然后堆栈指针sp加2。其操作单元只能是字,字从栈顶弹出时,低地址字节送低字节,高地址字节送高字节。
POP SS堆栈可以用来临时存放数据,以便随时恢复它们。也常用于子程序见传递参数。
注意几点:
1.因为堆栈指针sp总是指向已经存入数据的栈顶(不是空单元),所以push指令是将(SP)减2,后将内容压栈(即先修改SP是指指向空单元,后压入数据),而POP是先从栈顶弹出一个字,后将堆栈指针SP加2.
2.PUSH CS是合法的,但是POP CS是不合法的。
3.因为SP总是指向栈顶,而用PUSH和POP指令存取数时都是在栈顶进行的,所以堆栈是先进后出或叫后进先出的。栈底在高地址,堆栈是从高地址向低地址延伸的,所有栈底就是最初的栈顶。
4.用PUSH指令和POP指令时只能按字访问堆栈,不能按字节访问堆栈。
5.PUSH和POP指令都不影响标志。
.text:00404200 var_1B4 = dword ptr -1B4h
字单元赋值给变量var_1B4,它的值是ebp-1B4h。表示被调函数的局部变量地址。
.text:00404730 arg_0 = dword ptr 8
表示调用函数传入被调函数的参数值。都是以ebp为基址进行偏移。
.text:00404736 mov eax, ___security_cookie
___security_cookie该值是一个全局变量,其在编译阶段就被编译器所创建,随后写入.data区,保存在PE中;___security_cookie用来与局部变量与ebp之间的一个地址(它是在函数执行返回地址前的一个四字节地址,只要它被修改了,说明栈溢出了。可以先于返回地址被发现。。。)进行对比,确认该值是否与当时push到栈中的值是否一致,若不一致说明,栈溢出覆盖修改了这个值,那么就需要终止程序执行了。
___security_cookie详细可参考地址。
.text:00404746 loc_404746: ;
CODE XREF: sub_404730:loc_4047C3↓j
代码交叉引用,其中loc_404746是被引用者,引用者为loc_4047C3,向下箭头↓表示引用者位置在下面。
箭头↓后面的j表示调用类型为jump。在IDA中,指令转交控制权的方式叫做流(flow):
有三种基本流:
1.普通流:表示一条指令到另一条指令的顺序流,为所有非分支指令的默认执行流(比如SUB,ADD指令等)。
2.调用流↓p(Procedure):如果IDA认为某个函数并不返回,那么,在调用该函数时,它就不会为该函数分配普通流。由函数调用导致的交叉引用使用此后缀。也会有多个交叉引用出现情况,类似下面的跳转流。
3.跳转流↓j(Jump):每个无条件分支指令和条件分支指令将分配到一个跳转流。也会有多个交叉引用出现,比如:
.text:00404730 sub_404730 proc near ;
DATA XREF: sub_404120+71↑o
数据交叉引用:用于跟踪二进制文件访问数据的方式。数据交叉引用与IDA数据库中任何牵涉到虚拟地址的字节有关(数据交叉引用与栈变量毫无关系)。
最常用的三种数据交叉引用:
1.读取交叉引用
2.写入交叉引用
3.偏移量交叉引用
更详细可参考地址。
函数调用过程:
参数入栈:
将参数从右到左的顺序依次压入系统栈中
返回地址入栈:
将当前代码区调用指令的下一条指令地址压入栈中,待函数返回时继续执行代码区跳转(处理器从当前代码区跳到被调用函数的入口处)
栈帧调整:
保存当前栈帧状态,已备后面恢复本栈帧时使用(EBP入栈)
将当前栈帧切换到新栈帧(将esp装载进ebp,更新栈帧底部)
给新栈帧分配空间(把esp减去所需空间的大小,抬高栈顶)
更详细可参考地址
以下代码是经过IDA反汇编一个exe文件出来的一个小片段:
因此,大概可推测该函数代码片段意思,还可通过修改其值,再编译回exe文件,再通过其客户端接收程序,进一步验证一下。
汇编寻址方式
逆向基础地址
Github地址
.text:000000018000525C ; BOOL __stdcall DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) .text:000000018000525C public DllEntryPoint .text:000000018000525C DllEntryPoint proc near ; DATA XREF: .pdata:0000000180009474o .text:000000018000525C .text:000000018000525C arg_0 = qword ptr 8 .text:000000018000525C arg_8 = qword ptr 10h .text:000000018000525C .text:000000018000525C mov [rsp+arg_0], rbx .text:0000000180005261 mov [rsp+arg_8], rsi .text:0000000180005266 push rdi .text:0000000180005267 sub rsp, 20h .text:000000018000526B mov rdi, r8 .text:000000018000526E mov ebx, edx .text:0000000180005270 mov rsi, rcx .text:0000000180005273 cmp edx, 1 .text:0000000180005276 jnz short loc_18000527D .text:0000000180005278 call sub_180005688 .text:000000018000527D .text:000000018000527D loc_18000527D: ; CODE XREF: DllEntryPoint+1Aj .text:000000018000527D mov r8, rdi ; lpvReserved .text:0000000180005280 mov edx, ebx ; fdwReason .text:0000000180005282 mov rcx, rsi ; hinstDLL .text:0000000180005285 mov rbx, [rsp+28h+arg_0] .text:000000018000528A mov rsi, [rsp+28h+arg_8] .text:000000018000528F add rsp, 20h .text:0000000180005293 pop rdi .text:0000000180005294 jmp __DllMainCRTStartup .text:0000000180005294 DllEntryPoint endp
arg_0 = qword ptr 8:声明arg_0变量等于从地址rbp:8处取出一个四字宽度的值,地址rbp:8指向一个4字(8个字节)的值,且取别名为arg_0,以后要用到时,直接使用别名。C语言相当于*(rbp+8)赋给了arg_0。可参考地址
esp:32位栈指针寄存器(extended stack pointer),该指针指向系统栈最上面栈的顶部。
ebp:32位基址指针寄存器(extended base pointer),该指针指向系统栈(整个系统)中最上面栈帧(一个函数)的底部。
函数栈帧:esp和ebp之间内存空间为当前栈帧,ebp表示当前栈帧底部,esb表示当前栈帧顶部。
eip:指令寄存器(extended instruction pointer),该指针指向下一条待执行的指令地址。
参考地址。
rsp:64位的栈顶指针,相较于rbp处于低地址
rbp:64位的栈底指针,相较于rsp处于高地址
rbx:64位通用寄存器,64位基地址
rsi:用来向函数参数的指针,源
rdi:用来向函数参数的指针,目的
ebx:用来向函数参数的指针
call:将当前相对地址(IP)压入栈中,调用CALL后的子程序。
lea:将源操作数(即存储单元的有效地址偏移地址)传送到目的操作数。(运行时执行)
左边的是目的操作数,保存操作结果,该指令目的操作数只能是8个通用寄存器之一
右边的是源操作数,该指令的源操作数只能是一个存储单元,表达存储单元有多种寻址方式
jae: 大于等于转移
ja:前者大于后者转移,用于无符号比较(jump if above)
jl:条件跳转指令,用于有符号数 小于跳转,jump less。还有类似jg 大于跳转jump grater
xor: 异或,相同为0,不同为1。
xor <op1> <op2>
op1异或op2 后 结果放入op1
xor eax, eax 与 mov eax, 0作用相同,只是所占字节不同而已,前面为2字节,后面为5字节
1.它一般用来判断两个值是否相等
比如:
xor rcx, rsp
判断rcx与rsp是否相等,相等则rcx为0,不等,则为1
2.它一般用来给一个值清0
比如:
xor edx, edx
将edx赋值为0
jz: jump if zero(结果为0,则设置ZF零标志位为1,并跳转)
jnz:jump if not zero
test:逻辑与运算指令,根据结果设置标志寄存器ZF,结果本身不保存。运算结果为0时,ZF零标志置1,否则清0
test ax,bx与test bx,ax效果一样。
它一般用来判断寄存器值是否为0
比如:
test ax, ax
jz short loc_1400012C8
判断ax是否为0,为0,则跳转到子函数loc_1400012C8处执行
cmp:比较指令, cmp的功能相当于减法指令,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器
位来得知比较结果。
比如:
mov ax,8
mov bx,3
cmp ax,bx
执行后:
ax=8,ZF=0,PF=1,SF=0,CF=0,OF=0.
通过cmp指令执行后,相关标志位的值就可以看出比较的结果。
cmp ax,bx的逻辑含义是比较ax,bx中的值。
如果执行后:
ZF=1则AX=BX
ZF=0则AX!=BX
CF=1则AX<BX
CF=0则AX>=BX
CF=0&ZF=0则AX>BX
CF=1|ZF=1则AX<=BX
比如:
mov eax, 10
mov ebx, 10
cmp eax, ebx
相减求出结果,若为0,则ZF置1,反之结果为1,则ZF清0
jz SOME_WHERE ;检查ZF,为1就跳
jnz SOME_WHERE ;检查ZF,为0就跳
db:以表达式的值的字节形式初始化代码空间,只能位于CODE段之内,否则会发生错误。
格式:[标号:] db 表达式
表达式中可包含符号,字符串或表达式等项。各项之间以逗号隔开,字符串用引号括起来。括号内的标号时可选的,如果使用了标号,则标号的值将时表达式表中第一个字节的地址。
retn:段内转移子程序返回。
movzx:零扩展传送
setb:将其后寄存器位置1
neg:求其后寄存器的补码
lock:使CPU产生一个LOCK#信号,确保在多处理器系统或多线程竞争的环境下互斥的使用这个内存地址,当指令执行完毕时,锁定动作消失。
cmpxchg:比较交换指令,第一个操作数先与al/ax/eax比较;若相等则ZF置1,且第二个操作数覆盖第一个操作数。反之,ZF清0,第一个操作数覆盖al/ax/eax。在80486以上的CPU中支持。
.text:004092D9 lock cmpxchg [esi], ecx
jmp short opr:段内直接短跳转转移
因为偏移量需向前或向后跳转,因此它时一个有符号的数值。这种转移格式只允许在-128~+127字节范围内转移。
.text:004092DF jz short loc_4092EC
offset:取偏移地址。(编译时执行)
mov reg,offset xxx 比 lea reg,xxx 的指令长度少一个字节,且快一个时钟,因此,应优先选用 mov offset 指令
栈:汇编内把一段内存空间定义为一个栈,栈总是先进后出。
进栈指令Push:
push reg/mem/seg
sp<---sp-2
ss<---reg/mem/seg
先使堆栈指令sp减2,随后把一个字操作数存入堆栈顶部。其操作单元只能是字,进栈时低字节存放在低地址,高字节存放在高地址,sp相应的向低地址移动两个字节单元。
出栈指令:
pop reg/seg/mem; reg/seg/mem<--ss:[sp],sp<-sp+2
出栈指令把栈顶的一个字传送至指定的目的操作数,然后堆栈指针sp加2。其操作单元只能是字,字从栈顶弹出时,低地址字节送低字节,高地址字节送高字节。
POP SS堆栈可以用来临时存放数据,以便随时恢复它们。也常用于子程序见传递参数。
注意几点:
1.因为堆栈指针sp总是指向已经存入数据的栈顶(不是空单元),所以push指令是将(SP)减2,后将内容压栈(即先修改SP是指指向空单元,后压入数据),而POP是先从栈顶弹出一个字,后将堆栈指针SP加2.
2.PUSH CS是合法的,但是POP CS是不合法的。
3.因为SP总是指向栈顶,而用PUSH和POP指令存取数时都是在栈顶进行的,所以堆栈是先进后出或叫后进先出的。栈底在高地址,堆栈是从高地址向低地址延伸的,所有栈底就是最初的栈顶。
4.用PUSH指令和POP指令时只能按字访问堆栈,不能按字节访问堆栈。
5.PUSH和POP指令都不影响标志。
.text:00404200 var_1B4 = dword ptr -1B4h
字单元赋值给变量var_1B4,它的值是ebp-1B4h。表示被调函数的局部变量地址。
.text:00404730 arg_0 = dword ptr 8
表示调用函数传入被调函数的参数值。都是以ebp为基址进行偏移。
.text:00404736 mov eax, ___security_cookie
___security_cookie该值是一个全局变量,其在编译阶段就被编译器所创建,随后写入.data区,保存在PE中;___security_cookie用来与局部变量与ebp之间的一个地址(它是在函数执行返回地址前的一个四字节地址,只要它被修改了,说明栈溢出了。可以先于返回地址被发现。。。)进行对比,确认该值是否与当时push到栈中的值是否一致,若不一致说明,栈溢出覆盖修改了这个值,那么就需要终止程序执行了。
___security_cookie详细可参考地址。
.text:00404746 loc_404746: ;
CODE XREF: sub_404730:loc_4047C3↓j
代码交叉引用,其中loc_404746是被引用者,引用者为loc_4047C3,向下箭头↓表示引用者位置在下面。
箭头↓后面的j表示调用类型为jump。在IDA中,指令转交控制权的方式叫做流(flow):
有三种基本流:
1.普通流:表示一条指令到另一条指令的顺序流,为所有非分支指令的默认执行流(比如SUB,ADD指令等)。
2.调用流↓p(Procedure):如果IDA认为某个函数并不返回,那么,在调用该函数时,它就不会为该函数分配普通流。由函数调用导致的交叉引用使用此后缀。也会有多个交叉引用出现情况,类似下面的跳转流。
3.跳转流↓j(Jump):每个无条件分支指令和条件分支指令将分配到一个跳转流。也会有多个交叉引用出现,比如:
.text:00404730 sub_404730 proc near ;
DATA XREF: sub_404120+71↑o
数据交叉引用:用于跟踪二进制文件访问数据的方式。数据交叉引用与IDA数据库中任何牵涉到虚拟地址的字节有关(数据交叉引用与栈变量毫无关系)。
最常用的三种数据交叉引用:
1.读取交叉引用
2.写入交叉引用
3.偏移量交叉引用
更详细可参考地址。
函数调用过程:
参数入栈:
将参数从右到左的顺序依次压入系统栈中
返回地址入栈:
将当前代码区调用指令的下一条指令地址压入栈中,待函数返回时继续执行代码区跳转(处理器从当前代码区跳到被调用函数的入口处)
栈帧调整:
保存当前栈帧状态,已备后面恢复本栈帧时使用(EBP入栈)
将当前栈帧切换到新栈帧(将esp装载进ebp,更新栈帧底部)
给新栈帧分配空间(把esp减去所需空间的大小,抬高栈顶)
更详细可参考地址
以下代码是经过IDA反汇编一个exe文件出来的一个小片段:
04 loc_402A04: ; CODE XREF: sub_4029E0+29↓j // 代码交叉引用,即该子函数会在下面以jump形式被调用 .text:00402A04 mov al, [ecx] // 将ecx寄存器所指向的值赋给寄存器al [ecx]-存储器寻址 .text:00402A06 inc ecx //ecx自减1 .text:00402A07 test al, al // al与al,判断是否al为0 .text:00402A09 jnz short loc_402A04 // 若非0值,则跳转到loc_402A04处执行,即循环。 .text:00402A0B xor eax, eax // 将eax寄存器清0 .text:00402A0D sub ecx, edx // ecx中的值减去edx,将所得结果存回ecx .text:00402A0F add ecx, 1 // ecx自加1 .text:00402A12 setb al //将al寄存器置1 .text:00402A15 neg eax // 求eax的补码,反码+1,符号位不变 .text:00402A17 or eax, ecx //eax中的值与ecx求或,将结果存于eax中 .text:00402A19 push eax ; Size // eax压栈 作为其后函数的参数使用 .text:00402A1A call edi ; __imp_malloc // 调malloc函数申请eax中大小的内存空间 .text:00402A1C mov edi, eax // 返回值赋给edi .text:00402A1E add esp, 4 // 出栈4个字单元 .text:00402A21 mov [ebx+8], edi //将edi中的返回值赋给ebx+8 .text:00402A24 test edi, edi //测试返回值是否为0 .text:00402A26 jz short loc_402A73 //如果返回值为0,ZF标志位置1,则说明申请内存空间失败,则跳转到loc_402A73子程序处,反之继续执行下去 .text:00402A28 push esi ; lpName // 一连串的压栈操作,很大概率说明有函数调用要出现了。此处压栈形参列表最后一个参数lpName .text:00402A29 push [ebp+dwMaximumSizeLow] ; dwMaximumSizeLow //此处压栈倒数第二个参数 .text:00402A2C push 0 ; dwMaximumSizeHigh //此处压栈倒数第三个参数,其值为0 .text:00402A2E push 4 ; flProtect //此处压栈倒数第四个参数,其值为4 .text:00402A30 push [ebp+lpFileMappingAttributes] ; lpFileMappingAttributes //此处压栈倒数第5个参数 .text:00402A33 push 0FFFFFFFFh ; hFile //此处压栈倒数第六个,也就是第一个参数,其值为-1. text:00402A35 call ds:CreateFileMappingA //进入函数调用 .text:00402A3B mov [ebx], eax //将返回值保存在[ebx] .text:00402A3D test eax, eax //测试返回值是否为0 .text:00402A3F jz short loc_402A73 //若为0,则跳转到子函数loc_402A73处执行,反之,继续执行下去 .text:00402A41 push 0 ; dwNumberOfBytesToMap //后面一连串的压栈操作,与上面类似在函数调用前的压栈操作以保存传入的参数。 .text:00402A43 push 0 ; dwFileOffsetLow .text:00402A45 push 0 ; dwFileOffsetHigh .text:00402A47 push 0F001Fh ; dwDesiredAccess .text:00402A4C push eax ; hFileMappingObject .text:00402A4D call ds:MapViewOfFile//函数调用 .text:00402A53 mov [ebx+4], eax loc_402A73做的事情如以下代码所示: .text:00402A73 loc_402A73: ; CODE XREF: sub_4029E0+46↑j // 代码交叉引用在上面 .text:00402A73 ; sub_4029E0+5F↑j //代码交叉引用在上面 .text:00402A73 push ebx ; Memory //将ebx压栈 .text:00402A74 call ds:__imp_free //释放申请的内存 .text:00402A7A add esp, 4 // 出栈4个字单元以平衡栈
因此,大概可推测该函数代码片段意思,还可通过修改其值,再编译回exe文件,再通过其客户端接收程序,进一步验证一下。
汇编寻址方式
逆向基础地址
Github地址
相关文章推荐
- 从汇编的角度分析函数调用过程(1)
- 从汇编的角度分析函数调用过程(2)
- c语言内部(汇编代码分析)函数调用过程探究
- 分析linux下的进程地址空间,以及c语言的函数调用过程
- 函数调用过程-汇编代码分析
- 汇编代码分析----函数的调用堆栈过程(进程内核栈的切换过程)
- 汇编学习笔记:函数调用过程中的堆栈分析
- 汇编学习:函数调用过程中的堆栈分析
- x86_64架构下函数调用过程分析
- 反汇编及函数调用堆栈分析
- 1.3 函数调用反汇编解析以及调用惯例案例分析
- IDA反汇编/反编译静态分析iOS模拟器程序(三)函数表示与搜索函数
- C++中的虚函数调用原理的反汇编实例分析(1)
- c函数调用过程原理及函数栈帧分析
- C/C++—— 在构造函数中调用虚函数能实现多态吗(Vptr指针初始化的过程分析)
- 关于 接口与对象指针对成员函数的调用时的汇编执行行为分析
- ARM架构下函数调用过程分析
- 函数调用过程分析
- 汇编语言函数调用过程
- 函数调用和栈的内存分配过程分析