研究win程序逆向的初衷是为了更好的理解计算机系统的底层实现,理解计算机硬件架构与操作系统之间的联系,实现对软件系统的原子极优化。要想把win程序逆向研究透,可能并非一朝一夕。逆向需要你有多方面的知识,大的方面有组成原理,操作系统,编译原理等。往细了说,你还需要懂C/C++的语言实现与内存模型,汇编及其内存操作模型,win系统可执行程序的PE文件格式,还有msvc的工具链等。总结来说,就是你需要“懂原理,并熟练运用工具!”另外,说明一点:2021年了,现在的应用层技术层出不穷,逆向工程技术也远不止解析PE这些二进制理论了。像JAVA程序,在os层级又架了一层JVM,那你就要研究JVM和JAVA文件格式。还有,C#等CLR类型的程序,技术的多样性对逆向工程师提出了更高的要求。由于逆向的内容较多,我会分阶段来写这些内容。
相比各种图形工具,个人更喜欢使用原生工具链中的工具。所以这里就更大家说明下msvc提供的命令行工具(msvc大家使用visual studio下载就行):
1. cl windows系统原生的C/C++编译器
2. dumpbin 逆向分析的主角,反汇编命令行工具
3. undname 符号解析工具
使用C/C++开发的程序一般分为:1. exe可执行程序;2. dll动态链接库 这两个方面。
由于是应用层的程序,反汇编之后得到的原子级别的代码有几十万几百万行。如果只是单纯的去逐行看,逐行分析,那工作量将大的惊人!所以还是得明确一下目标,使用高级语言中“模块化”开发的思维进行模块化逆向。模块化逆向,如果是C,可以按函数为单位进行分析;如果是C++,可以以class为单位,再细一点也是以函数为单位,进行分析。基于每个函数模块,将汇编逆向解析成C/C++语言代码,这样就明确了每个函数的具体功能。
这里讲一下PE格式文件中的一些数据段(或者说section节):
1. .text 代码段,存放所有的指令代码,一般位于虚拟内存空间的高地址空间。
2. .data 可读写数据段,存放已初始化的全局变量,常量等。
3. .rdata only read数据段,同.data类似,但权限不同,不可写。
4. .bss 未初始化全局数据,PE文件不占多少空间,只记录大小,实际空间会在内存中展开。
5. .idata 导入函数段,存放外部函数地址。
6. .edata 导出函数段。
7. .rsrc 资源数据段。
另外说一点程序在内存中的行为:栈是从高地址往低地址空间扩张的,堆相反。
反编译一般命令:
获取符号表信息:dumpbin -SYMBOLS xxx.exe >xxx.as
获取全部:dumpbin -ALL xxx.exe >xxx.as
获取导出函数等:dumpbin -EXPORTS xxx.exe >xxx.as
反编译代码:dumpbin /DISASM xxx.exe >xxx.as
当然,导出函数和符号等信息还是可以进一步解析的,如果不解析你看到的一般是这样:
使用undname工具进行解析,命令为:
undname ??0gxSerialComm@@QAE@ABVO@@Z
undname可以传入文件进行批量解析,效率会高一些。解析出来的符号,一般是这样:
现在来看,就明了很多!接下来,根据解析出来的已有的信息进行一些整理,像上面这个,我们可以划分出class类,然后划分public/private等函数。根据RVA暂时分配地址到.text代码段定位出函数的汇编代码,接下来把它们翻译成C/C++语言。
这里再说明一些关于win C/C++编译器的函数调用约定:
1. __stdcall WINAPI调用约定,将函数的参数从右向左压栈传递,返回前由被调用者清理堆栈
2. __fastcall 快速调用,参数顺序:ecx,edx,栈(右->左),被调方清理
3. __cdecl 栈(右->左),被调方清理,可变参数vararg由此实现
4. __thiscall C++中的this指针调用方式,this放在ecx中
以上是windows系统常用的函数调用约定。每种调用约定都有各自的对函数签名进行命名的方式,就是上面符号表中的函数名样式。函数的调用约定在接下来的翻译汇编代码阶段会用到。
暂时先写到这里!