我从未尝试过代码的出现,但我的朋友向我展示了一个他认为我会感兴趣的老问题。这是一个有趣且可能出乎意料的解决方法。
该问题提供了一个用汇编语言编写的小程序,并询问:该程序的什么输入会产生特定的输出?语言非常简单(例如,甚至没有“添加”原语),程序的作用不是很明显,而且特定的代码片段有一些棘手的控制流,跳转相互交叉。
你可以研究代码来理解它,或者让一个快速运行者来暴力破解它,这是合理的,但这是我解决它的方法:让编译器优化代码(从程序集中找到结构),然后反编译器从该输出中恢复人类可读的高级结构,最终将程序集转换成更容易理解的东西。
输入程序是 30 行,从这个开始。该语言有一些修改内容的指令和一些相对跳转:
cpy ad cpy 4 c cpy 633 b inc d dec b jnz b -2 [...]
我们可以通过使用 goto 进行跳转,将其转换为一个小的等效 C 代码片段。只是为了好玩,这里有一个 awk 程序可以做到这一点(尽管它也只是一个稍长的 Python 等程序)。 (编辑:写完这篇文章后,我发现reddit 上有人有一个惊人相似的 awk 程序,哈! )
{ printf "L" NR ": " } /cpy/ { print $3 "=" $2 ";" } /inc/ { print $2 "++;" } /dec/ { print $2 "--;" } /jnz/ { print "if (" $2 "!=0) goto L" (NR+$3) ";" } /out/ { print "out(" $2 ");" }
根据上面的输入,这会产生如下输出:
L1: d=a; L2: c=4; L3: b=633; L4: d++; L5: b--; L6: if (b!=0) goto L4;
接下来,我们将该片段粘贴到一个.c
文件中,如下所示:
void run(int a) { // The other registers as used by the input language: int b = 0, c = 0, d = 0; // ...and then the generated code from above: L1: d=a; L2: c=4; // ... }
而且,对于核心技巧,我们使用 C 优化器对其进行编译。重要的是,C 优化器知道寻找循环之类的结构,并用执行相同操作的更简单的代码替换它们。
例如,如果你只看我上面粘贴的小片段,你可以认识到 L4-L6 有效地将b
添加到d
,编译器也可以解决这个问题。
$ cc -O2 -o cpy cpy.c
最后,我们将此输出加载到Ghidra中,它不仅会向我们展示程序集,还会尝试进一步推断结构以将其反编译为更高级别的 C 代码。
它会生成一个以以下方式打开的片段:
param_1 = param_1 + 0x9e4; iVar2 = param_1; do { ...
一方面,这已经非常简洁了——编译器设法弄清楚初始代码块(涉及输入中的常量 633)实际上只是添加 0x9e4!但另一方面,它仍然不明显,部分原因是没有变量名。
所以接下来我们转向 ChatGPT。提示:“你能重写这段代码以使用更好的变量名吗:”加上代码。它产生(免责声明:我重新运行它以获得更好的这篇文章的输出):
void run(int inputNumber) { int output; int currentNumber; currentNumber = inputNumber + 0x9e4; do { while (currentNumber == 0) { out(0); currentNumber = inputNumber; } int iteration = 0; do { if (currentNumber == 1) { output = 1; goto endLoop; } iteration++; currentNumber -= 2; } while (currentNumber != 0); output = 0; endLoop: out(output); currentNumber = inputNumber; if (iteration != 0) { currentNumber = iteration; } } while( true ); }
这段代码还是需要思考才能看懂。中间的 do-while 循环每次从currentNumber
中减去 2 时都会在iteration
中加一,您必须意识到这是除以二。但是从这里可以看出,这个程序将 0x9e4 添加到输入中,然后将其打印为二进制文件,这并不是一个巨大的飞跃。从那里开始,解决原始问题是一个相对较小的步骤。
实际上,我认为这不是我实际用来解决这个问题的方法。但由于我最近一直在关注反汇编,所以这些工具是最重要的,我认为从一个有趣的角度来研究它。
原文: https://neugierig.org/software/blog/2023/01/compiling-advent.html