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 :) |