一、c是怎么变成汇编的
1、裸函数是编译器不管的
⑴写一个空函数(里面什么都不写),f7f5打开反汇编
f11打开jmp
什么都没写里面还是有一大堆(是编译器和连接器做的)
⑵裸函数
f7f5查看反汇编找到调用的函数0040D708
f11打开
再f11打开,里面一行汇编代码都没有
⑶
①空函数
f7f5
运行后不进反汇编,先f10一行一行运行
运行至最后一行没有出错,证明空函数
②裸函数
f7f5
f10,出错
③原因
1️⃣将空函数打开反汇编看
call将0040D72D压入堆栈和把eip地址改到call后面0040101e
f11后再f11
一直f10到0040D70e,return后回到③的第一张图
可以调用空函数是因为里面生成了return,可以返回
2️⃣打开裸函数
也有call(作用和空函数里面的一样)
f11再f11,只有int3(相当于断点,程序执行到这就会停下来)
3️⃣如果想让裸函数运行,可以再c里面写汇编
__asm{} (最好是两个_)
f7f5再f11f11 有ret可以返回,就能执行
2、在裸函数里面实现一个加法
asm里面内容
二、调用约定:参数的传递顺序
1、_cdecl:c/c++默认的调用约定(写不写都相当于写了)
从后往前压,外平栈
参数从后往前压栈,谁调用00400D79C这个函数谁负责平衡堆栈,add esp,8是在外面平衡堆栈(是在主函数叫外平栈)
2、_ stdcall:
也是从后往前压,内平栈
f10运行到00400D79C,f11进入函数再f11,再一行一行的运行到ret
一般只有一个ret,而这一行是ret 8,就相当于是add esp,8
这种是在子函数里面,叫内平栈
3、_fastcall:用两个寄存器,传递速度快(不超过两个参数 ),还是从右到左
之前add是因为的是push了两个数 占用了8个字节,esp的值需要往上压8个字节
而mov是用两个寄存器传的值
f11打开进去看里面只有一个ret
传2个参数改成传4个
他前两行也是用的push,
进入函数,是ret 8
(逆向中不能根据ret8就判断是两个参数,可能是cdecl或stdcall,也可能是fastcall中4个参数有两个寄存器)
例外:如何区分一个call有几个参数
①根据函数调用:
错误判断:看有没有add,看call上面的值
有可能push 4是在plus4里面没用是在pop里面用的
有可能0040D836以上的值不一定是给此行的call用的可能是给下一个的
正确判断:一行一行分析再结合参数调用和堆栈平衡的代码
三、callingconventionr入口
1、修改入口例:随便写一个空函数,f7f5打开,旁边有一个call stack
第一个main是我们的程序,最后一行的KERNEL32那一行调用了mainCRTStartup这个方法而这个又调用了我们写的main程序
自己写的代码入(即callstack中第一个main)口不是真正程序入口
真正程序的入口是mainCRTStartup
------------------------------------------分割线---------------------------------------------
改入口点
打开setting,选择link,选output,entry-point symbol改成test
改了之后运行到void tset就不走了
2、找入口方法:找调用的函数后面有三个参数的
找到一个调用函数
push了三个参数,后面是add esp,0c;如果是两个参数平衡堆栈是add8,而0c是三个参数
进add里面观察:
(一)0:保存栈底
(二)1-3:提升堆栈
(三)6-8:保留控制台环境
(四)9-6:往缓冲区写内容
(五)8-8:有一个函数里面有5个参数三个push两个mov,调用函数是fastcall
①call进去
a、进第一个函数4DFFFFFF看传了多少个参数,打开看到ret0c,是传了三个参数也是内平衡,是stdingcall
b、24FFFFFF:前面有两个push和add8且是外平衡,是cdecl
c、11FFFFFF:与b中的地址一样