[翻译]如何成长为全栈逆向工程师

此文存档于看雪论坛本人的博客
原视频在此
原幻灯片在此

第一年:

  1. 阅读《Reversing:逆向工程揭密》
    从阅读Eldad Eilam的《Reversing:逆向工程揭密》开始。虽然这本书并不完美,但这是一个很好的开始。你会学到:

    • 如何使用常见的工具。
    • 如何对代码进行逆向工程。
    • 如何对文件格式进行逆向工程。
    • 复制保护(copy protection)的原理。
    • (一点)反编译器的原理。
  2. 学习汇编
    学习至少一种架构的汇编。x86是一个不错的开始。这不是因为它简单、格式统一、易于理解,或是“最好的架构”,事实上它一点也不是这样。但学习x86可以教会你很多东西。它是非常流行的,而且学习这种从某种意义上来说相当丑陋的汇编会让你对以后碰到的东西驾轻就熟。

    • 编写一些C代码,编译,反汇编,然后通过汇编代码手工反编译回C代码。
    • 让你的朋友编写并编译一些C代码。
      • 然后你反汇编并反编译。
      • 让你的朋友检查是否正确。
    • 重复操作,并试试看更复杂的代码。一定要学会浮点运算和向量(vector)。
  3. 逆向一个游戏
    游戏对于逆向分析来说是很好的素材。

    • 找一个老的3D游戏,年份最好是从90年代末期到00年代中期,而且是在自制引擎上运行的。
      • 一定要选择自制引擎的游戏,因为现成的引擎一般有着更开放的格式,这不是我们想要的。
    • 通过逆向工程了解它的数据存档格式。
      • 编写一个解密器。
    • 通过逆向工程了解他的模型格式。
      • 写一个渲染器。
      • 加分项:在OpenGL或WebGL上写这个渲染器并移植到Vulkan。
        • 理解3D API对于以后是非常有用的。
  4. 阅读《编译原理》
    阅读Aho等人著的“龙书”——《编译原理》。这不是一本最好的书,但是我个人没有读过那些可能更好的书,无法直接推荐。至少,这本书的确提供了一个很好的基础。你会学到:

    • 编译器如何工作。
    • 词法解析。
    • 语法分析。
    • 了解类型系统如何运行。
      • (以一种过分简单的,近乎于无用的方式)
    • 生成和优化代码。
    • 垃圾回收机制。
  5. 编写一个源到源编译器
    源到源编译器又被称为“转译器”(这可能是科技圈里最糟糕的术语)。这是接触编译器开发的一个很好的方式,甚至比阅读《编译原理》还要有价值。

    • 编写一个源到源编译器,用来将一种高级语言的源码转换到另一种高级语言的源码。
      • 好的源语言有Scheme、Forth、Python。
      • 好的目标语言有JavaScript、Python、Ruby。
      • 考虑创造你自己的源语言。
        • 这不是很难,这很有趣,你会从中受益良多。
      • 加分项:让你的源到源编译器适配多种不同的语言。
        • (祝你好运)
  6. 编写一个汇编器
    编写汇编器其实并不是非常有价值的练习,但是这种简单和短小的练习最终会对你有好处。花上几天或几周把这件事干完。

    • 编写你自己的将汇编代码翻译为机器码或字节码的汇编器。
      • 不要选择x86。
      • 选择MIPS、32位ARM、或通用中间语言(CIL)。

第二年:

  1. 编写另一个将源码转化为汇编代码的编译器
    这次,你将会将源码转化为汇编代码。这可能是你在这整个列表中将要做的最难的事,这也是我们第二年要做的第一件事,因为这很有价值。

    • 对某种目标架构编写一个将源码转化为汇编代码的编译器。
      • C可能是最佳的源语言。
        • 实际情况下,你将会针对C语言的一个子集来编写编译器。不要尝试编写你自己的完整的C编译器。
      • 目标的选择并不重要,但是一定要选择一个真实的架构,不要选择虚拟机。
        • 这样你就能学到更多。
  2. 阅读《C语言逆向编译技术——Reverse Compilation Techniques》
    理解反编译器原理的好处是说都说不完的。这会为开发反编译器和反混淆器、对代码进行逆向工程、破解任何东西打好基础。

    • 阅读Cifuentes的《C语言逆向编译技术——Reverse Compilation Techniques》。
      • 这是反编译的“圣经”,尽管此书已经很老。
  3. 编写一个字节码反编译器
    Android的Dalvik和.NET的通用中间语言(CIL)都很易于理解和上手。不像其他字节码,Dalvik和CIL真实地反映了源语言的样子。这使得它们成为入门的最佳选择。

    • 为Dalvik或者CIL编写反编译器。
      • 从基于goto的控制流开始。
      • 接着,尝试基于图论来重建控制流。
      • 接着,转化为静态单赋值(SSA)形式,以方便优化和代码整洁(cleanliness)。
  4. 编写一个机器码反编译器
    反编译机器码与反编译字节码是彻底不同的。源语言,如C、C++、Rust等等,是与目标架构(机器码)完全不同的。这意味着你的反编译器需要更多地去推断程序行为。这也意味着你在这个过程中会学到更多。

    • 写一个将机器码转化为伪C代码的反编译器。
      • 最好选择ARM,MIPS。
      • x86是一个糟糕的选择,但是会教会你很多并挑战你的信仰。
        • 我对此持矛盾态度。
  5. 阅读OSDev wiki
    OSDev wiki绝对是网上最佳的关于内核和操作系统的信息的来源。它涵盖了目标系统的细节、如何编写启动引导程序(bootloader)、基础的图形学和中断的原理、或其他内容。

    • 阅读OSDev wiki。
      • 接着多阅读几遍。
      • 再多阅读几遍。
  6. 编写一个“玩具”内核
    这可能是对于大部分人来说最陌生的一件事。这与你(无论是在本篇指南中还是在你的职业生涯中)做过的任何事都不同,但是你会学到许多东西。这也是相当有趣的。

    • 编写一个“玩具”内核。
      • 在显示器上展示一些文字,从键盘获取输入,或许还可以在显示器上显示一些基本图形。
      • 用C来编写,以保护模式的x86为目标。
        • 有很多文档能帮助你快速简单地编写内核并运行起来。
        • 最好不要选择Rust并以难懂的ARM单板机为目标。
  7. 阅读OSDev wiki

    • 再去阅读几遍OSDev wiki。
      • 因为你感觉到知识点欠缺了。
  8. 重新编写你的“玩具”内核
    既然你已经编写过一次内核了,那么就以一种更好的方式再做一次。你知道你第一次犯了一些可怕的错误,以及你把内核写成了完全不可移植的。是时候改改了。

    • 重新编写你的“玩具”内核。
      • 是时候换一种语言编写内核了。
      • 接着将内核移植到另一个目标架构,使尽可能多的内核代码保持不变。
        • 使一个内核,即使是“玩具”内核,拥有好的可移植性需要下很多功夫,并会教会你很多好的设计理念。
  9. 编写一个微内核
    你的第一个内核几乎肯定是一团糟,很可能没有任何分开的地址空间,或许还没有进程。是时候改改了。

    • 编写一个微内核。
      • 受L4启发的东西是一个很好的起点。
      • 如果你喜欢冒险,试着用汇编语言或Rust来编写内核。
        • 微内核本来就很小,所以无论是高级语言还是低级语言,代码量都不大。

第三年:

  1. 编写一个解释模拟器
    编写游戏系统的模拟器可以让你真正地将所学知识运用于实践。让我们从简单的东西开始。

    • 编写一个解释模拟器。
      • 选择一个知名的平台,比如NES、SNES、Gameboy、Playstation平台。
      • 这里,你的目标应该是代码的清晰性和简单性,而不是性能。
  2. 编写一个重编译模拟器
    现在我们可以尝试一些编译器的开发工作。重编译器是一个有趣的课题,只有极其少数人研究重编译器。

    • 编写一个重编译模拟器。
      • 这可以基于你刚才编写的解释模拟器,或基于完全不同的其他东西。
      • 我推荐选择有着完善文档的平台。
        • 因为你将要面临的挑战已经够多了。
      • 一个有趣的主意是重编译到JavaScript或.NET的通用中间语言(CIL)。
        • 这让你能够在你的即时编译器(JIT)上用相对小的工作量获得不错的性能。
        • 尽管我推荐编写一个针对x86或ARM的即时编译器(JIT)。
          • 这是一个编写编译器的挑战。
  3. 为一个黑盒平台编写一个模拟器
    现在很少有文档不完善的平台,但还是存在的。比如说原来的Xbox的文档就是不完善的。事实上,如果一个东西是特别老或特别新的,那么它有完善文档或好的现有的模拟器的可能性就更小。大多数从80年代末期到00年代中期的平台都有着比较完善的文档或模拟器。所以,你可以选择孩子的玩具,比如Vtech的手持设备,并尝试把代码从设备中拖下来并模拟它。这些代码几乎是没人碰过的。

    • 选择一个未知的或半未知的平台并编写一个模拟器。
      • 阅读你能找到的任何有关这个平台的信息。
      • 开始编写一个模拟器。
      • 阅读你能找到的任何汇编代码,即使是未知的部分。
      • 反复修改,直到满意。

评论

此博客中的热门博文

用Ubuntu 20.04编译Linux内核

macOS 10.15系统上用VMWare Fusion 11.5虚拟机安装Ubuntu 20.04系统步骤

一篇文章读懂Tor原理