Reorganize test directories
This commit is contained in:
22
tests/regress/Makefile
Normal file
22
tests/regress/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
CFLAGS += -I../include
|
||||
LDFLAGS += ../libunicorn.a $(shell pkg-config --libs glib-2.0) -lpthread -lm
|
||||
|
||||
TESTS = map_crash map_write
|
||||
TESTS += sigill sigill2
|
||||
TESTS += block_test
|
||||
TESTS += ro_mem_test nr_mem_test
|
||||
TESTS += timeout_segfault
|
||||
TESTS += rep_movsb
|
||||
TESTS += mem_unmap
|
||||
TESTS += mem_protect
|
||||
TESTS += mem_exec
|
||||
|
||||
all: $(TESTS)
|
||||
|
||||
clean:
|
||||
rm -f $(TESTS)
|
||||
|
||||
%: %.c
|
||||
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
|
||||
|
||||
.PHONY: all clean
|
27
tests/regress/arm_bxeq_hang.py
Executable file
27
tests/regress/arm_bxeq_hang.py
Executable file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from unicorn import *
|
||||
from unicorn.arm_const import *
|
||||
|
||||
import regress
|
||||
|
||||
class BxHang(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
|
||||
uc.mem_map(0x1000, 0x1000)
|
||||
uc.mem_write(0x1000, '1eff2f010000a0e1'.decode('hex'))
|
||||
uc.count = 0
|
||||
def hook_block(uc, addr, *args):
|
||||
print 'enter block 0x%04x' % addr
|
||||
uc.count += 1
|
||||
|
||||
uc.reg_write(UC_ARM_REG_LR, 0x1004)
|
||||
uc.hook_add(UC_HOOK_BLOCK, hook_block)
|
||||
print 'block should only run once'
|
||||
uc.emu_start(0x1000, 0x1004)
|
||||
|
||||
self.assertEqual(uc.count, 1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
32
tests/regress/arm_movr12_hang.py
Executable file
32
tests/regress/arm_movr12_hang.py
Executable file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from unicorn import *
|
||||
from unicorn.arm_const import *
|
||||
|
||||
import regress
|
||||
|
||||
class MovHang(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
|
||||
uc.mem_map(0x1000, 0x1000)
|
||||
uc.mem_write(0x1000, '00c000e3'.decode('hex')) # movw r12, #0
|
||||
|
||||
def hook_block(uc, addr, *args):
|
||||
print 'enter block 0x%04x' % addr
|
||||
uc.count += 1
|
||||
|
||||
uc.reg_write(UC_ARM_REG_R12, 0x123)
|
||||
self.assertEquals(uc.reg_read(UC_ARM_REG_R12), 0x123)
|
||||
|
||||
uc.hook_add(UC_HOOK_BLOCK, hook_block)
|
||||
uc.count = 0
|
||||
|
||||
#print 'block should only run once'
|
||||
uc.emu_start(0x1000, 0x1004, timeout=500)
|
||||
|
||||
self.assertEquals(uc.reg_read(UC_ARM_REG_R12), 0x0)
|
||||
self.assertEquals(uc.count, 1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
85
tests/regress/block_test.c
Normal file
85
tests/regress/block_test.c
Normal file
@ -0,0 +1,85 @@
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
static int count = 1;
|
||||
|
||||
// Callback function for tracing code (UC_HOOK_CODE & UC_HOOK_BLOCK)
|
||||
// @address: address where the code is being executed
|
||||
// @size: size of machine instruction being executed
|
||||
// @user_data: user data passed to tracing APIs.
|
||||
void cb_hookblock(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) {
|
||||
fprintf(stderr, "# >>> Tracing basic block at 0x%"PRIx64", block size = 0x%x\n", address, size);
|
||||
if (address != 0x1000000 && address != 0x1000200) {
|
||||
fprintf(stderr, "not ok %d - address != 0x1000000 && address != 0x1000200\n", count++);
|
||||
_exit(1);
|
||||
}
|
||||
fprintf(stderr, "ok %d - address (0x%x) is start of basic block\n", count++, (uint32_t)address);
|
||||
if (size != 0x200) {
|
||||
fprintf(stderr, "not ok %d - basic block size != 0x200\n", count++);
|
||||
_exit(1);
|
||||
}
|
||||
fprintf(stderr, "ok %d - basic block size is correct\n", count++);
|
||||
}
|
||||
|
||||
int main() {
|
||||
uc_engine *uc;
|
||||
|
||||
fprintf(stderr, "# basic block callback test\n");
|
||||
fprintf(stderr, "# there are only two basic blocks 0x1000000-0x10001ff and 0x1000200-0x10003ff\n");
|
||||
|
||||
uc_err err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_open\n", count++);
|
||||
|
||||
err = uc_mem_map(uc, 0x1000000, 4096, UC_PROT_ALL);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_mem_map\n", count++);
|
||||
|
||||
uint8_t code[1024];
|
||||
//build a program that consists of 1019 nops followed by a jump -512
|
||||
//this program contains exactly 2 basic blocks, a block of 512 nops, followed
|
||||
//by a loop body containing 507 nops and jump to the top of the loop
|
||||
//the first basic block begins at address 0x1000000, and the second
|
||||
//basic block begins at address 0x1000200
|
||||
memset(code, 0x90, sizeof(code));
|
||||
memcpy(code + 1024 - 5, "\xe9\x00\xfe\xff\xff", 5);
|
||||
|
||||
err = uc_mem_write(uc, 0x1000000, code, sizeof(code));
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_mem_write\n", count++);
|
||||
|
||||
uc_hook h1, h2;
|
||||
|
||||
err = uc_hook_add(uc, &h1, UC_HOOK_BLOCK, cb_hookblock, NULL, (uint64_t)1, (uint64_t)0);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_hook_add\n", count++);
|
||||
|
||||
err = uc_emu_start(uc, 0x1000000, 0x1000000 + sizeof(code), 0, 1030);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_emu_start\n", count++);
|
||||
|
||||
fprintf(stderr, "ok %d - Done", count++);
|
||||
|
||||
return 0;
|
||||
}
|
64
tests/regress/callback-pc.py
Executable file
64
tests/regress/callback-pc.py
Executable file
@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# reg_write() can't modify PC from within trace callbacks
|
||||
# Pull Request #4
|
||||
|
||||
from __future__ import print_function
|
||||
from unicorn import *
|
||||
from unicorn.arm_const import *
|
||||
|
||||
import regress
|
||||
|
||||
BASE_ADDRESS = 0x10000000
|
||||
|
||||
# sub sp, #0xc
|
||||
THUMB_CODE = "\x83\xb0" * 5
|
||||
|
||||
# callback for tracing instructions
|
||||
def hook_code(uc, address, size, user_data):
|
||||
print(">>> Tracing instruction at 0x%x, instruction size = %u" % (address, size))
|
||||
mu = user_data
|
||||
print(">>> Setting PC to 0xffffffff")
|
||||
mu.reg_write(UC_ARM_REG_PC, 0xffffffff)
|
||||
|
||||
# callback for tracing basic blocks
|
||||
def hook_block(uc, address, size, user_data):
|
||||
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
|
||||
mu = user_data
|
||||
print(">>> Setting PC to 0xffffffff")
|
||||
mu.reg_write(UC_ARM_REG_PC, 0xffffffff)
|
||||
|
||||
class CallBackPCTest(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
self.instruction_trace_test()
|
||||
|
||||
# set up emulation
|
||||
def instruction_trace_test(self):
|
||||
try:
|
||||
# initialize emulator in ARM's Thumb mode
|
||||
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
|
||||
|
||||
# map some memory
|
||||
mu.mem_map(BASE_ADDRESS, 2 * 1024 * 1024)
|
||||
|
||||
# write machine code to be emulated to memory
|
||||
mu.mem_write(BASE_ADDRESS, THUMB_CODE)
|
||||
|
||||
# setup stack
|
||||
mu.reg_write(UC_ARM_REG_SP, BASE_ADDRESS + 2 * 1024 * 1024)
|
||||
|
||||
# tracing all instructions with customized callback
|
||||
mu.hook_add(UC_HOOK_CODE, hook_code, user_data=mu)
|
||||
|
||||
# tracing all basic blocks with customized callback
|
||||
mu.hook_add(UC_HOOK_BLOCK, hook_block, user_data=mu)
|
||||
|
||||
# emulate machine code in infinite time
|
||||
mu.emu_start(BASE_ADDRESS, BASE_ADDRESS + len(THUMB_CODE))
|
||||
|
||||
except UcError as e:
|
||||
assertFalse(0, "ERROR: %s" % e)
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
37
tests/regress/crash_tb.py
Executable file
37
tests/regress/crash_tb.py
Executable file
@ -0,0 +1,37 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from unicorn import *
|
||||
from unicorn.x86_const import *
|
||||
|
||||
import regress
|
||||
|
||||
CODE_ADDR = 0x0
|
||||
binary1 = b'\xb8\x02\x00\x00\x00'
|
||||
binary2 = b'\xb8\x01\x00\x00\x00'
|
||||
|
||||
class CrashTB(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_64)
|
||||
|
||||
mu.mem_map(CODE_ADDR, 2 * 1024 * 1024)
|
||||
|
||||
# write machine code to be emulated to memory
|
||||
mu.mem_write(CODE_ADDR, binary1)
|
||||
|
||||
# emu for maximum 1 sec.
|
||||
mu.emu_start(CODE_ADDR, len(binary1), UC_SECOND_SCALE)
|
||||
|
||||
self.assertEqual(0x2, mu.reg_read(UC_X86_REG_RAX))
|
||||
|
||||
# write machine code to be emulated to memory
|
||||
mu.mem_write(CODE_ADDR, binary2)
|
||||
|
||||
# emu for maximum 1 sec.
|
||||
mu.emu_start(CODE_ADDR, len(binary2), UC_SECOND_SCALE)
|
||||
|
||||
self.assertEqual(0x1, mu.reg_read(UC_X86_REG_RAX))
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
||||
|
20
tests/regress/deadlock_1.py
Executable file
20
tests/regress/deadlock_1.py
Executable file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/python
|
||||
# From issue #1 of Ryan Hileman
|
||||
|
||||
from unicorn import *
|
||||
import regress
|
||||
|
||||
CODE = b"\x90\x91\x92"
|
||||
|
||||
class DeadLock(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_64)
|
||||
mu.mem_map(0x100000, 4 * 1024)
|
||||
mu.mem_write(0x100000, CODE)
|
||||
|
||||
with self.assertRaises(UcError):
|
||||
mu.emu_start(0x100000, 0x1000 + len(CODE))
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
20
tests/regress/emu_stop_segfault.py
Executable file
20
tests/regress/emu_stop_segfault.py
Executable file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""See https://github.com/unicorn-engine/unicorn/issues/65"""
|
||||
|
||||
import unicorn
|
||||
import regress
|
||||
|
||||
class EmuStopSegFault(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
ADDR = 0x10101000
|
||||
mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
|
||||
mu.mem_map(ADDR, 1024 * 4)
|
||||
mu.mem_write(ADDR, b'\x41')
|
||||
mu.emu_start(ADDR, ADDR + 1, count=1)
|
||||
# The following should not trigger a null pointer dereference
|
||||
self.assertEqual(None, mu.emu_stop())
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
69
tests/regress/fpu_ip.py
Executable file
69
tests/regress/fpu_ip.py
Executable file
@ -0,0 +1,69 @@
|
||||
#!/usr/bin/python
|
||||
from unicorn import *
|
||||
from unicorn.x86_const import *
|
||||
from capstone import *
|
||||
import regress
|
||||
|
||||
ESP = 0x2000
|
||||
PAGE_SIZE = 2 * 1024 * 1024
|
||||
|
||||
# mov [esp], DWORD 0x37f
|
||||
# fldcw [esp]
|
||||
# fnop
|
||||
# fnstenv [esp + 8]
|
||||
# pop ecx
|
||||
CODE = b'\xc7\x04\x24\x7f\x03\x00\x00\xd9\x2c\x24\xd9\xd0\xd9\x74\x24\x08\x59'
|
||||
|
||||
class SimpleEngine:
|
||||
def __init__(self):
|
||||
self.capmd = Cs(CS_ARCH_X86, CS_MODE_32)
|
||||
|
||||
def disas_single(self, data):
|
||||
for i in self.capmd.disasm(data, 16):
|
||||
print("\t%s\t%s" % (i.mnemonic, i.op_str))
|
||||
break
|
||||
|
||||
disasm = SimpleEngine()
|
||||
|
||||
def hook_code(uc, addr, size, user_data):
|
||||
mem = uc.mem_read(addr, size)
|
||||
print(" 0x%X:" % (addr)),
|
||||
disasm.disas_single(str(mem))
|
||||
|
||||
class FpuIP(regress.RegressTest):
|
||||
|
||||
def mem_reader(self, mu, addr, size, expected):
|
||||
tmp = mu.mem_read(addr, size)
|
||||
for out, exp in zip(tmp, expected):
|
||||
self.assertEqual(exp, out)
|
||||
|
||||
def test_32(self):
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_32)
|
||||
|
||||
mu.mem_map(0x0, PAGE_SIZE)
|
||||
mu.mem_write(0x4000, CODE)
|
||||
mu.reg_write(UC_X86_REG_ESP, ESP)
|
||||
mu.hook_add(UC_HOOK_CODE, hook_code)
|
||||
|
||||
mu.emu_start(0x4000, 0, 0, 5)
|
||||
esp = mu.reg_read(UC_X86_REG_ESP)
|
||||
self.assertEqual(0x2004, esp)
|
||||
expected = [0x0, 0x0, 0xa, 0x40]
|
||||
self.mem_reader(mu, esp + 14, 4, expected)
|
||||
|
||||
def test_64(self):
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_64)
|
||||
|
||||
mu.mem_map(0x0, PAGE_SIZE)
|
||||
mu.mem_write(0x4000, CODE)
|
||||
mu.reg_write(UC_X86_REG_ESP, ESP)
|
||||
mu.hook_add(UC_HOOK_CODE, hook_code)
|
||||
|
||||
mu.emu_start(0x4000, 0, 0, 5)
|
||||
rsp = mu.reg_read(UC_X86_REG_RSP)
|
||||
self.assertEqual(0x2012, rsp + 10)
|
||||
expected = [0x0, 0x0, 0xa, 0x40, 0x0, 0x0, 0x0, 0x0]
|
||||
self.mem_reader(mu, rsp + 10, 4, expected)
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
38
tests/regress/fpu_mem_write.py
Executable file
38
tests/regress/fpu_mem_write.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/python
|
||||
from unicorn import *
|
||||
from unicorn.x86_const import *
|
||||
|
||||
import regress
|
||||
|
||||
ESP = 0x2000
|
||||
PAGE_SIZE = 1 * 1024 * 1024
|
||||
|
||||
# wait
|
||||
# fnstcw word ptr [esp]
|
||||
# pop ecx
|
||||
CODE = b'\x9B\xD9\x3C\x24\x59'
|
||||
|
||||
def hook_mem_write(uc, access, address, size, value, user_data):
|
||||
print("mem WRITE: 0x%x, data size = %u, data value = 0x%x" % (address, size, value))
|
||||
return True
|
||||
|
||||
class FpuWrite(regress.RegressTest):
|
||||
|
||||
def mem_reader(self, mu, addr, size, expected):
|
||||
tmp = mu.mem_read(addr, size)
|
||||
for i, e in zip(tmp, expected):
|
||||
self.assertEquals(e, i)
|
||||
|
||||
def runTest(self):
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_32)
|
||||
mu.mem_map(0, PAGE_SIZE)
|
||||
mu.mem_write(0, CODE)
|
||||
mu.reg_write(UC_X86_REG_ESP, ESP)
|
||||
|
||||
mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_write)
|
||||
mu.emu_start(0x0, 5, 0, 2)
|
||||
esp = mu.reg_read(UC_X86_REG_ESP)
|
||||
self.mem_reader(mu, esp, 10, [0] * 10)
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
57
tests/regress/hang.py
Executable file
57
tests/regress/hang.py
Executable file
@ -0,0 +1,57 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from __future__ import print_function
|
||||
from unicorn import *
|
||||
from unicorn.x86_const import *
|
||||
|
||||
import regress
|
||||
|
||||
# callback for tracing instructions
|
||||
def hook_code(uc, address, size, user_data):
|
||||
tmp = uc.mem_read(address, size)
|
||||
print("[0x%x] =" %(address), end="")
|
||||
for i in tmp:
|
||||
print(" %02x" %i, end="")
|
||||
print("")
|
||||
|
||||
# callback for tracing Linux interrupt
|
||||
def hook_intr(uc, intno, user_data):
|
||||
# only handle Linux syscall
|
||||
rip = uc.reg_read(UC_X86_REG_RIP)
|
||||
if intno != 0x80:
|
||||
print("=== 0x%x: got interrupt %x, quit" %(rip, intno));
|
||||
uc.emu_stop()
|
||||
return
|
||||
|
||||
eax = uc.reg_read(UC_X86_REG_EAX)
|
||||
print(">>> 0x%x: interrupt 0x%x, EAX = 0x%x" %(rip, intno, eax))
|
||||
|
||||
class Hang(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
binary1 = b'\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\x41\x41\x41\x41'
|
||||
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_64)
|
||||
|
||||
mu.mem_map(0, 2 * 1024 * 1024)
|
||||
|
||||
# tracing all instructions with customized callback
|
||||
mu.hook_add(UC_HOOK_CODE, hook_code)
|
||||
|
||||
# handle interrupt ourself
|
||||
mu.hook_add(UC_HOOK_INTR, hook_intr)
|
||||
|
||||
# setup stack
|
||||
mu.reg_write(UC_X86_REG_RSP, 1024 * 1024)
|
||||
|
||||
# fill in memory with 0xCC (software breakpoint int 3)
|
||||
for i in xrange(1 * 1024):
|
||||
mu.mem_write(0 + i, b'\xcc')
|
||||
|
||||
# write machine code to be emulated to memory
|
||||
mu.mem_write(0, binary1)
|
||||
|
||||
self.assertEqual(mu.emu_start(0, len(binary1)), None)
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
39
tests/regress/jmp_ebx_hang.py
Executable file
39
tests/regress/jmp_ebx_hang.py
Executable file
@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""See https://github.com/unicorn-engine/unicorn/issues/82"""
|
||||
|
||||
import unicorn
|
||||
from unicorn import *
|
||||
import regress
|
||||
|
||||
CODE_ADDR = 0x10101000
|
||||
CODE = b'\xff\xe3' # jmp ebx
|
||||
|
||||
class JumEbxHang(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
|
||||
mu.mem_map(CODE_ADDR, 1024 * 4)
|
||||
mu.mem_write(CODE_ADDR, CODE)
|
||||
# If EBX is zero then an exception is raised, as expected
|
||||
mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, 0x0)
|
||||
|
||||
print(">>> jmp ebx (ebx = 0)");
|
||||
with self.assertRaises(UcError) as m:
|
||||
mu.emu_start(CODE_ADDR, CODE_ADDR + 2, count=1)
|
||||
|
||||
self.assertEqual(m.exception.errno, unicorn.UC_ERR_CODE_INVALID)
|
||||
|
||||
print(">>> jmp ebx (ebx = 0xaa96a47f)");
|
||||
mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
|
||||
mu.mem_map(CODE_ADDR, 1024 * 4)
|
||||
# If we write this address to EBX then the emulator hangs on emu_start
|
||||
mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, 0xaa96a47f)
|
||||
mu.mem_write(CODE_ADDR, CODE)
|
||||
with self.assertRaises(UcError) as m:
|
||||
mu.emu_start(CODE_ADDR, CODE_ADDR + 2, count=1)
|
||||
|
||||
self.assertEqual(m.exception.errno, unicorn.UC_ERR_CODE_INVALID)
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
31
tests/regress/map_crash.c
Normal file
31
tests/regress/map_crash.c
Normal file
@ -0,0 +1,31 @@
|
||||
#include <unicorn/unicorn.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define UC_BUG_WRITE_SIZE 13000
|
||||
#define UC_BUG_WRITE_ADDR 0x1000
|
||||
|
||||
int main()
|
||||
{
|
||||
int size;
|
||||
uint8_t *buf;
|
||||
uc_engine *uc;
|
||||
uc_err err = uc_open (UC_ARCH_X86, UC_MODE_64, &uc);
|
||||
if (err) {
|
||||
fprintf (stderr, "Cannot initialize unicorn\n");
|
||||
return 1;
|
||||
}
|
||||
size = UC_BUG_WRITE_SIZE;
|
||||
buf = malloc (size);
|
||||
if (!buf) {
|
||||
fprintf (stderr, "Cannot allocate\n");
|
||||
return 1;
|
||||
}
|
||||
memset (buf, 0, size);
|
||||
if (!uc_mem_map (uc, UC_BUG_WRITE_ADDR, size, UC_PROT_ALL)) {
|
||||
uc_mem_write (uc, UC_BUG_WRITE_ADDR, buf, size);
|
||||
}
|
||||
uc_close(uc);
|
||||
return 0;
|
||||
}
|
50
tests/regress/map_write.c
Normal file
50
tests/regress/map_write.c
Normal file
@ -0,0 +1,50 @@
|
||||
#include <unicorn/unicorn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ADDR 0x00400000
|
||||
#define SIZE 1024*64
|
||||
#define OVERFLOW 1
|
||||
|
||||
int main()
|
||||
{
|
||||
uc_engine *uc;
|
||||
uint8_t *buf, *buf2;
|
||||
int i;
|
||||
uc_err err;
|
||||
|
||||
err = uc_open (UC_ARCH_X86, UC_MODE_64, &uc);
|
||||
if (err) {
|
||||
printf ("uc_open %d\n", err);
|
||||
return 1;
|
||||
}
|
||||
err = uc_mem_map (uc, ADDR, SIZE, UC_PROT_ALL);
|
||||
if (err) {
|
||||
printf ("uc_mem_map %d\n", err);
|
||||
return 1;
|
||||
}
|
||||
buf = calloc (SIZE*2, 1);
|
||||
buf2 = calloc (SIZE, 1);
|
||||
for (i=0;i<SIZE; i++) {
|
||||
buf[i] = i & 0xff;
|
||||
}
|
||||
/* crash here */
|
||||
err = uc_mem_write (uc, ADDR, buf, SIZE+OVERFLOW);
|
||||
if (err) {
|
||||
printf ("uc_mem_map %d\n", err);
|
||||
return 1;
|
||||
}
|
||||
err = uc_mem_read (uc, ADDR+10, buf2, 4);
|
||||
if (err) {
|
||||
printf ("uc_mem_map %d\n", err);
|
||||
return 1;
|
||||
}
|
||||
if (buf2[0] != 0xa) {
|
||||
printf ("mem contents are wrong\n");
|
||||
return 1;
|
||||
}
|
||||
printf ("OK\n");
|
||||
free (buf);
|
||||
free (buf2);
|
||||
return 0;
|
||||
}
|
280
tests/regress/mem_exec.c
Normal file
280
tests/regress/mem_exec.c
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
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 <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <unistd.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 - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr);
|
||||
return false;
|
||||
case UC_MEM_EXEC_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, (uint64_t)1, (uint64_t)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, (uint64_t)1, (uint64_t)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_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID ucr\n", log_num++);
|
||||
return 8;
|
||||
} else {
|
||||
printf("ok %d - UC_HOOK_MEM_INVALID 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;
|
||||
}
|
302
tests/regress/mem_protect.c
Normal file
302
tests/regress/mem_protect.c
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
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 <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <unistd.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 - UC_HOOK_MEM_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, (uint64_t)1, (uint64_t)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, (uint64_t)1, (uint64_t)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_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID ucr\n", log_num++);
|
||||
return 7;
|
||||
} else {
|
||||
printf("ok %d - UC_HOOK_MEM_INVALID 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;
|
||||
}
|
293
tests/regress/mem_unmap.c
Normal file
293
tests/regress/mem_unmap.c
Normal file
@ -0,0 +1,293 @@
|
||||
/*
|
||||
|
||||
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 <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <unistd.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 - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr);
|
||||
return false;
|
||||
case UC_MEM_WRITE:
|
||||
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, (uint64_t)1, (uint64_t)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, (uint64_t)1, (uint64_t)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_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID ucr\n", log_num++);
|
||||
return 7;
|
||||
} else {
|
||||
printf("ok %d - UC_HOOK_MEM_INVALID 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;
|
||||
}
|
40
tests/regress/memmap.py
Executable file
40
tests/regress/memmap.py
Executable file
@ -0,0 +1,40 @@
|
||||
#!/usr/bin/python
|
||||
# By Ryan Hileman, issue #9
|
||||
|
||||
# this prints out 2 lines and the contents must be the same
|
||||
|
||||
from unicorn import *
|
||||
import regress
|
||||
|
||||
class MemMap(regress.RegressTest):
|
||||
|
||||
def test_mmap_write(self):
|
||||
uc = Uc(UC_ARCH_X86, UC_MODE_64)
|
||||
|
||||
uc.mem_map(0x8048000, 0x2000)
|
||||
uc.mem_write(0x8048000, 'test')
|
||||
s1 = str(uc.mem_read(0x8048000, 4)).encode('hex')
|
||||
|
||||
self.assertEqual('test'.encode('hex'), s1)
|
||||
|
||||
uc.mem_map(0x804a000, 0x8000)
|
||||
s2 = str(uc.mem_read(0x8048000, 4)).encode('hex')
|
||||
self.assertEqual(s1, s2)
|
||||
|
||||
def test_mmap_invalid(self):
|
||||
u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
|
||||
with self.assertRaises(UcError):
|
||||
u.mem_map(0x2000, 0)
|
||||
with self.assertRaises(UcError):
|
||||
u.mem_map(0x4000, 1)
|
||||
|
||||
def test_mmap_weird(self):
|
||||
u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
|
||||
|
||||
for i in xrange(20):
|
||||
with self.assertRaises(UcError):
|
||||
u.mem_map(i*0x1000, 5)
|
||||
u.mem_read(i*0x1000+6, 1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
35
tests/regress/memmap_segfault.py
Executable file
35
tests/regress/memmap_segfault.py
Executable file
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import unicorn
|
||||
from unicorn import *
|
||||
|
||||
import regress
|
||||
|
||||
class MmapSeg(regress.RegressTest):
|
||||
|
||||
def test_seg1(self):
|
||||
u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
|
||||
u.mem_map(0x2000, 0x1000)
|
||||
u.mem_read(0x2000, 1)
|
||||
|
||||
for i in range(50):
|
||||
u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
|
||||
u.mem_map(i*0x1000, 0x1000)
|
||||
u.mem_read(i*0x1000, 1)
|
||||
|
||||
for i in range(20):
|
||||
with self.assertRaises(UcError):
|
||||
u = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
|
||||
u.mem_map(i*0x1000, 5)
|
||||
u.mem_read(i*0x1000, 1)
|
||||
|
||||
def test_seg2(self):
|
||||
uc = Uc(UC_ARCH_X86, UC_MODE_32)
|
||||
uc.mem_map(0x0000, 0x2000)
|
||||
uc.mem_map(0x2000, 0x4000)
|
||||
uc.mem_write(0x1000, 0x1004 * ' ')
|
||||
self.assertTrue(1,
|
||||
'If not reached, then we have BUG (crash on x86_64 Linux).')
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
37
tests/regress/mips_branch_delay.py
Executable file
37
tests/regress/mips_branch_delay.py
Executable file
@ -0,0 +1,37 @@
|
||||
#!/usr/bin/python
|
||||
from capstone import *
|
||||
from unicorn import *
|
||||
from unicorn.mips_const import *
|
||||
|
||||
import regress
|
||||
|
||||
class MipsBranchDelay(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_LITTLE_ENDIAN)
|
||||
|
||||
def disas(code, addr):
|
||||
for i in md.disasm(code, addr):
|
||||
print '0x%x: %s %s' % (i.address, str(i.bytes).encode('hex'), i.op_str)
|
||||
|
||||
def hook_code(uc, addr, size, _):
|
||||
mem = str(uc.mem_read(addr, size))
|
||||
disas(mem, addr)
|
||||
|
||||
CODE = 0x400000
|
||||
asm = '0000a4126a00822800000000'.decode('hex')
|
||||
|
||||
print 'Input instructions:'
|
||||
disas(asm, CODE)
|
||||
print
|
||||
|
||||
print 'Hooked instructions:'
|
||||
|
||||
uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN)
|
||||
uc.hook_add(UC_HOOK_CODE, hook_code)
|
||||
uc.mem_map(CODE, 0x1000)
|
||||
uc.mem_write(CODE, asm)
|
||||
self.assertEqual(None, uc.emu_start(CODE, CODE + len(asm)))
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
41
tests/regress/mips_except.py
Executable file
41
tests/regress/mips_except.py
Executable file
@ -0,0 +1,41 @@
|
||||
#!/usr/bin/python
|
||||
from unicorn import *
|
||||
from unicorn.mips_const import *
|
||||
|
||||
import regress
|
||||
|
||||
def hook_intr(uc, intno, _):
|
||||
print 'interrupt', intno
|
||||
|
||||
CODE = 0x400000
|
||||
asm = '0000a48f'.decode('hex') # lw $a0, ($sp)
|
||||
|
||||
class MipsExcept(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN)
|
||||
uc.hook_add(UC_HOOK_INTR, hook_intr)
|
||||
uc.mem_map(CODE, 0x1000)
|
||||
uc.mem_write(CODE, asm)
|
||||
|
||||
with self.assertRaises(UcError) as m:
|
||||
uc.reg_write(UC_MIPS_REG_SP, 0x400001)
|
||||
uc.emu_start(CODE, CODE + len(asm), 300)
|
||||
|
||||
self.assertEqual(UC_ERR_READ_UNALIGNED, m.exception.errno)
|
||||
|
||||
with self.assertRaises(UcError) as m:
|
||||
uc.reg_write(UC_MIPS_REG_SP, 0xFFFFFFF0)
|
||||
uc.emu_start(CODE, CODE + len(asm), 200)
|
||||
|
||||
self.assertEqual(UC_ERR_READ_INVALID, m.exception.errno)
|
||||
|
||||
with self.assertRaises(UcError) as m:
|
||||
uc.reg_write(UC_MIPS_REG_SP, 0x80000000)
|
||||
uc.emu_start(CODE, CODE + len(asm), 100)
|
||||
|
||||
self.assertEqual(UC_ERR_READ_INVALID, m.exception.errno)
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
||||
|
35
tests/regress/movsd.py
Executable file
35
tests/regress/movsd.py
Executable file
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/python
|
||||
# By Ryan Hileman, issue #3
|
||||
|
||||
from capstone import *
|
||||
from unicorn import *
|
||||
from unicorn.x86_const import *
|
||||
|
||||
import regress
|
||||
code = 'f20f1005aa120000'.decode('hex')
|
||||
|
||||
def dis(mem, addr):
|
||||
md = Cs(CS_ARCH_X86, CS_MODE_64)
|
||||
return '\n'.join([
|
||||
'%s %s' % (i.mnemonic, i.op_str)
|
||||
for i in md.disasm(str(mem), addr)
|
||||
])
|
||||
|
||||
def hook_code(uc, addr, size, user_data):
|
||||
mem = uc.mem_read(addr, size)
|
||||
print 'instruction size:', size
|
||||
print 'instruction:', str(mem).encode('hex'), dis(mem, addr)
|
||||
print 'reference: ', code.encode('hex'), dis(code, addr)
|
||||
|
||||
class Movsd(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
addr = 0x400000
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_64)
|
||||
mu.hook_add(UC_HOOK_CODE, hook_code)
|
||||
mu.mem_map(addr, 8 * 1024 * 1024)
|
||||
mu.mem_write(addr, code)
|
||||
mu.emu_start(addr, addr + len(code))
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
110
tests/regress/nr_mem_test.c
Normal file
110
tests/regress/nr_mem_test.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
Non-readable memory test case
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
const uint8_t PROGRAM[] =
|
||||
"\x8b\x1d\x00\x00\x30\x00\xa1\x00\x00\x40\x00";
|
||||
// total size: 11 bytes
|
||||
|
||||
/*
|
||||
bits 32
|
||||
|
||||
mov ebx, [0x300000]
|
||||
mov eax, [0x400000]
|
||||
*/
|
||||
|
||||
// callback for tracing memory access (READ or WRITE)
|
||||
static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t address, int size, int64_t value, void *user_data)
|
||||
{
|
||||
|
||||
switch(type) {
|
||||
default:
|
||||
// return false to indicate we want to stop emulation
|
||||
return false;
|
||||
case UC_MEM_READ_PROT:
|
||||
printf(">>> non-readable memory is being read at 0x%"PRIx64 ", data size = %u\n",
|
||||
address, size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1, trace2;
|
||||
uc_err err;
|
||||
uint32_t eax, ebx;
|
||||
|
||||
printf("Memory protections test\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 1;
|
||||
}
|
||||
|
||||
uc_mem_map(uc, 0x100000, 0x1000, UC_PROT_READ);
|
||||
uc_mem_map(uc, 0x300000, 0x1000, UC_PROT_READ | UC_PROT_WRITE);
|
||||
uc_mem_map(uc, 0x400000, 0x1000, UC_PROT_WRITE);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
if (uc_mem_write(uc, 0x100000, PROGRAM, sizeof(PROGRAM))) {
|
||||
printf("Failed to write emulation code to memory, quit!\n");
|
||||
return 2;
|
||||
} else {
|
||||
printf("Allowed to write to read only memory via uc_mem_write\n");
|
||||
}
|
||||
|
||||
uc_mem_write(uc, 0x300000, (const uint8_t*)"\x41\x41\x41\x41", 4);
|
||||
uc_mem_write(uc, 0x400000, (const uint8_t*)"\x42\x42\x42\x42", 4);
|
||||
|
||||
//uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)0x400000, (uint64_t)0x400fff);
|
||||
|
||||
// intercept invalid memory events
|
||||
uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL);
|
||||
|
||||
// emulate machine code in infinite time
|
||||
printf("BEGIN execution\n");
|
||||
err = uc_emu_start(uc, 0x100000, 0x100000 + sizeof(PROGRAM), 0, 2);
|
||||
if (err) {
|
||||
printf("Expected failure on uc_emu_start() with error returned %u: %s\n",
|
||||
err, uc_strerror(err));
|
||||
} else {
|
||||
printf("UNEXPECTED uc_emu_start returned UC_ERR_OK\n");
|
||||
}
|
||||
printf("END execution\n");
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_EAX, &eax);
|
||||
printf("Final eax = 0x%x\n", eax);
|
||||
uc_reg_read(uc, UC_X86_REG_EBX, &ebx);
|
||||
printf("Final ebx = 0x%x\n", ebx);
|
||||
|
||||
uc_close(uc);
|
||||
|
||||
return 0;
|
||||
}
|
21
tests/regress/pshufb.py
Executable file
21
tests/regress/pshufb.py
Executable file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/python
|
||||
# By Ryan Hileman, issue #91
|
||||
|
||||
# Invalid instruction = test failed
|
||||
|
||||
from unicorn import *
|
||||
from unicorn.x86_const import *
|
||||
|
||||
import regress
|
||||
|
||||
class Pshufb(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
uc = Uc(UC_ARCH_X86, UC_MODE_64)
|
||||
uc.mem_map(0x2000, 0x1000)
|
||||
# pshufb xmm0, xmm1
|
||||
uc.mem_write(0x2000, '660f3800c1'.decode('hex'))
|
||||
uc.emu_start(0x2000, 0x2005)
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
31
tests/regress/reg_write_sign_extension.py
Executable file
31
tests/regress/reg_write_sign_extension.py
Executable file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""See https://github.com/unicorn-engine/unicorn/issues/98"""
|
||||
|
||||
import unicorn
|
||||
import regress
|
||||
|
||||
ADDR = 0xffaabbcc
|
||||
|
||||
def hook_mem_invalid(mu, access, address, size, value, user_data):
|
||||
print ">>> Access type: %u, expected value: 0x%x, actual value: 0x%x" % (access, ADDR, address)
|
||||
assert(address == ADDR)
|
||||
mu.mem_map(address & 0xfffff000, 4 * 1024)
|
||||
mu.mem_write(address, b'\xcc')
|
||||
return True
|
||||
|
||||
class RegWriteSignExt(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
|
||||
mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, ADDR)
|
||||
|
||||
mu.mem_map(0x10000000, 1024 * 4)
|
||||
# jmp ebx
|
||||
mu.mem_write(0x10000000, b'\xff\xe3')
|
||||
|
||||
mu.hook_add(unicorn.UC_HOOK_MEM_INVALID, hook_mem_invalid)
|
||||
mu.emu_start(0x10000000, 0x10000000 + 2, count=1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
34
tests/regress/regress.py
Executable file
34
tests/regress/regress.py
Executable file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import unittest
|
||||
|
||||
from os.path import dirname, basename, isfile
|
||||
import glob
|
||||
|
||||
# Find all unittest type in this directory and run it.
|
||||
|
||||
class RegressTest(unittest.TestCase):
|
||||
pass
|
||||
|
||||
def main():
|
||||
unittest.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
directory = dirname(__file__)
|
||||
if directory == '':
|
||||
directory = '.'
|
||||
modules = glob.glob(directory+"/*.py")
|
||||
__all__ = [ basename(f)[:-3] for f in modules if isfile(f)]
|
||||
suite = unittest.TestSuite()
|
||||
|
||||
for module in __all__:
|
||||
m = __import__(module)
|
||||
for cl in dir(m):
|
||||
try:
|
||||
realcl = getattr(m,cl)
|
||||
if issubclass(realcl, unittest.TestCase):
|
||||
suite.addTest(realcl())
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
unittest.TextTestRunner().run(suite)
|
184
tests/regress/rep_movsb.c
Normal file
184
tests/regress/rep_movsb.c
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
|
||||
rep movsb regression
|
||||
|
||||
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 <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
unsigned char PROGRAM[] =
|
||||
"\xbe\x00\x00\x20\x00\xbf\x00\x10\x20\x00\xb9\x14\x00\x00\x00\xf3"
|
||||
"\xa4\xf4";
|
||||
// total size: 18 bytes
|
||||
|
||||
/*
|
||||
bits 32
|
||||
|
||||
; assumes code section at 0x100000 r-x
|
||||
; assumes data section at 0x200000-0x202000, rw-
|
||||
|
||||
mov esi, 0x200000
|
||||
mov edi, 0x201000
|
||||
mov ecx, 20
|
||||
rep movsb
|
||||
hlt
|
||||
*/
|
||||
|
||||
static int log_num = 1;
|
||||
|
||||
// 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);
|
||||
_exit(-1);
|
||||
}
|
||||
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
|
||||
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);
|
||||
if (addr < 0x201000L) {
|
||||
//this is actually a read, we don't write in this range
|
||||
printf("not ok %d - write hook called for read of 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", log_num++, addr, size, value);
|
||||
}
|
||||
else {
|
||||
printf("ok %d - write hook called for write of 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", log_num++, addr, size, value);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1, trace2;
|
||||
uc_err err;
|
||||
uint8_t buf1[100], readbuf[100];
|
||||
|
||||
printf("# rep movsb test\n");
|
||||
|
||||
memset(buf1, 'A', 20);
|
||||
|
||||
// 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_mem_map(uc, 0x200000, 0x2000, UC_PROT_READ | UC_PROT_WRITE);
|
||||
|
||||
// fill in the data that we want to copy
|
||||
if (uc_mem_write(uc, 0x200000, buf1, 20)) {
|
||||
printf("not ok %d - Failed to write read buffer to memory, quit!\n", log_num++);
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
printf("ok %d - Read buffer 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 4;
|
||||
}
|
||||
else {
|
||||
printf("ok %d - Program written to memory\n", log_num++);
|
||||
}
|
||||
|
||||
if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++);
|
||||
return 5;
|
||||
}
|
||||
else {
|
||||
printf("ok %d - UC_HOOK_CODE installed\n", log_num++);
|
||||
}
|
||||
|
||||
// intercept memory write events only, NOT read events
|
||||
if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) {
|
||||
printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++);
|
||||
return 6;
|
||||
}
|
||||
else {
|
||||
printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++);
|
||||
}
|
||||
|
||||
// emulate machine code until told to stop by hook_code
|
||||
printf("# BEGIN execution\n");
|
||||
err = uc_emu_start(uc, 0x100000, 0x101000, 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");
|
||||
|
||||
//make sure that data got copied
|
||||
// fill in sections that shouldn't get touched
|
||||
if (uc_mem_read(uc, 0x201000, readbuf, 20)) {
|
||||
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, 20)) {
|
||||
printf("not ok %d - write buffer contents are incorrect\n", log_num++);
|
||||
}
|
||||
else {
|
||||
printf("ok %d - write buffer 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;
|
||||
}
|
211
tests/regress/ro_mem_test.c
Normal file
211
tests/regress/ro_mem_test.c
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
Non-writable memory test case
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
const uint8_t PROGRAM[] =
|
||||
"\xeb\x1a\x58\x83\xc0\x04\x83\xe0\xfc\x83\xc0\x01\xc7\x00\x78\x56"
|
||||
"\x34\x12\x83\xc0\x07\xc7\x00\x21\x43\x65\x87\x90\xe8\xe1\xff\xff"
|
||||
"\xff" "xxxxAAAAxxxBBBB";
|
||||
// total size: 33 bytes
|
||||
|
||||
/*
|
||||
jmp short bottom
|
||||
top:
|
||||
pop eax
|
||||
add eax, 4
|
||||
and eax, 0xfffffffc
|
||||
add eax, 1 ; unaligned
|
||||
mov dword [eax], 0x12345678 ; try to write into code section
|
||||
add eax, 7 ; aligned
|
||||
mov dword [eax], 0x87654321 ; try to write into code section
|
||||
nop
|
||||
bottom:
|
||||
call top
|
||||
*/
|
||||
|
||||
// callback for tracing instruction
|
||||
static void hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
uint32_t esp;
|
||||
printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size);
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_ESP, &esp);
|
||||
printf(">>> --- ESP is 0x%x\n", esp);
|
||||
|
||||
}
|
||||
|
||||
// callback for tracing memory access (READ or WRITE)
|
||||
static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t address, int size, int64_t value, void *user_data)
|
||||
{
|
||||
uint32_t esp;
|
||||
uc_reg_read(uc, UC_X86_REG_ESP, &esp);
|
||||
|
||||
switch(type) {
|
||||
default:
|
||||
// return false to indicate we want to stop emulation
|
||||
return false;
|
||||
case UC_MEM_WRITE:
|
||||
//if this is a push, esp has not been adjusted yet
|
||||
if (esp == (address + size)) {
|
||||
uint32_t upper;
|
||||
upper = (esp + 0xfff) & ~0xfff;
|
||||
printf(">>> Stack appears to be missing at 0x%"PRIx64 ", allocating now\n", address);
|
||||
// map this memory in with 2MB in size
|
||||
uc_mem_map(uc, upper - 0x8000, 0x8000, UC_PROT_READ | UC_PROT_WRITE);
|
||||
// return true to indicate we want to continue
|
||||
return true;
|
||||
}
|
||||
printf(">>> Missing memory is being WRITTEN at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n",
|
||||
address, size, value);
|
||||
return false;
|
||||
case UC_MEM_WRITE_PROT:
|
||||
printf(">>> RO memory is being WRITTEN at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n",
|
||||
address, size, value);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define STACK 0x500000
|
||||
#define STACK_SIZE 0x5000
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1, trace2;
|
||||
uc_err err;
|
||||
uint8_t bytes[8];
|
||||
uint32_t esp;
|
||||
int result;
|
||||
int map_stack = 0;
|
||||
|
||||
if (argc == 2 && strcmp(argv[1], "--map-stack") == 0) {
|
||||
map_stack = 1;
|
||||
}
|
||||
|
||||
printf("Memory mapping test\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 1;
|
||||
}
|
||||
|
||||
uc_mem_map(uc, 0x100000, 0x1000, UC_PROT_ALL);
|
||||
uc_mem_map(uc, 0x200000, 0x2000, UC_PROT_ALL);
|
||||
uc_mem_map(uc, 0x300000, 0x3000, UC_PROT_ALL);
|
||||
uc_mem_map(uc, 0x400000, 0x4000, UC_PROT_READ);
|
||||
|
||||
if (map_stack) {
|
||||
printf("Pre-mapping stack\n");
|
||||
uc_mem_map(uc, STACK, STACK_SIZE, UC_PROT_READ | UC_PROT_WRITE);
|
||||
} else {
|
||||
printf("Mapping stack on first invalid memory access\n");
|
||||
}
|
||||
|
||||
esp = STACK + STACK_SIZE;
|
||||
|
||||
uc_reg_write(uc, UC_X86_REG_ESP, &esp);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
if (uc_mem_write(uc, 0x400000, PROGRAM, sizeof(PROGRAM))) {
|
||||
printf("Failed to write emulation code to memory, quit!\n");
|
||||
return 2;
|
||||
} else {
|
||||
printf("Allowed to write to read only memory via uc_mem_write\n");
|
||||
}
|
||||
|
||||
//uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)0x400000, (uint64_t)0x400fff);
|
||||
|
||||
// intercept invalid memory events
|
||||
uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL);
|
||||
|
||||
// emulate machine code in infinite time
|
||||
printf("BEGIN execution - 1\n");
|
||||
err = uc_emu_start(uc, 0x400000, 0x400000 + sizeof(PROGRAM), 0, 10);
|
||||
if (err) {
|
||||
printf("Expected failue on uc_emu_start() with error returned %u: %s\n",
|
||||
err, uc_strerror(err));
|
||||
} else {
|
||||
printf("UNEXPECTED uc_emu_start returned UC_ERR_OK\n");
|
||||
}
|
||||
printf("END execution - 1\n");
|
||||
|
||||
// emulate machine code in infinite time
|
||||
printf("BEGIN execution - 2\n");
|
||||
//update eax to point to aligned memory (same as add eax,7 above)
|
||||
uint32_t eax = 0x40002C;
|
||||
uc_reg_write(uc, UC_X86_REG_EAX, &eax);
|
||||
//resume execution at the mov dword [eax], 0x87654321
|
||||
//to test an aligned write as well
|
||||
err = uc_emu_start(uc, 0x400015, 0x400000 + sizeof(PROGRAM), 0, 2);
|
||||
if (err) {
|
||||
printf("Expected failure on uc_emu_start() with error returned %u: %s\n",
|
||||
err, uc_strerror(err));
|
||||
} else {
|
||||
printf("UNEXPECTED uc_emu_start returned UC_ERR_OK\n");
|
||||
}
|
||||
printf("END execution - 2\n");
|
||||
|
||||
printf("Verifying content at 0x400025 is unchanged\n");
|
||||
if (!uc_mem_read(uc, 0x400025, bytes, 4)) {
|
||||
printf(">>> Read 4 bytes from [0x%x] = 0x%x\n", (uint32_t)0x400025, *(uint32_t*) bytes);
|
||||
if (0x41414141 != *(uint32_t*) bytes) {
|
||||
printf("ERROR content in read only memory changed\n");
|
||||
} else {
|
||||
printf("SUCCESS content in read only memory unchanged\n");
|
||||
}
|
||||
} else {
|
||||
printf(">>> Failed to read 4 bytes from [0x%x]\n", (uint32_t)(esp - 4));
|
||||
return 4;
|
||||
}
|
||||
|
||||
printf("Verifying content at 0x40002C is unchanged\n");
|
||||
if (!uc_mem_read(uc, 0x40002C, bytes, 4)) {
|
||||
printf(">>> Read 4 bytes from [0x%x] = 0x%x\n", (uint32_t)0x40002C, *(uint32_t*) bytes);
|
||||
if (0x42424242 != *(uint32_t*) bytes) {
|
||||
printf("ERROR content in read only memory changed\n");
|
||||
} else {
|
||||
printf("SUCCESS content in read only memory unchanged\n");
|
||||
}
|
||||
} else {
|
||||
printf(">>> Failed to read 4 bytes from [0x%x]\n", (uint32_t)(esp - 4));
|
||||
return 4;
|
||||
}
|
||||
|
||||
printf("Verifying content at bottom of stack is readable and correct\n");
|
||||
if (!uc_mem_read(uc, esp - 4, bytes, 4)) {
|
||||
printf(">>> Read 4 bytes from [0x%x] = 0x%x\n", (uint32_t)(esp - 4), *(uint32_t*) bytes);
|
||||
} else {
|
||||
printf(">>> Failed to read 4 bytes from [0x%x]\n", (uint32_t)(esp - 4));
|
||||
return 4;
|
||||
}
|
||||
|
||||
uc_close(uc);
|
||||
|
||||
return 0;
|
||||
}
|
46
tests/regress/sigill.c
Normal file
46
tests/regress/sigill.c
Normal file
@ -0,0 +1,46 @@
|
||||
#include <unicorn/unicorn.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define UC_BUG_WRITE_SIZE 128
|
||||
#define UC_BUG_WRITE_ADDR 0x1000 // fix this by change this to 0x2000
|
||||
|
||||
int got_sigill = 0;
|
||||
|
||||
void _interrupt(uc_engine *uc, uint32_t intno, void *user_data)
|
||||
{
|
||||
if (intno == 6) {
|
||||
uc_emu_stop(uc);
|
||||
got_sigill = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int size;
|
||||
uint8_t *buf;
|
||||
uc_engine *uc;
|
||||
uc_hook uh_trap;
|
||||
uc_err err = uc_open (UC_ARCH_X86, UC_MODE_64, &uc);
|
||||
if (err) {
|
||||
fprintf (stderr, "Cannot initialize unicorn\n");
|
||||
return 1;
|
||||
}
|
||||
size = UC_BUG_WRITE_SIZE;
|
||||
buf = malloc (size);
|
||||
if (!buf) {
|
||||
fprintf (stderr, "Cannot allocate\n");
|
||||
return 1;
|
||||
}
|
||||
memset (buf, 0, size);
|
||||
if (!uc_mem_map(uc, UC_BUG_WRITE_ADDR, size, UC_PROT_ALL)) {
|
||||
uc_mem_write(uc, UC_BUG_WRITE_ADDR,
|
||||
(const uint8_t*)"\xff\xff\xff\xff\xff\xff\xff\xff", 8);
|
||||
}
|
||||
uc_hook_add(uc, &uh_trap, UC_HOOK_INTR, _interrupt, NULL);
|
||||
uc_emu_start(uc, UC_BUG_WRITE_ADDR, UC_BUG_WRITE_ADDR+8, 0, 1);
|
||||
uc_close(uc);
|
||||
printf ("Correct: %s\n", got_sigill? "YES": "NO");
|
||||
return got_sigill? 0: 1;
|
||||
}
|
29
tests/regress/sigill2.c
Normal file
29
tests/regress/sigill2.c
Normal file
@ -0,0 +1,29 @@
|
||||
#include <unicorn/unicorn.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define UC_BUG_WRITE_SIZE 128
|
||||
#define UC_BUG_WRITE_ADDR 0x2000
|
||||
|
||||
int main()
|
||||
{
|
||||
int size;
|
||||
uint8_t *buf;
|
||||
uc_engine *uc;
|
||||
|
||||
uc_err err = uc_open (UC_ARCH_X86, UC_MODE_64, &uc);
|
||||
if (err) {
|
||||
fprintf (stderr, "Cannot initialize unicorn\n");
|
||||
return 1;
|
||||
}
|
||||
size = UC_BUG_WRITE_SIZE;
|
||||
if (!uc_mem_map (uc, UC_BUG_WRITE_ADDR, size, UC_PROT_ALL)) {
|
||||
uc_mem_write (uc, UC_BUG_WRITE_ADDR,
|
||||
(const uint8_t*)"\xff\xff\xff\xff\xff\xff\xff\xff", 8);
|
||||
}
|
||||
err = uc_emu_start(uc, UC_BUG_WRITE_ADDR, UC_BUG_WRITE_ADDR+8, 0, 1);
|
||||
uc_close(uc);
|
||||
printf ("Error = %u (%s)\n", err, uc_strerror(err));
|
||||
return err? -1: 0;
|
||||
}
|
24
tests/regress/sparc64.py
Executable file
24
tests/regress/sparc64.py
Executable file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from unicorn import *
|
||||
from unicorn.sparc_const import *
|
||||
|
||||
PAGE_SIZE = 1 * 1024 * 1024
|
||||
|
||||
uc = Uc(UC_ARCH_SPARC, UC_MODE_64)
|
||||
uc.reg_write(UC_SPARC_REG_SP, 100)
|
||||
print 'writing sp = 100'
|
||||
|
||||
# 0: b0 06 20 01 inc %i0
|
||||
# 4: b2 06 60 01 inc %i1
|
||||
|
||||
CODE = "\xb0\x06\x20\x01" \
|
||||
"\xb2\x06\x60\x01"
|
||||
|
||||
uc.mem_map(0, PAGE_SIZE)
|
||||
uc.mem_write(0, CODE)
|
||||
uc.emu_start(0, len(CODE), 0, 2)
|
||||
|
||||
print 'sp =', uc.reg_read(UC_SPARC_REG_SP)
|
||||
print 'i0 =', uc.reg_read(UC_SPARC_REG_I0)
|
||||
print 'i1 =', uc.reg_read(UC_SPARC_REG_I1)
|
205
tests/regress/sparc_reg.py
Executable file
205
tests/regress/sparc_reg.py
Executable file
@ -0,0 +1,205 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from unicorn import *
|
||||
from unicorn.sparc_const import *
|
||||
|
||||
PAGE_SIZE = 1 * 1024 * 1024
|
||||
|
||||
uc = Uc(UC_ARCH_SPARC, UC_MODE_32)
|
||||
uc.reg_write(UC_SPARC_REG_SP, 100)
|
||||
uc.reg_write(UC_SPARC_REG_FP, 200)
|
||||
|
||||
# 0x0: \x80\x00\x20\x01 add %g0, 1, %g0
|
||||
# 0x4: \x82\x00\x60\x01 add %g1, 1, %g1
|
||||
# 0x8: \x84\x00\xA0\x01 add %g2, 1, %g2
|
||||
# 0xc: \x86\x00\xE0\x01 add %g3, 1, %g3
|
||||
# 0x10: \x88\x01\x20\x01 add %g4, 1, %g4
|
||||
# 0x14: \x8A\x01\x60\x01 add %g5, 1, %g5
|
||||
# 0x18: \x8C\x01\xA0\x01 add %g6, 1, %g6
|
||||
# 0x1c: \x8E\x01\xE0\x01 add %g7, 1, %g7
|
||||
# 0x20: \x90\x02\x20\x01 add %o0, 1, %o0
|
||||
# 0x24: \x92\x02\x60\x01 add %o1, 1, %o1
|
||||
# 0x28: \x94\x02\xA0\x01 add %o2, 1, %o2
|
||||
# 0x2c: \x96\x02\xE0\x01 add %o3, 1, %o3
|
||||
# 0x30: \x98\x03\x20\x01 add %o4, 1, %o4
|
||||
# 0x34: \x9A\x03\x60\x01 add %o5, 1, %o5
|
||||
# 0x38: \x9C\x03\xA0\x01 add %sp, 1, %sp
|
||||
# 0x3c: \x9E\x03\xE0\x01 add %o7, 1, %o7
|
||||
# 0x40: \xA0\x04\x20\x01 add %l0, 1, %l0
|
||||
# 0x44: \xA2\x04\x60\x01 add %l1, 1, %l1
|
||||
# 0x48: \xA4\x04\xA0\x01 add %l2, 1, %l2
|
||||
# 0x4c: \xA6\x04\xE0\x01 add %l3, 1, %l3
|
||||
# 0x50: \xA8\x05\x20\x01 add %l4, 1, %l4
|
||||
# 0x54: \xAA\x05\x60\x01 add %l5, 1, %l5
|
||||
# 0x58: \xAC\x05\xA0\x01 add %l6, 1, %l6
|
||||
# 0x5c: \xAE\x05\xE0\x01 add %l7, 1, %l7
|
||||
# 0x0: \xB0\x06\x20\x01 add %i0, 1, %i0
|
||||
# 0x4: \xB2\x06\x60\x01 add %i1, 1, %i1
|
||||
# 0x8: \xB4\x06\xA0\x01 add %i2, 1, %i2
|
||||
# 0xc: \xB6\x06\xE0\x01 add %i3, 1, %i3
|
||||
# 0x10: \xB8\x07\x20\x01 add %i4, 1, %i4
|
||||
# 0x14: \xBA\x07\x60\x01 add %i5, 1, %i5
|
||||
# 0x18: \xBC\x07\xA0\x01 add %fp, 1, %fp
|
||||
# 0x1c: \xBE\x07\xE0\x01 add %i7, 1, %i7
|
||||
|
||||
|
||||
CODE = "\x80\x00\x20\x01" \
|
||||
"\x82\x00\x60\x01" \
|
||||
"\x84\x00\xA0\x01" \
|
||||
"\x86\x00\xE0\x01" \
|
||||
"\x88\x01\x20\x01" \
|
||||
"\x8A\x01\x60\x01" \
|
||||
"\x8C\x01\xA0\x01" \
|
||||
"\x8E\x01\xE0\x01" \
|
||||
"\x90\x02\x20\x01" \
|
||||
"\x92\x02\x60\x01" \
|
||||
"\x94\x02\xA0\x01" \
|
||||
"\x96\x02\xE0\x01" \
|
||||
"\x98\x03\x20\x01" \
|
||||
"\x9A\x03\x60\x01" \
|
||||
"\x9C\x03\xA0\x01" \
|
||||
"\x9E\x03\xE0\x01" \
|
||||
"\xA0\x04\x20\x01" \
|
||||
"\xA2\x04\x60\x01" \
|
||||
"\xA4\x04\xA0\x01" \
|
||||
"\xA6\x04\xE0\x01" \
|
||||
"\xA8\x05\x20\x01" \
|
||||
"\xAA\x05\x60\x01" \
|
||||
"\xAC\x05\xA0\x01" \
|
||||
"\xAE\x05\xE0\x01" \
|
||||
"\xB0\x06\x20\x01" \
|
||||
"\xB2\x06\x60\x01" \
|
||||
"\xB4\x06\xA0\x01" \
|
||||
"\xB6\x06\xE0\x01" \
|
||||
"\xB8\x07\x20\x01" \
|
||||
"\xBA\x07\x60\x01" \
|
||||
"\xBC\x07\xA0\x01" \
|
||||
"\xBE\x07\xE0\x01"
|
||||
|
||||
|
||||
uc.mem_map(0, PAGE_SIZE)
|
||||
uc.mem_write(0, CODE)
|
||||
uc.emu_start(0, len(CODE), 0, 32)
|
||||
|
||||
def print_registers(mu):
|
||||
g0 = mu.reg_read(UC_SPARC_REG_G0)
|
||||
g1 = mu.reg_read(UC_SPARC_REG_G1)
|
||||
g2 = mu.reg_read(UC_SPARC_REG_G2)
|
||||
g3 = mu.reg_read(UC_SPARC_REG_G3)
|
||||
g4 = mu.reg_read(UC_SPARC_REG_G4)
|
||||
g5 = mu.reg_read(UC_SPARC_REG_G5)
|
||||
g6 = mu.reg_read(UC_SPARC_REG_G6)
|
||||
g7 = mu.reg_read(UC_SPARC_REG_G7)
|
||||
|
||||
o0 = mu.reg_read(UC_SPARC_REG_O0)
|
||||
o1 = mu.reg_read(UC_SPARC_REG_O1)
|
||||
o2 = mu.reg_read(UC_SPARC_REG_O2)
|
||||
o3 = mu.reg_read(UC_SPARC_REG_O3)
|
||||
o4 = mu.reg_read(UC_SPARC_REG_O4)
|
||||
o5 = mu.reg_read(UC_SPARC_REG_O5)
|
||||
o6 = mu.reg_read(UC_SPARC_REG_O6)
|
||||
o7 = mu.reg_read(UC_SPARC_REG_O7)
|
||||
|
||||
l0 = mu.reg_read(UC_SPARC_REG_L0)
|
||||
l1 = mu.reg_read(UC_SPARC_REG_L1)
|
||||
l2 = mu.reg_read(UC_SPARC_REG_L2)
|
||||
l3 = mu.reg_read(UC_SPARC_REG_L3)
|
||||
l4 = mu.reg_read(UC_SPARC_REG_L4)
|
||||
l5 = mu.reg_read(UC_SPARC_REG_L5)
|
||||
l6 = mu.reg_read(UC_SPARC_REG_L6)
|
||||
l7 = mu.reg_read(UC_SPARC_REG_L7)
|
||||
|
||||
i0 = mu.reg_read(UC_SPARC_REG_I0)
|
||||
i1 = mu.reg_read(UC_SPARC_REG_I1)
|
||||
i2 = mu.reg_read(UC_SPARC_REG_I2)
|
||||
i3 = mu.reg_read(UC_SPARC_REG_I3)
|
||||
i4 = mu.reg_read(UC_SPARC_REG_I4)
|
||||
i5 = mu.reg_read(UC_SPARC_REG_I5)
|
||||
i6 = mu.reg_read(UC_SPARC_REG_I6)
|
||||
i7 = mu.reg_read(UC_SPARC_REG_I7)
|
||||
|
||||
pc = mu.reg_read(UC_SPARC_REG_PC)
|
||||
sp = mu.reg_read(UC_SPARC_REG_SP)
|
||||
fp = mu.reg_read(UC_SPARC_REG_FP)
|
||||
print(" G0 = %d" % g0)
|
||||
print(" G1 = %d" % g1)
|
||||
print(" G2 = %d" % g2)
|
||||
print(" G3 = %d" % g3)
|
||||
print(" G4 = %d" % g4)
|
||||
print(" G5 = %d" % g5)
|
||||
print(" G6 = %d" % g6)
|
||||
print(" G7 = %d" % g7)
|
||||
print("")
|
||||
print(" O0 = %d" % o0)
|
||||
print(" O1 = %d" % o1)
|
||||
print(" O2 = %d" % o2)
|
||||
print(" O3 = %d" % o3)
|
||||
print(" O4 = %d" % o4)
|
||||
print(" O5 = %d" % o5)
|
||||
print(" O6 = %d" % o6)
|
||||
print(" O7 = %d" % o7)
|
||||
print("")
|
||||
print(" L0 = %d" % l0)
|
||||
print(" L1 = %d" % l1)
|
||||
print(" L2 = %d" % l2)
|
||||
print(" L3 = %d" % l3)
|
||||
print(" L4 = %d" % l4)
|
||||
print(" L5 = %d" % l5)
|
||||
print(" L6 = %d" % l6)
|
||||
print(" L7 = %d" % l7)
|
||||
print("")
|
||||
print(" I0 = %d" % i0)
|
||||
print(" I1 = %d" % i1)
|
||||
print(" I2 = %d" % i2)
|
||||
print(" I3 = %d" % i3)
|
||||
print(" I4 = %d" % i4)
|
||||
print(" I5 = %d" % i5)
|
||||
print(" I6 = %d" % i6)
|
||||
print(" I7 = %d" % i7)
|
||||
print("")
|
||||
print(" PC = %d" % pc)
|
||||
print(" SP = %d" % sp)
|
||||
print(" FP = %d" % fp)
|
||||
print("")
|
||||
|
||||
print_registers(uc)
|
||||
|
||||
assert uc.reg_read(UC_SPARC_REG_PC) == 128 # make sure we executed all instructions
|
||||
assert uc.reg_read(UC_SPARC_REG_SP) == 101
|
||||
assert uc.reg_read(UC_SPARC_REG_FP) == 201
|
||||
|
||||
assert uc.reg_read(UC_SPARC_REG_G0) == 0 # G0 is always zero
|
||||
assert uc.reg_read(UC_SPARC_REG_G1) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_G2) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_G3) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_G4) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_G5) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_G6) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_G7) == 1
|
||||
|
||||
assert uc.reg_read(UC_SPARC_REG_O0) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_O1) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_O2) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_O3) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_O4) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_O5) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_O6) == 101
|
||||
assert uc.reg_read(UC_SPARC_REG_O7) == 1
|
||||
|
||||
assert uc.reg_read(UC_SPARC_REG_L0) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_L1) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_L2) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_L3) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_L4) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_L5) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_L6) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_L7) == 1
|
||||
|
||||
assert uc.reg_read(UC_SPARC_REG_I0) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_I1) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_I2) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_I3) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_I4) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_I5) == 1
|
||||
assert uc.reg_read(UC_SPARC_REG_I6) == 201
|
||||
assert uc.reg_read(UC_SPARC_REG_I7) == 1
|
149
tests/regress/timeout_segfault.c
Normal file
149
tests/regress/timeout_segfault.c
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
timeout_segfault.c
|
||||
|
||||
This program shows a case where the emulation timer keeps running after
|
||||
emulation has ended. It triggers an intermittent segfault when _timeout_fn()
|
||||
tries to call uc_emu_stop() after emulation has already been cleaned up. This
|
||||
code is the same as samples/sample_arm.c, except that it adds a timeout on each
|
||||
call to uc_emu_start(). See issue #78 for more details:
|
||||
https://github.com/unicorn-engine/unicorn/issues/78
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
// number of seconds to wait before timeout
|
||||
#define TIMEOUT 5
|
||||
|
||||
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, UC_SECOND_SCALE * TIMEOUT, 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, UC_SECOND_SCALE * TIMEOUT, 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)
|
||||
{
|
||||
test_arm();
|
||||
printf("==========================\n");
|
||||
test_thumb();
|
||||
|
||||
return 0;
|
||||
}
|
71
tests/regress/wrong_rip.py
Executable file
71
tests/regress/wrong_rip.py
Executable file
@ -0,0 +1,71 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from unicorn import *
|
||||
from unicorn.x86_const import *
|
||||
|
||||
import regress
|
||||
|
||||
binary1 = b'\xb8\x02\x00\x00\x00' # mov eax, 2
|
||||
binary2 = b'\xb8\x01\x00\x00\x00' # mov eax, 1
|
||||
|
||||
class WrongRIP(regress.RegressTest):
|
||||
|
||||
def test_step(self):
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_64)
|
||||
mu.mem_map(0, 2 * 1024 * 1024)
|
||||
# write machine code to be emulated to memory
|
||||
mu.mem_write(0, binary1 + binary2)
|
||||
# emu for maximum 1 instruction.
|
||||
mu.emu_start(0, 5, 0, 1)
|
||||
|
||||
self.assertEqual(0x2, mu.reg_read(UC_X86_REG_RAX))
|
||||
self.assertEqual(0x5, mu.reg_read(UC_X86_REG_RIP))
|
||||
|
||||
mu.emu_start(5, 10, 0, 1)
|
||||
self.assertEqual(0xa, mu.reg_read(UC_X86_REG_RIP))
|
||||
self.assertEqual(0x1, mu.reg_read(UC_X86_REG_RAX))
|
||||
|
||||
def test_step2(self):
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_64)
|
||||
mu.mem_map(0, 2 * 1024 * 1024)
|
||||
# write machine code to be emulated to memory
|
||||
mu.mem_write(0, binary1 + binary2)
|
||||
# emu for maximum 1 instruction.
|
||||
mu.emu_start(0, 10, 0, 1)
|
||||
self.assertEqual(0x2, mu.reg_read(UC_X86_REG_RAX))
|
||||
self.assertEqual(0x5, mu.reg_read(UC_X86_REG_RIP))
|
||||
|
||||
mu.emu_start(5, 10, 0, 1)
|
||||
self.assertEqual(0x1, mu.reg_read(UC_X86_REG_RAX))
|
||||
self.assertEqual(0xa, mu.reg_read(UC_X86_REG_RIP))
|
||||
|
||||
def test_step3(self):
|
||||
bin3 = b'\x40\x01\xc1\x31\xf6' # inc eax; add ecx, eax; xor esi, esi
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_32)
|
||||
mu.mem_map(0, 2 * 1024 * 1024)
|
||||
# write machine code to be emulated to memory
|
||||
mu.mem_write(0, bin3)
|
||||
# emu for maximum 1 instruction.
|
||||
mu.emu_start(0, 10, 0, 1)
|
||||
self.assertEqual(0x1, mu.reg_read(UC_X86_REG_EAX))
|
||||
self.assertEqual(0x1, mu.reg_read(UC_X86_REG_EIP))
|
||||
|
||||
def test_step_then_fin(self):
|
||||
bin4 = b'\x40\x01\xc1\x31\xf6\x90\x90\x90' # inc eax; add ecx, eax; xor esi, esi
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_32)
|
||||
mu.mem_map(0, 2 * 1024 * 1024)
|
||||
# write machine code to be emulated to memory
|
||||
mu.mem_write(0, bin4)
|
||||
# emu for maximum 1 instruction.
|
||||
mu.emu_start(0, len(binary1), 0, 1)
|
||||
|
||||
self.assertEqual(0x1, mu.reg_read(UC_X86_REG_EAX))
|
||||
self.assertEqual(0x1, mu.reg_read(UC_X86_REG_EIP))
|
||||
# emu to the end
|
||||
mu.emu_start(1, len(bin4))
|
||||
self.assertEqual(0x1, mu.reg_read(UC_X86_REG_EAX))
|
||||
self.assertEqual(len(bin4), mu.reg_read(UC_X86_REG_EIP))
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
||||
|
38
tests/regress/wrong_rip_arm.py
Executable file
38
tests/regress/wrong_rip_arm.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from unicorn import *
|
||||
from unicorn.x86_const import *
|
||||
from unicorn.arm_const import *
|
||||
|
||||
import regress
|
||||
|
||||
# adds r1, #0x48
|
||||
# ldrsb r7, [r7, r7]
|
||||
# ldrsh r7, [r2, r1]
|
||||
# ldr r0, [pc, #0x168]
|
||||
# cmp r7, #0xbf
|
||||
# str r7, [r5, #0x20]
|
||||
# ldr r1, [r5, #0x64]
|
||||
# strb r7, [r5, #0xc]
|
||||
# ldr r0, [pc, #0x1a0]
|
||||
binary1 = b'\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05'
|
||||
# binary1 = b'\x48\x31\xff\x57'
|
||||
#adds r1, #0x48
|
||||
#ldrsb r7, [r7, r7]
|
||||
|
||||
class WrongRIPArm(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
|
||||
mu.mem_map(0, 2 * 1024 * 1024)
|
||||
# write machine code to be emulated to memory
|
||||
mu.mem_write(0, binary1)
|
||||
mu.reg_write(UC_ARM_REG_R13, 1 * 1024 * 1024)
|
||||
# emu for maximum 1 instruction.
|
||||
mu.emu_start(0, len(binary1), 0, 1)
|
||||
self.assertEqual(0x48, mu.reg_read(UC_ARM_REG_R1))
|
||||
pos = mu.reg_read(UC_ARM_REG_R15)
|
||||
self.assertEqual(0x2, pos)
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
28
tests/regress/wrong_sp_arm.py
Executable file
28
tests/regress/wrong_sp_arm.py
Executable file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/python
|
||||
# By Ryan Hileman, issue #16
|
||||
|
||||
from unicorn import *
|
||||
from unicorn.arm_const import *
|
||||
from unicorn.arm64_const import *
|
||||
|
||||
import regress
|
||||
|
||||
class WrongSPArm(regress.RegressTest):
|
||||
|
||||
def test_32(self):
|
||||
with self.assertRaises(UcError):
|
||||
uc = Uc(UC_ARCH_ARM, UC_MODE_32)
|
||||
uc.reg_write(UC_ARM_REG_SP, 4)
|
||||
|
||||
def test_64(self):
|
||||
uc = Uc(UC_ARCH_ARM64, UC_MODE_ARM)
|
||||
uc.reg_write(UC_ARM64_REG_SP, 4)
|
||||
self.assertEqual(0x4, uc.reg_read(UC_ARM64_REG_SP))
|
||||
|
||||
def test_arm(self):
|
||||
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
|
||||
uc.reg_write(UC_ARM_REG_SP, 4)
|
||||
self.assertEqual(0x4, uc.reg_read(UC_ARM_REG_SP))
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
3
tests/unit/.gitignore
vendored
Normal file
3
tests/unit/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
test_x86
|
||||
test_mem_map
|
||||
test_sanity
|
31
tests/unit/Makefile
Normal file
31
tests/unit/Makefile
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
CFLAGS += -Wall -Werror -Wno-unused-function -g
|
||||
CFLAGS += -L ../../
|
||||
CFLAGS += -lcmocka -lunicorn
|
||||
CFLAGS += -I ../../include
|
||||
|
||||
ALL_TESTS = test_sanity test_x86 test_mem_map
|
||||
|
||||
.PHONY: all
|
||||
all: ${ALL_TESTS}
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm ${ALL_TESTS}
|
||||
|
||||
.PHONY: test
|
||||
test: export LD_LIBRARY_PATH=../../
|
||||
test: ${ALL_TESTS}
|
||||
./test_sanity
|
||||
./test_x86
|
||||
./test_mem_map
|
||||
|
||||
test_sanity: test_sanity.c
|
||||
test_x86: test_x86.c
|
||||
test_mem_map: test_mem_map.c
|
||||
|
||||
${ALL_TESTS}:
|
||||
gcc ${CFLAGS} -o $@ $^
|
||||
|
||||
|
||||
|
143
tests/unit/test_mem_map.c
Normal file
143
tests/unit/test_mem_map.c
Normal file
@ -0,0 +1,143 @@
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
|
||||
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_unmap),
|
||||
test(test_rw_across_boundaries),
|
||||
};
|
||||
#undef test
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
88
tests/unit/test_sanity.c
Normal file
88
tests/unit/test_sanity.c
Normal file
@ -0,0 +1,88 @@
|
||||
#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);
|
||||
}
|
750
tests/unit/test_x86.c
Normal file
750
tests/unit/test_x86.c
Normal file
@ -0,0 +1,750 @@
|
||||
#include "unicorn_test.h"
|
||||
#include <inttypes.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++];
|
||||
|
||||
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;
|
||||
|
||||
#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, (uint64_t)1, (uint64_t)0));
|
||||
|
||||
OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0));
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
static void test_i386(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uint32_t tmp;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
const uint8_t code[] = "\x41\x4a"; // INC ecx; DEC edx
|
||||
const uint64_t address = 0x1000000;
|
||||
|
||||
int r_ecx = 0x1234; // ECX register
|
||||
int r_edx = 0x7890; // EDX register
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
err = uc_mem_write(uc, address, code, sizeof(code)-1);
|
||||
uc_assert_success(err);
|
||||
|
||||
// initialize machine registers
|
||||
err = uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx);
|
||||
uc_assert_success(err);
|
||||
err = uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
|
||||
uc_assert_success(err);
|
||||
|
||||
// tracing all basic blocks with customized callback
|
||||
err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
|
||||
uc_assert_success(err);
|
||||
|
||||
// tracing all instruction by having @begin > @end
|
||||
err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
|
||||
uc_assert_success(err);
|
||||
|
||||
// emulate machine code in infinite time
|
||||
err = uc_emu_start(uc, address, address+sizeof(code)-1, 0, 0);
|
||||
uc_assert_success(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);
|
||||
|
||||
assert_int_equal(r_ecx, 0x1235);
|
||||
assert_int_equal(r_edx, 0x788F);
|
||||
|
||||
// read from memory
|
||||
err = uc_mem_read(uc, address, (uint8_t *)&tmp, 4);
|
||||
uc_assert_success(err);
|
||||
//printf(">>> Read 4 bytes from [0x%"PRIX64"] = 0x%x\n", address, tmp);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
static void test_i386_jump(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
const uint8_t code[] = "\xeb\x02\x90\x90\x90\x90\x90\x90"; // jmp 4; nop; nop; nop; nop; nop; nop
|
||||
const uint64_t address = 0x1000000;
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
err = uc_mem_write(uc, address, code, sizeof(code)-1);
|
||||
uc_assert_success(err);
|
||||
|
||||
// tracing 1 basic block with customized callback
|
||||
err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)address, (uint64_t)address);
|
||||
uc_assert_success(err);
|
||||
|
||||
// tracing 1 instruction at address
|
||||
err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)address, (uint64_t)address);
|
||||
uc_assert_success(err);
|
||||
|
||||
// emulate machine code in infinite time
|
||||
err = uc_emu_start(uc, address, address+sizeof(code)-1, 0, 0);
|
||||
uc_assert_success(err);
|
||||
|
||||
err = uc_close(uc);
|
||||
uc_assert_success(err);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// callback for IN instruction (X86).
|
||||
// this returns the data read from the port
|
||||
static uint32_t hook_in(uc_engine *uc, uint32_t port, int size, void *user_data)
|
||||
{
|
||||
uint32_t eip;
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_EIP, &eip);
|
||||
|
||||
//printf("--- reading from port 0x%x, size: %u, address: 0x%x\n", port, size, eip);
|
||||
|
||||
switch(size) {
|
||||
default:
|
||||
return 0; // should never reach this
|
||||
case 1:
|
||||
// read 1 byte to AL
|
||||
return 0xf1;
|
||||
case 2:
|
||||
// read 2 byte to AX
|
||||
return 0xf2;
|
||||
case 4:
|
||||
// read 4 byte to EAX
|
||||
return 0xf4;
|
||||
}
|
||||
}
|
||||
|
||||
// callback for OUT instruction (X86).
|
||||
static void hook_out(uc_engine *uc, uint32_t port, int size, uint32_t value, void *user_data)
|
||||
{
|
||||
uint32_t tmp;
|
||||
uint32_t eip;
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_EIP, &eip);
|
||||
|
||||
//printf("--- writing to port 0x%x, size: %u, value: 0x%x, address: 0x%x\n", port, size, value, eip);
|
||||
|
||||
// TODO: confirm that value is indeed the value of AL/AX/EAX
|
||||
switch(size) {
|
||||
default:
|
||||
return; // should never reach this
|
||||
case 1:
|
||||
uc_reg_read(uc, UC_X86_REG_AL, &tmp);
|
||||
break;
|
||||
case 2:
|
||||
uc_reg_read(uc, UC_X86_REG_AX, &tmp);
|
||||
break;
|
||||
case 4:
|
||||
uc_reg_read(uc, UC_X86_REG_EAX, &tmp);
|
||||
break;
|
||||
}
|
||||
|
||||
//printf("--- register value = 0x%x\n", tmp);
|
||||
}
|
||||
|
||||
static void test_i386_inout(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uc_hook trace1, trace2, trace3, trace4;
|
||||
|
||||
int r_eax = 0x1234; // EAX register
|
||||
int r_ecx = 0x6789; // ECX register
|
||||
|
||||
static const uint64_t address = 0x1000000;
|
||||
static const uint8_t code[] = {
|
||||
0x41, // inc ecx
|
||||
0xE4, 0x3F, // in al, 0x3F
|
||||
0x4A, // dec edx
|
||||
0xE6, 0x46, // out 0x46, al
|
||||
0x43, // inc ebx
|
||||
};
|
||||
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
err = uc_mem_write(uc, address, code, sizeof(code));
|
||||
uc_assert_success(err);
|
||||
|
||||
// initialize machine registers
|
||||
err = uc_reg_write(uc, UC_X86_REG_EAX, &r_eax);
|
||||
uc_assert_success(err);
|
||||
err = uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx);
|
||||
uc_assert_success(err);
|
||||
|
||||
// tracing all basic blocks with customized callback
|
||||
err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
|
||||
uc_assert_success(err);
|
||||
|
||||
// tracing all instructions
|
||||
err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
|
||||
uc_assert_success(err);
|
||||
|
||||
// uc IN instruction
|
||||
err = uc_hook_add(uc, &trace3, UC_HOOK_INSN, hook_in, NULL, UC_X86_INS_IN);
|
||||
uc_assert_success(err);
|
||||
|
||||
// uc OUT instruction
|
||||
err = uc_hook_add(uc, &trace4, UC_HOOK_INSN, hook_out, NULL, UC_X86_INS_OUT);
|
||||
uc_assert_success(err);
|
||||
|
||||
// emulate machine code in infinite time
|
||||
err = uc_emu_start(uc, address, address+sizeof(code), 0, 0);
|
||||
uc_assert_success(err);
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_EAX, &r_eax);
|
||||
uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx);
|
||||
//printf(">>> EAX = 0x%x\n", r_eax);
|
||||
//printf(">>> ECX = 0x%x\n", r_ecx);
|
||||
// TODO: Assert on the register values here
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// emulate code that loop forever
|
||||
static void test_i386_loop(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
|
||||
int r_ecx = 0x1234; // ECX register
|
||||
int r_edx = 0x7890; // EDX register
|
||||
|
||||
static const uint64_t address = 0x1000000;
|
||||
static const uint8_t code[] = {
|
||||
0x41, // inc ecx
|
||||
0x4a, // dec edx
|
||||
0xEB, 0xFE, // jmp $
|
||||
};
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
err = uc_mem_write(uc, address, code, sizeof(code));
|
||||
uc_assert_success(err);
|
||||
|
||||
// initialize machine registers
|
||||
err = uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx);
|
||||
uc_assert_success(err);
|
||||
err = uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
|
||||
uc_assert_success(err);
|
||||
|
||||
// emulate machine code in 2 seconds, so we can quit even
|
||||
// if the code loops
|
||||
err = uc_emu_start(uc, address, address+sizeof(code), 2*UC_SECOND_SCALE, 0);
|
||||
uc_assert_success(err);
|
||||
|
||||
// verify register values
|
||||
uc_assert_success(uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx));
|
||||
uc_assert_success(uc_reg_read(uc, UC_X86_REG_EDX, &r_edx));
|
||||
|
||||
assert_int_equal(r_ecx, 0x1235);
|
||||
assert_int_equal(r_edx, 0x788F);
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// emulate code that reads invalid memory
|
||||
static void test_i386_invalid_mem_read(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
|
||||
static const uint64_t address = 0x1000000;
|
||||
static const uint8_t code[] = {
|
||||
0x8b, 0x0D, 0xAA, 0xAA, 0xAA, 0xAA, // mov ecx, [0xAAAAAAAA]
|
||||
};
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
err = uc_mem_write(uc, address, code, sizeof(code));
|
||||
uc_assert_success(err);
|
||||
|
||||
// emulate machine code in infinite time
|
||||
err = uc_emu_start(uc, address, address+sizeof(code), 0, 0);
|
||||
uc_assert_err(UC_ERR_READ_INVALID, err);
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
}
|
||||
|
||||
// emulate code that writes invalid memory
|
||||
static void test_i386_invalid_mem_write(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
|
||||
static const uint64_t address = 0x1000000;
|
||||
static const uint8_t code[] = {
|
||||
0x89, 0x0D, 0xAA, 0xAA, 0xAA, 0xAA, // mov [0xAAAAAAAA], ecx
|
||||
};
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
err = uc_mem_write(uc, address, code, sizeof(code));
|
||||
uc_assert_success(err);
|
||||
|
||||
// emulate machine code in infinite time
|
||||
err = uc_emu_start(uc, address, address+sizeof(code), 0, 0);
|
||||
uc_assert_err(UC_ERR_WRITE_INVALID, err);
|
||||
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
}
|
||||
|
||||
// emulate code that jumps to invalid memory
|
||||
static void test_i386_jump_invalid(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
|
||||
static const uint64_t address = 0x1000000;
|
||||
static const uint8_t code[] = {
|
||||
0xE9, 0xE9, 0xEE, 0xEE, 0xEE, // jmp 0xEEEEEEEE
|
||||
};
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
err = uc_mem_write(uc, address, code, sizeof(code));
|
||||
uc_assert_success(err);
|
||||
|
||||
// emulate machine code in infinite time
|
||||
err = uc_emu_start(uc, address, address+sizeof(code), 0, 0);
|
||||
uc_assert_err(UC_ERR_CODE_INVALID, err);
|
||||
|
||||
|
||||
uc_assert_success(uc_close(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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 test_x86_64(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uc_hook trace1, trace2, trace3, trace4;
|
||||
|
||||
static const uint64_t address = 0x1000000;
|
||||
static const uint8_t code[] = "\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";
|
||||
|
||||
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;
|
||||
|
||||
|
||||
// Initialize emulator in X86-64bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
err = uc_mem_write(uc, address, code, sizeof(code) - 1);
|
||||
uc_assert_success(err);
|
||||
|
||||
// initialize machine registers
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_RSP, &rsp));
|
||||
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_RAX, &rax));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_RBX, &rbx));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_RCX, &rcx));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_RDX, &rdx));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_RSI, &rsi));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_RDI, &rdi));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_R8, &r8));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_R9, &r9));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_R10, &r10));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_R11, &r11));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_R12, &r12));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_R13, &r13));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_R14, &r14));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_R15, &r15));
|
||||
|
||||
// tracing all basic blocks with customized callback
|
||||
err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
|
||||
uc_assert_success(err);
|
||||
|
||||
// tracing all instructions in the range [address, address+20]
|
||||
err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code64, NULL, (uint64_t)address, (uint64_t)(address+20));
|
||||
uc_assert_success(err);
|
||||
|
||||
// tracing all memory WRITE access (with @begin > @end)
|
||||
err = uc_hook_add(uc, &trace3, UC_HOOK_MEM_WRITE, hook_mem64, NULL, (uint64_t)1, (uint64_t)0);
|
||||
uc_assert_success(err);
|
||||
|
||||
// tracing all memory READ access (with @begin > @end)
|
||||
err = uc_hook_add(uc, &trace4, UC_HOOK_MEM_READ, hook_mem64, NULL, (uint64_t)1, (uint64_t)0);
|
||||
uc_assert_success(err);
|
||||
|
||||
// emulate machine code in infinite time (last param = 0), or when
|
||||
// finishing all the code.
|
||||
err = uc_emu_start(uc, address, address+sizeof(code) - 1, 0, 0);
|
||||
uc_assert_success(err);
|
||||
|
||||
// Read registers
|
||||
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);
|
||||
|
||||
#if 0
|
||||
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);
|
||||
#endif
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// callback for SYSCALL instruction (X86).
|
||||
static void hook_syscall(uc_engine *uc, void *user_data)
|
||||
{
|
||||
uint64_t rax;
|
||||
|
||||
uc_assert_success(uc_reg_read(uc, UC_X86_REG_RAX, &rax));
|
||||
assert_int_equal(0x100, rax);
|
||||
|
||||
rax = 0x200;
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_RAX, &rax));
|
||||
}
|
||||
|
||||
static void test_x86_64_syscall(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1;
|
||||
uc_err err;
|
||||
|
||||
static const uint64_t address = 0x1000000;
|
||||
static const uint8_t code[] = {
|
||||
0x0F, 0x05, // SYSCALL
|
||||
};
|
||||
|
||||
int64_t rax = 0x100;
|
||||
|
||||
// Initialize emulator in X86-64bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
err = uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
err = uc_mem_write(uc, address, code, sizeof(code));
|
||||
uc_assert_success(err);
|
||||
|
||||
// hook interrupts for syscall
|
||||
err = uc_hook_add(uc, &trace1, UC_HOOK_INSN, hook_syscall, NULL, UC_X86_INS_SYSCALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
// initialize machine registers
|
||||
err = uc_reg_write(uc, UC_X86_REG_RAX, &rax);
|
||||
uc_assert_success(err);
|
||||
|
||||
// emulate machine code in infinite time (last param = 0), or when
|
||||
// finishing all the code.
|
||||
err = uc_emu_start(uc, address, address + sizeof(code), 0, 0);
|
||||
uc_assert_success(err);
|
||||
|
||||
// verify register values
|
||||
uc_assert_success(uc_reg_read(uc, UC_X86_REG_RAX, &rax));
|
||||
assert_int_equal(0x200, rax);
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void test_x86_16(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uint8_t tmp;
|
||||
|
||||
static const uint64_t address = 0;
|
||||
static const uint8_t code[] = {
|
||||
0x00, 0x00, // add byte ptr [bx + si], al
|
||||
};
|
||||
|
||||
int32_t eax = 7;
|
||||
int32_t ebx = 5;
|
||||
int32_t esi = 6;
|
||||
|
||||
// Initialize emulator in X86-16bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_16, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
// map 8KB memory for this emulation
|
||||
err = uc_mem_map(uc, address, 8 * 1024, UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
err = uc_mem_write(uc, address, code, sizeof(code));
|
||||
uc_assert_success(err);
|
||||
|
||||
// initialize machine registers
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EAX, &eax));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EBX, &ebx));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_ESI, &esi));
|
||||
|
||||
// emulate machine code in infinite time (last param = 0), or when
|
||||
// finishing all the code.
|
||||
err = uc_emu_start(uc, address, address+sizeof(code), 0, 0);
|
||||
uc_assert_success(err);
|
||||
|
||||
// read from memory
|
||||
uc_assert_success(uc_mem_read(uc, 11, &tmp, 1));
|
||||
assert_int_equal(7, tmp);
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int main(void) {
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(test_i386),
|
||||
cmocka_unit_test(test_i386_jump),
|
||||
cmocka_unit_test(test_i386_inout),
|
||||
cmocka_unit_test(test_i386_loop),
|
||||
cmocka_unit_test(test_i386_invalid_mem_read),
|
||||
cmocka_unit_test(test_i386_invalid_mem_write),
|
||||
cmocka_unit_test(test_i386_jump_invalid),
|
||||
|
||||
cmocka_unit_test(test_x86_64),
|
||||
cmocka_unit_test(test_x86_64_syscall),
|
||||
|
||||
cmocka_unit_test(test_x86_16),
|
||||
|
||||
cmocka_unit_test_setup_teardown(test_basic_blocks, setup32, teardown),
|
||||
};
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
42
tests/unit/unicorn_test.h
Normal file
42
tests/unit/unicorn_test.h
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef UNICORN_TEST_H
|
||||
#define UNICORN_TEST_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
/**
|
||||
* Assert that err matches expect
|
||||
*/
|
||||
#define uc_assert_err(expect, err) \
|
||||
do { \
|
||||
uc_err __err = err; \
|
||||
if (__err != expect) { \
|
||||
fail_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)
|
||||
|
||||
|
||||
#endif /* UNICORN_TEST_H */
|
Reference in New Issue
Block a user