-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathextractconfig.go
More file actions
340 lines (284 loc) · 10.7 KB
/
extractconfig.go
File metadata and controls
340 lines (284 loc) · 10.7 KB
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
package main
import (
"debug/elf"
"encoding/hex"
"fmt"
"math/big"
"strings"
"github.com/knightsc/gapstone"
log "github.com/sirupsen/logrus"
)
// Index positions for decrypt tool configuration
const (
DecryptConfigRSAPublicMod = 0
DecryptConfigRSAPublicExponent = 1
DecryptConfigRSAPrivateExponent = 2
DecryptConfigRSAFirstPrimeFactor = 3
DecryptConfigRSASecondPrimeFactor = 4
DecryptConfigRSADP = 5
DecryptConfigRSADQ = 6
DecryptConfigRSAQP = 7
DecryptConfigEncryptedFileExtension = 8
DecryptConfigRansomNoteFilename = 9
)
// This is the number of possible decrypt logics (block and chunk sizes for different sized files)
var NumDecryptLogics int = 6
type RansomEXXDecryptConfigItem struct {
itemIndex uint32
data []byte
}
func printSections(elfFile *elf.File) {
for _, sec := range elfFile.Sections {
log.Debugf("0x%x\t%s\n", sec.Addr, sec.Name)
}
}
// Returns the read value and the new cursor value (after incrementing)
func readUint32(elfFile *elf.File, raw []byte, cursor uint64) (value uint32, newCursor uint64) {
log.Debugf("Attempting to read uint32 from data offset: 0x%x\n", cursor)
value = elfFile.ByteOrder.Uint32(raw[cursor : cursor+4])
newCursor = cursor + 4
return
}
func readUint64(elfFile *elf.File, raw []byte, cursor uint64) (value uint64, newCursor uint64) {
log.Debugf("Attempting to read uint64 from data offset: 0x%x\n", cursor)
value = elfFile.ByteOrder.Uint64(raw[cursor : cursor+8])
newCursor = cursor + 8
return
}
func readConfigItem(elfFile *elf.File, raw []byte, cursor uint64) (value RansomEXXDecryptConfigItem, newCursor uint64) {
log.Debugf("Attempting to read RansomEXXDecryptConfigItem from data offset: 0x%x\n", cursor)
value = RansomEXXDecryptConfigItem{}
value.itemIndex, newCursor = readUint32(elfFile, raw, cursor)
length, newCursor := readUint32(elfFile, raw, newCursor)
value.data = raw[newCursor : newCursor+uint64(length-1)]
newCursor += uint64(length)
return
}
func parseConfig(elfFile *elf.File, raw []byte, configBufferOffset uint64) RansomEXXDecryptConfig {
configBufferLength, newCursor := readUint32(elfFile, raw, configBufferOffset)
// Calculate end of config buffer
end := configBufferOffset + uint64(configBufferLength)
numConfigItems, newCursor := readUint32(elfFile, raw, newCursor)
log.Debugf("Number of config items: 0x%x\n", numConfigItems)
log.Debugf("Config buffer length: 0x%x\n", configBufferLength)
var config RansomEXXDecryptConfig
for i := 0; i < int(numConfigItems); i++ {
if newCursor > end {
log.Fatalln("Cursor has seeked beyond config buffer size, config buffer may be corrupted or of an unknown format.")
}
var item RansomEXXDecryptConfigItem
item, newCursor = readConfigItem(elfFile, raw, newCursor)
log.Debugf("Found item at 0x%x with index %d\n", newCursor, item.itemIndex)
log.Debugf("Config item value:\n%s\n", hex.Dump(item.data))
var ok bool
switch item.itemIndex {
case DecryptConfigRSAPublicMod:
config.RSAPublicMod, ok = new(big.Int).SetString(string(item.data), 16)
if !ok {
log.Fatalf("Failed to convert config item with index %d into a bigint\n", item.itemIndex)
}
case DecryptConfigRSAPublicExponent:
config.RSAPublicExponent, ok = new(big.Int).SetString(string(item.data), 16)
if !ok {
log.Fatalf("Failed to convert config item with index %d into a bigint\n", item.itemIndex)
}
case DecryptConfigRSAPrivateExponent:
config.RSAPrivateExponent, ok = new(big.Int).SetString(string(item.data), 16)
if !ok {
log.Fatalf("Failed to convert config item with index %d into a bigint\n", item.itemIndex)
}
case DecryptConfigRSAFirstPrimeFactor:
config.RSAFirstPrimeFactor, ok = new(big.Int).SetString(string(item.data), 16)
if !ok {
log.Fatalf("Failed to convert config item with index %d into a bigint\n", item.itemIndex)
}
case DecryptConfigRSASecondPrimeFactor:
config.RSASecondPrimeFactor, ok = new(big.Int).SetString(string(item.data), 16)
if !ok {
log.Fatalf("Failed to convert config item with index %d into a bigint\n", item.itemIndex)
}
case DecryptConfigRSADP:
config.RSADP, ok = new(big.Int).SetString(string(item.data), 16)
if !ok {
log.Fatalf("Failed to convert config item with index %d into a bigint\n", item.itemIndex)
}
case DecryptConfigRSADQ:
config.RSADQ, ok = new(big.Int).SetString(string(item.data), 16)
if !ok {
log.Fatalf("Failed to convert config item with index %d into a bigint\n", item.itemIndex)
}
case DecryptConfigRSAQP:
config.RSAQP, ok = new(big.Int).SetString(string(item.data), 16)
if !ok {
log.Fatalf("Failed to convert config item with index %d into a bigint\n", item.itemIndex)
}
case DecryptConfigEncryptedFileExtension:
config.EncryptedFileExtension = string(item.data)
case DecryptConfigRansomNoteFilename:
config.RansomNoteFilename = string(item.data)
}
}
return config
}
func parseLogic(elfFile *elf.File, raw []byte, decryptLogicOffset uint64) []DecryptLogic {
cursor := decryptLogicOffset
var ret []DecryptLogic
for i := 0; i < NumDecryptLogics; i++ {
var logic DecryptLogic
logic.LowerLimit, cursor = readUint64(elfFile, raw, cursor)
logic.UpperLimit, cursor = readUint64(elfFile, raw, cursor)
logic.ChunkSize, cursor = readUint64(elfFile, raw, cursor)
logic.BlockSize, cursor = readUint64(elfFile, raw, cursor)
ret = append(ret, logic)
}
return ret
}
func extractConfig(decryptToolPath string, conf Config) RansomEXXDecryptConfig {
elfFile, err := elf.Open(decryptToolPath)
if err != nil {
log.Fatalln(err)
}
// Check binary is supported
if elfFile.Machine.String() != "EM_X86_64" {
log.Fatalf("%s is an unsupported architecture\n", elfFile.Machine.String())
}
printSections(elfFile)
text := elfFile.Section(".text")
if text == nil {
log.Fatalln("Failed to find .text section")
}
data := elfFile.Section(".data")
if data == nil {
log.Fatalln("Failed to find .data section")
}
// Get .text section data
textData, err := text.Data()
if err != nil {
log.Fatalln(err)
}
configLoadFromBufferOffset, err := FindWithSignature(textData, ConfigLoadFromBufferSignature)
if err != nil {
log.Fatalln("Failed to find ConfigLoadFromBuffer in the binary.", err)
}
mainOffset, err := FindWithSignature(textData, MainSignature)
if err != nil {
log.Fatalln("Failed to find main in the binary.", err)
}
getLogicByDataSizeOffset, err := FindWithSignature(textData, GetLogicByDataSizeSignature)
if err != nil {
log.Fatalln("Failed to find main in the binary.", err)
}
log.Debugf("Found ConfigLoadFromBuffer at 0x%x\n", configLoadFromBufferOffset)
log.Debugf("Found main at 0x%x\n", mainOffset)
log.Debugf("Found GetLogicByDataSize at 0x%x\n", getLogicByDataSizeOffset)
// Get .data section data
dataData, err := data.Data()
if err != nil {
log.Fatalln(err)
}
// Disassemble
engine, err := gapstone.New(
// TODO: Support more archs
gapstone.CS_ARCH_X86,
gapstone.CS_MODE_64,
)
if err != nil {
log.Fatalln("Failed to open gapstone")
}
defer engine.Close()
instructions, err := engine.Disasm(
textData, // code buffer
text.Addr,
conf.MaxInstructionsToDisassemble, // instructions to disassemble, 0 for all
)
if err != nil {
log.Fatalf("Disassembly error: %v", err)
}
// Find the call to ConfigLoadFromBuffer in main
configBufferDataOffset := findDataOffsetToConfigBuffer(text.Addr, data.Addr, instructions, configLoadFromBufferOffset, mainOffset)
log.Debugf("Found config at .data+0x%x:\n%s\n", configBufferDataOffset, hex.Dump(dataData[configBufferDataOffset:configBufferDataOffset+1024]))
// Find pointer to the decrypt logic buffer using the first lea instruction in GetLogicByDataSize
decryptLogicDataOffset := findDataOffsetToLogicBuffer(text.Addr, data.Addr, instructions, getLogicByDataSizeOffset)
log.Debugf("Found decrypt logic at .data+0x%x:\n%s\n", decryptLogicDataOffset, hex.Dump(dataData[decryptLogicDataOffset:decryptLogicDataOffset+128]))
config := parseConfig(elfFile, dataData, configBufferDataOffset)
config.DecryptLogicList = parseLogic(elfFile, dataData, decryptLogicDataOffset)
log.Println(config.DecryptLogicList)
return config
}
func findDataOffsetToConfigBuffer(
textAddr uint64,
dataAddr uint64,
instructions []gapstone.Instruction,
configLoadFromBufferOffset uint64,
mainOffset uint64,
) uint64 {
mainIndex := 0
callIndex := 0
for i, ins := range instructions {
if uint64(ins.Address) == textAddr+mainOffset {
mainIndex = i
}
if mainIndex != 0 {
log.Debugf("0x%x:\t%s\t\t%s\n", ins.Address, ins.Mnemonic, ins.OpStr)
}
var configLoadFromBufferOpStr string
configLoadFromBufferOpStr = fmt.Sprintf("0x%x", textAddr+configLoadFromBufferOffset)
if mainIndex != 0 && ins.Mnemonic == "call" && ins.OpStr == configLoadFromBufferOpStr {
log.Debugf("Found call to ConfigLoadFromBuffer in main at 0x%x.\n", ins.Address)
callIndex = i
break
}
}
// Find pointer to the config buffer
var configBufferOffset uint64
var leaIns gapstone.Instruction
log.Debugln("Working backwards to get the pointer to the config buffer")
for i := callIndex; i > -1; i-- {
ins := instructions[i]
log.Debugf("0x%x: %s\t%s\n", ins.Address, ins.Mnemonic, ins.OpStr)
if ins.Mnemonic == "lea" && ins.OpStr[:3] == "rax" {
leaIns = ins
log.Debugf("Found the write to rax at 0x%x\n", ins.Address)
fmt.Fscanf(strings.NewReader(ins.OpStr[12:len(ins.OpStr)-1]), "0x%x", &configBufferOffset)
break
}
}
if configBufferOffset == 0 {
log.Fatalln("Failed to find pointer to the config buffer")
}
log.Debugf("Config buffer pointer: 0x%x\n", configBufferOffset)
ripValue := leaIns.Address + leaIns.Size
log.Debugf("ripValue: 0x%x\n", ripValue)
return uint64(ripValue) + configBufferOffset - dataAddr
}
func findDataOffsetToLogicBuffer(
textAddr uint64,
dataAddr uint64,
instructions []gapstone.Instruction,
getLogicByDataSizeOffset uint64,
) uint64 {
var leaIns gapstone.Instruction
var decryptLogicBufferOffset uint64
GetLogicByDataSizeIndex := 0
for i, ins := range instructions {
if uint64(ins.Address) == textAddr+getLogicByDataSizeOffset {
GetLogicByDataSizeIndex = i
}
if GetLogicByDataSizeIndex != 0 {
log.Debugf("0x%x:\t%s\t\t%s\n", ins.Address, ins.Mnemonic, ins.OpStr)
}
if GetLogicByDataSizeIndex != 0 && ins.Mnemonic == "lea" {
leaIns = ins
log.Debugf("Found first lea in GetLogicByDataSize at 0x%x.\n", ins.Address)
fmt.Fscanf(strings.NewReader(ins.OpStr[12:len(ins.OpStr)-1]), "0x%x", &decryptLogicBufferOffset)
break
}
}
if decryptLogicBufferOffset == 0 {
log.Fatalln("Failed to find pointer to the decrypt logic buffer")
}
ripValue := leaIns.Address + leaIns.Size
log.Debugf("ripValue: 0x%x\n", ripValue)
log.Debugf("decryptLogicBufferOffset: 0x%x\n", decryptLogicBufferOffset)
return uint64(ripValue) + decryptLogicBufferOffset - dataAddr
}