[系统安全9]逆向知识笔记-寄存器、栈的存储方式、反汇编指令、跳转

一开始就上手就逆向病毒还是挺困难的,总是感觉到自己的基础打得还不够扎实,所以重新对基础的反汇编知识再进行学习。

1 知识点

寄存器、栈的存储方式、反汇编指令、逻辑运算

1.1 寄存器

1.1.1 寄存器含义

  • EAX : 扩展累加寄存器
  • EBX : 扩展基址寄存器
  • ECX : 扩展计数寄存器
  • EDX : 扩展数据寄存器
  • ESI : 扩展来源寄存器
  • EDI : 扩展目标寄存器
  • EBP:扩展基址寄存器
  • ESP : 扩展堆栈指针寄存器
  • EIP : 扩展的指令指针寄存器

这些寄存器的大小是32位(4个字节),他么可以容纳数据从0-FFFFFFFF(无符号数)

1.1.2 有特殊含义的寄存器

其他寄存器基本上可以随意使用,而以下三个比较特殊的寄存器则需要留意。

  • EBP: 主要用于栈和栈帧
  • ESP : 指向当前进程的栈空间地址
  • EIP : 总是指向下一条要被执行的指令

1.2 栈的存储方式

栈是内存中的一部分,有两个特殊的性质:

  • FILO(fisrt In Last Out 先进后出)
  • 地址反向增长(栈底为大地址,栈顶为小地址)

1.2.1 先进后出

例如有ABCDEF,那么push入栈的顺序就是最先进入的数据在栈底,然后后进的数据在之前的数据上方。先进入的入栈数据最后出来。

PUSH入栈之前

入栈之后的数据存放关系

pop取出栈数据的顺序则是先进去的数据后出,后进去的数据先出

如果栈空间已经满了,依旧向栈push数据会造成栈顶溢出。

如果栈的数据已经全部取出,再次pop则向下溢出,就会是栈空状态向下溢出。

1.2.2 地址反向增长

在栈中地址的存储方式为栈底存放的地址最大,栈顶存放的地址最小。

1.3 标志位

  • 32位的标志位寄存器有32个不同的标志位。

  • 每个标志位有两个属性:置1或置0

在逆向中需要关心的标志位只有3个,Z/O/C

ZF (Zero Flag): 零标志位。 它记录相关指令执行后,其结果是否为0。 
OF (Overflow Flag): 溢出标志位。它记录了有符号数运算的结果是否发生了溢出。 
CF (Carry Flag): 进位标志位。它记录了无符号数运算结果的最高位向更高位的进位值,或从更高位的借位值。 

1.4 逻辑运算

逻辑运算有一个很简单的口诀。

  • and 有一个为假,结果为假
  • or 有一个为真,结果为真
  • xor 两个为真或为假,结果都是假,两个对比数不同时,结果才为真。

1.5 反汇编指令

1.5.1 call指令

call有以下几种方式:

  • call 40400h(地址) ;直接跳到函数或过程的地址
  • call eax ; 函数或过程地址存放在eax
  • call dword ptr [eax] ; eax中存放着地址,[]取eax的值。
  • call dword ptr [eax+5] ; dword是4个字节(两个字)
  • call dword ptr [<&API>] ;执行一个系统API,取一个API函数的地址

1.5.2 mov指令

  • mov指令格式: mov dest , src

mov指令将src的内容拷贝到dest,mov指令总共有以下几种扩展:

  • mov/movsb/movsw/movsd edi,esi ;这些变体按串/字节/字/双字为单位将esi寄存器指向的数据复制到edi寄存器指向的空间。

mov还有两种扩展的形式:

  • movsx符号位扩展 , byte -> word,word -> dword(扩展后高位全用符号位填充),然后实现mov

  • movzx零扩展,byte -> word ,word -> dword(扩展位高位全用0填充),然后实现mov

1.5.3 cmp指令

  • cmp指令格式:cmp dest , src

cmp指令比较dest和src两个操作数,并通过比较结果设置C/O/Z标志位 。

cmp 指令大概有以下几种格式:

  • cmp eax ,ebx ; 如果相等,z标志位置1,否则为0
  • cmp eax , [404000] ; 将eax和404000地址处的dword型数据相比较并同上置位。eax是一个dword型的数据(双字型),所以[40400]取的是一个dword型的数据值

1.5.4 test指令

  • test指令格式:test dest , src

这个指令是对两个操作数进行按位的与运算,与and指令唯一不同之处是不将"与"的结果保存到dest。

test指令不会对两个操作数的内容进行修改,仅仅是在进行逻辑与运算后,对标志位进行重新置位。

如果eax的值为0,那么 test eax ,eax 逻辑与运算后,z标志位会被置为1,如果eax不为0,那么zf标志位会被置为0。

1.5.5 常用的跳转指令讲解

破解或者逆向遇到很多的跳转指令,关于跳转指令可以参考下图,然后在参考下面的文字:


1、判断单个标志位状态 

  这组指令单独判断5个状态标志之一,根据某一个状态标志是0或1决定是否跳转。 

  (1)JZ/JE和JNZ/JNE利用零标志ZF,判断结果是否为零(或相等) 

  JE指令(相等时转移) 

  JZ指令(等于0时转移) 

  这是当ZF=1时转移到目标地址的条件转移指令的两种助记符。这条指令既适用于判断无符号数的相等,又适用于判断带符号数的相等。 

  JNE指令(不相等转移) 

  JNZ指令(不等于0转移) 

  这是当ZF=0时能转移到目标地址的条件转移指令的两种助记符。这条指令也是既适用于判断无符号数,又适用于判断带符号数。 

  (2)JS和JNS利用符号标志SF,判断结果是正是负。 

  JS指令(为负转移)——当满足SF=1时,转移到目标地址 

  JNS指令(为正转移)——满足SF=0时,转移到目标地址 

  (3)JO和JNO利用溢出标志,判断结果是否产生溢出 

  JO指令(溢出转移)——OF=1时,转移到目标地址 

  JNO指令(未溢出转移)——OF=0时,转移到目标地址 

  (4)JP/JPE和JNP/JPO利用奇偶标志PF,判断结果中“1”的个数是偶是奇 

  JP/JPE指令(为偶转移)——满足PF=1时转移 

  JNP/JPO指令(为奇转移)——满足PF=0时转移 

  数据通讯为了可靠常要进行校验。常用的校验方法是奇偶校验,即把字符ASCII码的最高位用作校验位,是包括校验位在内的字符中为“1”的个数恒为奇数(奇校验),或恒为偶数(偶校验)。若采用奇校验,在字符ASCII中为“1”的个数已为奇数时,则令其最高位为“0”;否则令最高位为“1”。 

  (5)JC/JB/JNAE和JNC/JNB/JAE,利用进位标志CF,判断结果是否进位或借位,CF标志是比较常用的一个标志。 

  JC——满足CF=1时转移 JNC——满足CF=0时转移 

  JB(低于转移) JNB(不低于转移) 

  JNAE(不高于等于转移) JAE(高于等于转移) 
  
2、用于比较无符号数高低 

  为区别有符号数的大小,无符号数的大小用高(Above)、低(Below)表示,它需要利用CF确定高低、利用ZF标志确定相等(Equal)。两数的高低分成4种关系:低于(不高于等于)、不低于(高于等于)、低于等于(不高于)、不低于等于(高于);也就分别对应4条指令:JB(JNAE)、JNB(JAE)、JBE(JNA)、JNBE(JA)。 

  JA/JNBE 

  JA即高于转移,JNBE即不低于且不等于转移,高于则没有进位产生,即CF=0,不等于则ZF=0,所以这两条指令满足CF=0且ZF=0时转移 

  JAE/JNB 

  高于或等于转移/不低于转移是当CF=0(高于就不产生进位)或ZF=1(等于)时转移。 

  JB/JNAE 

  即低于/不高于且不等于转移,是当CF=1(产生借位)且ZF=0(不相等)时转移。 

  JBE/JNA 

  即低于或等于/不高于转移,是当CF=1(借位产生)或ZF=1(相等)时转移。 
  
3、用于比较有符号数大小 


  判断有符号数的大(Greater)、小(Less),需要组合OF、SF标志、并利用ZF标志确定相等与否。两数的大小分成4种关系:小于(不大于等于)、不小于(大于或等于)、小于等于(不大于)、不小于等于(大于);也就分别对应4条指令:JL(JNGE)、JNL(JGE)、JLE(JNG)、JNLE(JG)。 

  由上可见,条件转移指令之前常有CMP、TEST、加减运算、逻辑运算等影响标志的指令,利用这些指令执行后的标志或其组合状态形成条件。 

  JG/JNLE 

  大于/不小于且不等于转移,是当标志SF与OF同号(即<SF异或OF>=0)且ZF=0时转移 

  JGE/JNL 

  大于或等于/不小于转移,是当标志SF与OF同号(即<SF异或OF>=0)或ZF=0时转移 

  JL/JNGL 

  小于/不大于也不等于时转移,是当标志SF与OF异号(即<SF异或OF>=1)且ZF=0时转移【SF(符号标志位)、OF(溢出标志位)】

  JLE/LNG 

  小于或等于/不大于转移,是当标志SF与OF异号(即<SF异或OF>=1)或ZF=1时转移 


  
4、判断计数器CX是否为0 

  JCXZ LABEL ;CX=0,则转移;否则顺序执行 

1.6 反汇编指令-跳转练手程序

reverseMe.exe是一个用来练手的crackme程序,适合用来熟悉反汇编中修改跳转指令的练习。

打开的时候提示需要提供一个license文件(注册文件),然后需要提供正确的序列号。根据提示可以提炼出注册要求,满足以下两点程序就注册成功。

  • 1、提供正确的注册文件
  • 2、提供正确的注册序列号

OD载入这个程序,F8单步步过到0040107B这个地址处,call CreateFileA函数后的位置,前一条指令cmp eax,-0x01意思是比较Createfile函数返回值是否为0,修改ZF标志位。

JNZ指令不等于0时实现跳转的条件,如果zf标志位为1,往下执行就会弹出一个提示框,然后ExitProcess结束进程。

JNZ指令是根据标志位决定是否跳转,可以使用快捷键【空格键】或右键-汇编,将jnz改为jmp指令。就可以使其无条件跳转让程序朝着正确的顺序继续走了。

往下走会有两个关键的跳转指令jmp与JL,对两个跳转的修改如下:

jmp前一条指令是先对比注册文件是否为空,如果为空就跳转到错误提示处。

004010AE test eax,eax ; 如果文件为空
004010B0 jnz XreverseM.004010B4 ; 改成等于0的时候跳转
004010B2 jmp XreverseM.004010F7

jl前一条指令结合上下文理解,是对比序列号长度是否满足10个字符。

004010B6 xor esi,esi
004010B8 cmp dword ptr ds:[0x402173],0x10
004010BF jl XreverseM.004010F7 ; 改成大于时跳转
004010C1 mov al,byte ptr ds:[ebx+0x40211A]

第一个指令jmp可以填充成nop,选中指令后OD界面中右键-二进制-用NOP填充。

第二个指令JL是小于0时才执行跳转,那么把JL改成与它相反的JG指令就可以达到破解的目的了。

修改后

F8单步步过到004010D6地址处,遇到第三个关键跳转。

结合起来理解就是对比字符是否为0x47(G),否则就跳转到错误提示处,将这里的JL改成JG,或者是NOP掉就破解成功了

修改后的反汇编代码

右键【复制到可执行文件】-【所有修改】

在弹出的窗口中选择【全部复制】。

再下一步弹出的对话框中,右键出菜单选择【保存文件】。

破解成功界面

汇编代码如下:

00401000 >push 0x0                                 ; |/pModule = NULL
00401002  call <jmp.&KERNEL32.GetModuleHandleA>    ; |\GetModuleHandleA
00401007  mov dword ptr ds:[0x402177],eax          ; |
0040100C  mov dword ptr ds:[0x402197],0x4003       ; |
00401016  mov dword ptr ds:[0x40219B],reverseM.004>; |
00401020  mov dword ptr ds:[0x40219F],0x0          ; |
0040102A  mov dword ptr ds:[0x4021A3],0x0          ; |
00401034  mov eax,dword ptr ds:[0x402177]          ; |
00401039  mov dword ptr ds:[0x4021A7],eax          ; |
0040103E  push 0x4                                 ; |/RsrcName = 4.
00401040  push eax                                 ; ||hInst => NULL
00401041  call <jmp.&USER32.LoadIconA>             ; |\LoadIconA
00401046  mov dword ptr ds:[0x4021AB],eax          ; |
0040104B  push 0x7F00                              ; |/RsrcName = IDC_ARROW
00401050  push 0x0                                 ; ||hInst = NULL
00401052  call <jmp.&USER32.LoadCursorA>           ; |\LoadCursorA
00401057  mov dword ptr ds:[0x4021AF],eax          ; |
0040105C  push 0x0                                 ; |/hTemplateFile = NULL
0040105E  push reverseM.0040216F                   ; ||Attributes = READONLY|HIDDEN|SYSTEM|ARCHIVE|TEMPORARY|402048
00401063  push 0x3                                 ; ||Mode = OPEN_EXISTING
00401065  push 0x0                                 ; ||pSecurity = NULL
00401067  push 0x3                                 ; ||ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
00401069  push 0xC0000000                          ; ||Access = GENERIC_READ|GENERIC_WRITE
0040106E  push reverseM.00402079                   ; ||FileName = "Keyfile.dat"
00401073  call <jmp.&KERNEL32.CreateFileA>         ; |\CreateFileA
00401078  cmp eax,-0x1                             ; |
0040107B  jnz XreverseM.0040109A                   ;  JNZ指令(不等于0转移) ,那么会弹出一个提示框,然后结束进程
0040107D  push 0x0                                 ; |/Style = MB_OK|MB_APPLMODAL
0040107F  push reverseM.00402000                   ; ||Title = " Key File ReverseMe"
00401084  push reverseM.00402017                   ; ||Text = "Evaluation period out of date. Purchase new license"
00401089  push 0x0                                 ; ||hOwner = NULL
0040108B  call <jmp.&USER32.MessageBoxA>           ; |\MessageBoxA
00401090  call <jmp.&KERNEL32.ExitProcess>         ; \ExitProcess
00401095  jmp reverseM.0040121D
0040109A  push 0x0                                 ; /pOverlapped = NULL
0040109C  push reverseM.00402173                   ; |pBytesRead = reverseM.00402173
004010A1  push 0x46                                ; |BytesToRead = 46 (70.)
004010A3  push reverseM.0040211A                   ; |Buffer = reverseM.0040211A
004010A8  push eax                                 ; |hFile
004010A9  call <jmp.&KERNEL32.ReadFile>            ; \ReadFile
004010AE  test eax,eax                             ;  如果文件为空
004010B0  jnz XreverseM.004010B4                   ;  改成等于0的时候跳转
004010B2  jmp XreverseM.004010F7
004010B4  xor ebx,ebx
004010B6  xor esi,esi
004010B8  cmp dword ptr ds:[0x402173],0x10
004010BF  jl XreverseM.004010F7                    ;  改成大于时跳转
004010C1  mov al,byte ptr ds:[ebx+0x40211A]
004010C7  cmp al,0x0
004010C9  je XreverseM.004010D3
004010CB  cmp al,0x47
004010CD  jnz XreverseM.004010D0
004010CF  inc esi
004010D0  inc ebx
004010D1  jmp XreverseM.004010C1
004010D3  cmp esi,0x8
004010D6  jl XreverseM.004010F7
004010D8  jmp reverseM.00401205
004010DD  db 00

链接: https://pan.baidu.com/s/1nu8ffPf 密码: uhbw

参考文章

常用的跳转指令 大集合
http://www.jb51.net/softjc/17303.html

posted @ 2017-04-17 00:21  17bdw  阅读(1622)  评论(0编辑  收藏  举报