SHOW:
|
|
- or go back to the newest paste.
| 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 :) |