mirror of
				https://github.com/yuzu-emu/unicorn.git
				synced 2025-10-29 08:47:02 +00:00 
			
		
		
		
	* 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
		
			
				
	
	
		
			307 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			307 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /**
 | |
|  * Unicorn x86_32 self-modifying unit test
 | |
|  *
 | |
|  * This test demonstrates the flushing of instruction translation cache
 | |
|  * after a self-modification of Intel's x8's "IMUL Gv,Ev,Ib" instruction.
 | |
|  */
 | |
| #include "unicorn_test.h"
 | |
| #include <errno.h>
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <sys/stat.h>
 | |
| #include "unicorn/unicorn.h"
 | |
| 
 | |
| #define RIP_NEXT_TO_THE_SELFMODIFY_OPCODE (1)
 | |
| 
 | |
| 
 | |
| //  Demostration of a self-modifying "IMUL eax,mem,Ib" opcode
 | |
| //  And the QEMU's ability to flush the translation buffer properly
 | |
| 
 | |
| #define MIN(a, b) (a < b? a: b)
 | |
| 
 | |
| #define CODE_SPACE (2 * 1024 * 1024)
 | |
| #define PHY_STACK_REGION (0x60000000)
 | |
| 
 | |
| /* Called before every test to set up a new instance */
 | |
| static int setup(void **state)
 | |
| {
 | |
|     uc_engine *uc;
 | |
| 
 | |
|     uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
 | |
| 
 | |
|     *state = uc;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Called after every test to clean up */
 | |
| static int teardown(void **state)
 | |
| {
 | |
|     uc_engine *uc = *state;
 | |
| 
 | |
|     uc_assert_success(uc_close(uc));
 | |
| 
 | |
|     *state = NULL;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| static void dump_stack_mem(uc_engine *uc, const struct stat info)
 | |
| {
 | |
|     uint8_t tmp[256];
 | |
|     uint32_t size;
 | |
| 
 | |
|     size = sizeof(info.st_size);
 | |
|     if (size > 255) size = 255;
 | |
|     if (!uc_mem_read(uc, PHY_STACK_REGION, tmp, size)) 
 | |
|     {
 | |
|         uint32_t i;
 | |
| 
 | |
|         printf("Stack region dump");
 | |
|         for (i=0; i<size; i++) {
 | |
|             if ((i % 16) == 0) printf("\n%x: ", PHY_STACK_REGION+i);
 | |
|             printf("%x ", tmp[i]);
 | |
|         }
 | |
|         printf("\n");
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void print_registers(uc_engine *uc)
 | |
| {
 | |
|     int32_t eax, ecx, edx, ebx;
 | |
|     int32_t esp, ebp, esi, edi;
 | |
|     uc_reg_read(uc, UC_X86_REG_EAX, &eax);
 | |
|     uc_reg_read(uc, UC_X86_REG_ECX, &ecx);
 | |
|     uc_reg_read(uc, UC_X86_REG_EDX, &edx);
 | |
|     uc_reg_read(uc, UC_X86_REG_EBX, &ebx);
 | |
|     uc_reg_read(uc, UC_X86_REG_ESP, &esp);
 | |
|     uc_reg_read(uc, UC_X86_REG_EBP, &ebp);
 | |
|     uc_reg_read(uc, UC_X86_REG_ESI, &esi);
 | |
|     uc_reg_read(uc, UC_X86_REG_EDI, &edi);
 | |
| 
 | |
|     printf("Register dump:\n");
 | |
|     printf("eax %8.8x ", eax);
 | |
|     printf("ecx %8.8x ", ecx);
 | |
|     printf("edx %8.8x ", edx);
 | |
|     printf("ebx %8.8x\n", ebx);
 | |
|     printf("esp %8.8x ", esp);
 | |
|     printf("ebp %8.8x ", ebp);
 | |
|     printf("esi %8.8x ", esi);
 | |
|     printf("edi %8.8x ", edi);
 | |
|     printf("\n");
 | |
| }
 | |
| 
 | |
| 
 | |
| static void hook_code32(uc_engine *uc, 
 | |
|                         uint64_t address, 
 | |
|                         uint32_t size, 
 | |
|                         void *user_data,
 | |
|                         const struct stat info)
 | |
| {
 | |
|     //uint8_t opcode[256];
 | |
|     uint8_t tmp[16];
 | |
|     uint32_t tmp4[1];
 | |
|     uint32_t ecx;
 | |
| 
 | |
|     printf("\nhook_code32: Address: %"PRIx64", Opcode Size: %d\n", address, size);
 | |
|     print_registers(uc);
 | |
|     size = MIN(sizeof(tmp), size);
 | |
|     if (!uc_mem_read(uc, address, tmp, size)) 
 | |
|     {
 | |
|         uint32_t i;
 | |
| 
 | |
|         printf("Opcode: ");
 | |
|         for (i=0; i<size; i++) {
 | |
|             printf("%x ", tmp[i]);
 | |
|         }
 | |
|         printf("\n");
 | |
|     }
 | |
|     dump_stack_mem(uc, info);
 | |
| 
 | |
| 
 | |
|     if (address == 0x60000025)
 | |
|     {
 | |
|         //  double-check that opcode is
 | |
|         //      IMUL aex,[eax+0x41],0x10
 | |
|         if ((tmp[0] != 0x6b) ||
 | |
|             (tmp[1] != 0x41) ||
 | |
|             (tmp[2] != 0x41) ||
 | |
|             (tmp[3] != 0x10))
 | |
|         {
 | |
|             printf("FAILED set-up of opcode\n");
 | |
|             exit(-1);
 | |
|         }
 | |
|         printf("IMUL eax,[ecx+0x41],0x10\n");
 | |
| 
 | |
|         //  double-check that memory operand points to 0x6000003a
 | |
|         uc_reg_read(uc, UC_X86_REG_ECX, &ecx);
 | |
|         if (ecx != 0x5ffffff9)
 | |
|         {
 | |
|             printf("FAILED EAX register not having 0x5ffffff9\n");
 | |
|             exit(-1);
 | |
|         }
 | |
|         printf("ECX = %8.8x\n", ecx);
 | |
| 
 | |
|         printf("%8.8x + 0x41 = %8.8x\n", 0x5ffffff9, 0x5ffffff9 + 0x41);
 | |
| 
 | |
|         //  double-check that memory location 0x60000039
 | |
|         //  contains 0x5151494a
 | |
|         if (!uc_mem_read(uc, 0x6000003a, tmp4, 4)) 
 | |
|         {
 | |
|             if (tmp4[0] != 0x5151494a)
 | |
|             {
 | |
|                 printf("FAILED set-up\n");
 | |
|                 exit(-1);
 | |
|             }
 | |
|             printf("Proved that 0x6000003a contains the proper 0x5151494a\n");
 | |
|         }
 | |
|     //    dump_stack_mem(uc);
 | |
|     }
 | |
| 
 | |
|     // Stop after 'imul eax,[ecx+0x41],0x10
 | |
|     if (address == 0x60000029)
 | |
|     {
 | |
|         uint32_t eax;
 | |
|         // IMUL eax,mem,Ib
 | |
|         // mem = [ecx+0x41]
 | |
|         // ecx = 0x5ffffff9
 | |
|         // [6000003A] = 0x5151494a
 | |
|         // Stop after 'imul eax,[ecx+0x41],0x10
 | |
|         // This step basically shifts left 8-bit...elaborately.
 | |
|         // multiplying 0x5151494a x 0x10 = 0x151494a0
 | |
|         uc_reg_read(uc, UC_X86_REG_EAX, &eax);
 | |
|         if (eax != 0x151494a0)
 | |
|         {
 | |
|             fail_msg("FAIL: TB did not flush; eax is not the expected 0x151494a0\n");
 | |
|             print_registers(uc);
 | |
|             //dump_stack_mem(uc);
 | |
|             exit(-1);
 | |
|         }
 | |
|         printf("PASS\n");
 | |
|     }
 | |
|     print_registers(uc);
 | |
|     // dump_stack_mem(uc);
 | |
|       
 | |
|     return;
 | |
| }
 | |
| 
 | |
| static void hook_mem32(uc_engine *uc, 
 | |
|                        uc_mem_type type, 
 | |
|                        uint64_t address, 
 | |
|                        int size, 
 | |
|                        uint64_t value, 
 | |
|                        void *user_data)
 | |
| {
 | |
|     char ctype;
 | |
|     //uint32_t tmp[1];
 | |
| 
 | |
|     ctype = '?';
 | |
|     if (type == UC_MEM_READ) ctype = 'R';
 | |
|     if (type == UC_MEM_WRITE) ctype = 'W';
 | |
|     printf("hook_mem32(%c): Address: 0x%"PRIx64", Size: %d, Value:0x%"PRIx64"\n", ctype, address, size, value);
 | |
| 
 | |
|     // if (!uc_mem_read(uc, 0x6000003a, tmp, 4)) 
 | |
|     // {
 | |
|         // printf("  hook_mem32  0x6000003a: %8.8x\n", tmp[0]);
 | |
|     // }
 | |
|     return;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void test_tb_x86_64_32_imul_Gv_Ev_Ib(void **state)
 | |
| {
 | |
|     uc_engine *uc = *state;
 | |
|     uc_hook trace1, trace2;
 | |
|     struct stat info;
 | |
|     char * code = read_file("tb_x86.bin", &info);
 | |
|     //void *mem;
 | |
| #ifdef RIP_NEXT_TO_THE_SELFMODIFY_OPCODE
 | |
|     // These values assumes just before PC = 0x60000021
 | |
|     int64_t eax = 0x00000041;
 | |
|     int64_t ecx = 0x5ffffff8;
 | |
|     int64_t edx = 0x5ffffff8;
 | |
|     int64_t ebx = 0x034a129b;
 | |
|     int64_t esp = 0x6010229a;
 | |
|     int64_t ebp = 0x60000002;
 | |
|     int64_t esi = 0x1f350211;
 | |
|     int64_t edi = 0x488ac239;
 | |
| #else
 | |
|     //  These values assumes PC == 0x6000000
 | |
|     int64_t eax = 0x73952c43;
 | |
|     int64_t ecx = 0x6010229a;
 | |
|     int64_t edx = 0x2a500e50;
 | |
|     int64_t ebx = 0x034a1295;
 | |
|     int64_t esp = 0x6010229a;
 | |
|     int64_t ebp = 0x60000000;
 | |
|     int64_t esi = 0x1f350211;
 | |
|     int64_t edi = 0x488ac239;
 | |
| #endif
 | |
| 
 | |
|     //mem = calloc(1, CODE_SPACE);
 | |
|     // TODO examine
 | |
|     //assert_int_not_equal(0, mem);
 | |
| 
 | |
|     uc_assert_success(uc_open(UC_ARCH_X86, 
 | |
|                               UC_MODE_32, 
 | |
|                               &uc));
 | |
|     uc_assert_success(uc_mem_map(uc, 
 | |
|                                  PHY_STACK_REGION, 
 | |
|                                  CODE_SPACE, 
 | |
|                                  UC_PROT_ALL));
 | |
|     uc_assert_success(uc_mem_write(uc,
 | |
|                                    PHY_STACK_REGION,
 | |
|                                    code,
 | |
|                                    info.st_size));
 | |
|     uc_assert_success(uc_reg_write(uc, UC_X86_REG_EAX, &eax));
 | |
|     uc_assert_success(uc_reg_write(uc, UC_X86_REG_ECX, &ecx));
 | |
|     uc_assert_success(uc_reg_write(uc, UC_X86_REG_EDX, &edx));
 | |
|     uc_assert_success(uc_reg_write(uc, UC_X86_REG_EBX, &ebx));
 | |
|     uc_assert_success(uc_reg_write(uc, UC_X86_REG_ESP, &esp));
 | |
|     uc_assert_success(uc_reg_write(uc, UC_X86_REG_EBP, &ebp));
 | |
|     uc_assert_success(uc_reg_write(uc, UC_X86_REG_ESI, &esi));
 | |
|     uc_assert_success(uc_reg_write(uc, UC_X86_REG_EDI, &edi));
 | |
| 
 | |
|     uc_assert_success(uc_hook_add(uc,
 | |
|                 &trace1,
 | |
|                 UC_HOOK_CODE,
 | |
|                 hook_code32,
 | |
|                 NULL,
 | |
|                 1,
 | |
|                 0,
 | |
|                 info));
 | |
| 
 | |
|     uc_assert_success(uc_hook_add(uc,
 | |
|                 &trace2,
 | |
|                 UC_HOOK_MEM_VALID,
 | |
|                 hook_mem32,
 | |
|                 NULL,
 | |
|                 1,
 | |
|                 0));
 | |
| 
 | |
|     uc_assert_success(uc_emu_start(uc,
 | |
| #ifdef RIP_NEXT_TO_THE_SELFMODIFY_OPCODE
 | |
|     //  Register set (before self-modifying IMUL opcode)
 | |
|     //  Start at "0x00000021: xorb   %al, 0x30(%ecx)
 | |
|     //  Start at "0x00000021: xor    byte ptr [ecx + 0x30], al
 | |
|                        PHY_STACK_REGION+0x0021,   //  0x0024 didn't work
 | |
| #else
 | |
|                        PHY_STACK_REGION+0x0000,
 | |
| #endif
 | |
|                        PHY_STACK_REGION+info.st_size,
 | |
|                        0, 0));
 | |
| 
 | |
|     uc_assert_success(uc_close(uc));
 | |
| }
 | |
| 
 | |
| int
 | |
| main(void)
 | |
| {
 | |
| #define test(x)	cmocka_unit_test_setup_teardown(x, setup, teardown)
 | |
|     const struct CMUnitTest tests[] = {
 | |
|         test(test_tb_x86_64_32_imul_Gv_Ev_Ib)
 | |
|     };
 | |
| #undef test
 | |
|     return cmocka_run_group_tests(tests, NULL, NULL);
 | |
| }
 |