unicorn-engine / unicorn

Unicorn CPU emulator framework (ARM, AArch64, M68K, Mips, Sparc, PowerPC, RiscV, S390x, TriCore, X86)
http://www.unicorn-engine.org
GNU General Public License v2.0
7.33k stars 1.31k forks source link

Invalid memory fetch (UC_ERR_FETCH_UNMAPPED) (was: Hook function not called) #1896

Closed javacommons closed 8 months ago

javacommons commented 8 months ago

host_function is not called in the following example.

$ g++ host-calc.cpp -lunicorn -static
$ ./a.exe
Emulation result: 0 // <-- shoud be 8
// hook.cpp
#include <unicorn/unicorn.h>
#include <iostream>

int host_function(uc_engine* uc, void* user_data) {
    uint64_t arg1, arg2;
    uc_reg_read(uc, UC_X86_REG_RDI, &arg1);
    uc_reg_read(uc, UC_X86_REG_RSI, &arg2);

    uint64_t result = arg1 + arg2;
    std::cout << "arg1:" << arg1 << std::endl;
    std::cout << "arg2:" << arg2 << std::endl;
    std::cout << "result:" << result << std::endl;

    uc_reg_write(uc, UC_X86_REG_RAX, &result);

    return 0;
}

int main() {
    uc_engine* uc;
    uc_err err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
    if (err != UC_ERR_OK) {
        printf("uc_open() failed with error: %s\n", uc_strerror(err));
        return -1;
    }

    uint64_t code_address = 0x1000;
    uint8_t code[] = {
        0x48, 0xC7, 0xC6, 0x05, 0x00, 0x00, 0x00, // mov rsi, 5
        0x48, 0xC7, 0xC7, 0x03, 0x00, 0x00, 0x00, // mov rdi, 3
        0xE8, 0x00, 0x00, 0x00, 0x00,             // call host_function
        0xC3                                      // ret
    };

    uc_mem_map(uc, code_address, sizeof(code), UC_PROT_ALL);
    uc_mem_write(uc, code_address, code, sizeof(code));

    uc_hook hook;
    uc_hook_add(uc, &hook, UC_HOOK_INSN, (void*)host_function, NULL, code_address, code_address + sizeof(code));

    uc_reg_write(uc, UC_X86_REG_RSP, &code_address);
    uc_emu_start(uc, code_address, code_address + sizeof(code), 0, 0);

    uint64_t result;
    uc_reg_read(uc, UC_X86_REG_RAX, &result);

    printf("Emulation result: %lu\n", result);

    uc_close(uc);

    return 0;
}
javacommons commented 8 months ago

I modified the above code like this:

#include <unicorn/unicorn.h>
#include <assert.h>
#include <stdio.h>
#include <iostream>

#define OK(x) {uc_err __err; if ((__err = x)) { fprintf(stderr, "%s", uc_strerror(__err)); assert(false); } }

int host_function(uc_engine* uc, void* user_data) {
    std::cout << "host_function(begin)" << std::endl;
    uint64_t arg1, arg2;
    uc_reg_read(uc, UC_X86_REG_RDI, &arg1);
    uc_reg_read(uc, UC_X86_REG_RSI, &arg2);

    uint64_t result = arg1 + arg2;
    std::cout << "arg1:" << arg1 << std::endl;
    std::cout << "arg2:" << arg2 << std::endl;
    std::cout << "result:" << result << std::endl;

    uc_reg_write(uc, UC_X86_REG_RAX, &result);

    std::cout << "host_function(end)" << std::endl;
    return 0;
}

int main() {
    uc_engine* uc;
    uc_err err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
    if (err != UC_ERR_OK) {
        printf("uc_open() failed with error: %s\n", uc_strerror(err));
        return -1;
    }

    uint64_t code_address = 0x1000;
#if 0x0
// https://disasm.pro/
mov rsi, 5
mov rdi, 3
mov eax, 1
int 0x80 
ret
#endif
    char code[] = "\x48\xC7\xC6\x05\x00\x00\x00\x48\xC7\xC7\x03\x00\x00\x00\xB8\x01\x00\x00\x00\xCD\x80\xC3";

    OK(uc_mem_map(uc, code_address, 0x1000/*sizeof(code)*/, UC_PROT_ALL));
    OK(uc_mem_write(uc, code_address, code, sizeof(code)));

    uc_hook hook;
    OK(uc_hook_add(uc, &hook, UC_HOOK_INTR | UC_HOOK_MEM_UNMAPPED, (void*)host_function, NULL, code_address, code_address + sizeof(code)));

    OK(uc_reg_write(uc, UC_X86_REG_RSP, &code_address));
    OK(uc_emu_start(uc, code_address, code_address + sizeof(code), 0, 0));

    uint64_t result;
    OK(uc_reg_read(uc, UC_X86_REG_RAX, &result));

    printf("Emulation result: %lu\n", result);

    uc_close(uc);

    return 0;
}

Then, I got a different error:

$ g++ host-calc.cpp -lunicorn -static && ./a.exe
host_function(begin)
arg1:3
arg2:5
result:8
host_function(end)
Invalid memory fetch (UC_ERR_FETCH_UNMAPPED)Assertion failed: false, file host-calc.cpp, line 51
javacommons commented 8 months ago

If I changed the source code to ignore uc_emu_start()'s error, the result is just like I expected. Can I just ignore uc_emu_start()'s error?

    OK(uc_reg_write(uc, UC_X86_REG_RSP, &code_address));
#if 0x0 // ignore uc_emu_start()'s error
    OK(uc_emu_start(uc, code_address, code_address + sizeof(code), 0, 0));
#else
    uc_emu_start(uc, code_address, code_address + sizeof(code), 0, 0);
#endif
$ g++ host-calc.cpp -lunicorn -static && ./a.exe
host_function(begin)
arg1:3
arg2:5
result:8
host_function(end)
host_function(begin)
arg1:3
arg2:5
result:8
host_function(end)
Emulation result: 8
javacommons commented 8 months ago

Final source code:

When DEBUG is not defined:

$ g++ host-calc.cpp -lunicorn -static && ./a.exe
host_function(begin)
arg1:30
arg2:50
result:80
host_function(end)
host_function(begin)
arg1:3
arg2:5
result:8
host_function(end)
Emulation result: 8

When DEBUG is defined:

$ g++ host-calc.cpp -DDEBUG -lunicorn -static && ./a.exe
host_function(begin)
arg1:30
arg2:50
result:80
host_function(end)
host_function(begin)
arg1:3
arg2:5
result:8
host_function(end)
Invalid memory fetch (UC_ERR_FETCH_UNMAPPED)Assertion failed: false, file host-calc.cpp, line 50
#include <unicorn/unicorn.h>
#include <assert.h>
#include <stdio.h>
#include <iostream>

#define OK(x) {uc_err __err; if ((__err = x)) { fprintf(stderr, "%s", uc_strerror(__err)); assert(false); } }

int host_function(uc_engine* uc, void* user_data) {
    std::cout << "host_function(begin)" << std::endl;
    uint64_t arg1, arg2;
    uc_reg_read(uc, UC_X86_REG_RDI, &arg1);
    uc_reg_read(uc, UC_X86_REG_RSI, &arg2);
    uint64_t result = arg1 + arg2;
    std::cout << "arg1:" << arg1 << std::endl;
    std::cout << "arg2:" << arg2 << std::endl;
    std::cout << "result:" << result << std::endl;
    uc_reg_write(uc, UC_X86_REG_RAX, &result);
    std::cout << "host_function(end)" << std::endl;
    return 0;
}

int main() {
    uc_engine* uc;
    uc_err err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
    if (err != UC_ERR_OK) {
        printf("uc_open() failed with error: %s\n", uc_strerror(err));
        return -1;
    }
    uint64_t code_address = 0x1000;
#if 0x0
// https://disasm.pro/
mov rsi, 50
mov rdi, 30
mov eax, 1
int 0x80 
mov rsi, 5
mov rdi, 3
mov eax, 1
int 0x80 
ret
#endif
    char code[] = "\x48\xC7\xC6\x32\x00\x00\x00\x48\xC7\xC7\x1E\x00\x00\x00\xB8\x01\x00\x00\x00\xCD\x80\x48\xC7\xC6\x05\x00\x00\x00\x48\xC7\xC7\x03\x00\x00\x00\xB8\x01\x00\x00\x00\xCD\x80\xC3";
    OK(uc_mem_map(uc, code_address, 0x1000/*sizeof(code)*/, UC_PROT_ALL));
    OK(uc_mem_write(uc, code_address, code, sizeof(code)));
    uc_hook hook;
    OK(uc_hook_add(uc, &hook, UC_HOOK_INTR,
                   (void*)host_function, NULL, code_address, code_address + sizeof(code)));
    OK(uc_reg_write(uc, UC_X86_REG_RSP, &code_address));
#ifdef DEBUG // abort on uc_emu_start()'s error
    OK(uc_emu_start(uc, code_address, code_address + sizeof(code), 0, 0));
#else // ignore uc_emu_start()'s error
    uc_emu_start(uc, code_address, code_address + sizeof(code), 0, 0);
#endif
    uint64_t result;
    OK(uc_reg_read(uc, UC_X86_REG_RAX, &result));
    printf("Emulation result: %llu\n", result);
    uc_close(uc);
    return 0;
}
javacommons commented 8 months ago

Finally I could solve the problem (I'm closing this issue):

$ g++ host-calc.cpp -lunicorn -static && ./a.exe
host_function(begin)
arg1:30
arg2:50
result:80
host_function(end)
host_function(begin)
arg1:3
arg2:5
result:8
host_function(end)
Emulation result: 8
// https://disasm.pro/
mov rsi, 50
mov rdi, 30
mov eax, 1
int 0x80 
mov rsi, 5
mov rdi, 3
mov eax, 1
int 0x80
ret // <--unnecessary (was the cause of the problem)
#include <unicorn/unicorn.h>
#include <assert.h>
#include <stdio.h>
#include <iostream>

#define OK(x) {uc_err __err; if ((__err = x)) { fprintf(stderr, "%s", uc_strerror(__err)); assert(false); } }

int host_function(uc_engine* uc, void* user_data) {
    std::cout << "host_function(begin)" << std::endl;
    uint64_t arg1, arg2;
    uc_reg_read(uc, UC_X86_REG_RDI, &arg1);
    uc_reg_read(uc, UC_X86_REG_RSI, &arg2);
    uint64_t result = arg1 + arg2;
    std::cout << "arg1:" << arg1 << std::endl;
    std::cout << "arg2:" << arg2 << std::endl;
    std::cout << "result:" << result << std::endl;
    uc_reg_write(uc, UC_X86_REG_RAX, &result);
    std::cout << "host_function(end)" << std::endl;
    return 0;
}

int main() {
    uc_engine* uc;
    uc_err err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
    if (err != UC_ERR_OK) {
        printf("uc_open() failed with error: %s\n", uc_strerror(err));
        return -1;
    }
    uint64_t code_address = 0x1000;
#if 0x0
// https://disasm.pro/
mov rsi, 50
mov rdi, 30
mov eax, 1
int 0x80 
mov rsi, 5
mov rdi, 3
mov eax, 1
int 0x80 
#endif
    char code[] = "\x48\xC7\xC6\x32\x00\x00\x00\x48\xC7\xC7\x1E\x00\x00\x00\xB8\x01\x00\x00\x00\xCD\x80\x48\xC7\xC6\x05\x00\x00\x00\x48\xC7\xC7\x03\x00\x00\x00\xB8\x01\x00\x00\x00\xCD\x80";
    OK(uc_mem_map(uc, code_address, 0x1000/*sizeof(code)*/, UC_PROT_ALL));
    OK(uc_mem_write(uc, code_address, code, sizeof(code) - 1));
    uc_hook hook;
    OK(uc_hook_add(uc, &hook, UC_HOOK_INTR,
                   (void*)host_function, NULL, code_address, code_address + sizeof(code) - 1));
    OK(uc_reg_write(uc, UC_X86_REG_RSP, &code_address));
    OK(uc_emu_start(uc, code_address, code_address + sizeof(code) - 1, 0, 0));
    uint64_t result;
    OK(uc_reg_read(uc, UC_X86_REG_RAX, &result));
    printf("Emulation result: %lu\n", result);
    uc_close(uc);
    return 0;
}