源代码:
#include "stdafx.h"
void test1(){}
void test2(int a,int b) {}
void test3() {
int a = 100;
int b = a + 50;
}
void test4(int a, int b) {
int c = a + b;
int d = a*b;
}
int main()
{
char *p1 = "没有任何参数的空函数";//定义一个字符串,在OD 里面容易定位
test1();
char *p2 = "有形参的空函数";
test2(1, 2);
char *p3 = "没有参数但是有局部变量";
test3();
char *p4 = "有参数并且有局部变量";
test4(1,2);
return 0;
}
test1:
002C16A0 > 55 PUSH EBP
002C16A1 8BEC MOV EBP,ESP
002C16A3 81EC C0000000 SUB ESP,0xC0//前三行开辟栈帧
002C16A9 53 PUSH EBX
002C16AA 56 PUSH ESI
002C16AB 57 PUSH EDI //保存临时变量
002C16AC 8DBD 40FFFFFF LEA EDI,DWORD PTR SS:[EBP-0xC0]//将EBP-0xC0中的值保存于edi中
002C16B2 B9 30000000 MOV ECX,0x30//计数器循环30次
002C16B7 B8 CCCCCCCC MOV EAX,0xCCCCCCCC
002C16BC F3:AB REP STOS DWORD PTR ES:[EDI]//将eax中的值传入edi之中,循环30次
002C16BE 5F POP EDI
002C16BF 5E POP ESI
002C16C0 5B POP EBX
002C16C1 8BE5 MOV ESP,EBP
002C16C3 5D POP EBP//回收栈帧
002C16C4 C3 RETN//返回
test2:(和test1一摸一样,唯一的区别就是在call上面压入了两个参数)
002C17BE C745 F8 306B2C0>MOV DWORD PTR SS:[EBP-0x8],空函数.002C6B30
002C17C5 E8 7BF9FFFF CALL 空函数.002C1145 ; test1
002C17CA C745 EC 4C6B2C0>MOV DWORD PTR SS:[EBP-0x14],空函数.002C6B4C
002C17D1 6A 02 PUSH 0x2
002C17D3 6A 01 PUSH 0x1
002C17D5 E8 23FBFFFF CALL 空函数.002C12FD ; test2
002C16D0 > 55 PUSH EBP
002C16D1 8BEC MOV EBP,ESP
002C16D3 81EC C0000000 SUB ESP,0xC0
002C16D9 53 PUSH EBX
002C16DA 56 PUSH ESI
002C16DB 57 PUSH EDI
002C16DC 8DBD 40FFFFFF LEA EDI,DWORD PTR SS:[EBP-0xC0]
002C16E2 B9 30000000 MOV ECX,0x30
002C16E7 B8 CCCCCCCC MOV EAX,0xCCCCCCCC
002C16EC F3:AB REP STOS DWORD PTR ES:[EDI]
002C16EE 5F POP EDI
002C16EF 5E POP ESI
002C16F0 5B POP EBX
002C16F1 8BE5 MOV ESP,EBP
002C16F3 5D POP EBP
test3:
002C1700 > 55 PUSH EBP
002C1701 8BEC MOV EBP,ESP
002C1703 81EC D8000000 SUB ESP,0xD8
002C1709 53 PUSH EBX
002C170A 56 PUSH ESI
002C170B 57 PUSH EDI
002C170C 8DBD 28FFFFFF LEA EDI,DWORD PTR SS:[EBP-0xD8]
002C1712 B9 36000000 MOV ECX,0x36
002C1717 B8 CCCCCCCC MOV EAX,0xCCCCCCCC
002C171C F3:AB REP STOS DWORD PTR ES:[EDI]
002C171E C745 F8 6400000>MOV DWORD PTR SS:[EBP-0x8],0x64//局部变量a赋值100
002C1725 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-0x8]
002C1728 83C0 32 ADD EAX,0x32
002C172B 8945 EC MOV DWORD PTR SS:[EBP-0x14],EAX//局部变量b,b=a+50
002C172E 5F POP EDI
002C172F 5E POP ESI
002C1730 5B POP EBX
002C1731 8BE5 MOV ESP,EBP
002C1733 5D POP EBP
002C1734 C3 RETN
test4:
002C17F0 6A 02 PUSH 0x2
002C17F2 6A 01 PUSH 0x1
002C17F4 E8 D2FAFFFF CALL 空函数.002C12CB ; test4
002C17F9 83C4 08 ADD ESP,0x8
002C1750 > 55 PUSH EBP
002C1751 8BEC MOV EBP,ESP
002C1753 81EC D8000000 SUB ESP,0xD8
002C1759 53 PUSH EBX
002C175A 56 PUSH ESI
002C175B 57 PUSH EDI
002C175C 8DBD 28FFFFFF LEA EDI,DWORD PTR SS:[EBP-0xD8]
002C1762 B9 36000000 MOV ECX,0x36
002C1767 B8 CCCCCCCC MOV EAX,0xCCCCCCCC
002C176C F3:AB REP STOS DWORD PTR ES:[EDI]
002C176E 8B45 08 MOV EAX,DWORD PTR SS:[EBP+0x8]//将参数a赋值给eax
002C1771 0345 0C ADD EAX,DWORD PTR SS:[EBP+0xC]//将参数a与参数b相加赋值给eax,参数a,b为函数定义中的形参
002C1774 8945 F8 MOV DWORD PTR SS:[EBP-0x8],EAX//将a于b之和赋值给局部变量c
002C1777 8B45 08 MOV EAX,DWORD PTR SS:[EBP+0x8]//参数a赋值给eax
002C177A 0FAF45 0C IMUL EAX,DWORD PTR SS:[EBP+0xC]//参数a于参数b相乘赋值给eax
002C177E 8945 EC MOV DWORD PTR SS:[EBP-0x14],EAX//将a与b之积赋值给局部变量d,注意:局部变量的地址没有规定是可以在开辟的栈帧内随便定义的!!!
002C1781 5F POP EDI
002C1782 5E POP ESI
002C1783 5B POP EBX
002C1784 8BE5 MOV ESP,EBP
002C1786 5D POP EBP
002C1787 C3 RETN
for循环:
#include "stdafx.h"
int main()
{
int num = 0; //局部变量num
char *p1 = "这是第一个循环";
for (int i = 0; i < 10; i++)
{
}
char *p2 = "这是第二个循环";
for (;;)
{
}
char *p3 = "这是第三个循环";
for (int i = 0; i < 10; i++)
{
num++;
}
return 0;
}
主函数:
00311650 > 55 PUSH EBP
00311651 8BEC MOV EBP,ESP
00311653 81EC 08010000 SUB ESP,0x108
00311659 53 PUSH EBX
0031165A 56 PUSH ESI
0031165B 57 PUSH EDI
0031165C 8DBD F8FEFFFF LEA EDI,DWORD PTR SS:[EBP-0x108]
00311662 B9 42000000 MOV ECX,0x42
00311667 B8 CCCCCCCC MOV EAX,0xCCCCCCCC
0031166C F3:AB REP STOS DWORD PTR ES:[EDI]
0031166E C745 F8 0000000>MOV DWORD PTR SS:[EBP-0x8],0x0
00311675 C745 EC 306B310>MOV DWORD PTR SS:[EBP-0x14],for循环.00316B30
0031167C C745 E0 0000000>MOV DWORD PTR SS:[EBP-0x20],0x0
00311683 EB 09 JMP SHORT for循环.0031168E
00311685 8B45 E0 MOV EAX,DWORD PTR SS:[EBP-0x20]
00311688 83C0 01 ADD EAX,0x1
0031168B 8945 E0 MOV DWORD PTR SS:[EBP-0x20],EAX
0031168E 837D E0 0A CMP DWORD PTR SS:[EBP-0x20],0xA
00311692 7D 02 JGE SHORT for循环.00311696
00311694 ^ EB EF JMP SHORT for循环.00311685
00311696 C745 D4 446B310>MOV DWORD PTR SS:[EBP-0x2C],for循环.00316B44
0031169D - EB FE JMP SHORT for循环.0031169D
0031169F C745 C8 586B310>MOV DWORD PTR SS:[EBP-0x38],for循环.00316B58
003116A6 C745 BC 0000000>MOV DWORD PTR SS:[EBP-0x44],0x0
003116AD EB 09 JMP SHORT for循环.003116B8
003116AF 8B45 BC MOV EAX,DWORD PTR SS:[EBP-0x44]
003116B2 83C0 01 ADD EAX,0x1
003116B5 8945 BC MOV DWORD PTR SS:[EBP-0x44],EAX
003116B8 837D BC 0A CMP DWORD PTR SS:[EBP-0x44],0xA
003116BC 7D 0B JGE SHORT for循环.003116C9
003116BE 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-0x8]
003116C1 83C0 01 ADD EAX,0x1
003116C4 8945 F8 MOV DWORD PTR SS:[EBP-0x8],EAX
003116C7 ^ EB E6 JMP SHORT for循环.003116AF
003116C9 33C0 XOR EAX,EAX
003116CB 5F POP EDI
003116CC 5E POP ESI
003116CD 5B POP EBX
003116CE 8BE5 MOV ESP,EBP
003116D0 5D POP EBP
003116D1 C3 RETN
第一个for循环:
0031166E C745 F8 0000000>MOV DWORD PTR SS:[EBP-0x8],0x0
00311675 C745 EC 306B310>MOV DWORD PTR SS:[EBP-0x14],for循环.00316B30
0031167C C745 E0 0000000>MOV DWORD PTR SS:[EBP-0x20],0x0//i = 0;
00311683 EB 09 JMP SHORT for循环.0031168E//跳转到i的检测条件,即i<10,汇编里面是将i与10做比较
00311685 8B45 E0 MOV EAX,DWORD PTR SS:[EBP-0x20]//将i赋值给eax
00311688 83C0 01 ADD EAX,0x1//i++,i自增1
0031168B 8945 E0 MOV DWORD PTR SS:[EBP-0x20],EAX//将i+1之后的值再次赋值给i,实现了i++的功能
0031168E 837D E0 0A CMP DWORD PTR SS:[EBP-0x20],0xA//上面的jmp跳转至此处,检测条件为:将i和10进行比较
00311692 7D 02 JGE SHORT for循环.00311696//如果i大于10就会跳出此循环
00311694 EB EF JMP SHORT for循环.00311685//跳转至上面的地址继续执行循环
小结:
- 三个jmp,下面最后一个jmp跳往最上面一个jmp的后面,即:三个jmp,最后一个jmp跳往第一个jmp的后面一句,
(jmp+cmp,条件跳+jmp往上跳)
第二个for循环:
0031169D - EB FE JMP SHORT for循环.0031169D//死循环,一直在本地址跳转
第三个for循环:
003116A6 C745 BC 0000000 MOV DWORD PTR SS:[EBP-0x44],0x0//i=0;
003116AD EB 09 JMP SHORT for循环.003116B8//跳到条件检查,同上面第一个for循环
003116AF 8B45 BC MOV EAX,DWORD PTR SS:[EBP-0x44]//将i值赋给eax
003116B2 83C0 01 ADD EAX,0x1//i++
003116B5 8945 BC MOV DWORD PTR SS:[EBP-0x44],EAX//将i++后的值再次赋值给i,实现了i++
003116B8 837D BC 0A CMP DWORD PTR SS:[EBP-0x44],0xA//比较的条件
003116BC 7D 0B JGE SHORT for循环.003116C9//大于等于则直接跳出循环
003116BE 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-0x8]//num的值赋值给eax
003116C1 83C0 01 ADD EAX,0x1//num++
003116C4 8945 F8 MOV DWORD PTR SS:[EBP-0x8],EAX//实现了num++
003116C7 EB E6 JMP SHORT for循环.003116AF//跳到上面的地址继续执行循环体
总结:大体上逻辑与第一个for循环的逻辑一致,不过是多了num++的三句汇编
小结:
- 三个jmp,最后一个jmp跳往第一个jmp的后面一句,
(jmp+cmp,条件跳+jmp往上跳)
while循环
#include "stdafx.h"
int main()
{
int i = 0;
int sum = 0;
char*p = "这是while循环";
while (i < 10) {
sum += i;
}
char*p2 = "这是do while循环";
i = 0;
sum = 0;
do {
sum += i;
} while (i < 10);
return 0;
}
OD中对应汇编:
01041650 > 55 PUSH EBP
01041651 8BEC MOV EBP,ESP
01041653 81EC F0000000 SUB ESP,0xF0
01041659 53 PUSH EBX
0104165A 56 PUSH ESI
0104165B 57 PUSH EDI
0104165C 8DBD 10FFFFFF LEA EDI,DWORD PTR SS:[EBP-0xF0]
01041662 B9 3C000000 MOV ECX,0x3C
01041667 B8 CCCCCCCC MOV EAX,0xCCCCCCCC
0104166C F3:AB REP STOS DWORD PTR ES:[EDI]
0104166E C745 F8 0000000>MOV DWORD PTR SS:[EBP-0x8],0x0
01041675 C745 EC 0000000>MOV DWORD PTR SS:[EBP-0x14],0x0
0104167C C745 E0 CC6C040>MOV DWORD PTR SS:[EBP-0x20],while.01046CCC ; ASCII "这是while循环"
01041683 837D F8 0A CMP DWORD PTR SS:[EBP-0x8],0xA
01041687 7D 0B JGE SHORT while.01041694
01041689 8B45 EC MOV EAX,DWORD PTR SS:[EBP-0x14]
0104168C 0345 F8 ADD EAX,DWORD PTR SS:[EBP-0x8]
0104168F 8945 EC MOV DWORD PTR SS:[EBP-0x14],EAX
01041692 ^ EB EF JMP SHORT while.01041683
01041694 C745 D4 306B040>MOV DWORD PTR SS:[EBP-0x2C],while.01046B30
0104169B C745 F8 0000000>MOV DWORD PTR SS:[EBP-0x8],0x0
010416A2 C745 EC 0000000>MOV DWORD PTR SS:[EBP-0x14],0x0
010416A9 8B45 EC MOV EAX,DWORD PTR SS:[EBP-0x14]
010416AC 0345 F8 ADD EAX,DWORD PTR SS:[EBP-0x8]
010416AF 8945 EC MOV DWORD PTR SS:[EBP-0x14],EAX
010416B2 837D F8 0A CMP DWORD PTR SS:[EBP-0x8],0xA
010416B6 ^ 7C F1 JL SHORT while.010416A9
010416B8 33C0 XOR EAX,EAX
010416BA 5F POP EDI
010416BB 5E POP ESI
010416BC 5B POP EBX
010416BD 8BE5 MOV ESP,EBP
010416BF 5D POP EBP
010416C0 C3 RETN
while反汇编:
0104166E C745 F8 00000000 MOV DWORD PTR SS:[EBP-0x8],0x0//i=0;
01041675 C745 EC 00000000 MOV DWORD PTR SS:[EBP-0x14],0x0//sum=0
0104167C C745 E0 CC6C0401 MOV DWORD PTR SS:[EBP-0x20],while.01046CCC ; //字符串,ASCII "这是while循环"
01041683 837D F8 0A CMP DWORD PTR SS:[EBP-0x8],0xA//判断条件,i和10比较
01041687 7D 0B JGE SHORT while.01041694 //如果i>=10,跳出循环
01041689 8B45 EC MOV EAX,DWORD PTR SS:[EBP-0x14]//sum=eax
0104168C 0345 F8 ADD EAX,DWORD PTR SS:[EBP-0x8]//sum+i
0104168F 8945 EC MOV DWORD PTR SS:[EBP-0x14],EAX//sum=sum+i
01041692 ^ EB EF JMP SHORT while.01041683//跳转至判断条件处
do while反汇编代码:
0104169B C745 F8 00000000 MOV DWORD PTR SS:[EBP-0x8],0x0//i=0
010416A2 C745 EC 00000000 MOV DWORD PTR SS:[EBP-0x14],0x0//sum=0
010416A9 8B45 EC MOV EAX,DWORD PTR SS:[EBP-0x14]
010416AC 0345 F8 ADD EAX,DWORD PTR SS:[EBP-0x8]
010416AF 8945 EC MOV DWORD PTR SS:[EBP-0x14],EAX
010416B2 837D F8 0A CMP DWORD PTR SS:[EBP-0x8],0xA//先执行函数体,然后再判断条件
010416B6 7C F1 JL SHORT while.010416A9
while总结:
- while是判断条件不符合,直接跳转到循环外面,否则就一直执行循环体 (while和for循环有点类似),
cmp+2个jmp
,
while:cmp+条件跳+jmp往上跳
- do while 是先执行函数体,然后再判断,
cmp+条件跳(往上跳)
if语句
#include "stdafx.h"
int main()
{
int num = 10;
if (num == 10)
{
num = 20;
}
else if (num == 20)
{
num = 30;
}
num = 2;
if (num == 0)
{
num++;
}
if (num == 2)
{
num++;
}
return 0;
}
00241675 837D F8 0A CMP DWORD PTR SS:[EBP-0x8],0xA//num和10比较
00241679 75 09 JNZ SHORT if语句.00241684//如果不为10,就跳转至else if处,为10就不跳
0024167B C745 F8 14000000 MOV DWORD PTR SS:[EBP-0x8],0x14//num=20
00241682 EB 0D JMP SHORT if语句.00241691//跳走至00241691
00241684 837D F8 14 CMP DWORD PTR SS:[EBP-0x8],0x14//和20比较
00241688 75 07 JNZ SHORT if语句.00241691//如果不为20,就跳至00241691
第二个if语句:
00241698 837D F8 00 CMP DWORD PTR SS:[EBP-0x8],0x0//num和0比较
0024169C 75 09 JNZ SHORT if语句.002416A7//如果为0就不跳转,否则跳转至下个判断句
0024169E 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-0x8]
002416A1 83C0 01 ADD EAX,0x1//num++
002416A4 8945 F8 MOV DWORD PTR SS:[EBP-0x8],EAX
002416A7 837D F8 02 CMP DWORD PTR SS:[EBP-0x8],0x2//num和2比较
002416AB 75 09 JNZ SHORT if语句.002416B6//如果不是2,则直接跳转,否则直接运行代码
002416AD 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-0x8]
002416B0 83C0 01 ADD EAX,0x1//num++
002416B3 8945 F8 MOV DWORD PTR SS:[EBP-0x8],EAX
if语句总结:
- if…else if… 会出现多个JMP跳到判断外的同一个地址,
- if…if…. 跳转到的地方不一样,第一个会跳到的地方与第二个跳到的地方不一样。中间没有JMP跳转。
switch语句
int main()
{
int num = 10;
switch (num)
{
case 1: num = 1; break;
case 2: num = 2; break;
case 4: num = 4; break;
case 5: num = 5; break;
case 6:
case 7: num = 7; break;
default: num = 0; break;
}
return 0;
}
0035166E C745 F8 0A000000 MOV DWORD PTR SS:[EBP-0x8],0xA//num=10
00351675 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-0x8]
00351678 8985 30FFFFFF MOV DWORD PTR SS:[EBP-0xD0],EAX//将num的值放在ebp-0xd0处
0035167E 8B8D 30FFFFFF MOV ECX,DWORD PTR SS:[EBP-0xD0]
00351684 83E9 01 SUB ECX,0x1//将num-1=9
00351687 898D 30FFFFFF MOV DWORD PTR SS:[EBP-0xD0],ECX
0035168D 83BD 30FFFFFF 06 CMP DWORD PTR SS:[EBP-0xD0],0x6//将num-1=9与6做比较
00351694 77 3A JA SHORT switch.003516D0//如果大于6 直接跳到 num= 0 处 也就是 default处
00351696 8B95 30FFFFFF MOV EDX,DWORD PTR SS:[EBP-0xD0]
0035169C FF2495 E0163500 JMP DWORD PTR DS:[EDX*4+0x3516E0]//这个地方非常巧妙 0x3516E0位置是跳转表的地址 前面edx*4 代表索引 因为每个地址占4字节 因此每次增加4
$LN6:
003516A3 C745 F8 01000000 MOV DWORD PTR SS:[EBP-0x8],0x1//num=1
003516AA EB 2B JMP SHORT switch.003516D7//break
003516AC C745 F8 02000000 MOV DWORD PTR SS:[EBP-0x8],0x2//num=2
003516B3 EB 22 JMP SHORT switch.003516D7//break
003516B5 C745 F8 04000000 MOV DWORD PTR SS:[EBP-0x8],0x4//num=4
003516BC EB 19 JMP SHORT switch.003516D7//break
003516BE C745 F8 05000000 MOV DWORD PTR SS:[EBP-0x8],0x5//num=5
003516C5 EB 10 JMP SHORT switch.003516D7//break
003516C7 C745 F8 07000000 MOV DWORD PTR SS:[EBP-0x8],0x7//num=7
003516CE EB 07 JMP SHORT switch.003516D7//break
003516D0 C745 F8 00000000 MOV DWORD PTR SS:[EBP-0x8],0x0
003516D7 33C0 XOR EAX,EAX
003516D9 5F POP EDI
003516DA 5E POP ESI
003516DB 5B POP EBX
003516DC 8BE5 MOV ESP,EBP
003516DE 5D POP EBP
003516DF C3 RETN
总结:
如果出现多次JMP跳到同一个位置,可以初步判断就是switch
另外 跳转表这个位置是数据,不是代码 如果出现* jmp dword ptr [edx*4+ 0x3516E0]* 这样的代码,那就是switch。
另外,如果case分支非常少,编译器会将其优化成if…else if… 代码一模一样。