evandrix

PHDay CTF mach-o solution

Aug 26th, 2012
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. from struct import pack, unpack
  2.  
  3. # the crackme is a huge mach-o executable with a nice and original obfuscation
  4.  
  5. # I decided to study it under ollydbg by loading it with a custom loader (there is only few imports and all are standards (except the MD5 call))
  6. # it's ugly but it works well :) (and olly is definitively the best debugger to study obfuscated codes :) )
  7.  
  8. '''
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <openssl/md5.h>
  12. #include <windows.h>
  13.  
  14. int main()
  15. {
  16.    PBYTE maclol = VirtualAlloc(NULL, 0xB8744,  MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  17.    HANDLE hFile = CreateFileA("a.out", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
  18.    DWORD numberOfBytesRead;
  19.    int (*macmain)(int argc, char** argv);
  20.    char *argv[2] = {"a.out"};
  21.  
  22.    ReadFile(hFile, maclol, 0xB8744, &numberOfBytesRead, NULL);
  23.    maclol -= 0x1000;
  24.    maclol[0x056C3E] = 0x68;
  25.    *(PDWORD)(&maclol[0x056C3E+1]) = (DWORD)fread;
  26.    maclol[0x056C3E+5] = 0xC3;
  27.    maclol[0x056C44] = 0x68;
  28.    *(PDWORD)(&maclol[0x056C44+1]) = (DWORD)fseek;
  29.    maclol[0x056C44+5] = 0xC3;
  30.    maclol[0x056C4A] = 0x68;
  31.    *(PDWORD)(&maclol[0x056C4A+1]) = (DWORD)ftell;
  32.    maclol[0x056C4A+5] = 0xC3;
  33.    maclol[0x056C56] = 0x68;
  34.    *(PDWORD)(&maclol[0x056C56+1]) = (DWORD)memcmp;
  35.    maclol[0x056C56+5] = 0xC3;
  36.    maclol[0x056C5C] = 0x68;
  37.    *(PDWORD)(&maclol[0x056C5C+1]) = (DWORD)memcpy;
  38.    maclol[0x056C5C+5] = 0xC3;
  39.    maclol[0x056C62] = 0x68;
  40.    *(PDWORD)(&maclol[0x056C62+1]) = (DWORD)memset;
  41.    maclol[0x056C62+5] = 0xC3;
  42.    maclol[0x056C68] = 0x68;
  43.    *(PDWORD)(&maclol[0x056C68+1]) = (DWORD)printf;
  44.    maclol[0x056C68+5] = 0xC3;
  45.    maclol[0x056C38] = 0x68;
  46.    *(PDWORD)(&maclol[0x056C38+1]) = (DWORD)fopen;
  47.    maclol[0x056C38+5] = 0xC3;
  48.    maclol[0x056BEA] = 0x68;
  49.    *(PDWORD)(&maclol[0x056BEA+1]) = (DWORD)MD5;
  50.    maclol[0x056BEA+5] = 0xC3;
  51.    macmain = (int (*)(int argc, char** argv))(&maclol[0x40E40]);
  52.    macmain(2, argv);
  53.    return 0;
  54. }
  55. '''
  56.  
  57. # the verification involved 4 modular polynomials equations with 4 variables :
  58. # the key consist of 0x2000 4-uples of polynoms (P1, P2, P3, P4)
  59.  
  60. # C1, C2, C3 and C4 are calculated by using the following equations and must be equals to those stored in the binary :
  61. # C1 = P1*(x^1+x^2+x^3 [0xE]) + P2*(x^0+x^1+x^3 [0xB]) + P3*(x^0+x^2+x^3 [0xD]) + P4*(x^0+x^3     [0x9])
  62. # C2 = P1*(x^0+x^3     [0x9]) + P2*(x^1+x^2+x^3 [0xE]) + P3*(x^0+x^1+x^3 [0xB]) + P4*(x^0+x^2+x^3 [0xD])
  63. # C3 = P1*(x^0+x^2+x^3 [0xD]) + P2*(x^0+x^3     [0x9]) + P3*(x^1+x^2+x^3 [0xE]) + P4*(x^0+x^1+x^3 [0xB])
  64. # C4 = P1*(x^0+x^1+x^3 [0xB]) + P2*(x^0+x^2+x^3 [0xD]) + P3*(x^0+x^3     [0x9]) + P4*(x^1+x^2+x^3 [0xE])
  65.  
  66. # I coded the basic operation on this field and
  67. # a little Gaussian elimination to solve those equations and so recover the 4uples P1, P2, P3 and P4.
  68.  
  69. def mul_mod(P1, P2) :
  70.     tmp1 = P1
  71.     tmp2 = P2
  72.     r = 0
  73.     while tmp2 :
  74.         if tmp2 & 1 :
  75.             r ^= tmp1
  76.         tmp2 >>= 1
  77.         if tmp1 & 0x80000000 :
  78.             tmp1 ^= 0x8000001B
  79.         tmp1 <<= 1
  80.     return r
  81.    
  82. def mul(P1, P2) :
  83.     tmp1 = P1
  84.     tmp2 = P2
  85.     r = 0
  86.     while tmp2 :
  87.         if tmp2 & 1 :
  88.             r ^= tmp1
  89.         tmp1 <<= 1
  90.         tmp2 >>= 1
  91.     return r
  92.    
  93. def div(P1, P2) :
  94.     assert P1 > P2
  95.     i = 0
  96.     r = 0
  97.     m = P1
  98.     log2P2 = 0
  99.     while P2 >> log2P2 :
  100.         log2P2 += 1
  101.     log2P2 -= 1
  102.     while P1 >= (P2 << i) :
  103.         i += 1
  104.     while i >= 0 :
  105.         if m & (1 << (i + log2P2)) :
  106.             m ^= P2 << i
  107.             r |= 1 << i
  108.         i -= 1
  109.     return r, m
  110.  
  111. def eea(u, v):
  112.     u1 = 1
  113.     u2 = 0
  114.     u3 = u
  115.     v1 = 0
  116.     v2 = 1
  117.     v3 = v
  118.     while v3 != 0:
  119.         q, t3 = div(u3, v3)
  120.         t1 = u1 ^ mul(q, v1)
  121.         t2 = u2 ^ mul(q, v2)
  122.         u1 = v1
  123.         u2 = v2
  124.         u3 = v3
  125.         v1 = t1
  126.         v2 = t2
  127.         v3 = t3
  128.     return u1, u2, u3
  129.  
  130. def inv(P) :
  131.     u1, u2, u3 = eea(0x100000036, P)
  132.     assert(u3 == 1)
  133.     return u2
  134.  
  135. gauss = [
  136.  ([0, 0, 1, 0], [0xD, 0x9, 0xE, 0xB]),
  137.  ([0, 1, 0, 0], [0x9, 0xE, 0xB, 0xD]),
  138.  ([0, 0, 0, 1], [0xB, 0xD, 0x9, 0xE]),
  139.  ([1, 0, 0, 0], [0xE, 0xB, 0xD, 0x9])
  140. ]
  141.  
  142. for i in xrange(4) :
  143.     invx = inv(gauss[i][1][i])
  144.    
  145.     for j in xrange(4) :
  146.         gauss[i][0][j] = mul_mod(gauss[i][0][j], invx)
  147.         gauss[i][1][j] = mul_mod(gauss[i][1][j], invx)
  148.        
  149.     for j in xrange(4) :
  150.         if j != i :
  151.             coeff = gauss[j][1][i]
  152.             for k in xrange(4) :
  153.                 gauss[j][0][k] ^= mul_mod(coeff, gauss[i][0][k])
  154.                 gauss[j][1][k] ^= mul_mod(coeff, gauss[i][1][k])
  155.                
  156. # table of the reference (C1, C2, C3, C4) 4-uple values checked at the end of the execution
  157. table = file("table", "rb").read()
  158. key = ""
  159. while table :
  160.     out = unpack("<IIII", table[:0x10])
  161.     table = table[0x10:]
  162.     for i in xrange(4) :
  163.         r = 0
  164.         for j in xrange(4) :
  165.             r ^= mul_mod(gauss[i][0][j], out[j])
  166.         key += pack("<I", r)
  167. file("ctf-pass-file.txt", "wb").write(key)
  168.  
  169. # by the way, there is a bug in the crackme, only the first 0x11 bytes of the 0x20 000 bytes table are checked :)
Add Comment
Please, Sign In to add comment