【转】《基于VC平台下C++反汇编与逆向分析研究——No.2》
————————————————————————————————————————————————分析环境:WIN7sp1
所用工具:VC++6.0/OllyDBG/IDA
适用人群:有一定计算机基础,熟悉C/C++编程,熟悉X86系列汇编/了解OD/IDA等调试工具使用,对逆向安全有极大兴趣者!
————————————————————————————————————————————————
开篇前言:
数据类型和运算符是任何编程语言的基础,而对于逆向而言亦是,只有牢固的基础才能走得更远...
————————————————————————————————————————————————
正文部分:
本节主要从汇编层面来全面解析基本的数据类型和运算符,如下:
___________________________________________________________________________________________________
本帖隐藏的内容
[*]#include<stdio.h>
[*]int main()
[*]{
[*] int a,b,c,s;
[*] scanf("%d%d%d",&a,&b,&c);
[*] s=a+b+c;
[*] printf("%d",s);
[*] return 0;
[*]}
[*]
复制代码
_______________________________________________________________________________________________________
上面的源码是一个简单的三角形求周边的程序,好了,直接复制到VC++6.0中选择Debug方式编译生成,直接载入OD,来到main函数:
Debug版本:
00401010 >|> \55 push ebp ;保存ebp
00401011|.8BEC mov ebp,esp ;保存esp到ebp
00401013|.83EC 50 sub esp,0x50 ;开辟局部变量空间
00401016|.53 push ebx ;入栈ebx
00401017|.56 push esi ;入栈esi
00401018|.57 push edi ;入栈edi
00401019|.8D7D B0 lea edi, ;设置初始化内存地址
0040101C|.B9 14000000 mov ecx,0x14 ;设置循环次数
00401021|.B8 CCCCCCCC mov eax,0xCCCCCCCC ;赋值eax int 3中断
00401026|.F3:AB rep stos dword ptr es: ;循环复制int 3
00401028|.8D45 F4 lea eax, ;变量c的内存地址放到eax
0040102B|.50 push eax ;入栈c的地址
0040102C|.8D4D F8 lea ecx, ;变量b地址放到eax
0040102F|.51 push ecx ;入栈b的地址
00401030|.8D55 FC lea edx, ;变量a地址放到eax
00401033|.52 push edx ;入栈a的地址
00401034|.68 20504200 push Test2.00425020 ; /format = "%d%d%d"
00401039|.E8 D2000000 call Test2.scanf ; \scanf
0040103E|.83C4 10 add esp,0x10 ;恢复esp的值4个push
00401041|.8B45 FC mov eax, ;变量a的值放入eax
00401044|.0345 F8 add eax, ;变量b的值加上a的和放入eax中
00401047|.0345 F4 add eax, ;变量c的值加上ab的和放入eax
0040104A|.8945 F0 mov ,eax ;把abc的和放入变量s中
0040104D|.8B4D F0 mov ecx, ;变量s的值放入ecx
00401050|.51 push ecx ; /<%d>
00401051|.68 1C504200 push Test2.0042501C ; |format = "%d"
00401056|.E8 35000000 call Test2.printf ; \printf
0040105B|.83C4 08 add esp,0x8 ;恢复esp,2个push
0040105E|.33C0 xor eax,eax ;eax清零
00401060|.5F pop edi ;恢复寄存器edi
00401061|.5E pop esi ;恢复寄存器esi
00401062|.5B pop ebx ;恢复寄存器ebx
00401063|.83C4 50 add esp,0x50 ;恢复局部变量空间
00401066|.3BEC cmp ebp,esp ;测试esp值是否与之前一致
00401068|.E8 03010000 call Test2._chkesp ;如esp值不一致,调用调试信息
0040106D|.8BE5 mov esp,ebp ;恢复esp值
0040106F|.5D pop ebp ;恢复ebp
00401070\.C3 retn ;返回 等于ret加上add esp,4
和之前的Hello World大同小异,相信大家看起来都没问题,我希望大家能记熟这种框架模式,对以后逆向的深入有很大用处,我们再来看看 Release版本的:
Release版本:
00401000/$83EC 0C sub esp,0xC ;开辟局部变量空间
00401003|.8D4424 08 lea eax,dword ptr ss: ;把变量c的地址放入eax
00401007|.8D4C24 04 lea ecx,dword ptr ss: ;把变量b的地址放入ecx
0040100B|.8D5424 00 lea edx,dword ptr ss: ;把变量a的地址放入edx
0040100F|.50 push eax ;入栈c的地址
00401010|.51 push ecx ;入栈b的地址
00401011|.52 push edx ;入栈a的地址
00401012|.68 34804000 push Test2.00408034 ;ASCII "%d%d%d"
00401017|.E8 55000000 call Test2.00401071 ;调用scanf函数
0040101C|.8B4424 10 mov eax,dword ptr ss: ;把a的值放入eax
00401020|.8B4C24 14 mov ecx,dword ptr ss: ;把b的值放入ecx
00401024|.03C1 add eax,ecx ;a+b的和放入eax中
00401026|.8B4C24 18 mov ecx,dword ptr ss: ;把c的值放入ecx
0040102A|.03C1 add eax,ecx ;c的值和ab的和放入eax中
0040102C|.50 push eax ;入栈三数之和
0040102D|.68 30804000 push Test2.00408030 ;ASCII "%d"
00401032|.E8 09000000 call Test2.00401040 ;调用printf函数
00401037|.33C0 xor eax,eax ;eax清零
00401039|.83C4 24 add esp,0x24 ;恢复esp
0040103C\.C3 retn ;返回等于retadd esp,4
大家可能不理解的就是最后的add esp,0x24,OK,先看下入口处的sub esp,0xC,注意次数是十六进制,大家都知道一个push操作,eap就相当于sub esp,4 此处共6个push,所以这里的值等于6个push的值0x18加上sub esp,0xc 的0xC 正好是0x24,有疑问的大家可以试试在源码中多添加一个push操作来看看此处add esp的值!
_______________________________________________________________________________________________________
#include<stdio.h>
int main()
{
short int a=1;
int b=2;
long int c=3;
float d=1.222;
double e=1.333;
long double f=1.234;
char g='a';
char *h;
h=&g;
return 0;
}
_______________________________________________________________________________________________________
上面这段简单的源码中,涉及了很多基本数据类型,如:int整型 ,float单精度浮点型,double双精度浮点型,char字符型,指针类型,本节重要点就是来分析这些基本数据类型在汇编中的表现形式,先看下Debug版本的:
Debug版本:
00401250 >/> \55 push ebp ;保存ebp
00401251|.8BEC mov ebp,esp ;保存esp到ebp
00401253|.83EC 68 sub esp,0x68 ;开辟局部变量空间
00401256|.53 push ebx ;入栈ebx
00401257|.56 push esi ;入栈esi
00401258|.57 push edi ;入栈edi
00401259|.8D7D 98 lea edi, ;设置初始化局部变量地址
0040125C|.B9 1A000000 mov ecx,0x1A ;设置循环次数
00401261|.B8 CCCCCCCC mov eax,0xCCCCCCCC ;赋值eax int 3中断
00401266|.F3:AB rep stos dword ptr es: ;循环复制int3
00401268|.66:C745 FC 01>mov word ptr ss:,0x1 ;赋值1到局部变量short int类型a中,参数:word16位
0040126E|.C745 F8 02000>mov ,0x2 ;赋值2到局部变量int类型b中,参数:dword32位
00401275|.C745 F4 03000>mov ,0x3 ;赋值3到局部变量float类型c中,参数:dword32位
0040127C|.C745 F0 7F6A9>mov ,0x3F9C6A7F ;赋值0x3F9DF3B6到局部变量float类型d中,参数:dword32位
00401283|.C745 E8 8716D>mov ,0xCED91687 ;存放低位
0040128A|.C745 EC F753F>mov ,0x3FF553F7 ;double类型e 存放高位参数:qword 64位
00401291|.C745 E0 5839B>mov ,0xC8B43958 ;存放低位
00401298|.C745 E4 76BEF>mov ,0x3FF3BE76 ;long double类型f 存放高位参数:qword 64位
0040129F|.C645 DC 61 mov byte ptr ss:,0x61 ;char类型g Ascll码的a对应0x61参数:byte 8位
004012A3|.8D45 DC lea eax, ;获取字符变量地址到eax
004012A6|.8945 D8 mov ,eax ;把eax放入指针中h中
004012A9|.33C0 xor eax,eax ;eax清零
004012AB|.5F pop edi ;出栈edi
004012AC|.5E pop esi ;出栈esi
004012AD|.5B pop ebx ;出栈ebx
004012AE|.8BE5 mov esp,ebp ;恢复esp
004012B0 >|.5D pop ebp ;恢复ebp
004012B1\.C3 retn ;返回 等于ret add esp,4
分析前,我们先普及一下计算机微计量单位,位bit 即10101010 每个数为1位,字节byte 即大家看到的文件大小字节,一个字节等于8位,字word1个字等于2个字节即16位双字Dword1个双字等于2个字,即32位eax寄存器就是32位,4字qword同理就是64位!当然还有更大的,我们这里不做描述!
short int 类型为16位故:mov word ptr ss:,0x1 int类型为32位,故:mov ,0x2 默认就是32位,double为64位,就会使用2个32位来存放,即一个存放高位,一个低位!例如这句:double e=1.333; 1.333在内存中是以IEEE编码来存储,1.333对应的IEEE是3FF553F7CED91687,故会出现这两句汇编指令:mov ,0xCED91687 mov ,0x3FF553F7,Ascll码的0x61对应的就是小写字母a,好了,看下Release版
Release版本:
00401000/$33C0 xor eax,eax ;eax清零
00401002\.C3 retn ;返回等于retadd esp,4
这些变量未参数实际使用,Release会自动优化掉!
_______________________________________________________________________________________________________
[*]#include < stdio.h >
[*]int main()
[*]{
[*] int a = 2,
[*] b = 4,
[*] c;
[*] c = a + b;
[*] c = a * b;
[*] c = a / b;
[*] c = a % b;
[*] c = a && b;
[*] c = a || b;
[*] c = !b;
[*] c++;
[*] c--;
[*] return 0;
[*]}
[*]
复制代码
______________________________________________________________________________________________________
Debug版本:
00410680 >/> \55 push ebp ;ebp入栈保存
00410681|.8BEC mov ebp,esp ;保存esp
00410683|.83EC 54 sub esp,0x54 ;开辟局部变量空间
00410686|.53 push ebx ;入栈ebx
00410687|.56 push esi ;入栈esi
00410688|.57 push edi ;入栈edi
00410689|.8D7D AC lea edi, ;设置初始化局部变量地址
0041068C|.B9 15000000 mov ecx,0x15 ;循环次数
00410691|.B8 CCCCCCCC mov eax,0xCCCCCCCC ;赋值eax int 中断
00410696|.F3:AB rep stos dword ptr es: ;循环复制int 3 中断
00410698|.C745 FC 05000>mov ,0x5 ;赋值2到变量a
0041069F|.C745 F8 02000>mov ,0x2 ;赋值4到变量b
004106A6|.8B45 FC mov eax, ;赋值a的值到eax
004106A9|.0345 F8 add eax, ;b的值与a的值相加放入eax
004106AC|.8945 F4 mov ,eax ;ab的和放入变量c
004106AF|.8B4D FC mov ecx, ;赋值a的值到ecx
004106B2|.0FAF4D F8 imul ecx, ;变量b与变量a的乘积放入ecx
004106B6|.894D F4 mov ,ecx ;ab的积值放入变量c
004106B9|.8B45 FC mov eax, ;赋值a的值到eax
004106BC|.99 cdq ;双字32位扩展到64位,eax的高位放入edx中
004106BD|.F77D F8 idiv ;除法运算 idiv无符号除法
004106C0|.8945 F4 mov ,eax ;ab的商放入变量c
004106C3|.8B45 FC mov eax, ;赋值a的值到eax
004106C6|.99 cdq ;双字32位扩展到64位,eax的高位放入edx中
004106C7|.F77D F8 idiv ;除法运算 idiv无符号除法
004106CA|.8955 F4 mov ,edx ;ab的余数放入变量c
004106CD|.837D FC 00 cmp ,0x0 ;与运算,测试变量a是否为0,如为0就跳转
004106D1|.74 0F je short Test2.004106E2 ;a值不为0,未发生跳转
004106D3|.837D F8 00 cmp ,0x0 ;与运算,测试变量b是否为0,如为0就跳转
004106D7|.74 09 je short Test2.004106E2 ;b值不为0,未发生跳转
004106D9|.C745 F0 01000>mov ,0x1 ;与运算为真
004106E0|.EB 07 jmp short Test2.004106E9 ;无条件跳转
004106E2|>C745 F0 00000>mov ,0x0
004106E9|>8B55 F0 mov edx, ;与运算值放入edx
004106EC|.8955 F4 mov ,edx ;ab的与运算值放入变量c
004106EF|.837D FC 00 cmp ,0x0 ;或运算,测试变量a是否为0,如不等于0就跳转
004106F3|.75 0F jnz short Test2.00410704 ;a值不为0,发生跳转
004106F5|.837D F8 00 cmp ,0x0
004106F9|.75 09 jnz short Test2.00410704
004106FB|.C745 EC 00000>mov ,0x0
00410702|.EB 07 jmp short Test2.0041070B
00410704|>C745 EC 01000>mov ,0x1 ;ab或运算为真
0041070B|>8B45 EC mov eax, ;或运算值放入edx
0041070E|.8945 F4 mov ,eax ;ab的或运算值放入变量c
00410711|.33C9 xor ecx,ecx ;ecx清零
00410713|.837D F8 00 cmp ,0x0 ;求反运算,测试变量b是否为0
00410717|.0F94C1 sete cl ;依据zf标志位的值设置cl的值
0041071A|.894D F4 mov ,ecx ;b逻辑取反的值放入变量c
0041071D|.8B55 F4 mov edx, ;变量c的值放入edx
00410720|.83C2 01 add edx,0x1 ;edx自加1
00410723|.8955 F4 mov ,edx ;edx的值放入变量c
00410726|.8B45 F4 mov eax, ;变量c的值放入eax
00410729|.83E8 01 sub eax,0x1 ;eax自减1
0041072C|.8945 F4 mov ,eax ;eax的值放入变量c
0041072F|.33C0 xor eax,eax ;eax清零
00410731|?5F pop edi ;恢复edi
00410732|.5E pop esi ;恢复esi
00410733|?5B pop ebx ;恢复ebx
00410734|?8BE5 mov esp,ebp ;恢复esp的值
00410736|?5D pop ebp ;恢复ebp
00410737|?C3 retn ;返回等于ret add esp,4
上述每条汇编指令的注释我写的都很详细,大家可以对照源码来看,注意cdq指令 ,重点看下理解乘除法运算,以及逻辑运算!Release版本同上。
Release版本:
00401000/$33C0 xor eax,eax
00401002\.C3 retn
这些变量未参数实际使用,Release会自动优化掉!
_______________________________________________________________________________________________________
本节中,肯定没有全面涉及数据类型和运算符,这里只做为一种分析的方法,各位私下多练习下这些基础,吃透吃熟,对以深入后尤关重要!至于逻辑判断,下节我会详细介绍。
页:
[1]