反编译记事本(2) 预处理源代码
根据前面文章所说的, 创建记事本项目, 导入资源后, 下一步应该是解决编译错误了. 但是, 我现在发现先预处理一下代码是很有必要的.
前面反编译扫雷时, 导出的 C 文件有2800行左右, 代码量是比较少的, 因此, 整个项目只有一个文件也是合理的. 但是, 记事本有5000行左右, 属于有点大了. 对于更大的程序, 几十万行是很正常的, 如果都放在一个文件中, 编译会花费非常多的时间, 而且 Visual Studio也会很卡.
所以, 我决定加入预处理这一步. 主要的工作是 : 划分文件 . 次要工作是, 修复一些简单的 IDA Pro 导出错误, 去掉一些无用注释等.

划分文件
对于C/C++项目, 一个好的项目结构是将声明与定义分开. 具体的做法就是将声明放到 头文件中, 将定义放到头文件(.h, .hpp)中, 源文件(.c, .cpp)只包含需要用到的头文件. 这样可以极大地减少编译时间, 也可以减少Visual Studio分析代码的时间.
还有一点需要说明的是, 一份源文件不要太多行, 5000行以下是比较合理的.
对于 记事本项目, 开头部分就是函数声明, 需要将它们放到一个头文件中. 这里, 我创建了 notepad.h 文件, 并将这些函数声明全部移到其中. 目前只是简单划分, 后面分析出函数的具体功能后, 再将不同功能的函数放到不同模块中.


IDA Pro 语法错误
由 IDA Pro 导出的伪C代码文件是不能直接通过编译的, 因为其中包含了许多IDA Pro自己定义的格式, 这也是 伪C代码 这个词的由来.

重定义 __thiscall
__thiscall 是 C++ 中成员函数的参数传递方式, Visual Studio中是不支持直接声明 __thiscall 修饰的函数的. 需要使用宏定义将 __thiscall 改为 __cdecl, __cdecl 是C语言函数的默认参数传递方式. 当然, 也可以使用文本替换的方式. 具体做法是在源文件开头声明如下宏 :

如果已经有了这个宏, 那么可以忽略这一步(IDA Pro新版本中导出的代码似乎会自动加上这一句).

文本替换 this
this 是 C++ 中的关键字, 是不能用作函数参数的标识符的. 因此, 需要将 this 替换为其他合法标识符. 这里我将 this 替换为了 self .

去掉start函数
IDA Pro会导出一个名为start的函数.
start函数是可执行文件中的启动函数, start函数会调用main(main, WinMain, wWinMain等)函数. 我们项目的入口函数是main函数, start函数是编译器自动生成的, 里面的一些数据结构我们无需关心, 可能也找不到声明, 因此, start函数可能会导致编译错误.
对于start函数, 我们需要做的是通过它找到main函数, 然后直接去掉它. 根据程序类型, Win32的WinMain, wWinMain有4个参数, 控制台的main函数有0个或2个参数. main函都是在GetStartupInfo 函数后面的第一个非API函数.

对于记事本, 可以知道sub_1002936是main函数. 根据API函数GetModuleHandleA函数的最后一个字母 A (ASCII)可以知道这是一个多字节项目, main函数具体是WinMain. 如果最后一个字母是W, 那么就是wWinMain.

去掉库函数声明
源文件开头还有一大串被注释了的库函数声明, 有强迫症的话, 可以把这一段删除.


后话
可以通过 Git 的 preprocess 分支查看本篇文章每个步骤所做的事.
