程序有可能读取自己吗?
Is it possible for a program to read itself?
理论题。但是假设我写了一个汇编程序。我有“labelx:”我希望程序在这个内存地址读取并且只有这个大小并打印到标准输出。
会不会像
jmp labelx
然后我会使用 Write 系统调用,确保从 labelx 的下一条指令中读取:
mov rsi,rip
mov rdi,0x01
mov rdx,?
mov rax,0x01
syscall
然后输出到标准输出。
但是我如何获得读取自身的大小?特别是如果有
在我想阅读的代码之后加上标签或在之后编码。我必须手动
数数行数?
mov rdx,rip+(bytes*lines)
然后使用填充的寄存器进行系统调用,以便系统调用从 rsi 写入到 rdi。作为标准输出。
这Even可能吗?我是否必须首先使用读取系统调用,因为写入系统调用需要为 rsi 分配内存缓冲区。但是我假设 .text 已经分配了内存并且是只读的。如果可能的话,我是否必须在写入之前先分配到堆栈或堆或静态缓冲区?
顺便说一句,我正在使用 NASM 语法。对装配还很陌生。只是一个问题。
是的,.text
部分只是内存中的字节,与通常放置 msg: db "hello", 10
的部分 .rodata
没有区别。 x86 是冯·诺依曼架构(不是哈佛),因此代码指针和数据指针之间没有区别,除了您选择如何处理它们。在链接的可执行文件上使用 objdump -drwC -Mintel
以查看 machine-code 字节,或在 运行 进程中使用 GDB 的 x
命令以查看任意位置的字节。
您可以通过将标签放在您想要的部分的 start/end 并在代码中使用 mov edx, prog_end - prog_start
来计算尺寸 assembler想要 RDX 中的那个尺寸。
有关减去两个标签(在同一部分中)以获得大小的更多信息,请参阅 How does $ work in NASM, exactly?。 (其中 $
是当前行开头的隐式标签,尽管 $
可能不是您想要的。)
要将当前地址存入寄存器,您需要 RIP-relative LEA,而不是 mov
,因为 RIP 不是 general-purpose 寄存器并且没有特殊形式的 mov
读它。
here:
lea rsi, [rel here] ; with DEFAULT REL you could just use [here]
mov edi, 1 ; stdout fileno
mov edx, .end - here ; assemble-time constant size calculation
mov eax, 1 ; __NR_write
syscall
.end:
这完全是 position-independent,与您使用 mov esi, here
不同。 ()
LEA 可以 使用 lea rsi, [rel $]
到 assemble 到相同的 machine-code 字节,但是你想要一个标签,所以你可以减去他们。
我优化了您的 MOV 指令以使用 32 位 operand-size,隐式 zero-extending 到完整的 64 位 RDX 和 RAX。 (还有 RDI,但是 write(int fd, void *buf, size_t len)
无论如何只查看 EDI 的文件描述符)。
请注意,您可以 write
任何部分的任何字节;让代码块自行编写并没有什么特别之处。 在上面的示例中,将 start/end 标签放在任何地方。 (例如 foo:
和 .end:
,以及 mov edx, foo.end - foo
利用 NASM 本地标签的工作方式,通过附加到之前的 non-local 标签,以便您可以引用它们来自其他地方。或者只是给他们两个 non-dot 名字。)
理论题。但是假设我写了一个汇编程序。我有“labelx:”我希望程序在这个内存地址读取并且只有这个大小并打印到标准输出。
会不会像
jmp labelx
然后我会使用 Write 系统调用,确保从 labelx 的下一条指令中读取:
mov rsi,rip
mov rdi,0x01
mov rdx,?
mov rax,0x01
syscall
然后输出到标准输出。
但是我如何获得读取自身的大小?特别是如果有 在我想阅读的代码之后加上标签或在之后编码。我必须手动 数数行数?
mov rdx,rip+(bytes*lines)
然后使用填充的寄存器进行系统调用,以便系统调用从 rsi 写入到 rdi。作为标准输出。
这Even可能吗?我是否必须首先使用读取系统调用,因为写入系统调用需要为 rsi 分配内存缓冲区。但是我假设 .text 已经分配了内存并且是只读的。如果可能的话,我是否必须在写入之前先分配到堆栈或堆或静态缓冲区?
顺便说一句,我正在使用 NASM 语法。对装配还很陌生。只是一个问题。
是的,.text
部分只是内存中的字节,与通常放置 msg: db "hello", 10
的部分 .rodata
没有区别。 x86 是冯·诺依曼架构(不是哈佛),因此代码指针和数据指针之间没有区别,除了您选择如何处理它们。在链接的可执行文件上使用 objdump -drwC -Mintel
以查看 machine-code 字节,或在 运行 进程中使用 GDB 的 x
命令以查看任意位置的字节。
您可以通过将标签放在您想要的部分的 start/end 并在代码中使用 mov edx, prog_end - prog_start
来计算尺寸 assembler想要 RDX 中的那个尺寸。
有关减去两个标签(在同一部分中)以获得大小的更多信息,请参阅 How does $ work in NASM, exactly?。 (其中 $
是当前行开头的隐式标签,尽管 $
可能不是您想要的。)
要将当前地址存入寄存器,您需要 RIP-relative LEA,而不是 mov
,因为 RIP 不是 general-purpose 寄存器并且没有特殊形式的 mov
读它。
here:
lea rsi, [rel here] ; with DEFAULT REL you could just use [here]
mov edi, 1 ; stdout fileno
mov edx, .end - here ; assemble-time constant size calculation
mov eax, 1 ; __NR_write
syscall
.end:
这完全是 position-independent,与您使用 mov esi, here
不同。 (
LEA 可以 使用 lea rsi, [rel $]
到 assemble 到相同的 machine-code 字节,但是你想要一个标签,所以你可以减去他们。
我优化了您的 MOV 指令以使用 32 位 operand-size,隐式 zero-extending 到完整的 64 位 RDX 和 RAX。 (还有 RDI,但是 write(int fd, void *buf, size_t len)
无论如何只查看 EDI 的文件描述符)。
请注意,您可以 write
任何部分的任何字节;让代码块自行编写并没有什么特别之处。 在上面的示例中,将 start/end 标签放在任何地方。 (例如 foo:
和 .end:
,以及 mov edx, foo.end - foo
利用 NASM 本地标签的工作方式,通过附加到之前的 non-local 标签,以便您可以引用它们来自其他地方。或者只是给他们两个 non-dot 名字。)