import Unicorn2
This commit is contained in:
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from unicorn import *
|
||||
from unicorn.arm_const import *
|
||||
|
||||
import regress
|
||||
|
||||
class APSRAccess(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
code = (
|
||||
b'\x00\x00\xa0\xe1' + # 0: mov r0, r0
|
||||
b'\x08\x10\x9f\xe5' + # 4: ldr r1, [pc, #8]
|
||||
b'\x01\xf0\x28\xe1' + # 8: 01 f0 28 e1 msr apsr_nzcvq, r1
|
||||
b'\x00\x00\xa0\xe1' + # c: mov r0, r0
|
||||
b'\x00\x00\xa0\xe1' + # 10: mov r0, r0
|
||||
b'\x00\x00\x00\xff') # 14: data for inst @4
|
||||
|
||||
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
|
||||
uc.mem_map(0x1000, 0x1000)
|
||||
uc.mem_write(0x1000, code) # bxeq lr; mov r0, r0
|
||||
|
||||
uc.reg_write(UC_ARM_REG_APSR, 0)
|
||||
uc.emu_start(0x1000, 0x100c)
|
||||
|
||||
self.assertEqual(uc.reg_read(UC_ARM_REG_APSR), 0xf8000000)
|
||||
self.assertEqual(uc.reg_read(UC_ARM_REG_APSR_NZCV), 0xf0000000)
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
||||
52
tests/regress/arm_memcpy_neon.py
Normal file
52
tests/regress/arm_memcpy_neon.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from unicorn import *
|
||||
from unicorn.arm_const import *
|
||||
|
||||
# .text:0001F894 ADD PC, PC, R3
|
||||
# .text:0001F898 ; ---------------------------------------------------------------------------
|
||||
# .text:0001F898 VLD1.8 {D0}, [R1]!
|
||||
# .text:0001F89C VST1.8 {D0}, [R12]!
|
||||
# .text:0001F8A0 VLD1.8 {D0}, [R1]!
|
||||
# .text:0001F8A4 VST1.8 {D0}, [R12]!
|
||||
# .text:0001F8A8 VLD1.8 {D0}, [R1]!
|
||||
# .text:0001F8AC VST1.8 {D0}, [R12]!
|
||||
# .text:0001F8B0 VLD1.8 {D0}, [R1]!
|
||||
# .text:0001F8B4 VST1.8 {D0}, [R12]!
|
||||
# .text:0001F8B8 VLD1.8 {D0}, [R1]!
|
||||
# .text:0001F8BC VST1.8 {D0}, [R12]!
|
||||
# .text:0001F8C0 VLD1.8 {D0}, [R1]!
|
||||
# .text:0001F8C4 VST1.8 {D0}, [R12]!
|
||||
# .text:0001F8C8 VLD1.8 {D0}, [R1]!
|
||||
# .text:0001F8CC VST1.8 {D0}, [R12]!
|
||||
# .text:0001F8D0 TST R2, #4
|
||||
# .text:0001F8D4 LDRNE R3, [R1],#4
|
||||
# .text:0001F8D8 STRNE R3, [R12],#4
|
||||
# .text:0001F8DC MOVS R2, R2,LSL#31
|
||||
# .text:0001F8E0 LDRHCS R3, [R1],#2
|
||||
# .text:0001F8E4 LDRBNE R1, [R1]
|
||||
# .text:0001F8E8 STRHCS R3, [R12],#2
|
||||
# .text:0001F8EC STRBNE R1, [R12]
|
||||
shellcode = [0x3, 0xf0, 0x8f, 0xe0, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0xd, 0x7, 0x21, 0xf4, 0xd, 0x7, 0xc, 0xf4, 0x4, 0x0, 0x12, 0xe3, 0x4, 0x30, 0x91, 0x14, 0x4, 0x30, 0x8c, 0x14, 0x82, 0x2f, 0xb0, 0xe1, 0xb2, 0x30, 0xd1, 0x20, 0x0, 0x10, 0xd1, 0x15, 0xb2, 0x30, 0xcc, 0x20, 0x0, 0x10, 0xcc, 0x15]
|
||||
base = 0x1F894
|
||||
from_address = 0x1000
|
||||
to_address = 0x2000
|
||||
cplen = 8
|
||||
bs = b"c8"*cplen
|
||||
|
||||
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
|
||||
uc.mem_map(from_address, 0x1000)
|
||||
uc.mem_map(to_address, 0x1000)
|
||||
uc.mem_map(0x1F000, 0x1000)
|
||||
uc.mem_write(from_address, bs)
|
||||
uc.mem_write(base, bytes(shellcode))
|
||||
uc.reg_write(UC_ARM_REG_R12, to_address)
|
||||
uc.reg_write(UC_ARM_REG_R1, from_address)
|
||||
uc.reg_write(UC_ARM_REG_R2, cplen)
|
||||
uc.reg_write(UC_ARM_REG_R3, 0x24)
|
||||
# enable_vfp
|
||||
uc.reg_write(UC_ARM_REG_C1_C0_2, uc.reg_read(UC_ARM_REG_C1_C0_2) | (0xf << 20))
|
||||
uc.reg_write(UC_ARM_REG_FPEXC, 0x40000000)
|
||||
|
||||
uc.emu_start(base, base+len(shellcode))
|
||||
fr = uc.mem_read(from_address, len(bs))
|
||||
to = uc.mem_read(to_address, len(bs))
|
||||
print(f"memcpy result:\nfrom: {bytes(fr)}\nto: {bytes(to)}")
|
||||
15
tests/regress/arm_wfi_first_insn_of_tb.py
Normal file
15
tests/regress/arm_wfi_first_insn_of_tb.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from unicorn import *
|
||||
from unicorn.arm_const import *
|
||||
|
||||
# ADD R0, R10, R0;
|
||||
# B L0;
|
||||
# L0:
|
||||
# ADD R0, R10, R0; <--- we stop at here, the first instruction of the next TB.
|
||||
|
||||
code = b'\x00\x00\x8a\xe0\xff\xff\xff\xea\x00\x00\x8a\xe0'
|
||||
address = 0x1000
|
||||
|
||||
mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
|
||||
mu.mem_map(address, 0x1000)
|
||||
mu.mem_write(address, code)
|
||||
mu.emu_start(address, address + len(code) - 4)
|
||||
@@ -1,39 +0,0 @@
|
||||
import regress
|
||||
from unicorn import Uc, UC_ARCH_X86, UC_MODE_64, UC_HOOK_CODE
|
||||
|
||||
CODE = b"\x90" * 3
|
||||
CODE_ADDR = 0x1000
|
||||
|
||||
|
||||
class HookCounter(object):
|
||||
"""Counts number of hook calls."""
|
||||
|
||||
def __init__(self):
|
||||
self.hook_calls = 0
|
||||
|
||||
def bad_code_hook(self, uc, address, size, data):
|
||||
self.hook_calls += 1
|
||||
raise ValueError("Something went wrong")
|
||||
|
||||
def good_code_hook(self, uc, address, size, data):
|
||||
self.hook_calls += 1
|
||||
|
||||
|
||||
class TestExceptionInHook(regress.RegressTest):
|
||||
|
||||
def test_exception_in_hook(self):
|
||||
uc = Uc(UC_ARCH_X86, UC_MODE_64)
|
||||
uc.mem_map(CODE_ADDR, 0x1000)
|
||||
uc.mem_write(CODE_ADDR, CODE)
|
||||
|
||||
counter = HookCounter()
|
||||
uc.hook_add(UC_HOOK_CODE, counter.bad_code_hook, begin=CODE_ADDR, end=CODE_ADDR + len(CODE))
|
||||
uc.hook_add(UC_HOOK_CODE, counter.good_code_hook, begin=CODE_ADDR, end=CODE_ADDR + len(CODE))
|
||||
|
||||
self.assertRaises(ValueError, uc.emu_start, CODE_ADDR, CODE_ADDR + len(CODE))
|
||||
# Make sure hooks calls finish before raising (hook_calls == 2)
|
||||
self.assertEqual(counter.hook_calls, 2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regress.main()
|
||||
@@ -1,39 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
uint64_t starts[] = {0x10000000, 0x110004000ll};
|
||||
|
||||
int main(int argc, char **argv, char **envp) {
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
int i;
|
||||
// Initialize emulator in X86-64bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
|
||||
if (err) {
|
||||
printf("Failed on uc_open() with error returned: %u\n", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < (sizeof(starts) / sizeof(uint64_t)); i++) {
|
||||
uc_mem_map(uc, starts[i], 4096, UC_PROT_ALL);
|
||||
}
|
||||
|
||||
uint32_t count;
|
||||
uc_mem_region *regions;
|
||||
int err_count = 0;
|
||||
err = uc_mem_regions(uc, ®ions, &count);
|
||||
if (err == UC_ERR_OK) {
|
||||
for (i = 0; i < count; i++) {
|
||||
fprintf(stderr, "region %d: 0x%"PRIx64"-0x%"PRIx64" (%d)\n", i, regions[i].begin, regions[i].end - 1, regions[i].perms);
|
||||
if (regions[i].begin != starts[i]) {
|
||||
err_count++;
|
||||
fprintf(stderr, " ERROR: region start does not match requested start address, expected 0x%"PRIx64", found 0x%"PRIx64"\n",
|
||||
starts[i], regions[i].begin);
|
||||
}
|
||||
}
|
||||
uc_free(regions);
|
||||
}
|
||||
|
||||
uc_close(uc);
|
||||
return err_count;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
if (err) {
|
||||
printf("not ok - Failed on uc_open() with error returned: %u\n", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_ALL);
|
||||
if (err) {
|
||||
printf("not ok - Failed on uc_mem_map() with error returned: %u\n", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uc_mem_map(uc, 0x4000, 0x1000, UC_PROT_ALL);
|
||||
if (err) {
|
||||
printf("not ok - Failed on uc_mem_map() with error returned: %u\n", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = uc_mem_unmap(uc, 0x4000, 0x1000);
|
||||
if (err) {
|
||||
printf("not ok - Failed on uc_mem_unmap() with error returned: %u\n", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = uc_mem_unmap(uc, 0x4000, 0x1000);
|
||||
if (!err) {
|
||||
printf("not ok - second unmap succeeded\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Tests OK\n");
|
||||
uc_close(uc);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
/*
|
||||
Executable memory regions demo / unit test
|
||||
|
||||
Copyright(c) 2015 Chris Eagle
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
version 2 as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
*/
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
unsigned char PROGRAM[] =
|
||||
"\xeb\x45\x5e\x81\xe6\x00\xf0\xff\xff\x40\x40\x40\x40\x40\x40\x40"
|
||||
"\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40"
|
||||
"\x40\x40\x40\x40\x40\x40\x40\x89\xf7\x81\xc7\x00\x00\x10\x00\xb9"
|
||||
"\x4c\x00\x00\x00\x81\xff\x00\x00\x40\x00\x75\x01\xf4\xf3\xa4\x81"
|
||||
"\xe7\x00\xf0\xff\xff\xff\xe7\xe8\xb6\xff\xff\xff";
|
||||
// total size: 76 bytes
|
||||
|
||||
/*
|
||||
bits 32
|
||||
|
||||
; assumes r-x section at 0x100000
|
||||
; assumes rw- section at 0x200000
|
||||
; assumes r-- section at 0x300000
|
||||
; also needs an initialized stack
|
||||
|
||||
start:
|
||||
jmp bottom
|
||||
top:
|
||||
pop esi
|
||||
and esi, ~0xfff
|
||||
times 30 inc eax
|
||||
mov edi, esi
|
||||
add edi, 0x100000
|
||||
mov ecx, end - start
|
||||
rep movsb
|
||||
and edi, ~0xfff
|
||||
cmp edi, 0x400000
|
||||
jnz next_block
|
||||
hlt
|
||||
next_block:
|
||||
jmp edi
|
||||
bottom:
|
||||
call top
|
||||
end:
|
||||
*/
|
||||
|
||||
int test_num = 0;
|
||||
uint32_t tests[] = {
|
||||
0x41414141,
|
||||
0x43434343,
|
||||
0x45454545
|
||||
};
|
||||
|
||||
static int log_num = 1;
|
||||
|
||||
#define CODE_SECTION 0x100000
|
||||
#define CODE_SIZE 0x1000
|
||||
|
||||
// callback for tracing instruction
|
||||
static void hook_code(uc_engine *uc, uint64_t addr, uint32_t size, void *user_data)
|
||||
{
|
||||
uint8_t opcode;
|
||||
|
||||
if (uc_mem_read(uc, addr, &opcode, 1) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_mem_read fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr);
|
||||
}
|
||||
|
||||
// printf("ok %d - uc_mem_read for opcode at address 0x%" PRIx64 "\n", log_num++, addr);
|
||||
switch (opcode) {
|
||||
case 0xf4: //hlt
|
||||
printf("# Handling HLT\n");
|
||||
if (uc_emu_stop(uc) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_emu_stop fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr);
|
||||
_exit(-1);
|
||||
} else {
|
||||
printf("ok %d - hlt encountered, uc_emu_stop called\n", log_num++);
|
||||
}
|
||||
break;
|
||||
default: //all others
|
||||
// printf("# Handling OTHER\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// callback for tracing memory access (READ or WRITE)
|
||||
static void hook_mem_write(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t addr, int size, int64_t value, void *user_data)
|
||||
{
|
||||
printf("# write to memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value);
|
||||
}
|
||||
|
||||
// callback for tracing invalid memory access (READ or WRITE)
|
||||
static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t addr, int size, int64_t value, void *user_data)
|
||||
{
|
||||
switch(type) {
|
||||
default:
|
||||
printf("not ok %d - memory invalid type: %d at 0x%" PRIx64 "\n", log_num++, type, addr);
|
||||
return false;
|
||||
case UC_MEM_FETCH_PROT:
|
||||
printf("# Fetch from non-executable memory at 0x%"PRIx64 "\n", addr);
|
||||
|
||||
//make page executable
|
||||
if (uc_mem_protect(uc, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_EXEC) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_mem_protect fail for address: 0x%" PRIx64 "\n", log_num++, addr);
|
||||
} else {
|
||||
printf("ok %d - uc_mem_protect success at 0x%" PRIx64 "\n", log_num++, addr);
|
||||
}
|
||||
return true;
|
||||
case UC_MEM_WRITE_PROT:
|
||||
printf("# write to non-writeable memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value);
|
||||
|
||||
if (uc_mem_protect(uc, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_mem_protect fail during hook_mem_invalid callback, addr: 0x%" PRIx64 "\n", log_num++, addr);
|
||||
} else {
|
||||
printf("ok %d - uc_mem_protect success\n", log_num++);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1, trace2;
|
||||
uc_err err;
|
||||
uint32_t esp, eip;
|
||||
int32_t buf1[1024], buf2[1024], readbuf[1024];
|
||||
int i;
|
||||
|
||||
//don't really care about quality of randomness
|
||||
srand(time(NULL));
|
||||
for (i = 0; i < 1024; i++) {
|
||||
buf1[i] = rand();
|
||||
buf2[i] = rand();
|
||||
}
|
||||
|
||||
printf("# Memory protect test\n");
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
if (err) {
|
||||
printf("not ok %d - Failed on uc_open() with error returned: %u\n", log_num++, err);
|
||||
return 1;
|
||||
} else {
|
||||
printf("ok %d - uc_open() success\n", log_num++);
|
||||
}
|
||||
|
||||
uc_mem_map(uc, 0x100000, 0x1000, UC_PROT_READ | UC_PROT_EXEC);
|
||||
uc_mem_map(uc, 0x1ff000, 0x2000, UC_PROT_READ | UC_PROT_WRITE);
|
||||
uc_mem_map(uc, 0x300000, 0x2000, UC_PROT_READ);
|
||||
uc_mem_map(uc, 0xf00000, 0x1000, UC_PROT_READ | UC_PROT_WRITE);
|
||||
|
||||
esp = 0xf00000 + 0x1000;
|
||||
|
||||
// Setup stack pointer
|
||||
if (uc_reg_write(uc, UC_X86_REG_ESP, &esp)) {
|
||||
printf("not ok %d - Failed to set esp. quit!\n", log_num++);
|
||||
return 2;
|
||||
} else {
|
||||
printf("ok %d - ESP set\n", log_num++);
|
||||
}
|
||||
|
||||
// fill in sections that shouldn't get touched
|
||||
if (uc_mem_write(uc, 0x1ff000, buf1, sizeof(buf1))) {
|
||||
printf("not ok %d - Failed to write random buffer 1 to memory, quit!\n", log_num++);
|
||||
return 3;
|
||||
} else {
|
||||
printf("ok %d - Random buffer 1 written to memory\n", log_num++);
|
||||
}
|
||||
|
||||
if (uc_mem_write(uc, 0x301000, buf2, sizeof(buf2))) {
|
||||
printf("not ok %d - Failed to write random buffer 2 to memory, quit!\n", log_num++);
|
||||
return 4;
|
||||
} else {
|
||||
printf("ok %d - Random buffer 2 written to memory\n", log_num++);
|
||||
}
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
if (uc_mem_write(uc, 0x100000, PROGRAM, sizeof(PROGRAM))) {
|
||||
printf("not ok %d - Failed to write emulation code to memory, quit!\n", log_num++);
|
||||
return 5;
|
||||
} else {
|
||||
printf("ok %d - Program written to memory\n", log_num++);
|
||||
}
|
||||
|
||||
if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed to install UC_HOOK_CODE ucr\n", log_num++);
|
||||
return 6;
|
||||
} else {
|
||||
printf("ok %d - UC_HOOK_CODE installed\n", log_num++);
|
||||
}
|
||||
|
||||
// intercept memory write events
|
||||
if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL, 1, 0) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE ucr\n", log_num++);
|
||||
return 7;
|
||||
} else {
|
||||
printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++);
|
||||
}
|
||||
|
||||
// intercept invalid memory events
|
||||
if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE_PROT | UC_HOOK_MEM_FETCH_PROT, hook_mem_invalid, NULL, 1, 0) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed to install memory invalid handler\n", log_num++);
|
||||
return 8;
|
||||
} else {
|
||||
printf("ok %d - memory invalid handler installed\n", log_num++);
|
||||
}
|
||||
|
||||
// emulate machine code until told to stop by hook_code
|
||||
printf("# BEGIN execution\n");
|
||||
err = uc_emu_start(uc, 0x100000, 0x400000, 0, 0);
|
||||
if (err != UC_ERR_OK) {
|
||||
printf("not ok %d - Failure on uc_emu_start() with error %u:%s\n", log_num++, err, uc_strerror(err));
|
||||
return 9;
|
||||
} else {
|
||||
printf("ok %d - uc_emu_start complete\n", log_num++);
|
||||
}
|
||||
printf("# END execution\n");
|
||||
|
||||
// get ending EIP
|
||||
if (uc_reg_read(uc, UC_X86_REG_EIP, &eip)) {
|
||||
printf("not ok %d - Failed to read eip.\n", log_num++);
|
||||
} else {
|
||||
printf("ok %d - Ending EIP 0x%x\n", log_num++, eip);
|
||||
}
|
||||
|
||||
//make sure that random blocks didn't get nuked
|
||||
// fill in sections that shouldn't get touched
|
||||
if (uc_mem_read(uc, 0x1ff000, readbuf, sizeof(readbuf))) {
|
||||
printf("not ok %d - Failed to read random buffer 1 from memory\n", log_num++);
|
||||
} else {
|
||||
printf("ok %d - Random buffer 1 read from memory\n", log_num++);
|
||||
if (memcmp(buf1, readbuf, 4096)) {
|
||||
printf("not ok %d - Random buffer 1 contents are incorrect\n", log_num++);
|
||||
} else {
|
||||
printf("ok %d - Random buffer 1 contents are correct\n", log_num++);
|
||||
}
|
||||
}
|
||||
|
||||
if (uc_mem_read(uc, 0x301000, readbuf, sizeof(readbuf))) {
|
||||
printf("not ok %d - Failed to read random buffer 2 from memory\n", log_num++);
|
||||
} else {
|
||||
printf("ok %d - Random buffer 2 read from memory\n", log_num++);
|
||||
if (memcmp(buf2, readbuf, 4096)) {
|
||||
printf("not ok %d - Random buffer 2 contents are incorrect\n", log_num++);
|
||||
} else {
|
||||
printf("ok %d - Random buffer 2 contents are correct\n", log_num++);
|
||||
}
|
||||
}
|
||||
|
||||
if (uc_close(uc) == UC_ERR_OK) {
|
||||
printf("ok %d - uc_close complete\n", log_num++);
|
||||
} else {
|
||||
printf("not ok %d - uc_close complete\n", log_num++);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
|
||||
uint64_t baseranges[] = {0,0,0,0};
|
||||
int step =0;
|
||||
|
||||
uint64_t urnd(){
|
||||
uint64_t rnd = rand();
|
||||
rnd = rnd << 32;
|
||||
rnd += rand();
|
||||
return rnd;
|
||||
}
|
||||
uint64_t get_addr(){
|
||||
uint64_t base = ((uint64_t)urnd())%4;
|
||||
uint64_t addr= baseranges[base] + urnd()%(4096*10);
|
||||
return addr;
|
||||
}
|
||||
|
||||
uint64_t get_aligned_addr(){
|
||||
uint64_t addr = get_addr();
|
||||
return addr - (addr % 4096);
|
||||
}
|
||||
|
||||
uint64_t get_len(){
|
||||
uint64_t len = (urnd() % (4096*5))+1;
|
||||
return len;
|
||||
}
|
||||
|
||||
uint64_t get_aligned_len(){
|
||||
uint64_t len = get_len();
|
||||
len = len - (len %4096);
|
||||
len = ((len == 0) ? 4096 : len);
|
||||
return len;
|
||||
}
|
||||
|
||||
void perform_map_step(uc_engine *uc){
|
||||
uint64_t addr = get_aligned_addr();
|
||||
uint64_t len = get_aligned_len();
|
||||
printf("map(uc,0x%"PRIx64",0x%"PRIx64"); //%d\n", addr, len, step);
|
||||
uc_mem_map(uc, addr, len, UC_PROT_READ | UC_PROT_WRITE);
|
||||
}
|
||||
|
||||
void perform_unmap_step(uc_engine *uc){
|
||||
uint64_t addr = get_aligned_addr();
|
||||
uint64_t len = get_aligned_len();
|
||||
printf("unmap(uc,0x%"PRIx64",0x%"PRIx64"); //%d\n", addr, len, step);
|
||||
uc_mem_unmap(uc, addr, len);
|
||||
}
|
||||
|
||||
void perform_write_step(uc_engine *uc){
|
||||
char buff[4096*4];
|
||||
memset((void *)buff, 0, 4096*4);
|
||||
uint64_t addr = get_addr();
|
||||
uint64_t len = get_len()%(4096*3);
|
||||
printf("write(uc,0x%"PRIx64",0x%"PRIx64"); //%d\n", addr, len, step);
|
||||
uc_mem_write(uc, addr, buff, len);
|
||||
}
|
||||
|
||||
void perform_read_step(uc_engine *uc){
|
||||
char buff[4096*4];
|
||||
uint64_t addr = get_addr();
|
||||
uint64_t len = get_len()%(4096*4);
|
||||
printf("read(uc,0x%"PRIx64",0x%"PRIx64"); //%d\n", addr, len, step);
|
||||
uc_mem_read(uc, addr, buff, len);
|
||||
}
|
||||
|
||||
void perform_fuzz_step(uc_engine *uc){
|
||||
switch( ((uint32_t)rand())%4 ){
|
||||
case 0: perform_map_step(uc); break;
|
||||
case 1: perform_unmap_step(uc); break;
|
||||
case 2: perform_read_step(uc); break;
|
||||
case 3: perform_write_step(uc); break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
if(argc<2){
|
||||
printf("usage: mem_fuzz $seed\n");
|
||||
return 1;
|
||||
}
|
||||
int seed = atoi(argv[1]);
|
||||
int i = 0;
|
||||
|
||||
//don't really care about quality of randomness
|
||||
srand(seed);
|
||||
printf("running with seed %d\n",seed);
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
|
||||
if (err) {
|
||||
printf("Failed on uc_open() with error returned: %u\n", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for(i = 0; i < 2048; i++){
|
||||
step++;
|
||||
perform_fuzz_step(uc);
|
||||
}
|
||||
// fill in sections that shouldn't get touched
|
||||
|
||||
if (uc_close(uc) != UC_ERR_OK) {
|
||||
printf("Failed on uc_close\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#include <unicorn/unicorn.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
uc_engine *u;
|
||||
uc_err err;
|
||||
|
||||
printf("mem_map_0x100000000.c \n");
|
||||
|
||||
if ((err = uc_open(UC_ARCH_X86, UC_MODE_32, &u)) != UC_ERR_OK) {
|
||||
printf("uc_open() failed: %s\n", uc_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((err = uc_mem_map(u, 0x100000000, 0x002c0000, UC_PROT_ALL)) != UC_ERR_OK) {
|
||||
printf("uc_mem_map() failed: %s\n", uc_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
if ((err = uc_mem_map(u, 0x0018D000, 0x00006000, UC_PROT_ALL)) != UC_ERR_OK) {
|
||||
printf("uc_mem_map() failed: %s\n", uc_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((err = uc_close(u)) != UC_ERR_OK) {
|
||||
printf("uc_close() failed: %s\n", uc_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Success.\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#include <unicorn/unicorn.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
uc_engine *u;
|
||||
uc_err err;
|
||||
if ((err = uc_open(UC_ARCH_X86, UC_MODE_32, &u)) != UC_ERR_OK) {
|
||||
printf("uc_open() failed: %s\n", uc_strerror(err));
|
||||
}
|
||||
printf("Trying large map.\n");
|
||||
if ((err = uc_mem_map(u, 0x60802000, (unsigned) 0x28bd211200004000, UC_PROT_ALL)) != UC_ERR_OK) {
|
||||
printf("uc_mem_map() failed: %s\n", uc_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
printf("Success.\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
#define ADDRESS1 0x10000000
|
||||
#define ADDRESS2 0x20000000
|
||||
#define SIZE (80 * 1024 * 1024)
|
||||
|
||||
static void VM_exec()
|
||||
{
|
||||
int c;
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
|
||||
// Initialize emulator in X86-64bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
if(err)
|
||||
{
|
||||
printf("Failed on uc_open() with error returned: %s\n", uc_strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
repeat:
|
||||
err = uc_mem_map(uc, ADDRESS1, SIZE, UC_PROT_ALL);
|
||||
if(err != UC_ERR_OK)
|
||||
{
|
||||
printf("Failed to map memory %s\n", uc_strerror(err));
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = uc_mem_map(uc, ADDRESS2, SIZE, UC_PROT_ALL);
|
||||
if(err != UC_ERR_OK)
|
||||
{
|
||||
printf("Failed to map memory %s\n", uc_strerror(err));
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = uc_mem_unmap(uc, ADDRESS1, SIZE);
|
||||
if(err != UC_ERR_OK)
|
||||
{
|
||||
printf("Failed to unmap memory %s\n", uc_strerror(err));
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = uc_mem_unmap(uc, ADDRESS2, SIZE);
|
||||
if(err != UC_ERR_OK)
|
||||
{
|
||||
printf("Failed to unmap memory %s\n", uc_strerror(err));
|
||||
goto err;
|
||||
}
|
||||
|
||||
for(;;)
|
||||
{
|
||||
c = getchar(); //pause here and analyse memory usage before exiting with a program like VMMap;
|
||||
if(c != 'e')
|
||||
goto repeat;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
err:
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
VM_exec();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,300 +0,0 @@
|
||||
/*
|
||||
uc_mem_protect demo / unit test
|
||||
|
||||
Copyright(c) 2015 Chris Eagle
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
version 2 as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
*/
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
unsigned char PROGRAM[] =
|
||||
"\xc7\x05\x00\x00\x20\x00\x41\x41\x41\x41\x90\xc7\x05\x00\x00\x20"
|
||||
"\x00\x42\x42\x42\x42\xc7\x05\x00\x00\x30\x00\x43\x43\x43\x43\x90"
|
||||
"\xc7\x05\x00\x00\x30\x00\x44\x44\x44\x44\xc7\x05\x00\x00\x40\x00"
|
||||
"\x45\x45\x45\x45\x90\xc7\x05\x00\x00\x40\x00\x46\x46\x46\x46\xc7"
|
||||
"\x05\x00\xf8\x3f\x00\x47\x47\x47\x47\xc7\x05\x00\x18\x40\x00\x48"
|
||||
"\x48\x48\x48\xf4";
|
||||
// total size: 84 bytes
|
||||
|
||||
/*
|
||||
bits 32
|
||||
|
||||
; assumes code section at 0x100000
|
||||
; assumes data section at 0x200000, initially rw
|
||||
; assumes data section at 0x300000, initially rw
|
||||
; assumes data section at 0x400000, initially rw
|
||||
|
||||
; with installed hooks unmaps or maps on each nop
|
||||
|
||||
mov dword [0x200000], 0x41414141
|
||||
nop ; mark it RO
|
||||
mov dword [0x200000], 0x42424242
|
||||
|
||||
mov dword [0x300000], 0x43434343
|
||||
nop ; mark it RO
|
||||
mov dword [0x300000], 0x44444444
|
||||
|
||||
mov dword [0x400000], 0x45454545
|
||||
nop ; mark it RO
|
||||
mov dword [0x400000], 0x46464646
|
||||
mov dword [0x3ff800], 0x47474747 ; make sure surrounding areas remained RW
|
||||
mov dword [0x401800], 0x48484848 ; make sure surrounding areas remained RW
|
||||
|
||||
hlt ; tell hook function we are done
|
||||
*/
|
||||
|
||||
int test_num = 0;
|
||||
uint32_t tests[] = {
|
||||
0x41414141,
|
||||
0x43434343,
|
||||
0x45454545
|
||||
};
|
||||
|
||||
static int log_num = 1;
|
||||
|
||||
#define CODE_SECTION 0x100000
|
||||
#define CODE_SIZE 0x1000
|
||||
|
||||
// callback for tracing instruction
|
||||
static void hook_code(uc_engine *uc, uint64_t addr, uint32_t size, void *user_data)
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint32_t testval;
|
||||
if (uc_mem_read(uc, addr, &opcode, 1) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_mem_read fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr);
|
||||
}
|
||||
printf("ok %d - uc_mem_read for opcode at address 0x%" PRIx64 "\n", log_num++, addr);
|
||||
switch (opcode) {
|
||||
case 0x90: //nop
|
||||
printf("# Handling NOP\n");
|
||||
if (uc_mem_read(uc, 0x200000 + test_num * 0x100000, &testval, sizeof(testval)) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_mem_read fail for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000);
|
||||
} else {
|
||||
printf("ok %d - good uc_mem_read for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000);
|
||||
printf("# uc_mem_read for test %d\n", test_num);
|
||||
|
||||
if (testval == tests[test_num]) {
|
||||
printf("ok %d - passed test %d\n", log_num++, test_num);
|
||||
} else {
|
||||
printf("not ok %d - failed test %d\n", log_num++, test_num);
|
||||
printf("# Expected: 0x%x\n",tests[test_num]);
|
||||
printf("# Received: 0x%x\n", testval);
|
||||
}
|
||||
}
|
||||
if (uc_mem_protect(uc, 0x200000 + test_num * 0x100000, 0x1000, UC_PROT_READ) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_mem_protect fail during hook_code callback, addr: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000);
|
||||
} else {
|
||||
printf("ok %d - uc_mem_protect success\n", log_num++);
|
||||
}
|
||||
test_num++;
|
||||
break;
|
||||
case 0xf4: //hlt
|
||||
printf("# Handling HLT\n");
|
||||
if (uc_emu_stop(uc) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_emu_stop fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr);
|
||||
_exit(-1);
|
||||
} else {
|
||||
printf("ok %d - hlt encountered, uc_emu_stop called\n", log_num++);
|
||||
}
|
||||
break;
|
||||
default: //all others
|
||||
printf("# Handling OTHER\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// callback for tracing memory access (READ or WRITE)
|
||||
static void hook_mem_write(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t addr, int size, int64_t value, void *user_data)
|
||||
{
|
||||
printf("# write to memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value);
|
||||
}
|
||||
|
||||
// callback for tracing invalid memory access (READ or WRITE)
|
||||
static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t addr, int size, int64_t value, void *user_data)
|
||||
{
|
||||
uint32_t testval;
|
||||
switch(type) {
|
||||
default:
|
||||
printf("not ok %d - memory invalid type: %d at 0x%" PRIx64 "\n", log_num++, type, addr);
|
||||
return false;
|
||||
case UC_MEM_WRITE_PROT:
|
||||
printf("# write to non-writeable memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value);
|
||||
|
||||
if (uc_mem_read(uc, addr, &testval, sizeof(testval)) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_mem_read fail for address: 0x%" PRIx64 "\n", log_num++, addr);
|
||||
} else {
|
||||
printf("ok %d - uc_mem_read success after mem_protect at test %d\n", log_num++, test_num - 1);
|
||||
}
|
||||
|
||||
if (uc_mem_protect(uc, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_mem_protect fail during hook_mem_invalid callback, addr: 0x%" PRIx64 "\n", log_num++, addr);
|
||||
} else {
|
||||
printf("ok %d - uc_mem_protect success\n", log_num++);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1, trace2;
|
||||
uc_err err;
|
||||
uint32_t addr, testval;
|
||||
int32_t buf1[1024], buf2[1024], readbuf[1024];
|
||||
int i;
|
||||
|
||||
//don't really care about quality of randomness
|
||||
srand(time(NULL));
|
||||
for (i = 0; i < 1024; i++) {
|
||||
buf1[i] = rand();
|
||||
buf2[i] = rand();
|
||||
}
|
||||
|
||||
printf("# Memory protect test\n");
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
if (err) {
|
||||
printf("not ok %d - Failed on uc_open() with error returned: %u\n", log_num++, err);
|
||||
return 1;
|
||||
} else {
|
||||
printf("ok %d - uc_open() success\n", log_num++);
|
||||
}
|
||||
|
||||
uc_mem_map(uc, CODE_SECTION, CODE_SIZE, UC_PROT_READ | UC_PROT_EXEC);
|
||||
uc_mem_map(uc, 0x200000, 0x1000, UC_PROT_READ | UC_PROT_WRITE);
|
||||
uc_mem_map(uc, 0x300000, 0x1000, UC_PROT_READ | UC_PROT_WRITE);
|
||||
uc_mem_map(uc, 0x3ff000, 0x3000, UC_PROT_READ | UC_PROT_WRITE);
|
||||
|
||||
// fill in sections that shouldn't get touched
|
||||
if (uc_mem_write(uc, 0x3ff000, buf1, sizeof(buf1))) {
|
||||
printf("not ok %d - Failed to write random buffer 1 to memory, quit!\n", log_num++);
|
||||
return 2;
|
||||
} else {
|
||||
printf("ok %d - Random buffer 1 written to memory\n", log_num++);
|
||||
}
|
||||
|
||||
if (uc_mem_write(uc, 0x401000, buf2, sizeof(buf2))) {
|
||||
printf("not ok %d - Failed to write random buffer 2 to memory, quit!\n", log_num++);
|
||||
return 3;
|
||||
} else {
|
||||
printf("ok %d - Random buffer 2 written to memory\n", log_num++);
|
||||
}
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
if (uc_mem_write(uc, CODE_SECTION, PROGRAM, sizeof(PROGRAM))) {
|
||||
printf("not ok %d - Failed to write emulation code to memory, quit!\n", log_num++);
|
||||
return 4;
|
||||
} else {
|
||||
printf("ok %d - Program written to memory\n", log_num++);
|
||||
}
|
||||
|
||||
if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed to install UC_HOOK_CODE ucr\n", log_num++);
|
||||
return 5;
|
||||
} else {
|
||||
printf("ok %d - UC_HOOK_CODE installed\n", log_num++);
|
||||
}
|
||||
|
||||
// intercept memory write events
|
||||
if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL, 1, 0) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE ucr\n", log_num++);
|
||||
return 6;
|
||||
} else {
|
||||
printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++);
|
||||
}
|
||||
|
||||
// intercept invalid memory events
|
||||
if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE_PROT, hook_mem_invalid, NULL, 1, 0) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed to install memory invalid handler\n", log_num++);
|
||||
return 7;
|
||||
} else {
|
||||
printf("ok %d - memory invalid handler installed\n", log_num++);
|
||||
}
|
||||
|
||||
// emulate machine code until told to stop by hook_code
|
||||
printf("# BEGIN execution\n");
|
||||
err = uc_emu_start(uc, CODE_SECTION, CODE_SECTION + CODE_SIZE, 0, 0);
|
||||
if (err != UC_ERR_OK) {
|
||||
printf("not ok %d - Failure on uc_emu_start() with error %u:%s\n", log_num++, err, uc_strerror(err));
|
||||
return 8;
|
||||
} else {
|
||||
printf("ok %d - uc_emu_start complete\n", log_num++);
|
||||
}
|
||||
printf("# END execution\n");
|
||||
|
||||
//read from the remapped memory
|
||||
testval = 0x42424242;
|
||||
for (addr = 0x200000; addr <= 0x400000; addr += 0x100000) {
|
||||
uint32_t val;
|
||||
if (uc_mem_read(uc, addr, &val, sizeof(val)) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed uc_mem_read for address 0x%x\n", log_num++, addr);
|
||||
} else {
|
||||
printf("ok %d - Good uc_mem_read from 0x%x\n", log_num++, addr);
|
||||
}
|
||||
if (val != testval) {
|
||||
printf("not ok %d - Read 0x%x, expected 0x%x\n", log_num++, val, testval);
|
||||
} else {
|
||||
printf("ok %d - Correct value retrieved\n", log_num++);
|
||||
}
|
||||
testval += 0x02020202;
|
||||
}
|
||||
|
||||
//account for the two mods made by the machine code
|
||||
buf1[512] = 0x47474747;
|
||||
buf2[512] = 0x48484848;
|
||||
|
||||
//make sure that random blocks didn't get nuked
|
||||
// fill in sections that shouldn't get touched
|
||||
if (uc_mem_read(uc, 0x3ff000, readbuf, sizeof(readbuf))) {
|
||||
printf("not ok %d - Failed to read random buffer 1 from memory\n", log_num++);
|
||||
} else {
|
||||
printf("ok %d - Random buffer 1 read from memory\n", log_num++);
|
||||
if (memcmp(buf1, readbuf, 4096)) {
|
||||
printf("not ok %d - Random buffer 1 contents are incorrect\n", log_num++);
|
||||
} else {
|
||||
printf("ok %d - Random buffer 1 contents are correct\n", log_num++);
|
||||
}
|
||||
}
|
||||
|
||||
if (uc_mem_read(uc, 0x401000, readbuf, sizeof(readbuf))) {
|
||||
printf("not ok %d - Failed to read random buffer 2 from memory\n", log_num++);
|
||||
} else {
|
||||
printf("ok %d - Random buffer 2 read from memory\n", log_num++);
|
||||
if (memcmp(buf2, readbuf, 4096)) {
|
||||
printf("not ok %d - Random buffer 2 contents are incorrect\n", log_num++);
|
||||
} else {
|
||||
printf("ok %d - Random buffer 2 contents are correct\n", log_num++);
|
||||
}
|
||||
}
|
||||
|
||||
if (uc_close(uc) == UC_ERR_OK) {
|
||||
printf("ok %d - uc_close complete\n", log_num++);
|
||||
} else {
|
||||
printf("not ok %d - uc_close complete\n", log_num++);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
/*
|
||||
|
||||
uc_mem_unmap demo / unit test
|
||||
|
||||
Copyright(c) 2015 Chris Eagle
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
version 2 as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
*/
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
unsigned char PROGRAM[] =
|
||||
"\xc7\x05\x00\x00\x20\x00\x41\x41\x41\x41\x90\xc7\x05\x00\x00\x20"
|
||||
"\x00\x42\x42\x42\x42\xc7\x05\x00\x00\x30\x00\x43\x43\x43\x43\x90"
|
||||
"\xc7\x05\x00\x00\x30\x00\x44\x44\x44\x44\xc7\x05\x00\x00\x40\x00"
|
||||
"\x45\x45\x45\x45\x90\xc7\x05\x00\x00\x40\x00\x46\x46\x46\x46\xf4";
|
||||
// total size: 64 bytes
|
||||
|
||||
/*
|
||||
; assumes code section at 0x100000
|
||||
; assumes data section at 0x200000, initially rw
|
||||
; assumes data section at 0x300000, initially rw
|
||||
; assumes data section at 0x400000, initially rw
|
||||
|
||||
; with installed hooks unmaps or maps on each nop
|
||||
|
||||
mov dword [0x200000], 0x41414141
|
||||
nop ; unmap it
|
||||
mov dword [0x200000], 0x42424242
|
||||
|
||||
mov dword [0x300000], 0x43434343
|
||||
nop ; unmap it
|
||||
mov dword [0x300000], 0x44444444
|
||||
|
||||
mov dword [0x400000], 0x45454545
|
||||
nop ; unmap it
|
||||
mov dword [0x400000], 0x46464646
|
||||
|
||||
hlt ; tell hook function we are done
|
||||
*/
|
||||
|
||||
int test_num = 0;
|
||||
uint32_t tests[] = {
|
||||
0x41414141,
|
||||
0x43434343,
|
||||
0x45454545
|
||||
};
|
||||
|
||||
static int log_num = 1;
|
||||
|
||||
#define CODE_SECTION 0x100000
|
||||
#define CODE_SIZE 0x1000
|
||||
|
||||
// callback for tracing instruction
|
||||
static void hook_code(uc_engine *uc, uint64_t addr, uint32_t size, void *user_data)
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint32_t testval;
|
||||
if (uc_mem_read(uc, addr, &opcode, 1) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_mem_read fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr);
|
||||
}
|
||||
printf("ok %d - uc_mem_read for opcode at address 0x%" PRIx64 "\n", log_num++, addr);
|
||||
switch (opcode) {
|
||||
case 0x90: //nop
|
||||
printf("# Handling NOP\n");
|
||||
if (uc_mem_read(uc, 0x200000 + test_num * 0x100000, &testval, sizeof(testval)) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_mem_read fail for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000);
|
||||
} else {
|
||||
printf("ok %d - good uc_mem_read for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000);
|
||||
printf("# uc_mem_read for test %d\n", test_num);
|
||||
|
||||
if (testval == tests[test_num]) {
|
||||
printf("ok %d - passed test %d\n", log_num++, test_num);
|
||||
} else {
|
||||
printf("not ok %d - failed test %d\n", log_num++, test_num);
|
||||
printf("# Expected: 0x%x\n",tests[test_num]);
|
||||
printf("# Received: 0x%x\n", testval);
|
||||
}
|
||||
}
|
||||
if (uc_mem_unmap(uc, 0x200000 + test_num * 0x100000, 0x1000) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_mem_unmap fail during hook_code callback, addr: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000);
|
||||
} else {
|
||||
printf("ok %d - uc_mem_unmap success\n", log_num++);
|
||||
}
|
||||
test_num++;
|
||||
break;
|
||||
case 0xf4: //hlt
|
||||
printf("# Handling HLT\n");
|
||||
if (uc_emu_stop(uc) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_emu_stop fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr);
|
||||
_exit(-1);
|
||||
} else {
|
||||
printf("ok %d - hlt encountered, uc_emu_stop called\n", log_num++);
|
||||
}
|
||||
break;
|
||||
default: //all others
|
||||
printf("# Handling OTHER\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// callback for tracing memory access (READ or WRITE)
|
||||
static void hook_mem_write(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t addr, int size, int64_t value, void *user_data)
|
||||
{
|
||||
printf("# write to memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value);
|
||||
}
|
||||
|
||||
// callback for tracing invalid memory access (READ or WRITE)
|
||||
static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t addr, int size, int64_t value, void *user_data)
|
||||
{
|
||||
uint32_t testval;
|
||||
switch(type) {
|
||||
default:
|
||||
printf("not ok %d - memory invalid type: %d at 0x%" PRIx64 "\n", log_num++, type, addr);
|
||||
return false;
|
||||
case UC_MEM_WRITE_UNMAPPED:
|
||||
printf("# write to invalid memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value);
|
||||
|
||||
if (uc_mem_read(uc, addr, &testval, sizeof(testval)) != UC_ERR_OK) {
|
||||
printf("ok %d - uc_mem_read fail for address: 0x%" PRIx64 "\n", log_num++, addr);
|
||||
} else {
|
||||
printf("not ok %d - uc_mem_read success after unmap at test %d\n", log_num++, test_num - 1);
|
||||
}
|
||||
|
||||
if (uc_mem_map(uc, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) {
|
||||
printf("not ok %d - uc_mem_map fail during hook_mem_invalid callback, addr: 0x%" PRIx64 "\n", log_num++, addr);
|
||||
} else {
|
||||
printf("ok %d - uc_mem_map success\n", log_num++);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1, trace2;
|
||||
uc_err err;
|
||||
uint32_t addr, testval;
|
||||
int32_t buf1[1024], buf2[1024], readbuf[1024];
|
||||
int i;
|
||||
|
||||
//don't really care about quality of randomness
|
||||
srand(time(NULL));
|
||||
for (i = 0; i < 1024; i++) {
|
||||
buf1[i] = rand();
|
||||
buf2[i] = rand();
|
||||
}
|
||||
|
||||
printf("# Memory unmapping test\n");
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
if (err) {
|
||||
printf("not ok %d - Failed on uc_open() with error returned: %u\n", log_num++, err);
|
||||
return 1;
|
||||
} else {
|
||||
printf("ok %d - uc_open() success\n", log_num++);
|
||||
}
|
||||
|
||||
uc_mem_map(uc, CODE_SECTION, CODE_SIZE, UC_PROT_READ | UC_PROT_EXEC);
|
||||
uc_mem_map(uc, 0x200000, 0x1000, UC_PROT_READ | UC_PROT_WRITE);
|
||||
uc_mem_map(uc, 0x300000, 0x1000, UC_PROT_READ | UC_PROT_WRITE);
|
||||
uc_mem_map(uc, 0x3ff000, 0x3000, UC_PROT_READ | UC_PROT_WRITE);
|
||||
|
||||
// fill in sections that shouldn't get touched
|
||||
if (uc_mem_write(uc, 0x3ff000, buf1, sizeof(buf1))) {
|
||||
printf("not ok %d - Failed to write random buffer 1 to memory, quit!\n", log_num++);
|
||||
return 2;
|
||||
} else {
|
||||
printf("ok %d - Random buffer 1 written to memory\n", log_num++);
|
||||
}
|
||||
|
||||
if (uc_mem_write(uc, 0x401000, buf2, sizeof(buf1))) {
|
||||
printf("not ok %d - Failed to write random buffer 2 to memory, quit!\n", log_num++);
|
||||
return 3;
|
||||
} else {
|
||||
printf("ok %d - Random buffer 2 written to memory\n", log_num++);
|
||||
}
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
if (uc_mem_write(uc, CODE_SECTION, PROGRAM, sizeof(PROGRAM))) {
|
||||
printf("not ok %d - Failed to write emulation code to memory, quit!\n", log_num++);
|
||||
return 4;
|
||||
} else {
|
||||
printf("ok %d - Program written to memory\n", log_num++);
|
||||
}
|
||||
|
||||
if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed to install UC_HOOK_CODE ucr\n", log_num++);
|
||||
return 5;
|
||||
} else {
|
||||
printf("ok %d - UC_HOOK_CODE installed\n", log_num++);
|
||||
}
|
||||
|
||||
// intercept memory write events
|
||||
if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL, 1, 0) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE ucr\n", log_num++);
|
||||
return 6;
|
||||
} else {
|
||||
printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++);
|
||||
}
|
||||
|
||||
// intercept invalid memory events
|
||||
if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_invalid, NULL, 1, 0) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed to install memory invalid handler\n", log_num++);
|
||||
return 7;
|
||||
} else {
|
||||
printf("ok %d - memory invalid handler installed\n", log_num++);
|
||||
}
|
||||
|
||||
// emulate machine code until told to stop by hook_code
|
||||
printf("# BEGIN execution\n");
|
||||
err = uc_emu_start(uc, CODE_SECTION, CODE_SECTION + CODE_SIZE, 0, 0);
|
||||
if (err != UC_ERR_OK) {
|
||||
printf("not ok %d - Failure on uc_emu_start() with error %u:%s\n", log_num++, err, uc_strerror(err));
|
||||
return 8;
|
||||
} else {
|
||||
printf("ok %d - uc_emu_start complete\n", log_num++);
|
||||
}
|
||||
printf("# END execution\n");
|
||||
|
||||
//read from the remapped memory
|
||||
testval = 0x42424242;
|
||||
for (addr = 0x200000; addr <= 0x400000; addr += 0x100000) {
|
||||
uint32_t val;
|
||||
if (uc_mem_read(uc, addr, &val, sizeof(val)) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed uc_mem_read for address 0x%x\n", log_num++, addr);
|
||||
} else {
|
||||
printf("ok %d - Good uc_mem_read from 0x%x\n", log_num++, addr);
|
||||
}
|
||||
if (val != testval) {
|
||||
printf("not ok %d - Read 0x%x, expected 0x%x\n", log_num++, val, testval);
|
||||
} else {
|
||||
printf("ok %d - Correct value retrieved\n", log_num++);
|
||||
}
|
||||
testval += 0x02020202;
|
||||
}
|
||||
|
||||
//make sure that random blocks didn't get nuked
|
||||
// fill in sections that shouldn't get touched
|
||||
if (uc_mem_read(uc, 0x3ff000, readbuf, sizeof(readbuf))) {
|
||||
printf("not ok %d - Failed to read random buffer 1 from memory\n", log_num++);
|
||||
} else {
|
||||
printf("ok %d - Random buffer 1 read from memory\n", log_num++);
|
||||
if (memcmp(buf1, readbuf, 4096)) {
|
||||
printf("not ok %d - Random buffer 1 contents are incorrect\n", log_num++);
|
||||
} else {
|
||||
printf("ok %d - Random buffer 1 contents are correct\n", log_num++);
|
||||
}
|
||||
}
|
||||
|
||||
if (uc_mem_read(uc, 0x401000, readbuf, sizeof(readbuf))) {
|
||||
printf("not ok %d - Failed to read random buffer 2 from memory\n", log_num++);
|
||||
} else {
|
||||
printf("ok %d - Random buffer 2 read from memory\n", log_num++);
|
||||
if (memcmp(buf2, readbuf, 4096)) {
|
||||
printf("not ok %d - Random buffer 2 contents are incorrect\n", log_num++);
|
||||
} else {
|
||||
printf("ok %d - Random buffer 2 contents are correct\n", log_num++);
|
||||
}
|
||||
}
|
||||
|
||||
if (uc_close(uc) == UC_ERR_OK) {
|
||||
printf("ok %d - uc_close complete\n", log_num++);
|
||||
} else {
|
||||
printf("not ok %d - uc_close complete\n", log_num++);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/* Unicorn Emulator Engine */
|
||||
/* By Nguyen Anh Quynh, 2015 */
|
||||
|
||||
/* Sample code to demonstrate how to emulate ARM code */
|
||||
|
||||
// windows specific
|
||||
#ifdef _MSC_VER
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
#define PRIx64 "llX"
|
||||
#ifdef DYNLOAD
|
||||
#include "unicorn_dynload.h"
|
||||
#else // DYNLOAD
|
||||
#include <unicorn/unicorn.h>
|
||||
#ifdef _WIN64
|
||||
#pragma comment(lib, "unicorn_staload64.lib")
|
||||
#else // _WIN64
|
||||
#pragma comment(lib, "unicorn_staload.lib")
|
||||
#endif // _WIN64
|
||||
#endif // DYNLOAD
|
||||
|
||||
// posix specific
|
||||
#else // _MSC_VER
|
||||
#include <unicorn/unicorn.h>
|
||||
#endif // _MSC_VER
|
||||
|
||||
|
||||
// code to be emulated
|
||||
#define ARM_CODE "\x37\x00\xa0\xe3\x03\x10\x42\xe0" // mov r0, #0x37; sub r1, r2, r3
|
||||
#define THUMB_CODE "\x83\xb0" // sub sp, #0xc
|
||||
|
||||
// memory address where emulation starts
|
||||
#define ADDRESS 0x10000
|
||||
|
||||
static void hook_block(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
printf(">>> Tracing basic block at 0x%"PRIx64 ", block size = 0x%x\n", address, size);
|
||||
}
|
||||
|
||||
static void hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size);
|
||||
}
|
||||
|
||||
static void test_arm(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
int r0 = 0x1234; // R0 register
|
||||
int r2 = 0x6789; // R1 register
|
||||
int r3 = 0x3333; // R2 register
|
||||
int r1; // R1 register
|
||||
|
||||
printf("Emulate ARM code\n");
|
||||
|
||||
// Initialize emulator in ARM mode
|
||||
err = uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc);
|
||||
if (err) {
|
||||
printf("Failed on uc_open() with error returned: %u (%s)\n",
|
||||
err, uc_strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
uc_mem_write(uc, ADDRESS, ARM_CODE, sizeof(ARM_CODE) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
uc_reg_write(uc, UC_ARM_REG_R0, &r0);
|
||||
uc_reg_write(uc, UC_ARM_REG_R2, &r2);
|
||||
uc_reg_write(uc, UC_ARM_REG_R3, &r3);
|
||||
|
||||
// tracing all basic blocks with customized callback
|
||||
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
|
||||
|
||||
// tracing one instruction at ADDRESS with customized callback
|
||||
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS);
|
||||
|
||||
// emulate machine code in infinite time (last param = 0), or when
|
||||
// finishing all the code.
|
||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(ARM_CODE) -1, 0, 0);
|
||||
if (err) {
|
||||
printf("Failed on uc_emu_start() with error returned: %u\n", err);
|
||||
}
|
||||
|
||||
// now print out some registers
|
||||
printf(">>> Emulation done. Below is the CPU context\n");
|
||||
|
||||
uc_reg_read(uc, UC_ARM_REG_R0, &r0);
|
||||
uc_reg_read(uc, UC_ARM_REG_R1, &r1);
|
||||
printf(">>> R0 = 0x%x\n", r0);
|
||||
printf(">>> R1 = 0x%x\n", r1);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
static void test_thumb(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
int sp = 0x1234; // R0 register
|
||||
|
||||
printf("Emulate THUMB code\n");
|
||||
|
||||
// Initialize emulator in ARM mode
|
||||
err = uc_open(UC_ARCH_ARM, UC_MODE_THUMB, &uc);
|
||||
if (err) {
|
||||
printf("Failed on uc_open() with error returned: %u (%s)\n",
|
||||
err, uc_strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
uc_mem_write(uc, ADDRESS, THUMB_CODE, sizeof(THUMB_CODE) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
uc_reg_write(uc, UC_ARM_REG_SP, &sp);
|
||||
|
||||
// tracing all basic blocks with customized callback
|
||||
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
|
||||
|
||||
// tracing one instruction at ADDRESS with customized callback
|
||||
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS);
|
||||
|
||||
// emulate machine code in infinite time (last param = 0), or when
|
||||
// finishing all the code.
|
||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(THUMB_CODE) -1, 0, 0);
|
||||
if (err) {
|
||||
printf("Failed on uc_emu_start() with error returned: %u\n", err);
|
||||
}
|
||||
|
||||
// now print out some registers
|
||||
printf(">>> Emulation done. Below is the CPU context\n");
|
||||
|
||||
uc_reg_read(uc, UC_ARM_REG_SP, &sp);
|
||||
printf(">>> SP = 0x%x\n", sp);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
// dynamically load shared library
|
||||
#ifdef DYNLOAD
|
||||
if (!uc_dyn_load(NULL, 0)) {
|
||||
printf("Error dynamically loading shared library.\n");
|
||||
printf("Please check that unicorn.dll/unicorn.so is available as well as\n");
|
||||
printf("any other dependent dll/so files.\n");
|
||||
printf("The easiest way is to place them in the same directory as this app.\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// test memleak
|
||||
while(1) {
|
||||
test_arm();
|
||||
printf("==========================\n");
|
||||
test_thumb();
|
||||
}
|
||||
|
||||
// dynamically free shared library
|
||||
#ifdef DYNLOAD
|
||||
uc_dyn_free();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/* Unicorn Emulator Engine */
|
||||
/* By Nguyen Anh Quynh, 2015 */
|
||||
|
||||
/* Sample code to demonstrate how to emulate ARM64 code */
|
||||
|
||||
// windows specific
|
||||
#ifdef _MSC_VER
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
#define PRIx64 "llX"
|
||||
#ifdef DYNLOAD
|
||||
#include "unicorn_dynload.h"
|
||||
#else // DYNLOAD
|
||||
#include <unicorn/unicorn.h>
|
||||
#ifdef _WIN64
|
||||
#pragma comment(lib, "unicorn_staload64.lib")
|
||||
#else // _WIN64
|
||||
#pragma comment(lib, "unicorn_staload.lib")
|
||||
#endif // _WIN64
|
||||
#endif // DYNLOAD
|
||||
|
||||
// posix specific
|
||||
#else // _MSC_VER
|
||||
#include <unicorn/unicorn.h>
|
||||
#endif // _MSC_VER
|
||||
|
||||
|
||||
// code to be emulated
|
||||
#define ARM_CODE "\xab\x01\x0f\x8b" // add x11, x13, x15
|
||||
|
||||
// memory address where emulation starts
|
||||
#define ADDRESS 0x10000
|
||||
|
||||
static void hook_block(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
printf(">>> Tracing basic block at 0x%"PRIx64 ", block size = 0x%x\n", address, size);
|
||||
}
|
||||
|
||||
static void hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size);
|
||||
}
|
||||
|
||||
static void test_arm64(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
int64_t x11 = 0x1234; // X11 register
|
||||
int64_t x13 = 0x6789; // X13 register
|
||||
int64_t x15 = 0x3333; // X15 register
|
||||
|
||||
printf("Emulate ARM64 code\n");
|
||||
|
||||
// Initialize emulator in ARM mode
|
||||
err = uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc);
|
||||
if (err) {
|
||||
printf("Failed on uc_open() with error returned: %u (%s)\n",
|
||||
err, uc_strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
uc_mem_write(uc, ADDRESS, ARM_CODE, sizeof(ARM_CODE) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
uc_reg_write(uc, UC_ARM64_REG_X11, &x11);
|
||||
uc_reg_write(uc, UC_ARM64_REG_X13, &x13);
|
||||
uc_reg_write(uc, UC_ARM64_REG_X15, &x15);
|
||||
|
||||
// tracing all basic blocks with customized callback
|
||||
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
|
||||
|
||||
// tracing one instruction at ADDRESS with customized callback
|
||||
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS);
|
||||
|
||||
// emulate machine code in infinite time (last param = 0), or when
|
||||
// finishing all the code.
|
||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(ARM_CODE) -1, 0, 0);
|
||||
if (err) {
|
||||
printf("Failed on uc_emu_start() with error returned: %u\n", err);
|
||||
}
|
||||
|
||||
// now print out some registers
|
||||
printf(">>> Emulation done. Below is the CPU context\n");
|
||||
|
||||
uc_reg_read(uc, UC_ARM64_REG_X11, &x11);
|
||||
printf(">>> X11 = 0x%" PRIx64 "\n", x11);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
// dynamically load shared library
|
||||
#ifdef DYNLOAD
|
||||
if (!uc_dyn_load(NULL, 0)) {
|
||||
printf("Error dynamically loading shared library.\n");
|
||||
printf("Please check that unicorn.dll/unicorn.so is available as well as\n");
|
||||
printf("any other dependent dll/so files.\n");
|
||||
printf("The easiest way is to place them in the same directory as this app.\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
while(1) {
|
||||
test_arm64();
|
||||
}
|
||||
|
||||
// dynamically free shared library
|
||||
#ifdef DYNLOAD
|
||||
uc_dyn_free();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
/* Unicorn Emulator Engine */
|
||||
/* By Loi Anh Tuan, 2015 */
|
||||
|
||||
/* Sample code to demonstrate how to emulate m68k code */
|
||||
|
||||
// windows specific
|
||||
#ifdef _MSC_VER
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
#define PRIx64 "llX"
|
||||
#ifdef DYNLOAD
|
||||
#include "unicorn_dynload.h"
|
||||
#else // DYNLOAD
|
||||
#include <unicorn/unicorn.h>
|
||||
#ifdef _WIN64
|
||||
#pragma comment(lib, "unicorn_staload64.lib")
|
||||
#else // _WIN64
|
||||
#pragma comment(lib, "unicorn_staload.lib")
|
||||
#endif // _WIN64
|
||||
#endif // DYNLOAD
|
||||
|
||||
// posix specific
|
||||
#else // _MSC_VER
|
||||
#include <unicorn/unicorn.h>
|
||||
#endif // _MSC_VER
|
||||
|
||||
// code to be emulated
|
||||
#define M68K_CODE "\x76\xed" // movq #-19, %d3
|
||||
|
||||
// memory address where emulation starts
|
||||
#define ADDRESS 0x10000
|
||||
|
||||
static void hook_block(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
printf(">>> Tracing basic block at 0x%"PRIx64 ", block size = 0x%x\n", address, size);
|
||||
}
|
||||
|
||||
static void hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size);
|
||||
}
|
||||
|
||||
static void test_m68k(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1, trace2;
|
||||
uc_err err;
|
||||
|
||||
int d0 = 0x0000; // d0 data register
|
||||
int d1 = 0x0000; // d1 data register
|
||||
int d2 = 0x0000; // d2 data register
|
||||
int d3 = 0x0000; // d3 data register
|
||||
int d4 = 0x0000; // d4 data register
|
||||
int d5 = 0x0000; // d5 data register
|
||||
int d6 = 0x0000; // d6 data register
|
||||
int d7 = 0x0000; // d7 data register
|
||||
|
||||
int a0 = 0x0000; // a0 address register
|
||||
int a1 = 0x0000; // a1 address register
|
||||
int a2 = 0x0000; // a2 address register
|
||||
int a3 = 0x0000; // a3 address register
|
||||
int a4 = 0x0000; // a4 address register
|
||||
int a5 = 0x0000; // a5 address register
|
||||
int a6 = 0x0000; // a6 address register
|
||||
int a7 = 0x0000; // a6 address register
|
||||
|
||||
int pc = 0x0000; // program counter
|
||||
int sr = 0x0000; // status register
|
||||
|
||||
printf("Emulate M68K code\n");
|
||||
|
||||
// Initialize emulator in M68K mode
|
||||
err = uc_open(UC_ARCH_M68K, UC_MODE_BIG_ENDIAN, &uc);
|
||||
if (err) {
|
||||
printf("Failed on uc_open() with error returned: %u (%s)\n",
|
||||
err, uc_strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
uc_mem_write(uc, ADDRESS, M68K_CODE, sizeof(M68K_CODE) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
uc_reg_write(uc, UC_M68K_REG_D0, &d0);
|
||||
uc_reg_write(uc, UC_M68K_REG_D1, &d1);
|
||||
uc_reg_write(uc, UC_M68K_REG_D2, &d2);
|
||||
uc_reg_write(uc, UC_M68K_REG_D3, &d3);
|
||||
uc_reg_write(uc, UC_M68K_REG_D4, &d4);
|
||||
uc_reg_write(uc, UC_M68K_REG_D5, &d5);
|
||||
uc_reg_write(uc, UC_M68K_REG_D6, &d6);
|
||||
uc_reg_write(uc, UC_M68K_REG_D7, &d7);
|
||||
|
||||
uc_reg_write(uc, UC_M68K_REG_A0, &a0);
|
||||
uc_reg_write(uc, UC_M68K_REG_A1, &a1);
|
||||
uc_reg_write(uc, UC_M68K_REG_A2, &a2);
|
||||
uc_reg_write(uc, UC_M68K_REG_A3, &a3);
|
||||
uc_reg_write(uc, UC_M68K_REG_A4, &a4);
|
||||
uc_reg_write(uc, UC_M68K_REG_A5, &a5);
|
||||
uc_reg_write(uc, UC_M68K_REG_A6, &a6);
|
||||
uc_reg_write(uc, UC_M68K_REG_A7, &a7);
|
||||
|
||||
uc_reg_write(uc, UC_M68K_REG_PC, &pc);
|
||||
uc_reg_write(uc, UC_M68K_REG_SR, &sr);
|
||||
|
||||
// tracing all basic blocks with customized callback
|
||||
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
|
||||
|
||||
// tracing all instruction
|
||||
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
|
||||
|
||||
// emulate machine code in infinite time (last param = 0), or when
|
||||
// finishing all the code.
|
||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(M68K_CODE)-1, 0, 0);
|
||||
if (err) {
|
||||
printf("Failed on uc_emu_start() with error returned: %u\n", err);
|
||||
}
|
||||
|
||||
// now print out some registers
|
||||
printf(">>> Emulation done. Below is the CPU context\n");
|
||||
|
||||
uc_reg_read(uc, UC_M68K_REG_D0, &d0);
|
||||
uc_reg_read(uc, UC_M68K_REG_D1, &d1);
|
||||
uc_reg_read(uc, UC_M68K_REG_D2, &d2);
|
||||
uc_reg_read(uc, UC_M68K_REG_D3, &d3);
|
||||
uc_reg_read(uc, UC_M68K_REG_D4, &d4);
|
||||
uc_reg_read(uc, UC_M68K_REG_D5, &d5);
|
||||
uc_reg_read(uc, UC_M68K_REG_D6, &d6);
|
||||
uc_reg_read(uc, UC_M68K_REG_D7, &d7);
|
||||
|
||||
uc_reg_read(uc, UC_M68K_REG_A0, &a0);
|
||||
uc_reg_read(uc, UC_M68K_REG_A1, &a1);
|
||||
uc_reg_read(uc, UC_M68K_REG_A2, &a2);
|
||||
uc_reg_read(uc, UC_M68K_REG_A3, &a3);
|
||||
uc_reg_read(uc, UC_M68K_REG_A4, &a4);
|
||||
uc_reg_read(uc, UC_M68K_REG_A5, &a5);
|
||||
uc_reg_read(uc, UC_M68K_REG_A6, &a6);
|
||||
uc_reg_read(uc, UC_M68K_REG_A7, &a7);
|
||||
|
||||
uc_reg_read(uc, UC_M68K_REG_PC, &pc);
|
||||
uc_reg_read(uc, UC_M68K_REG_SR, &sr);
|
||||
|
||||
printf(">>> A0 = 0x%x\t\t>>> D0 = 0x%x\n", a0, d0);
|
||||
printf(">>> A1 = 0x%x\t\t>>> D1 = 0x%x\n", a1, d1);
|
||||
printf(">>> A2 = 0x%x\t\t>>> D2 = 0x%x\n", a2, d2);
|
||||
printf(">>> A3 = 0x%x\t\t>>> D3 = 0x%x\n", a3, d3);
|
||||
printf(">>> A4 = 0x%x\t\t>>> D4 = 0x%x\n", a4, d4);
|
||||
printf(">>> A5 = 0x%x\t\t>>> D5 = 0x%x\n", a5, d5);
|
||||
printf(">>> A6 = 0x%x\t\t>>> D6 = 0x%x\n", a6, d6);
|
||||
printf(">>> A7 = 0x%x\t\t>>> D7 = 0x%x\n", a7, d7);
|
||||
printf(">>> PC = 0x%x\n", pc);
|
||||
printf(">>> SR = 0x%x\n", sr);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
// dynamically load shared library
|
||||
#ifdef DYNLOAD
|
||||
if (!uc_dyn_load(NULL, 0)) {
|
||||
printf("Error dynamically loading shared library.\n");
|
||||
printf("Please check that unicorn.dll/unicorn.so is available as well as\n");
|
||||
printf("any other dependent dll/so files.\n");
|
||||
printf("The easiest way is to place them in the same directory as this app.\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// test memleak
|
||||
while(1) {
|
||||
test_m68k();
|
||||
}
|
||||
|
||||
// dynamically free shared library
|
||||
#ifdef DYNLOAD
|
||||
uc_dyn_free();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
/* Unicorn Emulator Engine */
|
||||
/* By Nguyen Anh Quynh, 2015 */
|
||||
|
||||
/* Sample code to demonstrate how to emulate Mips code (big endian) */
|
||||
|
||||
// windows specific
|
||||
#ifdef _MSC_VER
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
#define PRIx64 "llX"
|
||||
#ifdef DYNLOAD
|
||||
#include "unicorn_dynload.h"
|
||||
#else // DYNLOAD
|
||||
#include <unicorn/unicorn.h>
|
||||
#ifdef _WIN64
|
||||
#pragma comment(lib, "unicorn_staload64.lib")
|
||||
#else // _WIN64
|
||||
#pragma comment(lib, "unicorn_staload.lib")
|
||||
#endif // _WIN64
|
||||
#endif // DYNLOAD
|
||||
|
||||
// posix specific
|
||||
#else // _MSC_VER
|
||||
#include <unicorn/unicorn.h>
|
||||
#endif // _MSC_VER
|
||||
|
||||
|
||||
// code to be emulated
|
||||
#define MIPS_CODE_EB "\x34\x21\x34\x56" // ori $at, $at, 0x3456;
|
||||
#define MIPS_CODE_EL "\x56\x34\x21\x34" // ori $at, $at, 0x3456;
|
||||
|
||||
// memory address where emulation starts
|
||||
#define ADDRESS 0x10000
|
||||
|
||||
static void hook_block(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
printf(">>> Tracing basic block at 0x%"PRIx64 ", block size = 0x%x\n", address, size);
|
||||
}
|
||||
|
||||
static void hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size);
|
||||
}
|
||||
|
||||
static void test_mips_eb(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
int r1 = 0x6789; // R1 register
|
||||
|
||||
printf("Emulate MIPS code (big-endian)\n");
|
||||
|
||||
// Initialize emulator in MIPS mode
|
||||
err = uc_open(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN, &uc);
|
||||
if (err) {
|
||||
printf("Failed on uc_open() with error returned: %u (%s)\n",
|
||||
err, uc_strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
uc_mem_write(uc, ADDRESS, MIPS_CODE_EB, sizeof(MIPS_CODE_EB) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
uc_reg_write(uc, UC_MIPS_REG_1, &r1);
|
||||
|
||||
// tracing all basic blocks with customized callback
|
||||
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
|
||||
|
||||
// tracing one instruction at ADDRESS with customized callback
|
||||
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS);
|
||||
|
||||
// emulate machine code in infinite time (last param = 0), or when
|
||||
// finishing all the code.
|
||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(MIPS_CODE_EB) - 1, 0, 0);
|
||||
if (err) {
|
||||
printf("Failed on uc_emu_start() with error returned: %u (%s)\n", err, uc_strerror(err));
|
||||
}
|
||||
|
||||
// now print out some registers
|
||||
printf(">>> Emulation done. Below is the CPU context\n");
|
||||
|
||||
uc_reg_read(uc, UC_MIPS_REG_1, &r1);
|
||||
printf(">>> R1 = 0x%x\n", r1);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
static void test_mips_el(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
int r1 = 0x6789; // R1 register
|
||||
|
||||
printf("===========================\n");
|
||||
printf("Emulate MIPS code (little-endian)\n");
|
||||
|
||||
// Initialize emulator in MIPS mode
|
||||
err = uc_open(UC_ARCH_MIPS, UC_MODE_MIPS32, &uc);
|
||||
if (err) {
|
||||
printf("Failed on uc_open() with error returned: %u (%s)\n",
|
||||
err, uc_strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
uc_mem_write(uc, ADDRESS, MIPS_CODE_EL, sizeof(MIPS_CODE_EL) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
uc_reg_write(uc, UC_MIPS_REG_1, &r1);
|
||||
|
||||
// tracing all basic blocks with customized callback
|
||||
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
|
||||
|
||||
// tracing one instruction at ADDRESS with customized callback
|
||||
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)ADDRESS, (uint64_t)ADDRESS);
|
||||
|
||||
// emulate machine code in infinite time (last param = 0), or when
|
||||
// finishing all the code.
|
||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(MIPS_CODE_EL) - 1, 0, 0);
|
||||
if (err) {
|
||||
printf("Failed on uc_emu_start() with error returned: %u (%s)\n", err, uc_strerror(err));
|
||||
}
|
||||
|
||||
// now print out some registers
|
||||
printf(">>> Emulation done. Below is the CPU context\n");
|
||||
|
||||
uc_reg_read(uc, UC_MIPS_REG_1, &r1);
|
||||
printf(">>> R1 = 0x%x\n", r1);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
// dynamically load shared library
|
||||
#ifdef DYNLOAD
|
||||
if (!uc_dyn_load(NULL, 0)) {
|
||||
printf("Error dynamically loading shared library.\n");
|
||||
printf("Please check that unicorn.dll/unicorn.so is available as well as\n");
|
||||
printf("any other dependent dll/so files.\n");
|
||||
printf("The easiest way is to place them in the same directory as this app.\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// test memleak
|
||||
while(1) {
|
||||
test_mips_eb();
|
||||
test_mips_el();
|
||||
}
|
||||
|
||||
// dynamically free shared library
|
||||
#ifdef DYNLOAD
|
||||
uc_dyn_free();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/* Unicorn Emulator Engine */
|
||||
/* By Nguyen Anh Quynh, 2015 */
|
||||
|
||||
/* Sample code to demonstrate how to emulate Sparc code */
|
||||
|
||||
// windows specific
|
||||
#ifdef _MSC_VER
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
#define PRIx64 "llX"
|
||||
#ifdef DYNLOAD
|
||||
#include "unicorn_dynload.h"
|
||||
#else // DYNLOAD
|
||||
#include <unicorn/unicorn.h>
|
||||
#ifdef _WIN64
|
||||
#pragma comment(lib, "unicorn_staload64.lib")
|
||||
#else // _WIN64
|
||||
#pragma comment(lib, "unicorn_staload.lib")
|
||||
#endif // _WIN64
|
||||
#endif // DYNLOAD
|
||||
|
||||
// posix specific
|
||||
#else // _MSC_VER
|
||||
#include <unicorn/unicorn.h>
|
||||
#endif // _MSC_VER
|
||||
|
||||
|
||||
// code to be emulated
|
||||
#define SPARC_CODE "\x86\x00\x40\x02" // add %g1, %g2, %g3;
|
||||
//#define SPARC_CODE "\xbb\x70\x00\x00" // illegal code
|
||||
|
||||
// memory address where emulation starts
|
||||
#define ADDRESS 0x10000
|
||||
|
||||
static void hook_block(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
printf(">>> Tracing basic block at 0x%"PRIx64 ", block size = 0x%x\n", address, size);
|
||||
}
|
||||
|
||||
static void hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size);
|
||||
}
|
||||
|
||||
static void test_sparc(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
int g1 = 0x1230; // G1 register
|
||||
int g2 = 0x6789; // G2 register
|
||||
int g3 = 0x5555; // G3 register
|
||||
|
||||
printf("Emulate SPARC code\n");
|
||||
|
||||
// Initialize emulator in Sparc mode
|
||||
err = uc_open(UC_ARCH_SPARC, UC_MODE_SPARC32|UC_MODE_BIG_ENDIAN, &uc);
|
||||
if (err) {
|
||||
printf("Failed on uc_open() with error returned: %u (%s)\n",
|
||||
err, uc_strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
uc_mem_write(uc, ADDRESS, SPARC_CODE, sizeof(SPARC_CODE) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
uc_reg_write(uc, UC_SPARC_REG_G1, &g1);
|
||||
uc_reg_write(uc, UC_SPARC_REG_G2, &g2);
|
||||
uc_reg_write(uc, UC_SPARC_REG_G3, &g3);
|
||||
|
||||
// tracing all basic blocks with customized callback
|
||||
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
|
||||
|
||||
// tracing all instructions with customized callback
|
||||
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
|
||||
|
||||
// emulate machine code in infinite time (last param = 0), or when
|
||||
// finishing all the code.
|
||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(SPARC_CODE) - 1, 0, 0);
|
||||
if (err) {
|
||||
printf("Failed on uc_emu_start() with error returned: %u (%s)\n",
|
||||
err, uc_strerror(err));
|
||||
}
|
||||
|
||||
// now print out some registers
|
||||
printf(">>> Emulation done. Below is the CPU context\n");
|
||||
|
||||
uc_reg_read(uc, UC_SPARC_REG_G3, &g3);
|
||||
printf(">>> G3 = 0x%x\n", g3);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
// dynamically load shared library
|
||||
#ifdef DYNLOAD
|
||||
if (!uc_dyn_load(NULL, 0)) {
|
||||
printf("Error dynamically loading shared library.\n");
|
||||
printf("Please check that unicorn.dll/unicorn.so is available as well as\n");
|
||||
printf("any other dependent dll/so files.\n");
|
||||
printf("The easiest way is to place them in the same directory as this app.\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// test memleak
|
||||
while(1) {
|
||||
test_sparc();
|
||||
}
|
||||
|
||||
// dynamically free shared library
|
||||
#ifdef DYNLOAD
|
||||
uc_dyn_free();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,304 +0,0 @@
|
||||
/* Unicorn Emulator Engine */
|
||||
/* By Nguyen Anh Quynh & Dang Hoang Vu, 2015 */
|
||||
|
||||
/* Sample code to demonstrate how to emulate X86 code */
|
||||
|
||||
// windows specific
|
||||
#ifdef _MSC_VER
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
#define PRIx64 "llX"
|
||||
#ifdef DYNLOAD
|
||||
#include "unicorn_dynload.h"
|
||||
#else // DYNLOAD
|
||||
#include <unicorn/unicorn.h>
|
||||
#ifdef _WIN64
|
||||
#pragma comment(lib, "unicorn_staload64.lib")
|
||||
#else // _WIN64
|
||||
#pragma comment(lib, "unicorn_staload.lib")
|
||||
#endif // _WIN64
|
||||
#endif // DYNLOAD
|
||||
|
||||
// posix specific
|
||||
#else // _MSC_VER
|
||||
#include <unicorn/unicorn.h>
|
||||
#endif // _MSC_VER
|
||||
|
||||
// common includes
|
||||
#include <string.h>
|
||||
|
||||
|
||||
// code to be emulated
|
||||
#define X86_CODE32 "\x41\x4a" // INC ecx; DEC edx
|
||||
#define X86_CODE32_JUMP "\xeb\x02\x90\x90\x90\x90\x90\x90" // jmp 4; nop; nop; nop; nop; nop; nop
|
||||
// #define X86_CODE32_SELF "\xeb\x1c\x5a\x89\xd6\x8b\x02\x66\x3d\xca\x7d\x75\x06\x66\x05\x03\x03\x89\x02\xfe\xc2\x3d\x41\x41\x41\x41\x75\xe9\xff\xe6\xe8\xdf\xff\xff\xff\x31\xd2\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xca\x7d\x41\x41\x41\x41"
|
||||
//#define X86_CODE32 "\x51\x51\x51\x51" // PUSH ecx;
|
||||
#define X86_CODE32_LOOP "\x41\x4a\xeb\xfe" // INC ecx; DEC edx; JMP self-loop
|
||||
#define X86_CODE32_MEM_WRITE "\x89\x0D\xAA\xAA\xAA\xAA\x41\x4a" // mov [0xaaaaaaaa], ecx; INC ecx; DEC edx
|
||||
#define X86_CODE32_MEM_READ "\x8B\x0D\xAA\xAA\xAA\xAA\x41\x4a" // mov ecx,[0xaaaaaaaa]; INC ecx; DEC edx
|
||||
|
||||
#define X86_CODE32_JMP_INVALID "\xe9\xe9\xee\xee\xee\x41\x4a" // JMP outside; INC ecx; DEC edx
|
||||
#define X86_CODE32_INOUT "\x41\xE4\x3F\x4a\xE6\x46\x43" // INC ecx; IN AL, 0x3f; DEC edx; OUT 0x46, AL; INC ebx
|
||||
|
||||
//#define X86_CODE64 "\x41\xBC\x3B\xB0\x28\x2A \x49\x0F\xC9 \x90 \x4D\x0F\xAD\xCF\x49\x87\xFD\x90\x48\x81\xD2\x8A\xCE\x77\x35\x48\xF7\xD9" // <== still crash
|
||||
//#define X86_CODE64 "\x41\xBC\x3B\xB0\x28\x2A\x49\x0F\xC9\x90\x4D\x0F\xAD\xCF\x49\x87\xFD\x90\x48\x81\xD2\x8A\xCE\x77\x35\x48\xF7\xD9"
|
||||
#define X86_CODE64 "\x41\xBC\x3B\xB0\x28\x2A\x49\x0F\xC9\x90\x4D\x0F\xAD\xCF\x49\x87\xFD\x90\x48\x81\xD2\x8A\xCE\x77\x35\x48\xF7\xD9\x4D\x29\xF4\x49\x81\xC9\xF6\x8A\xC6\x53\x4D\x87\xED\x48\x0F\xAD\xD2\x49\xF7\xD4\x48\xF7\xE1\x4D\x19\xC5\x4D\x89\xC5\x48\xF7\xD6\x41\xB8\x4F\x8D\x6B\x59\x4D\x87\xD0\x68\x6A\x1E\x09\x3C\x59"
|
||||
#define X86_CODE16 "\x00\x00" // add byte ptr [bx + si], al
|
||||
#define X86_CODE64_SYSCALL "\x0f\x05" // SYSCALL
|
||||
|
||||
// memory address where emulation starts
|
||||
#define ADDRESS 0x1000000
|
||||
|
||||
// callback for tracing basic blocks
|
||||
static void hook_block(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
printf(">>> Tracing basic block at 0x%"PRIx64 ", block size = 0x%x\n", address, size);
|
||||
}
|
||||
|
||||
// callback for tracing instruction
|
||||
static void hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
int eflags;
|
||||
printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size);
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_EFLAGS, &eflags);
|
||||
printf(">>> --- EFLAGS is 0x%x\n", eflags);
|
||||
|
||||
// Uncomment below code to stop the emulation using uc_emu_stop()
|
||||
// if (address == 0x1000009)
|
||||
// uc_emu_stop(uc);
|
||||
}
|
||||
|
||||
// callback for tracing instruction
|
||||
static void hook_code64(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
uint64_t rip;
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_RIP, &rip);
|
||||
printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size);
|
||||
printf(">>> RIP is 0x%"PRIx64 "\n", rip);
|
||||
|
||||
// Uncomment below code to stop the emulation using uc_emu_stop()
|
||||
// if (address == 0x1000009)
|
||||
// uc_emu_stop(uc);
|
||||
}
|
||||
|
||||
static void hook_mem64(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t address, int size, int64_t value, void *user_data)
|
||||
{
|
||||
switch(type) {
|
||||
default: break;
|
||||
case UC_MEM_READ:
|
||||
printf(">>> Memory is being READ at 0x%"PRIx64 ", data size = %u\n",
|
||||
address, size);
|
||||
break;
|
||||
case UC_MEM_WRITE:
|
||||
printf(">>> Memory is being WRITE at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n",
|
||||
address, size, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_i386(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uint32_t tmp;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
int r_ecx = 0x1234; // ECX register
|
||||
int r_edx = 0x7890; // EDX register
|
||||
|
||||
printf("Emulate i386 code\n");
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
if (err) {
|
||||
printf("Failed on uc_open() with error returned: %u\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
if (uc_mem_write(uc, ADDRESS, X86_CODE32, sizeof(X86_CODE32) - 1)) {
|
||||
printf("Failed to write emulation code to memory, quit!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// initialize machine registers
|
||||
uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx);
|
||||
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
|
||||
|
||||
// tracing all basic blocks with customized callback
|
||||
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
|
||||
|
||||
// tracing all instruction by having @begin > @end
|
||||
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
|
||||
|
||||
// emulate machine code in infinite time
|
||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32) - 1, 0, 0);
|
||||
if (err) {
|
||||
printf("Failed on uc_emu_start() with error returned %u: %s\n",
|
||||
err, uc_strerror(err));
|
||||
}
|
||||
|
||||
// now print out some registers
|
||||
printf(">>> Emulation done. Below is the CPU context\n");
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx);
|
||||
uc_reg_read(uc, UC_X86_REG_EDX, &r_edx);
|
||||
printf(">>> ECX = 0x%x\n", r_ecx);
|
||||
printf(">>> EDX = 0x%x\n", r_edx);
|
||||
|
||||
// read from memory
|
||||
if (!uc_mem_read(uc, ADDRESS, &tmp, sizeof(tmp)))
|
||||
printf(">>> Read 4 bytes from [0x%x] = 0x%x\n", ADDRESS, tmp);
|
||||
else
|
||||
printf(">>> Failed to read 4 bytes from [0x%x]\n", ADDRESS);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
static void test_x86_64(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uc_hook trace1, trace2, trace3, trace4;
|
||||
|
||||
int64_t rax = 0x71f3029efd49d41d;
|
||||
int64_t rbx = 0xd87b45277f133ddb;
|
||||
int64_t rcx = 0xab40d1ffd8afc461;
|
||||
int64_t rdx = 0x919317b4a733f01;
|
||||
int64_t rsi = 0x4c24e753a17ea358;
|
||||
int64_t rdi = 0xe509a57d2571ce96;
|
||||
int64_t r8 = 0xea5b108cc2b9ab1f;
|
||||
int64_t r9 = 0x19ec097c8eb618c1;
|
||||
int64_t r10 = 0xec45774f00c5f682;
|
||||
int64_t r11 = 0xe17e9dbec8c074aa;
|
||||
int64_t r12 = 0x80f86a8dc0f6d457;
|
||||
int64_t r13 = 0x48288ca5671c5492;
|
||||
int64_t r14 = 0x595f72f6e4017f6e;
|
||||
int64_t r15 = 0x1efd97aea331cccc;
|
||||
|
||||
int64_t rsp = ADDRESS + 0x200000;
|
||||
|
||||
|
||||
printf("Emulate x86_64 code\n");
|
||||
|
||||
// Initialize emulator in X86-64bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
|
||||
if (err) {
|
||||
printf("Failed on uc_open() with error returned: %u\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
if (uc_mem_write(uc, ADDRESS, X86_CODE64, sizeof(X86_CODE64) - 1)) {
|
||||
printf("Failed to write emulation code to memory, quit!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// initialize machine registers
|
||||
uc_reg_write(uc, UC_X86_REG_RSP, &rsp);
|
||||
|
||||
uc_reg_write(uc, UC_X86_REG_RAX, &rax);
|
||||
uc_reg_write(uc, UC_X86_REG_RBX, &rbx);
|
||||
uc_reg_write(uc, UC_X86_REG_RCX, &rcx);
|
||||
uc_reg_write(uc, UC_X86_REG_RDX, &rdx);
|
||||
uc_reg_write(uc, UC_X86_REG_RSI, &rsi);
|
||||
uc_reg_write(uc, UC_X86_REG_RDI, &rdi);
|
||||
uc_reg_write(uc, UC_X86_REG_R8, &r8);
|
||||
uc_reg_write(uc, UC_X86_REG_R9, &r9);
|
||||
uc_reg_write(uc, UC_X86_REG_R10, &r10);
|
||||
uc_reg_write(uc, UC_X86_REG_R11, &r11);
|
||||
uc_reg_write(uc, UC_X86_REG_R12, &r12);
|
||||
uc_reg_write(uc, UC_X86_REG_R13, &r13);
|
||||
uc_reg_write(uc, UC_X86_REG_R14, &r14);
|
||||
uc_reg_write(uc, UC_X86_REG_R15, &r15);
|
||||
|
||||
// tracing all basic blocks with customized callback
|
||||
uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
|
||||
|
||||
// tracing all instructions in the range [ADDRESS, ADDRESS+20]
|
||||
uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code64, NULL, (uint64_t)ADDRESS, (uint64_t)(ADDRESS+20));
|
||||
|
||||
// tracing all memory WRITE access (with @begin > @end)
|
||||
uc_hook_add(uc, &trace3, UC_HOOK_MEM_WRITE, hook_mem64, NULL, (uint64_t)1, (uint64_t)0);
|
||||
|
||||
// tracing all memory READ access (with @begin > @end)
|
||||
uc_hook_add(uc, &trace4, UC_HOOK_MEM_READ, hook_mem64, NULL, (uint64_t)1, (uint64_t)0);
|
||||
|
||||
// emulate machine code in infinite time (last param = 0), or when
|
||||
// finishing all the code.
|
||||
err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE64) - 1, 0, 0);
|
||||
if (err) {
|
||||
printf("Failed on uc_emu_start() with error returned %u: %s\n",
|
||||
err, uc_strerror(err));
|
||||
}
|
||||
|
||||
// now print out some registers
|
||||
printf(">>> Emulation done. Below is the CPU context\n");
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_RAX, &rax);
|
||||
uc_reg_read(uc, UC_X86_REG_RBX, &rbx);
|
||||
uc_reg_read(uc, UC_X86_REG_RCX, &rcx);
|
||||
uc_reg_read(uc, UC_X86_REG_RDX, &rdx);
|
||||
uc_reg_read(uc, UC_X86_REG_RSI, &rsi);
|
||||
uc_reg_read(uc, UC_X86_REG_RDI, &rdi);
|
||||
uc_reg_read(uc, UC_X86_REG_R8, &r8);
|
||||
uc_reg_read(uc, UC_X86_REG_R9, &r9);
|
||||
uc_reg_read(uc, UC_X86_REG_R10, &r10);
|
||||
uc_reg_read(uc, UC_X86_REG_R11, &r11);
|
||||
uc_reg_read(uc, UC_X86_REG_R12, &r12);
|
||||
uc_reg_read(uc, UC_X86_REG_R13, &r13);
|
||||
uc_reg_read(uc, UC_X86_REG_R14, &r14);
|
||||
uc_reg_read(uc, UC_X86_REG_R15, &r15);
|
||||
|
||||
printf(">>> RAX = 0x%" PRIx64 "\n", rax);
|
||||
printf(">>> RBX = 0x%" PRIx64 "\n", rbx);
|
||||
printf(">>> RCX = 0x%" PRIx64 "\n", rcx);
|
||||
printf(">>> RDX = 0x%" PRIx64 "\n", rdx);
|
||||
printf(">>> RSI = 0x%" PRIx64 "\n", rsi);
|
||||
printf(">>> RDI = 0x%" PRIx64 "\n", rdi);
|
||||
printf(">>> R8 = 0x%" PRIx64 "\n", r8);
|
||||
printf(">>> R9 = 0x%" PRIx64 "\n", r9);
|
||||
printf(">>> R10 = 0x%" PRIx64 "\n", r10);
|
||||
printf(">>> R11 = 0x%" PRIx64 "\n", r11);
|
||||
printf(">>> R12 = 0x%" PRIx64 "\n", r12);
|
||||
printf(">>> R13 = 0x%" PRIx64 "\n", r13);
|
||||
printf(">>> R14 = 0x%" PRIx64 "\n", r14);
|
||||
printf(">>> R15 = 0x%" PRIx64 "\n", r15);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
// dynamically load shared library
|
||||
#ifdef DYNLOAD
|
||||
if (!uc_dyn_load(NULL, 0)) {
|
||||
printf("Error dynamically loading shared library.\n");
|
||||
printf("Please check that unicorn.dll/unicorn.so is available as well as\n");
|
||||
printf("any other dependent dll/so files.\n");
|
||||
printf("The easiest way is to place them in the same directory as this app.\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
while(1) {
|
||||
test_i386();
|
||||
test_x86_64();
|
||||
}
|
||||
|
||||
// dynamically free shared library
|
||||
#ifdef DYNLOAD
|
||||
uc_dyn_free();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
13
tests/regress/mips_cp1.py
Normal file
13
tests/regress/mips_cp1.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from unicorn import *
|
||||
from unicorn.mips_const import *
|
||||
|
||||
|
||||
|
||||
# .text:00416CB0 cfc1 $v1, FCSR
|
||||
shellcode = [0x44, 0x43, 0xF8, 0x00]
|
||||
base = 0x416CB0
|
||||
|
||||
uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN)
|
||||
uc.mem_map(0x416000, 0x1000)
|
||||
uc.mem_write(base, bytes(shellcode))
|
||||
uc.emu_start(base, base + len(shellcode))
|
||||
21
tests/regress/x86_ld_crash.py
Executable file
21
tests/regress/x86_ld_crash.py
Executable file
File diff suppressed because one or more lines are too long
21
tests/regress/x86_set_ip.py
Normal file
21
tests/regress/x86_set_ip.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from unicorn import *
|
||||
from unicorn.x86_const import *
|
||||
|
||||
count = 0
|
||||
|
||||
def cb(uc, addr, sz, data):
|
||||
global count
|
||||
count += 1
|
||||
print(f"addr: {hex(addr)} count: {count}")
|
||||
if count == 5:
|
||||
uc.emu_stop()
|
||||
else:
|
||||
uc.reg_write(UC_X86_REG_RIP, 0x2000)
|
||||
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_64)
|
||||
|
||||
mu.mem_map(0x1000, 0x4000)
|
||||
mu.mem_write(0x1000, b"\x90" * 5)
|
||||
mu.mem_write(0x2000, b"\x90" * 5)
|
||||
mu.hook_add(UC_HOOK_CODE, cb)
|
||||
mu.emu_start(0x1000, 0x2000+1, 0, 0)
|
||||
3
tests/unit/.gitignore
vendored
3
tests/unit/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
!*.c
|
||||
test_*
|
||||
*.bin
|
||||
@@ -1,65 +0,0 @@
|
||||
CFLAGS += -Wall -Werror -Wno-unused-function -g
|
||||
CFLAGS += -D__USE_MINGW_ANSI_STDIO=1
|
||||
CFLAGS += -L ../../ -I ../../include
|
||||
CFLAGS += -L ../../cmocka/src -I ../../cmocka/include
|
||||
CFLAGS += -L /usr/local/lib -I /usr/local/include
|
||||
ASFLAGS += --32
|
||||
OBJCOPY = objcopy
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
UNAME_M := $(shell uname -m)
|
||||
LDLIBS += -pthread
|
||||
ifeq ($(UNAME_S), Linux)
|
||||
LDLIBS += -lrt
|
||||
else ifeq ($(UNAME_S), Darwin)
|
||||
OBJCOPY = gobjcopy
|
||||
ASFLAGS = -arch i386
|
||||
endif
|
||||
|
||||
LDLIBS += -lcmocka -lunicorn
|
||||
|
||||
EXECUTE_VARS = LD_LIBRARY_PATH=../../cmocka/src:../../ DYLD_LIBRARY_PATH=../../
|
||||
|
||||
ifeq ($(UNICORN_ASAN),yes)
|
||||
CC = clang -fsanitize=address -fno-omit-frame-pointer
|
||||
CXX = clang++ -fsanitize=address -fno-omit-frame-pointer
|
||||
AR = llvm-ar
|
||||
LDFLAGS := -fsanitize=address ${LDFLAGS}
|
||||
endif
|
||||
|
||||
ALL_TESTS_SOURCES = $(wildcard *.c)
|
||||
TEST_ASSEMBLY = $(wildcard *.s)
|
||||
TEST_PROGS = $(TEST_ASSEMBLY:%.s=%.o)
|
||||
TEST_BINS = $(TEST_PROGS:%.o=%.bin)
|
||||
ALL_TESTS = $(ALL_TESTS_SOURCES:%.c=%)
|
||||
|
||||
ifneq (,$(findstring x86,$(UNAME_M)))
|
||||
ALL_TESTS += $(TEST_BINS)
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
all: ${ALL_TESTS}
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf ${ALL_TESTS}
|
||||
|
||||
%.bin: %.o
|
||||
${OBJCOPY} -O binary $^ $@
|
||||
hexdump -C $@
|
||||
|
||||
.PHONY: test
|
||||
test: all
|
||||
${EXECUTE_VARS} ./test_sanity
|
||||
${EXECUTE_VARS} ./test_x86
|
||||
${EXECUTE_VARS} ./test_mem_map
|
||||
${EXECUTE_VARS} ./test_mem_map_ptr
|
||||
${EXECUTE_VARS} ./test_mem_high
|
||||
${EXECUTE_VARS} ./test_multihook
|
||||
${EXECUTE_VARS} ./test_pc_change
|
||||
${EXECUTE_VARS} ./test_hookcounts
|
||||
echo "skipping test_tb_x86"
|
||||
echo "skipping test_x86_soft_paging"
|
||||
echo "skipping test_hang"
|
||||
echo "skipping test_x86_sh1_enter_leave"
|
||||
echo "skipping test_x86_rip_bug"
|
||||
1837
tests/unit/acutest.h
Normal file
1837
tests/unit/acutest.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
.text
|
||||
sidt (esp)
|
||||
sgdt (esp+6)
|
||||
@@ -1,6 +0,0 @@
|
||||
dec %eax
|
||||
mov (%eax), %eax
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@@ -1,9 +0,0 @@
|
||||
.text
|
||||
inc %ecx
|
||||
inc %ecx
|
||||
inc %ecx
|
||||
inc %ecx
|
||||
inc %ecx
|
||||
inc %ecx
|
||||
inc %edx
|
||||
inc %edx
|
||||
@@ -1,90 +0,0 @@
|
||||
mov %esp,%ecx
|
||||
fxch %st(5)
|
||||
fnstenv -0xc(%ecx)
|
||||
pop %ebp
|
||||
push %ebp
|
||||
pop %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
inc %ebx
|
||||
inc %ebx
|
||||
inc %ebx
|
||||
inc %ebx
|
||||
inc %ebx
|
||||
inc %ebx
|
||||
aaa
|
||||
push %ecx
|
||||
pop %edx
|
||||
push $0x41
|
||||
pop %eax
|
||||
push %eax
|
||||
xor %al,0x30(%ecx)
|
||||
inc %ecx
|
||||
imul $0x51,0x41(%ecx),%eax
|
||||
xor 0x42(%ecx),%al
|
||||
xor 0x42(%edx),%al
|
||||
xor %al,0x42(%edx)
|
||||
inc %ecx
|
||||
inc %edx
|
||||
pop %eax
|
||||
push %eax
|
||||
cmp %al,0x42(%ecx)
|
||||
jne .+0x4c
|
||||
dec %ecx
|
||||
push %ecx
|
||||
push %ecx
|
||||
push %ecx
|
||||
push %edx
|
||||
inc %edi
|
||||
xor 0x34(%edi),%eax
|
||||
push %ecx
|
||||
push %ebp
|
||||
push %ecx
|
||||
push %esi
|
||||
push %eax
|
||||
inc %edi
|
||||
inc %edi
|
||||
cmp %al,0x39(%edi)
|
||||
push %eax
|
||||
dec %edx
|
||||
push %eax
|
||||
dec %ebx
|
||||
push %eax
|
||||
dec %esp
|
||||
push %eax
|
||||
dec %ebp
|
||||
push %eax
|
||||
dec %esi
|
||||
push %eax
|
||||
dec %edi
|
||||
push %eax
|
||||
push %eax
|
||||
push %eax
|
||||
xor %eax, 0x42(%edi)
|
||||
inc %edi
|
||||
inc %edx
|
||||
push %eax
|
||||
xor $0x50,%al
|
||||
pop %edx
|
||||
push %eax
|
||||
inc %ebp
|
||||
push %ecx
|
||||
push %edx
|
||||
inc %esi
|
||||
xor 0x31(%edi),%al
|
||||
push %eax
|
||||
dec %ebp
|
||||
push %ecx
|
||||
push %ecx
|
||||
push %eax
|
||||
dec %esi
|
||||
inc %ecx
|
||||
inc %ecx
|
||||
249
tests/unit/test_arm.c
Normal file
249
tests/unit/test_arm.c
Normal file
@@ -0,0 +1,249 @@
|
||||
#include "unicorn_test.h"
|
||||
|
||||
const uint64_t code_start = 0x1000;
|
||||
const uint64_t code_len = 0x4000;
|
||||
|
||||
static void uc_common_setup(uc_engine** uc, uc_arch arch, uc_mode mode, const char* code, uint64_t size) {
|
||||
OK(uc_open(arch, mode, uc));
|
||||
OK(uc_mem_map(*uc, code_start, code_len, UC_PROT_ALL));
|
||||
OK(uc_mem_write(*uc, code_start, code, size));
|
||||
}
|
||||
|
||||
static void test_arm_nop() {
|
||||
uc_engine* uc;
|
||||
char code[] = "\x00\xf0\x20\xe3"; // nop
|
||||
int r_r0 = 0x1234;
|
||||
int r_r2 = 0x6789;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_ARM, code, sizeof(code) - 1);
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R0, &r_r0));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R2, &r_r2));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R0, &r_r0));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R2, &r_r2));
|
||||
TEST_CHECK(r_r0 == 0x1234);
|
||||
TEST_CHECK(r_r2 == 0x6789);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_thumb_sub() {
|
||||
uc_engine* uc;
|
||||
char code[] = "\x83\xb0"; // sub sp, #0xc
|
||||
int r_sp = 0x1234;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB, code, sizeof(code) - 1);
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SP, &r_sp));
|
||||
|
||||
OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_SP, &r_sp));
|
||||
TEST_CHECK(r_sp == 0x1228);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_armeb_sub() {
|
||||
uc_engine* uc;
|
||||
char code[] = "\xe3\xa0\x00\x37\xe0\x42\x10\x03"; // mov r0, #0x37; sub r1, r2, r3
|
||||
int r_r0 = 0x1234;
|
||||
int r_r2 = 0x6789;
|
||||
int r_r3 = 0x3333;
|
||||
int r_r1;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_ARM | UC_MODE_BIG_ENDIAN, code, sizeof(code) - 1);
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R0, &r_r0));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R2, &r_r2));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R3, &r_r3));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R0, &r_r0));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R1, &r_r1));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R2, &r_r2));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R3, &r_r3));
|
||||
|
||||
TEST_CHECK(r_r0 == 0x37);
|
||||
TEST_CHECK(r_r2 == 0x6789);
|
||||
TEST_CHECK(r_r3 == 0x3333);
|
||||
TEST_CHECK(r_r1 == 0x3456);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_thumbeb_sub() {
|
||||
uc_engine* uc;
|
||||
char code[] = "\xb0\x83"; // sub sp, #0xc
|
||||
int r_sp = 0x1234;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB | UC_MODE_BIG_ENDIAN, code, sizeof(code) - 1);
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SP, &r_sp));
|
||||
|
||||
OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_SP, &r_sp));
|
||||
TEST_CHECK(r_sp == 0x1228);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_thumb_ite_count_callback(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) {
|
||||
uint64_t* count = (uint64_t*)user_data;
|
||||
|
||||
(*count) += 1;
|
||||
}
|
||||
|
||||
static void test_arm_thumb_ite() {
|
||||
uc_engine* uc;
|
||||
uc_hook hook;
|
||||
char code[] = "\x9a\x42\x15\xbf\x00\x9a\x01\x9a\x78\x23\x15\x23"; // cmp r2, r3; itete ne; ldrne r2, [sp]; ldreq r2, [sp,#4]; movne r3, #0x78; moveq r3, #0x15
|
||||
int r_sp = 0x8000;
|
||||
int r_r2 = 0;
|
||||
int r_r3 = 1;
|
||||
int r_pc = 0;
|
||||
uint64_t count = 0;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB, code, sizeof(code) - 1);
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SP, &r_sp));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R2, &r_r2));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R3, &r_r3));
|
||||
|
||||
OK(uc_mem_map(uc, r_sp, 0x1000, UC_PROT_ALL));
|
||||
r_r2 = 0x68;
|
||||
OK(uc_mem_write(uc, r_sp, &r_r2, 4));
|
||||
r_r2 = 0x4d;
|
||||
OK(uc_mem_write(uc, r_sp + 4, &r_r2, 4));
|
||||
|
||||
OK(uc_hook_add(uc, &hook, UC_HOOK_CODE, test_arm_thumb_ite_count_callback, &count, 1, 0));
|
||||
|
||||
// Execute four instructions at a time.
|
||||
OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R2, &r_r2));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R3, &r_r3));
|
||||
TEST_CHECK(r_r2 == 0x68);
|
||||
TEST_CHECK(count == 4);
|
||||
|
||||
r_pc = code_start;
|
||||
r_r2 = 0;
|
||||
count = 0;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R2, &r_r2));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R3, &r_r3));
|
||||
for (int i = 0; i < 6 && r_pc < code_start + sizeof(code) - 1; i++) {
|
||||
// Execute one instruction at a time.
|
||||
OK(uc_emu_start(uc, r_pc | 1, code_start + sizeof(code) - 1, 0, 1));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_PC, &r_pc));
|
||||
}
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R2, &r_r2));
|
||||
|
||||
TEST_CHECK(r_r2 == 0x68);
|
||||
TEST_CHECK(r_r3 == 0x78);
|
||||
TEST_CHECK(count == 4);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_m_thumb_mrs() {
|
||||
uc_engine* uc;
|
||||
char code[] = "\xef\xf3\x14\x80\xef\xf3\x00\x81"; // mrs r0, control; mrs r1, apsr
|
||||
uint32_t r_control = 0b10;
|
||||
uint32_t r_apsr = (0b10101 << 27);
|
||||
uint32_t r_r0, r_r1;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB | UC_MODE_MCLASS, code, sizeof(code) - 1);
|
||||
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CONTROL, &r_control));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_APSR_NZCVQ, &r_apsr));
|
||||
OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R0, &r_r0));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R1, &r_r1));
|
||||
|
||||
TEST_CHECK(r_r0 == 0b10);
|
||||
TEST_CHECK(r_r1 == (0b10101 << 27));
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_m_control() {
|
||||
uc_engine* uc;
|
||||
int r_control, r_msp, r_psp;
|
||||
|
||||
OK(uc_open(UC_ARCH_ARM, UC_MODE_THUMB | UC_MODE_MCLASS, &uc));
|
||||
|
||||
r_control = 0; // Make sure we are using MSP.
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CONTROL, &r_control));
|
||||
|
||||
r_msp = 0x1000;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R13, &r_msp));
|
||||
|
||||
r_control = 0b10; // Make the switch.
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CONTROL, &r_control));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R13, &r_psp));
|
||||
TEST_CHECK(r_psp != r_msp);
|
||||
|
||||
r_psp = 0x2000;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R13, &r_psp));
|
||||
|
||||
r_control = 0; // Switch again
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CONTROL, &r_control));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R13, &r_msp));
|
||||
TEST_CHECK(r_psp != r_msp);
|
||||
TEST_CHECK(r_msp == 0x1000);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
//
|
||||
// Some notes:
|
||||
// Qemu raise a special exception EXCP_EXCEPTION_EXIT to handle the EXC_RETURN. We can't
|
||||
// help user handle EXC_RETURN since unicorn is designed not to handle any CPU exception.
|
||||
//
|
||||
static void test_arm_m_exc_return_hook_interrupt(uc_engine* uc, int intno, void* data) {
|
||||
int r_pc;
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_PC, &r_pc));
|
||||
TEST_CHECK(intno == 8); // EXCP_EXCEPTION_EXIT: Return from v7M exception.
|
||||
TEST_CHECK((r_pc | 1) == 0xFFFFFFFD);
|
||||
OK(uc_emu_stop(uc));
|
||||
}
|
||||
|
||||
static void test_arm_m_exc_return() {
|
||||
uc_engine* uc;
|
||||
char code[] = "\x6f\xf0\x02\x00\x00\x47"; // mov r0, #0xFFFFFFFD; bx r0;
|
||||
int r_ipsr;
|
||||
int r_sp = 0x8000;
|
||||
uc_hook hook;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB | UC_MODE_MCLASS, code, sizeof(code) - 1);
|
||||
OK(uc_mem_map(uc, r_sp - 0x1000, 0x1000, UC_PROT_ALL));
|
||||
OK(uc_hook_add(uc, &hook, UC_HOOK_INTR, test_arm_m_exc_return_hook_interrupt, NULL, 0, 0));
|
||||
|
||||
r_sp -= 0x1c;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SP, &r_sp));
|
||||
|
||||
r_ipsr = 16; // We are in whatever exception.
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_IPSR, &r_ipsr));
|
||||
|
||||
OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code) - 1, 0, 2)); // Just execute 2 instructions.
|
||||
|
||||
OK(uc_hook_del(uc, hook));
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
TEST_LIST = {
|
||||
{ "test_arm_nop", test_arm_nop },
|
||||
{ "test_arm_thumb_sub", test_arm_thumb_sub },
|
||||
{ "test_armeb_sub", test_armeb_sub },
|
||||
{ "test_arm_thumbeb_sub", test_arm_thumbeb_sub },
|
||||
{ "test_arm_thumb_ite", test_arm_thumb_ite },
|
||||
{ "test_arm_m_thumb_mrs", test_arm_m_thumb_mrs },
|
||||
{ "test_arm_m_control", test_arm_m_control },
|
||||
{ "test_arm_m_exc_return", test_arm_m_exc_return },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
8
tests/unit/test_arm64.c
Normal file
8
tests/unit/test_arm64.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "unicorn_test.h"
|
||||
|
||||
const uint64_t code_start = 0x1000;
|
||||
const uint64_t code_len = 0x4000;
|
||||
|
||||
TEST_LIST = {
|
||||
{ NULL, NULL }
|
||||
};
|
||||
@@ -1,127 +0,0 @@
|
||||
#include "unicorn_test.h"
|
||||
#include <unicorn/unicorn.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void test_idt_gdt_i386(/*void **state*/)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uint8_t buf[6];
|
||||
uc_x86_mmr idt;
|
||||
uc_x86_mmr gdt;
|
||||
uc_x86_mmr ldt;
|
||||
uc_x86_mmr tr;
|
||||
|
||||
struct stat info;
|
||||
char * code = read_file("gdt_idx.bin", &info);
|
||||
|
||||
const uint64_t address = 0x1000000;
|
||||
|
||||
int r_esp = address + 0x1000 - 0x100; // initial esp
|
||||
|
||||
idt.base = 0x12345678;
|
||||
idt.limit = 0xabcd;
|
||||
gdt.base = 0x87654321;
|
||||
gdt.limit = 0xdcba;
|
||||
|
||||
ldt.base = 0xfedcba98;
|
||||
ldt.limit = 0x11111111;
|
||||
ldt.selector = 0x3333;
|
||||
ldt.flags = 0x55555555;
|
||||
|
||||
tr.base = 0x22222222;
|
||||
tr.limit = 0x33333333;
|
||||
tr.selector = 0x4444;
|
||||
tr.flags = 0x66666666;
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
// map 1 page memory for this emulation
|
||||
err = uc_mem_map(uc, address, 0x1000, UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
err = uc_mem_write(uc, address, code, info.st_size);
|
||||
uc_assert_success(err);
|
||||
|
||||
// initialize machine registers
|
||||
err = uc_reg_write(uc, UC_X86_REG_ESP, &r_esp);
|
||||
uc_assert_success(err);
|
||||
err = uc_reg_write(uc, UC_X86_REG_IDTR, &idt);
|
||||
uc_assert_success(err);
|
||||
err = uc_reg_write(uc, UC_X86_REG_GDTR, &gdt);
|
||||
uc_assert_success(err);
|
||||
err = uc_reg_write(uc, UC_X86_REG_LDTR, &ldt);
|
||||
uc_assert_success(err);
|
||||
err = uc_reg_write(uc, UC_X86_REG_TR, &tr);
|
||||
uc_assert_success(err);
|
||||
|
||||
memset(&idt, 0, sizeof(idt));
|
||||
memset(&gdt, 0, sizeof(gdt));
|
||||
memset(&ldt, 0, sizeof(ldt));
|
||||
memset(&tr, 0, sizeof(tr));
|
||||
|
||||
// emulate machine code in infinite time
|
||||
err = uc_emu_start(uc, address, address+sizeof(code)-1, 0, 0);
|
||||
uc_assert_success(err);
|
||||
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_IDTR, &idt);
|
||||
assert(idt.base == 0x12345678);
|
||||
assert(idt.limit == 0xabcd);
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_GDTR, &gdt);
|
||||
assert(gdt.base == 0x87654321);
|
||||
assert(gdt.limit == 0xdcba);
|
||||
|
||||
//userspace can only set ldt selector, remainder are loaded from
|
||||
//GDT/LDT, but we allow all to emulator user
|
||||
uc_reg_read(uc, UC_X86_REG_LDTR, &ldt);
|
||||
assert(ldt.base == 0xfedcba98);
|
||||
assert(ldt.limit == 0x11111111);
|
||||
assert(ldt.selector == 0x3333);
|
||||
assert(ldt.flags == 0x55555555);
|
||||
|
||||
//userspace can only set tr selector, remainder are loaded from
|
||||
//GDT/LDT, but we allow all to emulator user
|
||||
uc_reg_read(uc, UC_X86_REG_TR, &tr);
|
||||
assert(tr.base == 0x22222222);
|
||||
assert(tr.limit == 0x33333333);
|
||||
assert(tr.selector == 0x4444);
|
||||
assert(tr.flags == 0x66666666);
|
||||
|
||||
// read from memory
|
||||
err = uc_mem_read(uc, r_esp, buf, 6);
|
||||
uc_assert_success(err);
|
||||
|
||||
assert(memcmp(buf, "\xcd\xab\x78\x56\x34\x12", 6) == 0);
|
||||
|
||||
// read from memory
|
||||
err = uc_mem_read(uc, r_esp + 6, buf, 6);
|
||||
uc_assert_success(err);
|
||||
|
||||
assert(memcmp(buf, "\xba\xdc\x21\x43\x65\x87", 6) == 0);
|
||||
|
||||
uc_close(uc);
|
||||
free(code);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int main(void) {
|
||||
/*
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(test_idt_gdt_i386)
|
||||
};
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
*/
|
||||
test_idt_gdt_i386();
|
||||
|
||||
fprintf(stderr, "success\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
refer to issue #575.
|
||||
to run correctly unicorn needs to be compiled for AArch64.
|
||||
*/
|
||||
|
||||
#include "unicorn_test.h"
|
||||
#include <stdio.h>
|
||||
#include "unicorn/unicorn.h"
|
||||
|
||||
uint64_t trunc_page(uint64_t addr)
|
||||
{
|
||||
return (addr & ~(4095));
|
||||
}
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int init(void **state)
|
||||
{
|
||||
printf("[+] Initializing Unicorn...\n");
|
||||
uc_engine *uc;
|
||||
|
||||
if (uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc) != UC_ERR_OK) {
|
||||
printf("Error on open. Be sure that your unicorn library supports AArch64.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*state = uc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
printf("[+] Exiting...\n");
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uc_close(uc);
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_hang(void **state)
|
||||
{
|
||||
uint32_t code[] = {
|
||||
0xd503201f, /* NOP */
|
||||
0xd503201f, /* NOP */
|
||||
0xd503201f, /* NOP */
|
||||
0xaa0103e0 /* MOV X0, X1 */
|
||||
};
|
||||
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uint64_t x0 = 0;
|
||||
uint64_t x1 = 1;
|
||||
|
||||
/*
|
||||
* emulation will hang if some instruction hits every quarter of a page,
|
||||
* i.e. these offsets:
|
||||
* 0x1400, 0x1800, 0x1c00, 0x2000
|
||||
*
|
||||
* in this test, the code to be emulated is mapped just before the 0x1400
|
||||
* offset, so that the final instruction emulated (MOV X0, X1) hits the offset,
|
||||
* causing the hang.
|
||||
* If you try to write the code just four bytes behind, the hang doesn't occur.
|
||||
*
|
||||
* So far, this strange behaviour has only been observed with AArch64 Unicorn APIs.
|
||||
*/
|
||||
|
||||
uint64_t addr = 0x13f0; // try to map at (0x13f0 - 0x4) and the hang doesn't occur
|
||||
uint64_t trunc_addr = trunc_page(addr); // round down to nearest page
|
||||
|
||||
uc_mem_map(uc, trunc_addr, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
|
||||
if (uc_mem_write(uc, addr, &code, sizeof(code))) {
|
||||
printf("error on write\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uc_reg_write(uc, UC_ARM64_REG_X0, &x0);
|
||||
uc_reg_write(uc, UC_ARM64_REG_X1, &x1);
|
||||
|
||||
if (uc_emu_start(uc, addr, addr + sizeof(code), 0, 0)) {
|
||||
printf("error on start\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uc_reg_read(uc, UC_ARM64_REG_X0, &x0);
|
||||
uc_reg_read(uc, UC_ARM64_REG_X1, &x1);
|
||||
|
||||
printf("x0: %"PRIx64"\n", x0);
|
||||
printf("x1: %"PRIx64"\n", x1);
|
||||
}
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(test_hang, init, teardown),
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);;
|
||||
}
|
||||
@@ -1,281 +0,0 @@
|
||||
// Test hook evocation count
|
||||
//
|
||||
// Objective is to demonstrate finer duration control of
|
||||
// emulation by counts of instruction code
|
||||
//
|
||||
#include "unicorn_test.h"
|
||||
#include "unicorn/unicorn.h"
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
#define OK(x) uc_assert_success(x)
|
||||
|
||||
volatile int expected_instructions = 0;
|
||||
volatile int total_instructions = 0;
|
||||
|
||||
|
||||
// NOTE: It would appear that this UC_HOOK_CODE is being done before the
|
||||
// uc_count_fb hook.
|
||||
// So, termination by uc->emu_count has not been done yet here...
|
||||
static void test_code_hook(uc_engine *uc,
|
||||
uint64_t address,
|
||||
uint32_t size,
|
||||
void *user_data)
|
||||
{
|
||||
|
||||
++total_instructions;
|
||||
if (total_instructions == expected_instructions)
|
||||
{
|
||||
uc_emu_stop(uc);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("instruction at 0x%"PRIx64": ", address);
|
||||
uint8_t tmp[256];
|
||||
if (!uc_mem_read(uc, address, tmp, size)) {
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
printf("0x%x ", tmp[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int setup32(void **state)
|
||||
{
|
||||
uc_hook trace1;
|
||||
uc_engine *uc;
|
||||
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
|
||||
*state = uc;
|
||||
|
||||
// trace all instructions
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, test_code_hook, NULL, 1, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
OK(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void
|
||||
test_hook_count(uc_engine *uc,
|
||||
const uint8_t *code,
|
||||
int start_offset,
|
||||
int code_length,
|
||||
int count)
|
||||
{
|
||||
|
||||
#define BASEADDR 0x1000000
|
||||
#define MEMSIZE (2 * 1024 * 1024)
|
||||
|
||||
uint64_t address = BASEADDR + (count * MEMSIZE);
|
||||
total_instructions = 0;
|
||||
|
||||
#undef BASEADDR
|
||||
|
||||
// map a new 2MB memory for this emulation
|
||||
OK(uc_mem_map(uc, address, MEMSIZE, UC_PROT_ALL));
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
OK(uc_mem_write(uc, address, code, code_length));
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Address: %"PRIx64"\n", address);
|
||||
printf("Start : %"PRIx64"\n", address + start_offset);
|
||||
printf("End : %"PRIx64"\n", address + code_length - 1);
|
||||
printf("Count : %d\n", count);
|
||||
#endif
|
||||
expected_instructions = count;
|
||||
OK(uc_emu_start(uc,
|
||||
address+start_offset,
|
||||
address+code_length,
|
||||
0,
|
||||
count));
|
||||
|
||||
assert_int_equal(expected_instructions, total_instructions);
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
OK(uc_mem_unmap(uc, address, MEMSIZE));
|
||||
}
|
||||
|
||||
|
||||
/* Perform fine-grain emulation control of exactly 1 instruction */
|
||||
/* of 1-opcode code space*/
|
||||
static void test_hook_count_1_begin(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint8_t code[] = {
|
||||
0x41, // inc ECX @0x1000000
|
||||
};
|
||||
int code_length = sizeof(code);
|
||||
int start_offset = 0;
|
||||
int ins_count = 1;
|
||||
|
||||
test_hook_count(uc, code, start_offset, code_length, ins_count);
|
||||
}
|
||||
|
||||
|
||||
/* Perform fine-grain emulation control of exactly 1 instruction */
|
||||
static void test_hook_count_1_midpoint(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint8_t code[] = {
|
||||
0x41, // inc ECX @0x1000000
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX @0x1000003
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x42, // inc EDX @0x1000006
|
||||
0x42, // inc EDX
|
||||
};
|
||||
int code_length = sizeof(code);
|
||||
int start_offset = code_length/2;
|
||||
int ins_count = 1;
|
||||
|
||||
test_hook_count(uc, code, start_offset, code_length, ins_count);
|
||||
}
|
||||
|
||||
|
||||
/* Perform fine-grain emulation control of exactly 1 instruction */
|
||||
static void test_hook_count_1_end(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint8_t code[] = {
|
||||
0x41, // inc ECX @0x1000000
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX @0x1000003
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x42, // inc EDX @0x1000006
|
||||
0x42, // inc EDX
|
||||
};
|
||||
int code_length = sizeof(code);
|
||||
int start_offset = code_length - 1;
|
||||
int ins_count = 1;
|
||||
|
||||
test_hook_count(uc, code, start_offset, code_length, ins_count);
|
||||
}
|
||||
|
||||
|
||||
/* Perform fine-grain emulation control over a range of */
|
||||
/* varied instruction steps. */
|
||||
static void test_hook_count_range(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint8_t code[] = {
|
||||
0x41, // inc ECX @0x1000000
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX @0x1000003
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x42, // inc EDX @0x1000006
|
||||
0x42, // inc EDX
|
||||
};
|
||||
int code_length = sizeof(code);
|
||||
int start_offset;
|
||||
int ins_count = 2;
|
||||
|
||||
for (start_offset = 2; start_offset < (code_length - ins_count); start_offset++)
|
||||
{
|
||||
printf("Iteration %d\n", start_offset);
|
||||
test_hook_count(uc, code, start_offset, code_length, ins_count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void test_hook_count_end(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint8_t code[] = {
|
||||
0x41, // inc ECX @0x1000000
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX @0x1000003
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x42, // inc EDX @0x1000006
|
||||
0x42, // inc EDX
|
||||
};
|
||||
int code_length = sizeof(code);
|
||||
int ins_count = 3;
|
||||
int start_offset = sizeof(code) - ins_count;
|
||||
|
||||
test_hook_count(uc, code, start_offset, code_length, ins_count);
|
||||
}
|
||||
|
||||
|
||||
static void test_hook_count_begins(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint8_t code[] = {
|
||||
0x41, // inc ECX @0x1000000
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX @0x1000003
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x42, // inc EDX @0x1000006
|
||||
0x42, // inc EDX
|
||||
};
|
||||
int code_length = sizeof(code);
|
||||
int ins_count = 3;
|
||||
int start_offset = 0;
|
||||
|
||||
test_hook_count(uc, code, start_offset, code_length, ins_count);
|
||||
}
|
||||
|
||||
|
||||
static void test_hook_count_midpoint(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint8_t code[] = {
|
||||
0x41, // inc ECX @0x1000000
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX @0x1000003
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x42, // inc EDX @0x1000006
|
||||
0x42, // inc EDX
|
||||
};
|
||||
int code_length = sizeof(code);
|
||||
int ins_count = 3;
|
||||
int start_offset = 2;
|
||||
|
||||
test_hook_count(uc, code, start_offset, code_length, ins_count);
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(test_hook_count_1_begin, setup32, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_hook_count_1_midpoint, setup32, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_hook_count_1_end, setup32, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_hook_count_begins, setup32, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_hook_count_range, setup32, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_hook_count_midpoint, setup32, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_hook_count_end, setup32, teardown),
|
||||
};
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
8
tests/unit/test_m68k.c
Normal file
8
tests/unit/test_m68k.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "unicorn_test.h"
|
||||
|
||||
const uint64_t code_start = 0x1000;
|
||||
const uint64_t code_len = 0x4000;
|
||||
|
||||
TEST_LIST = {
|
||||
{ NULL, NULL }
|
||||
};
|
||||
@@ -1,131 +0,0 @@
|
||||
/**
|
||||
* Unicorn memory API tests
|
||||
*
|
||||
* This tests memory read/write and map/unmap functionality.
|
||||
* One is necessary for doing the other.
|
||||
*/
|
||||
#include "unicorn_test.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "unicorn/unicorn.h"
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int setup(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// mapping the last pages will silently fail
|
||||
static void test_last_page_map(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uint8_t writebuf[0x10];
|
||||
memset(writebuf, 0xCC, sizeof(writebuf));
|
||||
|
||||
const uint64_t mem_len = 0x1000;
|
||||
const uint64_t last_page = 0xfffffffffffff000;
|
||||
uc_assert_success(uc_mem_map(uc, last_page, mem_len, UC_PROT_NONE));
|
||||
uc_assert_success(uc_mem_write(uc, last_page, writebuf, sizeof(writebuf)));
|
||||
}
|
||||
|
||||
// segfaults with NULL-deref (caused by UC_PROT_NONE)
|
||||
static void test_nullptr_deref_wrong_perms(void **state){
|
||||
uc_engine *uc = *state;
|
||||
const uint64_t base_addr = 0x400000;
|
||||
uc_assert_success(uc_mem_map(uc, base_addr, 4096, UC_PROT_NONE));
|
||||
uc_emu_start(uc, base_addr, base_addr + 1, 0, 0);
|
||||
}
|
||||
|
||||
static int number_of_memory_reads = 0;
|
||||
|
||||
static void hook_mem64(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *user_data)
|
||||
{
|
||||
number_of_memory_reads += 1;
|
||||
printf(">>> Memory is being accessed at 0x%"PRIx64 ", data size = %u\n", address, size);
|
||||
}
|
||||
|
||||
//if a read is performed from a big address whith a non-zero last digit, multiple read events are triggered
|
||||
static void test_high_address_reads(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace2;
|
||||
|
||||
uint64_t addr = 0x0010000000000001;
|
||||
//addr = 0x0010000000000000; // uncomment to fix wrong? behaviour
|
||||
//addr = 90000000; // uncomment to fix wrong? behaviour
|
||||
//
|
||||
uc_mem_map(uc, addr-(addr%4096), 4096*2, UC_PROT_ALL);
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_RAX, &addr));
|
||||
const uint64_t base_addr = 0x40000;
|
||||
uint8_t code[] = {0x48,0x8b,0x00,0x90,0x90,0x90,0x90}; // mov rax, [rax], nops
|
||||
uc_assert_success(uc_mem_map(uc, base_addr, 4096, UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc, base_addr, code, 7));
|
||||
uc_assert_success(uc_hook_add(uc, &trace2, UC_HOOK_MEM_READ, hook_mem64, NULL, 1, 0));
|
||||
uc_assert_success(uc_emu_start(uc, base_addr, base_addr + 3, 0, 0));
|
||||
if(number_of_memory_reads != 1) {
|
||||
fail_msg("wrong number of memory reads for instruction %i", number_of_memory_reads);
|
||||
}
|
||||
}
|
||||
|
||||
//if a read is performed from a big address whith a non-zero last digit, 0 will be read
|
||||
static void test_high_address_read_values(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
struct stat info;
|
||||
char * code = read_file("high_address.bin", &info);
|
||||
if (code == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t addr = 0x0010000000000001;
|
||||
//addr = 0x000ffffffffffff8; // uncomment to fix wrong behaviour
|
||||
//addr = 90000000; // uncomment to fix wrong behaviour
|
||||
//
|
||||
uint8_t content[] = {0x42,0x42,0x42,0x42, 0x42,0x42,0x42,0x42};
|
||||
uc_assert_success(uc_mem_map(uc, addr-(addr%4096), 4096*2, UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc, addr, content, 8));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_RAX, &addr));
|
||||
const uint64_t base_addr = 0x40000;
|
||||
uc_assert_success(uc_mem_map(uc, base_addr, 4096, UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc, base_addr, code, info.st_size));
|
||||
uc_assert_success(uc_emu_start(uc, base_addr, base_addr + 3, 0, 0));
|
||||
uint64_t rax = 0;
|
||||
uc_assert_success(uc_reg_read(uc, UC_X86_REG_RAX, &rax));
|
||||
if(rax != 0x4242424242424242) {
|
||||
fail_msg("wrong memory read from code %"PRIx64, rax);
|
||||
}
|
||||
|
||||
free(code);
|
||||
}
|
||||
|
||||
|
||||
int main(void) {
|
||||
#define test(x) cmocka_unit_test_setup_teardown(x, setup, teardown)
|
||||
const struct CMUnitTest tests[] = {
|
||||
test(test_last_page_map),
|
||||
test(test_high_address_reads),
|
||||
test(test_high_address_read_values),
|
||||
test(test_nullptr_deref_wrong_perms),
|
||||
};
|
||||
#undef test
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
/**
|
||||
* Unicorn memory API tests
|
||||
*
|
||||
* This tests memory read/write and map/unmap functionality.
|
||||
* One is necessary for doing the other.
|
||||
*/
|
||||
#include "unicorn_test.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int setup(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* A basic test showing mapping of memory, and reading/writing it
|
||||
*/
|
||||
static void test_basic(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint64_t mem_start = 0x1000;
|
||||
const uint64_t mem_len = 0x1000;
|
||||
const uint64_t test_addr = mem_start + 0x100;
|
||||
|
||||
/* Map a region */
|
||||
uc_assert_success(uc_mem_map(uc, mem_start, mem_len, UC_PROT_NONE));
|
||||
|
||||
/* Write some data to it */
|
||||
uc_assert_success(uc_mem_write(uc, test_addr, "test", 4));
|
||||
|
||||
uint8_t buf[4];
|
||||
memset(buf, 0xCC, sizeof(buf));
|
||||
|
||||
/* Read it back */
|
||||
uc_assert_success(uc_mem_read(uc, test_addr, buf, sizeof(buf)));
|
||||
|
||||
/* And make sure it matches what we expect */
|
||||
assert_memory_equal(buf, "test", 4);
|
||||
|
||||
/* Unmap the region */
|
||||
//uc_assert_success(uc_mem_unmap(uc, mem_start, mem_len));
|
||||
}
|
||||
|
||||
static void test_bad_read(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uint8_t readbuf[0x10];
|
||||
memset(readbuf, 0xCC, sizeof(readbuf));
|
||||
|
||||
uint8_t checkbuf[0x10];
|
||||
memset(checkbuf, 0xCC, sizeof(checkbuf));
|
||||
|
||||
/* Reads to unmapped addresses should fail */
|
||||
/* TODO: Which error? */
|
||||
uc_assert_fail(uc_mem_read(uc, 0x1000, readbuf, sizeof(readbuf)));
|
||||
|
||||
/* And our buffer should be unchanged */
|
||||
assert_memory_equal(readbuf, checkbuf, sizeof(checkbuf));
|
||||
}
|
||||
|
||||
static void test_bad_write(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uint8_t writebuf[0x10];
|
||||
memset(writebuf, 0xCC, sizeof(writebuf));
|
||||
|
||||
/* Writes to unmapped addresses should fail */
|
||||
/* TODO: Which error? */
|
||||
uc_assert_fail(uc_mem_write(uc, 0x1000, writebuf, sizeof(writebuf)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Verify that we can read/write across memory map region boundaries
|
||||
*/
|
||||
static void test_rw_across_boundaries(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
/* Map in two adjacent regions */
|
||||
uc_assert_success(uc_mem_map(uc, 0, 0x1000, 0)); /* 0x0000 - 0x1000 */
|
||||
uc_assert_success(uc_mem_map(uc, 0x1000, 0x1000, 0)); /* 0x1000 - 0x2000 */
|
||||
|
||||
const uint64_t addr = 0x1000 - 2; /* 2 bytes before end of block */
|
||||
|
||||
/* Write some data across the boundary */
|
||||
uc_assert_success(uc_mem_write(uc, addr, "test", 4));
|
||||
|
||||
uint8_t buf[4];
|
||||
memset(buf, 0xCC, sizeof(buf));
|
||||
|
||||
/* Read the data across the boundary */
|
||||
uc_assert_success(uc_mem_read(uc, addr, buf, sizeof(buf)));
|
||||
|
||||
assert_memory_equal(buf, "test", 4);
|
||||
}
|
||||
|
||||
/* Try to unmap memory that has not been mapped */
|
||||
static void test_bad_unmap(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
/* TODO: Which error should this return? */
|
||||
uc_assert_fail(uc_mem_unmap(uc, 0x0, 0x1000));
|
||||
}
|
||||
|
||||
|
||||
/* Try to map overlapped memory range */
|
||||
static void test_unmap_double_map(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uc_assert_success(uc_mem_map(uc, 0, 0x4000, 0)); /* 0x0000 - 0x4000 */
|
||||
uc_assert_fail(uc_mem_map(uc, 0x0000, 0x1000, 0)); /* 0x1000 - 0x1000 */
|
||||
}
|
||||
|
||||
static void test_overlap_unmap_double_map(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
uc_mem_map( uc, 0x1000, 0x2000, 0);
|
||||
uc_mem_map( uc, 0x1000, 0x1000, 0);
|
||||
uc_mem_unmap(uc, 0x2000, 0x1000);
|
||||
}
|
||||
|
||||
static void test_strange_map(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
uc_mem_map( uc, 0x0,0x3000,0);
|
||||
uc_mem_unmap(uc, 0x1000,0x1000);
|
||||
uc_mem_map( uc, 0x3000,0x1000,0);
|
||||
uc_mem_map( uc, 0x4000,0x1000,0);
|
||||
uc_mem_map( uc, 0x1000,0x1000,0);
|
||||
uc_mem_map( uc, 0x5000,0x1000,0);
|
||||
uc_mem_unmap(uc, 0x0,0x1000);
|
||||
}
|
||||
|
||||
static void test_query_page_size(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
size_t page_size;
|
||||
uc_assert_success(uc_query(uc, UC_QUERY_PAGE_SIZE, &page_size));
|
||||
assert_int_equal(4096, page_size);
|
||||
}
|
||||
|
||||
void mem_write(uc_engine* uc, uint64_t addr, uint64_t len){
|
||||
uint8_t* buff = alloca(len);
|
||||
memset(buff,0,len);
|
||||
uc_mem_write(uc, addr, buff, len);
|
||||
|
||||
}
|
||||
|
||||
void mem_read(uc_engine* uc, uint64_t addr, uint64_t len){
|
||||
uint8_t* buff = alloca(len);
|
||||
uc_mem_read(uc, addr, buff, len);
|
||||
}
|
||||
|
||||
void map(uc_engine* uc, uint64_t addr, uint64_t len){
|
||||
uc_mem_map(uc, addr, len, UC_PROT_READ | UC_PROT_WRITE);
|
||||
}
|
||||
|
||||
void unmap(uc_engine* uc, uint64_t addr, uint64_t len){
|
||||
uc_mem_unmap(uc, addr, len);
|
||||
}
|
||||
|
||||
//most likely same bug as in test_strange_map, but looked different in fuzzer (sefault instead of assertion fail)
|
||||
static void test_assertion_fail(void **state){
|
||||
uc_engine *uc = *state;
|
||||
|
||||
map(uc,0x2000,0x4000); //5
|
||||
unmap(uc,0x3000,0x2000); //11
|
||||
map(uc,0x0,0x2000); //23
|
||||
map(uc,0x3000,0x2000); //24
|
||||
map(uc,0x9000,0x4000); //32
|
||||
map(uc,0x8000,0x1000); //34
|
||||
unmap(uc,0x1000,0x4000); //35
|
||||
}
|
||||
|
||||
static void test_bad_offset(void **state){
|
||||
uc_engine *uc = *state;
|
||||
map(uc,0x9000,0x4000); //17
|
||||
map(uc,0x4000,0x2000); //32
|
||||
unmap(uc,0x5000,0x1000); //35
|
||||
map(uc,0x0,0x1000); //42
|
||||
map(uc,0x5000,0x4000); //51
|
||||
map(uc,0x2000,0x1000); //53
|
||||
map(uc,0x1000,0x1000); //55
|
||||
unmap(uc,0x7000,0x3000); //58
|
||||
unmap(uc,0x5000,0x1000); //59
|
||||
unmap(uc,0x4000,0x2000); //70
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(void) {
|
||||
#define test(x) cmocka_unit_test_setup_teardown(x, setup, teardown)
|
||||
const struct CMUnitTest tests[] = {
|
||||
test(test_basic),
|
||||
//test(test_bad_read),
|
||||
//test(test_bad_write),
|
||||
test(test_bad_offset),
|
||||
test(test_assertion_fail),
|
||||
test(test_bad_unmap),
|
||||
test(test_rw_across_boundaries),
|
||||
test(test_unmap_double_map),
|
||||
test(test_overlap_unmap_double_map),
|
||||
test(test_strange_map),
|
||||
test(test_query_page_size),
|
||||
};
|
||||
#undef test
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/**
|
||||
* Unicorn memory API tests
|
||||
*
|
||||
* This tests manual pointer-backed memory.
|
||||
*/
|
||||
#include "unicorn_test.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int setup(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* A basic test showing mapping of memory, and reading/writing it
|
||||
*/
|
||||
static void test_basic(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint64_t mem_start = 0x1000;
|
||||
const uint64_t mem_len = 0x1000;
|
||||
const uint64_t test_addr = mem_start;
|
||||
|
||||
void *host_mem = calloc(1, mem_len);
|
||||
|
||||
/* Map a region */
|
||||
uc_assert_success(uc_mem_map_ptr(uc, mem_start, mem_len, UC_PROT_ALL, host_mem));
|
||||
|
||||
/* Write some data to it */
|
||||
uc_assert_success(uc_mem_write(uc, test_addr, "test", 4));
|
||||
|
||||
uint8_t buf[4];
|
||||
memset(buf, 0xCC, sizeof(buf));
|
||||
|
||||
/* Read it back */
|
||||
uc_assert_success(uc_mem_read(uc, test_addr, buf, sizeof(buf)));
|
||||
|
||||
/* And make sure it matches what we expect */
|
||||
assert_memory_equal(buf, "test", 4);
|
||||
|
||||
/* Unmap the region */
|
||||
uc_assert_success(uc_mem_unmap(uc, mem_start, mem_len));
|
||||
|
||||
assert_memory_equal(buf, host_mem, 4);
|
||||
|
||||
free(host_mem);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
#define test(x) cmocka_unit_test_setup_teardown(x, setup, teardown)
|
||||
const struct CMUnitTest tests[] = {
|
||||
test(test_basic),
|
||||
};
|
||||
#undef test
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
90
tests/unit/test_mips.c
Normal file
90
tests/unit/test_mips.c
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "unicorn_test.h"
|
||||
|
||||
const uint64_t code_start = 0x10000000;
|
||||
const uint64_t code_len = 0x4000;
|
||||
|
||||
static void uc_common_setup(uc_engine** uc, uc_arch arch, uc_mode mode, const char* code, uint64_t size) {
|
||||
OK(uc_open(arch, mode, uc));
|
||||
OK(uc_mem_map(*uc, code_start, code_len, UC_PROT_ALL));
|
||||
OK(uc_mem_write(*uc, code_start, code, size));
|
||||
}
|
||||
|
||||
static void test_mips_el_ori() {
|
||||
uc_engine* uc;
|
||||
char code[] = "\x56\x34\x21\x34"; // ori $at, $at, 0x3456;
|
||||
int r_r1 = 0x6789;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_32 | UC_MODE_LITTLE_ENDIAN, code, sizeof(code) - 1);
|
||||
OK(uc_reg_write(uc, UC_MIPS_REG_1, &r_r1));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_MIPS_REG_1, &r_r1));
|
||||
|
||||
TEST_CHECK(r_r1 == 0x77df);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_mips_eb_ori() {
|
||||
uc_engine* uc;
|
||||
char code[] = "\x34\x21\x34\x56"; // ori $at, $at, 0x3456;
|
||||
int r_r1 = 0x6789;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_32 | UC_MODE_BIG_ENDIAN, code, sizeof(code) - 1);
|
||||
OK(uc_reg_write(uc, UC_MIPS_REG_1, &r_r1));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_MIPS_REG_1, &r_r1));
|
||||
|
||||
TEST_CHECK(r_r1 == 0x77df);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_mips_stop_at_branch() {
|
||||
uc_engine* uc;
|
||||
char code[] = "\x02\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00"; // j 0x8; nop;
|
||||
int r_pc = 0x0;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_32 | UC_MODE_LITTLE_ENDIAN, code, sizeof(code) - 1);
|
||||
|
||||
// Execute one instruction with branch delay slot.
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 1));
|
||||
|
||||
OK(uc_reg_read(uc, UC_MIPS_REG_PC, &r_pc));
|
||||
|
||||
// Even if we just execute one instruction, the instruction in the
|
||||
// delay slot would also be executed.
|
||||
TEST_CHECK(r_pc == code_start + 0x8);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_mips_stop_at_delay_slot() {
|
||||
uc_engine* uc;
|
||||
char code[] = "\x02\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00"; // j 0x8; nop;
|
||||
int r_pc = 0x0;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_32 | UC_MODE_LITTLE_ENDIAN, code, sizeof(code) - 1);
|
||||
|
||||
// Stop at the delay slot by design.
|
||||
OK(uc_emu_start(uc, code_start, code_start + 4, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_MIPS_REG_PC, &r_pc));
|
||||
|
||||
// The branch instruction isn't committed and the PC is not updated.
|
||||
// Users is responsible to restart emulation at the branch instruction.
|
||||
TEST_CHECK(r_pc == code_start);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
TEST_LIST = {
|
||||
{ "test_mips_stop_at_branch", test_mips_stop_at_branch },
|
||||
{ "test_mips_stop_at_delay_slot", test_mips_stop_at_delay_slot},
|
||||
{ "test_mips_el_ori", test_mips_el_ori},
|
||||
{ "test_mips_eb_ori", test_mips_eb_ori},
|
||||
{ NULL, NULL }
|
||||
};
|
||||
@@ -1,111 +0,0 @@
|
||||
#include "unicorn_test.h"
|
||||
#include "unicorn/unicorn.h"
|
||||
|
||||
#define OK(x) uc_assert_success(x)
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int setup32(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
OK(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
struct bb {
|
||||
uint64_t addr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct bbtest {
|
||||
const struct bb *blocks;
|
||||
unsigned int blocknum;
|
||||
};
|
||||
|
||||
|
||||
static void test_basic_blocks_hook(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
struct bbtest *bbtest = user_data;
|
||||
const struct bb *bb = &bbtest->blocks[bbtest->blocknum];
|
||||
|
||||
printf("block hook 1: %d == %zu\n", size, bb->size);
|
||||
assert_int_equal(address, bb->addr);
|
||||
assert_int_equal((size_t)size, bb->size);
|
||||
}
|
||||
|
||||
static void test_basic_blocks_hook2(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
struct bbtest *bbtest = user_data;
|
||||
const struct bb *bb = &bbtest->blocks[bbtest->blocknum++];
|
||||
|
||||
printf("block hook 2: %d == %zu\n", size, bb->size);
|
||||
assert_int_equal(address, bb->addr);
|
||||
assert_int_equal((size_t)size, bb->size);
|
||||
}
|
||||
|
||||
static void test_basic_blocks(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
#define BASEADDR 0x1000000
|
||||
|
||||
uint64_t address = BASEADDR;
|
||||
const uint8_t code[] = {
|
||||
0x33, 0xC0, // xor eax, eax
|
||||
0x90, // nop
|
||||
0x90, // nop
|
||||
0xEB, 0x00, // jmp $+2
|
||||
0x90, // nop
|
||||
0x90, // nop
|
||||
0x90, // nop
|
||||
};
|
||||
|
||||
static const struct bb blocks[] = {
|
||||
{BASEADDR, 6},
|
||||
{BASEADDR+ 6, 3},
|
||||
};
|
||||
|
||||
struct bbtest bbtest = {
|
||||
.blocks = blocks,
|
||||
.blocknum = 0,
|
||||
};
|
||||
|
||||
|
||||
#undef BASEADDR
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
OK(uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL));
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
OK(uc_mem_write(uc, address, code, sizeof(code)));
|
||||
|
||||
// trace all basic blocks
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, test_basic_blocks_hook, &bbtest, 1, 0));
|
||||
OK(uc_hook_add(uc, &trace2, UC_HOOK_BLOCK, test_basic_blocks_hook2, &bbtest, 1, 0));
|
||||
|
||||
OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(test_basic_blocks, setup32, teardown),
|
||||
};
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
// Test PC change during the callback. by Nguyen Anh Quynh, 2016
|
||||
#include "unicorn_test.h"
|
||||
#include "unicorn/unicorn.h"
|
||||
#include "sys/stat.h"
|
||||
|
||||
#define OK(x) uc_assert_success(x)
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int setup32(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
OK(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void test_code_hook(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
uint8_t tmp[256];
|
||||
int32_t r_eip = 0x1000006;
|
||||
printf("instruction at 0x%"PRIx64": ", address);
|
||||
|
||||
if (!uc_mem_read(uc, address, tmp, size)) {
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
printf("0x%x ", tmp[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (address == 0x1000003) {
|
||||
// change the PC to "inc EDX"
|
||||
uc_reg_write(uc, UC_X86_REG_EIP, &r_eip);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_pc_change(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace1;
|
||||
int32_t r_ecx = 3, r_edx = 15;
|
||||
struct stat info;
|
||||
char *code = read_file("pc_change.bin", &info);
|
||||
if (code == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
#define BASEADDR 0x1000000
|
||||
|
||||
uint64_t address = BASEADDR;
|
||||
|
||||
#undef BASEADDR
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
OK(uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL));
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
OK(uc_mem_write(uc, address, code, info.st_size));
|
||||
|
||||
uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx);
|
||||
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
|
||||
printf("ECX = %u, EDX = %u\n", r_ecx, r_edx);
|
||||
|
||||
// trace all instructions
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, test_code_hook, NULL, 1, 0));
|
||||
|
||||
OK(uc_emu_start(uc, address, address+info.st_size, 0, 0));
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx);
|
||||
uc_reg_read(uc, UC_X86_REG_EDX, &r_edx);
|
||||
|
||||
printf("ECX = %u, EDX = %u\n", r_ecx, r_edx);
|
||||
assert_int_equal(r_ecx, 6);
|
||||
assert_int_equal(r_edx, 17);
|
||||
free(code);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(test_pc_change, setup32, teardown),
|
||||
};
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
8
tests/unit/test_ppc.c
Normal file
8
tests/unit/test_ppc.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "unicorn_test.h"
|
||||
|
||||
const uint64_t code_start = 0x1000;
|
||||
const uint64_t code_len = 0x4000;
|
||||
|
||||
TEST_LIST = {
|
||||
{ NULL, NULL }
|
||||
};
|
||||
8
tests/unit/test_riscv.c
Normal file
8
tests/unit/test_riscv.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "unicorn_test.h"
|
||||
|
||||
const uint64_t code_start = 0x1000;
|
||||
const uint64_t code_len = 0x4000;
|
||||
|
||||
TEST_LIST = {
|
||||
{ NULL, NULL }
|
||||
};
|
||||
@@ -1,88 +0,0 @@
|
||||
#include "unicorn_test.h"
|
||||
|
||||
/* Make sure the uc_assert macros work with constants */
|
||||
static void test_uc_assert_macros_constants(void **state)
|
||||
{
|
||||
const uc_err nomem = UC_ERR_NOMEM;
|
||||
|
||||
uc_assert_success(UC_ERR_OK);
|
||||
uc_assert_err(UC_ERR_NOMEM, nomem);
|
||||
uc_assert_fail(UC_ERR_VERSION);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static uc_err feedback(uc_err err, int *callcount)
|
||||
{
|
||||
assert_int_equal(++(*callcount), 1);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the uc_assert macros work with function calls
|
||||
* and only evaluate them once!
|
||||
*/
|
||||
static void test_uc_assert_macros_func_calls(void **state)
|
||||
{
|
||||
int callcount;
|
||||
|
||||
callcount = 0;
|
||||
uc_assert_success(feedback(UC_ERR_OK, &callcount));
|
||||
|
||||
callcount = 0;
|
||||
uc_assert_err(UC_ERR_NOMEM, feedback(UC_ERR_NOMEM, &callcount));
|
||||
|
||||
callcount = 0;
|
||||
uc_assert_fail(feedback(UC_ERR_VERSION, &callcount));
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void fail_uc_assert_success(void **state)
|
||||
{
|
||||
uc_assert_success(UC_ERR_NOMEM);
|
||||
}
|
||||
|
||||
static void fail_uc_assert_err(void **state)
|
||||
{
|
||||
const uc_err ok = UC_ERR_OK;
|
||||
uc_assert_err(UC_ERR_VERSION, ok);
|
||||
}
|
||||
|
||||
static void fail_uc_assert_fail(void **state)
|
||||
{
|
||||
uc_assert_fail(UC_ERR_OK);
|
||||
}
|
||||
|
||||
static void test_uc_assert_macros_fail(void **state)
|
||||
{
|
||||
/* A test-inside-a-test */
|
||||
|
||||
const struct CMUnitTest tests[] = {
|
||||
/* these should all fail */
|
||||
cmocka_unit_test(fail_uc_assert_success),
|
||||
cmocka_unit_test(fail_uc_assert_err),
|
||||
cmocka_unit_test(fail_uc_assert_fail),
|
||||
};
|
||||
|
||||
print_message("\n\n--------------------------------------------------------------------------------\n");
|
||||
print_message("START: Failure of the following tests is expected.\n\n");
|
||||
|
||||
assert_int_not_equal(0, cmocka_run_group_tests(tests, NULL, NULL));
|
||||
|
||||
print_message("\n\nEND: Failure of the preceding tests was expected.\n");
|
||||
print_message("--------------------------------------------------------------------------------\n\n");
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(test_uc_assert_macros_constants),
|
||||
cmocka_unit_test(test_uc_assert_macros_func_calls),
|
||||
cmocka_unit_test(test_uc_assert_macros_fail),
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
8
tests/unit/test_sparc.c
Normal file
8
tests/unit/test_sparc.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "unicorn_test.h"
|
||||
|
||||
const uint64_t code_start = 0x1000;
|
||||
const uint64_t code_len = 0x4000;
|
||||
|
||||
TEST_LIST = {
|
||||
{ NULL, NULL }
|
||||
};
|
||||
@@ -1,306 +0,0 @@
|
||||
/**
|
||||
* Unicorn x86_32 self-modifying unit test
|
||||
*
|
||||
* This test demonstrates the flushing of instruction translation cache
|
||||
* after a self-modification of Intel's x8's "IMUL Gv,Ev,Ib" instruction.
|
||||
*/
|
||||
#include "unicorn_test.h"
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include "unicorn/unicorn.h"
|
||||
|
||||
#define RIP_NEXT_TO_THE_SELFMODIFY_OPCODE (1)
|
||||
|
||||
|
||||
// Demostration of a self-modifying "IMUL eax,mem,Ib" opcode
|
||||
// And the QEMU's ability to flush the translation buffer properly
|
||||
|
||||
#define MIN(a, b) (a < b? a: b)
|
||||
|
||||
#define CODE_SPACE (2 * 1024 * 1024)
|
||||
#define PHY_STACK_REGION (0x60000000)
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int setup(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void dump_stack_mem(uc_engine *uc, const struct stat info)
|
||||
{
|
||||
uint8_t tmp[256];
|
||||
uint32_t size;
|
||||
|
||||
size = sizeof(info.st_size);
|
||||
if (size > 255) size = 255;
|
||||
if (!uc_mem_read(uc, PHY_STACK_REGION, tmp, size))
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
printf("Stack region dump");
|
||||
for (i=0; i<size; i++) {
|
||||
if ((i % 16) == 0) printf("\n%x: ", PHY_STACK_REGION+i);
|
||||
printf("%x ", tmp[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_registers(uc_engine *uc)
|
||||
{
|
||||
int32_t eax, ecx, edx, ebx;
|
||||
int32_t esp, ebp, esi, edi;
|
||||
uc_reg_read(uc, UC_X86_REG_EAX, &eax);
|
||||
uc_reg_read(uc, UC_X86_REG_ECX, &ecx);
|
||||
uc_reg_read(uc, UC_X86_REG_EDX, &edx);
|
||||
uc_reg_read(uc, UC_X86_REG_EBX, &ebx);
|
||||
uc_reg_read(uc, UC_X86_REG_ESP, &esp);
|
||||
uc_reg_read(uc, UC_X86_REG_EBP, &ebp);
|
||||
uc_reg_read(uc, UC_X86_REG_ESI, &esi);
|
||||
uc_reg_read(uc, UC_X86_REG_EDI, &edi);
|
||||
|
||||
printf("Register dump:\n");
|
||||
printf("eax %8.8x ", eax);
|
||||
printf("ecx %8.8x ", ecx);
|
||||
printf("edx %8.8x ", edx);
|
||||
printf("ebx %8.8x\n", ebx);
|
||||
printf("esp %8.8x ", esp);
|
||||
printf("ebp %8.8x ", ebp);
|
||||
printf("esi %8.8x ", esi);
|
||||
printf("edi %8.8x ", edi);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
static void hook_code32(uc_engine *uc,
|
||||
uint64_t address,
|
||||
uint32_t size,
|
||||
void *user_data,
|
||||
const struct stat info)
|
||||
{
|
||||
//uint8_t opcode[256];
|
||||
uint8_t tmp[16];
|
||||
uint32_t tmp4[1];
|
||||
uint32_t ecx;
|
||||
|
||||
printf("\nhook_code32: Address: %"PRIx64", Opcode Size: %d\n", address, size);
|
||||
print_registers(uc);
|
||||
size = MIN(sizeof(tmp), size);
|
||||
if (!uc_mem_read(uc, address, tmp, size))
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
printf("Opcode: ");
|
||||
for (i=0; i<size; i++) {
|
||||
printf("%x ", tmp[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
dump_stack_mem(uc, info);
|
||||
|
||||
|
||||
if (address == 0x60000025)
|
||||
{
|
||||
// double-check that opcode is
|
||||
// IMUL aex,[eax+0x41],0x10
|
||||
if ((tmp[0] != 0x6b) ||
|
||||
(tmp[1] != 0x41) ||
|
||||
(tmp[2] != 0x41) ||
|
||||
(tmp[3] != 0x10))
|
||||
{
|
||||
printf("FAILED set-up of opcode\n");
|
||||
exit(-1);
|
||||
}
|
||||
printf("IMUL eax,[ecx+0x41],0x10\n");
|
||||
|
||||
// double-check that memory operand points to 0x6000003a
|
||||
uc_reg_read(uc, UC_X86_REG_ECX, &ecx);
|
||||
if (ecx != 0x5ffffff9)
|
||||
{
|
||||
printf("FAILED EAX register not having 0x5ffffff9\n");
|
||||
exit(-1);
|
||||
}
|
||||
printf("ECX = %8.8x\n", ecx);
|
||||
|
||||
printf("%8.8x + 0x41 = %8.8x\n", 0x5ffffff9, 0x5ffffff9 + 0x41);
|
||||
|
||||
// double-check that memory location 0x60000039
|
||||
// contains 0x5151494a
|
||||
if (!uc_mem_read(uc, 0x6000003a, tmp4, 4))
|
||||
{
|
||||
if (tmp4[0] != 0x5151494a)
|
||||
{
|
||||
printf("FAILED set-up\n");
|
||||
exit(-1);
|
||||
}
|
||||
printf("Proved that 0x6000003a contains the proper 0x5151494a\n");
|
||||
}
|
||||
// dump_stack_mem(uc);
|
||||
}
|
||||
|
||||
// Stop after 'imul eax,[ecx+0x41],0x10
|
||||
if (address == 0x60000029)
|
||||
{
|
||||
uint32_t eax;
|
||||
// IMUL eax,mem,Ib
|
||||
// mem = [ecx+0x41]
|
||||
// ecx = 0x5ffffff9
|
||||
// [6000003A] = 0x5151494a
|
||||
// Stop after 'imul eax,[ecx+0x41],0x10
|
||||
// This step basically shifts left 8-bit...elaborately.
|
||||
// multiplying 0x5151494a x 0x10 = 0x151494a0
|
||||
uc_reg_read(uc, UC_X86_REG_EAX, &eax);
|
||||
if (eax != 0x151494a0)
|
||||
{
|
||||
fail_msg("FAIL: TB did not flush; eax is not the expected 0x151494a0\n");
|
||||
print_registers(uc);
|
||||
//dump_stack_mem(uc);
|
||||
exit(-1);
|
||||
}
|
||||
printf("PASS\n");
|
||||
}
|
||||
print_registers(uc);
|
||||
// dump_stack_mem(uc);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void hook_mem32(uc_engine *uc,
|
||||
uc_mem_type type,
|
||||
uint64_t address,
|
||||
int size,
|
||||
uint64_t value,
|
||||
void *user_data)
|
||||
{
|
||||
char ctype;
|
||||
//uint32_t tmp[1];
|
||||
|
||||
ctype = '?';
|
||||
if (type == UC_MEM_READ) ctype = 'R';
|
||||
if (type == UC_MEM_WRITE) ctype = 'W';
|
||||
printf("hook_mem32(%c): Address: 0x%"PRIx64", Size: %d, Value:0x%"PRIx64"\n", ctype, address, size, value);
|
||||
|
||||
// if (!uc_mem_read(uc, 0x6000003a, tmp, 4))
|
||||
// {
|
||||
// printf(" hook_mem32 0x6000003a: %8.8x\n", tmp[0]);
|
||||
// }
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void test_tb_x86_64_32_imul_Gv_Ev_Ib(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace1, trace2;
|
||||
struct stat info;
|
||||
char * code = read_file("tb_x86.bin", &info);
|
||||
//void *mem;
|
||||
#ifdef RIP_NEXT_TO_THE_SELFMODIFY_OPCODE
|
||||
// These values assumes just before PC = 0x60000021
|
||||
int64_t eax = 0x00000041;
|
||||
int64_t ecx = 0x5ffffff8;
|
||||
int64_t edx = 0x5ffffff8;
|
||||
int64_t ebx = 0x034a129b;
|
||||
int64_t esp = 0x6010229a;
|
||||
int64_t ebp = 0x60000002;
|
||||
int64_t esi = 0x1f350211;
|
||||
int64_t edi = 0x488ac239;
|
||||
#else
|
||||
// These values assumes PC == 0x6000000
|
||||
int64_t eax = 0x73952c43;
|
||||
int64_t ecx = 0x6010229a;
|
||||
int64_t edx = 0x2a500e50;
|
||||
int64_t ebx = 0x034a1295;
|
||||
int64_t esp = 0x6010229a;
|
||||
int64_t ebp = 0x60000000;
|
||||
int64_t esi = 0x1f350211;
|
||||
int64_t edi = 0x488ac239;
|
||||
#endif
|
||||
|
||||
//mem = calloc(1, CODE_SPACE);
|
||||
// TODO examine
|
||||
//assert_int_not_equal(0, mem);
|
||||
|
||||
uc_assert_success(uc_open(UC_ARCH_X86,
|
||||
UC_MODE_32,
|
||||
&uc));
|
||||
uc_assert_success(uc_mem_map(uc,
|
||||
PHY_STACK_REGION,
|
||||
CODE_SPACE,
|
||||
UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc,
|
||||
PHY_STACK_REGION,
|
||||
code,
|
||||
info.st_size));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EAX, &eax));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_ECX, &ecx));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EDX, &edx));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EBX, &ebx));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_ESP, &esp));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EBP, &ebp));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_ESI, &esi));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EDI, &edi));
|
||||
|
||||
uc_assert_success(uc_hook_add(uc,
|
||||
&trace1,
|
||||
UC_HOOK_CODE,
|
||||
hook_code32,
|
||||
NULL,
|
||||
1,
|
||||
0,
|
||||
info));
|
||||
|
||||
uc_assert_success(uc_hook_add(uc,
|
||||
&trace2,
|
||||
UC_HOOK_MEM_VALID,
|
||||
hook_mem32,
|
||||
NULL,
|
||||
1,
|
||||
0));
|
||||
|
||||
uc_assert_success(uc_emu_start(uc,
|
||||
#ifdef RIP_NEXT_TO_THE_SELFMODIFY_OPCODE
|
||||
// Register set (before self-modifying IMUL opcode)
|
||||
// Start at "0x00000021: xorb %al, 0x30(%ecx)
|
||||
// Start at "0x00000021: xor byte ptr [ecx + 0x30], al
|
||||
PHY_STACK_REGION+0x0021, // 0x0024 didn't work
|
||||
#else
|
||||
PHY_STACK_REGION+0x0000,
|
||||
#endif
|
||||
PHY_STACK_REGION+info.st_size,
|
||||
0, 0));
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
#define test(x) cmocka_unit_test_setup_teardown(x, setup, teardown)
|
||||
const struct CMUnitTest tests[] = {
|
||||
test(test_tb_x86_64_32_imul_Gv_Ev_Ib)
|
||||
};
|
||||
#undef test
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,269 +0,0 @@
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
#include "unicorn_test.h"
|
||||
|
||||
/**
|
||||
* Initialize i386 Unicorn Instance
|
||||
*/
|
||||
static int setup_i386(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize amd64 Unicorn Instance
|
||||
*/
|
||||
static int setup_amd64(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown a Unicorn Instance
|
||||
*/
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
bool good;
|
||||
uint64_t actual;
|
||||
uint64_t expected;
|
||||
} TestData;
|
||||
|
||||
const uint64_t CodePage = 0x10000;
|
||||
const uint64_t CodeSize = 0x4000;
|
||||
|
||||
/**
|
||||
* Hook for reading unmapped memory in the i386 Unicorn Instance.
|
||||
*
|
||||
* BUG: EIP from uc_reg_read does not match expected value.
|
||||
*/
|
||||
static bool mem_hook_i386(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t address, int size, int64_t value, void *user_data)
|
||||
{
|
||||
TestData *data = user_data;
|
||||
if (type == UC_MEM_READ_UNMAPPED)
|
||||
{
|
||||
uint32_t eip;
|
||||
uint32_t eax;
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_EIP, &eip);
|
||||
uc_reg_read(uc, UC_X86_REG_EAX, &eax);
|
||||
|
||||
data->actual = eip;
|
||||
data->expected = CodePage + 0x05;
|
||||
|
||||
/**
|
||||
* Code:
|
||||
* 0x10000: mov eax, 0x41414141 ;; <- Returned EIP
|
||||
* 0x10005: mov ecx, [eax] ;; <- Expected EIP
|
||||
*/
|
||||
if ((eax == 0x41414141) && // Proof we're at 0x10005.
|
||||
(eip != (CodePage + 0x5))) // Proof uc_reg_read is wrong
|
||||
{
|
||||
data->good = false;
|
||||
}
|
||||
else
|
||||
data->good = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for reading unmapped memory in the amd64 Unicorn Instance.
|
||||
*
|
||||
* BUG: RIP from uc_reg_read does not match expected value.
|
||||
*/
|
||||
static bool mem_hook_amd64(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t address, int size, int64_t value, void *user_data)
|
||||
{
|
||||
TestData *data = user_data;
|
||||
if (type == UC_MEM_READ_UNMAPPED)
|
||||
{
|
||||
uint64_t rip;
|
||||
uint64_t rax;
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_RIP, &rip);
|
||||
uc_reg_read(uc, UC_X86_REG_RAX, &rax);
|
||||
|
||||
data->actual = rip;
|
||||
data->expected = CodePage + 0x0A;
|
||||
|
||||
/**
|
||||
* Code:
|
||||
* 0x10000: mov rax, 0x4141414141414141 ;; <- Returned RIP
|
||||
* 0x10005: mov rcx, [rax] ;; <- Expected RIP
|
||||
*/
|
||||
if ((rax == 0x4141414141414141) && // Proof we're at 0x10005
|
||||
(rip != (CodePage + 0xA))) // Proof uc_reg_read is wrong
|
||||
{
|
||||
data->good = false;
|
||||
}
|
||||
else
|
||||
data->good = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty Code Hook.
|
||||
*/
|
||||
static void code_hook(uc_engine *uc, uint64_t addr, uint32_t size, void *user)
|
||||
{
|
||||
(void) uc;
|
||||
(void) addr;
|
||||
(void) size;
|
||||
(void) user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the bug for i386.
|
||||
*
|
||||
* 1. Map Code Page
|
||||
* 2. Write Code to page.
|
||||
* 3. Install Unmapped Read hook.
|
||||
* 4. Run the VM.
|
||||
*/
|
||||
static void test_i386(void **state)
|
||||
{
|
||||
TestData data;
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace1;
|
||||
|
||||
const uint8_t i386_bug[] = {
|
||||
0xb8, 0x41, 0x41, 0x41, 0x41, // mov eax, 0x41414141
|
||||
0x8b, 0x08 // mov ecx, [eax]
|
||||
};
|
||||
|
||||
uc_assert_success(uc_mem_map(uc, CodePage, CodeSize, UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc, CodePage, i386_bug, sizeof(i386_bug)));
|
||||
uc_assert_success(uc_hook_add(uc, &trace1, UC_HOOK_MEM_READ_UNMAPPED, mem_hook_i386, &data, 1, 0));
|
||||
uc_assert_fail(uc_emu_start(uc, CodePage, CodePage + sizeof(i386_bug), 0, 0));
|
||||
|
||||
if (!data.good)
|
||||
fail_msg("De-synced RIP value. 0x%"PRIX64" != 0x%"PRIX64"\n", data.expected, data.actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the bug for amd64..
|
||||
*
|
||||
* 1. Map Code Page
|
||||
* 2. Write Code to page.
|
||||
* 3. Install Unmapped Read hook.
|
||||
* 4. Run the VM.
|
||||
*/
|
||||
static void test_amd64(void **state)
|
||||
{
|
||||
TestData data;
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace1;
|
||||
|
||||
const uint8_t amd64_bug[] = {
|
||||
0x48, 0xb8, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41,
|
||||
0x48, 0x8b, 0x08
|
||||
};
|
||||
|
||||
uc_assert_success(uc_mem_map(uc, CodePage, CodeSize, UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc, CodePage, amd64_bug, sizeof(amd64_bug)));
|
||||
uc_assert_success(uc_hook_add(uc, &trace1, UC_HOOK_MEM_READ_UNMAPPED, mem_hook_amd64, &data, 1, 0));
|
||||
uc_assert_fail(uc_emu_start(uc, CodePage, CodePage + sizeof(amd64_bug), 0, 0));
|
||||
|
||||
if (!data.good)
|
||||
fail_msg("De-synced RIP value. 0x%"PRIX64" != 0x%"PRIX64"\n", data.expected, data.actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test temporary fix for bug for i386.
|
||||
*
|
||||
* 1. Map Code Page
|
||||
* 2. Write Code to page.
|
||||
* 3. Install Unmapped Read hook.
|
||||
* 4. Install Code hook.
|
||||
* 5. Run the VM.
|
||||
*/
|
||||
static void test_i386_fix(void **state)
|
||||
{
|
||||
TestData data;
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
const uint8_t i386_bug[] = {
|
||||
0xb8, 0x41, 0x41, 0x41, 0x41, // mov eax, 0x41414141
|
||||
0x8b, 0x08 // mov ecx, [eax]
|
||||
};
|
||||
|
||||
uc_assert_success(uc_mem_map(uc, CodePage, CodeSize, UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc, CodePage, i386_bug, sizeof(i386_bug)));
|
||||
uc_assert_success(uc_hook_add(uc, &trace1, UC_HOOK_MEM_READ_UNMAPPED, mem_hook_i386, &data, 1, 0));
|
||||
uc_assert_success(uc_hook_add(uc, &trace2, UC_HOOK_CODE, code_hook, NULL, 1, 0));
|
||||
uc_assert_fail(uc_emu_start(uc, CodePage, CodePage + sizeof(i386_bug), 0, 0));
|
||||
|
||||
if (!data.good)
|
||||
fail_msg("De-synced RIP value. 0x%"PRIX64" != 0x%"PRIX64"\n", data.expected, data.actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test temporary fix for bug for amd64..
|
||||
*
|
||||
* 1. Map Code Page
|
||||
* 2. Write Code to page.
|
||||
* 3. Install Unmapped Read hook.
|
||||
* 4. Install Code hook.
|
||||
* 5. Run the VM.
|
||||
*/
|
||||
static void test_amd64_fix(void **state)
|
||||
{
|
||||
TestData data;
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
const uint8_t amd64_bug[] = {
|
||||
0x48, 0xb8, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41,
|
||||
0x48, 0x8b, 0x08
|
||||
};
|
||||
|
||||
uc_assert_success(uc_mem_map(uc, CodePage, CodeSize, UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc, CodePage, amd64_bug, sizeof(amd64_bug)));
|
||||
uc_assert_success(uc_hook_add(uc, &trace1, UC_HOOK_MEM_READ_UNMAPPED, mem_hook_amd64, &data, 1, 0));
|
||||
uc_assert_success(uc_hook_add(uc, &trace2, UC_HOOK_CODE, code_hook, NULL, 1, 0));
|
||||
uc_assert_fail(uc_emu_start(uc, CodePage, CodePage + sizeof(amd64_bug), 0, 0));
|
||||
|
||||
if (!data.good)
|
||||
fail_msg("De-synced RIP value. 0x%"PRIX64" != 0x%"PRIX64"\n", data.expected, data.actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all tests
|
||||
*/
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(test_i386, setup_i386, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_amd64, setup_amd64, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_i386_fix, setup_i386, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_amd64_fix, setup_amd64, teardown)
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
@@ -1,433 +0,0 @@
|
||||
#include "unicorn/unicorn.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "unicorn_test.h"
|
||||
|
||||
|
||||
#define OK(x) uc_assert_success(x)
|
||||
|
||||
#define CF_MASK (1<<0)
|
||||
#define PF_MASK (1<<2)
|
||||
#define ZF_MASK (1<<6)
|
||||
#define SF_MASK (1<<7)
|
||||
#define OF_MASK (1<<11)
|
||||
#define ALL_MASK (OF_MASK|SF_MASK|ZF_MASK|PF_MASK|CF_MASK)
|
||||
#define NO_MASK 0xFFFFFFFF
|
||||
|
||||
typedef struct _reg_value
|
||||
{
|
||||
uint32_t regId, regValue, mask;
|
||||
} reg_value;
|
||||
|
||||
typedef struct _instruction
|
||||
{
|
||||
const char* asmStr;
|
||||
uint8_t code[16]; //x86 inst == 15 bytes max
|
||||
uint32_t codeSize;
|
||||
reg_value* values;
|
||||
uint32_t nbValues;
|
||||
uint32_t addr;
|
||||
} instruction;
|
||||
|
||||
typedef struct _block
|
||||
{
|
||||
instruction* insts[255];
|
||||
uint32_t nbInsts;
|
||||
uint32_t size;
|
||||
} block;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#define CAT2(X, Y) X ## Y
|
||||
#define CAT(X, Y) CAT2(X, Y)
|
||||
|
||||
#define BLOCK_START(BLOCK) \
|
||||
{ \
|
||||
block* blockPtr = &BLOCK; \
|
||||
blockPtr->nbInsts = 0; \
|
||||
instruction* instPtr = NULL;
|
||||
|
||||
#define BLOCK_END() }
|
||||
|
||||
#define BLOCK_ADD(CODE_ASM, CODE) \
|
||||
const uint8_t CAT(code, __LINE__)[] = CODE; \
|
||||
instPtr = newInstruction(CAT(code, __LINE__), sizeof(CAT(code, __LINE__)), CODE_ASM, NULL, 0); \
|
||||
addInstructionToBlock(blockPtr, instPtr);
|
||||
|
||||
#define BLOCK_ADD_CHECK(CODE_ASM, CODE, REGVALUES) \
|
||||
const uint8_t CAT(code, __LINE__)[] = CODE; \
|
||||
const reg_value CAT(regValues, __LINE__)[] = REGVALUES; \
|
||||
instPtr = newInstruction(CAT(code, __LINE__), sizeof(CAT(code, __LINE__)), CODE_ASM, CAT(regValues, __LINE__), sizeof(CAT(regValues, __LINE__)) / sizeof(reg_value)); \
|
||||
addInstructionToBlock(blockPtr, instPtr);
|
||||
|
||||
#define V(...) { __VA_ARGS__ }
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
instruction* newInstruction(const uint8_t * _code, uint32_t _codeSize, const char* _asmStr, const reg_value* _values, uint32_t _nbValues);
|
||||
void addInstructionToBlock(block* _b, instruction* _i);
|
||||
uint32_t loadBlock(uc_engine *_uc, block* _block, uint32_t _at);
|
||||
void freeBlock(block* _block);
|
||||
const char* getRegisterName(uint32_t _regid);
|
||||
uint32_t getRegisterValue(uc_engine *uc, uint32_t _regid);
|
||||
instruction* getInstruction(block * _block, uint32_t _addr);
|
||||
void initRegisters(uc_engine *uc);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void hook_code_test_i386_shl(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
uint32_t i;
|
||||
block* b = (block*)user_data;
|
||||
instruction* currInst = getInstruction(b, (uint32_t)address);
|
||||
assert_true(currInst != NULL);
|
||||
|
||||
printf("|\teip=%08x - %s\n", (uint32_t)address, currInst->asmStr);
|
||||
|
||||
for (i = 0; i < currInst->nbValues; i++)
|
||||
{
|
||||
uint32_t regValue = getRegisterValue(uc, currInst->values[i].regId);
|
||||
printf("|\t\ttesting %s : ", getRegisterName(currInst->values[i].regId));
|
||||
assert_int_equal(regValue & currInst->values[i].mask, currInst->values[i].regValue);
|
||||
printf("ok\n");
|
||||
}
|
||||
|
||||
if (currInst->code[0] == 0xCC)
|
||||
OK(uc_emu_stop(uc));
|
||||
}
|
||||
|
||||
bool hook_mem_invalid(uc_engine *uc, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user_data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
printf("hook_mem_invalid: UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", type, addr); break;
|
||||
case UC_MEM_READ_UNMAPPED:
|
||||
printf("hook_mem_invalid: Read from invalid memory at 0x%" PRIx64 ", data size = %u\n", addr, size); break;
|
||||
case UC_MEM_WRITE_UNMAPPED:
|
||||
printf("hook_mem_invalid: Write to invalid memory at 0x%" PRIx64 ", data size = %u, data value = 0x%" PRIx64 "\n", addr, size, value); break;
|
||||
case UC_MEM_FETCH_PROT:
|
||||
printf("hook_mem_invalid: Fetch from non-executable memory at 0x%" PRIx64 "\n", addr); break;
|
||||
case UC_MEM_WRITE_PROT:
|
||||
printf("hook_mem_invalid: Write to non-writeable memory at 0x%" PRIx64 ", data size = %u, data value = 0x%" PRIx64 "\n", addr, size, value); break;
|
||||
case UC_MEM_READ_PROT:
|
||||
printf("hook_mem_invalid: Read from non-readable memory at 0x%" PRIx64 ", data size = %u\n", addr, size); break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#define ADDR_CODE 0x100000
|
||||
#define ADDR_STACK 0x200000
|
||||
|
||||
|
||||
|
||||
static void test_i386_shl_cl(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1;
|
||||
block b;
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
OK(uc_mem_map(uc, ADDR_CODE, 0x1000, UC_PROT_ALL));
|
||||
|
||||
initRegisters(uc);
|
||||
|
||||
BLOCK_START(b);
|
||||
BLOCK_ADD( "mov ebx, 3Ch", V(0xBB, 0x3C, 0x00, 0x00, 0x00));
|
||||
BLOCK_ADD_CHECK("mov cl, 2", V(0xB1, 0x02), V(V(UC_X86_REG_EBX, 0x3C, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("shl ebx, cl", V(0xD3, 0xE3), V(V(UC_X86_REG_CL, 0x2, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("lahf", V(0x9F), V(V(UC_X86_REG_EBX, 0xF0, NO_MASK), V(UC_X86_REG_EFLAGS, 0x4, ALL_MASK)));
|
||||
BLOCK_ADD_CHECK("int3", V(0xCC), V(V(UC_X86_REG_AH, 0x4, PF_MASK)));
|
||||
BLOCK_END();
|
||||
|
||||
loadBlock(uc, &b, ADDR_CODE);
|
||||
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, hook_code_test_i386_shl, &b, 1, 0));
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL, 1, 0));
|
||||
|
||||
// emulate machine code in infinite time
|
||||
OK(uc_emu_start(uc, ADDR_CODE, ADDR_CODE + b.size, 0, 0));
|
||||
|
||||
freeBlock(&b);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
static void test_i386_shl_imm(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1;
|
||||
block b;
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
OK(uc_mem_map(uc, ADDR_CODE, 0x1000, UC_PROT_ALL));
|
||||
|
||||
initRegisters(uc);
|
||||
|
||||
BLOCK_START(b);
|
||||
BLOCK_ADD( "mov ebx, 3Ch", V(0xBB, 0x3C, 0x00, 0x00, 0x00));
|
||||
BLOCK_ADD( "shl ebx, 2", V(0xC1, 0xE3, 0x02));
|
||||
BLOCK_ADD_CHECK("lahf", V(0x9F), V(V(UC_X86_REG_EBX, 0xF0, NO_MASK), V(UC_X86_REG_EFLAGS, 0x4, ALL_MASK)));
|
||||
BLOCK_ADD_CHECK("int3", V(0xCC), V(V(UC_X86_REG_AH, 0x4, PF_MASK)));
|
||||
BLOCK_END();
|
||||
|
||||
loadBlock(uc, &b, ADDR_CODE);
|
||||
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, hook_code_test_i386_shl, &b, 1, 0));
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL, 1, 0));
|
||||
|
||||
// emulate machine code in infinite time
|
||||
OK(uc_emu_start(uc, ADDR_CODE, ADDR_CODE + b.size, 0, 0));
|
||||
|
||||
freeBlock(&b);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
static void test_i386_enter_leave(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1;
|
||||
block b;
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
OK(uc_mem_map(uc, ADDR_CODE, 0x1000, UC_PROT_ALL));
|
||||
OK(uc_mem_map(uc, ADDR_STACK - 0x1000, 0x1000, UC_PROT_ALL));
|
||||
|
||||
initRegisters(uc);
|
||||
|
||||
BLOCK_START(b);
|
||||
BLOCK_ADD( "mov esp, 0x200000", V(0xBC, 0x00, 0x00, 0x20, 0x00));
|
||||
BLOCK_ADD_CHECK("mov eax, 1", V(0xB8, 0x01, 0x00, 0x00, 0x00), V(V(UC_X86_REG_ESP, 0x200000, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("call 0x100015", V(0xE8, 0x06, 0x00, 0x00, 0x00), V(V(UC_X86_REG_EAX, 0x1, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("mov eax, 3", V(0xB8, 0x03, 0x00, 0x00, 0x00), V(V(UC_X86_REG_EAX, 0x2, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("int3", V(0xCC), V(V(UC_X86_REG_EAX, 0x3, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("enter 0x10,0", V(0xC8, 0x10, 0x00, 0x00), V(V(UC_X86_REG_ESP, 0x200000 - 4, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("mov eax, 2", V(0xB8, 0x02, 0x00, 0x00, 0x00), V(V(UC_X86_REG_ESP, 0x200000 - 4 - 4 - 0x10, NO_MASK), V(UC_X86_REG_EBP, 0x200000 - 4 - 4, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("leave", V(0xC9), V(V(UC_X86_REG_EAX, 0x2, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("ret", V(0xC3), V(V(UC_X86_REG_ESP, 0x200000 - 4, NO_MASK)));
|
||||
BLOCK_END();
|
||||
|
||||
loadBlock(uc, &b, ADDR_CODE);
|
||||
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, hook_code_test_i386_shl, &b, 1, 0));
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL, 1, 0));
|
||||
|
||||
// emulate machine code in infinite time
|
||||
OK(uc_emu_start(uc, ADDR_CODE, ADDR_CODE + b.size, 0, 0));
|
||||
|
||||
freeBlock(&b);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
static void test_i386_enter_nested_leave(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1;
|
||||
block b;
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
OK(uc_mem_map(uc, ADDR_CODE, 0x1000, UC_PROT_ALL));
|
||||
OK(uc_mem_map(uc, ADDR_STACK - 0x1000, 0x1000, UC_PROT_ALL));
|
||||
|
||||
initRegisters(uc);
|
||||
|
||||
BLOCK_START(b);
|
||||
BLOCK_ADD( "mov esp, 0x200000", V(0xBC, 0x00, 0x00, 0x20, 0x00));
|
||||
BLOCK_ADD_CHECK("mov eax, 1", V(0xB8, 0x01, 0x00, 0x00, 0x00), V(V(UC_X86_REG_ESP, 0x200000, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("call 0x100015", V(0xE8, 0x06, 0x00, 0x00, 0x00), V(V(UC_X86_REG_EAX, 0x1, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("mov eax, 3", V(0xB8, 0x03, 0x00, 0x00, 0x00), V(V(UC_X86_REG_EAX, 0x2, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("int3", V(0xCC), V(V(UC_X86_REG_EAX, 0x3, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("mov ebp, esp", V(0x89, 0xE5), V(V(UC_X86_REG_ESP, 0x200000 - 4, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("enter 0x10,1", V(0xC8, 0x10, 0x00, 0x01), V(V(UC_X86_REG_EBP, 0x200000 - 4, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("mov eax, 2", V(0xB8, 0x02, 0x00, 0x00, 0x00), V(V(UC_X86_REG_ESP, 0x200000 - 4 - 2*4 - 0x10, NO_MASK), V(UC_X86_REG_EBP, 0x200000 - 4 - 4, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("leave", V(0xC9), V(V(UC_X86_REG_EAX, 0x2, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("ret", V(0xC3), V(V(UC_X86_REG_ESP, 0x200000 - 4, NO_MASK)));
|
||||
BLOCK_END();
|
||||
|
||||
loadBlock(uc, &b, ADDR_CODE);
|
||||
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, hook_code_test_i386_shl, &b, 1, 0));
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL, 1, 0));
|
||||
|
||||
// emulate machine code in infinite time
|
||||
OK(uc_emu_start(uc, ADDR_CODE, ADDR_CODE + b.size, 0, 0));
|
||||
|
||||
freeBlock(&b);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int main(void) {
|
||||
const struct CMUnitTest tests[] = {
|
||||
|
||||
cmocka_unit_test(test_i386_shl_cl),
|
||||
cmocka_unit_test(test_i386_shl_imm),
|
||||
cmocka_unit_test(test_i386_enter_leave),
|
||||
cmocka_unit_test(test_i386_enter_nested_leave),
|
||||
};
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
instruction* newInstruction(const uint8_t * _code, uint32_t _codeSize, const char* _asmStr, const reg_value* _values, uint32_t _nbValues)
|
||||
{
|
||||
instruction* inst = (instruction*)malloc(sizeof(instruction));
|
||||
|
||||
inst->asmStr = _asmStr;
|
||||
memcpy(inst->code, _code, _codeSize);
|
||||
inst->codeSize = _codeSize;
|
||||
inst->nbValues = 0;
|
||||
if (_values)
|
||||
{
|
||||
inst->values = (reg_value*)malloc(_nbValues*sizeof(reg_value));
|
||||
memcpy(inst->values, _values, _nbValues*sizeof(reg_value));
|
||||
inst->nbValues = _nbValues;
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
void addInstructionToBlock(block* _b, instruction* _i)
|
||||
{
|
||||
_b->insts[_b->nbInsts++] = _i;
|
||||
}
|
||||
|
||||
uint32_t loadBlock(uc_engine *_uc, block* _block, uint32_t _at)
|
||||
{
|
||||
uint32_t i, j, offset;
|
||||
|
||||
for (i = 0, offset = 0; i < _block->nbInsts; i++)
|
||||
{
|
||||
const uint32_t codeSize = _block->insts[i]->codeSize;
|
||||
const uint8_t* code = _block->insts[i]->code;
|
||||
_block->insts[i]->addr = _at + offset;
|
||||
printf("load: %08X: ", _block->insts[i]->addr);
|
||||
for (j = 0; j < codeSize; j++) printf("%02X ", code[j]);
|
||||
for (j = 0; j < 15 - codeSize; j++) printf(" ");
|
||||
printf("%s\n", _block->insts[i]->asmStr);
|
||||
OK(uc_mem_write(_uc, _at + offset, code, codeSize));
|
||||
offset += codeSize;
|
||||
}
|
||||
_block->size = offset;
|
||||
return offset;
|
||||
}
|
||||
|
||||
void freeBlock(block* _block)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < _block->nbInsts; i++)
|
||||
{
|
||||
if (_block->insts[i]->nbValues > 0)
|
||||
free(_block->insts[i]->values);
|
||||
free(_block->insts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void initRegisters(uc_engine *uc)
|
||||
{
|
||||
// initialize machine registers
|
||||
uint32_t zero = 0;
|
||||
OK(uc_reg_write(uc, UC_X86_REG_EAX, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_EBX, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_ECX, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_EDX, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_EBP, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_ESP, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_EDI, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_ESI, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_EFLAGS, &zero));
|
||||
}
|
||||
|
||||
instruction* getInstruction(block* _block, uint32_t _addr)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < _block->nbInsts; i++)
|
||||
{
|
||||
if (_block->insts[i]->addr == _addr)
|
||||
return _block->insts[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* getRegisterName(uint32_t _regid)
|
||||
{
|
||||
switch (_regid)
|
||||
{
|
||||
//8
|
||||
case UC_X86_REG_AH: return "AH";
|
||||
case UC_X86_REG_AL: return "AL";
|
||||
case UC_X86_REG_BH: return "BH";
|
||||
case UC_X86_REG_BL: return "BL";
|
||||
case UC_X86_REG_CL: return "CL";
|
||||
case UC_X86_REG_CH: return "CH";
|
||||
case UC_X86_REG_DH: return "DH";
|
||||
case UC_X86_REG_DL: return "DL";
|
||||
//16
|
||||
case UC_X86_REG_AX: return "AX";
|
||||
case UC_X86_REG_BX: return "BX";
|
||||
case UC_X86_REG_CX: return "CX";
|
||||
case UC_X86_REG_DX: return "DX";
|
||||
//32
|
||||
case UC_X86_REG_EAX: return "EAX";
|
||||
case UC_X86_REG_EBX: return "EBX";
|
||||
case UC_X86_REG_ECX: return "ECX";
|
||||
case UC_X86_REG_EDX: return "EDX";
|
||||
case UC_X86_REG_EDI: return "EDI";
|
||||
case UC_X86_REG_ESI: return "ESI";
|
||||
case UC_X86_REG_EBP: return "EBP";
|
||||
case UC_X86_REG_ESP: return "ESP";
|
||||
case UC_X86_REG_EIP: return "EIP";
|
||||
case UC_X86_REG_EFLAGS: return "EFLAGS";
|
||||
|
||||
default: fail();
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
uint32_t getRegisterValue(uc_engine *uc, uint32_t _regid)
|
||||
{
|
||||
switch (_regid)
|
||||
{
|
||||
//8
|
||||
case UC_X86_REG_AH: case UC_X86_REG_AL:
|
||||
case UC_X86_REG_BH: case UC_X86_REG_BL:
|
||||
case UC_X86_REG_CL: case UC_X86_REG_CH:
|
||||
case UC_X86_REG_DH: case UC_X86_REG_DL:
|
||||
{
|
||||
uint8_t val = 0;
|
||||
OK(uc_reg_read(uc, _regid, &val));
|
||||
return val;
|
||||
}
|
||||
//16
|
||||
case UC_X86_REG_AX: case UC_X86_REG_BX:
|
||||
case UC_X86_REG_CX: case UC_X86_REG_DX:
|
||||
{
|
||||
uint16_t val = 0;
|
||||
OK(uc_reg_read(uc, _regid, &val));
|
||||
return val;
|
||||
}
|
||||
//32
|
||||
case UC_X86_REG_EAX: case UC_X86_REG_EBX:
|
||||
case UC_X86_REG_ECX: case UC_X86_REG_EDX:
|
||||
case UC_X86_REG_EDI: case UC_X86_REG_ESI:
|
||||
case UC_X86_REG_EBP: case UC_X86_REG_ESP:
|
||||
case UC_X86_REG_EIP: case UC_X86_REG_EFLAGS:
|
||||
{
|
||||
uint32_t val = 0;
|
||||
OK(uc_reg_read(uc, _regid, &val));
|
||||
return val;
|
||||
}
|
||||
|
||||
default: fail();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
#include "unicorn_test.h"
|
||||
#include "unicorn/unicorn.h"
|
||||
|
||||
/*
|
||||
Two tests here for software paging
|
||||
Low paging: Test paging using virtual addresses already mapped by Unicorn
|
||||
High paging: Test paging using virtual addresses not mapped by Unicorn
|
||||
*/
|
||||
|
||||
static void test_low_paging(void **state) {
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
int r_eax;
|
||||
|
||||
/* The following x86 code will map emulated physical memory
|
||||
to virtual memory using pages and attempt
|
||||
to read/write from virtual memory
|
||||
|
||||
Specifically, the virtual memory address range
|
||||
has been mapped by Unicorn (0x7FF000 - 0x7FFFFF)
|
||||
|
||||
Memory area purposes:
|
||||
0x1000 = page directory
|
||||
0x2000 = page table (identity map first 4 MiB)
|
||||
0x3000 = page table (0x007FF000 -> 0x00004000)
|
||||
0x4000 = data area (0xBEEF)
|
||||
*/
|
||||
const uint8_t code[] = {
|
||||
/* Zero memory for page directories and page tables */
|
||||
0xBF, 0x00, 0x10, 0x00, 0x00, /* MOV EDI, 0x1000 */
|
||||
0xB9, 0x00, 0x10, 0x00, 0x00, /* MOV ECX, 0x1000 */
|
||||
0x31, 0xC0, /* XOR EAX, EAX */
|
||||
0xF3, 0xAB, /* REP STOSD */
|
||||
|
||||
/* Load DWORD [0x4000] with 0xDEADBEEF to retrieve later */
|
||||
0xBF, 0x00, 0x40, 0x00, 0x00, /* MOV EDI, 0x4000 */
|
||||
0xB8, 0xEF, 0xBE, 0x00, 0x00, /* MOV EAX, 0xBEEF */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
|
||||
/* Identity map the first 4MiB of memory */
|
||||
0xB9, 0x00, 0x04, 0x00, 0x00, /* MOV ECX, 0x400 */
|
||||
0xBF, 0x00, 0x20, 0x00, 0x00, /* MOV EDI, 0x2000 */
|
||||
0xB8, 0x03, 0x00, 0x00, 0x00, /* MOV EAX, 3 */
|
||||
/* aLoop: */
|
||||
0xAB, /* STOSD */
|
||||
0x05, 0x00, 0x10, 0x00, 0x00, /* ADD EAX, 0x1000 */
|
||||
0xE2, 0xF8, /* LOOP aLoop */
|
||||
|
||||
/* Map physical address 0x4000 to virtual address 0x7FF000 */
|
||||
0xBF, 0xFC, 0x3F, 0x00, 0x00, /* MOV EDI, 0x3FFC */
|
||||
0xB8, 0x03, 0x40, 0x00, 0x00, /* MOV EAX, 0x4003 */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
|
||||
/* Add page tables into page directory */
|
||||
0xBF, 0x00, 0x10, 0x00, 0x00, /* MOV EDI, 0x1000 */
|
||||
0xB8, 0x03, 0x20, 0x00, 0x00, /* MOV EAX, 0x2003 */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
0xBF, 0x04, 0x10, 0x00, 0x00, /* MOV EDI, 0x1004 */
|
||||
0xB8, 0x03, 0x30, 0x00, 0x00, /* MOV EAX, 0x3003 */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
|
||||
/* Load the page directory register */
|
||||
0xB8, 0x00, 0x10, 0x00, 0x00, /* MOV EAX, 0x1000 */
|
||||
0x0F, 0x22, 0xD8, /* MOV CR3, EAX */
|
||||
|
||||
/* Enable paging */
|
||||
0x0F, 0x20, 0xC0, /* MOV EAX, CR0 */
|
||||
0x0D, 0x00, 0x00, 0x00, 0x80, /* OR EAX, 0x80000000 */
|
||||
0x0F, 0x22, 0xC0, /* MOV CR0, EAX */
|
||||
|
||||
/* Clear EAX */
|
||||
0x31, 0xC0, /* XOR EAX, EAX */
|
||||
|
||||
/* Load using virtual memory address; EAX = 0xBEEF */
|
||||
0xBE, 0x00, 0xF0, 0x7F, 0x00, /* MOV ESI, 0x7FF000 */
|
||||
0x8B, 0x06, /* MOV EAX, [ESI] */
|
||||
0xF4, /* HLT */
|
||||
};
|
||||
|
||||
/* Initialise X86-32bit mode */
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
/* Map 8MB of memory at base address 0 */
|
||||
err = uc_mem_map(uc, 0, (8 * 1024 * 1024), UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
/* Write code into memory at address 0 */
|
||||
err = uc_mem_write(uc, 0, code, sizeof(code));
|
||||
uc_assert_success(err);
|
||||
|
||||
/* Start emulation */
|
||||
err = uc_emu_start(uc, 0, sizeof(code), 0, 0);
|
||||
uc_assert_success(err);
|
||||
|
||||
/* The code should have loaded 0xBEEF into EAX */
|
||||
uc_reg_read(uc, UC_X86_REG_EAX, &r_eax);
|
||||
assert_int_equal(r_eax, 0xBEEF);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
|
||||
static void test_high_paging(void **state) {
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
int r_eax;
|
||||
|
||||
/* The following x86 code will map emulated physical memory
|
||||
to virtual memory using pages and attempt
|
||||
to read/write from virtual memory
|
||||
|
||||
Specifically, the virtual memory address range
|
||||
has not been mapped by UC (0xFFFFF000 - 0xFFFFFFFF)
|
||||
|
||||
Memory area purposes:
|
||||
0x1000 = page directory
|
||||
0x2000 = page table (identity map first 4 MiB)
|
||||
0x3000 = page table (0xFFFFF000 -> 0x00004000)
|
||||
0x4000 = data area (0xDEADBEEF)
|
||||
*/
|
||||
const uint8_t code[] = {
|
||||
/* Zero memory for page directories and page tables */
|
||||
0xBF, 0x00, 0x10, 0x00, 0x00, /* MOV EDI, 0x1000 */
|
||||
0xB9, 0x00, 0x10, 0x00, 0x00, /* MOV ECX, 0x1000 */
|
||||
0x31, 0xC0, /* XOR EAX, EAX */
|
||||
0xF3, 0xAB, /* REP STOSD */
|
||||
|
||||
/* Load DWORD [0x4000] with 0xDEADBEEF to retrieve later */
|
||||
0xBF, 0x00, 0x40, 0x00, 0x00, /* MOV EDI, 0x4000 */
|
||||
0xB8, 0xEF, 0xBE, 0x00, 0x00, /* MOV EAX, 0xBEEF */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
|
||||
/* Identity map the first 4MiB of memory */
|
||||
0xB9, 0x00, 0x04, 0x00, 0x00, /* MOV ECX, 0x400 */
|
||||
0xBF, 0x00, 0x20, 0x00, 0x00, /* MOV EDI, 0x2000 */
|
||||
0xB8, 0x03, 0x00, 0x00, 0x00, /* MOV EAX, 3 */
|
||||
/* aLoop: */
|
||||
0xAB, /* STOSD */
|
||||
0x05, 0x00, 0x10, 0x00, 0x00, /* ADD EAX, 0x1000 */
|
||||
0xE2, 0xF8, /* LOOP aLoop */
|
||||
|
||||
/* Map physical address 0x4000 to virtual address 0xFFFFF000 */
|
||||
0xBF, 0xFC, 0x3F, 0x00, 0x00, /* MOV EDI, 0x3FFC */
|
||||
0xB8, 0x03, 0x40, 0x00, 0x00, /* MOV EAX, 0x4003 */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
|
||||
/* Add page tables into page directory */
|
||||
0xBF, 0x00, 0x10, 0x00, 0x00, /* MOV EDI, 0x1000 */
|
||||
0xB8, 0x03, 0x20, 0x00, 0x00, /* MOV EAX, 0x2003 */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
0xBF, 0xFC, 0x1F, 0x00, 0x00, /* MOV EDI, 0x1FFC */
|
||||
0xB8, 0x03, 0x30, 0x00, 0x00, /* MOV EAX, 0x3003 */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
|
||||
/* Load the page directory register */
|
||||
0xB8, 0x00, 0x10, 0x00, 0x00, /* MOV EAX, 0x1000 */
|
||||
0x0F, 0x22, 0xD8, /* MOV CR3, EAX */
|
||||
|
||||
/* Enable paging */
|
||||
0x0F, 0x20, 0xC0, /* MOV EAX, CR0 */
|
||||
0x0D, 0x00, 0x00, 0x00, 0x80, /* OR EAX, 0x80000000 */
|
||||
0x0F, 0x22, 0xC0, /* MOV CR0, EAX */
|
||||
|
||||
/* Clear EAX */
|
||||
0x31, 0xC0, /* XOR EAX, EAX */
|
||||
|
||||
/* Load using virtual memory address; EAX = 0xBEEF */
|
||||
0xBE, 0x00, 0xF0, 0xFF, 0xFF, /* MOV ESI, 0xFFFFF000 */
|
||||
0x8B, 0x06, /* MOV EAX, [ESI] */
|
||||
0xF4, /* HLT */
|
||||
};
|
||||
|
||||
/* Initialise X86-32bit mode */
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
/* Map 4MB of memory at base address 0 */
|
||||
err = uc_mem_map(uc, 0, (4 * 1024 * 1024), UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
/* Write code into memory at address 0 */
|
||||
err = uc_mem_write(uc, 0, code, sizeof(code));
|
||||
uc_assert_success(err);
|
||||
|
||||
/* Start emulation */
|
||||
err = uc_emu_start(uc, 0, sizeof(code), 0, 0);
|
||||
uc_assert_success(err);
|
||||
|
||||
/* The code should have loaded 0xBEEF into EAX */
|
||||
uc_reg_read(uc, UC_X86_REG_EAX, &r_eax);
|
||||
assert_int_equal(r_eax, 0xBEEF);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
|
||||
int main(void) {
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(test_low_paging),
|
||||
cmocka_unit_test(test_high_paging),
|
||||
};
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
#ifndef UNICORN_TEST_H
|
||||
#define UNICORN_TEST_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdio.h>
|
||||
#include <unicorn/unicorn.h>
|
||||
#include <sys/stat.h>
|
||||
#include "acutest.h"
|
||||
|
||||
/**
|
||||
* Assert that err matches expect
|
||||
@@ -14,44 +11,14 @@
|
||||
#define uc_assert_err(expect, err) \
|
||||
do { \
|
||||
uc_err __err = err; \
|
||||
if (__err != expect) { \
|
||||
fail_msg("%s", uc_strerror(__err)); \
|
||||
if (!TEST_CHECK(__err == expect)) { \
|
||||
TEST_MSG("%s", uc_strerror(__err)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Assert that err is UC_ERR_OK
|
||||
*/
|
||||
#define uc_assert_success(err) uc_assert_err(UC_ERR_OK, err)
|
||||
|
||||
/**
|
||||
* Assert that err is anything but UC_ERR_OK
|
||||
*
|
||||
* Note: Better to use uc_assert_err(<specific error>, err),
|
||||
* as this serves to document which errors a function will return
|
||||
* in various scenarios.
|
||||
*/
|
||||
#define uc_assert_fail(err) \
|
||||
do { \
|
||||
uc_err __err = err; \
|
||||
if (__err == UC_ERR_OK) { \
|
||||
fail_msg("%s", uc_strerror(__err)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
char * read_file(const char *filename, struct stat *info) {
|
||||
stat(filename, info);
|
||||
char *code = malloc(info->st_size);
|
||||
if (code == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
FILE *fp = fopen(filename, "r");
|
||||
if (fp == NULL) {
|
||||
free(code);
|
||||
return NULL;
|
||||
}
|
||||
fread(code, info->st_size, 1, fp);
|
||||
return code;
|
||||
}
|
||||
#define OK(stat) uc_assert_err(UC_ERR_OK, stat)
|
||||
|
||||
#endif /* UNICORN_TEST_H */
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// Zero memory for page directories and page tables
|
||||
mov $0x1000,%edi
|
||||
mov $0x1000,%ecx
|
||||
xor %eax,%eax
|
||||
rep stos %eax,(%edi)
|
||||
|
||||
// Load DWORD [0x4000] with 0xDEADBEEF to retrieve later
|
||||
mov $0x4000,%edi
|
||||
mov $0xBEEF,%eax
|
||||
mov %eax, (%edi)
|
||||
|
||||
// Identify map the first 4MiB of memory
|
||||
mov $0x400,%ecx
|
||||
mov $0x2000,%edi
|
||||
mov $3, %eax
|
||||
loop:
|
||||
stos %eax,(%edi)
|
||||
add $0x1000,%eax
|
||||
loop loop
|
||||
|
||||
// Map phyiscal address 0x4000 to cirtual address 0x7FF000
|
||||
mov $0x3ffc,%edi
|
||||
mov $0x4003,%eax
|
||||
mov %eax, (%edi)
|
||||
|
||||
// Add page tables into page directory
|
||||
mov $0x1000, %edi
|
||||
mov $0x2003, %eax
|
||||
mov %eax, (%edi)
|
||||
mov $0x1004, %edi
|
||||
mov $0x3003, %eax
|
||||
mov %eax, (%edi)
|
||||
|
||||
// Load the page directory register
|
||||
mov $0x1000, %eax
|
||||
mov %eax, %cr3
|
||||
|
||||
// Enable paging
|
||||
mov %cr0, %eax
|
||||
or $0x80000000, %eax
|
||||
|
||||
// Clear EAX
|
||||
mov %eax, %cr0
|
||||
|
||||
//Load using virtual memory address; EAX = 0xBEEF
|
||||
xor %eax,%eax
|
||||
mov $0x7FF000, %esi
|
||||
mov (%esi), %eax
|
||||
hlt
|
||||
Reference in New Issue
Block a user