汇编程序究竟将宏翻译成什么?

逆向工程 拆卸 二元分析 文件格式
2021-06-30 10:32:14

我挖掘了简单的二进制文件,例如 x86 引导加载程序、Sega 视频游戏二进制文件等。我知道这些文件倾向于使用汇编宏来定义数据等。我无法弄清楚汇编器倾向于翻译什么宏(它们是指令、静态使用的自定义格式数据条目、寻址模式/特殊操作码等)。

例如,x86 汇编器支持DEFINE BYTE宏。汇编程序将这些(和类似的)宏转换为二进制的汇编过程是什么?它们会变成静态的、在文件本身中使用的自定义二进制格式,还是变成 x86 指令等等?

您可以尝试的示例:

[ORG 0x7C02]
[BITS 16]
jmp $
times 510-($-$$) db 0
dw 0xAA55
3个回答

汇编语言不仅仅是一组可转换为机器代码的助记符。汇编语言的语法将规定您如何指定操作数、指定它们的顺序、如何指定操作数的大小等等。

此外,汇编语言通常会包含一组指令,以帮助您更轻松地生活,并且可能包含用于声明宏的工具。有指令,如之间的区别ddresd以及times其指示汇编做的非常具体的事情和宏通常只是其被汇编在预处理阶段扩张的捷径。宏可以扩展为代码、数据、指令或其任意组合。

在您的示例中,汇编器被要求用零 ( db 0)填充到 510 字节 ( 510 - ($-$$))的大小,最后0xAA55将附加字值以使总节大小为 512 字节。

更一般地,诸如dbdw、 和 之类的数据声明dd会被转换为一个.data部分中的初始化数据,而诸如此类的数据“保留”resb可能会被转换为文件部分,从而导致创建.bss在进程加载时分配部分。请记住,这在很大程度上还取决于您可能使用的任何部分指令,这些指令还规定了数据可以在何时何地分配。

db 0xxh 将按二进制编码,因此如果发出 db 0cch

只有一个字节 0xcc 将被编码在 db 发出的确切位置

dw 将编码一个词

即 dw 0aa55h 在二进制中将被视为 AA 55

dd = DWORD == 2 WORDS == 4 BYTES 所以 dd 01337BABEh 将被编码为 13 37 ba 二进制
dq = qword == 2 dwords == 4 words == 8 bytes

组装和链接之前的目录内容

   :\>dir /b
    bootlo.asm

asm文件的内容

:\>type bootlo.asm
.386
.model flat, stdcall
.code
ORG 337h
start:
jmp @F
db 0bh dup (0CCh)
@@:
dw 0AA55h
dd 01337babeh
dq 05D0DDEED1337BABEh
retn
end start

组装文件

:\>ml /coff /nologo  bootlo.asm /link /subsystem:windows /nologo  

 Assembling: bootlo.asm

目录帖子程序集和链接的内容

:\>dir /b
bootlo.asm
bootlo.exe
bootlo.obj
mllink$.lnk

解释如下

org 337 has become default start of code section + 337  

ie 0x1000 + 0x337  so during runtime it would be entry point RVA + default base 

ie  0x400000 + 0x1000 + 0x337 == 0x401337   

at 401337 you have will have a jump encoded 其大小将是下一个标签的地址和当前指令之间的差值

下一个标签紧随其后

db 0bh0xcc在示例中编码但是0xabor 0xffor 0x00orany byte可以被编码并且汇编器将准确地发出what was asked for&how many times它被要求

所以会有一个短跳转即eb 0b (x86操作码)

按照标签 dw 0AA55h 发出,因此在 400000 + 1000 + 337 + 0x0b
AA 55 or 55 AA should be seen at 401344 (beware endianness)

如果 AA 55 存在,可以转储并检查组装的二进制文件

:\>echo. & dumpbin /ALL bootlo.exe | grep -i entry & echo. & dumpbin /all bootlo
.exe | grep -A 1 -B 1 -i 55



1337 RVA of entry point


  00401330: 00 00 00 00 00 00 00 EB 0B CC CC CC CC CC CC CC  .......δ.╠╠╠╠╠╠╠
  00401340: CC CC CC CC 55 AA BE BA 37 13 BE BA 37 13 ED DE  ╠╠╠╠U¬╛║7.╛║7.φ▐
  00401350: 0D 5D C3                                         .]├

:\>

您所指的是编译器在进入链接器之前在汇编器级别插入的附加信息或宏。

事实上,链接器可能会使用这些信息来优化最终的可执行文件(在不同程序的大小或内存布局方面)。并且,宏可能会被编译器内部的模板函数替换。

但是,所有这些信息和宏只出现在这里,因为您看到的是一项正在进行的工作。在输出一个真正的可执行文件之前,编译器仍然需要吞下更多的汇编文件。并且,在该过程的最后,所有这些中间标志(指定给编译器)都将被删除和/或替换为实际代码。

这是一个小例子。

C文件

#include <stdio.h>

int main ()
{
  printf("Hello World!\n");
  return 0;
}

汇编文件(在链接器之前)

    .file   "sample.c"
    .section    .rodata
.LC0:
    .string "Hello World!"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $.LC0, %edi
    call    puts
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 4.8.2-14) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

纯可执行文件(链接器之后)

00000000004004fd <main>:
  4004fd:       55                      push   %rbp
  4004fe:       48 89 e5                mov    %rsp,%rbp
  400501:       bf c4 05 40 00          mov    $0x4005c4,%edi
  400506:       e8 d5 fe ff ff          callq  4003e0 <puts@plt>
  40050b:       b8 00 00 00 00          mov    $0x0,%eax
  400510:       5d                      pop    %rbp
  400511:       c3                      retq   
  400512:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  400519:       00 00 00 
  40051c:       0f 1f 40 00             nopl   0x0(%rax)