编译汇编操作码
Compiling Assembler Opcodes
我想知道是否可以用等效的操作码替换汇编指令。 (即能够编译操作码而不是指令)
如果是这样,是否可以在运行时操纵这些操作码?
干杯
if it were possible to replace assembler instructions by their equivalent opcodes.
是的,您可以编译操作码,生成的机器代码将是相同的。
例如 x86-32 短无用的汇编代码:
uselessFunc:
xor eax,eax
ret
也可以用操作码来写:
uselessFunc:
db 0x31, 0xC0 ; opcode "xor eax,eax"
db 0xC3 ; opcode "ret"
两个来源将产生相同的三个字节的机器码:31 C0 C3
.
is it be possible to manipulate these opcodes at runtime
这与来源的形式完全无关。在 运行 时,您可以操作任何您具有写访问权限(最好是读+写访问权限)的内存。但是在你修改操作码之后,如果你想 运行 它们,你还需要执行对该内存的访问。
现代x86机器上现代OS像linux这不是默认配置,默认情况下代码段是只读+可执行,数据段是读+写,但不是可执行文件,因此如果您尝试修改代码的操作码,您会在写入期间因无效内存访问而崩溃,如果您尝试在数据段中执行操作码,则会触发 no-exec 错误。
所以像 Java VM 和类似的应用程序,它们在 运行 时间生成代码,然后执行它("JIT" 即时编译器编译 java 来自 .class
文件的操作码在 运行 时间转换为本机机器代码以获得重复执行的部分的更好性能)不仅 produce/modify 操作码,还管理目标内存页面其他系统调用使它们首先可写,然后将它们更改为 no-read+exec 代码内存页。 IE。通常这是可能的,但在许多目标环境中,您必须使用额外的系统服务才能使其正常工作。
请记住,自我修改代码在现代被认为是不好的做法,不仅因为它更难调试,而且如果以天真的方式使用,它可能会对性能产生巨大影响(再次以 x86 为例 CPUs 在执行前仅修改操作码的几个字节将使 CPU 中所有可能的 caches/prefetch 行无效,使其暂时停止 re-read/decode 指令)。在某些 CPU 上,memory/cache 模型比在 x86 上更弱,因此过晚修改操作码可能会被 CPU 忽略,因为它已经解码了旧内容并将执行.
但只要你知道自己在做什么,producing/modifying 操作码是可能的。它不以任何方式依赖于您的源代码形式,无论您如何生成原始二进制文件,无论您是使用汇编或 C 语言源代码编写这些操作码,还是直接将它们作为字节值写入 hex 编辑器中,都无关紧要。
对于上面的两个例子,在这两种情况下你都可以这样做:
mov byte [uselessFunc+1],0xD8 ; modify xor eax,eax to xor eax,ebx
如果您将获得对内存目标区域的写访问权限,并且它将保留可执行权限,那么在这两种情况下,这都会将 xor eax,eax
变为 xor eax,ebx
。
我想知道是否可以用等效的操作码替换汇编指令。 (即能够编译操作码而不是指令) 如果是这样,是否可以在运行时操纵这些操作码? 干杯
if it were possible to replace assembler instructions by their equivalent opcodes.
是的,您可以编译操作码,生成的机器代码将是相同的。
例如 x86-32 短无用的汇编代码:
uselessFunc:
xor eax,eax
ret
也可以用操作码来写:
uselessFunc:
db 0x31, 0xC0 ; opcode "xor eax,eax"
db 0xC3 ; opcode "ret"
两个来源将产生相同的三个字节的机器码:31 C0 C3
.
is it be possible to manipulate these opcodes at runtime
这与来源的形式完全无关。在 运行 时,您可以操作任何您具有写访问权限(最好是读+写访问权限)的内存。但是在你修改操作码之后,如果你想 运行 它们,你还需要执行对该内存的访问。
现代x86机器上现代OS像linux这不是默认配置,默认情况下代码段是只读+可执行,数据段是读+写,但不是可执行文件,因此如果您尝试修改代码的操作码,您会在写入期间因无效内存访问而崩溃,如果您尝试在数据段中执行操作码,则会触发 no-exec 错误。
所以像 Java VM 和类似的应用程序,它们在 运行 时间生成代码,然后执行它("JIT" 即时编译器编译 java 来自 .class
文件的操作码在 运行 时间转换为本机机器代码以获得重复执行的部分的更好性能)不仅 produce/modify 操作码,还管理目标内存页面其他系统调用使它们首先可写,然后将它们更改为 no-read+exec 代码内存页。 IE。通常这是可能的,但在许多目标环境中,您必须使用额外的系统服务才能使其正常工作。
请记住,自我修改代码在现代被认为是不好的做法,不仅因为它更难调试,而且如果以天真的方式使用,它可能会对性能产生巨大影响(再次以 x86 为例 CPUs 在执行前仅修改操作码的几个字节将使 CPU 中所有可能的 caches/prefetch 行无效,使其暂时停止 re-read/decode 指令)。在某些 CPU 上,memory/cache 模型比在 x86 上更弱,因此过晚修改操作码可能会被 CPU 忽略,因为它已经解码了旧内容并将执行.
但只要你知道自己在做什么,producing/modifying 操作码是可能的。它不以任何方式依赖于您的源代码形式,无论您如何生成原始二进制文件,无论您是使用汇编或 C 语言源代码编写这些操作码,还是直接将它们作为字节值写入 hex 编辑器中,都无关紧要。
对于上面的两个例子,在这两种情况下你都可以这样做:
mov byte [uselessFunc+1],0xD8 ; modify xor eax,eax to xor eax,ebx
如果您将获得对内存目标区域的写访问权限,并且它将保留可执行权限,那么在这两种情况下,这都会将 xor eax,eax
变为 xor eax,ebx
。