-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Editing an instruction before being called doesn't do anything. #1561
Comments
You have to clear translation cache via |
Thanks for the quick reply.
|
You have to clear cache once you have rewrite the memory. However, such SMC code should be handled already before translation cache so if you could provide a minimal reproduction then I would have a look.
…________________________________
From: Kit ***@***.***>
Sent: Wednesday, March 2, 2022 6:03:29 PM
To: unicorn-engine/unicorn ***@***.***>
Cc: lazymio ***@***.***>; Comment ***@***.***>
Subject: Re: [unicorn-engine/unicorn] Editing an instruction before being called doesn't do anything. (Issue #1561)
Thanks for the quick reply.
I've added the code below after making the memory change - but r3 is still being modified.
#define TB_COUNT (8)
#define TCG_MAX_INSNS (512) // from tcg.h
#define CODE_LEN TB_COUNT *TCG_MAX_INSNS
for (int i = 0; i < TB_COUNT; i++)
{
uc_ctl_remove_cache(
uc,
code_start_address + i * TCG_MAX_INSNS,
code_start_address + i * TCG_MAX_INSNS + 1);
}
―
Reply to this email directly, view it on GitHub<#1561 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AHJULO3ROSOSVKJU6PHJ2MLU56NODANCNFSM5PXEUD5A>.
You are receiving this because you commented.Message ID: ***@***.***>
|
|
Thanks for offering to look at a minimal reproduction. Once the emulation starts I'm trying to change an instruction to do nothing! |
@wtdcode do you have any suggestions? I would be very grateful! |
Sorry at this moment I'm a bit busy. Spare me some time and I would have a look. |
I see why it doesn't work. The Another workaround:
|
I might have found another workaround.
This then seems to go back and execute the new code. Is this working 'as expected' ? |
I'm also going to try your workaround now too. Stop/clear cache/start the emulation. |
This sounds interesting, it in fact indeed "soft restarts" the emulation. But if you don't clear cache, you probably still execute the same old instruction. Could you post a small sample? |
|
Okay, I got it. When you set PC, you are in fact triggering a restart on the current PC. Since the translating block is identified by its start, if the current PC is not the start of the translation block, unicorn will re-translate from the current PC since it's not cached. The only problem is that, if the instruction you are going to modify is the first instruction of a translation block (in most cases, equal to a basic block), then this workaround won't work. |
I've tried your workaround - but I absolutely cannot get it working. It seems to go around again and try and emulate the code a second time.
|
|
I slightly change your sample to make it work. #include <string.h>
#include <unicorn/unicorn.h>
#define ARM_CODE "\x0b\x10\xa0\xe3" \
"\x06\x20\xa0\xe3" \
"\x01\x20\x82\xE2" \
"\x01\x20\x82\xe0\x08\x80\xa0\xe1"
// int r2=0xbeef;
// 0x10000 0b 10 a0 e3 mov r1, #0xb
// 0x10004 f0 20 a0 e3 mov r2, #0x6 <- going to replace this with : mov r2, #0xf0
// 0x10008 01 20 82 E2 add r2,1
// 0x1000c 01 20 82 e0 add r2, r2, r1
// 0x10010 08 80 a0 e1 mov r8, r8
uc_hook hk_change_instruction;
#define ADDRESS 0x1000
#define ADDRESS_TO_CHANGE 0x1004
static uc_err _uc_err_check(uc_err err, const char *expr)
{
if (err)
{
fprintf(stderr, "Failed on %s with error: %s\n", expr, uc_strerror(err));
exit(1);
}
return err;
}
#define UC_ERR_CHECK(x) _uc_err_check(x, #x)
void remove_cache(uc_engine *uc)
{
uc_tb tb;
uc_err err;
err = uc_ctl_request_cache(uc, ADDRESS, &tb);
printf(">>> TB is cached at 0x%" PRIx64 " which has %" PRIu16" instructions with %" PRIu16 " bytes.\n",tb.pc, tb.icount, tb.size);
UC_ERR_CHECK(err);
printf(">>> removing cache from 0x%" PRIx64 " to 0x%" PRIx64 ".\n",tb.pc, tb.pc+ tb.size);
err = uc_ctl_remove_cache(uc, tb.pc, tb.pc + tb.size);
UC_ERR_CHECK(err);
}
static void hook_code_change_instruction(uc_engine *uc, uint64_t address, uint32_t size,
void *user_data)
{
uc_err err;
#define NEW_ARM_CODE "\xf0\x20\xA0\xE3"
printf("\nChanging instruction at 0x%x.\n", ADDRESS_TO_CHANGE);
printf("Was: mov r2, #0x06 [06 20 a0 e3]\n");
printf("Becomes: mov r2, #0xf0 [f0 20 a0 e3]\n");
printf ("Writing new memory \n");
uc_mem_write(uc, ADDRESS_TO_CHANGE, NEW_ARM_CODE, sizeof(NEW_ARM_CODE) - 1);
err=uc_hook_del(uc, hk_change_instruction);
UC_ERR_CHECK(err);
printf ("Stopping emulation now\n");
err=uc_emu_stop(uc);
UC_ERR_CHECK(err);
}
static void hook_code(uc_engine *uc, uint64_t address, uint32_t size,
void *user_data)
{
int r1, r2;
uc_reg_read(uc, UC_ARM_REG_R1, &r1);
uc_reg_read(uc, UC_ARM_REG_R2, &r2);
printf(">>> R1=0x%x R2=0x%x\n\n", r1, r2);
printf(">>> Tracing instruction at 0x%" PRIx64, address);
uint8_t *tmp;
tmp = alloca(sizeof(uint8_t) * size);
// Read the line of code (opcode)
if (!uc_mem_read(uc, address, tmp, size))
{
printf("\t\t\t");
for (int i = 0; i < size; i++)
{
printf("%02x ", tmp[i]);
}
}
}
static void test_arm(void)
{
uc_engine *uc;
uc_err err;
uc_hook hk_trace1;
// Initialize emulator in ARM mode
err = uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc);
UC_ERR_CHECK(err);
// map 2MB memory for this emulation
err = uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
UC_ERR_CHECK(err);
// write machine code to be emulated to memory
err = uc_mem_write(uc, ADDRESS, ARM_CODE, sizeof(ARM_CODE) - 1);
UC_ERR_CHECK(err);
// initialize machine registers
int r2 = 0xbeef; // R2 register
uc_reg_write(uc, UC_ARM_REG_R2, &r2);
uc_hook_add(uc, &hk_trace1, UC_HOOK_CODE, hook_code, NULL, 1, 0); // to display and disassemble the code
uc_hook_add(uc, &hk_change_instruction, UC_HOOK_CODE, hook_code_change_instruction, NULL, ADDRESS_TO_CHANGE, ADDRESS_TO_CHANGE);
// finishing all the code.
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(ARM_CODE) - 1, 0, 0);
UC_ERR_CHECK(err);
remove_cache(uc);
printf ("Starting emulation again. Address range: 0x%x-0x%lx\n", ADDRESS_TO_CHANGE,ADDRESS + sizeof(ARM_CODE) - 1 );
err = uc_emu_start(uc,ADDRESS_TO_CHANGE, ADDRESS + sizeof(ARM_CODE) - 1,0,0);
UC_ERR_CHECK(err);
printf("\n>>> Emulation done.\n");
printf(">>> R2 should NOT 0x12 \n");
uc_reg_read(uc, UC_ARM_REG_R2, &r2);
printf(">>> R2=0x%x\n", r2);
uc_close(uc);
}
int main(int argc, char **argv, char **envp)
{
printf("\n\n\n ----- START/STOP emulation -----\n");
test_arm();
return 0;
} |
I think maybe #1580 is the proper way to do this. Let me think a bit how to make life easier... |
Another workaround #include <string.h>
#include <unicorn/unicorn.h>
#define ARM_CODE "\x0b\x10\xa0\xe3" \
"\x06\x20\xa0\xe3" \
"\x01\x20\x82\xE2" \
"\x01\x20\x82\xe0\x08\x80\xa0\xe1"
// int r2=0xbeef;
// 0x10000 0b 10 a0 e3 mov r1, #0xb
// 0x10004 f0 20 a0 e3 mov r2, #0x6 <- going to replace this with : mov r2, #0xf0
// 0x10008 01 20 82 E2 add r2,1
// 0x1000c 01 20 82 e0 add r2, r2, r1
// 0x10010 08 80 a0 e1 mov r8, r8
uc_hook hk_change_instruction;
#define ADDRESS 0x1000
#define ADDRESS_TO_CHANGE 0x1004
static uc_err _uc_err_check(uc_err err, const char *expr)
{
if (err)
{
fprintf(stderr, "Failed on %s with error: %s\n", expr, uc_strerror(err));
exit(1);
}
return err;
}
#define UC_ERR_CHECK(x) _uc_err_check(x, #x)
void remove_cache(uc_engine *uc)
{
uc_tb tb;
uc_err err;
err = uc_ctl_request_cache(uc, ADDRESS, &tb);
printf(">>> TB is cached at 0x%" PRIx64 " which has %" PRIu16" instructions with %" PRIu16 " bytes.\n",tb.pc, tb.icount, tb.size);
UC_ERR_CHECK(err);
printf(">>> removing cache from 0x%" PRIx64 " to 0x%" PRIx64 ".\n",tb.pc, tb.pc+ tb.size);
err = uc_ctl_remove_cache(uc, tb.pc, tb.pc + tb.size);
UC_ERR_CHECK(err);
}
static void hook_code_change_instruction(uc_engine *uc, uint64_t address, uint32_t size,
void *user_data)
{
uc_err err;
#define NEW_ARM_CODE "\xf0\x20\xA0\xE3"
printf("\nChanging instruction at 0x%x.\n", ADDRESS_TO_CHANGE);
printf("Was: mov r2, #0x06 [06 20 a0 e3]\n");
printf("Becomes: mov r2, #0xf0 [f0 20 a0 e3]\n");
printf ("Writing new memory \n");
uc_mem_write(uc, ADDRESS_TO_CHANGE, NEW_ARM_CODE, sizeof(NEW_ARM_CODE) - 1);
err=uc_hook_del(uc, hk_change_instruction);
UC_ERR_CHECK(err);
remove_cache(uc);
printf ("Starting emulation again. Address range: 0x%x-0x%lx\n", ADDRESS_TO_CHANGE,ADDRESS + sizeof(ARM_CODE) - 1 );
err = uc_emu_start(uc,ADDRESS_TO_CHANGE, ADDRESS + sizeof(ARM_CODE) - 1,0,0);
UC_ERR_CHECK(err);
printf ("Stopping emulation now\n");
err=uc_emu_stop(uc);
UC_ERR_CHECK(err);
}
static void hook_code(uc_engine *uc, uint64_t address, uint32_t size,
void *user_data)
{
int r1, r2;
uc_reg_read(uc, UC_ARM_REG_R1, &r1);
uc_reg_read(uc, UC_ARM_REG_R2, &r2);
printf(">>> R1=0x%x R2=0x%x\n", r1, r2);
printf(">>> Tracing instruction at 0x%" PRIx64, address);
uint8_t *tmp;
tmp = alloca(sizeof(uint8_t) * size);
// Read the line of code (opcode)
if (!uc_mem_read(uc, address, tmp, size))
{
printf("\t\t\t");
for (int i = 0; i < size; i++)
{
printf("%02x ", tmp[i]);
}
printf("\n\n");
}
}
static void test_arm(void)
{
uc_engine *uc;
uc_err err;
uc_hook hk_trace1;
// Initialize emulator in ARM mode
err = uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc);
UC_ERR_CHECK(err);
// map 2MB memory for this emulation
err = uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
UC_ERR_CHECK(err);
// write machine code to be emulated to memory
err = uc_mem_write(uc, ADDRESS, ARM_CODE, sizeof(ARM_CODE) - 1);
UC_ERR_CHECK(err);
// initialize machine registers
int r2 = 0xbeef; // R2 register
uc_reg_write(uc, UC_ARM_REG_R2, &r2);
uc_hook_add(uc, &hk_trace1, UC_HOOK_CODE, hook_code, NULL, 1, 0); // to display and disassemble the code
uc_hook_add(uc, &hk_change_instruction, UC_HOOK_CODE, hook_code_change_instruction, NULL, ADDRESS_TO_CHANGE, ADDRESS_TO_CHANGE);
// finishing all the code.
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(ARM_CODE) - 1, 0, 0);
UC_ERR_CHECK(err);
printf("\n>>> Emulation done.\n");
printf(">>> R2 should NOT 0x12 \n");
uc_reg_read(uc, UC_ARM_REG_R2, &r2);
printf(">>> R2=0x%x\n", r2);
uc_close(uc);
}
int main(int argc, char **argv, char **envp)
{
printf("\n\n\n ----- START/STOP emulation -----\n");
test_arm();
return 0;
} |
And the most promising ones: #include <string.h>
#include <unicorn/unicorn.h>
#define ARM_CODE "\x0b\x10\xa0\xe3" \
"\x06\x20\xa0\xe3" \
"\x01\x20\x82\xE2" \
"\x01\x20\x82\xe0\x08\x80\xa0\xe1"
// int r2=0xbeef;
// 0x10000 0b 10 a0 e3 mov r1, #0xb
// 0x10004 f0 20 a0 e3 mov r2, #0x6 <- going to replace this with : mov r2, #0xf0
// 0x10008 01 20 82 E2 add r2,1
// 0x1000c 01 20 82 e0 add r2, r2, r1
// 0x10010 08 80 a0 e1 mov r8, r8
uc_hook hk_change_instruction;
#define ADDRESS 0x1000
#define ADDRESS_TO_CHANGE 0x1004
static uc_err _uc_err_check(uc_err err, const char *expr)
{
if (err)
{
fprintf(stderr, "Failed on %s with error: %s\n", expr, uc_strerror(err));
exit(1);
}
return err;
}
#define UC_ERR_CHECK(x) _uc_err_check(x, #x)
void remove_cache(uc_engine *uc)
{
uc_tb tb;
uc_err err;
err = uc_ctl_request_cache(uc, ADDRESS, &tb);
printf(">>> TB is cached at 0x%" PRIx64 " which has %" PRIu16" instructions with %" PRIu16 " bytes.\n",tb.pc, tb.icount, tb.size);
UC_ERR_CHECK(err);
printf(">>> removing cache from 0x%" PRIx64 " to 0x%" PRIx64 ".\n",tb.pc, tb.pc+ tb.size);
err = uc_ctl_remove_cache(uc, tb.pc, tb.pc + tb.size);
UC_ERR_CHECK(err);
}
static void hook_code_change_instruction(uc_engine *uc, uint64_t address, uint32_t size,
void *user_data)
{
uc_err err;
#define NEW_ARM_CODE "\xf0\x20\xA0\xE3"
printf("\nChanging instruction at 0x%x.\n", ADDRESS_TO_CHANGE);
printf("Was: mov r2, #0x06 [06 20 a0 e3]\n");
printf("Becomes: mov r2, #0xf0 [f0 20 a0 e3]\n");
printf ("Writing new memory \n");
uc_mem_write(uc, ADDRESS_TO_CHANGE, NEW_ARM_CODE, sizeof(NEW_ARM_CODE) - 1);
err=uc_hook_del(uc, hk_change_instruction);
UC_ERR_CHECK(err);
remove_cache(uc);
printf ("Starting emulation again. Address range: 0x%x-0x%lx\n", ADDRESS_TO_CHANGE,ADDRESS + sizeof(ARM_CODE) - 1 );
err = uc_emu_start(uc,address, address + 4,0,0);
UC_ERR_CHECK(err);
address += 4;
err=uc_reg_write(uc, UC_ARM_REG_PC, &address);
UC_ERR_CHECK(err);
}
static void hook_code(uc_engine *uc, uint64_t address, uint32_t size,
void *user_data)
{
int r1, r2;
uc_reg_read(uc, UC_ARM_REG_R1, &r1);
uc_reg_read(uc, UC_ARM_REG_R2, &r2);
printf(">>> R1=0x%x R2=0x%x\n", r1, r2);
printf(">>> Tracing instruction at 0x%" PRIx64, address);
uint8_t *tmp;
tmp = alloca(sizeof(uint8_t) * size);
// Read the line of code (opcode)
if (!uc_mem_read(uc, address, tmp, size))
{
printf("\t\t\t");
for (int i = 0; i < size; i++)
{
printf("%02x ", tmp[i]);
}
printf("\n\n");
}
}
static void test_arm(void)
{
uc_engine *uc;
uc_err err;
uc_hook hk_trace1;
// Initialize emulator in ARM mode
err = uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc);
UC_ERR_CHECK(err);
// map 2MB memory for this emulation
err = uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
UC_ERR_CHECK(err);
// write machine code to be emulated to memory
err = uc_mem_write(uc, ADDRESS, ARM_CODE, sizeof(ARM_CODE) - 1);
UC_ERR_CHECK(err);
// initialize machine registers
int r2 = 0xbeef; // R2 register
uc_reg_write(uc, UC_ARM_REG_R2, &r2);
uc_hook_add(uc, &hk_trace1, UC_HOOK_CODE, hook_code, NULL, 1, 0); // to display and disassemble the code
uc_hook_add(uc, &hk_change_instruction, UC_HOOK_CODE, hook_code_change_instruction, NULL, ADDRESS_TO_CHANGE, ADDRESS_TO_CHANGE);
// finishing all the code.
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(ARM_CODE) - 1, 0, 0);
UC_ERR_CHECK(err);
printf("\n>>> Emulation done.\n");
printf(">>> R2 should NOT 0x12 \n");
uc_reg_read(uc, UC_ARM_REG_R2, &r2);
printf(">>> R2=0x%x\n", r2);
uc_close(uc);
}
int main(int argc, char **argv, char **envp)
{
printf("\n\n\n ----- START/STOP emulation -----\n");
test_arm();
return 0;
} Like your previous one: #include <string.h>
#include <unicorn/unicorn.h>
#define ARM_CODE "\x0b\x10\xa0\xe3" \
"\x06\x20\xa0\xe3" \
"\x01\x20\x82\xE2" \
"\x01\x20\x82\xe0\x08\x80\xa0\xe1"
// int r2=0xbeef;
// 0x10000 0b 10 a0 e3 mov r1, #0xb
// 0x10004 f0 20 a0 e3 mov r2, #0x6 <- going to replace this with : mov r2, #0xf0
// 0x10008 01 20 82 E2 add r2,1
// 0x1000c 01 20 82 e0 add r2, r2, r1
// 0x10010 08 80 a0 e1 mov r8, r8
uc_hook hk_change_instruction;
#define ADDRESS 0x1000
#define ADDRESS_TO_CHANGE 0x1004
static uc_err _uc_err_check(uc_err err, const char *expr)
{
if (err)
{
fprintf(stderr, "Failed on %s with error: %s\n", expr, uc_strerror(err));
exit(1);
}
return err;
}
#define UC_ERR_CHECK(x) _uc_err_check(x, #x)
void remove_cache(uc_engine *uc)
{
uc_tb tb;
uc_err err;
err = uc_ctl_request_cache(uc, ADDRESS, &tb);
printf(">>> TB is cached at 0x%" PRIx64 " which has %" PRIu16" instructions with %" PRIu16 " bytes.\n",tb.pc, tb.icount, tb.size);
UC_ERR_CHECK(err);
printf(">>> removing cache from 0x%" PRIx64 " to 0x%" PRIx64 ".\n",tb.pc, tb.pc+ tb.size);
err = uc_ctl_remove_cache(uc, tb.pc, tb.pc + tb.size);
UC_ERR_CHECK(err);
}
static void hook_code_change_instruction(uc_engine *uc, uint64_t address, uint32_t size,
void *user_data)
{
uc_err err;
#define NEW_ARM_CODE "\xf0\x20\xA0\xE3"
printf("\nChanging instruction at 0x%x.\n", ADDRESS_TO_CHANGE);
printf("Was: mov r2, #0x06 [06 20 a0 e3]\n");
printf("Becomes: mov r2, #0xf0 [f0 20 a0 e3]\n");
printf ("Writing new memory \n");
uc_mem_write(uc, ADDRESS_TO_CHANGE, NEW_ARM_CODE, sizeof(NEW_ARM_CODE) - 1);
err=uc_hook_del(uc, hk_change_instruction);
UC_ERR_CHECK(err);
remove_cache(uc);
err=uc_reg_write(uc, UC_ARM_REG_PC, &address);
UC_ERR_CHECK(err);
}
static void hook_code(uc_engine *uc, uint64_t address, uint32_t size,
void *user_data)
{
int r1, r2;
uc_reg_read(uc, UC_ARM_REG_R1, &r1);
uc_reg_read(uc, UC_ARM_REG_R2, &r2);
printf(">>> R1=0x%x R2=0x%x\n", r1, r2);
printf(">>> Tracing instruction at 0x%" PRIx64, address);
uint8_t *tmp;
tmp = alloca(sizeof(uint8_t) * size);
// Read the line of code (opcode)
if (!uc_mem_read(uc, address, tmp, size))
{
printf("\t\t\t");
for (int i = 0; i < size; i++)
{
printf("%02x ", tmp[i]);
}
printf("\n\n");
}
}
static void test_arm(void)
{
uc_engine *uc;
uc_err err;
uc_hook hk_trace1;
// Initialize emulator in ARM mode
err = uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc);
UC_ERR_CHECK(err);
// map 2MB memory for this emulation
err = uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
UC_ERR_CHECK(err);
// write machine code to be emulated to memory
err = uc_mem_write(uc, ADDRESS, ARM_CODE, sizeof(ARM_CODE) - 1);
UC_ERR_CHECK(err);
// initialize machine registers
int r2 = 0xbeef; // R2 register
uc_reg_write(uc, UC_ARM_REG_R2, &r2);
uc_hook_add(uc, &hk_trace1, UC_HOOK_CODE, hook_code, NULL, 1, 0); // to display and disassemble the code
uc_hook_add(uc, &hk_change_instruction, UC_HOOK_CODE, hook_code_change_instruction, NULL, ADDRESS_TO_CHANGE, ADDRESS_TO_CHANGE);
// finishing all the code.
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(ARM_CODE) - 1, 0, 0);
UC_ERR_CHECK(err);
printf("\n>>> Emulation done.\n");
printf(">>> R2 should NOT 0x12 \n");
uc_reg_read(uc, UC_ARM_REG_R2, &r2);
printf(">>> R2=0x%x\n", r2);
uc_close(uc);
}
int main(int argc, char **argv, char **envp)
{
printf("\n\n\n ----- START/STOP emulation -----\n");
test_arm();
return 0;
} |
In a word, you have a few workarounds:
Personally, I think 3 & 4 look clean enough and just documenting it somewhere should be fine for this issue. If you think it's okay, we can close this issue. |
That's fantastic. I've got it working now. I've ended up using number 1. It looks cleaner and easier to follow in my final code. |
I document this behavior here. Closed. |
I'm emulating some ARM code.
My program changes the next instruction in memory to a NOP. (
mov r8,r8
).It displays the correct op codes - but it runs the old instruction.
Instruction
Address: 0x08001064 opcodes: c3 71 : strb r3, [r0, #7]
value of
r3
is0x000000ce
Next instruction - (modified to do nothing once the
uc_emu_start
is called)Address: 0x08001066 opcode: c0 46 : mov r8, r8
Value of
r3
is0xe00000a0
r3
isn't touched - so it looks like it's using the old code.I guess my question is this: should it work??
The text was updated successfully, but these errors were encountered: