您的位置:首页 > 其它

IDA 汇编命令分析以及函数调用过程

2017-08-15 19:39 645 查看
dll的文件,入口函数DllEntryPoint:

.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地址
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息