View difference between Paste ID: CgqR8Teb and LSq9xKPP
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 :)