-
-
Notifications
You must be signed in to change notification settings - Fork 185
/
pe.py
executable file
·130 lines (95 loc) · 3.11 KB
/
pe.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env python3
# a script to collide arbitrary Windows Executables with MD5
# Ange Albertini 2018-2021
# takes 2 PE,
# gets both PE headers+DD+sections
# moves them next to each other,
# adjust offsets of section tables
# copy sections next to each other
# update SizeOfHeaders of file 2
import sys, struct, hashlib, os
def getPEhdr(d):
PEoffset = d.find(b"PE\0\0")
peHDR = d[PEoffset:]
Machine = struct.unpack("H", peHDR[4:4+2])[0]
SecCount = struct.unpack("h", peHDR[0x6:0x6+2])[0]
bits = None
if Machine == 0x014C:
bits = 32
elif Machine == 0x8664:
bits = 64
if bits is None:
print("ERROR: unknown arch")
sys.exit()
NumDiffOff = 0x74 if bits == 32 else 0x84
NumDD = struct.unpack("i", peHDR[NumDiffOff:NumDiffOff+4])[0]
SecTblOff = NumDiffOff + 4 + NumDD * 2 * 4
# get the offset of the first section
SectsStart = struct.unpack("i", peHDR[SecTblOff+0x14:SecTblOff+0x14+4])[0]
PElen = SecTblOff + SecCount * 0x28
return PEoffset, PElen, SecCount, PEoffset + SecTblOff, SectsStart
def relocateSections(d, SecTblOff, SecCount, delta):
for i in range(SecCount):
offset = SecTblOff + i*0x28 + 0x14
PhysOffset = struct.unpack("i", d[offset:offset+4])[0]
d = b"".join([
d[:offset],
struct.pack("i", PhysOffset + delta),
d[offset+4:]
])
return d
fn1, fn2 = sys.argv[1:3]
# the following 2 depends on computed collision blocks
# to leave room for 9 collision blocks after CPC
HEADER1OFF = 0x2C0
# arbitrary length to support most PE headers
HEADER2OFF = 0x480
# too lazy to get optimal file alignment :p
SECTIONSOFF = 0x1000
with open("pe1.bin", "rb") as f:
prefix1 = f.read()
with open("pe2.bin", "rb") as f:
prefix2 = f.read()
with open(fn1, "rb") as f:
d1 = f.read()
with open(fn2, "rb") as f:
d2 = f.read()
# best PE validation ever :p
assert d1.startswith(b"MZ")
assert d2.startswith(b"MZ")
PEoff1, HdrLen1, NumSec1, SecTblOff1, SectsStart1 = getPEhdr(d1)
PEoff2, HdrLen2, NumSec2, SecTblOff2, SectsStart2 = getPEhdr(d2)
if HdrLen1 > HEADER2OFF - HEADER1OFF:
print("ERROR: PE header 1 is too big")
sys.exit()
if HdrLen2 > SECTIONSOFF - HEADER2OFF:
print("ERROR: PE header 2 is too big")
sys.exit()
#SizeOfHeader increased
d2 = b"".join([
d2[:PEoff2 + 0x54],
struct.pack("i", SECTIONSOFF),
d2[PEoff2 + 0x54 + 4:]
])
d1 = relocateSections(d1, SecTblOff1, NumSec1, SECTIONSOFF - SectsStart1)
Sections1 = d1[SectsStart1:]
Sections2 = d2[SectsStart2:]
# let's hope the first batch of sections are aligned to file Alignments ;)
OffSec2 = len(Sections1) + SECTIONSOFF
d2 = relocateSections(d2, SecTblOff2, NumSec2, OffSec2 - SectsStart2)
suffix = b"".join([
d1[PEoff1:PEoff1+HdrLen1],
(HEADER2OFF - (HEADER1OFF + HdrLen1)) * b"\0",
d2[PEoff2:PEoff2+HdrLen2],
(SECTIONSOFF - (HEADER2OFF + HdrLen2)) * b"\0",
Sections1,
Sections2
])
with open("collision1.exe", "wb") as f:
f.write(prefix1 + suffix)
with open("collision2.exe", "wb") as f:
f.write(prefix2 + suffix)
md5 = hashlib.md5(prefix1 + suffix).hexdigest()
assert md5 == hashlib.md5(prefix2 + suffix).hexdigest()
print("Success!")
print("Common MD5: %s" % md5)