简明、现代而优雅的破解技术笔记(二)
本帖最后由 qzhsjz 于 2021-2-5 21:51 编辑[前言](https://www.52pojie.cn/thread-1362431-1-1.html)
# 简明、现代而优雅的破解技术笔记 第一课
(论坛的自动超链接机制会让我的标题出bug,这个问题修复之前我的 Markdown 标题中不能出现“破解技术”四个字)
## 需要提前准备安装的工具环境
- Microsoft Visual Studio 2019 社区版(完全免费) [官网下载](https://visualstudio.microsoft.com/zh-hans/downloads/)
安装时记得选择 `C++桌面开发` 工作负载
- IDA [本站 IDA 7.5 发布帖](https://www.52pojie.cn/thread-1345176-1-1.html)
## 正向开发:开始设计密码判断程序
即使你是一个连C++也没有接触过的新手,也不要害怕,下面我会演示如何用 Visual Studio 默认创建的 Hello World 代码修改出一个判断密码的小程序。
### 在 Visual Studio 中创建工程
- 打开 Visual Studio
- 选择 `创建新项目`
- 语言选择 `C++` ,平台选择 `Windows` 。
- 在列表中找到 `控制台应用` ,并点击下一步。
- 把项目位置改成你希望用来盛放你的作业的位置,并且给项目起个名字,比如 `我的第一个密码判断程序` 。
- 勾选 `将解决方案和项目放在同一目录中` ,本例我们不需要复杂的解决方案与项目管理功能,勾选这一项可以减少文件夹层级,看起来顺眼一些。
- 点击 `创建` 。
我们可以看到,Visual Studio 自动为我们配置好了工程,并生成了一个源代码文件,文件的内容是一个 Hello World 程序。
### 修改 Visual Studio 生成的初始程序代码
在上方的 `Debug x86`中把 `x86` 换成 `x64` ,并点击右侧带有绿色小三角的 `本地Windows调试器` ,就可以看到 Hello World 程序的运行结果。
这就是这个程序代码的作用。将这个调试窗口关掉,我们继续查看程序代码本身。
如果你接触过一些基础的C++课程,或者至少知道如何编写Hello World,你会觉得很熟悉,这里面除去大括号之外的三行代码,第一行是引入 `iostream` 这个C++模块,第二行是定义了一个 `main` 函数,而第三行则是调用 `iostream` 模块中在 `std` 命名空间中定义的流对象 `cout` ,并通过 `<<` 运算符来向 `cout` 中输出一个字符串 `"Hello World!\n"` 。
如果你没有接触过C++,完全不知道这三行代码是什么意思,也不要着急,上期所述的C++标准委员会的官方文档中对这些东西有详细解释,搜索关键字即可。
> 在C++标准委员会的文档中,我们可以搜索到 `cout` 和 `cin` 两个流的具体用法和示例代码,请点击链接:(https://zh.cppreference.com/w/cpp/io/cin) (https://zh.cppreference.com/w/cpp/io/cout)
根据以上查到的用法和示例代码,我们可以发现,如果我们想把用户输入读入一个变量,那么就可以 `cin >> 这个变量;` ,如果想要输出一个东西,我们就可以 `cout << 这个东西;` 。
在C++中,想使用的变量需要预先定义,而且需要声明该变量的数据类型。变量声明的语法是 `变量类型 变量名;` 。
于是,我们现在可以读入用户输入了,并且把它输出出来试一下。代码如下:
```
#include <iostream>
int main()
{
std::string 用户输入的内容;
std::cin >> 用户输入的内容;
std::cout << 用户输入的内容;
}
```
再次点击运行,你会发现你制作了一款复读机。
知道了如何输入和输出,我们现在只需判断一下用户的输入与真码是不是一致就可以了。另外,为了让程序独立运行时最后能够停住看结果,需要在最后加一个等待用户输入的命令。所以最后的代码变成了这样:
```
#include <iostream>
int main()
{
std::string 用户输入的内容;
std::string 真码 = "52Pojie-AngeloTheCat";
std::cin >> 用户输入的内容;
if (用户输入的内容 == 真码)
std::cout << "密码正确!\n";
else
std::cout << "密码错误!\n";
system("pause");
}
```
再次点击运行,至此,一个小小的密码验证程序就完成了。
> 这个密码验证程序似乎对中文的控制台用户输入有bug,也可能是我的系统开了强制Unicode内码的缘故,暂时没有解决。可能使用区分宽窄字符的C++标准库,并且系统窄字符默认编码是UTF-8时就会出现这个问题,如果想要bug少一些,处理输入的时候请尽量使用 `conio.h` 或Windows API。
## 逆向分析:使用IDA查看编译好的密码判断程序
### IDA的安装与配置
站长发的 IDA 7.5 是绿色版的,只需要绿化即可。
为了能够让IDA搜索中文字符串,需要在绿化后桌面上出现的 IDA 快捷方式中添加参数 `-dCULTURE=all` ,如图:
### 开始我们的第一次逆向分析
将我们编译好的程序拖入64位 IDA 的快捷方式,将会打开 IDA ,初次打开 IDA 时,会让你同意一个用户协议,同意即可。一般来讲,将程序拖入 IDA 后,将会弹出选择架构与 ABI 的界面,默认为 AMD64 架构和 Windows ABI ,这是正确的,所以这里点击OK。
之后 IDA 会弹窗告诉你,你要分析的这个exe文件存在一个调试符号表(PDB)文件,并询问你是否需要加载这个文件。这里我们选择“是”,看看得出来的结果是什么。
由于我们的程序非常小,IDA 很快就会加载完成。IDA 加载完成后会自动跳转到 main 函数的位置,这时显示的是 IDA 的流程框图,可以看到,好像已经出现了什么不得了的东西……
我们按下 `F5` ,这是 IDA 的反编译快捷键,将会反编译当前选择的函数。
简直是不讲武德!我们刚才在main函数中所编写的逻辑,几乎一点不差地还原了出来。可以大致看到,if语句成立的条件是 `v7==v8` ,而 v7 是调用了 `cin` 获取的用户输入,v8 则是密码的明文。
对于有C++基础的人,可以看到,从反编译后的第16行代码开始,和我们刚刚写的代码完全一致。先构造两个 `string` 对象 `v7` 和 `v8` ,并且将v8初始化为真码的内容。再调用 `cin` 的 `>>` 运算符并且右操作数是 `v7` 这个 `string` 。然后调用 `string` 类的 `==` 运算符判断两个string,v7和v8是否相等,如果相等就调用 `cout` 对象的 `<<` 运算符,输出密码正确的提示,否则就还是调用 `cout` 对象的 `<<` 运算符,输出密码错误的提示。
而反编译的9-15行代码,则是 VC 的 Debug 版生成出来的程序自带的 Debug 环境初始化代码。
至此,由于我们写了一个实在太简单的程序,并且给IDA提供了符号表,所以我们轻而易举地就看到了这个程序的全部代码。我们现在来尝试修改这个程序的代码,将if的条件反向,密码正确时输出密码错误,密码错误时输出密码正确。
### 对第一个程序的修改(Hacking)
返回 IDA View 视图(框图),可以看到主函数的执行逻辑,左边是密码正确的逻辑,右边是密码错误的逻辑。区分走哪边是靠上面框中最后一条指令 `jz` 来实现的。
我们将光标定位在 `jz` 处,点击 Edit -> Patch program -> Assemble。
在弹出来的框中将 `jz` 改为 `jnz`。
点击 OK 后 IDA 会自动给你下一个地址处的汇编让你改,此处我们不需要继续再改了,应该停止修改,按 Cancel。
> `jz` 是一条汇编指令,它一般和 `test` 一同出现。在上一条 `test` 中,如果用于比较的两个寄存器与起来的值是0(也就是说两个寄存器都是0),那么 `jz` 就会跳转。`jz` 的意思是 jump zero,为零时跳转,`jnz` 也是一条汇编指令,意思是不为零时就跳转。`jz` 和 `jnz` 也并不是一定要和 `test` 一同出现,他们判断为不为0的其实是CPU的状态寄存器。`test` 是一种会修改状态寄存器的指令。
再次按下 F5 ,看看生成的反编译代码 B 和之前的反编译代码 A 有什么区别。
点击 Edit -> Patch program -> Apply patches to input file。勾选 `Create Backup` 是个好习惯。
点击OK。运行一下你刚保存的程序,看看和你编写出来的原程序有什么区别。
恭喜你完成了人生中第一次破解自己写的程序!
**在下一期内容里,我将带领大家对这个小程序做一些改进,至少不会让IDA一载入就在第一屏看见所有逻辑。**
沉睡的琥珀 发表于 2021-2-24 11:06
我的疑问:
楼主大大,源代码是否只要任何的修改,都会让已有的Hacking操作失效?
是这样的,IDA中修改的是编译好的程序,补丁也是补丁那一个程序文件。
如果你重新编译了程序,那么自然编译出来的新程序会覆盖掉原来编译出来的程序。
编译之后需要重新分析的,所以这就是逆向工程中的一个麻烦点,就是每个分析只能对一个具体的二进制文件负责。不过掌握了方法之后分析二进制文件是很快的。 对于Unicode编码下的C++,请尝试使用wstring、wcin、wcout来输入中文 cj13888 发表于 2021-1-31 11:20
学习了,希望能跟着楼主找饭吃
哈哈,我现在都快没饭吃了{:1_936:}
话说我这个帖子人气还是不行啊,是我不会起标题还是讲的太难了? 谢谢楼主! 膜拜楼主,高产啊,感谢 谢谢楼主 楼主牛的,感谢分享{:1_927:} 希望楼主以后能多出些IDA的有关教程和sao操作,IDA Pro book全英文又涉及大量专业词汇实在太难了。。IDA F5应该算是最无脑的操作了吧,期待下一次的教程,按楼主的意思应该是带有基础加密算法的pj吧,期待! 感谢楼主 学习了 Bluezzz 发表于 2021-1-31 10:00
希望楼主以后能多出些IDA的有关教程和sao操作,IDA Pro book全英文又涉及大量专业词汇实在太难了。。IDA F5 ...
我记得有一本已经翻译出来的书,叫 IDA Pro 权威指南(第二版),有兴趣可以参考一下。
我之前买过,但是现在想用了找不到了…… 谢谢楼主的教程,学习了 很棒的教程 小白受教了