我正在尝试创建一个能够在运行时编码指令的汇编程序(用于JIT编译器)。很抱歉有很长的代码段,但这是显示我问题的最短可编译示例。
#include <stdint.h>
#include <iostream>
#include <windows.h>
typedef void (*function)();
uint8_t* instructionBuffer;
uint32_t pos;
/**
* Creates the instruction buffer;
*/
void assembler_initialize() {
instructionBuffer = (uint8_t*) VirtualAllocEx(GetCurrentProcess(), 0, 1024,
MEM_COMMIT, PAGE_EXECUTE_READWRITE);
pos = 0;
}
/**
* Writes a call to the given address to the instruction buffer
*/
void assembler_emit_call(uint32_t value) {
// CALL opcode
instructionBuffer[pos++] = 0xFF;
// opcode extension 2, read a 32bit address
instructionBuffer[pos++] = 0x15;
// Address as little endian
instructionBuffer[pos++] = (value >> 0) & 0xFF;
instructionBuffer[pos++] = (value >> 8) & 0xFF;
instructionBuffer[pos++] = (value >> 16) & 0xFF;
instructionBuffer[pos++] = (value >> 24) & 0xFF;
}
/**
* Writes a RET to the instruction buffer
*/
void assembler_emit_ret() {
instructionBuffer[pos++] = 0xC3;
}
/**
* The function to call
*/
void __cdecl myFunction() {
std::cout << "Hello world!" << std::endl;
}
/**
*
*/
int main(int argc, char **argv) {
assembler_initialize();
assembler_emit_call((uint32_t) &myFunction);
assembler_emit_ret();
// Output the address
std::cout << std::hex << (uint32_t) &myFunction << std::endl;
// Output the opcodes
for (uint32_t i = 0; i < 100; i++) {
std::cout << std::hex << (uint32_t) instructionBuffer[i] << " ";
}
std::cout << std::endl;
// Call the function
function f = (function) instructionBuffer;
f();
return 0;
}
输出告诉我,myFunction
的地址是0x4017c5
,并且写了这些操作码:
CALL ModRM Addr (le) RET Zeros
ff 15 c5 17 40 0 c3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ...
但是,我的程序在尝试执行代码时崩溃了。编码CALL
指令时是否遗漏了什么?
答案 0 :(得分:6)
它不起作用,因为调用指令不正确。实际上x86上没有CALL absolute_address
指令。
在您的示例中,您将生成以下X86代码:
FF 15 xx xx xx xx
是对地址xx xx xx xx的间接调用。这将采用xx xx xx xx处的地址并在那里进行调用。
示例强>
FF 15 10 20 30 00
这将查看地址0x302010:
00302010: 11 22 33 00 xx xx xx xx
它找到值0x00332211并调用该地址的函数。
通过assembler_emit_call
中的以下修改,程序运行正常。
void assembler_emit_call(uint32_t value) {
// CALL opcode
instructionBuffer[pos++] = 0xb8; // mov eax, address
// Address as little endian
instructionBuffer[pos++] = (value >> 0) & 0xFF;
instructionBuffer[pos++] = (value >> 8) & 0xFF;
instructionBuffer[pos++] = (value >> 16) & 0xFF;
instructionBuffer[pos++] = (value >> 24) & 0xFF;
instructionBuffer[pos++] = 0xff ; // call eax
instructionBuffer[pos++] = 0xd0 ;
instructionBuffer[pos++] = 0xc3 ; // ret
}
顺便说一句
instructionBuffer[pos++] = (value >> 0) & 0xFF;
instructionBuffer[pos++] = (value >> 8) & 0xFF;
instructionBuffer[pos++] = (value >> 16) & 0xFF;
instructionBuffer[pos++] = (value >> 24) & 0xFF;
可以替换为
*(DWORD*)(instructionBuffer + pos) = value ;
pos += 4 ;