├── README.md ├── box86-tests ├── args04.txt ├── args05.txt ├── ref03.txt ├── ref04.txt ├── ref05.txt ├── ref06.txt ├── ref07.txt ├── ref08.txt ├── ref11.txt ├── ref12.txt ├── test03.c ├── test04.c ├── test05.c ├── test06.c ├── test07.c ├── test08.c ├── test11.c └── test12.c ├── cc.py ├── cfstyle.py ├── iatindir.py ├── indir.py ├── main.py ├── pefile.py ├── recompiler.py ├── stubgen.py ├── test.exe ├── tlshooks.py ├── vmem.py └── x87.py /README.md: -------------------------------------------------------------------------------- 1 | # peshit 2 | 3 | This is a **proof-of-concept** X86-to-ARM static recompiler. As of now it is able to correctly run a MinGW-compiled "Hello, world!" program. From the user's perspective, it takes a Windows .EXE file for X86 and outputs a (hopefully) equivalent Windows .EXE file for ARM. 4 | 5 | Exceptions in recompiler code are postponed until runtime, so that e.g.bogus "instructions" from .rodata do not need to be supported. 6 | 7 | ## Checklist 8 | 9 | X86 implementation status: 10 | 11 | - [x] "Hello, world!" compiles and runs 12 | - [ ] (8/14) [box86](https://github.com/ptitSeb/box86) test suite compiles and runs 13 | - [ ] [box86](https://github.com/ptitSeb/box86) test suite compiles and runs with MSVC 14 | - [ ] At least one real application compiles and runs 15 | 16 | Output compatibility: 17 | 18 | - [x] Recompiled executables run in Wine on ARM 19 | - [ ] Recompiled executables run in Windows on ARM 20 | - [ ] Recompiled executables can be inspected using binutils 21 | 22 | ## Non-goals 23 | 24 | * JIT support 25 | * Running on a native Windows system, except for finished recompiled executables 26 | 27 | ## Usage 28 | 29 | Dependencies: 30 | * A Linux system 31 | * Python 3 with capstone library 32 | * i686-w64-mingw32-gcc 33 | * arm-linux-gnueabihf-gcc 34 | * llvm-mingw 35 | 36 | Usage: 37 | 38 | `python3 main.py input.exe output.exe` 39 | 40 | Specify valid toolchain prefixes in `cc.py` before usage. 41 | 42 | ## Development & debugging 43 | 44 | The modules and their functions are as follows: 45 | 46 | ``` 47 | cc.py Wrappers for toolchains 48 | cfstyle.py CF flag handling via "CF styles" (add/sub/xor) 49 | iatindir.py Import table indirection (the native one contains ARM function addresses) 50 | indir.py Indirect jump/call/return handling 51 | main.py Orchestrates the whole thing 52 | pefile.py PE file parsing & output 53 | recompiler.py The main recompiler code, translates X86 instructions to ARM instructions 54 | stubgen.py Glue code for translating WinAPI calls 55 | tlshooks.py Translation of TLS callback functions 56 | vmem.py Virtual memory emulation for recompiler's internal use 57 | ``` 58 | 59 | Recompiler configuration (in `recompiler.py`): 60 | 61 | ``` 62 | TRACE = False # if True, EIP, translated PC, CPSR, and general-purpose registers will be printed before execution of each X86 instruction 63 | TRACEBACK = True # if True (default), full tracebacks of recompiler exceptions will be embedded inside the executable and printed if the execution reaches the failed instruction 64 | ``` 65 | -------------------------------------------------------------------------------- /box86-tests/args04.txt: -------------------------------------------------------------------------------- 1 | yeah 2 | -------------------------------------------------------------------------------- /box86-tests/args05.txt: -------------------------------------------------------------------------------- 1 | 7 2 | -------------------------------------------------------------------------------- /box86-tests/ref03.txt: -------------------------------------------------------------------------------- 1 | Hello World! 2 | -------------------------------------------------------------------------------- /box86-tests/ref04.txt: -------------------------------------------------------------------------------- 1 | Hello, argc=2 argv[1]=yeah 2 | -------------------------------------------------------------------------------- /box86-tests/ref05.txt: -------------------------------------------------------------------------------- 1 | fact(7)=5040 2 | Prime list 0..5040: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997 1009 1013 1019 1021 1031 1033 1039 1049 1051 1061 1063 1069 1087 1091 1093 1097 1103 1109 1117 1123 1129 1151 1153 1163 1171 1181 1187 1193 1201 1213 1217 1223 1229 1231 1237 1249 1259 1277 1279 1283 1289 1291 1297 1301 1303 1307 1319 1321 1327 1361 1367 1373 1381 1399 1409 1423 1427 1429 1433 1439 1447 1451 1453 1459 1471 1481 1483 1487 1489 1493 1499 1511 1523 1531 1543 1549 1553 1559 1567 1571 1579 1583 1597 1601 1607 1609 1613 1619 1621 1627 1637 1657 1663 1667 1669 1693 1697 1699 1709 1721 1723 1733 1741 1747 1753 1759 1777 1783 1787 1789 1801 1811 1823 1831 1847 1861 1867 1871 1873 1877 1879 1889 1901 1907 1913 1931 1933 1949 1951 1973 1979 1987 1993 1997 1999 2003 2011 2017 2027 2029 2039 2053 2063 2069 2081 2083 2087 2089 2099 2111 2113 2129 2131 2137 2141 2143 2153 2161 2179 2203 2207 2213 2221 2237 2239 2243 2251 2267 2269 2273 2281 2287 2293 2297 2309 2311 2333 2339 2341 2347 2351 2357 2371 2377 2381 2383 2389 2393 2399 2411 2417 2423 2437 2441 2447 2459 2467 2473 2477 2503 2521 2531 2539 2543 2549 2551 2557 2579 2591 2593 2609 2617 2621 2633 2647 2657 2659 2663 2671 2677 2683 2687 2689 2693 2699 2707 2711 2713 2719 2729 2731 2741 2749 2753 2767 2777 2789 2791 2797 2801 2803 2819 2833 2837 2843 2851 2857 2861 2879 2887 2897 2903 2909 2917 2927 2939 2953 2957 2963 2969 2971 2999 3001 3011 3019 3023 3037 3041 3049 3061 3067 3079 3083 3089 3109 3119 3121 3137 3163 3167 3169 3181 3187 3191 3203 3209 3217 3221 3229 3251 3253 3257 3259 3271 3299 3301 3307 3313 3319 3323 3329 3331 3343 3347 3359 3361 3371 3373 3389 3391 3407 3413 3433 3449 3457 3461 3463 3467 3469 3491 3499 3511 3517 3527 3529 3533 3539 3541 3547 3557 3559 3571 3581 3583 3593 3607 3613 3617 3623 3631 3637 3643 3659 3671 3673 3677 3691 3697 3701 3709 3719 3727 3733 3739 3761 3767 3769 3779 3793 3797 3803 3821 3823 3833 3847 3851 3853 3863 3877 3881 3889 3907 3911 3917 3919 3923 3929 3931 3943 3947 3967 3989 4001 4003 4007 4013 4019 4021 4027 4049 4051 4057 4073 4079 4091 4093 4099 4111 4127 4129 4133 4139 4153 4157 4159 4177 4201 4211 4217 4219 4229 4231 4241 4243 4253 4259 4261 4271 4273 4283 4289 4297 4327 4337 4339 4349 4357 4363 4373 4391 4397 4409 4421 4423 4441 4447 4451 4457 4463 4481 4483 4493 4507 4513 4517 4519 4523 4547 4549 4561 4567 4583 4591 4597 4603 4621 4637 4639 4643 4649 4651 4657 4663 4673 4679 4691 4703 4721 4723 4729 4733 4751 4759 4783 4787 4789 4793 4799 4801 4813 4817 4831 4861 4871 4877 4889 4903 4909 4919 4931 4933 4937 4943 4951 4957 4967 4969 4973 4987 4993 4999 5003 5009 5011 5021 5023 5039 3 | (un)signed char = -5/83 (un)signed int = -53/65500 total=65525 4 | 65500/5=13100, 65500%5=0 5 | 65525/5=13105, 65525%5=0 6 | 65525/-53=-1236 + 17 7 | -------------------------------------------------------------------------------- /box86-tests/ref06.txt: -------------------------------------------------------------------------------- 1 | [02] Second thread executing 2 | [02] Thread done. 3 | 4 | [00] Done. 5 | -------------------------------------------------------------------------------- /box86-tests/ref07.txt: -------------------------------------------------------------------------------- 1 | 0 is 0.000000, sin(pi/2) is 1.000000 and 3*1.5 is 4.500000. 2 | -------------------------------------------------------------------------------- /box86-tests/ref08.txt: -------------------------------------------------------------------------------- 1 | 31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185 -------------------------------------------------------------------------------- /box86-tests/ref11.txt: -------------------------------------------------------------------------------- 1 | Create/start 2 threads 2 | Thread 1: Entered (10/20) 3 | Thread 1: foo(), TLS data=0 2 "-1-" 4 | Thread 2: Entered (10/20) 5 | Thread 2: foo(), TLS data=1 4 "-2-" 6 | Thread 2: bar(), TLS data=1 4 "-2-" 7 | Thread 1: bar(), TLS data=0 2 "-1-" 8 | Main completed 9 | -------------------------------------------------------------------------------- /box86-tests/ref12.txt: -------------------------------------------------------------------------------- 1 | 1000000000000 => 1000000000000.000000 2 | -1000000000000 => -1000000000000.000000 3 | 999999999999.000000 => 999999999999 4 | (angle_t)268435456.000000 = 268435456 == 0x10000000 5 | go PI trucated=3, -PI rounded=-3 6 | -------------------------------------------------------------------------------- /box86-tests/test03.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char **argv) 4 | { 5 | printf("Hello World!\n"); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /box86-tests/test04.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char **argv) 4 | { 5 | printf("Hello, argc=%d argv[%d]=%s\n", argc, argc-1, argv[argc-1]); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /box86-tests/test05.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int fact(int i) { 7 | if (i<2) return i; 8 | return i*fact(i-1); 9 | } 10 | 11 | #define SET(M) dels[M/8] |= (1<<(M%8)) 12 | #define GET(M) ((dels[M/8]>>(M%8))&1) 13 | 14 | int main(int argc, const char** argv) 15 | { 16 | int j = 5; 17 | if(argc>1) 18 | j = atoi(argv[1]); 19 | if(j==0) 20 | j=5; 21 | if(j>15) j=15; 22 | 23 | int k = fact(j); 24 | printf("fact(%d)=%d\n", j, k); 25 | 26 | uint8_t* dels = (char*)calloc((k+7)/8, 1); 27 | SET(0); 28 | SET(1); 29 | 30 | for (int i=2; i 2 | #include 3 | #include 4 | 5 | const int thread_count = 2; 6 | HANDLE th[2]; 7 | DWORD tid[2]; 8 | const char *thread_messages[2] = { 9 | "First thread executing", 10 | "Second thread executing" 11 | }; 12 | 13 | DWORD __stdcall doSomething(void *arg) 14 | { 15 | DWORD id = GetCurrentThreadId(); 16 | int num = -1; 17 | 18 | for (int i = 0 ; i < thread_count ; ++i) 19 | { 20 | if (id == tid[i]) 21 | { 22 | num = i + 1; 23 | if (num == 2) printf("[%02d] %s\n", num, thread_messages[i]); 24 | break; 25 | } 26 | } 27 | 28 | for (unsigned int i = 0 ; i < 0x10000 ; ++i); 29 | if (num == 2) printf("[%02d] Thread done.\n", num); 30 | 31 | return 0; 32 | } 33 | 34 | int main(int argc, char const *argv[]) 35 | { 36 | int err; 37 | 38 | for (int i = 0 ; i < thread_count ; ++i) 39 | { 40 | //printf("[00] Thread %d starting\n", i + 1); 41 | th[i] = CreateThread(NULL, 0, doSomething, NULL, 0, tid+i); 42 | if (!th[i]) 43 | { 44 | printf("[00] Couldn't create thread %d: %d\n", i + 1, GetLastError()); 45 | } 46 | for (unsigned int i = 0 ; i < 0x1000 ; ++i); 47 | } 48 | 49 | //printf("[00] Waiting for all threads to end...\n"); 50 | for (int i = 0 ; i < thread_count ; ++i) 51 | WaitForSingleObject(th[i], INFINITE); 52 | printf("\n[00] Done.\n"); 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /box86-tests/test07.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifndef M_PI 5 | #define M_PI 3.14159265358979323846 6 | #endif 7 | 8 | asm("_my_sin:\njmp _sin"); 9 | typeof(sin) my_sin; 10 | 11 | int main(int argc, char **argv) 12 | { 13 | long double zero = 0.0; 14 | 15 | double si = my_sin(M_PI / 2.0); 16 | 17 | int a = 3; 18 | float b = 1.5; 19 | float mul = a * b; 20 | 21 | printf("0 is %lf, sin(pi/2) is %f and 3*1.5 is %f.\n", (double)zero, si, mul); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /box86-tests/test08.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | //from https://crypto.stanford.edu/pbc/notes/pi/code.html 5 | // 800 first decimals of PI 6 | 7 | int main() { 8 | int r[2800 + 1]; 9 | int i, k; 10 | int b, d; 11 | int c = 0; 12 | 13 | for (i = 0; i < 2800; i++) { 14 | r[i] = 2000; 15 | } 16 | 17 | for (k = 2800; k > 0; k -= 14) { 18 | d = 0; 19 | 20 | i = k; 21 | for (;;) { 22 | d += r[i] * 10000; 23 | b = 2 * i - 1; 24 | 25 | r[i] = d % b; 26 | d /= b; 27 | i--; 28 | if (i == 0) break; 29 | d *= i; 30 | } 31 | printf("%.4d", c + d / 10000); 32 | c = d % 10000; 33 | } 34 | 35 | return 0; 36 | } -------------------------------------------------------------------------------- /box86-tests/test11.c: -------------------------------------------------------------------------------- 1 | #define _MULTI_THREADED 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void foo(void); /* Functions that use the TLS data */ 9 | void bar(void); 10 | 11 | #define checkResults(string, val) { \ 12 | if (!val) { \ 13 | printf("Failed with %d at %s", GetLastError(), string); \ 14 | exit(1); \ 15 | } \ 16 | } 17 | 18 | /* 19 | Use the keyword provided by pthread.h to delcare the following variable 20 | is thread specific, i.e. it is only visible to a specific thread, 21 | not shared/common to all thread. 22 | These variables are stored in thread local storage (TLS) area. 23 | */ 24 | __thread int TLS_data1 = 10; 25 | __thread int TLS_data2 = 20; 26 | __thread char TLS_data3[10]; 27 | 28 | #define NUMTHREADS 2 29 | HANDLE thread[NUMTHREADS]; 30 | DWORD tids[NUMTHREADS]; 31 | 32 | typedef struct { 33 | int data1; 34 | int data2; 35 | } threadparm_t; 36 | 37 | DWORD __stdcall thread_run(void *parm) 38 | { 39 | int rc; 40 | threadparm_t *gData; 41 | 42 | printf("Thread %d: Entered (%d/%d)\n", (GetCurrentThreadId()==tids[0])?1:2, TLS_data1, TLS_data2); 43 | 44 | gData = (threadparm_t *)parm; 45 | 46 | /* Assign the value from global variable to thread specific variable*/ 47 | TLS_data1 = gData->data1; 48 | TLS_data2 = gData->data2; 49 | strcpy(TLS_data3, "---"); 50 | TLS_data3[1] = (GetCurrentThreadId()==tids[0])?'1':'2'; 51 | 52 | foo(); 53 | return 0; 54 | } 55 | 56 | void foo() { 57 | printf("Thread %d: foo(), TLS data=%d %d \"%s\"\n", 58 | (GetCurrentThreadId()==tids[0])?1:2, TLS_data1, TLS_data2, TLS_data3); 59 | while(!thread[1]) 60 | usleep(300); 61 | if(GetCurrentThreadId()==tids[0]) 62 | WaitForSingleObject(thread[1], INFINITE); 63 | bar(); 64 | } 65 | 66 | void bar() { 67 | printf("Thread %d: bar(), TLS data=%d %d \"%s\"\n", 68 | (GetCurrentThreadId()==tids[0])?1:2, TLS_data1, TLS_data2, TLS_data3); 69 | return; 70 | } 71 | 72 | 73 | int main(int argc, char **argv) 74 | { 75 | int rc=0; 76 | int i; 77 | threadparm_t gData[NUMTHREADS]; 78 | 79 | printf("Create/start %d threads\n", NUMTHREADS); 80 | for (i=0; i < NUMTHREADS; i++) { 81 | /* Create per-thread TLS data and pass it to the thread */ 82 | gData[i].data1 = i; 83 | gData[i].data2 = (i+1)*2; 84 | thread[i] = CreateThread(NULL, 0, thread_run, &gData[i], 0, tids+i); 85 | checkResults("CreateThread()\n", thread[i]); 86 | usleep(200); 87 | } 88 | 89 | //printf("Wait for all threads to complete, and release their resources\n"); 90 | for (i=0; i < NUMTHREADS; i++) { 91 | WaitForSingleObject(thread[i], INFINITE); 92 | //checkResults("pthread_join()\n", rc); 93 | } 94 | 95 | printf("Main completed\n"); 96 | return 0; 97 | } 98 | 99 | -------------------------------------------------------------------------------- /box86-tests/test12.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | typedef uint32_t uint32; 6 | typedef uint32 angle_t; 7 | 8 | int main(int argc, char **argv) 9 | { 10 | int64_t i64 = 1000000000000; 11 | double d = i64; 12 | printf("%lli => %f\n", i64, d); 13 | i64 = -i64; 14 | d = i64; 15 | printf("%lli => %f\n", i64, d); 16 | d = 999999999999.0; 17 | i64 = d; 18 | printf("%f => %lli\n", d, i64); 19 | 20 | d = M_PI/4.0; 21 | d = d*(1<<30)/M_PI; 22 | angle_t u32 = (angle_t)d; 23 | printf("(angle_t)%f = %u == 0x%08X\n", d, u32, u32); 24 | 25 | int16_t a=0, b=0; 26 | asm volatile ( 27 | "fldpi \n" 28 | "fisttp %0 \n" 29 | : "=m" (a)); 30 | asm volatile ( 31 | "fldpi \n" 32 | "fchs \n" 33 | "fistp %0 \n" 34 | : "=m" (b)); 35 | printf("go PI trucated=%d, -PI rounded=%d\n", a, b); 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /cc.py: -------------------------------------------------------------------------------- 1 | import pefile, subprocess, tempfile, json, os 2 | 3 | CC_X86 = 'i686-w64-mingw32-' 4 | CC_ARM = 'llvm-mingw-20201020-ucrt-ubuntu-18.04/bin/armv7-w64-mingw32-' 5 | CC_ARM_HOST = 'arm-unknown-linux-gnueabihf-' 6 | 7 | def nm(cc_prefix, file, text_vma): 8 | p = subprocess.Popen((cc_prefix+'nm', file), stdout=subprocess.PIPE, encoding='utf-8') 9 | ans = p.communicate(None)[0] 10 | assert not p.wait() 11 | return {i[2].split('@', 1)[0]: int(i[0], 16) - text_vma for i in map(str.split, ans.split('\n')) if len(i) == 3} 12 | 13 | def elf_extract_text(data): 14 | assert data[:4] == b'\x7fELF' 15 | shoff = int.from_bytes(data[32:36], 'little') 16 | shnum = int.from_bytes(data[48:50], 'little') 17 | for i in range(shnum): 18 | off = shoff + 40 * i 19 | entry = data[off:off+40] 20 | flags = int.from_bytes(entry[8:12], 'little') 21 | addr = int.from_bytes(entry[12:16], 'little') 22 | offset = int.from_bytes(entry[16:20], 'little') 23 | size = int.from_bytes(entry[20:24], 'little') 24 | if flags == 6: # ax 25 | return (addr, data[offset:offset+size]) 26 | return None 27 | 28 | def compile_x86(c_code, asm_code, link_addr): 29 | with tempfile.TemporaryDirectory() as file: 30 | p = subprocess.Popen((CC_X86+'gcc', '-ffreestanding', '-O0', '-x', 'c', '-', '-nostdlib', '-static', '-Ttext='+hex(link_addr), '-o', file+'/out.dll'), stdin=subprocess.PIPE, encoding='utf-8') 31 | p.communicate('asm('+json.dumps(asm_code).replace('\\u00', '\\x')+');\n'+c_code) 32 | assert not p.wait() 33 | with open(file+'/out.dll', 'rb') as f: 34 | pe = pefile.PeFile(f.read()) 35 | text_section = next(i for i in pe.sections if i[0] == '.text') 36 | text_vma = text_section[1] 37 | text_memsz = text_section[2] 38 | text = text_section[4] 39 | return (text[:text_memsz], nm(CC_X86, file+'/out.dll', text_vma)) 40 | 41 | # mingw arm clang does not allow `.code 32`, so this hack 42 | def clang_sucks(asm_code): 43 | ans = [] 44 | ii = iter(asm_code.split('\n')) 45 | for i in ii: 46 | if i.split()[:1] == ['.def']: 47 | while next(ii).strip() != '.endef': pass 48 | elif i.strip() == '.section\t.rdata,"dr"': pass 49 | else: 50 | ans.append(i) 51 | return '\n'.join(ans) 52 | 53 | def compile_arm(c_code, asm_code, link_addr): 54 | with tempfile.TemporaryDirectory() as file: 55 | p = subprocess.Popen((CC_ARM+'gcc', '-fno-addrsig', '-fno-asynchronous-unwind-tables', '-x', 'c', '-', '-S', '-o', '-'), stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding='utf-8') 56 | asm_code = clang_sucks(p.communicate(c_code)[0] + asm_code + '\n.global _start\n_start:\n') 57 | assert not p.wait() 58 | p = subprocess.Popen((CC_ARM_HOST+'gcc', '-march=armv8-a', '-mfpu=neon', '-x', 'assembler', '-', '-nostdlib', '-static', '-Ttext='+hex(link_addr), '-o', file+'/out.elf'), stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding='utf-8') 59 | p.communicate(asm_code) 60 | if p.wait(): 61 | __import__('pydoc').pager(asm_code) 62 | assert False 63 | with open(file+'/out.elf', 'rb') as f: 64 | text_vma, text = elf_extract_text(f.read()) 65 | return (text, nm(CC_ARM_HOST, file+'/out.elf', text_vma)) 66 | -------------------------------------------------------------------------------- /cfstyle.py: -------------------------------------------------------------------------------- 1 | import recompiler 2 | 3 | def propagate_cf_uses(cfg, cf_uses): 4 | cf_uses_0 = set(cf_uses) 5 | q = list(cf_uses) 6 | cf_uses = set() 7 | q2 = [] 8 | while q: 9 | while q: 10 | i = q.pop() 11 | if i in cf_uses: continue 12 | cf_uses.add(i) 13 | if i not in cfg: continue 14 | q2.extend(cfg[i]) 15 | q, q2 = q2, q 16 | assert cf_uses_0.issubset(cf_uses) 17 | return cf_uses 18 | 19 | def calculate_cf_style(cfg, cf_uses, ef_uses, cf_styles): 20 | cf_uses = propagate_cf_uses(cfg, cf_uses) 21 | ef_uses = propagate_cf_uses(cfg, ef_uses) 22 | return {i: j if i in cf_uses or (i in ef_uses and j in recompiler.CF_FUCKUP) else ('none' if i in ef_uses else 'noone') for i, j in cf_styles.items()} 23 | -------------------------------------------------------------------------------- /iatindir.py: -------------------------------------------------------------------------------- 1 | import pefile, vmem, sys, collections 2 | 3 | class CaseInsensitive(str): 4 | def __hash__(self): return hash(self.lower()) 5 | def __eq__(self, other): return self.lower() == other.lower() 6 | 7 | def read_string(x, v, addr): 8 | ans = [] 9 | while True: 10 | c = v[addr] 11 | if not c: break 12 | ans.append(c) 13 | addr += 1 14 | return bytes(ans).decode('latin-1') 15 | 16 | def parse_iat(x, v): 17 | it = x.offset_table[1][0] 18 | ans = collections.OrderedDict() 19 | while any(v[it:it+4]): 20 | p_lookup = x.base_addr + int.from_bytes(v[it:it+4], 'little') 21 | dllname = read_string(x, v, x.base_addr + int.from_bytes(v[it+12:it+16], 'little')) 22 | p_iat = x.base_addr + int.from_bytes(v[it+16:it+20], 'little') 23 | symbols = collections.OrderedDict() 24 | while any(v[p_lookup:p_lookup+4]): 25 | symbol_descr = x.base_addr + int.from_bytes(v[p_lookup:p_lookup+4], 'little') 26 | symbol_hint = int.from_bytes(v[symbol_descr:symbol_descr+2], 'little') 27 | symbol_name = read_string(x, v, symbol_descr + 2) 28 | symbols[symbol_name] = (symbol_hint, p_iat) 29 | p_lookup += 4 30 | p_iat += 4 31 | ans[CaseInsensitive(dllname)] = symbols 32 | it += 20 33 | return ans 34 | 35 | def build_iat(x, v, iat): 36 | strings = [b''] 37 | string_idxs = {} 38 | def make_string(s): 39 | string_idxs[s] = len(strings[0]) 40 | strings[0] += (s + '\0').encode('latin-1') 41 | dlls_size = 20 42 | symt_size = 0 43 | iat_size = 0 44 | for dllname, dll in iat.items(): 45 | dlls_size += 20 46 | symt_size += 4 47 | make_string(dllname) 48 | for name, (hint, old_iat) in dll.items(): 49 | if len(strings[0]) % 2: strings[0] += b'\0' 50 | make_string(hint.to_bytes(2, 'little').decode('latin-1') + name) 51 | symt_size += 4 52 | iat_size += 4 53 | strings = strings[0] 54 | strings += bytes((-len(strings)) % 4) 55 | addr, arr = v.alloc(dlls_size + symt_size + iat_size + len(strings), 0xc0000000, '.iat2') 56 | addr -= x.base_addr 57 | dlls_offset = 0 58 | symt_offset = dlls_offset + dlls_size 59 | iat_offset = symt_offset + symt_size 60 | strings_offset = addr + iat_offset + iat_size 61 | symbols = [] 62 | for dllname, dll in iat.items(): 63 | arr[dlls_offset:dlls_offset+4] = (addr + symt_offset).to_bytes(4, 'little') 64 | arr[dlls_offset+12:dlls_offset+16] = (strings_offset + string_idxs[dllname]).to_bytes(4, 'little') 65 | arr[dlls_offset+16:dlls_offset+20] = (addr + iat_offset).to_bytes(4, 'little') 66 | dlls_offset += 20 67 | for name, (hint, old_iat) in dll.items(): 68 | symbols.append((old_iat, x.base_addr + addr + iat_offset, dllname, name)) 69 | arr[symt_offset:symt_offset+4] = (strings_offset + string_idxs[hint.to_bytes(2, 'little').decode('latin-1')+name]).to_bytes(4, 'little') 70 | symt_offset += 4 71 | iat_offset += 4 72 | symt_offset += 4 73 | arr[strings_offset-addr:strings_offset-addr+len(strings)] = strings 74 | x.offset_table[1] = (addr + x.base_addr, len(strings) + strings_offset) 75 | return symbols 76 | 77 | def iatindir(x, v, add_symbols=[], deps={}): 78 | iat = parse_iat(x, v) 79 | for dllname, name in add_symbols: 80 | dllname = CaseInsensitive(dllname) 81 | if dllname not in iat: iat[dllname] = collections.OrderedDict() 82 | if name not in iat[dllname]: iat[dllname][name] = (0, None) 83 | syms = [j for i in iat.values() for j in i] 84 | syms_s = set(syms) 85 | for i in syms: 86 | if i not in deps: continue 87 | for dllname, name in deps[i]: 88 | dllname = CaseInsensitive(dllname) 89 | if dllname not in iat: iat[dllname] = collections.OrderedDict() 90 | if name not in iat[dllname]: iat[dllname][name] = (0, None) 91 | if name not in syms_s: 92 | syms_s.add(name) 93 | syms.append(name) 94 | return build_iat(x, v, iat) 95 | 96 | def main(): 97 | with open(sys.argv[1], 'rb') as file: x = pefile.PeFile(file.read()) 98 | v = vmem.VirtualMemory(x.sections, x.mem_align) 99 | symbols = iatindir(x, v) 100 | glue_len = 12 * len(symbols) + 5 101 | addr, arr = v.alloc(glue_len, 0x60000000, '.glue') 102 | glue = b'' 103 | for i, j, k, l in symbols: 104 | glue += b'\xff\x35'+j.to_bytes(4, 'little') 105 | glue += b'\x8f\x05'+i.to_bytes(4, 'little') 106 | glue += b'\xe9' 107 | entry = int.from_bytes(x.pe_header[40:44], 'little') + x.base_addr 108 | glue += (entry - (addr + glue_len)).to_bytes(4, 'little', signed=True) 109 | assert len(glue) == glue_len 110 | arr[:glue_len] = glue 111 | x.pe_header[40:44] = (addr - x.base_addr).to_bytes(4, 'little') 112 | x.pe_header[22] |= 1 113 | with open(sys.argv[2], 'wb') as file: file.write(x.to_bytes()) 114 | 115 | if __name__ == '__main__': 116 | main() 117 | -------------------------------------------------------------------------------- /indir.py: -------------------------------------------------------------------------------- 1 | import pefile, vmem 2 | 3 | def gen_indir_tables(x, v): 4 | total_code_sz = 0 5 | for i in x.sections: 6 | if i[3] & 0x20000000: 7 | total_code_sz += i[2] 8 | table_sz = total_code_sz * 4 9 | table, table_buf = v.alloc(table_sz, 0x40000000, '.indirb') 10 | ans = [] 11 | for i in x.sections: 12 | if i[3] & 0x20000000: 13 | start = i[1] 14 | end = i[1] + i[2] 15 | ans.append((start, end, table)) 16 | table += 4*(end-start) 17 | return (ans, table_buf) 18 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import pefile, vmem, iatindir, stubgen, recompiler, cc, indir, tlshooks, x87 2 | 3 | arm_crt = r''' 4 | asm("sys_write:\npush {r7, lr}\nmov r7, #4\nsvc #0\npop {r7, pc}"); 5 | 6 | int sys_write(int, const void*, int); 7 | 8 | void dbg_puts(const char* str) 9 | { 10 | int l = 0; 11 | while(str[l]) 12 | l++; 13 | sys_write(2, str, l); 14 | } 15 | 16 | void dbg_putn(unsigned int ii) 17 | { 18 | char c[9]; 19 | for(int i = 7; i >= 0; i--) 20 | { 21 | int x = ii & 15; 22 | ii >>= 4; 23 | if(x < 10) 24 | c[i] = x + '0'; 25 | else 26 | c[i] = x + 'a' - 10; 27 | } 28 | c[8] = 0; 29 | dbg_puts(c); 30 | } 31 | 32 | void emu_unsupported_c(const char* mnemonic) 33 | { 34 | dbg_puts("Unsupported instruction: "); 35 | dbg_puts(mnemonic); 36 | dbg_puts("\n"); 37 | *(void* volatile*)0; 38 | } 39 | 40 | void emu_fault(void) 41 | { 42 | dbg_puts("SIGSEGV while reading instruction.\n"); 43 | *(void* volatile*)0; 44 | } 45 | 46 | void emu_indir_invalid(unsigned int addr) 47 | { 48 | dbg_puts("Indirect branch to invalid address 0x"); 49 | dbg_putn(addr); 50 | dbg_puts(".\n"); 51 | *(void* volatile*)0; 52 | } 53 | 54 | void emu_trace_get_fpu_regs(unsigned int*); 55 | unsigned int emu_trace_get_fpu_fpscr(void); 56 | 57 | void emu_trace_c(unsigned int eip, unsigned int regs[12], int do_dump_fpu) 58 | { 59 | dbg_puts("eip="); 60 | dbg_putn(eip); 61 | #define D(x, y) dbg_puts(" " #x "="); dbg_putn(regs[y]) 62 | D(translated, 11); 63 | D(cpsr, 0); 64 | D(eax, 3); 65 | D(ecx, 4); 66 | D(edx, 5); 67 | D(ebx, 6); 68 | D(esp, 7); 69 | D(ebp, 8); 70 | D(esi, 9); 71 | D(edi, 10); 72 | #undef D 73 | if(do_dump_fpu) 74 | { 75 | unsigned int fpu_regs[20]; 76 | emu_trace_get_fpu_regs(fpu_regs); 77 | for(int i = 0; i < 10; i++) 78 | { 79 | if(i == do_dump_fpu) 80 | dbg_puts(" |"); 81 | char buf[5] = {' ', 'd', '0'+i, '=', 0}; 82 | dbg_puts(buf); 83 | dbg_putn(fpu_regs[2*i+1]); 84 | dbg_putn(fpu_regs[2*i]); 85 | } 86 | dbg_puts(" fpscr="); 87 | dbg_putn(emu_trace_get_fpu_fpscr()); 88 | } 89 | dbg_puts("\n"); 90 | } 91 | 92 | unsigned int emu_trace_callback_entry(unsigned int param) 93 | { 94 | dbg_puts("<<< callback\n"); 95 | return param; 96 | } 97 | 98 | void emu_trace_callback_exit(void) 99 | { 100 | dbg_puts(">>> callback\n"); 101 | } 102 | 103 | double emu_fsin_c(double d) 104 | { 105 | return ((double(*)(double))WINAPI::[sin])(d); 106 | } 107 | 108 | unsigned int emu_do_callback(void* fn, void* reserved, ...); 109 | void emu_callback_ret(void); 110 | unsigned int emu_call_native(void* param, unsigned int param_len, void* fn); 111 | 112 | void* emu_malloc(unsigned int sz) 113 | { 114 | //FIXME 115 | return ((void*(*)(void*, unsigned int, int, int))WINAPI::[VirtualAlloc])(0, sz, 0x1000, 0x40); 116 | } 117 | 118 | void emu_cacheflush(void* addr, unsigned int sz) 119 | { 120 | unsigned int handle = ((unsigned int(*)(void))WINAPI::[GetCurrentProcess])(); 121 | ((void(*)(unsigned int, void*, unsigned int))WINAPI::[FlushInstructionCache])(handle, addr, sz); 122 | } 123 | ''' 124 | 125 | def castrate(x): 126 | x.dos_stub[:] = b'MZ' + bytes(62) 127 | x.pe_header[8:20] = bytes(12) 128 | x.pe_header[26:40] = bytes(14) 129 | x.pe_header[44:52] = bytes(8) 130 | x.pe_header[66:72] = bytes(6) 131 | x.pe_header[74:80] = bytes(6) 132 | x.pe_header[94:116] = bytes(22) 133 | x.sections.sort(key=lambda x:x[1]) 134 | 135 | def process_winapi(code, import_map): 136 | sp = code.split('WINAPI::[') 137 | for i in range(1, len(sp)): 138 | x, y = sp[i].split(']', 1) 139 | sp[i] = '(*(void**)' + hex(import_map[x]) + ')' + y 140 | return ''.join(sp) 141 | 142 | def main(f1, f2, ex_symbols=None): 143 | with open(f1, 'rb') as file: 144 | x = pefile.PeFile(file.read()) 145 | ex_symbol_list = [] 146 | if ex_symbols != None: 147 | with open(ex_symbols) as file: 148 | for line in file: 149 | line = tuple(line.split('#', 1)[0].split()) 150 | if line: 151 | ex_symbol_list.append(line) 152 | v = vmem.VirtualMemory(x.sections, x.mem_align) 153 | imports = iatindir.iatindir(x, v, [('kernel32.dll', 'VirtualAlloc'), ('kernel32.dll', 'GetCurrentProcess'), ('kernel32.dll', 'FlushInstructionCache')]+x87.api_deps+ex_symbol_list, deps=stubgen.wrapper_deps) 154 | import_map = {l: j for i, j, k, l in imports} 155 | ex_syms = {i[1] for i in ex_symbol_list} 156 | imports = [i for i in imports if i[0] != None or i[3] in ex_syms] 157 | x86_stub, arm_stub, wrapper_names = stubgen.gen_decls('i686', {l: '*(void**)'+hex(j) for i, j, k, l in imports}) 158 | x86_stub += stubgen.gen_dlsymtab(ex_symbol_list) 159 | x86_code, x86_syms = cc.compile_x86(x86_stub, '', v.sim_alloc()) 160 | x86_addr, x86_buf = v.alloc(len(x86_code), 0x60000000, '.glue1') 161 | x86_buf[:len(x86_code)] = x86_code 162 | for i, j, k, l in imports: 163 | if i == None or l in stubgen.external_non_funcs: continue 164 | v[i:i+4] = (x86_addr + x86_syms['_'+l]).to_bytes(4, 'little') 165 | x86_entry = x.base_addr + int.from_bytes(x.pe_header[40:44], 'little') 166 | arm_tls_hook, x86_tls_callbacks = tlshooks.install_tls_hooks(x, v) 167 | indir_tbl, indir_buf = indir.gen_indir_tables(x, v) 168 | indir_buf_offset = 0 169 | preseed = [x86_entry] 170 | arm_asm, indir_bl = recompiler.transpile(x, v, preseed, x86_entry, wrapper_names) 171 | x.sections[:] = [(i, j, k, l&~0x20000000, m) for i, j, k, l, m in x.sections] 172 | arm_c_code = arm_crt+arm_stub+'\n' 173 | arm_c_code += 'unsigned int emu_dispatch_indir(unsigned int addr)\n{\n' 174 | arm_c_code += ' if(addr == 0x179)\n' 175 | arm_c_code += ' return 1 | (unsigned int)&emu_callback_ret;\n' 176 | arm_c_code += ' unsigned int ans = 0;\n' 177 | for i, j, k in indir_tbl: 178 | arm_c_code += ' if(addr >= '+hex(i)+'u && addr <= '+hex(j)+'u)\n' 179 | arm_c_code += ' ans = ((volatile unsigned int*)'+hex(k)+')[addr - '+hex(i)+'u];\n' 180 | arm_c_code += ' if(ans == 0)\n' 181 | arm_c_code += ' emu_indir_invalid(addr);\n' 182 | arm_c_code += ' return ans;\n' 183 | arm_c_code += '}\n' 184 | arm_c_code += 'void iat_fixup(void)\n{\n' 185 | for i, j, k, l in imports: 186 | if l in stubgen.external_non_funcs: 187 | arm_c_code += ' *(void* volatile*)0x%x = (void*)0x%x;\n'%(i, j) 188 | arm_c_code += '}\n' 189 | arm_c_code += 'void emu_tls_hook(void* handle, unsigned int reason, void* reserved)\n{\n' 190 | arm_c_code += ' if(reason == 1)\n' 191 | arm_c_code += ' iat_fixup();\n' 192 | for i in x86_tls_callbacks: 193 | arm_c_code += ' emu_do_callback((void*)'+hex(i)+', 0, handle, reason, reserved);\n' 194 | arm_c_code += '}\n' 195 | arm_c_code = process_winapi(arm_c_code, import_map) 196 | arm_code, arm_syms = cc.compile_arm(arm_c_code, arm_asm, v.sim_alloc()) 197 | arm_addr, arm_buf = v.alloc(len(arm_code), 0x60000000, '.glue2') 198 | arm_buf[:len(arm_code)] = arm_code 199 | for i, j, k in indir_tbl: 200 | for l in range(i, j): 201 | if l not in indir_bl and 'x86_%x'%l in arm_syms: 202 | indir_buf[indir_buf_offset:indir_buf_offset+4] = (arm_addr + arm_syms['x86_%x'%l] | 1).to_bytes(4, 'little') 203 | indir_buf_offset += 4 204 | x.pe_header[4:6] = b'\xc4\x01' 205 | x.pe_header[40:44] = ((arm_addr + arm_syms['emu_entry'] | 1) - x.base_addr).to_bytes(4, 'little') 206 | v[arm_tls_hook:arm_tls_hook+4] = (arm_addr + arm_syms['emu_tls_hook'] | 1).to_bytes(4, 'little') 207 | castrate(x) 208 | with open(f2, 'wb') as file: 209 | file.write(x.to_bytes()) 210 | 211 | if __name__ == '__main__': 212 | import sys 213 | main(*sys.argv[1:]) 214 | -------------------------------------------------------------------------------- /pefile.py: -------------------------------------------------------------------------------- 1 | class PeFile: 2 | def __init__(self, data): 3 | assert data[:2] == b'MZ' 4 | pe_offset = int.from_bytes(data[60:64], 'little') 5 | assert data[pe_offset:pe_offset+2] == b'PE' 6 | self.dos_stub = bytearray(data[:pe_offset]) 7 | self.pe_header = bytearray(data[pe_offset:pe_offset+120]) 8 | noffsets = int.from_bytes(self.pe_header[116:120], 'little') 9 | nsections = int.from_bytes(self.pe_header[6:8], 'little') 10 | self.base_addr = base_addr = int.from_bytes(self.pe_header[52:56], 'little') 11 | self.mem_align = int.from_bytes(self.pe_header[56:60], 'little') 12 | self.offset_table = [] 13 | for i in range(noffsets): 14 | t = data[pe_offset+120+i*8:pe_offset+128+i*8] 15 | rva = int.from_bytes(t[:4], 'little') 16 | l = int.from_bytes(t[4:], 'little') 17 | if l == 0: 18 | self.offset_table.append(None) 19 | elif i == 4: 20 | self.offset_table.append(data[rva:rva+l]) 21 | else: 22 | self.offset_table.append((rva + base_addr, l)) 23 | self.sections = [] 24 | for i in range(nsections): 25 | t = data[pe_offset+120+noffsets*8+i*40:pe_offset+160+noffsets*8+i*40] 26 | name = t[:8].decode('latin-1') 27 | while name[-1:] == '\0': name = name[:-1] 28 | memsz = int.from_bytes(t[8:12], 'little') 29 | rva = int.from_bytes(t[12:16], 'little') 30 | filesz = int.from_bytes(t[16:20], 'little') 31 | offset = int.from_bytes(t[20:24], 'little') 32 | flags = int.from_bytes(t[36:40], 'little') 33 | self.sections.append((name, base_addr+rva, memsz, flags, bytearray(data[offset:offset+filesz]))) 34 | @classmethod 35 | def from_bytes(self, data): 36 | return self(data) 37 | def to_bytes(self): 38 | self.dos_stub[60:64] = len(self.dos_stub).to_bytes(4, 'little') 39 | ans = bytearray(self.dos_stub) 40 | self.base_addr = base_addr = int.from_bytes(self.pe_header[52:56], 'little') 41 | self.mem_align = int.from_bytes(self.pe_header[56:60], 'little') 42 | file_align = int.from_bytes(self.pe_header[60:64], 'little') 43 | self.pe_header[88:92] = bytes(4) # checksum 44 | self.pe_header[6:8] = len(self.sections).to_bytes(2, 'little') 45 | self.pe_header[116:120] = len(self.offset_table).to_bytes(4, 'little') 46 | self.pe_header[80:84] = (max(i[1] + i[2] for i in self.sections) - base_addr).to_bytes(4, 'little') 47 | ans += self.pe_header 48 | o_o = [] 49 | for i in self.offset_table: 50 | if i == None: 51 | ans += bytes(8) 52 | elif isinstance(i, bytes): 53 | o_o.append(len(ans)) 54 | ans += bytes(4) + len(i).to_bytes(4, 'little') 55 | else: 56 | va, l = i 57 | ans += (va - base_addr).to_bytes(4, 'little') + l.to_bytes(4, 'little') 58 | sz = len(ans) + 40 * len(self.sections) 59 | for name, va, memsz, flags, data in self.sections: 60 | rva = va - base_addr 61 | filesz = len(data) 62 | sz += (-sz) % file_align 63 | sz += (-sz) % 4096 64 | offset = sz 65 | sz += filesz + (-filesz) % 4096 66 | ans += name.encode('latin-1') + bytes(8 - len(name)) 67 | ans += memsz.to_bytes(4, 'little') 68 | ans += rva.to_bytes(4, 'little') 69 | ans += filesz.to_bytes(4, 'little') 70 | ans += offset.to_bytes(4, 'little') 71 | ans += bytes(12) 72 | ans += flags.to_bytes(4, 'little') 73 | for name, va, memsz, flags, data in self.sections: 74 | ans += bytes((-len(ans)) % file_align) 75 | ans += bytes((-len(ans)) % 4096) 76 | ans += data 77 | ans += bytes((-len(ans)) % 4096) 78 | o_o = iter(o_o) 79 | for i in self.offset_table: 80 | if isinstance(i, bytes): 81 | o = next(o_o) 82 | ans += bytes((-len(ans)) % 16) 83 | ans[o:o+4] = len(ans).to_bytes(4, 'little') 84 | ans += i 85 | return bytes(ans) 86 | -------------------------------------------------------------------------------- /recompiler.py: -------------------------------------------------------------------------------- 1 | import capstone, pefile, vmem, io, json, collections, cfstyle, traceback, x87, sys 2 | 3 | TRACE = True 4 | TRACEBACKS = True 5 | 6 | def read_instr_at(cs, v, addr): 7 | addr0 = addr 8 | b = b'' 9 | while len(b) <= 16: 10 | try: return next(cs.disasm(b, addr0)) 11 | except StopIteration: pass 12 | try: b += bytes((v[addr],)) 13 | except IndexError: return None 14 | addr += 1 15 | return None 16 | 17 | cf_add_style_instrs = ('add', 'lock add', 'lock xadd', 'adc') 18 | cf_sub_style_instrs = ('sub', 'cmp', 'lock cmpxchg', 'neg') 19 | cf_xor_style_instrs = ('xor', 'and', 'test', 'or') 20 | cf_shift_style_instrs = ('shl', 'sal', 'shr', 'sar', 'ror') 21 | cf_fuckup_style_instrs = ('idiv', 'div') 22 | cf_bt_style_instrs = ('bt',) 23 | cf_nyi_style_instrs = ('rol',) 24 | CF_FUCKUP = {'bt', 'fstsw', 'fpu_emu', 'nyi'} 25 | cf_ccodes = {'a', 'b', 'ae', 'be', 'g', 'l', 'ge', 'le'} 26 | 27 | def translate_cf_style(cur_cf_style, instr, op_str): 28 | if instr in cf_add_style_instrs: 29 | cur_cf_style = 'add' 30 | elif instr in cf_sub_style_instrs: 31 | cur_cf_style = 'sub' 32 | elif instr in cf_xor_style_instrs: 33 | cur_cf_style = 'xor' 34 | elif instr in ('mul', 'imul'): 35 | cur_cf_style = 'imul' 36 | elif instr in cf_shift_style_instrs: 37 | cur_cf_style = 'shift' 38 | elif instr in cf_fuckup_style_instrs: 39 | cur_cf_style = 'noone' 40 | elif instr in cf_bt_style_instrs: 41 | cur_cf_style = 'bt' 42 | elif instr in cf_nyi_style_instrs: 43 | cur_cf_style = 'nyi' 44 | elif instr in ('inc', 'dec', 'lock inc', 'lock dec'): 45 | cur_cf_style = 'inc' 46 | elif instr in ('fist', 'fistp') and op_str.startswith('word ptr ['): 47 | cur_cf_style = 'fpu_emu' 48 | elif instr in ('jmp', 'call', 'ret', 'bnd ret'): 49 | cur_cf_style = 'noone' 50 | return cur_cf_style 51 | 52 | def get_labels(cs, x, v, preseed0): 53 | preseed = [] 54 | for i in x.sections: 55 | if i[3] & 0x20000000: # executable 56 | preseed.append(i[1]) # vma 57 | preseed.extend(preseed0) 58 | preseed.sort() 59 | preseed1 = list(preseed) 60 | preseed_set = set(preseed) 61 | labels = set() 62 | label_list = [] 63 | cf_uses = set() 64 | ef_uses = set() 65 | cfg = collections.defaultdict(list) 66 | d_cfg = collections.defaultdict(list) 67 | b_cfg = collections.defaultdict(list) 68 | instrs = {} 69 | cf_styles = {} 70 | for i in preseed: 71 | cur_cf_style = 'none' 72 | while i not in labels: 73 | if len(labels) % 1000 == 0: 74 | report_progress(len(labels)) 75 | iii = i 76 | labels.add(i) 77 | label_list.append(i) 78 | ii = read_instr_at(cs, v, i) 79 | if ii == None: break 80 | cf_styles[i] = cur_cf_style 81 | instrs[i] = (ii.mnemonic, ii.op_str) 82 | i += len(ii.bytes) 83 | if ((ii.mnemonic[0] == 'j' or ii.mnemonic.startswith('bnd j')) and ii.mnemonic not in ('jmp', 'bnd jmp')) or ii.mnemonic.startswith('set') or ii.mnemonic.startswith('cmov'): 84 | ef_uses.add(iii) 85 | if ii.mnemonic in ('adc', 'sbb'): cf_uses.add(iii) 86 | if (ii.mnemonic[0] == 'j' or ii.mnemonic.startswith('bnd j') or ii.mnemonic == 'call') and ii.op_str.startswith('0x'): 87 | if ii.mnemonic[0] == 'j' and ii.mnemonic[1:] in cf_ccodes: cf_uses.add(iii) 88 | if ii.mnemonic.startswith('bnd j') and ii.mnemonic[5:] in cf_ccodes: cf_uses.add(iii) 89 | dst = int(ii.op_str, 16) 90 | if dst not in preseed_set: 91 | preseed.append(dst) 92 | preseed_set.add(dst) 93 | if ii.mnemonic.startswith('cmov') and ii.mnemonic[4:] in cf_ccodes: cf_uses.add(iii) 94 | if ii.mnemonic in cf_add_style_instrs: 95 | cur_cf_style = 'add' 96 | elif ii.mnemonic in cf_sub_style_instrs: 97 | cur_cf_style = 'sub' 98 | elif ii.mnemonic in cf_xor_style_instrs: 99 | cur_cf_style = 'xor' 100 | elif ii.mnemonic in ('mul', 'imul'): 101 | cur_cf_style = 'imul' 102 | elif ii.mnemonic in cf_shift_style_instrs: 103 | cur_cf_style = 'shift' 104 | elif ii.mnemonic in cf_fuckup_style_instrs: 105 | cur_cf_style = 'noone' 106 | elif ii.mnemonic in cf_bt_style_instrs: 107 | cur_cf_style = 'bt' 108 | elif ii.mnemonic in cf_nyi_style_instrs: 109 | cur_cf_style = 'nyi' 110 | elif ii.mnemonic in ('inc', 'dec', 'lock inc', 'lock dec'): 111 | cur_cf_style = 'inc' 112 | elif ii.mnemonic in ('fist', 'fistp') and ii.op_str.startswith('word ptr ['): 113 | cur_cf_style = 'fpu_emu' 114 | # cutting the CFG on CF-style changes is intentional 115 | elif ii.mnemonic in ('jmp', 'call'): 116 | try: dst = int(ii.op_str, 16) 117 | except ValueError: pass 118 | else: cfg[dst].append(iii) 119 | cur_cf_style = 'noone' 120 | elif ii.mnemonic.startswith('j'): 121 | dst = int(ii.op_str, 16) 122 | cfg[dst].append(iii) 123 | cfg[i].append(iii) 124 | elif ii.mnemonic in ('ret', 'bnd ret'): 125 | cur_cf_style = 'noone' 126 | else: 127 | cfg[i].append(iii) 128 | if ii.mnemonic in ('jmp', 'call'): 129 | try: dst = int(ii.op_str, 16) 130 | except ValueError: pass 131 | else: 132 | d_cfg[iii].append(dst) 133 | b_cfg[iii].append(i) 134 | elif ii.mnemonic.startswith('j'): 135 | dst = int(ii.op_str, 16) 136 | d_cfg[iii].append(dst) 137 | d_cfg[iii].append(i) 138 | elif ii.mnemonic in ('ret', 'bnd ret'): 139 | b_cfg[iii].append(i) 140 | else: 141 | d_cfg[iii].append(i) 142 | #cf_styles = x87.calculate_states(d_cfg, b_cfg, instrs, preseed1, translate_cf_style, 'noone') 143 | return label_list, cfstyle.calculate_cf_style(cfg, cf_uses, ef_uses, cf_styles), x87.calculate_fpu_states(d_cfg, b_cfg, instrs, preseed1) 144 | 145 | arm_crt = '''\ 146 | .p2align 2 147 | .extern emu_unsupported_c 148 | .extern emu_dispatch_indir 149 | .global emu_do_callback 150 | .global emu_callback_ret 151 | 152 | emu_unsupported: 153 | mov r0, lr 154 | sub r0, r0, #1 155 | b emu_unsupported_c 156 | 157 | emu_do_callback: 158 | push {r2, r3} 159 | ldr r2, =0x179 160 | push {r2} 161 | mov r2, r4 162 | mov r3, lr 163 | ldr r4, =16383 164 | bl __chkstk 165 | mov lr, r3 166 | mov r4, r2 167 | mov r2, sp 168 | ldr r3, =65532 169 | sub r3, r2, r3 170 | mov sp, r3 171 | push {r1, r4, r5, r6, r7, r8, r9, r10, r11, lr} 172 | mov r8, r2 173 | bl emu_dispatch_indir 174 | bl emu_trace_callback_entry //debug 175 | bx r0 176 | emu_callback_ret: 177 | bl emu_trace_callback_exit //debug 178 | mov r0, r4 179 | pop {r1, r4, r5, r6, r7, r8, r9, r10, r11, lr} 180 | mov r2, sp 181 | ldr r3, =65544 182 | add r2, r3 183 | mov sp, r2 184 | bx lr 185 | 186 | emu_call_native: 187 | push {fp, lr} 188 | mov fp, sp 189 | sub sp, r1 190 | sub sp, #16 191 | mov r3, sp 192 | tst r3, #7 193 | beq 1f 194 | sub sp, #4 195 | 1: 196 | cbz r1, 3f 197 | mov r3, sp 198 | add r1, r3 199 | 2: 200 | ldr ip, [r0], #4 201 | str ip, [r3], #4 202 | cmp r1, r3 203 | bne 2b 204 | 3: 205 | mov ip, r2 206 | pop {r0, r1, r2, r3} 207 | blx ip 208 | mov sp, fp 209 | pop {fp, pc} 210 | 211 | emu_entry: 212 | //bl iat_fixup //XXX 213 | ldr r4, =16384 214 | bl __chkstk 215 | mov r0, sp 216 | mov r8, r0 217 | sub r0, r0, #65536 218 | mov sp, r0 219 | bl ENTRY 220 | 221 | emu_rep_stosd: 222 | str r4, [r11], #4 223 | sub r5, r5, #1 224 | cbz r5, 1f 225 | b emu_rep_stosd 226 | 1: 227 | bx lr 228 | 229 | emu_rep_movsd: 230 | ldr r0, [r10], #4 231 | str r0, [r11], #4 232 | sub r5, r5, #1 233 | cbz r5, 1f 234 | b emu_rep_stosd 235 | 1: 236 | bx lr 237 | 238 | emu_lock_cmpxchg: 239 | //r0 = lea dst 240 | //r1 = src 241 | ldrex r2, [r0] 242 | cmp r4, r2 243 | it eq 244 | moveq r2, r1 245 | strex r3, r2, [r0] 246 | cbz r3, 1f 247 | b emu_lock_cmpxchg 248 | 1: 249 | it ne 250 | movne r4, r2 251 | bx lr 252 | 253 | emu_idiv: 254 | //r0 = divisor 255 | bl emu_unsupported 256 | .ascii "idiv 64\\0" 257 | 258 | emu_div: 259 | //r0 = divisor 260 | tst r0, r0 261 | beq emu_sigfpe 262 | mov r1, #65536 263 | vmov s18, r1 264 | vcvt.f64.u32 d9, s18 265 | vmul.f64 d9, d9, d9 266 | vmov s22, r6 267 | vcvt.f64.u32 d11, s22 268 | vmul.f64 d11, d11, d9 269 | vmov s18, r4 270 | vcvt.f64.u32 d9, s18 271 | vadd.f64 d9, d9, d11 272 | vmov s22, r0 273 | vcvt.f64.u32 d11, s22 274 | vdiv.f64 d9, d9, d11 275 | vcvt.u32.f64 s18, d9 276 | vmov r1, s18 277 | umull r2, r3, r0, r1 278 | subs r2, r2, r4 279 | sbcs r3, r3, r6 280 | bcs emu_div_inc 281 | adds r2, r2, r0 282 | adcs r3, r3, #0 283 | bcs emu_div_not_dec 284 | emu_div_inc: 285 | add r1, r1, #2 286 | emu_div_dec: 287 | sub r1, r1, #1 288 | emu_div_not_dec: 289 | umull r2, r3, r0, r1 290 | subs r2, r4, r2 291 | sbcs r3, r6, r3 292 | bne emu_sigfpe 293 | cmp r2, r0 294 | bcs emu_sigfpe 295 | mov r6, r2 296 | mov r4, r1 297 | bx lr 298 | 299 | emu_sigfpe: 300 | mov r0, #0 301 | udiv r0, r0, r0 302 | 303 | emu_load_xword: 304 | //r0 = tbyte ptr 305 | //d9 = ans 306 | ldr r1, [r0, #2] 307 | ldr r2, [r0, #6] 308 | mov r3, r2 309 | and r3, #0xc0000000 310 | lsl r2, #6 311 | lsr r2, #2 312 | lsr ip, r1, #28 313 | orr r2, ip 314 | lsl r1, #4 315 | orr r2, r3 316 | vmov d9, r1, r2 317 | bx lr 318 | 319 | emu_store_xword: 320 | //r0 = tbyte ptr 321 | //d9 = ans 322 | vmov r1, r2, d9 323 | lsr r1, #4 324 | lsl ip, r2, #28 325 | orr r1, ip 326 | mov r3, r2 327 | and r3, #0x80000000 328 | eor r2, #0x40000000 329 | lsl r2, #1 330 | asr r2, #4 331 | lsr r2, #1 332 | eor r2, #0x40000000 333 | orr r2, r3 334 | str r2, [r0, #6] 335 | str r1, [r0, #2] 336 | bx lr 337 | 338 | emu_trace: 339 | mrs r1, cpsr 340 | push {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} 341 | mov r1, sp 342 | bl emu_trace_c 343 | pop {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} 344 | msr cpsr_f, r1 345 | bx lr 346 | 347 | emu_trace_get_fpu_regs: 348 | vstr d0, [r0] 349 | vstr d1, [r0, #8] 350 | vstr d2, [r0, #16] 351 | vstr d3, [r0, #24] 352 | vstr d4, [r0, #32] 353 | vstr d5, [r0, #40] 354 | vstr d6, [r0, #48] 355 | vstr d7, [r0, #56] 356 | vstr d8, [r0, #64] 357 | vstr d9, [r0, #72] 358 | bx lr 359 | 360 | emu_trace_get_fpu_fpscr: 361 | vmrs r0, fpscr 362 | bx lr 363 | 364 | //fpu sw layout: busy C3 t_o_p C2 C1 C0 sum stack inexact undf ovf /0 denormal_operand invalid 365 | 366 | emu_fxam: 367 | //d10 = arg 368 | //r0 = (fake sw) << 16 369 | mov r0, #0 370 | vmov r2, r3, d10 371 | //C1 = sign 372 | tst r3, #0x80000000 373 | beq emu_fxam_nonneg 374 | orr r0, #0x02000000 375 | eor r3, #0x80000000 376 | emu_fxam_nonneg: 377 | // zero? 378 | orrs r2, r3 379 | beq emu_fxam_zero 380 | // nan? 381 | ldr r2, =0x7ff00000 382 | cmp r3, r2 383 | bhi emu_fxam_nan 384 | // inf? 385 | beq emu_fxam_inf 386 | // denorm? 387 | cmp r3, #0x00100000 388 | bcc emu_fxam_denorm 389 | // probably normal 390 | orr r0, #0x04000000 391 | bx lr 392 | emu_fxam_nan: 393 | orr r0, #0x01000000 394 | bx lr 395 | emu_fxam_inf: 396 | orr r0, #0x05000000 397 | bx lr 398 | emu_fxam_zero: 399 | orr r0, #0x40000000 400 | bx lr 401 | emu_fxam_denorm: 402 | orr r0, #0x44000000 403 | bx lr 404 | 405 | emu_fsin: 406 | //d9 = arg 407 | //d9 = ans 408 | push {fp, lr} 409 | mrs fp, cpsr 410 | vmov r0, r1, d0 411 | push {r0, r1} 412 | vmov r0, r1, d1 413 | push {r0, r1} 414 | vmov r0, r1, d2 415 | push {r0, r1} 416 | vmov r0, r1, d3 417 | push {r0, r1} 418 | vmov r0, r1, d4 419 | push {r0, r1} 420 | vmov r0, r1, d5 421 | push {r0, r1} 422 | vmov r0, r1, d6 423 | push {r0, r1} 424 | vmov r0, r1, d7 425 | push {r0, r1} 426 | vmov r0, r1, d8 427 | push {r0, r1} 428 | vmov r0, r1, d10 429 | push {r0, r1} 430 | vmov d0, d9 431 | bl emu_fsin_c 432 | vmov d9, d0 433 | pop {r0, r1} 434 | vmov d10, r0, r1 435 | pop {r0, r1} 436 | vmov d8, r0, r1 437 | pop {r0, r1} 438 | vmov d7, r0, r1 439 | pop {r0, r1} 440 | vmov d6, r0, r1 441 | pop {r0, r1} 442 | vmov d5, r0, r1 443 | pop {r0, r1} 444 | vmov d4, r0, r1 445 | pop {r0, r1} 446 | vmov d3, r0, r1 447 | pop {r0, r1} 448 | vmov d2, r0, r1 449 | pop {r0, r1} 450 | vmov d1, r0, r1 451 | pop {r0, r1} 452 | vmov d0, r0, r1 453 | msr cpsr_f, fp 454 | pop {fp, pc} 455 | 456 | emu_fstcw: 457 | //r0 = cw << 16 458 | vmrs r1, fpscr 459 | mov r2, #0x003c0000 460 | and r0, r2, r1, lsl #9 461 | mov r2, #0x0c000000 462 | and r2, r2, r1, lsl #2 463 | orr r0, r2 464 | orr r0, #0x03000000 465 | bx lr 466 | 467 | emu_fldcw: 468 | //r0 = cw << 16 469 | vmrs r1, fpscr 470 | ldr r2, =0xff3fe1ff 471 | and r1, r2 472 | mov r2, #0x00001e00 473 | and r2, r2, r1, lsr #9 474 | orr r1, r2 475 | mov r2, #0x00c00000 476 | and r2, r2, r1, lsr #2 477 | orr r1, r2 478 | vmsr fpscr, r1 479 | bx lr 480 | 481 | emu_fist64: 482 | push {r4, lr} 483 | mov r4, r0 484 | mrs ip, cpsr 485 | vmov r0, r1, d9 486 | lsl r1, #1 487 | lsr r1, #1 488 | ldr r2, =0x43e00000 489 | cmp r1, r2 490 | bcs emu_fist64_overflow 491 | ldr r2, =0x08000000 492 | cmp r1, r2 493 | bcc emu_fist64_zero 494 | vmov r0, r1, d9 495 | sub r1, #0x02000000 496 | vmov d11, r0, r1 497 | vcvt.s32.f64 s22, d11 498 | vmov r3, s22 499 | vcvt.f64.s32 d11, s22 500 | vmov r0, r1, d11 501 | add r1, #0x02000000 502 | vmov d11, r0, r1 503 | vsub.f64 d9, d11 504 | vmov r0, r1, d9 505 | sub r1, #0x01000000 506 | vmov d11, r0, r1 507 | vcvt.s32.f64 s22, d11 508 | vmov r2, s22 509 | add r3, r3, r2, asr #16 510 | lsl r2, #16 511 | vcvt.f64.s32 d11, s22 512 | vmov r0, r1, d11 513 | add r1, #0x01000000 514 | vmov d11, r0, r1 515 | vsub.f64 d9, d11 516 | tst r4, r4 517 | it eq 518 | vcvteq.s32.f64 s18, d9 519 | it ne 520 | vcvtrne.s32.f64 s18, d9 521 | vmov r0, s18 522 | asr r1, r0, #31 523 | adds r0, r2 524 | adc r1, r3 525 | msr cpsr_f, ip 526 | pop {r4, pc} 527 | emu_fist64_overflow: 528 | mov r0, #0 529 | mov r1, #0x80000000 530 | msr cpsr_f, ip 531 | pop {r4, pc} 532 | emu_fist64_zero: 533 | mov r0, #0 534 | mov r1, #0 535 | msr cpsr_f, ip 536 | pop {r4, pc} 537 | 538 | __chkstk: 539 | cbz r4, 3f 540 | lsl r4, r4, #2 541 | sub r12, sp, r4 542 | sub r4, sp, #2048 543 | sub r4, #2048 544 | cmp r4, r12 545 | bcc 2f 546 | 1: 547 | str r4, [r4] 548 | sub r4, #2048 549 | sub r4, #2048 550 | cmp r4, r12 551 | bcs 1b 552 | 2: 553 | str r12, [r12] 554 | sub r4, sp, r12 555 | 3: 556 | bx lr 557 | ''' 558 | 559 | if not TRACE: 560 | arm_crt = '\n'.join(i for i in arm_crt.split('\n') if not i.endswith('//debug')) 561 | 562 | def asciz(s): 563 | s += '\0' 564 | while len(s) % 4: s += '\0' 565 | return '.ascii '+json.dumps(s).replace('\\u00', '\\x') 566 | 567 | reg_mapping = { 568 | 'eax': 'r4', 569 | 'ecx': 'r5', 570 | 'edx': 'r6', 571 | 'ebx': 'r7', 572 | 'esp': 'r8', 573 | 'ebp': 'r9', 574 | 'esi': 'r10', 575 | 'edi': 'r11', 576 | } 577 | 578 | regs_16bit = {'ax', 'cx', 'dx', 'bx', 'sp', 'bp', 'si', 'di'} 579 | regs_8bit = {'al', 'ah', 'cl', 'ch', 'dl', 'dh', 'bl', 'bh'} 580 | 581 | def guess_bitness(instr): 582 | instr = instr.replace(',', ' , ') 583 | if 'byte' in instr.split() or set(instr.split()) & regs_8bit: return 8 584 | elif 'word' in instr.split() or set(instr.split()) & regs_16bit: return 16 585 | else: return 32 586 | 587 | def report_progress(compiled, total=0, fail=0, last_s=['']): 588 | if compiled < 0: 589 | s = '' 590 | elif total > 0: 591 | s = '%f%%, %d of %d instructions compiled (%d fails)'%(100 * compiled / total, compiled, total, fail) 592 | else: 593 | s = 'get_labels: %d instructions processed'%compiled 594 | s2 = s + ' '*(len(last_s[0]) - len(s)) 595 | last_s[0] = s 596 | print(s2, end='\r', file=sys.stderr) 597 | 598 | def read_mem(f, addr, ldrop): # TODO: rename to rw_mem 599 | log2 = {1: 0, 2: 1, 4: 2, 8: 3} 600 | try: addr = int(addr, 16) 601 | except ValueError: pass 602 | else: 603 | print('ldr r2, =0x%x'%addr, file=f) 604 | print(ldrop+', [r2]', file=f) 605 | return 606 | if addr in reg_mapping: 607 | print('%s, [%s]'%(ldrop, reg_mapping[addr]), file=f) 608 | return 609 | addr = addr.replace(' - ', ' + -') 610 | if addr.count(' + ') == 2: 611 | left, middle, right = addr.split(' + ') 612 | right = int(right, 0) 613 | if '*' in middle: 614 | middle, scale = middle.split('*') 615 | scale = log2[int(scale)] 616 | print('add r2, %s, %s, lsl %d'%(reg_mapping[left], reg_mapping[middle], scale), file=f) 617 | else: 618 | print('add r2, %s, %s'%(reg_mapping[left], reg_mapping[middle]), file=f) 619 | if right in range(-255, 256) and not (ldrop.startswith('v') and right % 4): 620 | print('%s, [r2, #%s]'%(ldrop, hex(right)), file=f) 621 | else: 622 | print('ldr r3, ='+hex(right), file=f) 623 | print('add r2, r3', file=f) 624 | print(ldrop+', [r2]', file=f) 625 | return 626 | if ' + ' in addr: 627 | left, right = addr.split(' + ', 1) 628 | if left in reg_mapping: 629 | try: right = int(right, 0) 630 | except ValueError: pass 631 | else: 632 | if right in range(-255, 256) and not (ldrop.startswith('v') and right % 4): 633 | print(ldrop+', [%s, #%s]'%(reg_mapping[left], hex(right)), file=f) 634 | else: 635 | print('ldr r2, ='+hex(right), file=f) 636 | print('add r2, r2, '+reg_mapping[left], file=f) 637 | print(ldrop+', [r2]', file=f) 638 | return 639 | try: 640 | lea(f, addr, 'r2') 641 | print(ldrop+', [r2]', file=f) 642 | except AssertionError as e: 643 | assert False, "read_mem fallback failed: %s"%e 644 | 645 | def read_tls(f, addr, reg): 646 | try: addr = int(addr, 16) 647 | except ValueError: pass 648 | else: 649 | print('mrc p15, #0, %s, c13, c0, #2'%reg, file=f) 650 | if addr in range(-255, 256): 651 | print('ldr %s, [%s, #%s]'%(reg, reg, hex(addr)), file=f) 652 | else: 653 | print('ldr r2, =%s'%hex(addr), file=f) 654 | print('ldr %s, [%s, r2]'%(reg, reg), file=f) 655 | 656 | def write_tls(f, addr, reg): 657 | try: addr = int(addr, 16) 658 | except ValueError: pass 659 | else: 660 | print('mrc p15, #0, r2, c13, c0, #2', file=f) 661 | if addr in range(-255, 256): 662 | print('str %s, [r2, #%s]'%(reg, hex(addr)), file=f) 663 | else: 664 | print('ldr r3, =%s'%hex(addr), file=f) 665 | print('str %s, [r2, r3]'%reg, file=f) 666 | 667 | def write_mem(f, addr, strop): 668 | read_mem(f, addr, ldrop=strop) 669 | 670 | def read_arg(f, arg, reg, fake=False, bitness=32): 671 | try: arg = int(arg, 16) 672 | except ValueError: pass 673 | else: 674 | arg <<= (32 - bitness) 675 | arg &= 0xffffffff 676 | if arg in range(-255, 256): 677 | return '#0x%x'%arg 678 | else: 679 | print('ldr %s, =0x%x'%(reg, arg), file=f) 680 | return reg 681 | if arg in reg_mapping: return reg_mapping[arg] 682 | if fake: return reg 683 | if arg.startswith('dword ptr ['): 684 | read_mem(f, arg.split('[', 1)[1].split(']', 1)[0], 'ldr '+reg) 685 | return reg 686 | if arg.startswith('dword ptr fs:['): 687 | read_tls(f, arg.split('[', 1)[1].split(']', 1)[0], reg) 688 | return reg 689 | if arg.startswith('word ptr ['): 690 | read_mem(f, arg.split('[', 1)[1].split(']', 1)[0], 'ldrh '+reg) 691 | print('lsl %s, #16'%reg, file=f) 692 | return reg 693 | if arg.startswith('byte ptr ['): 694 | read_mem(f, arg.split('[', 1)[1].split(']', 1)[0], 'ldrb '+reg) 695 | print('lsl %s, #24'%reg, file=f) 696 | return reg 697 | if arg in regs_8bit: 698 | big_reg = reg_mapping['e' + arg[0] + 'x'] 699 | if arg[1] == 'l': 700 | print('lsl %s, %s, #24'%(reg, big_reg), file=f) 701 | else: 702 | print('lsr %s, %s, #8'%(reg, big_reg), file=f) 703 | print('lsl %s, #24'%reg, file=f) 704 | return reg 705 | if arg in regs_16bit: 706 | big_reg = reg_mapping['e' + arg] 707 | print('lsl %s, %s, #16'%(reg, big_reg), file=f) 708 | return reg 709 | assert False, "read_arg failed" 710 | 711 | def write_arg(f, arg, reg): 712 | if arg.startswith('dword ptr ['): 713 | write_mem(f, arg.split('[', 1)[1].split(']', 1)[0], 'str '+reg) 714 | return 715 | if arg.startswith('dword ptr fs:['): 716 | write_tls(f, arg.split('[', 1)[1].split(']', 1)[0], reg) 717 | return 718 | if arg.startswith('word ptr ['): 719 | print('lsr %s, %s, #16'%(reg, reg), file=f) 720 | write_mem(f, arg.split('[', 1)[1].split(']', 1)[0], 'strh '+reg) 721 | return 722 | if arg.startswith('byte ptr ['): 723 | print('lsr %s, %s, #24'%(reg, reg), file=f) 724 | write_mem(f, arg.split('[', 1)[1].split(']', 1)[0], 'strb '+reg) 725 | return 726 | if '[' in arg: 727 | assert False, "write_arg failed (unknown memory addressing mode)" 728 | if arg in regs_8bit: 729 | assert reg in ('r0', 'r1') 730 | big_reg = reg_mapping['e' + arg[0] + 'x'] 731 | if arg[1] == 'l': 732 | print('lsr %s, #24'%reg, file=f) 733 | print('ldr r2, =0xffffff00', file=f) 734 | else: 735 | print('lsr %s, #16'%reg, file=f) 736 | print('ldr r2, =0xffff00ff', file=f) 737 | print('and %s, r2'%big_reg, file=f) 738 | print('orr %s, %s'%(big_reg, reg), file=f) 739 | return 740 | if arg in regs_16bit: 741 | assert reg in ('r0', 'r1') 742 | big_reg = reg_mapping['e' + arg] 743 | print('lsr %s, #16'%reg, file=f) 744 | print('ldr r2, =0xffff0000', file=f) 745 | print('and %s, r2'%big_reg, file=f) 746 | print('orr %s, %s'%(big_reg, reg), file=f) 747 | return 748 | if arg in reg_mapping and reg != reg_mapping[arg]: 749 | print('mov %s, %s'%(reg_mapping[arg], reg), file=f) 750 | return 751 | if arg not in reg_mapping or reg != reg_mapping[arg]: 752 | assert False, "write_arg failed (register wtf)" 753 | 754 | def load_into_reg(f, arg, reg): 755 | data = read_arg(f, arg, reg) 756 | if data != reg: 757 | print('mov %s, %s'%(reg, data), file=f) 758 | 759 | def get_as_reg(f, arg, reg, bitness=32): 760 | data = read_arg(f, arg, reg, bitness=bitness) 761 | if data.startswith('#'): 762 | print('mov %s, %s'%(reg, data), file=f) 763 | return reg 764 | return data 765 | 766 | def lea(f, addr, reg): 767 | log2 = {1: 0, 2: 1, 4: 2, 8: 3} 768 | addr = addr.replace(' - ', ' + -') 769 | try: addr = int(addr, 0) 770 | except ValueError: pass 771 | else: 772 | print('ldr %s, =%s'%(reg, hex(addr)), file=f) 773 | return 774 | if addr.count(' + ') == 2: 775 | left, middle, right = addr.split(' + ') 776 | if '*' in middle: 777 | middle, scale = middle.split('*') 778 | print('add %s, %s, %s, lsl #%d'%(reg, reg_mapping[left], reg_mapping[middle], log2[int(scale)]), file=f) 779 | else: 780 | print('add %s, %s, %s'%(reg, reg_mapping[left], reg_mapping[middle]), file=f) 781 | right = int(right, 0) 782 | if right in range(-255, 256): 783 | print('add %s, %s, #%d'%(reg, reg, right), file=f) 784 | else: 785 | assert reg != 'r3' 786 | print('ldr r3, ='+hex(right), file=f) 787 | print('add %s, r3'%reg, file=f) 788 | return 789 | if ' + ' in addr: 790 | left, right = addr.split(' + ', 1) 791 | left_scale = None 792 | if '*' in left: 793 | left, left_scale = left.split('*') 794 | left_scale = int(left_scale) 795 | if left in reg_mapping: 796 | try: right = int(right, 0) 797 | except ValueError: pass 798 | else: 799 | if right in range(-255, 256) and left_scale == None: 800 | print('add %s, %s, #%s'%(reg, reg_mapping[left], hex(right)), file=f) 801 | else: 802 | midreg = reg if reg != reg_mapping[left] else 'r3' 803 | print('ldr %s, =%s'%(midreg, hex(right)), file=f) 804 | if left_scale == None: 805 | print('add %s, %s, %s'%(reg, midreg, reg_mapping[left]), file=f) 806 | else: 807 | print('add %s, %s, %s, lsl #%d'%(reg, midreg, reg_mapping[left], log2[left_scale]), file=f) 808 | return 809 | if right in reg_mapping: 810 | print('add %s, %s, %s'%(reg, reg_mapping[left], reg_mapping[right]), file=f) 811 | return 812 | if '*' in right: 813 | right, scale = right.split('*') 814 | scale = int(scale) 815 | assert right in reg_mapping, "lea failed" 816 | print('add %s, %s, %s, lsl #%d'%(reg, reg_mapping[left], reg_mapping[right], log2[scale]), file=f) 817 | return 818 | if '*' in addr: 819 | left, right = addr.split('*', 1) 820 | assert left in reg_mapping, "lea wtf" 821 | print('lsl %s, %s, #%d'%(reg, reg_mapping[left], log2[int(right)]), file=f) 822 | return 823 | if addr in reg_mapping: 824 | print('mov %s, %s'%(reg, reg_mapping[addr]), file=f) 825 | return 826 | assert False, "lea: unsupported addressing mode" 827 | 828 | def emit(f, cs, x, v, labels, cf_style, fpu_state, wrapper_names): 829 | tr_success = 0 830 | tr_fail = 0 831 | wrapper_names = iter(wrapper_names) 832 | def check_jump_to(target): 833 | assert target in cf_style and cf_style[target] in ('none', 'noone', cur_cf_style) and not (cf_style[target] == 'none' and (cur_cf_style == 'noone' or cur_cf_style in CF_FUCKUP)), "TODO: CF style mismatch" 834 | #assert fpu_state[target] == cur_fpu_state, "FPU mismatch" 835 | x87.check_jump(cur_fpu_state, fpu_state[target]) 836 | def condjump(cc, target): 837 | if not isinstance(cc, tuple): cc = (cc,) 838 | if abs(target - l) < 0x10000 and not TRACE: 839 | for i in cc: 840 | print('b%s x86_%x'%(i, target), file=f) 841 | else: 842 | for i in cc[:-1]: 843 | print('b%s x86_%x_jump'%(i, l), file=f) 844 | i = {'eq': 'ne', 'ne': 'eq', 'cs': 'cc', 'cc': 'cs', 'hs': 'lo', 'lo': 'hs', 'mi': 'pl', 'pl': 'mi', 'vs': 'vc', 'vc': 'vs', 'hi': 'ls', 'ls': 'hi', 'ge': 'lt', 'lt': 'ge', 'gt': 'le', 'le': 'gt'}[cc[-1]] 845 | print('b%s x86_%x_not_jump'%(i, l), file=f) 846 | print('x86_%x_jump:'%l, file=f) 847 | print('b x86_%x'%target, file=f) 848 | print('x86_%x_not_jump:'%l, file=f) 849 | cnt_ok = 0 850 | for i, l in enumerate(labels): 851 | print('.global x86_%x'%l, file=f) 852 | print('x86_%x:'%l, file=f) 853 | ii = read_instr_at(cs, v, l) 854 | ok = True 855 | if ii == None: 856 | print('bl emu_fault', file=f) # no way to recover 857 | ok = False 858 | else: 859 | if TRACE: 860 | print('ldr r0, ='+hex(l), file=f) 861 | print('mov r2, #%d'%(fpu_state[l][0] + 1 if ii.mnemonic.startswith('f') else 0), file=f) 862 | print('bl emu_trace', file=f) 863 | try: 864 | bitness = guess_bitness(ii.op_str) 865 | instr = ii.mnemonic 866 | print('// %x: %s %s'%(l, instr, ii.op_str), file=f) 867 | try: cur_cf_style = cf_style[l] 868 | except KeyError: assert False, "unknown CF style" 869 | try: cur_fpu_state = fpu_state[l] 870 | except KeyError: assert False, "unknown FPU state" 871 | if instr in ('add', 'sub', 'xor', 'and', 'or') or instr == 'adc' and cur_cf_style == 'add' or instr == 'sbb' and cur_cf_style == 'sub' or instr == 'lock add' and ('[' not in ii.op_str or ii.op_str.find('[') > ii.op_str.find(',')): 872 | if instr == 'xor': instr = 'eor' 873 | if instr == 'or': instr = 'orr' 874 | if instr == 'sbb': instr = 'sbc' 875 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 876 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness) 877 | arg2r = read_arg(f, arg2, 'r1', bitness=bitness) 878 | print('%ss %s, %s, %s'%(instr, arg1r, arg1r, arg2r), file=f) 879 | write_arg(f, arg1, arg1r) 880 | elif instr in ('cmp', 'test'): 881 | if instr == 'test': instr = 'tst' 882 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 883 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness) 884 | arg2r = read_arg(f, arg2, 'r1', bitness=bitness) 885 | print('%s %s, %s'%(instr, arg1r, arg2r), file=f) 886 | elif instr == 'mov': 887 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 888 | #if '[' in arg1: 889 | # write_arg(f, arg1, get_as_reg(f, arg2, 'r0')) 890 | #elif '[' in arg2: 891 | # read_arg(f, arg2, read_arg(f, arg1, 'r0'), bitness=bitness) 892 | #else: 893 | # try: arg2i = int(arg2, 0) 894 | # except ValueError: print('mov %s, %s'%(reg_mapping[arg1], reg_mapping[arg2]), file=f) 895 | # else: print('ldr %s, =%s'%(reg_mapping[arg1], arg2i), file=f) 896 | write_arg(f, arg1, get_as_reg(f, arg2, read_arg(f, arg1, 'r0', bitness=bitness, fake=True), bitness=bitness)) 897 | elif instr == 'call': 898 | try: target = int(ii.op_str, 16) 899 | except ValueError: 900 | load_into_reg(f, ii.op_str.strip(), 'r0') 901 | print('ldr r1, ='+hex(l+len(ii.bytes)), file=f) 902 | print('str r1, [r8, #-4]!', file=f) 903 | print('bl emu_dispatch_indir', file=f) 904 | print('bx r0', file=f) 905 | else: 906 | assert target in cf_style and cf_style[target] in ('none', 'noone'), "call to CF-aware code" 907 | assert cur_fpu_state == fpu_state[target] == x87.DEFAULT_STATE, "FPU ABI violation: caller %r, callee %r"%(cur_fpu_state, fpu_state[target]) 908 | print('ldr r1, ='+hex(l+len(ii.bytes)), file=f) 909 | print('str r1, [r8, #-4]!', file=f) 910 | if TRACE: 911 | print('ldr r0, =1+x86_%x'%target, file=f) 912 | print('bx r0', file=f) 913 | else: 914 | print('b x86_%x'%target, file=f) 915 | ok = False 916 | elif instr == 'push': 917 | assert ii.op_str.strip() != 'esp' 918 | print('str %s, [r8, #-4]!'%get_as_reg(f, ii.op_str.strip(), 'r0'), file=f) 919 | elif instr == 'je': 920 | assert cur_cf_style not in CF_FUCKUP 921 | target = int(ii.op_str, 16) 922 | check_jump_to(target) 923 | #assert target in cf_style and cf_style[target] in ('none', 'noone', cur_cf_style), "TODO: CF style mismatch" 924 | condjump('eq', target) 925 | elif instr in ('jne', 'bnd jne'): 926 | assert cur_cf_style not in CF_FUCKUP 927 | target = int(ii.op_str, 16) 928 | check_jump_to(target) 929 | #assert target in cf_style and cf_style[target] in ('none', 'noone', cur_cf_style), "TODO: CF style mismatch" 930 | condjump('ne', target) 931 | elif instr in ('js', 'bnd js'): 932 | assert cur_cf_style not in CF_FUCKUP 933 | target = int(ii.op_str, 16) 934 | check_jump_to(target) 935 | #assert target in cf_style and cf_style[target] in ('none', 'noone', cur_cf_style), "TODO: CF style mismatch" 936 | condjump('mi', target) 937 | elif instr in ('jns', 'bnd jbs'): 938 | assert cur_cf_style not in CF_FUCKUP 939 | target = int(ii.op_str, 16) 940 | check_jump_to(target) 941 | #assert target in cf_style and cf_style[target] in ('none', 'noone', cur_cf_style), "TODO: CF style mismatch" 942 | condjump('pl', target) 943 | elif instr in ('jo', 'bnd jo'): 944 | assert cur_cf_style not in CF_FUCKUP 945 | target = int(ii.op_str, 16) 946 | check_jump_to(target) 947 | condjump('vs', target) 948 | elif instr in ('jno', 'bnd jno'): 949 | assert cur_cf_style not in CF_FUCKUP 950 | target = int(ii.op_str, 16) 951 | check_jump_to(target) 952 | condjump('vc', target) 953 | elif instr in ('jb', 'bnd jb'): 954 | target = int(ii.op_str, 16) 955 | cc = {'add': 'cs', 'sub': 'cc', 'bt': 'cs'}[cur_cf_style] 956 | check_jump_to(target) 957 | #assert target in cf_style and cf_style[target] in ('none', 'noone', cur_cf_style), "TODO: CF style mismatch" 958 | condjump(cc, target) 959 | elif instr in ('ja', 'bnd ja'): 960 | target = int(ii.op_str, 16) 961 | assert cur_cf_style == 'sub', "TODO: ja with %s-style CF"%cur_cf_style 962 | check_jump_to(target) 963 | #assert target in cf_style and cf_style[target] in ('none', 'noone', cur_cf_style), "TODO: CF style mismatch" 964 | condjump('hi', target) 965 | elif instr in ('jbe', 'bnd jbe'): 966 | target = int(ii.op_str, 16) 967 | assert cur_cf_style == 'sub', "TODO: jbe with %s-style CF"%cur_cf_style 968 | check_jump_to(target) 969 | #assert target in cf_style and cf_style[target] in ('none', 'noone', cur_cf_style), "TODO: CF style mismatch" 970 | condjump('ls', target) 971 | elif instr in ('jae', 'bnd jae'): 972 | target = int(ii.op_str, 16) 973 | cc = {'add': 'cc', 'sub': 'cs', 'bt': 'cc'}[cur_cf_style] 974 | check_jump_to(target) 975 | #assert target in cf_style and cf_style[target] in ('none', 'noone', cur_cf_style), "TODO: CF style mismatch" 976 | condjump(cc, target) 977 | elif instr in ('jl', 'jle', 'jg', 'jge'): 978 | assert cur_cf_style in ('add', 'sub', 'xor'), cur_cf_style 979 | target = int(ii.op_str, 16) 980 | check_jump_to(target) 981 | #assert target in cf_style and cf_style[target] in ('none', 'noone', cur_cf_style), "TODO: CF style mismatch" 982 | if cur_cf_style == 'xor': 983 | if instr == 'jl': 984 | condjump('mi', target) 985 | elif instr == 'jge': 986 | condjump('pl', target) 987 | elif instr == 'jg': 988 | print('beq x86_%x_nojump'%l, file=f) 989 | condjump('pl', target) 990 | print('x86_%x_nojump:'%l, file=f) 991 | elif instr == 'jle': 992 | condjump(('eq', 'mi'), target) 993 | else: 994 | if len(instr) == 2: instr += 't' 995 | condjump(instr[1:], target) 996 | elif instr == 'lea': 997 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 998 | assert arg2.startswith('['), "invalid lea" 999 | lea(f, arg2.split('[', 1)[1].split(']', 1)[0], reg_mapping[arg1]) 1000 | elif instr == 'syscall': 1001 | print('mov r0, r4', file=f) 1002 | print('bl wrapper_'+next(wrapper_names), file=f) 1003 | elif instr == 'nop': pass 1004 | elif instr == 'leave': 1005 | print('mov r8, r9', file=f) 1006 | print('ldr r9, [r8], #4', file=f) 1007 | elif instr in ('ret', 'bnd ret'): 1008 | cur_fpu_state = x87.ret_hack(f, cur_fpu_state) 1009 | x87.check_jump(cur_fpu_state, x87.DEFAULT_STATE) 1010 | print('ldr r0, [r8], #4', file=f) 1011 | try: stdcall = int(ii.op_str, 0) 1012 | except ValueError: pass 1013 | else: print('add r8, r8, #'+hex(stdcall), file=f) 1014 | print('bl emu_dispatch_indir', file=f) 1015 | print('bx r0', file=f) 1016 | ok = False 1017 | elif instr == 'pop': 1018 | arg = ii.op_str.strip() 1019 | assert arg != 'esp' 1020 | arg_r = read_arg(f, arg, 'r0', fake=True, bitness=bitness) 1021 | print('ldr %s, [r8], #4'%arg_r, file=f) 1022 | write_arg(f, arg, arg_r) 1023 | elif instr == 'not': 1024 | arg = ii.op_str.strip() 1025 | arg_r = read_arg(f, arg, 'r0', bitness=bitness) 1026 | print('rsb %s, %s, #-1'%(arg_r, arg_r), file=f) 1027 | write_arg(f, arg, arg_r) 1028 | elif instr == 'neg': 1029 | arg = ii.op_str.strip() 1030 | arg_r = read_arg(f, arg, 'r0', bitness=bitness) 1031 | print('rsbs %s, %s, #0'%(arg_r, arg_r), file=f) 1032 | write_arg(f, arg, arg_r) 1033 | elif instr == 'jmp': 1034 | try: target = int(ii.op_str, 16) 1035 | except ValueError: 1036 | load_into_reg(f, ii.op_str.strip(), 'r0') 1037 | print('mrs r1, cpsr', file=f) 1038 | print('push {r1}', file=f) 1039 | print('bl emu_dispatch_indir', file=f) 1040 | print('pop {r1}', file=f) 1041 | print('msr cpsr_f, r1', file=f) 1042 | print('bx r0', file=f) 1043 | else: 1044 | check_jump_to(target) 1045 | #assert target in cf_style and cf_style[target] in ('none', 'noone', cur_cf_style), "TODO: CF style mismatch" 1046 | if TRACE: 1047 | print('ldr r0, =1+x86_%x'%target, file=f) 1048 | print('bx r0', file=f) 1049 | else: 1050 | print('b x86_%x'%target, file=f) 1051 | ok = False 1052 | elif instr == 'rep stosd': 1053 | print('bl emu_rep_stosd', file=f) 1054 | elif instr == 'rep movsd': 1055 | print('bl emu_rep_movsd', file=f) 1056 | elif instr == 'lock cmpxchg': 1057 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1058 | if arg1 in reg_mapping: 1059 | print('cmp r4,', reg_mapping[arg1], file=f) 1060 | print('it eq', file=f) 1061 | print('moveq %s, %s'%(reg_mapping[arg1], reg_mapping[arg2]), file=f) 1062 | print('movne r4,', reg_mapping[arg1], file=f) 1063 | else: 1064 | assert arg1.startswith('dword ptr ['), "wtf??" 1065 | lea(f, arg1.split('[', 1)[1].split(']', 1)[0], 'r0') 1066 | print('mov r1,', reg_mapping[arg2], file=f) 1067 | print('bl emu_lock_cmpxchg', file=f) 1068 | elif instr == 'xchg': 1069 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1070 | if arg2 in reg_mapping: 1071 | arg1, arg2 = arg2, arg1 1072 | assert arg1 in reg_mapping, "invalid xchg" 1073 | if arg2 in reg_mapping: 1074 | print('mov r0, %s'%reg_mapping[arg1], file=f) 1075 | print('mov %s, %s'%(reg_mapping[arg1], reg_mapping[arg2]), file=f) 1076 | print('mov '+reg_mapping[arg2]+', r0', file=f) 1077 | else: 1078 | lea(f, arg2.split('[', 1)[1].split(']', 1)[0], 'r0') 1079 | print('ldrex r1, [r0]', file=f) 1080 | print('strex r2, '+reg_mapping[arg1]+', [r0]', file=f) 1081 | print('cbz r2, x86_%x_xchg'%l, file=f) 1082 | print('b x86_%x'%l, file=f) 1083 | print('x86_%x_xchg:'%l, file=f) 1084 | print('mov '+reg_mapping[arg1]+', r1', file=f) 1085 | elif instr in ('sete', 'setne'): 1086 | assert cur_cf_style not in CF_FUCKUP 1087 | print('mov r0, #0', file=f) 1088 | cc = instr[3:] 1089 | if cc == 'e': cc = 'eq' 1090 | print('it', cc, file=f) 1091 | print('mov'+cc+' r0, #1', file=f) 1092 | print('lsl r0, #24', file=f) 1093 | write_arg(f, ii.op_str, 'r0') 1094 | elif instr in ('movzx', 'movsx'): 1095 | #assert cur_cf_style not in CF_FUCKUP 1096 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1097 | bitness1 = guess_bitness(arg1) 1098 | bitness2 = guess_bitness(arg2) 1099 | assert bitness2 < bitness1, "invalid movzx" 1100 | arg2r = read_arg(f, arg2, read_arg(f, arg1, 'r0', fake=True), bitness=bitness2) 1101 | print(('a' if instr[3] == 's' else 'l')+'sr %s, #%d'%(arg2r, bitness1 - bitness2), file=f) 1102 | write_arg(f, arg1, arg2r) 1103 | elif instr == 'fninit': pass # TODO: stub 1104 | elif instr in ('cmove', 'cmovne', 'cmovs', 'cmovns'): 1105 | assert cur_cf_style not in CF_FUCKUP 1106 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1107 | assert arg1 in reg_mapping and arg2 in reg_mapping, "weird cmov args" 1108 | cc = {'e': 'eq', 'ne': 'ne', 's': 'mi', 'ns': 'pl'}[instr[4:]] 1109 | print('it', cc, file=f) 1110 | print('mov%s %s, %s'%(cc, reg_mapping[arg1], reg_mapping[arg2]), file=f) 1111 | elif instr in ('cmovl', 'cmovle', 'cmovg', 'cmovge'): 1112 | assert cur_cf_style in ('add', 'sub'), cur_cf_style 1113 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1114 | assert arg1 in reg_mapping and arg2 in reg_mapping, "weird cmov args" 1115 | cc = instr[4:] 1116 | if len(cc) == 1: cc += 't' 1117 | print('it', cc, file=f) 1118 | print('mov%s %s, %s'%(cc, reg_mapping[arg1], reg_mapping[arg2]), file=f) 1119 | elif instr in ('cmovb', 'cmovae'): 1120 | assert cur_cf_style in ('add', 'sub'), cur_cf_style 1121 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1122 | assert arg1 in reg_mapping and arg2 in reg_mapping, "weird cmov args" 1123 | cc = ('cs', 'cc')[(cur_cf_style == 'sub') ^ (instr == 'cmovae')] 1124 | print('it', cc, file=f) 1125 | print('mov%s %s, %s'%(cc, reg_mapping[arg1], reg_mapping[arg2]), file=f) 1126 | elif instr in ('cmova', 'cmovbe'): 1127 | assert cur_cf_style == 'sub', cur_cf_style 1128 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1129 | assert arg1 in reg_mapping and arg2 in reg_mapping, "weird cmov args" 1130 | cc = {'cmova': 'hi', 'cmovbe': 'ls'}[instr] 1131 | print('it', cc, file=f) 1132 | print('mov%s %s, %s'%(cc, reg_mapping[arg1], reg_mapping[arg2]), file=f) 1133 | elif instr == 'imul' and ',' not in ii.op_str: 1134 | assert bitness == 32 1135 | arg_r = read_arg(f, ii.op_str, 'r0', bitness=bitness) 1136 | print('mov r2, r4', file=f) 1137 | print('smull r4, r6, r2, %s'%arg_r, file=f) 1138 | if cf_style[l+len(ii.bytes)] not in ('none', 'noone'): 1139 | print('add r2, r6, #1', file=f) 1140 | print('cmp r2, #2', file=f) 1141 | elif instr == 'imul': 1142 | assert bitness == 32 and ',' in ii.op_str, "TODO" 1143 | if ii.op_str.count(',') == 2: 1144 | arg1, arg2, imm = map(str.strip, ii.op_str.split(',')) 1145 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness) 1146 | arg2r = read_arg(f, arg2, 'r0', bitness=bitness) 1147 | print('ldr r2, ='+str(int(imm, 0)), file=f) 1148 | if cf_style[l + len(ii.bytes)] in ('none', 'noone'): 1149 | print('mul %s, r2, %s'%(arg1r, arg2r), file=f) 1150 | else: 1151 | print('smull %s, r3, r2, %s'%(arg1r, arg2r), file=f) 1152 | print('add r3, r3, #1', file=f) 1153 | print('cmp r3, #2', file=f) 1154 | write_arg(f, arg1, arg1r) 1155 | else: 1156 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1157 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness) 1158 | arg2r = read_arg(f, arg2, 'r1', bitness=bitness) 1159 | assert l + len(ii.bytes) in cf_style, "wtf" 1160 | if cf_style[l + len(ii.bytes)] in ('none', 'noone'): 1161 | if arg1r != arg2r: 1162 | print('mul %s, %s, %s'%(arg1r, arg2r, arg1r), file=f) 1163 | else: 1164 | print('mov r2, '+arg1r, file=f) 1165 | print('mul %s, r2'%arg1r, file=f) 1166 | else: 1167 | if arg1r == arg2r: 1168 | print('smull %s, r3, %s, %s'%(arg1r, arg1r, arg2r), file=f) 1169 | else: 1170 | print('mov r2, '+arg1r, file=f) 1171 | print('smull %s, r3, r2, %s'%(arg1r, arg2r), file=f) 1172 | print('add r3, r3, #1', file=f) 1173 | print('cmp r3, #2', file=f) 1174 | write_arg(f, arg1, arg1r) 1175 | elif instr in ('shr', 'sar'): 1176 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1177 | bitness1 = guess_bitness(arg1) 1178 | op = ('l' if instr == 'shr' else 'a')+'srs' 1179 | assert cur_cf_style in ('none', 'noone', 'add'), "wtf??" 1180 | if arg2 == 'cl': 1181 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness1) 1182 | print('and r2, r5, #%d'%(bitness1 - 1), file=f) 1183 | print('cbnz r2, x86_%x_do'%l, file=f) 1184 | print('b x86_%x'%(l+len(ii.bytes)), file=f) 1185 | print('x86_%x_do:'%l, file=f) 1186 | print(op, '%s, r2'%arg1r, file=f) 1187 | if bitness1 != 32: 1188 | print(op, '%s, #%d'%(arg1r, 32 - bitness1), file=f) 1189 | if bitness1 != 32: print('lsl %s, #%d'%(arg1r, 32 - bitness1), file=f) 1190 | write_arg(f, arg1, arg1r) 1191 | else: 1192 | arg2 = int(arg2, 0) 1193 | if arg2 != 0: 1194 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness1) 1195 | print(op, '%s, #%d'%(arg1r, arg2 + 32 - bitness1), file=f) 1196 | if bitness1 != 32: print('lsl %s, #%d'%(arg1r, 32 - bitness1), file=f) 1197 | write_arg(f, arg1, arg1r) 1198 | elif instr in ('shl', 'sal'): 1199 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1200 | bitness1 = guess_bitness(arg1) 1201 | assert cur_cf_style in ('none', 'noone', 'add'), "wtf??" 1202 | if arg2 == 'cl': 1203 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness1) 1204 | print('and r2, r5, #%d'%(bitness1 - 1), file=f) 1205 | print('cbnz r2, x86_%x_do'%l, file=f) 1206 | print('b x86_%x'%(l+len(ii.bytes)), file=f) 1207 | print('x86_%x_do:'%l, file=f) 1208 | print('lsls %s, r2'%arg1r, file=f) 1209 | write_arg(f, arg1, arg1r) 1210 | else: 1211 | arg2 = int(arg2, 0) 1212 | if arg2 != 0: 1213 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness1) 1214 | print('lsls %s, #%d'%(arg1r, arg2 + 32 - bitness1), file=f) 1215 | write_arg(f, arg1, arg1r) 1216 | elif instr == 'ror': 1217 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1218 | bitness1 = guess_bitness(arg1) 1219 | assert bitness1 == 32 1220 | assert cur_cf_style in ('none', 'noone', 'add'), "wtf??" 1221 | if arg2 == 'cl': 1222 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness1) 1223 | print('and r2, r5, #31', file=f) 1224 | print('cbnz r2, x86_%x_do'%l, file=f) 1225 | print('b x86_%x'%(l+len(ii.bytes)), file=f) 1226 | print('x86_%x_do:'%l, file=f) 1227 | print('rors %s, r2'%arg1r, file=f) 1228 | write_arg(f, arg1, arg1r) 1229 | else: 1230 | arg2 = int(arg2, 0) 1231 | if arg2 != 0: 1232 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness1) 1233 | print('rors %s, %s, #%d'%(arg1r, arg1r, arg2), file=f) 1234 | write_arg(f, arg1, arg1r) 1235 | elif instr == 'rol': 1236 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1237 | bitness1 = guess_bitness(arg1) 1238 | assert bitness1 == 32 1239 | if arg2 == 'cl': 1240 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness1) 1241 | print('and r2, r5, #31', file=f) 1242 | print('rsb r2, r2, #32', file=f) 1243 | print('ror %s, r2'%arg1r, file=f) 1244 | write_arg(f, arg1, arg1r) 1245 | else: 1246 | arg2 = int(arg2, 0) 1247 | if arg2 != 0: 1248 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness1) 1249 | print('ror %s, %s, #%d'%(arg1r, arg1r, 32-arg2), file=f) 1250 | write_arg(f, arg1, arg1r) 1251 | elif instr == 'cdq': 1252 | print('mov r6, r4, asr #31', file=f) 1253 | elif instr == 'idiv': 1254 | arg_r = read_arg(f, ii.op_str, 'r0', bitness=bitness) 1255 | print('sub r2, r6, r4, asr #31', file=f) 1256 | print('cbz r2, x86_%x_fast'%l, file=f) 1257 | if arg_r != 'r0': print('mov r0, %s'%arg_r, file=f) 1258 | print('bl emu_idiv', file=f) 1259 | print('b x86_%x'%(l+len(ii.bytes)), file=f) 1260 | print('x86_%x_fast:'%l, file=f) 1261 | print('sdiv r3, r4, %s'%arg_r, file=f) 1262 | print('mul r2, r3, %s'%arg_r, file=f) 1263 | print('sub r6, r4, r2', file=f) 1264 | print('mov r4, r3', file=f) 1265 | elif instr == 'div': 1266 | arg_r = read_arg(f, ii.op_str, 'r0', bitness=bitness) 1267 | print('cbz r6, x86_%x_fast'%l, file=f) 1268 | if arg_r != 'r0': print('mov r0, %s'%arg_r, file=f) 1269 | print('bl emu_div', file=f) 1270 | print('b x86_%x'%(l+len(ii.bytes)), file=f) 1271 | print('x86_%x_fast:'%l, file=f) 1272 | print('udiv r3, r4, %s'%arg_r, file=f) 1273 | print('mul r2, r3, %s'%arg_r, file=f) 1274 | print('sub r6, r4, r2', file=f) 1275 | print('mov r4, r3', file=f) 1276 | elif instr == 'bt': 1277 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1278 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness) 1279 | arg2r = read_arg(f, arg2, 'r1', bitness=bitness) 1280 | if arg2r.startswith('#'): 1281 | shift = int(arg2r[1:], 0) & (bitness - 1) 1282 | if shift == 0 and bitness == 32: 1283 | print('lsrs r0, %s, #1'%arg1r, file=f) 1284 | else: 1285 | print('lsls r0, %s, #%d'%(arg1r, bitness - shift), file=f) 1286 | else: 1287 | print('and r2, %s, #%d'%(arg2r, bitness-1), file=f) 1288 | print('lsr r0, %s, r2'%arg1r, file=f) 1289 | print('lsrs r0, #%d'%(33 - bitness), file=f) 1290 | elif instr == 'mul': 1291 | arg_r = read_arg(f, ii.op_str, 'r0', bitness=bitness) 1292 | if arg_r in ('r4', 'r6'): 1293 | print('mov r0, %s'%arg_r, file=f) 1294 | arg_r = r0 1295 | print('umull r4, r6, %s, r4'%arg_r, file=f) 1296 | assert l + len(ii.bytes) in cf_style 1297 | if cf_style[l + len(ii.bytes)] not in ('none', 'noone'): 1298 | print('cmp r6, #1', file=f) 1299 | elif instr == 'lock add': 1300 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1301 | assert arg1.startswith('dword ptr [') 1302 | lea(f, arg1.split('[', 1)[1].split(']', 1)[0], 'r0') 1303 | arg2r = read_arg(f, arg2, 'r1', bitness=bitness) 1304 | print('x86_%x_loop:'%l, file=f) 1305 | print('ldrex r2, [r0]', file=f) 1306 | print('adds r2, '+arg2r, file=f) 1307 | print('strex r3, r2, [r0]', file=f) 1308 | print('cbz r3, x86_%x_done'%l, file=f) 1309 | print('b x86_%x_loop'%l, file=f) 1310 | print('x86_%x_done:'%l, file=f) 1311 | elif instr == 'lock xadd': 1312 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1313 | assert bitness == 32 and arg2 in reg_mapping 1314 | if '[' not in arg1: 1315 | print('adds r0, %s, %s'%(reg_mapping[arg1], reg_mapping[arg2]), file=f) 1316 | print('mov %s, %s'%(reg_mapping[arg2], reg_mapping[arg1]), file=f) 1317 | print('mov %s, r0'%reg_mapping[arg1], file=f) 1318 | else: 1319 | lea(f, arg1.split('[', 1)[1].split(']', 1)[0], 'r0') 1320 | print('x86_%x_loop:'%l, file=f) 1321 | print('ldrex r1, [r0]', file=f) 1322 | print('adds r2, r1, '+reg_mapping[arg2], file=f) 1323 | print('strex r3, r2, [r0]', file=f) 1324 | print('cbz r3, x86_%x_ok'%l, file=f) 1325 | print('b x86_%x_loop'%l, file=f) 1326 | print('x86_%x_ok:'%l, file=f) 1327 | print('mov %s, r1'%reg_mapping[arg2], file=f) 1328 | elif instr in ('wait', 'fwait'): pass 1329 | elif instr == 'adc' and cur_cf_style == 'sub': 1330 | #invert CF 1331 | print('mov r0, #0', file=f) 1332 | print('it cc', file=f) 1333 | print('movcc r0, #0x80000000', file=f) 1334 | print('adds r0, r0', file=f) 1335 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1336 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness) 1337 | arg2r = read_arg(f, arg2, 'r1', bitness=bitness) 1338 | print('adcs %s, %s, %s'%(arg1r, arg1r, arg2r), file=f) 1339 | write_arg(f, arg1, arg1r) 1340 | elif instr == 'sbb' and cur_cf_style == 'add': 1341 | #invert CF 1342 | print('mov r0, #0', file=f) 1343 | print('it cc', file=f) 1344 | print('movcc r0, #0x80000000', file=f) 1345 | print('adds r0, r0', file=f) 1346 | arg1, arg2 = map(str.strip, ii.op_str.split(',')) 1347 | arg1r = read_arg(f, arg1, 'r0', bitness=bitness) 1348 | arg2r = read_arg(f, arg2, 'r1', bitness=bitness) 1349 | print('sbcs %s, %s, %s'%(arg1r, arg1r, arg2r), file=f) 1350 | write_arg(f, arg1, arg1r) 1351 | elif instr in ('adc', 'sbb'): 1352 | assert False, "%s unsupported with %s-style CF"%(instr, cur_cf_style) 1353 | elif instr.startswith('f'): 1354 | x87.emit(f, cf_style, cur_cf_style, fpu_state[l], l, ii, bitness) 1355 | elif instr == 'cwde': 1356 | print('lsl r4, #16', file=f) 1357 | print('asr r4, #16', file=f) 1358 | elif instr in ('inc', 'dec') or instr in ('lock inc', 'lock dec') and '[' not in ii.op_str: 1359 | instr = {'inc': 'add', 'dec': 'sub', 'lock inc': 'add', 'lock dec': 'sub'}[instr] 1360 | arg_r = read_arg(f, ii.op_str.strip(), 'r0', bitness=bitness) 1361 | print('%ss %s, %s, #1'%(instr, arg_r, arg_r), file=f) 1362 | write_arg(f, ii.op_str.strip(), arg_r) 1363 | elif instr in ('lock inc', 'lock dec'): 1364 | assert bitness == 32 1365 | instr = {'lock inc': 'add', 'lock dec': 'sub'}[instr] 1366 | lea(f, ii.op_str.split('[', 1)[1].split(']', 1)[0], 'r0') 1367 | print('x86_%x_retry:'%l, file=f) 1368 | print('ldrex r1, [r0]', file=f) 1369 | print('%ss r1, r1, #1'%instr, file=f) 1370 | print('strex r2, r1, [r0]', file=f) 1371 | print('cbz r2, x86_%x_ok'%l, file=f) 1372 | print('b x86_%x_retry'%l, file=f) 1373 | print('x86_%x_ok:'%l, file=f) 1374 | elif instr == 'stosd': 1375 | assert ii.op_str.strip() == 'dword ptr es:[edi], eax' 1376 | print('str r4, [r11], #4', file=f) 1377 | elif instr == 'movsd': 1378 | assert ii.op_str.strip() == 'dword ptr es:[edi], dword ptr [esi]' 1379 | print('ldr r0, [r10], #4', file=f) 1380 | print('str r0, [r11], #4', file=f) 1381 | else: 1382 | assert False, "unknown mnemonic %s"%ii.mnemonic 1383 | tr_success += 1 1384 | except Exception: 1385 | tr_fail += 1 1386 | instr = (ii.mnemonic+' '+ii.op_str).strip() 1387 | if TRACEBACKS: instr += '\n' + traceback.format_exc() 1388 | print('bl emu_unsupported', file=f) # no way to recover 1389 | print(asciz(instr.strip()), file=f) 1390 | ok = False 1391 | if ok and (i + 1 < len(labels) and labels[i+1] != l + len(ii.bytes) or TRACE and cnt_ok > 128): 1392 | print('b x86_%x'%(l + len(ii.bytes)), file=f) 1393 | ok = False 1394 | if not ok: 1395 | print('.ltorg', file=f) 1396 | cnt_ok = 0 1397 | else: 1398 | cnt_ok += 1 1399 | if i % 1000 == 0: report_progress(i, len(labels), tr_fail) 1400 | report_progress(-1) 1401 | print('%d instructions OK, %d instructions FAIL'%(tr_success, tr_fail)) 1402 | 1403 | def transpile(x, v, preseed, entry, wrapper_names): 1404 | cs = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_32) 1405 | labels, cf_style, fpu_state = get_labels(cs, x, v, preseed) 1406 | indir_bl = {i for i in cf_style if cf_style[i] not in ('none', 'noone') or fpu_state[i] != x87.DEFAULT_STATE} 1407 | f = io.StringIO() 1408 | emit(f, cs, x, v, labels, cf_style, fpu_state, wrapper_names) 1409 | return arm_crt.replace('ENTRY', 'x86_%x'%entry)+f.getvalue(), indir_bl 1410 | 1411 | if __name__ == '__main__': 1412 | import sys 1413 | x = pefile.PeFile(open(sys.argv[1], 'rb').read()) 1414 | v = vmem.VirtualMemory(x.sections, x.mem_align) 1415 | print(transpile(x, v, [])) 1416 | -------------------------------------------------------------------------------- /stubgen.py: -------------------------------------------------------------------------------- 1 | import subprocess, pycparser.c_generator, recompiler, json 2 | 3 | headers = '''\ 4 | #define _WIN32_WINNT 1000000000 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | //abi-only declarations 13 | int __getmainargs(int * _Argc, char *** _Argv, char *** _Env, int _DoWildCard, void * _StartInfo); 14 | int __lconv_init(void); 15 | char* __p__acmdln(void); 16 | void __set_app_type(int); 17 | void _initterm(void(**)(void), void(**)(void)); 18 | ''' 19 | 20 | custom_wrappers = { 21 | 'atexit': '''\ 22 | void wrapper_atexit(struct { void* callback; int ret; }* opaque) 23 | { 24 | void* arm_code = emu_malloc(0x10); 25 | unsigned short* thumb = arm_code; 26 | thumb[0] = 0x4901; 27 | thumb[1] = 0x4802; 28 | thumb[2] = 0x4708; 29 | thumb[3] = 0; 30 | unsigned int* consts = arm_code; 31 | consts[2] = 1 | (unsigned int)&emu_do_callback; 32 | consts[3] = (unsigned int)opaque->callback; 33 | emu_cacheflush(arm_code, 16); 34 | opaque->ret = ((int(*)(unsigned int))WINAPI::[atexit])(1|(unsigned int)arm_code); 35 | } 36 | ''', 37 | '_onexit': '''\ 38 | void wrapper__onexit(struct { void* callback; void* ret; }* opaque) 39 | { 40 | void* arm_code = emu_malloc(0x10); 41 | unsigned short* thumb = arm_code; 42 | thumb[0] = 0x4901; 43 | thumb[1] = 0x4802; 44 | thumb[2] = 0x4708; 45 | thumb[3] = 0; 46 | unsigned int* consts = arm_code; 47 | consts[2] = 1 | (unsigned int)&emu_do_callback; 48 | consts[3] = (unsigned int)opaque->callback; 49 | emu_cacheflush(arm_code, 16); 50 | opaque->ret = (((void*(*)(unsigned int))WINAPI::[_onexit])(1|(unsigned int)arm_code)) ? opaque->callback : (void*)0; 51 | } 52 | ''', 53 | '_initterm': ('''\ 54 | void _initterm(void(** start)(void), void(** end)(void)) 55 | { 56 | while(start < end) 57 | { 58 | if(*start) 59 | (**start)(); 60 | start++; 61 | } 62 | } 63 | ''', ''), 64 | 'printf': ('''\ 65 | asm(".global _printf\\n_printf:\\npush %eax\\nmov %esp, %eax\\nsyscall\\npop %eax\\nret"); 66 | ''', '''\ 67 | #define DO_PRINTF(SINGLE, DOUBLE, LF) do {\\ 68 | int nll = -1;\\ 69 | for(unsigned int i = 0; fmts[i]; i++)\\ 70 | {\\ 71 | if(nll < 0)\\ 72 | {\\ 73 | if(fmts[i] == '%')\\ 74 | nll = 0;\\ 75 | continue;\\ 76 | }\\ 77 | switch(fmts[i])\\ 78 | {\\ 79 | case 'l':\\ 80 | nll++;\\ 81 | break;\\ 82 | case 'j':\\ 83 | case 'L':\\ 84 | nll = 2;\\ 85 | break;\\ 86 | case 'd':\\ 87 | case 'i':\\ 88 | case 'u':\\ 89 | case 'o':\\ 90 | case 'x':\\ 91 | case 'X':\\ 92 | case 'c':\\ 93 | {\\ 94 | if(nll == 2)\\ 95 | DOUBLE;\\ 96 | else\\ 97 | SINGLE;\\ 98 | nll = -1;\\ 99 | break;\\ 100 | }\\ 101 | case 's':\\ 102 | case 'S':\\ 103 | case 'p':\\ 104 | case 'n':\\ 105 | nll = -1;\\ 106 | case '*':\\ 107 | SINGLE;\\ 108 | break;\\ 109 | case 'f':\\ 110 | case 'F':\\ 111 | case 'e':\\ 112 | case 'E':\\ 113 | case 'g':\\ 114 | case 'G':\\ 115 | case 'a':\\ 116 | case 'A':\\ 117 | DOUBLE;\\ 118 | nll = -1;\\ 119 | break;\\ 120 | case '%':\\ 121 | nll = -1;\\ 122 | break;\\ 123 | }\\ 124 | }\\ 125 | } while(0) 126 | 127 | void wrapper_printf(unsigned int* stack) 128 | { 129 | char* fmts = (void*)stack[2]; 130 | unsigned int nwords = 1; 131 | DO_PRINTF(nwords++, nwords += nwords % 2 + 2, ); 132 | unsigned int* src = stack + 2; 133 | unsigned int tgt[nwords]; 134 | tgt[0] = (unsigned int)fmts; 135 | unsigned int src_idx = 1, tgt_idx = 1; 136 | DO_PRINTF(tgt[tgt_idx++] = src[src_idx++], (tgt_idx += tgt_idx % 2, tgt[tgt_idx++] = src[src_idx++], tgt[tgt_idx++] = src[src_idx++]), src_idx++); 137 | stack[0] = (unsigned int)emu_call_native(tgt, nwords*4, WINAPI::[printf]); 138 | } 139 | 140 | #undef DO_PRINTF 141 | '''), 142 | 'CreateThread': '''\ 143 | DWORD thread_callback(void* param) 144 | { 145 | void** x = param; 146 | void* x86_function = x[0]; 147 | void* x86_param = x[1]; 148 | ((typeof(&SetEvent))WINAPI::[SetEvent])((HANDLE)x[2]); 149 | return emu_do_callback(x86_function, 0, x86_param); 150 | } 151 | 152 | void wrapper_CreateThread(struct 153 | { 154 | LPSECURITY_ATTRIBUTES lpThreadAttributes; 155 | SIZE_T dwStackSize; 156 | LPTHREAD_START_ROUTINE lpStartAddress; 157 | LPVOID lpParameter; 158 | DWORD dwCreationFlags; 159 | LPDWORD lpThreadId; 160 | HANDLE ans; 161 | }* param) 162 | { 163 | HANDLE evt = ((typeof(&CreateEventA))WINAPI::[CreateEventA])(NULL, FALSE, FALSE, "thread creation"); 164 | if(!evt) 165 | { 166 | param->ans = evt; 167 | return; 168 | } 169 | void* x[3] = {param->lpStartAddress, param->lpParameter, (void*)evt}; 170 | HANDLE thr = ((typeof(&CreateThread))WINAPI::[CreateThread])(param->lpThreadAttributes, param->dwStackSize, thread_callback, x, param->dwCreationFlags, param->lpThreadId); 171 | ((typeof(&WaitForSingleObject))WINAPI::[WaitForSingleObject])(evt, INFINITE); 172 | ((typeof(&CloseHandle))WINAPI::[CloseHandle])(evt); 173 | param->ans = thr; 174 | } 175 | ''', 176 | 'GetProcAddress': ('''\ 177 | struct emu_dlsymtab_entry 178 | { 179 | char* name; 180 | void* func; 181 | }; 182 | 183 | extern struct emu_dlsymtab_entry emu_dlsymtab_start[], emu_dlsymtab_end[]; 184 | 185 | struct opaque 186 | { 187 | struct emu_dlsymtab_entry* begin; 188 | struct emu_dlsymtab_entry* end; 189 | HMODULE hModule; 190 | LPCSTR lpProcName; 191 | FARPROC ans; 192 | }; 193 | 194 | __attribute__((stdcall)) FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName) 195 | { 196 | struct opaque opaq; 197 | opaq.begin = emu_dlsymtab_start; 198 | opaq.end = emu_dlsymtab_end; 199 | opaq.hModule = hModule; 200 | opaq.lpProcName = lpProcName; 201 | asm volatile("syscall"::"a"(&opaq):"memory"); 202 | return opaq.ans; 203 | }''', '''\ 204 | struct emu_dlsymtab_entry 205 | { 206 | char* name; 207 | void* func; 208 | }; 209 | 210 | struct opaque 211 | { 212 | struct emu_dlsymtab_entry* begin; 213 | struct emu_dlsymtab_entry* end; 214 | HMODULE hModule; 215 | LPCSTR lpProcName; 216 | FARPROC ans; 217 | }; 218 | 219 | void wrapper_GetProcAddress(struct opaque* opaq) 220 | { 221 | HANDLE hModule = opaq->hModule; 222 | LPCSTR lpProcName = opaq->lpProcName; 223 | struct emu_dlsymtab_entry* left = opaq->begin; 224 | struct emu_dlsymtab_entry* right = opaq->end; 225 | int idx = 0; 226 | do 227 | { 228 | struct emu_dlsymtab_entry* sep = right; 229 | while(sep - left > 1) 230 | { 231 | struct emu_dlsymtab_entry* mid = left + (sep - left) / 2; 232 | if(mid->name[idx] >= lpProcName[idx]) 233 | sep = mid; 234 | else 235 | left = mid; 236 | } 237 | if(left->name[idx] >= lpProcName[idx]) 238 | sep = left; 239 | if(sep == right || sep->name[idx] != lpProcName[idx]) 240 | goto return0; 241 | left = sep; 242 | while(right - sep > 1) 243 | { 244 | struct emu_dlsymtab_entry* mid = sep + (right - sep) / 2; 245 | if(mid->name[idx] > lpProcName[idx]) 246 | right = mid; 247 | else 248 | sep = mid; 249 | } 250 | if(left == right || left->name[idx] != lpProcName[idx]) 251 | goto return0; 252 | } 253 | while(lpProcName[idx++]); 254 | opaq->ans = left->func; 255 | return; 256 | return0: 257 | dbg_puts("GetProcAddress: could not resolve "); 258 | dbg_puts(opaq->lpProcName); 259 | dbg_puts("\\n"); 260 | opaq->ans = 0; 261 | return; 262 | } 263 | ''') 264 | } 265 | 266 | wrapper_deps = {'CreateThread': [('kernel32.dll', 'CreateEventA'), ('kernel32.dll', 'SetEvent'), ('kernel32.dll', 'WaitForSingleObject'), ('kernel32.dll', 'CloseHandle')]} 267 | 268 | def get_headers(arch): 269 | p = subprocess.Popen((arch+'-w64-mingw32-gcc', '-E', '-P', '-'), stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding='utf-8') 270 | out = p.communicate(headers)[0] 271 | assert not p.wait() 272 | return out 273 | 274 | def preprocess_headers(arch): 275 | out = get_headers(arch) 276 | out = out.split('__attribute__((__stdcall__))') 277 | out = ''.join(i.rstrip()[:-1]+' volatile volatile const (' if i.rstrip()[-1:] == '(' else (i+' volatile volatile const' if not i[:-1].isalnum() and i[:-1] != '_' else '__attribute__((__stdcall__))') for i in out[:-1])+out[-1] 278 | stage2 = '''\ 279 | #define __builtin_va_list va_list 280 | typedef int va_list; 281 | #define __attribute__(...) 282 | #define __inline 283 | #define __inline__ 284 | #define __volatile__ 285 | #define __extension__ 286 | #define extern 287 | #define __restrict__ restrict 288 | '''+out 289 | p = subprocess.Popen((arch+'-w64-mingw32-gcc', '-E', '-P', '-'), stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding='utf-8') 290 | out = p.communicate(stage2)[0] 291 | assert not p.wait() 292 | stage3 = '''\ 293 | #define __asm__(...) 294 | '''+out 295 | p = subprocess.Popen((arch+'-w64-mingw32-gcc', '-E', '-P', '-'), stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding='utf-8') 296 | out = p.communicate(stage3)[0] 297 | assert not p.wait() 298 | return '\n'.join(i for i in out.split('\n') if not i.startswith('#pragma ')) 299 | 300 | def parse_headers(arch): 301 | headers = preprocess_headers(arch) 302 | return pycparser.CParser().parse(headers) 303 | 304 | def extract_decls(arch): 305 | headers = parse_headers(arch) 306 | return {i.name: i for i in headers.ext if isinstance(i, pycparser.c_ast.Decl) and isinstance(i.type, pycparser.c_ast.FuncDecl)} 307 | 308 | def process(cgen, fn, upstream): 309 | if fn.name in custom_wrappers and isinstance(custom_wrappers[fn.name], tuple): 310 | return custom_wrappers[fn.name] 311 | try: 312 | if any(isinstance(j, pycparser.c_ast.EllipsisParam) for j in fn.type.args.params): 313 | print('Warning: '+fn.name+': varargs not supported') 314 | return (cgen.visit(fn).strip()+'{ *(void* volatile*)0; }\n', '') 315 | mx = '__' + max(['arg']+[i.name for i in fn.type.args.params if i.name != None], key=len) 316 | for i, j in enumerate(fn.type.args.params): 317 | if isinstance(j.type.type, pycparser.c_ast.IdentifierType) and j.type.type.names == ['void']: 318 | continue 319 | if j.name == None: 320 | j.name = j.type.declname = mx+str(i+1) 321 | if isinstance(j.type, pycparser.c_ast.ArrayDecl): 322 | j.type = pycparser.c_ast.PtrDecl(type=j.type.type, quals=[]) 323 | except: 324 | raise Exception("error while processing "+fn.name) 325 | args = [i.name for i in fn.type.args.params if i.name != None] 326 | is_void_fn = isinstance(fn.type.type.type, pycparser.c_ast.IdentifierType) and fn.type.type.type.names == ['void'] 327 | if fn.quals[-3:] == ['volatile', 'volatile', 'const']: 328 | fn.quals[-3:] = ('__attribute__((stdcall))',) 329 | if isinstance(fn.type.type, pycparser.c_ast.PtrDecl) and fn.type.type.quals[-3:] == ['volatile', 'volatile', 'const']: 330 | fn.type.type.quals[-3:] = ('__attribute__((stdcall))',) 331 | ans_name = mx+'0' 332 | opaque = mx+'o' 333 | decl = cgen.visit(fn).strip() 334 | decl_type = fn.type.type 335 | while not isinstance(decl_type, pycparser.c_ast.TypeDecl): 336 | decl_type = decl_type.type 337 | decl_type.declname = ans_name 338 | outer = decl+'\n' 339 | outer += '{\n struct\n {\n' 340 | for i in args: 341 | outer += ' typeof(%s) %s;\n'%(i, i) 342 | if not is_void_fn: 343 | outer += ' typeof(%s) %s;\n'%(fn.name+'('+', '.join(args)+')', ans_name) 344 | outer += ' } '+opaque+' = {\n' 345 | for i in args: 346 | outer += ' '+i+',\n' 347 | outer += ' };\n' 348 | outer += ' asm volatile("syscall"::"a"(&'+opaque+'):"memory");\n' 349 | if not is_void_fn: 350 | outer += ' return '+opaque+'.'+ans_name+';\n' 351 | outer += '}\n' 352 | inner = 'void wrapper_'+fn.name+'(struct\n{\n' 353 | for i in fn.type.args.params: 354 | if i.name != None: 355 | inner += ' '+cgen.visit(i)+';\n' 356 | if not is_void_fn: 357 | inner += ' '+cgen.visit(pycparser.c_ast.Decl(name=ans_name, type=fn.type.type, quals=[], storage=[], funcspec=[], init=None, bitsize=None))+';\n' 358 | inner += '}* '+opaque+')\n{\n' 359 | if recompiler.TRACE: 360 | inner += ' dbg_puts("calling %s\\n");\n'%fn.name 361 | inner += ' ' 362 | if not is_void_fn: 363 | inner += opaque+'->'+ans_name+' = ' 364 | inner += '((typeof(&'+fn.name+'))('+upstream+'))('+', '.join(opaque+'->'+i for i in args)+');\n' 365 | inner += '}\n' 366 | decl_type.declname = fn.name 367 | if fn.name in custom_wrappers: inner = custom_wrappers[fn.name] 368 | return (outer, inner) 369 | 370 | external_non_funcs = {'__initenv'} 371 | 372 | def gen_decls(arch, fns): 373 | decls = extract_decls(arch) 374 | cgen = pycparser.c_generator.CGenerator() 375 | x86_code = get_headers(arch) 376 | arm_code = headers 377 | wrapper_names = [] 378 | for i, j in fns.items(): 379 | if i == '__initenv': continue 380 | if i in decls: 381 | x, y = process(cgen, decls[i], j) 382 | else: 383 | print('Warning:', i, 'is a stub') 384 | x = 'void '+i+'(void)\n{\n asm volatile("syscall");\n}\n' 385 | y = 'void wrapper_'+i+'(void)\n{\n emu_unsupported_c("'+i+' is a stub!\\n");\n}\n' 386 | x86_code += x 387 | arm_code += y 388 | if y != '': 389 | wrapper_names.append(i) 390 | return (x86_code, arm_code, wrapper_names) 391 | 392 | def gen_dlsymtab(symbols): 393 | names = sorted([j[1] for j in symbols]) 394 | ans = '.align 4\n' 395 | ans += '_emu_dlsymtab_start:\n' 396 | for i in names: 397 | ans += '.long .L_'+i+'\n' 398 | ans += '.long _'+i+'\n' 399 | ans += '_emu_dlsymtab_end:\n' 400 | for i in names: 401 | ans += '.L_'+i+':\n' 402 | ans += '.asciz "'+i+'"\n' 403 | return 'asm(' + json.dumps(ans) + ');' 404 | -------------------------------------------------------------------------------- /test.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sleirsgoevy/peshit/9cbc8169f8db6eeef01e3c7aa3add6cc343c34dd/test.exe -------------------------------------------------------------------------------- /tlshooks.py: -------------------------------------------------------------------------------- 1 | def install_tls_hooks(x, v): 2 | tls = x.offset_table[9] 3 | addr, _ = v.alloc(36, 0xc0000000, '.tls2') 4 | if tls == None: 5 | tls_bytes = b'\0\0\0\0\0\0\0\0'+(addr + 24).to_bytes(4, 'little')+b'\0\0\0\0\0\0\0\0\0\0\0\0' 6 | callbacks = [] 7 | else: 8 | assert tls[1] == 24 9 | tls_bytes = v[tls[0]:tls[0]+24] 10 | callbacks_addr = int.from_bytes(tls_bytes[12:16], 'little') 11 | callbacks = [] 12 | while any(v[callbacks_addr:callbacks_addr+4]): 13 | callbacks.append(int.from_bytes(v[callbacks_addr:callbacks_addr+4], 'little')) 14 | callbacks_addr += 4 15 | v[addr:addr+24] = tls_bytes 16 | arm_addr = addr + 32 17 | v[addr+12:addr+16] = arm_addr.to_bytes(4, 'little') 18 | x.offset_table[9] = (addr, 24) 19 | return arm_addr, callbacks 20 | -------------------------------------------------------------------------------- /vmem.py: -------------------------------------------------------------------------------- 1 | class VirtualMemory: 2 | def __init__(self, sections, align): 3 | self.sections = sections 4 | self.align = align 5 | self.maxvma = 0 6 | for name, va, memsz, flags, data in self.sections: 7 | self.maxvma = max(self.maxvma, va+memsz) 8 | def _section_at(self, va): 9 | l = -1 10 | r = len(self.sections) 11 | while r - l > 1: 12 | m = (l+r)//2 13 | if self.sections[m][1] > va: 14 | r = m 15 | else: 16 | l = m 17 | return l 18 | def __getitem__(self, idx): 19 | if isinstance(idx, slice): 20 | return bytes(map(self.__getitem__, range(idx.start, idx.stop, idx.step if idx.step != None else 1))) 21 | s = self._section_at(idx) 22 | if s < 0 or idx >= self.sections[s][1]+self.sections[s][2]: 23 | raise IndexError("unmapped VM read at 0x%x"%idx) 24 | return self.sections[s][4][idx-self.sections[s][1]] 25 | def __setitem__(self, idx, data): 26 | if isinstance(idx, slice): 27 | r = range(idx.start, idx.stop, idx.step if idx.step != None else 1) 28 | if len(r) != len(data): 29 | raise ValueError("lvalue and rvalue have different structures") 30 | for i, x in zip(r, data): 31 | self[i] = x 32 | return 33 | s = self._section_at(idx) 34 | if s < 0 or idx >= self.sections[s][1]+self.sections[s][2]: 35 | raise IndexError("unmapped VM write at 0x%x"%idx) 36 | self.sections[s][4][idx-self.sections[s][1]] = data 37 | def alloc(self, size, flags, sect_name=''): 38 | va = self.maxvma 39 | va += (-va) % self.align 40 | data = bytearray(size) 41 | self.sections.append((sect_name, va, size, flags, data)) 42 | self.maxvma = va + size 43 | return (va, data) 44 | def sim_alloc(self): 45 | return self.maxvma + (-self.maxvma) % self.align 46 | -------------------------------------------------------------------------------- /x87.py: -------------------------------------------------------------------------------- 1 | import recompiler, struct, math 2 | 3 | DEFAULT_STATE = (0, 'wtf') 4 | 5 | def type_pun(f): 6 | return int.from_bytes(struct.pack('