Add a new hook type UC_HOOK_EDGE_GENERATED and corresponding sample

This commit is contained in:
lazymio 2021-11-01 23:27:35 +01:00
parent b7e82d460c
commit c11b9aa5c3
No known key found for this signature in database
GPG Key ID: DFF27E34A47CB873
5 changed files with 80 additions and 10 deletions

View File

@ -157,10 +157,19 @@ typedef enum uc_hook_idx {
UC_HOOK_MEM_FETCH_IDX, UC_HOOK_MEM_FETCH_IDX,
UC_HOOK_MEM_READ_AFTER_IDX, UC_HOOK_MEM_READ_AFTER_IDX,
UC_HOOK_INSN_INVALID_IDX, UC_HOOK_INSN_INVALID_IDX,
UC_HOOK_EDGE_GENERATED_IDX,
UC_HOOK_MAX, UC_HOOK_MAX,
} uc_hook_idx; } uc_hook_idx;
// Copy the essential information from TranslationBlock
#define UC_TB_COPY(uc_tb, tb) \
do { \
(uc_tb)->pc = tb->pc; \
(uc_tb)->icount = tb->icount; \
(uc_tb)->size = tb->size; \
} while (0)
// The lowest 6 bits are used for hook type index. // The lowest 6 bits are used for hook type index.
#define UC_HOOK_IDX_MASK ((1 << 6) - 1) #define UC_HOOK_IDX_MASK ((1 << 6) - 1)

View File

@ -232,6 +232,22 @@ typedef uint32_t (*uc_cb_insn_in_t)(uc_engine *uc, uint32_t port, int size,
typedef void (*uc_cb_insn_out_t)(uc_engine *uc, uint32_t port, int size, typedef void (*uc_cb_insn_out_t)(uc_engine *uc, uint32_t port, int size,
uint32_t value, void *user_data); uint32_t value, void *user_data);
// Represent a TranslationBlock.
typedef struct uc_tb {
uint64_t pc;
uint16_t icount;
uint16_t size;
} uc_tb;
/*
Callback function for new edges between translation blocks.
@cur_tb: Current TB which is to be generated.
@prev_tb: The previous TB.
*/
typedef void (*uc_hook_edge_gen_t)(uc_engine *uc, uc_tb *cur_tb, uc_tb *prev_tb,
void *user_data);
/* /*
Callback function for MMIO read Callback function for MMIO read
@ -302,6 +318,12 @@ typedef enum uc_hook_type {
UC_HOOK_MEM_READ_AFTER = 1 << 13, UC_HOOK_MEM_READ_AFTER = 1 << 13,
// Hook invalid instructions exceptions. // Hook invalid instructions exceptions.
UC_HOOK_INSN_INVALID = 1 << 14, UC_HOOK_INSN_INVALID = 1 << 14,
// Hook on new edge generation. Could be useful in program analysis.
//
// NOTE: This is different from UC_HOOK_BLOCK in 2 ways:
// 1. The hook is called before executing code.
// 2. The hook is only called when generation is triggered.
UC_HOOK_EDGE_GENERATED = 1 << 15
} uc_hook_type; } uc_hook_type;
// Hook type for all events of unmapped memory access // Hook type for all events of unmapped memory access
@ -391,13 +413,6 @@ typedef enum uc_query_type {
// result = True) // result = True)
} uc_query_type; } uc_query_type;
// Represent a TranslationBlock.
typedef struct uc_tb {
uint64_t pc;
uint16_t icount;
uint16_t size;
} uc_tb;
// The implementation of uc_ctl is like what Linux ioctl does but slightly // The implementation of uc_ctl is like what Linux ioctl does but slightly
// different. // different.
// //

View File

@ -245,6 +245,10 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
TranslationBlock *tb; TranslationBlock *tb;
target_ulong cs_base, pc; target_ulong cs_base, pc;
uint32_t flags; uint32_t flags;
uc_tb cur_tb, prev_tb;
uc_engine *uc = cpu->uc;
struct list_item *cur;
struct hook *hook;
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
if (tb == NULL) { if (tb == NULL) {
@ -265,6 +269,23 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
if (last_tb) { if (last_tb) {
tb_add_jump(last_tb, tb_exit, tb); tb_add_jump(last_tb, tb_exit, tb);
} }
UC_TB_COPY(&cur_tb, tb);
if (last_tb) {
UC_TB_COPY(&prev_tb, last_tb);
for (cur = uc->hook[UC_HOOK_EDGE_GENERATED_IDX].head;
cur != NULL && (hook = (struct hook *)cur->data); cur = cur->next) {
if (hook->to_delete) {
continue;
}
if (HOOK_BOUND_CHECK(hook, (uint64_t)tb->pc)) {
((uc_hook_edge_gen_t)hook->callback)(uc, &cur_tb, &prev_tb, hook->user_data);
}
}
}
return tb; return tb;
} }

View File

@ -1049,9 +1049,7 @@ static uc_err uc_gen_tb(struct uc_struct *uc, uint64_t addr, uc_tb *out_tb)
} }
if (out_tb != NULL) { if (out_tb != NULL) {
out_tb->pc = tb->pc; UC_TB_COPY(out_tb, tb);
out_tb->size = tb->size;
out_tb->icount = tb->icount;
} }
return UC_ERR_OK; return UC_ERR_OK;

View File

@ -73,10 +73,17 @@ static void test_uc_ctl_read(void)
uc_close(uc); uc_close(uc);
} }
static void trace_new_edge(uc_engine *uc, uc_tb *cur, uc_tb *prev, void *data)
{
printf(">>> Getting a new edge from 0x%" PRIx64 " to 0x%" PRIx64 ".\n",
prev->pc + prev->size - 1, cur->pc);
}
void test_uc_ctl_exits() void test_uc_ctl_exits()
{ {
uc_engine *uc; uc_engine *uc;
uc_err err; uc_err err;
uc_hook h;
int r_eax, r_ebx; int r_eax, r_ebx;
uint64_t exits[] = {ADDRESS + 6, ADDRESS + 8}; uint64_t exits[] = {ADDRESS + 6, ADDRESS + 8};
@ -102,6 +109,14 @@ void test_uc_ctl_exits()
return; return;
} }
// We trace if any new edge is generated.
err = uc_hook_add(uc, &h, UC_HOOK_EDGE_GENERATED, trace_new_edge, NULL, 0,
-1);
if (err) {
printf("Failed on uc_hook_add() with error returned: %u\n", err);
return;
}
// Enable multiple exits. // Enable multiple exits.
err = uc_ctl_exits_enabled(uc, true); err = uc_ctl_exits_enabled(uc, true);
if (err) { if (err) {
@ -183,6 +198,7 @@ static void test_uc_ctl_tb_cache()
uc_engine *uc; uc_engine *uc;
uc_err err; uc_err err;
uc_tb tb; uc_tb tb;
uc_hook h;
char code[CODE_LEN]; char code[CODE_LEN];
double standard, cached, evicted; double standard, cached, evicted;
@ -211,6 +227,17 @@ static void test_uc_ctl_tb_cache()
return; return;
} }
// We trace if any new edge is generated.
// Note: In this sample, there is only **one** basic block while muliple
// translation blocks is generated due to QEMU tcg buffer limit. In this
// case, we don't consider it as a new edge.
err = uc_hook_add(uc, &h, UC_HOOK_EDGE_GENERATED, trace_new_edge, NULL, 0,
-1);
if (err) {
printf("Failed on uc_hook_add() with error returned: %u\n", err);
return;
}
// Do emulation without any cache. // Do emulation without any cache.
standard = time_emulation(uc, ADDRESS, ADDRESS + sizeof(code) - 1); standard = time_emulation(uc, ADDRESS, ADDRESS + sizeof(code) - 1);