我可以练习阅读武器代码的任何地方吗?

时间:2016-10-23 21:45:06

标签: assembly arm

我目前正在我的计算机组织课程中学习我的期中考试,并且在其中有一个部分,您需要阅读并理解代码以找到某个寄存器的值。我只是想知道是否有任何好的建议,我可以在那里练习那个特定的部分。在谷歌搜索之后我似乎无法找到任何类型的东西,因此我想我会问这里。

另外我很抱歉,如果这是一个重复的问题,我环顾网站并没有找到这样的东西,但如果有的话,链接到它会非常感激。

提前谢谢你!

2 个答案:

答案 0 :(得分:1)

我在获取工具并尝试营地。

unsigned int fun ( unsigned int a, unsigned int b )
{
    return(a+b+1);
}

如前所述,问题是优化器,如果你不优化,你会得到这样的东西:

arm-none-eabi-gcc -c fun.c -o fun.o
arm-none-eabi-objdump -D fun.o

00000000 <fun>:
   0:   e52db004    push    {r11}       ; (str r11, [sp, #-4]!)
   4:   e28db000    add r11, sp, #0
   8:   e24dd00c    sub sp, sp, #12
   c:   e50b0008    str r0, [r11, #-8]
  10:   e50b100c    str r1, [r11, #-12]
  14:   e51b2008    ldr r2, [r11, #-8]
  18:   e51b300c    ldr r3, [r11, #-12]
  1c:   e0823003    add r3, r2, r3
  20:   e2833001    add r3, r3, #1
  24:   e1a00003    mov r0, r3
  28:   e24bd000    sub sp, r11, #0
  2c:   e49db004    pop {r11}       ; (ldr r11, [sp], #4)
  30:   e12fff1e    bx  lr

它实际上非常易读,但需要比优化更多的工作:

arm-none-eabi-gcc -O2 -c fun.c -o fun.o
arm-none-eabi-objdump -D fun.o

00000000 <fun>:
   0:   e2811001    add r1, r1, #1
   4:   e0810000    add r0, r1, r0
   8:   e12fff1e    bx  lr

您将开始了解如何编写未经优化的简单代码,这是IMO的另一个好教训。未经优化的,至少对于GCC来说,他们强烈希望设置一个堆栈帧然后它将采用传入的操作数并将它们保存到堆栈中。它认为需要的任何本地或中间变量也会获得堆栈空间。 C代码的每一行都是按顺序从堆栈中单独处理的,因此操作数从堆栈中取出并保存回结果,即使它们直接返回到同一个变量中。因此,术语“优化”可以通过将内容保存在寄存器中来轻松删除大量代码。

您可以在没有堆栈框架的情况下进行编译,这是读者的练习,非常简单的谷歌搜索。

如果你可以克服优化器(或者如果没有优化可以容忍所有堆栈内容),你也可以开始看到操作中的调用约定。

    unsigned int more_fun ( unsigned int, unsigned int );
    unsigned int fun ( unsigned int a, unsigned int b )
    {
        return(more_fun(a+1,b+2)+3);
    }

   0:   e92d4010    push    {r4, lr}
   4:   e2811002    add r1, r1, #2
   8:   e2800001    add r0, r0, #1
   c:   ebfffffe    bl  0 <more_fun>
  10:   e8bd4010    pop {r4, lr}
  14:   e2800003    add r0, r0, #3
  18:   e12fff1e    bx  lr

立即1添加到r0,因此必须是我们的第一个参数。 2与r1相关,第二个参数。从被调用函数返回的r0中添加了三个,所以r0必须是这样的简单函数返回的地方(64位返回值和结构和东西是读者的练习,你也可以阅读推荐的调用约定来自arm,但是1)编译器可以做任何他们想做的事2)有时候编译函数和反汇编就容易得多了。

这里的另一个谜是为什么r4被推?或者你的编译器可能会将r3或其他寄存器与lr一起推送,或者它可能没有。另一个SO问题多次询问而没有寻找答案(因为很难搜索)。 ARM建议将堆栈保持64位对齐,在这种情况下r4只是任意寄存器,可能只需要推送两个并弹出两个寄存器。为什么然后在前一个他们没有推r11另一个寄存器?很明显,要么gnu没有看到在中断期间需要担心堆栈对齐,而且在构建堆栈帧时两次堆栈调整补偿使其成为64位对齐,您只需要针对一些指令进行中断曝光。我不知道ARM推荐的是什么。

您可以获取任何大小的任何项目所需的所有现有代码,并尝试阅读反汇编,具体取决于您可能最终构建的堆栈内容如未经优化的代码。您最终可能会遇到许多与将immediates加载到寄存器相关的很好的优化,这些寄存器不能在一条指令中完成,但不想刻录pc相对负载。也许这正是你所追求的,现实世界的代码是什么样的,你能读懂它吗?你会希望它的未经优化的结尾有更多的代码,但是从C到机器代码的有点线性转换。优化的代码包括操作的重新排序,死代码消除,与即时和其他数学相关的技巧,以及尾部优化和其他内容。然后,如果你冒险进入混合ARM /拇指更多的乐趣发生。你添加浮点更有趣。

没有理由期望任何两个不同的品牌编译器(gnu,llvm等)从同一输入产生相同的输出,也没有理由期望同一品牌的任何两个版本(缺乏更好的术语)编译器使用相同的命令行选项从相同的源生成相同的结果。这样再次增加了乐趣。

工具一直存在的底线,只是使用它们的问题。

答案 1 :(得分:0)

也许你可以自己练习在ARM程序集中做一些练习,以了解事情是如何工作的。例如,计算整数数组的总和,然后计算平均值,找到最大等...

之后,您需要熟悉调用约定。因此,我建议你阅读一些来自操作系统的代码,比如Raspberry Pi的DIY裸机操作系统。

最后,你可以在ARM中练习一些逆向工程/破解,你将会做好充分准备!