程序进入到helloworld的入口之后发生了什么?

程序从入口点 401280 开始执行代码

[esp 28ff8c ebp 28ff94]

push ebp [esp=28ff88 ebp=28ff94 在栈中保存了调用子程序前的栈基址 28ff94]

mov ebp,esp [把现在的栈顶 28ff88 设置为新的栈基址,方便通过ebp来调用变量参数,并且可以通过 mov esp,ebp 平衡栈.]

sub esp,8 [栈指针向上(较低的地址)移动8字节的内容空间]

mov dword ptr ss:[esp],1 [向上一步的空间写入8字节长度的数字1]

call dword ptr ds:[&msvcrt.__set_app_type] [获得数据段地址的值,并call到这个地址(子函数)call = push eip jmp]

Kernel32.BaseThreadInitThunk

mov edi,edi [没有什么意义,可以用来 inline hook,以及作为热补丁用的,配合上面的5个nop]

push ebp

mov ebp,esp [保存调用函数之前的栈基址,设置本函数的栈基址 esp-4]

push dword ptr ss:[ebp+8] [call push用了4字节, push ebp用了4字节,所以要到call前压栈的参数的话,需要加8字节,将得到的参数1压栈]

call msvcrt.75f31585 [应该是调到msvcrt中的一个函数]

msvcrt.75f31585

mov edi,edi [不用说了吧]

push ebp

mov ebp,esp [这两句就算是标准代码了,设置当前栈基址]

push 0 [入参]

call dword ptr ds:[<&API-MS-Win-Core-L...>] [从数据段获取call地址 KernelBa.GetModuleHandleW(获取模块句柄)]

mov edi,edi

push ebp

mov ebp,esp

cmp dword ptr ss:[ebp+8],0 [参数为0,0表示获取的是当前模块]

jnz short KernelBa.75b710ad [判断参数是否为0,如果不是0就短跳到执行 75b70e41 函数,这里的参数为0,所以不用跳]

mov eax,dword ptr fs:[18] [得到的是线程的关键信息 7efdd000]

mov eax,dword ptr ds:[eax+30] [得到 7efdd030 地址的值 00 e0 fd 7e(7efde000)]

mov eax,dword ptr ds:[eax+8] [得到 7efde008 的值 00 00 04 00(400000) 程序的入口点]

jmp short KernelBa.75b710c4 [无条件短跳]

[这里] pop ebp [恢复函数开始时的ebp]

retn 4 [先 pop ip返回到了call dword ptr ds:[<&API-MS-Win-Core-L...>] 的下一行,然后 sp 加对应的值,因为之前 push 了一个参数(pop=esp+4),所以这里 sp 要加 4,使之栈平衡]

test eax,eax [and , 不覆盖 eax, 影响]

je msvcrt.75f41dd1 [判断返回结果是否为零,如果为零跳到这里,表示没有获取到模块]

push eax [将结果压栈 400000]

call msvcrt.75f315ca [执行子函数,参数400000]

msvcrt.75f315ca

push c [第2个参数压栈]

push msvcrt.75f31628 [第1个参数,好像是call之前的模块基址?]

call msvcrt.75f29836 [执行子函数]

msvcrt.75f29836

push msvcrt.75f48cd5 [?]

push dword ptr fs:[0] [指向当前线程的结构化异常处理结构(SEH) 28ffc4(ds: ff ff ff ff)]

mov eax,dword ptr ss:[esp+10] [栈指针移动10字节后的地址的值存入 eax(c)]

mov dword prt ss:[esp+10],ebp [将ebp覆盖第2个参数c (28ff6c)]

lea ebp,dword ptr ss:[esp+10] [将存有ebp内容的地址保存到ebp]

sub esp,eax [段指针减少12字节]

push ebx [第4个参数 7efde000 压栈]

push esi [第3个参数 0 压栈]

push edi [第2个参数 0 压栈]

mov eax,dword ptr ds:[75fc060c] [第1个参数应该是调用系统函数,地址是 af9a2329]

xor dword ptr ss:[ebp-4],eax [75f31628 xor af9a2329 (ebp-4=da693501)]

xor eax,ebp [af9a2329 xor 0028ff60 (eax=afb2dc49)]

push eax [压栈]

mov dword ptr ss:[ebp-18],esp [将栈顶指针保存到这里(24字节)]

push dword ptr ss:[ebp-8] [返回地址 75f315d6 压栈]

mov eax,dword ptr ss:[ebp-4] [da693501 -> eax,就是第1个xor算出来的值]

mov dword ptr ss:[ebp-4],-2 [将-2(fffffffe)放到这里]

mov dword ptr ss:[ebp-8],eax [将 75f315d6 变成 da693501 ,因为这个地方的值已经重新入栈]

lea eax,dword ptr ss:[ebp-10] [得到栈地址 28ff50,下面就是 75f48cd5 ,子函数刚开始时压栈的值]

mov dword prt fs:[0],eax [当前线程的结构化异常处理结构(SEH) 28ffc4 变为 28ff50 在地址 7fedd000]

retn [pop 75f315d6 ip 返回]

xor eax,eax [清零]

mov ecx,dword ptr ss:[ebp+8] [400000 -> ecx]

test ecx,ecx [判断是否为0]

je short msvcrt.75f31619 [为0就跳]

cmp ecx,-1 [判断是否是-1]

je short msvcrt.75f31619 [为-1还是跳]

and dword ptr ss:[ebp-4],eax [fffffffe and 0 结果为 0]

mov edx,5a4d [DOS头]

cmp word ptr ds:[ecx],dx [判断文件开头是否是 4d5a ,PE文件判断]

jnz short msvcrt.75f31612 [如果不为零则跳转异常处理,这里是正常]

mov edx,dword ptr ds:[ecx+3c] [80 -> edx PE文件相对文件的偏移]

test edx,edx [判断是否是0]

jl short msvcrt.75f31612 [这里表示如果结果是负数就跳异常处理]

cmp edx,10000000 [80-10000000]

jnb short msvcrt.75f31612 [这里表示如果结果不小于就跳异常处理]

lea eax,dword ptr ds:[ecx+edx] [获得PE头所在地址 400080 -> eax]

mov dword ptr ss:[ebp-1c],eax [将400080放到栈中]

cmp dword ptr ds:[eax],4550 [判断是否是字符串 PE]

jnz msvcrt.75f68095 [如果不是PE则跳转处理]

mov dword ptr ss:[ebp-4],-2 [应该是参数?(fffffffe)]

call msvcrt.75f2987b [子函数跳转]

msvcrt.75f2987b

mov ecx,dword ptr ss:[ebp-10] [第4个参数 异常处理结构地址的指针]

mov dword ptr fs:[0],ecx [不能直接段寄存器传值,所以使用ecx]

pop ecx [异常处理结构地址的指针]

pop edi [第4个参数出栈]

pop edi [第3个参数出栈 0]

pop esi [第2个参数出栈 0]

pop ebx [第1个参数出栈 7efde000]

mov esp,ebp [将栈偏移设置为 KernelBa.GetModuleHandleW(获取模块句柄) 时候的栈顶,为返回做准备]

pop ebp [恢复 ebp]

push ecx [将结果压栈 75f3161e,因为之前出栈了]

retn [然后跳到这个地址,这个地址就是 call msvcrt.75f2987b 下一行代码的地址]

retn 4 [返回到msvcrt.75f315ca]

test eax,eax [判断返回值是否是0 PE文件判断函数的返回值]

je msvcrt.75f41dd1 [如果是0则调到这里,提示非PE文件,应该吧]

movzx eax,word ptr ds:[eax+5c] [40013c的值 0300 扩展为 00000003 并赋值给 eax (3 console)]

cmp ax,2 [3-2=1]

je msvcrt.75f300f1 [如果是 winGUI系统,则跳]

cmp ax,3 [是否是命令行程序]

jnz msvcrt.75f41dd1 [如果也不是命令行系统,则跳]

xor eax,eax [清零]

inc eax [eax+1]

pop ebp [回复栈基址]

retn [返回到 msvcrt.75f31585]

pop ecx [ecx=1]

mov dword ptr ds:[75fc0030],eax [1赋值到数据段]

pop ebp [28ff88 程序 1280 push ebp 后 mov ebp,esp 后的值]

retn [返回程序空间,执行第6行代码]

call 401150 [程序内调用子函数]

401150

push ebp

mov ebp,esp

push ebx [7efde000 线程关键信息]

sub esp,24 [36字节空间]

mov dword ptr ss:[esp],未命名1.00401000 [在栈顶放入401000]

call [设置异常捕获函数 4018e4]

004018e4

jmp dword ptr ds:[&KERNEL32.SetUnhandledExceptionFilter] [跳转到这个函数的实现过程]

KERNEL32.SetUnhandledExceptionFilter

mov edi,edi

push ebp

mov ebp,esp [设置新的栈顶,函数进入子函数时push ebp后的 esp]

sub esp,220 [为什么移动这么大的距离?]

push ebx [7efde000 线程关键信息]

push esi [0]

mov esi,dword ptr ss:[ebp+8] [401000 程序入口点]

test esi,esi [esi 是否是0 程序入口地址]

je Kernel32.75d089be [如果是0,则跳到异常处理?]

lea eax,dword ptr ss:[ebp-220] [获得移动前的栈偏移地址 28fd28]

push eax [压栈 28fd28 栈顶]

push esi [压栈 程序入口]

call Kernel32.75d0888e [子程序处理]

mov edi,edi

push ebp

mov ebp,esp [设置新的栈顶]

sub esp,2c [移动2c距离]

push esi [401000压栈]

push edi [0压栈]

push 1c [1c(28)压栈]

lea eax,dword ptr ss:[ebp-2c] [开始的esp偏移,用于接收VirtualQueryEx函数返回的信息,虽然返回的机构大小是1c]

push eax [28fce4栈偏移]

push dword ptr ss:[ebp+8] [ebp+ 获得参数1(401000)到局部变量]

call 75D0444F= [查询地址空间中内存地址的信息]

查询地址空间中内存地址的信息 kernel32的导入函数

jmp dword ptr ds:[&API-MS-Win-Core-Memory-L1-1-0.VirtualQuery] [KernelBa.VirtualQuery]

mov edi,edi

push ebp

mov ebp,esp [设置新的栈顶]

push dword ptr ss:[ebp+10] [1c MEMORY_BASIC_INFORMATION结构的大小]

push dword ptr ss:[ebp+c] [28fce4栈偏移 指向MEMORY_BASIC_INFORMATION结构的指针]

push dword ptr ss:[ebp+8] [401000 查询内存的地址]

push -1 [参数1为-1,共4个参数 进程句柄]

call KernelBa.VirtualQueryEx [查询地址空间中内存地址的信息]

KernelBa.VirtualQueryEx

mov edi,edi

push ebp

mov ebp,esp

lea eax,dword ptr ss:[ebp+14] [获得第4个参数的地址 28fcc4 给 eax 保存有结构的大小]

push eax [地址压栈 存储该函数处理 返回 的信息的长度的ULONG的地址]

push dword ptr ss:[ebp+14] [值压栈,局部变量 结构体大小 Buffer的最大长度]

push dword ptr ss:[ebp+10] [用于存储获取到的内存信息的结构地址]

push 0 [查询内存信息的类别]

push dword ptr ss:[ebp+c] [查询内存的地址 401000]

push dword ptr ss:[ebp+8] [进程句柄 -1]

call dword ptr ds:[<&ntdll.NtQueryVirtualMemory>] [查询指定进程的某个虚拟地址控件所在的内存对象的一些信息]

ntdll.NtQueryVirtualMemory

mov eax,20

xor ecx,ecx [清零]

lea edx,dword ptr ss:[esp+4] [获得返回地址]

call dword ptr fs:[c0] [线程 的 TEB结构 WOW32Reserved 7efdd0c0 -> 741f2320]

jmp far 0033:741f271e [长跳转,不理解]

77ae01c4 RtlUserThreadStart

mov dwrod ptr ss:[esp+4],eax [将程序入口地址 401280 写入]

...没了,程序显示了 helloworld ,处于等待哦 getchar() 但是我在程序输入任意字符后就跳到

77aefcb2 了,然后就显示已终止 OD显示 ERR0R_FILE_NOT_FOUND LASTERR 2

调用链:

1280->

128d call -> 75f32804

75f3280c call -> msvcrt.75f31585

75f3158c call -> KernelBa.GetModuleHandleW

75f31592 -> msvcrt.75f315ca

75f315d2 call -> msvcrt.75f29836

75f31619 call -> msvcrt.75f2987b

retn ->msvct.75f3161e

75F315C4 -> 75F32811

1293 call -> 1150

18e4 jmp -> KERNEL32.SetUnhandledExceptionFilter

75d087e9 call -> Kernel32.75d0888e

call 75D0444F -> KernelBa.VirtualQuery

call KernelBa.VirtualQueryEx

call ntdll.NtQueryVirtualMemory

call -> ntdll.741f2320

推荐阅读更多精彩内容