仿真平台:VS2008
使用单步调试的方法设置断点,可知程序先执行
int gi;
然后再进入主函数执行。
查看
gi=12;
对应的汇编
下一节讲讨论一下Windows下的内联汇编
现在,再看一个程序
关于函数的压栈出栈以及对应的汇编代码,会在后面的文章里面提到,到时在回头看这些汇编代码,就很简单了~
一:全局变量引发的故事
#include "stdafx.h"
int gi;
int _tmain(int argc, _TCHAR* argv[])
{
gi=12;
return 0;
}
查看其对应的汇编#include "stdafx.h"
int gi;
int _tmain(int argc, _TCHAR* argv[])
{
00411370 55 push ebp
00411371 8B EC mov ebp,esp
00411373 81 EC C0 00 00 00 sub esp,0C0h
00411379 53 push ebx
0041137A 56 push esi
0041137B 57 push edi
0041137C 8D BD 40 FF FF FF lea edi,[ebp+FFFFFF40h]
00411382 B9 30 00 00 00 mov ecx,30h
00411387 B8 CC CC CC CC mov eax,0CCCCCCCCh
0041138C F3 AB rep stos dword ptr es:[edi]
gi=12;
0041138E C7 05 40 71 41 00 0C 00 00 00 mov dword ptr ds:[00417140h],0Ch
return 0;
00411398 33 C0 xor eax,eax
}
使用单步调试的方法设置断点,可知程序先执行
int gi;
然后再进入主函数执行。
查看
gi=12;
对应的汇编
0041138E C7 05 40 71 41 00 0C 00 00 00 mov dword ptr ds:[00417140h],0Ch
一般:MOV DS:[地址值],存储值
看对应的语句可知 mov 对应机器码 c7 05 地址对应为00417140h 存储值为0ch (小端机存储方式)
注: 小端机:整数逻辑上最低字节放在内存的最低地址,次低字节放在次低地址,即低地址放低字节,高地址放高字节
大端机与之相反
为了证明地址00417040h的确被赋值了12,我们使用vs的内存查看功能
我们发现,的确12这个数是被赋值到了地址00417140h
既然我们发现指令只不过是一些字节的组合,那么我们自己来分配一段内存,将mov指令的机器码填入,在把EIP的值指向该地址
看下面一段程序:
int i, gi;
void * address;
void main()
{
_asm {
mov address, offset _lb1;
jmp address;
}
i = 2;
_lb1:
gi = 12;
}
其中,标签_lb1代表着一个地址,供EIP跳转下一节讲讨论一下Windows下的内联汇编
现在,再看一个程序
#include<stdio.h>
#include<stdlib.h>
int gi;
int address;
void *buildCode()
{
char*code=(char*)malloc(16);//mov+jmp两个指令一共16个字节
char*pMov=code;//mov指令的地址开头
char*pJmp=code+10;//jmp指令的地址开头
char*pAddress;
pMov[0]=0xc7;
pMov[1]=0x05;//mov的二进制码为c705
pAddress=pMov+2;//mov gi的地址,数据 此处为gi的地址指针
*((int*)pAddress)=(int)&gi;//将gi的地址赋值到指定位置
*((int*)(pAddress+4))=18;//将数据赋值到指定位置
pJmp[0]=0xff;
pJmp[1]=0x25;//jmp的二进制代码为ff25
*((int*)(&pJmp[2]))=(int)&address;//jmp指令将要跳转的地址
return code;//返回首地址
}
void main()
{
void*code=buildCode();
_asm{
mov address,offset _lb1
}
gi=12;
printf("gi=%d\n",gi);
_asm jmp code//执行buildCode()
gi=13;
_lb1:
printf("g1=%d\n",gi);
getchar();
}
此代码的执行结果为:
看懂了吗?呵呵,还是比较简单的
我们再来看看对应的汇编代码
buildCode()函数对应的代码
#include<stdio.h>
#include<stdlib.h>
int gi;
int address;
void *buildCode()
{
004113C0 55 push ebp
004113C1 8B EC mov ebp,esp
004113C3 81 EC F0 00 00 00 sub esp,0F0h
004113C9 53 push ebx
004113CA 56 push esi
004113CB 57 push edi
004113CC 8D BD 10 FF FF FF lea edi,[ebp+FFFFFF10h]
004113D2 B9 3C 00 00 00 mov ecx,3Ch
004113D7 B8 CC CC CC CC mov eax,0CCCCCCCCh
004113DC F3 AB rep stos dword ptr es:[edi]
char*code=(char*)malloc(16);//mov+jmp两个指令一共16个字节
004113DE 8B F4 mov esi,esp
004113E0 6A 10 push 10h
004113E2 FF 15 CC 82 41 00 call dword ptr ds:[004182CCh]
004113E8 83 C4 04 add esp,4
004113EB 3B F4 cmp esi,esp
004113ED E8 53 FD FF FF call 00411145
004113F2 89 45 F8 mov dword ptr [ebp-8],eax
char*pMov=code;//mov指令的地址开头
004113F5 8B 45 F8 mov eax,dword ptr [ebp-8]
004113F8 89 45 EC mov dword ptr [ebp-14h],eax
char*pJmp=code+10;//jmp指令的地址开头
004113FB 8B 45 F8 mov eax,dword ptr [ebp-8]
004113FE 83 C0 0A add eax,0Ah
00411401 89 45 E0 mov dword ptr [ebp-20h],eax
char*pAddress;
pMov[0]=0xc7;
00411404 8B 45 EC mov eax,dword ptr [ebp-14h]
00411407 C6 00 C7 mov byte ptr [eax],0C7h
pMov[1]=0x05;//mov的二进制码为c705
0041140A 8B 45 EC mov eax,dword ptr [ebp-14h]
0041140D C6 40 01 05 mov byte ptr [eax+1],5
pAddress=pMov+2;//mov gi的地址,数据 此处为gi的地址指针
00411411 8B 45 EC mov eax,dword ptr [ebp-14h]
00411414 83 C0 02 add eax,2
00411417 89 45 D4 mov dword ptr [ebp-2Ch],eax
*((int*)pAddress)=(int)&gi;//将gi的地址赋值到指定位置
0041141A 8B 45 D4 mov eax,dword ptr [ebp-2Ch]
0041141D C7 00 6C 71 41 00 mov dword ptr [eax],41716Ch
*((int*)(pAddress+4))=18;//将数据赋值到指定位置
00411423 8B 45 D4 mov eax,dword ptr [ebp-2Ch]
00411426 C7 40 04 12 00 00 00 mov dword ptr [eax+4],12h
pJmp[0]=0xff;
0041142D 8B 45 E0 mov eax,dword ptr [ebp-20h]
00411430 C6 00 FF mov byte ptr [eax],0FFh
pJmp[1]=0x25;//jmp的二进制代码为ff25
00411433 8B 45 E0 mov eax,dword ptr [ebp-20h]
00411436 C6 40 01 25 mov byte ptr [eax+1],25h
*((int*)(&pJmp[2]))=(int)&address;//jmp指令将要跳转的地址
0041143A 8B 45 E0 mov eax,dword ptr [ebp-20h]
0041143D C7 40 02 68 71 41 00 mov dword ptr [eax+2],417168h
return code;//返回首地址
00411444 8B 45 F8 mov eax,dword ptr [ebp-8]
}
00411447 5F pop edi
00411448 5E pop esi
00411449 5B pop ebx
0041144A 81 C4 F0 00 00 00 add esp,0F0h
00411450 3B EC cmp ebp,esp
00411452 E8 EE FC FF FF call 00411145
00411457 8B E5 mov esp,ebp
00411459 5D pop ebp
0041145A C3 ret
主函数对应的代码
void main()
{
004114C0 55 push ebp
004114C1 8B EC mov ebp,esp
004114C3 81 EC CC 00 00 00 sub esp,0CCh
004114C9 53 push ebx
004114CA 56 push esi
004114CB 57 push edi
004114CC 8D BD 34 FF FF FF lea edi,[ebp+FFFFFF34h]
004114D2 B9 33 00 00 00 mov ecx,33h
004114D7 B8 CC CC CC CC mov eax,0CCCCCCCCh
004114DC F3 AB rep stos dword ptr es:[edi]
void*code=buildCode();
004114DE E8 A9 FB FF FF call 0041108C
004114E3 89 45 F8 mov dword ptr [ebp-8],eax
_asm{
mov address,offset _lb1
004114E6 C7 05 68 71 41 00 24 15 41 00 mov dword ptr ds:[00417168h],411524h
}
gi=12;
004114F0 C7 05 6C 71 41 00 0C 00 00 00 mov dword ptr ds:[0041716Ch],0Ch
printf("gi=%d\n",gi);
004114FA 8B F4 mov esi,esp
004114FC A1 6C 71 41 00 mov eax,dword ptr ds:[0041716Ch]
00411501 50 push eax
00411502 68 44 57 41 00 push 415744h
00411507 FF 15 C0 82 41 00 call dword ptr ds:[004182C0h]
0041150D 83 C4 08 add esp,8
00411510 3B F4 cmp esi,esp
00411512 E8 2E FC FF FF call 00411145
_asm jmp code//执行buildCode()
00411517 FF 65 F8 jmp dword ptr [ebp-8]
gi=13;
0041151A C7 05 6C 71 41 00 0D 00 00 00 mov dword ptr ds:[0041716Ch],0Dh
_lb1:
printf("g1=%d\n",gi);
00411524 8B F4 mov esi,esp
00411526 A1 6C 71 41 00 mov eax,dword ptr ds:[0041716Ch]
0041152B 50 push eax
0041152C 68 3C 57 41 00 push 41573Ch
00411531 FF 15 C0 82 41 00 call dword ptr ds:[004182C0h]
00411537 83 C4 08 add esp,8
0041153A 3B F4 cmp esi,esp
0041153C E8 04 FC FF FF call 00411145
getchar();
00411541 8B F4 mov esi,esp
00411543 FF 15 C4 82 41 00 call dword ptr ds:[004182C4h]
00411549 3B F4 cmp esi,esp
0041154B E8 F5 FB FF FF call 00411145
}
关于函数的压栈出栈以及对应的汇编代码,会在后面的文章里面提到,到时在回头看这些汇编代码,就很简单了~