|
————————————————————————————————————————————————
分析环境:WIN7sp1
所用工具:VC++6.0/OllyDBG/IDA
适用人群:有一定计算机基础,熟悉C/C++编程,熟悉X86系列汇编/了解OD/IDA等调试工具使用,对逆向安全有极大兴趣者!
————————————————————————————————————————————————
开篇前言:
数据类型和运算符是任何编程语言的基础,而对于逆向而言亦是,只有牢固的基础才能走得更远...
————————————————————————————————————————————————
正文部分:
本节主要从汇编层面来全面解析程序的基本流程控制语句:while,do-while,goto 如下:
___________________________________________________________________________________________________
本帖隐藏的内容- #include < stdio.h >
- int main()
- {
- int x = 1,
- y = 0;
- while (x <= 100)
- {
- y = x + y;
- x++;
- }
- printf("%d", y);
- return 0;
- }
[color=rgb(81, 112, 58) !important]复制代码
___________________________________________________________________________________________________
上面是一个使用while语句来计算1+2+3...+100的和的程序,使用了一个计数器!
Debug版本:
00401010 >|> \55 push ebp ; ebp入栈保存
00401011 |. 8BEC mov ebp,esp ; esp保存到ebp
00401013 |. 83EC 48 sub esp,0x48 ; 开辟局部变量空间
00401016 |. 53 push ebx ; ebx入栈保存
00401017 |. 56 push esi ; esi入栈保存
00401018 |. 57 push edi ; edi入栈保存
00401019 |. 8D7D B8 lea edi,[local.18] ; 设置CC操作起始化地址
0040101C |. B9 12000000 mov ecx,0x12 ; 设置循环次数
00401021 |. B8 CCCCCCCC mov eax,0xCCCCCCCC ; 赋值eax CC int 3
00401026 |. F3:AB rep stos dword ptr es:[edi] ; 循环复制cc
00401028 |. C745 FC 01000>mov [local.1],0x1 ; 赋值局部变量x
0040102F |. C745 F8 00000>mov [local.2],0x0 ; 赋值局部变量y
00401036 |> 837D FC 64 /cmp [local.1],0x64 ; 测试x是否为100
0040103A |. 7F 14 |jg short Test3.00401050 ; 如果为100就跳出while循环
0040103C |. 8B45 FC |mov eax,[local.1] ; 变量x值放入eax
0040103F |. 0345 F8 |add eax,[local.2] ; x+y的值放入eax
00401042 |. 8945 F8 |mov [local.2],eax ; eax的值放入变量y
00401045 |. 8B4D FC |mov ecx,[local.1] ; 变量x的值放入ecx
00401048 |. 83C1 01 |add ecx,0x1 ; exc自加1
0040104B |. 894D FC |mov [local.1],ecx ; ecx赋值到变量x
0040104E |.^ EB E6 \jmp short Test3.00401036 ; 无条件跳转到while条件判断处
00401050 |> 8B55 F8 mov edx,[local.2] ; 最终y的值放入edx
00401053 |. 52 push edx ; /<%d>
00401054 |. 68 1C204200 push Test3.0042201C ; |format = "%d"
00401059 |. E8 32000000 call Test3.printf ; \printf
0040105E |. 83C4 08 add esp,0x8 ; 恢复esp
00401061 |. 33C0 xor eax,eax ; eax清零
00401063 |. 5F pop edi ; 恢复edi
00401064 |. 5E pop esi ; 恢复esi
00401065 |. 5B pop ebx ; 恢复ebx
00401066 |. 83C4 48 add esp,0x48 ; 恢复局部变量空间
00401069 |. 3BEC cmp ebp,esp ; 测试堆栈平衡
0040106B |. E8 A0000000 call Test3._chkesp ; 调用调试信息
00401070 |. 8BE5 mov esp,ebp ; 恢复esp
00401072 |. 5D pop ebp ; 恢复ebp
00401073 \. C3 retn ; 返回 ret add esp,4
while循环基本结构:
cmp ;判断while条件是否满足
jg ;不满足就跳出while循环
... ;while执行体
jmp ;无条件跳转到条件判断处
Release版本:
00401000 /$ B8 01000000 mov eax,1 ; eax赋值为1
00401005 |. 33C9 xor ecx,ecx ; 清零ecx,计数之用
00401007 |> 03C8 /add ecx,eax ; eax加上ecx放入ecx
00401009 |. 40 |inc eax ; eax自加1
0040100A |. 83F8 64 |cmp eax,64 ; 和100做比较
0040100D |.^ 7E F8 \jle short Test3.00401007 ; 如果不满足条件就跳回add ecx,eax
0040100F |. 51 push ecx ; 参数
00401010 |. 68 30704000 push Test3.00407030 ; ASCII "%d"
00401015 |. E8 06000000 call Test3.00401020 ; printf函数
0040101A |. 83C4 08 add esp,8 ; 恢复esp
0040101D |. 33C0 xor eax,eax ; eax清零
0040101F \. C3 retn ; 返回 ret add esp,4
while循环基本结构:
自动判断是否满足while条件,如果否就直接跳过while,这里满足
... ;while执行体
cmp ;判断while条件是否满足
jle ;不满足就跳出while循环
总结:根据反汇编两种不同的编译方式可以看出,编译器已经自动识别出原始条件是否满足首次while条件,如满足则直接执行while内容,不满足就直接跳过while。
___________________________________________________________________________________________________
- #include < stdio.h >
- int main()
- {
- int x;
- do
- {
- printf("请输入一个数字");
- scanf("%d", &x);
- printf("你输入的数字是:%d\n", x);
- } while ( x != 0 );
- return 0;
- }
[color=rgb(81, 112, 58) !important]复制代码
___________________________________________________________________________________________________
上面程序是判断用户的输入数值,不等于0就一直输入,直到等于0就跳出do-while循环
Debug版本:
00401010 |> \55 push ebp ; ebp入栈保存
00401011 |. 8BEC mov ebp,esp ; esp保存到ebp
00401013 |. 83EC 44 sub esp,44 ; 开辟局部变量空间
00401016 |. 53 push ebx ; ebx入栈保存
00401017 |. 56 push esi ; esi入栈保存
00401018 |. 57 push edi ; edi入栈保存
00401019 |. 8D7D BC lea edi,dword ptr ss:[ebp-44] ; 设置CC操作起始地址
0040101C |. B9 11000000 mov ecx,11 ; 设置循环次数
00401021 |. B8 CCCCCCCC mov eax,CCCCCCCC ; 赋值 eax CC int 3
00401026 |. F3:AB rep stos dword ptr es:[edi] ; 循环复制 CC
00401028 |> 68 B82F4200 /push Test3.00422FB8 ; /参数入栈
0040102D |. E8 5E000000 |call Test3.00401090 ; \调用printf函数
00401032 |. 83C4 04 |add esp,4 ; 恢复esp
00401035 |. 8D45 FC |lea eax,dword ptr ss:[ebp-4] ; 取变量x的地址到eax
00401038 |. 50 |push eax ; /参数入栈
00401039 |. 68 1C204200 |push Test3.0042201C ; |Arg1 = 0042201C ASCII "%d"
0040103E |. E8 BDE80000 |call Test3.0040F900 ; \调用scanf函数
00401043 |. 83C4 08 |add esp,8 ; 恢复esp
00401046 |. 8B4D FC |mov ecx,dword ptr ss:[ebp-4] ; 变量x的值放入ecx
00401049 |. 51 |push ecx ; /参数入栈
0040104A |. 68 A42F4200 |push Test3.00422FA4 ; |参数入栈
0040104F |. E8 3C000000 |call Test3.00401090 ; \调用printf函数
00401054 |. 83C4 08 |add esp,8 ; 恢复esp
00401057 |. 837D FC 00 |cmp dword ptr ss:[ebp-4],0 ; 判断x值是否为0
0040105B |.^ 75 CB \jnz short Test3.00401028 ; 不为0即跳到到do循环出执行
0040105D |. 33C0 xor eax,eax ; eax清零
0040105F |. 5F pop edi ; 恢复edi
00401060 |. 5E pop esi ; 恢复esi
00401061 |. 5B pop ebx ; 恢复ebx
00401062 |. 83C4 44 add esp,44 ; 恢复局部变量空间
00401065 |. 3BEC cmp ebp,esp ; 测试堆栈平衡
00401067 |. E8 A4000000 call Test3.00401110 ; 调用调试信息
0040106C |. 8BE5 mov esp,ebp ; 恢复esp
0040106E |. 5D pop ebp ; 恢复ebp
0040106F \. C3 retn ; 返回 ret add esp,4
do-while基本结构:
.... ;do循环执行体
cmp ;条件判断
jnz ;如果不满足条件就跳出循环
Release版本:
00401000 /$ 51 push ecx ; ecx入栈保存
00401001 |> 68 48804000 /push Test3.00408048 ; 参数
00401006 |. E8 4C000000 |call Test3.00401057 ; printf函数
0040100B |. 8D4424 04 |lea eax,dword ptr ss:[esp+4] ; 取变量x地址到eax
0040100F |. 50 |push eax ; 参数入栈
00401010 |. 68 44804000 |push Test3.00408044 ; ASCII "%d"
00401015 |. E8 26000000 |call Test3.00401040 ; scanf函数
0040101A |. 8B4C24 0C |mov ecx,dword ptr ss:[esp+C] ; 变量值赋值到ecx
0040101E |. 51 |push ecx ; 参数
0040101F |. 68 30804000 |push Test3.00408030 ; 参数
00401024 |. E8 2E000000 |call Test3.00401057 ; printf函数
00401029 |. 8B4424 14 |mov eax,dword ptr ss:[esp+14] ; 变量值赋值到eax中
0040102D |. 83C4 14 |add esp,14 ; 恢复esp
00401030 |. 85C0 |test eax,eax ; 判断eax是否为零
00401032 |.^ 75 CD \jnz short Test3.00401001 ; 如果不为零,就跳回执行
00401034 |. 33C0 xor eax,eax ; eax清零
00401036 |. 59 pop ecx ; 恢复ecx
00401037 \. C3 retn ; 返回 ret add esp,4
do-while基本结构:
.... ;do循环执行体
test ;条件判断
jnz ;如果不满足条件就跳出循环
两种编译模式的汇编判断流程很相似,这也就是do-while和while的区别,不管是否满足条件,都会先执行一次再做判断!
___________________________________________________________________________________________________
goto语句:
- #include < stdio.h >
- int main()
- {
- int a;
- qq: printf("请输入一个数字:");
- scanf("%d", &a);
- if (a == 0)
- goto qq;
- return 0;
- }
[color=rgb(81, 112, 58) !important]复制代码
___________________________________________________________________________________________________
上面程序是判断用户输入,如果值为0就跳回重新输入判断,这里使用了goto语句
Debug版本:
00401010 >|> \55 push ebp ; 保存ebp
00401011 |. 8BEC mov ebp,esp ; 保存esp到ebp
00401013 |. 83EC 44 sub esp,44 ; 开辟局部变量空间
00401016 |. 53 push ebx ; ebx入栈保存
00401017 |. 56 push esi ; esi入栈保存
00401018 |. 57 push edi ; edi入栈保存
00401019 |. 8D7D BC lea edi,[local.17] ; 设置CC操作起始地址
0040101C |. B9 11000000 mov ecx,11 ; 设置循环次数
00401021 |. B8 CCCCCCCC mov eax,CCCCCCCC ; 赋值 eax CC int 3
00401026 |. F3:AB rep stos dword ptr es:[edi] ; 循环复制 CC
00401028 |> 68 20504200 /push offset Test3.??_C@_0BB@FLPH@?G?k?J?d?H?k?R?$LL?$LI?>; 参数
0040102D |. E8 AE000000 |call Test3.printfgvdbgind_blockeressges ; printf函数
00401032 |. 83C4 04 |add esp,4 ; 恢复esp
00401035 |. 8D45 FC |lea eax,[local.1] ; 取变量地址到eax
00401038 |. 50 |push eax ; 参数
00401039 |. 68 1C504200 |push offset Test3.??_C@_02MECO@?$CFd?$AA@4k?5at?50x?$CF0>; ASCII "%d"
0040103E |. E8 3D000000 |call Test3.scanfalloc_basee_block_pages ; scanf函数
00401043 |. 83C4 08 |add esp,8 ; 恢复esp
00401046 |. 837D FC 00 |cmp [local.1],0 ; 比较是否为0
0040104A |. 75 02 |jnz short Test3.0040104E ; 为0就执行if语句内容
0040104C |.^ EB DA \jmp short Test3.00401028 ; jmp无条件跳转
0040104E |> 33C0 xor eax,eax ; eax清零
00401050 |. 5F pop edi ; 恢复edi
00401051 |. 5E pop esi ; 恢复esi
00401052 |. 5B pop ebx ; 恢复ebx
00401053 |. 83C4 44 add esp,44 ; 恢复局部变量空间
00401056 |. 3BEC cmp ebp,esp ; 测试堆栈平衡
00401058 |. E8 03010000 call Test3.__chkespleBuffers@4ingsW@4location?5size?3?5?$>; 调用调试信息
0040105D |. 8BE5 mov esp,ebp ; 恢复esp
0040105F |. 5D pop ebp ; 恢复ebp
00401060 \. C3 retn ; 返回 ret add esp,4
Release版本:
00401000 /$ 51 push ecx ; ecx入栈保存
00401001 |. 68 34804000 push Test3.00408034 ; 参数
00401006 |. E8 5C000000 call Test3.00401067 ; printf函数
0040100B |. 8D4424 04 lea eax,dword ptr ss:[esp+4] ; 取变量地址到eax
0040100F |. 50 push eax ; 参数
00401010 |. 68 30804000 push Test3.00408030 ; ASCII "%d"
00401015 |. E8 36000000 call Test3.00401050 ; scanf函数
0040101A |. 8B4424 0C mov eax,dword ptr ss:[esp+C] ; 变量值赋值到eax
0040101E |. 83C4 0C add esp,0C ; 恢复esp
00401021 |. 85C0 test eax,eax ; 测试eax是否为0
00401023 |. 75 24 jnz short Test3.00401049 ; 不满足就跳出if语句
00401025 |> 68 34804000 /push Test3.00408034 ; 参数
0040102A |. E8 38000000 |call Test3.00401067 ; printf函数
0040102F |. 8D4C24 04 |lea ecx,dword ptr ss:[esp+4] ; 取变量地址到ecx
00401033 |. 51 |push ecx ; 参数
00401034 |. 68 30804000 |push Test3.00408030 ; ASCII "%d"
00401039 |. E8 12000000 |call Test3.00401050 ; scanf函数
0040103E |. 8B4424 0C |mov eax,dword ptr ss:[esp+C] ; 变量值赋值到eax
00401042 |. 83C4 0C |add esp,0C ; 恢复esp
00401045 |. 85C0 |test eax,eax ; 测试eax是否为0
00401047 |.^ 74 DC \je short Test3.00401025 ; 满足就跳回执行if语句
00401049 |> 33C0 xor eax,eax ; eax清零
0040104B |. 59 pop ecx ; 恢复ecx
0040104C \. C3 retn ; 返回 ret add esp,4
goto语句框架:
在debug版本中,goto语句等价鱼jmp指令,但在这个Release版本中却没有goto语句的影子,原因在于由于if语句的原因,这里直接忽略了goto语句,而是把goto语句对应的内容直接拷贝到了if语句下!换种情况,也是jmp指令!
___________________________________________________________________________________________________
本节主要对程序的流程化控制逆向分析完毕,各位私下还需要多多逆向联系,对日后大有帮助!好了,基础篇到此为止,中级篇主要讲解数组,指针,函数,以及类和对象...
|
|