1

例如,在 Assembly x86 中,我们可以使用 .data 条目部分并静态定义一个数据字节,如下所示:

MSG db 'CAGA', AAFF

我的问题涉及汇编器如何或做什么来实现将数据插入到二进制文件中,假设我们正在组装到一个平面二进制(bin)文件。

我想知道,因为我正在尝试反编译,并更好地了解如何使用机器代码编程。

看,我想用机器代码对系统软件进行编码,但汇编器抽象出一些机器代码概念(如静态数据声明、对齐、指令宽度、语句结构、操作数或一般代码),我处于停滞状态。

我只是在问,就机器代码而言,它是如何在这些基础知识中进行布局的:

程序的 .data 部分如何静态添加到文件中,当 CPU 获取指令时,它如何在运行时/处理时使用?例如,在下面的这个程序中,它是一个 x86 引导加载程序,在 FASM 上采用 Intel 语法汇编代码,

    [BITS 16]   ;Tells the assembler that its a 16 bit code
[ORG 0x7C00]    ;Origin, tell the assembler that where the code will
                ;be in memory after it is been loaded

MOV SI, HelloString ;Store string pointer to SI
CALL PrintString    ;Call print string procedure
JMP $       ;Infinite loop, hang it here.


PrintCharacter: ;Procedure to print character on screen
    ;Assume that ASCII value is in register AL
MOV AH, 0x0E    ;Tell BIOS that we need to print one charater on screen.
MOV BH, 0x00    ;Page no.
MOV BL, 0x07    ;Text attribute 0x07 is lightgrey font on black background

INT 0x10    ;Call video interrupt
RET     ;Return to calling procedure



PrintString:    ;Procedure to print string on screen
    ;Assume that string starting pointer is in register SI

next_character: ;Lable to fetch next character from string
MOV AL, [SI]    ;Get a byte from string and store in AL register
INC SI      ;Increment SI pointer
OR AL, AL   ;Check if value in AL is zero (end of string)
JZ exit_function ;If end then return
CALL PrintCharacter ;Else print the character which is in AL register
JMP next_character  ;Fetch next character from string
exit_function:  ;End label
RET     ;Return from procedure


;Data
HelloString db 'Hello World', 0 ;HelloWorld string ending with 0

TIMES 510 - ($ - $$) db 0   ;Fill the rest of sector with 0
DW 0xAA55           ;Add boot signature at the end of bootloader

HelloString db 'Hello World', 0 ”作为 0 和 1 静态插入到 bin 文件中,但是在机器代码中,如何通过存储字符串指针将静态二进制数据作为操作数添加到 MOV SI 指令中地址到寄存器?

基本上,文件中的静态二进制数据字节如何作为要移入源索引寄存器的代码操作数执行?

4

2 回答 2

0

您的问题的答案取决于您的二进制文件的执行方式。

假设目前使用像 Linux 或 MS-windows 这样的操作系统,二进制文件很复杂,带有头信息等。让我们忽略这一点,并专注于程序在内存中后的外观。

它稍微简单一些。

代码区

代码前奏

MOV AL,BL ...

代码后奏曲

数据区

MYDATA:数据“caga”,0AH

该区域传统上被称为分段。现在这个名字被英特尔劫持了,它们被微软和 linux (ELF) 称为部分。虽然有很强的关系。MOV 是您的程序开始的地方,但需要设置前奏和后奏(否则您将永远不会再回到 Linux)。在 Pentium 上的典型 Linux/Windows 情况下,您会看到 Intel 段硬件支持这些部分。最重要的是,您将无法跳转到 MYDATA,即将程序计数器设置为包含 MYDATA。相反,硬件会在 linux 上将您视为“分段错误”,在 Windows 上可能会出现蓝屏。

可以肯定的是,您可以执行“数据”,因为实际上代码只是在正确的位置和时间执行的数据。例如,您可以通过放入类似的行来强制执行以下内容 SECTION .text

(傻,我知道。我不是编造的。)

在汇编程序代码部分中,您可以说它 MOV AL,BL 会将 0x88 和 0xD8 放在代码段中的两个连续字节中。如果你把同样的效果

DB 0x88, 0xD8

在您的程序中完全相同的位置。

你会明白,上面的内容被大大简化了。例如,一个典型的 ELF 二进制文件可以很容易地包含 20 个部分。它对于英特尔来说也有些具体,但具体比抽象更容易理解。

您的示例显然是一个引导情况。它不完整,因为缺少 HelloString。现在如果我们将它添加到程序的末尾,HelloString 会被记住为一个地址,即一个数字。在组装 MOV SI,.. 指令时,留下了一个孔来填充该数字。在处理整个文件一次后,HelloString 地址是已知的,并且汇编器将其填充。

格罗杰斯·阿尔伯特

于 2013-12-02T17:11:59.123 回答
0

MOV SI, HelloString

真的是简写

MOV SI, OFFSET HelloString

实际字符串 HelloString 被放置在二进制流中,就像代码作为字节序列一样。对于分段代码,它通常位于一个单独的数据段中,可以根据平台改变名称。对于 COM 文件之类的东西,数据通常只跟在代码区域之后,通常从适当对齐的地址开始。

更新:

我现在无法访问 16 位系统,但下面是 32 位 x86 代码中类似的内容:

Binary code    Instruction
BEA43F4600     mov esi, offset @HelloString  
E8F2FFFFFF     call PrintString
EBFE           @1: jmp @1
48656C6C6F     @HelloString: "Hello"

当然,@HelloString 的实际偏移量和 PrintString 的实际地址取决于上下文。

于 2013-07-26T21:41:16.887 回答