环境 vs2010 + WIN7 64

OllyDbg简介

是汇编级的调试器,VS是源码级的
反汇编窗口: 显示被调试程序的反汇编代码(地址栏、HEX数据栏、汇编指令栏、注释栏)
寄存器窗口: 显示当前所选线程的CPU寄存器的内容
信息窗口: 显示反汇编窗口中选中的第一个命令的参数及一些跳转目的地之、字符串等
数据窗口: 显示内存或文件的内容
堆栈窗口: 显示当前线程的堆栈

OllyDbg调式方式

方式1:用OD调试器直接打开 方式2: 附加到已经打开进程

OllyDbg定位地址

Ctrl + G 输入WIN32 API函数名(如:MessageBoxW,MessageBoxA)

OllyDbg断点

方式1: 直接在选中的行上按下F2 方式2: bp 指令地址(如果地址是一条指令的中间,那么会出错) ;bp breakpoint 方式3: 直接在选中的行双击 可通过查看->int3断点 可以查看打上的断点

PDB文件

全称为“程序数据库”文件,存储了被编译文件的调式信息,大多数场景是调式应用程序。 OllDbg调试器可以读取.pdb文件

调试中我们经常需要用到的快捷键:

F2:设置断点 F8:单步步过 F7:单步步入 F4:运行到选定位置 F9:运行 CTRL + F9:执行到返回 ALT + F9:执行到用户代码

命令号 输入 dd 地址 可以定位到该地址

C++代码内嵌汇编

内嵌汇编:__asm mov aaa,0x778899

函数调用的反汇编形式

  1. 函数与CALL(OD分析代码将函数(子程序)分块) C/C++函数调用翻译成汇编相当于call(子程序调用)
  2. 关闭C/C++优化 项目属性 -> C/C++ -> 优化 -> 禁用 /Od

1)int add(int a, int b) { return a + b; }

aaa = add(0x11,0x22) 优化的: 要看怎么优化的,有的直接没有相关语句,有的是直接将a+b的值mov给aaa

未被优化的: 地址 十六进制数据 指令 注释 00351021 6A 22 PUSH 22 ; /b = 34. 00351023 6A 11 PUSH 11 ; |a = 17. 00351025 E8 D6FFFFFF CALL add ; \add

add函数: 地址 十六进制数据 指令 注释 00351000 55 PUSH EBP ; INT test1.add(a,b) 00351001 8BEC MOV EBP,ESP 00351003 8B45 08 MOV EAX,DWORD PTR SS:[ARG.1] 00351006 0345 0C ADD EAX,DWORD PTR SS:[ARG.2] 00351009 5D POP EBP 0035100A C3 RETN

2)int add(int a, int b) { return a + b; }

aaa = add(aaa,0x22)

地址 十六进制数据 指令 注释 007C1021 |. 6A 22 PUSH 22 ; /b = 34. 007C1023 |. A1 18307C00 MOV EAX,DWORD PTR DS:[aaa] ; | 007C1028 |. 50 PUSH EAX ; |a => [7C3018] = 1 007C1029 |. E8 D2FFFFFF CALL add ; \add 007C102E |. 83C4 08 ADD ESP,8 007C1031 |. A3 18307C00 MOV DWORD PTR DS:[aaa],EAX

加法反汇编形式

int i = 0; i = i + 100; i = i + 0x222;

001E1091 |. C745 FC 00000 MOV DWORD PTR SS:[LOCAL.1],0 001E1098 |. 8B45 FC MOV EAX,DWORD PTR SS:[LOCAL.1] 001E109B |. 83C0 64 ADD EAX,64 001E109E |. 8945 FC MOV DWORD PTR SS:[LOCAL.1],EAX 001E10A1 |. 8B4D FC MOV ECX,DWORD PTR SS:[LOCAL.1] 001E10A4 |. 81C1 22020000 ADD ECX,222 001E10AA |. 894D FC MOV DWORD PTR SS:[LOCAL.1],ECX

进制与内存单元长度修饰

  1. 十六进制 十六进制同我们日常的十进制表示法不一样。它由0-9,A-F组成。 9 +1 = 10 ; //十进制 F +1 = 10 ; //十六进制 99 +1 = 100 ; //十进制 FF +1 = 100 ; //十六进制

  2. 字节, 字,双字 字节 : Byte类型 (unsigned char) 0-255
    字 : WORD类型 (unsigned short) 0-65535 双字 : DWORD类型 (unsigned int) 0-4294967295

测试: int i = 0; int* p = &i; __asm { mov ebx, p add [ebx], 0x443332211 add word ptr [ebx], 0x443332211 add dword ptr[ebx], 0x443332211 }

char c = 3; short s = 4; int d = 5;

__asm { add c, 0x87654321 add s, 0x87654321 add d, 0x87654321 }

008C109E |. C745 EC 00000 MOV DWORD PTR SS:[LOCAL.5],0 008C10A5 |. 8D45 EC LEA EAX,[LOCAL.5] 008C10A8 |. 8945 E8 MOV DWORD PTR SS:[LOCAL.6],EAX 008C10AB |. 8B5D E8 MOV EBX,DWORD PTR SS:[LOCAL.6] 008C10AE |. 8003 11 ADD BYTE PTR DS:[EBX],11 008C10B1 |. 66:8103 1122 ADD WORD PTR DS:[EBX],2211 008C10B6 |. 8103 11223343 ADD DWORD PTR DS:[EBX],43332211 008C10BC |. C645 FB 03 MOV BYTE PTR SS:[LOCAL.2+3],3 008C10C0 |. B9 04000000 MOV ECX,4 008C10C5 |. 66:894D F4 MOV WORD PTR SS:[LOCAL.3],CX 008C10C9 |. C745 F0 05000 MOV DWORD PTR SS:[LOCAL.4],5 008C10D0 |. 8045 FB 21 ADD BYTE PTR SS:[LOCAL.2+3],21 008C10D4 |. 66:8145 F4 21 ADD WORD PTR SS:[LOCAL.3],4321 008C10DA |. 8145 F0 21436 ADD DWORD PTR SS:[LOCAL.4],87654321

32位通用寄存器

EAX AX AH AL 32 16 8 8 同样的还有 EBX BX BH BL ECX CX CH CL EDX DX DH DL

OD命令行查看内存: dd (data dword) dw(data word) db(data byte) OD命令行看寄存器: ? eax / ? ax / ? al

MOVS 和 MOVZX

MOVSX 符号拓展传送 MOVZX 零拓展传送

  1. MOVSX 与 MOVZX MOVSX 操作数A,操作数B MOVZX 操作数A,操作数B 相同点:操作数B所在空间必须小于操作数A 注意: 1)格式与MOV基本相同 2)能完成小存储单元向打的存储单元的数据传送 比如 : movsx eax,bx movzx ebx,ax movsx eax, bx

MOVSX,MOVZX与MOV指令区别: 1)MOVSX,MOVZX的操作数B所占控件必须小于操作数A 2)MOV指令是原值传送,不会改动。而MOVSX和MOVZX有可能会改动

MOVSX与MOVZX区别: 1)MOVSX将用操作数B的符号位拓展填充操作数A的余下空间: -如果是负数则符号位为1 -如果是正数则符号位为0,和MOVZX功能相同 2)MOVZX将用0来拓展填充操作数A的余下空间 操作数A - - - - 操作数B 00 - -

MOVSX: char c = -0x33; //unsigned char 0-255 char -128-127 int i = 0x78654321; //带符号位传送 i = c;

00D51093 |. C645 FF 33 MOV BYTE PTR SS:[LOCAL.1+3],33 00D51097 |. C745 F8 21436 MOV DWORD PTR SS:[LOCAL.2],78654321 00D5109E |. 0FBE45 FF MOVSX EAX,BYTE PTR SS:[LOCAL.1+3] 00D510A2 |. 8945 F8 MOV DWORD PTR SS:[LOCAL.2],EAX

MOVZX: char c = -0x33; //unsigned char 0-255 char -128-127 int i = 0x78654321; //带符号位传送 i = (unsigned char)c;

00921093 |. C645 FF CD MOV BYTE PTR SS:[LOCAL.1+3],0CD 00921097 |. C745 F8 21436 MOV DWORD PTR SS:[LOCAL.2],78654321 0092109E |. 0FB645 FF MOVZX EAX,BYTE PTR SS:[LOCAL.1+3] 009210A2 |. 8945 F8 MOV DWORD PTR SS:[LOCAL.2],EAX

LEA指令(Load effective address)

  1. LEA指令格式 有效的地址传送指令LEA 格式:LEA 操作数A,操作数B 功能:将操作数B的有效地址传送到指定的某个寄存器,操作数A必须是寄存器(32位系统上就是32位寄存器)

OD使用小结

  1. OD调式 重新开始: Ctrl + F2 转到地址: Ctrl + G 断点切换: F2 断电窗口: Alt+B 运行:F9 暂停:F12 单步步过: F8遇到call跳过 单步步入: F7遇到call进入 运行到选定位置: F4 反汇编到窗口跟随: 回车键enter 跟随: 回车键Enter进入某个地址

  2. 右键菜单 - goto +号: 转到下一步

  3. 号: 转到上一步
  4. 号: 转到当前指令地址

  5. 命令栏指令 bp 下断点 bc 清除断点 dd 以双字方式显示数据 dw 以字方式显示数据 db 以字节方式显示数据 dc 一字符方式显示数据 ? 计算表达式的值

  6. OD修改汇编指令 选中汇编指令,按下空格后,直接输入汇编代码

汇编减法指令

  1. sub(substract)指令 格式:sub 操作数A,操作数B A = A - B 功能: 两个操作数的相减,即从A中减去B,其结果放在A中

ZF零标志位:若当前的运算结果为零,则ZF为1,否则为0. 注意: SUB指令影响ZF标志位 操作数不能都是存储器

int a = 0x109; int b = 8; a = a - b;

00401093 |. C745 FC 09010 MOV DWORD PTR SS:[LOCAL.1],109 0040109A |. C745 F8 08000 MOV DWORD PTR SS:[LOCAL.2],8 004010A1 |. 8B45 FC MOV EAX,DWORD PTR SS:[LOCAL.1] 004010A4 |. 2B45 F8 SUB EAX,DWORD PTR SS:[LOCAL.2] 004010A7 |. 8945 FC MOV DWORD PTR SS:[LOCAL.1],EAX

CMP和转移指令

  1. 比较指令CMP 格式:CMP 操作数A,操作数B // A-B 功能:两个操作数的相减,即从A中减去B,其结果会影响标志位,对标志位的影响与SUB指令相同。CMP指令主要用于配合条件转移指令使用,如:JZ/JE 当ZF=1时跳转

  2. 等于条件转移指令JE/JZ 格式:JE/JZ 标号 //等于跳转 功能:ZF=1,跳转到指定地址执行 说明: 1)JE和JZ等价,他们是根据标志位ZF转移的指令 2)JE、JZ均为一条指令的两种助记符表示方法 测试: int a = 4; if (a != 3) { printf("do if a != 3"); } printf("do if a = 3");

005A1091 |. C745 FC 04000 MOV DWORD PTR SS:[LOCAL.1],4 005A1098 |. 837D FC 03 CMP DWORD PTR SS:[LOCAL.1],3 005A109C |. 74 0D JE SHORT 005A10AB 005A109E |. 68 0C215A00 PUSH OFFSET 005A210C /_Format = "do if a != 3" 005A10A3 |. E8 98FFFFFF CALL printf \printf 005A10A8 |. 83C4 04 ADD ESP,4 005A10AB |> 68 1C215A00 PUSH OFFSET 005A211C /_Format = "do if a = 3" 005A10B0 |. E8 8BFFFFFF CALL printf \printf

  1. 不等于条件转移指令JNE/JNZ 格式:JNE/JNZ 标号 //不等于跳转 功能:ZF=0,跳转到指定地址执行

int a = 4; if (a == 3) { printf("do if a == 3"); return 0 } printf("do if a != 3"); 或 int a = 4; __asm { mov eax,3 sub eax,a jz end //ZF=1跳转 } printf("do if a != 3"); return 0; end: printf("do if a == 3"); return 0;

00651091 |. C745 FC 04000 MOV DWORD PTR SS:[LOCAL.1],4 00651098 |. 837D FC 03 CMP DWORD PTR SS:[LOCAL.1],3 0065109C |. 75 0D JNE SHORT 006510AB 0065109E |. 68 0C216500 PUSH OFFSET 0065210C /_Format = "do if a == 3" 006510A3 |. E8 98FFFFFF CALL printf \printf 006510A8 |. 83C4 04 ADD ESP,4 006510AB |> 68 1C216500 PUSH OFFSET 0065211C /_Format = "do if a != 3" 006510B0 |. E8 8BFFFFFF CALL printf \printf

  1. 无条件跳转指令jmp和goto指令(c语言中的goto汇编后就为jmp) 1)GOTO与JMP 无条件跳转指令 格式 JMP A 2)其中A为转移的目的地址。程序转移到目的地址所指向的指令继续往下执行。 3)本组指令对标志位无影响

  2. 有符号 小于/不大于等于 转移指令 JL/JNGE 格式:JL/JNGE 标号地址 功能:小于/不大于等于时 转移到标号地址 JL 有符号 小于 则跳转 (Jump if less) JNGE 有符号 不大于等于 则跳转(Jump if less) SF = 1; 符号标志位为1 则跳转到标号地址执行(结果为负数)

int a = 0x0A; int b = 0x20; if (a >= b) { printf("do a >= b\n"); } printf("do a < b/n");

  1. 有符号 小于等于/不大于 转移指令JLE/JNG 格式: JLE/JNG 标号地址 功能: 小于等于/不大于 时转到标号地址 JNG 有符号 不大于 则跳转 (Jump if Not Greater) JLE 有符号 小于等于 则跳转(Jump if Less or Equal) ZF = 1 || SF != OF (溢出标志位) (SF=1 && OF=0 两个数相减 结果为负,且没有溢出) (SF=0 && OF=1 两个数相减 结果为正,且有溢出) 负-正

int a = 0x0A; int b = 0x20; if (a > b) { printf("do a >= b\n"); } printf("do a < b/n");

  1. 有符号 大于/不小于等于 转移指令JG/JNLE 格式: JG/JNLE 标号地址 功能: 大于/不小于等于 时转到目标地址 JG 有符号 大于 则跳转 (Jump if Greater) JNLE 有符号 不小于等于 则跳转(Jump if Not Less or Equal) ZF = 0 && SF = OF (溢出标志位) (SF=1 && OF=1 两个数相减 结果为负,有溢出) (SF=0 && OF=0 两个数相减 结果为正,没有溢出) int a = 4; if (a <=3 ) { printf("do a <= 3\n"); } printf("a > 3");

  2. 程序状态字寄存器 1)CF进位标志位 当执行一个加法(减法)运算时,最高位产生进位(或借位)时,CF为1,否则为0

2)ZF零标志位 若当前的运算结果为零,则ZF为1,否则为0

3)SF符号标志位 该标志位于运算结果的最高位相同,即运算结果为负,则SF为1,否则为0

4)OF溢出标志位 若运算结果超出机器能够表示的范围称为溢出,此时OF为1,否则为0 三条原则: 同号相加和异号相减才会发生溢出 同号相加结果的符号与参与运算的符号不同就溢出 异号相减结果的符号位与被减数的符号位不同就产生溢出

5)PF奇偶标志 当运算结果的最低16位中含1的个数为偶数则PF=1,否则PF=0

6)AF辅助进位标志 一个加法(减法)运算结果的底4位向高4位有进位(或借位)时则AF=1,否则AF=0

7)TF跟踪标志 该标志位为方便程序调式而设置。若TF=1,CPU处于单步工作方式,即在每条指令执行结束后,结束中断。

8)IF中断标志位 该标志位用来控制CPU是否响应可屏蔽中断,若IF=1则运行中断,否则禁止中断

9)DF方向标志 该标志位用来控制串处理指令的处理方向。若DF=1则串处理过程中自动递减,否则自动递增。

  1. 无符号大于转移指令 JA / JNBE JA 高于
    JNBE 不低于等于

JA与JG区别 JG是带符号数比价 JA是无符号数比较 CF=0 && ZF = 0则跳转

  1. 无符号大于等于转移指令 JNB/JAE/JNC JNB 不低于
    JAE 高于等于 JNC Jump Not carry 没有进位时跳转 CF

unsigned int a = 5, b = 10; if( a2<b2 ) { ..... }

  1. 无符号小于转移指令 JB/JNAE/JC JB 低于
    JNAE 不高于等于 JC Jump carry 进位时跳转 CF
  1. 无符号大于转移指令 JBE /JNA JBE 低于等于 JNA 高于

call 框架

EBP 寄存器 栈底指针 ESP 寄存器 栈顶指针

  1. EBP栈底指针 EPB是一个特殊的寄存器,通过EBP+偏移量 可以访问CALL里边的局部变量,它的低16位叫BP。 // EAX和AX的关系

  2. ESP 栈顶指针 ESP栈顶指针与EBP栈底指针构成一段空间大小,一般就是本CALL局部变量的空间大小总和。 ESP指针配合EBP使用。 //SP

main函数调用fun1 1)保存ebp栈底指针,也就是将他入栈。 2)将esp赋值给ebp 3)ebp出栈 | | 00000000 | | | | | |----| ....fun1...| | ---新的esp | |----| 局部变量 | |---| esp ---新的ebp
....main...| |
| |---| ebp | | | | ffffffff

如果fun1为空时: 则call fun1的内容为: 004D1080 /$ 55 PUSH EBP
004D1081 |. 8BEC MOV EBP,ESP 004D1083 |. 5D POP EBP 004D1084 . C3 RETN

总结: 1)每个CALL会分配一个独立的栈段空间,供局部变量使用 2)CALL栈平衡。进入CALL前与处CALL后,EBP和ESP的值不变。

PUSH、POP和CALL栈平衡RETN指令

  1. PUSH 入栈指令(压栈指令): 格式:PUSH 操作数 相当于: sub esp,4 mov [esp],ebp 操作数可以是寄存器,村暑期,或者立即数

  2. POP出栈指令 (弹栈指令) 格式: POP 操作数 pop ebp 相当于: mov ebp ,[esp] add esp,4 操作数是寄存器,或者存储器,不能是立即数

  3. 分析 1)测试PUSH和POP与ESP栈顶指针关系

2)CALL和RET(RETN)与ESP的关系 CALL执行时会PUSH EIP RET执行时会POP EIP 3)参数和局部变量的表示 [EBP - ??] ;本call中的局部变量 [EBP + ??] ;上一个call局部变量,作为传入参数

调用约定

vs中 配置属性 ---> C/C++ ---> 高级 ---> 调用约定

注: C中不加说明的默认函数为cdecl方式 C++也一样,但是默认的调用方式可以在IDE(开发环境)中设置 带有可变参数的函数必须且智能使用cdecl方式,如: int printf(char fmtstr, ...); int scanf(char fmtstr, ...);

  1. cdecl 调用约定(C Declaration) cdecl 函数参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈(默认约定) 1)源代码: int cdecl add1(int a, int b) { return a + b; }

2)生成的汇编代码: 007D1080 /$ 55 PUSH EBP INT test1.add1(a,b) 007D1081 |. 8BEC MOV EBP,ESP 007D1083 |. 8B45 08 MOV EAX,DWORD PTR SS:[ARG.1] 007D1086 |. 0345 0C ADD EAX,DWORD PTR SS:[ARG.2] 007D1089 |. 5D POP EBP 007D108A . C3 RETN

3)调用代码: 007D10A8 |. 6A 04 PUSH 4 /b = 4 007D10AA |. 6A 03 PUSH 3 |a = 3 007D10AC |. E8 CFFFFFFF CALL add1 \add1 007D10B1 |. 83C4 08 ADD ESP,8

  1. stdcall 调用约定 API函数调用约定 stdcall stdcall是standardcall得缩写,是C++的标准调用方式;所有参数从右到左依次入栈,如果是调用类成员的话,最后一个入栈的是this指针。 这些堆栈中的参数由被调用的函数在返回后清除,使用的指令时retn X, X表示参数占用的字节数(内存空间大小),CPU在ret之后自动弹出X个字的栈空间,称为自动清栈。

1)源代码: int __stdcall add2(int a, int b) { return a + b; }

2)生成的汇编代码: 00C31090 /$ 55 PUSH EBP INT test1.add2(a,b) 00C31091 |. 8BEC MOV EBP,ESP 00C31093 |. 8B45 08 MOV EAX,DWORD PTR SS:[ARG.1] 00C31096 |. 0345 0C ADD EAX,DWORD PTR SS:[ARG.2] 00C31099 |. 5D POP EBP 00C3109A . C2 0800 RETN 8

3)调用代码: 00C310C7 |. 6A 04 PUSH 4 /b = 4 00C310C9 |. 6A 03 PUSH 3 |a = 3 00C310CB |. E8 C0FFFFFF CALL add2 \add2

  1. fastcall 调用约定 fastcall是编译器指定的快速调用方式 fastcall通常规定将前两个(或者若干个)参数有寄存器传递,其余参数还是通过堆栈传递 不同编译器编译的程序规定的寄存器不同。返回方式和stdcall相同。 1)源代码: 参数较少的情况: int cdecl add1(int a, int b) { return a + b; }

参数较多的情况: int __fastcall add4(int a, int b, int c, int d) { return a + b +c +d; }

2)生成的汇编代码: 参数较少的情况: 00E010A0 /$ 55 PUSH EBP INT test1.add3(void) 00E010A1 |. 8BEC MOV EBP,ESP 00E010A3 |. 83EC 08 SUB ESP,8 00E010A6 |. 8955 F8 MOV DWORD PTR SS:[LOCAL.2],EDX 00E010A9 |. 894D FC MOV DWORD PTR SS:[LOCAL.1],ECX 00E010AC |. 8B45 FC MOV EAX,DWORD PTR SS:[LOCAL.1] 00E010AF |. 0345 F8 ADD EAX,DWORD PTR SS:[LOCAL.2] 00E010B2 |. 8BE5 MOV ESP,EBP 00E010B4 |. 5D POP EBP 00E010B5 . C3 RETN

参数较多的情况: 009A10C0 /$ 55 PUSH EBP INT test1.add4(c,d) 009A10C1 |. 8BEC MOV EBP,ESP 009A10C3 |. 83EC 08 SUB ESP,8 009A10C6 |. 8955 F8 MOV DWORD PTR SS:[LOCAL.2],EDX 009A10C9 |. 894D FC MOV DWORD PTR SS:[LOCAL.1],ECX 009A10CC |. 8B45 FC MOV EAX,DWORD PTR SS:[LOCAL.1] 009A10CF |. 0345 F8 ADD EAX,DWORD PTR SS:[LOCAL.2] 009A10D2 |. 0345 08 ADD EAX,DWORD PTR SS:[ARG.1] 009A10D5 |. 0345 0C ADD EAX,DWORD PTR SS:[ARG.2] 009A10D8 |. 8BE5 MOV ESP,EBP 009A10DA |. 5D POP EBP 009A10DB . C2 0800 RETN 8

3)调用代码: 参数较少的情况: 00E010F3 |. BA 04000000 MOV EDX,4 00E010F8 |. B9 03000000 MOV ECX,3 00E010FD |. E8 9EFFFFFF CALL add3 [add3 参数传递没有用到栈,所以不同堆栈平衡

参数较多的情况: 009A1125 |. 6A 06 PUSH 6 /d = 6 009A1127 |. 6A 05 PUSH 5 |c = 5 009A1129 |. BA 04000000 MOV EDX,4 | 009A112E |. B9 03000000 MOV ECX,3 | 009A1133 |. E8 88FFFFFF CALL add4 \add4 但是如果参数比较多的话,参数传递会用到栈,那就需要自己平衡堆栈(意思是通过retn)了

  1. vectorcall __vectorcall 调用约定指定尽可能在寄存器中传递函数的参数。 经测试与fastcall一样

常见C语句反汇编练习

  1. if-else 结构反汇编代码分析 因为if{。。。}语句块中的代码被执行的话,else{。。。}语句块的代码不会被执行 所以if{。。。}中的代码执行完成之后,需要(jmp)无条件跳转到else{。。。}语句块后面的代码执行。 源代码: int a = 1, b = 2; if (a > b) { printf("a>b\n"); } else { printf("a<=b"); }

汇编: 00F61093 |. C745 FC 01000 MOV DWORD PTR SS:[LOCAL.1],1 00F6109A |. C745 F8 02000 MOV DWORD PTR SS:[LOCAL.2],2 00F610A1 |. 8B45 FC MOV EAX,DWORD PTR SS:[LOCAL.1] 00F610A4 |. 3B45 F8 CMP EAX,DWORD PTR SS:[LOCAL.2] 00F610A7 |. 7E 0F JLE SHORT 00F610B8 00F610A9 |. 68 0C21F600 PUSH OFFSET 00F6210C /_Format = "a>b " 00F610AE |. E8 8DFFFFFF CALL printf \printf 00F610B3 |. 83C4 04 ADD ESP,4 00F610B6 |. EB 0D JMP SHORT 00F610C5 00F610B8 |> 68 1421F600 PUSH OFFSET 00F62114 /_Format = "a<=b" 00F610BD |. E8 7EFFFFFF CALL printf \printf 00F610C2 |. 83C4 04 ADD ESP,4 00F610C5 |> 33C0 XOR EAX,EAX

  1. switch-case结构反汇编代码分析 1)普通情况 源代码: int a = 3; switch (a) { case 1: printf("111/n"); break; case 2: printf("222/n"); break; case 10: printf("333/n"); break; default: printf("default/n"); break; } printf("end/n");

汇编: 00071093 |. C745 F8 03000 MOV DWORD PTR SS:[LOCAL.2],3 0007109A |. 8B45 F8 MOV EAX,DWORD PTR SS:[LOCAL.2] 0007109D |. 8945 FC MOV DWORD PTR SS:[LOCAL.1],EAX 000710A0 |. 837D FC 01 CMP DWORD PTR SS:[LOCAL.1],1 000710A4 |. 74 0E JE SHORT 000710B4 000710A6 |. 837D FC 02 CMP DWORD PTR SS:[LOCAL.1],2 000710AA |. 74 17 JE SHORT 000710C3 000710AC |. 837D FC 0A CMP DWORD PTR SS:[LOCAL.1],0A 000710B0 |. 74 20 JE SHORT 000710D2 000710B2 |. EB 2D JMP SHORT 000710E1 000710B4 |> 68 0C210700 PUSH OFFSET 0007210C /_Format = "111/n" 000710B9 |. E8 82FFFFFF CALL printf \printf 000710BE |. 83C4 04 ADD ESP,4 000710C1 |. EB 2B JMP SHORT 000710EE 000710C3 |> 68 14210700 PUSH OFFSET 00072114 /_Format = "222/n" 000710C8 |. E8 73FFFFFF CALL printf \printf 000710CD |. 83C4 04 ADD ESP,4 000710D0 |. EB 1C JMP SHORT 000710EE 000710D2 |> 68 1C210700 PUSH OFFSET 0007211C /_Format = "333/n" 000710D7 |. E8 64FFFFFF CALL printf \printf 000710DC |. 83C4 04 ADD ESP,4 000710DF |. EB 0D JMP SHORT 000710EE 000710E1 |> 68 24210700 PUSH OFFSET 00072124 /_Format = "default/n" 000710E6 |. E8 55FFFFFF CALL printf \printf 000710EB |. 83C4 04 ADD ESP,4 000710EE |> 68 30210700 PUSH OFFSET 00072130 /_Format = "end/n" 000710F3 |. E8 48FFFFFF CALL printf \printf 000710F8 |. 83C4 04 ADD ESP,4

2)跳转表 源代码: int a = 0x20; switch (a) { case 0x13: printf("0x13/n"); break; case 0x15: printf("0x15/n"); break; case 0x10: printf("0x10/n"); break; case 0x20: printf("0x20/n"); break; case 0x22: printf("0x22/n"); break; default: printf("default/n"); break; } printf("end/n");

汇编: 00F01093 |. C745 F8 20000 MOV DWORD PTR SS:[LOCAL.2],20 00F0109A |. 8B45 F8 MOV EAX,DWORD PTR SS:[LOCAL.2] 00F0109D |. 8945 FC MOV DWORD PTR SS:[LOCAL.1],EAX 00F010A0 |. 8B4D FC MOV ECX,DWORD PTR SS:[LOCAL.1] 00F010A3 |. 83E9 10 SUB ECX,10 00F010A6 |. 894D FC MOV DWORD PTR SS:[LOCAL.1],ECX 00F010A9 |. 837D FC 12 CMP DWORD PTR SS:[LOCAL.1],12 00F010AD |. 77 5C JA SHORT 00F0110B 00F010AF |. 8B55 FC MOV EDX,DWORD PTR SS:[LOCAL.1] 00F010B2 |. 0FB682 4411F0 MOVZX EAX,BYTE PTR DS:[EDX+0F01144] ASCII , switch (事例 0..5, 6 退出) 00F010B9 |. FF2485 2C11F0 JMP NEAR DWORD PTR DS:[EAX*4+0F0112C] 00F010C0 |> 68 1021F000 PUSH OFFSET 00F02110 /_Format = "0x13/n", 事例 1 of switch test1.0F010B2 00F010C5 |. E8 76FFFFFF CALL printf \printf 00F010CA |. 83C4 04 ADD ESP,4 00F010CD |. EB 49 JMP SHORT 00F01118 00F010CF |> 68 1821F000 PUSH OFFSET 00F02118 /_Format = "0x15/n", 事例 2 of switch test1.0F010B2 00F010D4 |. E8 67FFFFFF CALL printf \printf 00F010D9 |. 83C4 04 ADD ESP,4 00F010DC |. EB 3A JMP SHORT 00F01118 00F010DE |> 68 2021F000 PUSH OFFSET 00F02120 /_Format = "0x10/n", 事例 0 of switch test1.0F010B2 00F010E3 |. E8 58FFFFFF CALL printf \printf 00F010E8 |. 83C4 04 ADD ESP,4 00F010EB |. EB 2B JMP SHORT 00F01118 00F010ED |> 68 2821F000 PUSH OFFSET 00F02128 /_Format = "0x20/n", 事例 3 of switch test1.0F010B2 00F010F2 |. E8 49FFFFFF CALL printf \printf 00F010F7 |. 83C4 04 ADD ESP,4 00F010FA |. EB 1C JMP SHORT 00F01118 00F010FC |> 68 3021F000 PUSH OFFSET 00F02130 /_Format = "0x22/n", 事例 4 of switch test1.0F010B2 00F01101 |. E8 3AFFFFFF CALL printf \printf 00F01106 |. 83C4 04 ADD ESP,4 00F01109 |. EB 0D JMP SHORT 00F01118 00F0110B |> 68 3821F000 PUSH OFFSET 00F02138 /_Format = "default/n", 事例 5 of switch test1.0F010B2 00F01110 |. E8 2BFFFFFF CALL printf \printf 00F01115 |. 83C4 04 ADD ESP,4 00F01118 |> 68 4421F000 PUSH OFFSET 00F02144 /_Format = "end/n" 00F0111D |. E8 1EFFFFFF CALL printf \printf 00F01122 |. 83C4 04 ADD ESP,4

最重要的地方是: // 将要判断的值与最小的值相减,再与最大值和最小值的差作比较,如果都比他大了直接跳转到default。 00F010A3 |. 83E9 10 SUB ECX,10
00F010A6 |. 894D FC MOV DWORD PTR SS:[LOCAL.1],ECX 00F010A9 |. 837D FC 12 CMP DWORD PTR SS:[LOCAL.1],12 00F010AD |. 77 5C JA SHORT 00F0110B 00F010AF |. 8B55 FC MOV EDX,DWORD PTR SS:[LOCAL.1]

//找到跳转表的值放入EAX,[EAX4+0F0112C]计算过后的值要是要跳转的case语句。 00F010B2 |. 0FB682 4411F0 MOVZX EAX,BYTE PTR DS:[EDX+0F01144] ASCII , switch (事例 0..5, 6 退出) 00F010B9 |. FF2485 2C11F0 JMP NEAR DWORD PTR DS:[EAX4+0F0112C]

所以在逆向的时候应该这样做: 1)先找到目标值 2)通过sub找到最小值 3)通过cmp找到最大值 4)通过JMP NEAR DWORD PTR DS:[EAX*4+0F0112C]找到0F0112C附近的值,然后回推。

  1. for循环结构反汇编代码分析 for (int i=1; 1<=10; ++i) { printf (“%d",i); } (1)禁用优化情况下的for循环反汇编代码(0x43字节) (2)大小最小化优化情况下的for循环反汇编代码(0x27字节) (3)最大化速度优化情况下的for循环汇编代码 (4)完全优化

  2. 自增++和自减--语句 (1)加1指令inc inc操作符 inc a 相当于adda,1 //i++ ++i 优点速度比add指令快,占用空间小 这条指令执行结果影响AF、OF、PF、SF、ZF标志位,但不影响CF进位标志位 (2)减1指令dec dec操作符 dec a相当于sub a,1 //i-- --i 优点速度比sub指令快,占用空间小 这条指令执行结果影响AF、OF、PF、SF、ZE标志位,但不影响CF进位标志位 (3)用汇编理解前++、前--、后++、后--区别

  3. do- while和 while循环结构反汇编代码分析 (1)do- while循环 (2) while循环

十五、浮点指令

  1. st0至st7 80位的两用寄存器 MMX FPU FPU: 8个80位浮点寄存器(数据),16位状态寄存器,16位控制寄存器,16为标识寄存器。 使用FPU指令对这些寄存器进行操作,这些寄存器构成一个循环栈,st7栈底,st0栈顶, 当一个值被压入时,被存入st0,原来st0中的值被存入st7 MMX: 将8个FPU寄存器重命名为8个64位MMX寄存器,即mm0到mm7。[号称多媒体处理技术] 57条MMX指令,加快了整形浮点运算,但是对于复杂浮点运算无帮助 3.浮点指令 1). FLD类似于PUSH指令 向st0中放入数据 2). FSTP类似于POP指令 st0中的数据取出 3). FADD类似于ADD指令 格式 fadd memvar //st0 = st0 + memvar 4). FSUB类似于SUB指令 格式 fsub memvar //st0 = st0 - memvar 5). FMUL乘法指令 格式 fmul memvar //st0 = st0 * memva 6). FDIV除法指令 格式 FDIV memvar //st0 = st0 / menvar 7). FILD指令 整数入栈指令 FILD memvar / st0 = (double)memvar 8) cvttsd2si 浮点转整数指令 cvttsd2si r32, st0/m32 运用截断处理将sto/m32中的一个单精度浮点值转换成r32 中的一个有符号的双字整数

十六、位移指令

  1. 逻辑位移指令 (1)SHR 逻辑右移指令 右移一位相当于整除2,用0来补位 (2)SHL 逻辑左移指令 左移一位相当于乘2,用0来补位(有可能会溢出)

  2. 算术位移指令 (1) SAR 算术右移指令 SAR与SHR指令 SAR右移时保留操作数的符号,即用符号位来补足 SHR右移时总是用0来补足 (2)SAL 算术左移指令 SAL与SHL功能完全一样

十七、逻辑运算符 1.或运算 1)逻辑或(C语言中的||) 假假为假 if( a || b) { } 截断原理 2)按位或(C语言中的|) 0×33 00110011 | 0x66 01100110 = 0x77 01110111 OR指令

2.与运算 1)逻辑与(C语言中的&&) 真真为真 if(a && b) { } 截断原理 2)按位与(C语言中的&) 0×33 00110011 & 0x66 01100110 = 0x22 00100010 AND指令

  1. 非运算 1) 逻辑取反(C语言中的!) 假变真真变假 SETE (SETZ) 取ZF标志位的值保存 SETNE (SETNZ) 将ZF标志位的值取反后保存 2) 按位取反(C语言中的~) ~ 0x33 00110011 = 0xcc 11001100 NOT指令

  2. 异或运算 1)按位异或(C语言中的^) 1^1=0; 0^0=0; 相同为0 0^1=1; 1^0=1; 不同为1 1101^0110=1011

XOR指令 xor eax,eax 将eax置0 不借助第三个变量,将两个数做交换 int a = 5, b=7; a = a + b; b = a - b; a = a - b; 或 a = a ^ b; b = a ^ b; a = a ^ b;

十八、字符操作相关指令 1.字符串的比较函数 strcmp反汇编分析 vs设置项目->属性->C/C++->优化->启动内部函数->"否

2.REPNE和 SCASB指令 1) SCASB指令 SCASB编译后: SCAS BTYP PTR ES:[EID] 相当于: cmp byte prt [edi], al 对标志位的影响相当于sub指令,同时还会修改寄存器EDI的值: 如果标志位DF为0,则 inc EDI; 如果标志位DF为1,则 dec EDI;

2) REPNE指令 repnz scasb编译后: REPNE SCAS BTYP PTR ES:[EID] 当ECX!=0并且ZF=0时,重复执行后边的指令( SCAS BTYP PTR ES:[EID]), 每执行一次ECX的值减1 RENE和REPNZ是同一条指令的不同助记符

3) SCASB、 SCASW、 SCASD指令 SCASB SCAS BTYP PTR [EDI] char s1[0] byte 1 SCASW SCAS WORD PTR [EDI] short s1[0] word 2 SCASD SCAS DWORD PTR [EDI] int s1[0] dword 4

REPNE SCAS BTYP PTR[EDI] 当ECX!=0并且ZF=0、DF=0时,重复执行后面的指令 每次执行一次EDI的值加1,ECX的值减1 REPNE SCAS WORD PTR [EDI] 当ECX!=0并且ZF=0、DF=0时,重复执行后面的指令 每次执行一次EDI的值加2,ECX的值减1 REPNE SCAS DWORD PTR[EDI] 当ECX!=0并且ZF=0、DF=0时,重复执行后面的指令 每次执行一次EDI的值加4,ECX的值减1 (调试时,按F7才能单步执行)

4) 实例运用 1.计算字符串长度 2.定位特定字符串位置(索引) 3.在内存中定位一串特征码

小结: RENZ/ REPNE与 SCASB指令结合使用,表示当串未结束(ECX!=0),且当 对应串元素不相同(ZF=0)时,继续重复执行串比较指令

  1. REPE/REPZ和 CMPSB、 CMPSW、 CMPSD指令 1) CMPS cmps byte ptr [edi], byte ptr [esi] cmps word ptr [edi], byte ptr [esi] cmps dword ptr [edi], byte ptr [esi] 对标志位的影响相当于sub指令,同时还会修改寄存器EDI和ESI的值: 如果标志DF为0,则edi、esi按相对于大小( byte word dword)递增 如果标志DF为1,则edi、esi按相对于大小( byte word dword)递减 2)REPE/REPZ repe/ repz cmps当ecx!=0并且ZF=1时,重复执行后面的指令 每执行一次ecx的值减1 3)实例运用 比较串是否相等

4.汇编编写字符串比较函数 1) asm_strcmp函数 __declspec( naked) 告诉编译器用纯汇编方式编译函数,不自动添加寄存器保护和堆栈平衡代码 寄存器入栈保护 维持堆栈平衡 asm_strcmpA/asm_strcmpW 2)STD/CLD指令(DF方向标志位相关) STD DF=1 CLD DF=0

十九、串存储和串的加载指令 串存储指令 STOSB、 STOSW、 STOSD stosb stos byte ptr [edi] stosw stos word ptr [edi] stosd stos dword ptr [edi] 相当于: mov byte ptr [edi], al mov word ptr [edi],ax mov dword ptr [edi],eax

rep stosb rep stos byte ptr [edi] 用al的值填充 byte ptr[edi],每次ecx值减1,edi的值增加1 rep stosw rep stos word ptr [edi] 用ax的值填充 byte ptr[edi],每次ecx值减1,edi的值增加2 rep stosd rep stos dword ptr [edi] 用eax的值填充 byte ptr[edi],每次ecx值减1,edi的值增加4

定位main函数位置的步骤: 第一步:打开程序,程序启动后停在这里,直接jmp跳转

第二步:jmp跳转跟随之后,找到ca11 dword ptr[<& MSVCR100.exit>]退出的代码的位置

第三步:ca11 dword ptr[<& MSVCR100.exit>]的前一个ca11就是main函数

  1. 串载入指令 LODSB、 LODSW、 LODSD lodsb lods byte ptr [esi] lodsw lods word ptr [esi] lodsd lods dword ptr [esi] 相当于: mov al, byte ptr [esi] mov ax, word ptr [esi] mov eax, dword ptr [esi]

rep lodsb rep lods byte ptr [esi] 用 byte ptr[esi]的值,填充al,每次ecx值减1,esi的值增加1 rep lodsw rep lods word ptr [esi] 用 word ptr[esi]的值,填充ax,每次ecx值减1,esi的值增加2 rep lodsd rep lods dword ptr [esi] 用 dword ptr[esi]的值,填充eax,每次ecκ值减1,esi的值增加4

十、循环控制指令 格式: LOOP标号 loop start/end 功能: (1)ECX = ECX -1 (2)ECⅩ!=0,则转移至标号处循环执行 (3)直至ECX=0,继续执行后面的指令 本指令是用ECX寄存器作为计数器,来控制程序的循环

二十一、条件置位指令 1.(==比较时) SETZ(SETE) 取ZF标志位的值保存 2.(!=比较时) SETNZ*( SETNE)将ZF标志位值取反后保存 3,SETG(>比较时) setg al 当ZF==0&&SF==1时al=1 4.SETL(<比较时) setl al 当SF==1 || OF==1时al=1

  1. SETGE(>=比较时) SERGE操作数 操作数可以是一个字节的存储单元,可以是一个字节宽度的寄存器 作用:>=时,设定操作数值1,否则为0,一般与cmp指令组合使用 标志位:JGE对标志的需求SF=OF时,操作数的值将为1
  2. SETGE(<比较时) SETLE操作数 操作数可以是一个字节的存储单元,可以是一个字节宽度的寄存器 作用:<=时,设定操作数值1,否则为0,一般与cmp指令组合使用 标志位:JLE对标志的需求ZF==1 || SF!=OF时,操作数的值将为1

results matching ""

    No results matching ""