Start moving examples in S files (#851)

* Move assembly to S files

* more assembly files

* osx compilation change

* makefile mistake

* add objcopy from crosstool

* use gobjcopy on osx

* start cmocka install cleanup

* move wget to directory option

* move back to cd

* fix copy

* First cut

* free allocated memory

* bad idea

too much switching between python and c

* add debug

* cleanup bad size
This commit is contained in:
Stephen 2017-06-24 19:14:22 -07:00 committed by Nguyen Anh Quynh
parent 7f116846c0
commit da21bd0589
14 changed files with 217 additions and 85 deletions

View file

@ -1,8 +1,7 @@
language: c language: c
sudo: false sudo: false
before_install: before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install cmocka crosstool-ng; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install cmocka; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./install-cmocka-linux.sh; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./install-cmocka-linux.sh; fi
script: script:
- make && make -C bindings/go && make -C bindings/go test && make test - make && make -C bindings/go && make -C bindings/go test && make test

View file

@ -1,13 +1,12 @@
#!/bin/sh #!/bin/sh -ex
set -ex
mkdir -p cmocka mkdir -p cmocka
#wget https://cmocka.org/files/1.1/cmocka-1.1.0.tar.xz -O /tmp/cmocka-1.1.0.tar.xz #wget https://cmocka.org/files/1.1/cmocka-1.1.0.tar.xz -O /tmp/cmocka-1.1.0.tar.xz
wget --no-check-certificate http://cmocka.org/files/1.1/cmocka-1.1.0.tar.xz -O /tmp/cmocka-1.1.0.tar.xz wget --no-check-certificate https://cmocka.org/files/1.1/cmocka-1.1.1.tar.xz -P /tmp/
tar -xf /tmp/cmocka-1.1.0.tar.xz -C /tmp tar -xf /tmp/cmocka-1.1.1.tar.xz -C /tmp
cd cmocka && cmake -DUNIT_TESTING=On /tmp/cmocka-1.1.0 && make && make test cd cmocka && cmake -DUNIT_TESTING=On /tmp/cmocka-1.1.1 && make && make test
# cmake builds an so instead of a dll in mingw/msys # cmake builds an so instead of a dll in mingw/msys
if [[ ! -z $MSYSTEM ]]; then if [[ ! -z $MSYSTEM ]]; then
cp src/cmocka.so src/cmocka.dll cp src/cmocka.so src/cmocka.dll
fi fi
# cmocka does not include headers in build # cmocka does not include headers in build
cp -R /tmp/cmocka-1.1.0/include/ . cp -R /tmp/cmocka-1.1.1/include/ .

View file

@ -1,2 +1,3 @@
!*.c !*.c
test_* test_*
*.bin

View file

@ -2,10 +2,15 @@ CFLAGS += -Wall -Werror -Wno-unused-function -g
CFLAGS += -D__USE_MINGW_ANSI_STDIO=1 CFLAGS += -D__USE_MINGW_ANSI_STDIO=1
CFLAGS += -L ../../ -I ../../include CFLAGS += -L ../../ -I ../../include
CFLAGS += -L ../../cmocka/src -I ../../cmocka/include CFLAGS += -L ../../cmocka/src -I ../../cmocka/include
ASFLAGS += --32
OBJCOPY = objcopy
UNAME_S := $(shell uname -s) UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Linux) ifeq ($(UNAME_S), Linux)
LDLIBS += -lrt -pthread LDLIBS += -lrt -pthread
else ifeq ($(UNAME_S), Darwin)
OBJCOPY = gobjcopy
ASFLAGS = -arch i386
endif endif
LDLIBS += -lcmocka -lunicorn LDLIBS += -lcmocka -lunicorn
@ -20,17 +25,24 @@ LDFLAGS := -fsanitize=address ${LDFLAGS}
endif endif
ALL_TESTS_SOURCES = $(wildcard *.c) ALL_TESTS_SOURCES = $(wildcard *.c)
TEST_ASSEMBLY = $(wildcard *.s)
TEST_PROGS = $(TEST_ASSEMBLY:%.s=%.o)
TEST_BINS = $(TEST_PROGS:%.o=%.bin)
ALL_TESTS = $(ALL_TESTS_SOURCES:%.c=%) ALL_TESTS = $(ALL_TESTS_SOURCES:%.c=%)
.PHONY: all .PHONY: all
all: ${ALL_TESTS} all: ${TEST_BINS} ${ALL_TESTS}
.PHONY: clean .PHONY: clean
clean: clean:
rm -rf ${ALL_TESTS} rm -rf ${TEST_BINS} ${ALL_TESTS}
%.bin: %.o
${OBJCOPY} -O binary $^ $@
hexdump -C $@
.PHONY: test .PHONY: test
test: ${ALL_TESTS} test: all
${EXECUTE_VARS} ./test_sanity ${EXECUTE_VARS} ./test_sanity
${EXECUTE_VARS} ./test_x86 ${EXECUTE_VARS} ./test_x86
${EXECUTE_VARS} ./test_mem_map ${EXECUTE_VARS} ./test_mem_map

3
tests/unit/gdt_idx.s Normal file
View file

@ -0,0 +1,3 @@
.text
sidt (esp)
sgdt (esp+6)

View file

@ -0,0 +1,6 @@
dec %eax
mov (%eax), %eax
nop
nop
nop
nop

9
tests/unit/pc_change.s Normal file
View file

@ -0,0 +1,9 @@
.text
inc %ecx
inc %ecx
inc %ecx
inc %ecx
inc %ecx
inc %ecx
inc %edx
inc %edx

90
tests/unit/tb_x86.s Normal file
View file

@ -0,0 +1,90 @@
mov %esp,%ecx
fxch %st(5)
fnstenv -0xc(%ecx)
pop %ebp
push %ebp
pop %ecx
dec %ecx
dec %ecx
dec %ecx
dec %ecx
dec %ecx
dec %ecx
dec %ecx
dec %ecx
dec %ecx
dec %ecx
inc %ebx
inc %ebx
inc %ebx
inc %ebx
inc %ebx
inc %ebx
aaa
push %ecx
pop %edx
push $0x41
pop %eax
push %eax
xor %al,0x30(%ecx)
inc %ecx
imul $0x51,0x41(%ecx),%eax
xor 0x42(%ecx),%al
xor 0x42(%edx),%al
xor %al,0x42(%edx)
inc %ecx
inc %edx
pop %eax
push %eax
cmp %al,0x42(%ecx)
jne .+0x4c
dec %ecx
push %ecx
push %ecx
push %ecx
push %edx
inc %edi
xor 0x34(%edi),%eax
push %ecx
push %ebp
push %ecx
push %esi
push %eax
inc %edi
inc %edi
cmp %al,0x39(%edi)
push %eax
dec %edx
push %eax
dec %ebx
push %eax
dec %esp
push %eax
dec %ebp
push %eax
dec %esi
push %eax
dec %edi
push %eax
push %eax
push %eax
xor %eax, 0x42(%edi)
inc %edi
inc %edx
push %eax
xor $0x50,%al
pop %edx
push %eax
inc %ebp
push %ecx
push %edx
inc %esi
xor 0x31(%edi),%al
push %eax
dec %ebp
push %ecx
push %ecx
push %eax
dec %esi
inc %ecx
inc %ecx

View file

@ -1,45 +1,9 @@
#include "unicorn_test.h"
#include <unicorn/unicorn.h> #include <unicorn/unicorn.h>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
/**
* Assert that err matches expect
*/
#define uc_assert_err(expect, err) \
do { \
uc_err __err = err; \
if (__err != expect) { \
fprintf(stderr, "%s", uc_strerror(__err)); \
exit(1); \
} \
} 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) { \
fprintf(stderr, "%s", uc_strerror(__err)); \
exit(1); \
} \
} while (0)
#define OK(x) uc_assert_success(x)
/******************************************************************************/
static void test_idt_gdt_i386(/*void **state*/) static void test_idt_gdt_i386(/*void **state*/)
{ {
uc_engine *uc; uc_engine *uc;
@ -50,7 +14,9 @@ static void test_idt_gdt_i386(/*void **state*/)
uc_x86_mmr ldt; uc_x86_mmr ldt;
uc_x86_mmr tr; uc_x86_mmr tr;
const uint8_t code[] = "\x0f\x01\x0c\x24\x0f\x01\x44\x24\x06"; // sidt [esp]; sgdt [esp+6] struct stat info;
char * code = read_file("gdt_idx.bin", &info);
const uint64_t address = 0x1000000; const uint64_t address = 0x1000000;
int r_esp = address + 0x1000 - 0x100; // initial esp int r_esp = address + 0x1000 - 0x100; // initial esp
@ -79,7 +45,7 @@ static void test_idt_gdt_i386(/*void **state*/)
uc_assert_success(err); uc_assert_success(err);
// write machine code to be emulated to memory // write machine code to be emulated to memory
err = uc_mem_write(uc, address, code, sizeof(code)-1); err = uc_mem_write(uc, address, code, info.st_size);
uc_assert_success(err); uc_assert_success(err);
// initialize machine registers // initialize machine registers
@ -141,7 +107,7 @@ static void test_idt_gdt_i386(/*void **state*/)
assert(memcmp(buf, "\xba\xdc\x21\x43\x65\x87", 6) == 0); assert(memcmp(buf, "\xba\xdc\x21\x43\x65\x87", 6) == 0);
uc_close(uc); uc_close(uc);
free(code);
} }
/******************************************************************************/ /******************************************************************************/

View file

@ -90,6 +90,8 @@ static void test_high_address_reads(void **state)
static void test_high_address_read_values(void **state) static void test_high_address_read_values(void **state)
{ {
uc_engine *uc = *state; uc_engine *uc = *state;
struct stat info;
char * code = read_file("high_address.bin", &info);
uint64_t addr = 0x0010000000000001; uint64_t addr = 0x0010000000000001;
//addr = 0x000ffffffffffff8; // uncomment to fix wrong behaviour //addr = 0x000ffffffffffff8; // uncomment to fix wrong behaviour
@ -100,15 +102,16 @@ static void test_high_address_read_values(void **state)
uc_assert_success(uc_mem_write(uc, addr, content, 8)); uc_assert_success(uc_mem_write(uc, addr, content, 8));
uc_assert_success(uc_reg_write(uc, UC_X86_REG_RAX, &addr)); uc_assert_success(uc_reg_write(uc, UC_X86_REG_RAX, &addr));
const uint64_t base_addr = 0x40000; const uint64_t base_addr = 0x40000;
uint8_t code[] = {0x48,0x8b,0x00,0x90,0x90,0x90,0x90}; // mov rax, [rax], nops
uc_assert_success(uc_mem_map(uc, base_addr, 4096, UC_PROT_ALL)); uc_assert_success(uc_mem_map(uc, base_addr, 4096, UC_PROT_ALL));
uc_assert_success(uc_mem_write(uc, base_addr, code, 7)); uc_assert_success(uc_mem_write(uc, base_addr, code, info.st_size));
uc_assert_success(uc_emu_start(uc, base_addr, base_addr + 3, 0, 0)); uc_assert_success(uc_emu_start(uc, base_addr, base_addr + 3, 0, 0));
uint64_t rax = 0; uint64_t rax = 0;
uc_assert_success(uc_reg_read(uc, UC_X86_REG_RAX, &rax)); uc_assert_success(uc_reg_read(uc, UC_X86_REG_RAX, &rax));
if(rax != 0x4242424242424242) { if(rax != 0x4242424242424242) {
fail_msg("wrong memory read from code %"PRIx64, rax); fail_msg("wrong memory read from code %"PRIx64, rax);
} }
free(code);
} }

View file

@ -1,6 +1,7 @@
// Test PC change during the callback. by Nguyen Anh Quynh, 2016 // Test PC change during the callback. by Nguyen Anh Quynh, 2016
#include "unicorn_test.h" #include "unicorn_test.h"
#include "unicorn/unicorn.h" #include "unicorn/unicorn.h"
#include "sys/stat.h"
#define OK(x) uc_assert_success(x) #define OK(x) uc_assert_success(x)
@ -54,21 +55,12 @@ static void test_pc_change(void **state)
uc_engine *uc = *state; uc_engine *uc = *state;
uc_hook trace1; uc_hook trace1;
int32_t r_ecx = 3, r_edx = 15; int32_t r_ecx = 3, r_edx = 15;
struct stat info;
char *code = read_file("pc_change.bin", &info);
#define BASEADDR 0x1000000 #define BASEADDR 0x1000000
uint64_t address = BASEADDR; uint64_t address = BASEADDR;
const uint8_t code[] = {
0x41, // inc ECX @0x1000000
0x41, // inc ECX
0x41, // inc ECX
0x41, // inc ECX @0x1000003
0x41, // inc ECX
0x41, // inc ECX
0x42, // inc EDX @0x1000006
0x42, // inc EDX
};
#undef BASEADDR #undef BASEADDR
@ -76,7 +68,7 @@ static void test_pc_change(void **state)
OK(uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL)); OK(uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL));
// write machine code to be emulated to memory // write machine code to be emulated to memory
OK(uc_mem_write(uc, address, code, sizeof(code))); OK(uc_mem_write(uc, address, code, info.st_size));
uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx); uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx);
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx); uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
@ -85,7 +77,7 @@ static void test_pc_change(void **state)
// trace all instructions // trace all instructions
OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, test_code_hook, NULL, 1, 0)); OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, test_code_hook, NULL, 1, 0));
OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0)); OK(uc_emu_start(uc, address, address+info.st_size, 0, 0));
uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx); uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx);
uc_reg_read(uc, UC_X86_REG_EDX, &r_edx); uc_reg_read(uc, UC_X86_REG_EDX, &r_edx);
@ -93,6 +85,7 @@ static void test_pc_change(void **state)
printf("ECX = %u, EDX = %u\n", r_ecx, r_edx); printf("ECX = %u, EDX = %u\n", r_ecx, r_edx);
assert_int_equal(r_ecx, 6); assert_int_equal(r_ecx, 6);
assert_int_equal(r_edx, 17); assert_int_equal(r_edx, 17);
free(code);
} }
int main(void) int main(void)

View file

@ -8,6 +8,7 @@
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/stat.h>
#include "unicorn/unicorn.h" #include "unicorn/unicorn.h"
#define RIP_NEXT_TO_THE_SELFMODIFY_OPCODE (1) #define RIP_NEXT_TO_THE_SELFMODIFY_OPCODE (1)
@ -21,17 +22,6 @@
#define CODE_SPACE (2 * 1024 * 1024) #define CODE_SPACE (2 * 1024 * 1024)
#define PHY_STACK_REGION (0x60000000) #define PHY_STACK_REGION (0x60000000)
#define X86_CODE32_ALPHA_MIXED \
"\x89\xe1\xd9\xcd\xd9\x71\xf4\x5d\x55\x59\x49\x49\x49\x49\x49\x49" \
"\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58" \
"\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30" \
"\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x51\x51\x51\x52" \
"\x47\x33\x47\x34\x51\x55\x51\x56\x50\x47\x47\x38\x47\x39\x50\x4a" \
"\x50\x4b\x50\x4c\x50\x4d\x50\x4e\x50\x4f\x50\x50\x50\x31\x47\x42" \
"\x47\x42\x50\x34\x50\x5a\x50\x45\x51\x52\x46\x32\x47\x31\x50\x4d" \
"\x51\x51\x50\x4e\x41\x41"
/* Called before every test to set up a new instance */ /* Called before every test to set up a new instance */
static int setup(void **state) static int setup(void **state)
{ {
@ -57,12 +47,12 @@ static int teardown(void **state)
static void dump_stack_mem(uc_engine *uc) static void dump_stack_mem(uc_engine *uc, const struct stat info)
{ {
uint8_t tmp[256]; uint8_t tmp[256];
uint32_t size; uint32_t size;
size = sizeof(X86_CODE32_ALPHA_MIXED); size = sizeof(info.st_size);
if (size > 255) size = 255; if (size > 255) size = 255;
if (!uc_mem_read(uc, PHY_STACK_REGION, tmp, size)) if (!uc_mem_read(uc, PHY_STACK_REGION, tmp, size))
{ {
@ -106,7 +96,8 @@ static void print_registers(uc_engine *uc)
static void hook_code32(uc_engine *uc, static void hook_code32(uc_engine *uc,
uint64_t address, uint64_t address,
uint32_t size, uint32_t size,
void *user_data) void *user_data,
const struct stat info)
{ {
//uint8_t opcode[256]; //uint8_t opcode[256];
uint8_t tmp[16]; uint8_t tmp[16];
@ -126,7 +117,7 @@ static void hook_code32(uc_engine *uc,
} }
printf("\n"); printf("\n");
} }
dump_stack_mem(uc); dump_stack_mem(uc, info);
if (address == 0x60000025) if (address == 0x60000025)
@ -222,6 +213,8 @@ static void test_tb_x86_64_32_imul_Gv_Ev_Ib(void **state)
{ {
uc_engine *uc = *state; uc_engine *uc = *state;
uc_hook trace1, trace2; uc_hook trace1, trace2;
struct stat info;
char * code = read_file("tb_x86.bin", &info);
//void *mem; //void *mem;
#ifdef RIP_NEXT_TO_THE_SELFMODIFY_OPCODE #ifdef RIP_NEXT_TO_THE_SELFMODIFY_OPCODE
// These values assumes just before PC = 0x60000021 // These values assumes just before PC = 0x60000021
@ -258,8 +251,8 @@ static void test_tb_x86_64_32_imul_Gv_Ev_Ib(void **state)
UC_PROT_ALL)); UC_PROT_ALL));
uc_assert_success(uc_mem_write(uc, uc_assert_success(uc_mem_write(uc,
PHY_STACK_REGION, PHY_STACK_REGION,
X86_CODE32_ALPHA_MIXED, code,
sizeof(X86_CODE32_ALPHA_MIXED) - 1)); info.st_size));
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EAX, &eax)); uc_assert_success(uc_reg_write(uc, UC_X86_REG_EAX, &eax));
uc_assert_success(uc_reg_write(uc, UC_X86_REG_ECX, &ecx)); uc_assert_success(uc_reg_write(uc, UC_X86_REG_ECX, &ecx));
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EDX, &edx)); uc_assert_success(uc_reg_write(uc, UC_X86_REG_EDX, &edx));
@ -275,7 +268,8 @@ static void test_tb_x86_64_32_imul_Gv_Ev_Ib(void **state)
hook_code32, hook_code32,
NULL, NULL,
1, 1,
0)); 0,
info));
uc_assert_success(uc_hook_add(uc, uc_assert_success(uc_hook_add(uc,
&trace2, &trace2,
@ -294,7 +288,7 @@ static void test_tb_x86_64_32_imul_Gv_Ev_Ib(void **state)
#else #else
PHY_STACK_REGION+0x0000, PHY_STACK_REGION+0x0000,
#endif #endif
PHY_STACK_REGION+sizeof(X86_CODE32_ALPHA_MIXED) - 1, PHY_STACK_REGION+info.st_size,
0, 0)); 0, 0));
uc_assert_success(uc_close(uc)); uc_assert_success(uc_close(uc));

View file

@ -6,6 +6,7 @@
#include <setjmp.h> #include <setjmp.h>
#include <cmocka.h> #include <cmocka.h>
#include <unicorn/unicorn.h> #include <unicorn/unicorn.h>
#include <sys/stat.h>
/** /**
* Assert that err matches expect * Assert that err matches expect
@ -38,5 +39,12 @@ do { \
} \ } \
} while (0) } while (0)
char * read_file(const char *filename, struct stat *info) {
stat(filename, info);
char *code = malloc(info->st_size);
FILE *fp = fopen(filename, "r");
fread(code, info->st_size, 1, fp);
return code;
}
#endif /* UNICORN_TEST_H */ #endif /* UNICORN_TEST_H */

View file

@ -0,0 +1,49 @@
// Zero memory for page directories and page tables
mov $0x1000,%edi
mov $0x1000,%ecx
xor %eax,%eax
rep stos %eax,(%edi)
// Load DWORD [0x4000] with 0xDEADBEEF to retrieve later
mov $0x4000,%edi
mov $0xBEEF,%eax
mov %eax, (%edi)
// Identify map the first 4MiB of memory
mov $0x400,%ecx
mov $0x2000,%edi
mov $3, %eax
loop:
stos %eax,(%edi)
add $0x1000,%eax
loop loop
// Map phyiscal address 0x4000 to cirtual address 0x7FF000
mov $0x3ffc,%edi
mov $0x4003,%eax
mov %eax, (%edi)
// Add page tables into page directory
mov $0x1000, %edi
mov $0x2003, %eax
mov %eax, (%edi)
mov $0x1004, %edi
mov $0x3003, %eax
mov %eax, (%edi)
// Load the page directory register
mov $0x1000, %eax
mov %eax, %cr3
// Enable paging
mov %cr0, %eax
or $0x80000000, %eax
// Clear EAX
mov %eax, %cr0
//Load using virtual memory address; EAX = 0xBEEF
xor %eax,%eax
mov $0x7FF000, %esi
mov (%esi), %eax
hlt