逆向工程入门指北,Copyright @ 2020 Reverier, XDSEC for MoeCTF beginners only.
逆向工程是什么?
简而言之,你可以把逆向工程理解为从产品倒推设计方案的一门技术。这是一门比较黑客向的东西,想尽办法恢复别人深藏在产品中的设计思想,并收归己用 or 进行更深层次的破解与利用。
CTF 中的逆向工程指的都是软件逆向工程,逆向的对象不仅包括你最常见的 Windows 或者 Linux 操作系统上的那些二进制文件,也包括安卓的安装包,物联网设备的固件,形形色色的中间语言 (例如 Java 的 class 文件,既不属于机器代码,也不属于人类可读的代码,而是一种能够运行在 Java Runtime, 也成为 Java 虚拟机 JVM 的东西上的字节码), 总之一切能隐藏逻辑,并具有执行特定功能的文件,都是软件逆向工程的目标。
我要从哪里开始呢?
就从最简单的 Hello World 开始吧。
逆向工程是一个逆向的过程,那正向是什么呢?没错,是编程。也许你不会编程,但是歪打正着做对了一些题,这是可以的,但你越往下走,就越艰难。你要熟悉正向过程,才能在逆向分析中游刃有余。
所以,如果你还不会 C 语言,请现在开始学习编程吧。入门编程请从 C 语言开始。
为什么选择 C 语言
可是为什么不用人生苦短的 Python 呢?为什么不从 C++ 开始呢?为什么不从其他更加高级/现代的语言开始呢?
你可能会有这样的疑问,没错,编程考验的是逻辑处理能力,但是逆向并不只是逻辑处理。你需要更贴近计算机的运行原理。C 语言在保证贴近高级语言的同时,提供了足够底层的支持,并且 C 语言的结构相对简单,没有类与对象等各种复杂特性,因此更适合拿来理解计算机执行程序的原理。更高级的语言你学着挺舒服,学完了一问运行原理你还是啥都不知道,即使你想去了解也很难,高级语言的实现过程一般都是你想象不到的复杂。
同时 C 语言具有设计上的缺陷,但绝大部分的其他高级语言的解释器/编译器/运行时还是使用 C 语言编写出来的,学会 C 语言之后再去入门其他的语言门槛会低很多,但是你先学 Python 再学 C, 你会发现 C 比你想象中的要难,所以入门编程请从 C 语言开始。
如果上面说了这么多都无法打动你的话,那么接下来的话你听好了:C 语言是大一上学期必修课,并且在你整个大学过程中都阴魂不散,这下你该去学了吧?
学了一点 C 语言之后呢?
接下来你就可以尝试着上手逆向工程题目了。有个叫做 IDA 的工具可以帮你把神必的机器代码变成汇编语言,还能帮你把让人头晕的汇编语言变成类似 C 语言的代码。接下来你就可以安心的分析程序逻辑了,这就像是在看别人的代码,然后揣度别人的意思一样,通过分析程序内部的算法,再运用一点逻辑和数学的知识,你就能成功的把隐藏的信息给挖出来。
关于 IDA 如何使用,我们就不再介绍了,篇幅写不下,本文只是给你指明一个方向,具体使用方法还需要你自己通过谷歌或者百度一步步探索,作为学长我可以为你引路,但不能当你的老师/保姆呀。至于资源去哪里找,下一题有 IDA 的资源链接。
逆向题一般是什么套路?
我们不从逆向工程师的角度来看,我们从一个软件开发者的角度来看。
比如让你写一个密码验证器,你会怎么做?
strcmp(input, "this_is_password");你可能会这么写,但是这样写毫无逻辑可言,就是简单的字符串比对。你编译一下扔进 IDA, 然后按下 F5 键,你就能发现你的密码就躺在程序里,可以被轻松破解掉。
那安全一点的写法呢?
你可以用各种算法来保证你的数据没有那么容易被别人给搞出来,比如你可以使用异或:
char password_enc[] = {98, 126, 127, 101, 73, 127, 101, 73, 102, 119, 101, 101, 97, 121, 100, 114};
// password_enc 的每一位和 22 进行异或,就能得到真实的密码 "this_is_password"
for (int i = 0; i < 16; i++) {
if (input[i] ^ 22 != password_enc[i]) {
printf("Password is wrong!\n");
exit(0);
}
}
printf("Password is right!\n");虽然这个算法也很简单,但编译之后的破译难度是不是增加了?
那么作为一个逆向手,你的主要工作就是:
- 找到目标数据,无论是加密过的还是没有加密过的
- 从你的输入开始,逐步分析他的代码都干了些什么,一直到输出的位置
- 把算法抽象出来,理解他究竟对目标数据做了什么
- 尝试通过写出逆算法,把加密的目标数据成功还原为加密前的数据。
例如上面那个异或数据的例子,我们就很容易破解:
// 因为 a^b = c 时,b^c = a, 所以我们可以这样还原数据:
char password_enc[] = {98, 126, 127, 101, 73, 127, 101, 73, 102, 119, 101, 101, 97, 121, 100, 114};
char password[17];
for (int i = 0; i < 16; i++) {
password[i] = password_enc[i] ^ 22;
}
password[16] = 0;
printf("%s\n", password);简单吧。
真实做题中你会逐渐遇到更难的加密方式与算法,这不仅要求你的编程能力要逐步提高,还要求你要有耐心,逆向的过程一般都是冗长而无聊的,但是解出题目那一瞬的快感可以让你高兴一整天 (我是这样的).
你说的好像挺简单,但是好像不是所有题目都长这样啊
当然。我前面介绍过,任何能够执行的文件都是我们逆向的目标,所以针对不同语言,我们有不同的工具。遇到新的逆向题目不要害怕,越是古怪的题目可能越简单。这个时候搜索引擎就派上大用场了,你可以找到各种形形色色的工具,但最终还是要回归到上面讲的逻辑分析上。
逆向只有这些嘛?
并不,随着软件保护技术的发展,先后出现了加壳,花指令,指令虚拟化,虚拟机等等等各种更加高级的保护手法,有时候可能 IDA 也对这些保护束手无策。你可能还需要动态调试工具,通过程序运行一步一步分析在保护之下的真实逻辑,你可能也需要脱壳工具来帮助你脱掉程序的保护壳,有时候脱壳工具也不管用,你还需要了解手动脱壳…你以为我会在这里给你全部说一遍嘛?不可能的,说完我都可以出书了。
我的目标已经达成了,万事开头难,我只想帮你迈出第一步,后面的探索过程就留给你自己啦。学会用搜索引擎,并在搜索引擎无能为力时懂得找学长寻求帮助,你的进步一定会非常快的。但学习逆向工程的路不会很顺利,请做好准备哦~
加油,祝你在逆向工程领域能取得好成绩!
本题 flag: moectf{0hhhhhhh_I_kn0w_hoW_t0_R3v3rs3!}
