-
Notifications
You must be signed in to change notification settings - Fork 70
Expand file tree
/
Copy pathInflativeLoading.py
More file actions
578 lines (461 loc) · 22.5 KB
/
InflativeLoading.py
File metadata and controls
578 lines (461 loc) · 22.5 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
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
import ctypes, struct
from keystone import *
import argparse
import random
def print_banner():
banner="""
██╗███╗ ██╗███████╗██╗ █████╗ ████████╗██╗██╗ ██╗███████╗
██║████╗ ██║██╔════╝██║ ██╔══██╗╚══██╔══╝██║██║ ██║██╔════╝
██║██╔██╗ ██║█████╗ ██║ ███████║ ██║ ██║██║ ██║█████╗
██║██║╚██╗██║██╔══╝ ██║ ██╔══██║ ██║ ██║╚██╗ ██╔╝██╔══╝
██║██║ ╚████║██║ ███████╗██║ ██║ ██║ ██║ ╚████╔╝ ███████╗
╚═╝╚═╝ ╚═══╝╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═══╝ ╚══════╝
██╗ ██████╗ █████╗ ██████╗ ██╗███╗ ██╗ ██████╗
██║ ██╔═══██╗██╔══██╗██╔══██╗██║████╗ ██║██╔════╝
██║ ██║ ██║███████║██║ ██║██║██╔██╗ ██║██║ ███╗
██║ ██║ ██║██╔══██║██║ ██║██║██║╚██╗██║██║ ██║
███████╗╚██████╔╝██║ ██║██████╔╝██║██║ ╚████║╚██████╔╝
╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝
"""
print(banner)
print("Author: Senzee")
print("Github Repository: https://github.com/senzee1984/InflativeLoading")
print("Twitter: senzee@1984")
print("Website: https://winslow1984.com")
print("Description: Dynamically convert a native PE to PIC shellcode")
print("Attention: Bugs are expected, more support and improvements are coming!\n\n\n")
def generate_asm_by_cmdline(new_cmd):
new_cmd_length = len(new_cmd) * 2 + 12
unicode_cmd = [ord(c) for c in new_cmd]
fixed_instructions = [
"mov rsi, [rax + 0x20]; # RSI = Address of ProcessParameter",
"add rsi, 0x70; # RSI points to CommandLine member",
f"mov byte ptr [rsi], {new_cmd_length}; # Set Length to the length of new commandline",
"mov byte ptr [rsi+2], 0xff; # Set the max length of cmdline to 0xff bytes",
"mov rsi, [rsi+8]; # RSI points to the string",
"mov dword ptr [rsi], 0x002e0031; # Push '.1'",
"mov dword ptr [rsi+0x4], 0x00780065; # Push 'xe'",
"mov dword ptr [rsi+0x8], 0x00200065; # Push ' e'"
]
start_offset = 0xC
dynamic_instructions = []
for i, char in enumerate(unicode_cmd):
hex_char = format(char, '04x')
offset = start_offset + (i * 2)
if i % 2 == 0:
dword = hex_char
else:
dword = hex_char + dword
instruction = f"mov dword ptr [rsi+0x{offset-2:x}], 0x{dword};"
dynamic_instructions.append(instruction)
if len(unicode_cmd) % 2 != 0:
instruction = f"mov word ptr [rsi+0x{offset:x}], 0x{dword};"
dynamic_instructions.append(instruction)
final_offset = start_offset + len(unicode_cmd) * 2
dynamic_instructions.append(f"mov byte ptr [rsi+0x{final_offset:x}], 0;")
instructions = fixed_instructions + dynamic_instructions
return "\n".join(instructions)
def read_dump_file(file_path):
with open(file_path, 'rb') as file:
return bytearray(file.read())
def print_shellcode(sc):
for i in range(min(20, len(sc))):
line = sc[i * 20:(i + 1) * 20]
formatted_line = ''.join([f"\\x{b:02x}" for b in line])
print(f"buf += b\"{formatted_line}\"")
print("......"+str(len(sc)-400) +" more bytes......")
def generate_nop_sequence(desired_length):
print("[+] Generating NOP-like instructions to pad shellcode stub up to 0x1000 bytes")
nop_like_instructions = [
{"instruction": [0x90], "length": 1}, # NOP
{"instruction": [0x86, 0xdb], "length": 2}, # xchg bl, bl;
{"instruction": [0x66, 0x87, 0xf6], "length": 3}, # xchg si, si;
{"instruction": [0x48, 0x9c, 0x48, 0x93], "length": 4}, # xchg rax, rbx; xchg rbx, rax;
{"instruction": [0x66, 0x83, 0xc2, 0x00], "length": 4}, # add dx, 0
{"instruction": [0x48, 0xff, 0xc0, 0x48, 0xff, 0xc8], "length": 6}, # inc rax; dec rax;
{"instruction": [0x49, 0xf7, 0xd8, 0x49, 0xf7, 0xd8], "length": 6}, # neg r8; neg 48;
{"instruction": [0x48, 0x83, 0xc0, 0x01, 0x48, 0xff, 0xc8], "length": 7}, # add rax,0x1; dec rax;
{"instruction": [0x48, 0x83, 0xe9, 0x2, 0x48, 0xff, 0xc1, 0x48, 0xff, 0xc1], "length": 10}, # sub rcx, 2; inc rcx; inc rcx
]
sequence = bytearray()
current_length = 0
while current_length < desired_length:
available_instructions = [instr for instr in nop_like_instructions if current_length + instr["length"] <= desired_length]
if not available_instructions:
sequence.extend([0x90] * (desired_length - current_length))
break
selected_instruction = random.choice(available_instructions)
sequence.extend(selected_instruction["instruction"])
current_length += selected_instruction["length"]
#sequence_hex = ' '.join(format(byte, '02x') for byte in sequence)
return sequence
def obfuscate_header(pe_header_array, segments):
obfuscated_header = bytearray(random.getrandbits(8) for _ in range(len(pe_header_array)))
# Iterate through each segment and restore the original bytes in those segments
for segment in segments:
offset, length = next(iter(segment.items()))
obfuscated_header[offset:offset + length] = pe_header_array[offset:offset + length]
return obfuscated_header
def modify_pe_signatures(segments):
print("[!] Dynamically generated instructions to obfuscate remained PE signatures:")
instructions = []
for segment in segments:
offset, size = next(iter(segment.items()))
# for segment, size in segments.items():
if size == 2: # For WORD
random_value = random.getrandbits(16)
instruction = f" mov word ptr [rbx+{offset:#x}], {random_value:#04x};"
elif size == 4: # For DWORD
random_value = random.getrandbits(32)
instruction = f" mov dword ptr [rbx+{offset:#x}], {random_value:#08x};"
elif size == 8: # For QWORD
random_value1 = random.getrandbits(32)
random_value2 = random.getrandbits(32)
instruction = f" mov dword ptr [rbx+{offset:#x}], {random_value:#08x};\n mov dword ptr [rbx+{offset+4:#x}], {random_value2:#08x};"
else:
raise ValueError("Unsupported size for segment.")
instructions.append(instruction)
return "\n".join(instructions)
if __name__ == "__main__":
print_banner()
parser = argparse.ArgumentParser(description='Dynamically generate shellcode stub to append to the dump file')
parser.add_argument('--file', '-f', required=True, dest='input_file',help='The input binary file dumped by DumpPEFromMemory.exe')
parser.add_argument('--cmdline', '-c', required=False, default="", dest='cmdline',help='Supplied command line')
parser.add_argument('--bin', '-b', required=True, dest='bin',help='Save the PIC code as a bin file')
parser.add_argument('--obfuscate', '-o', required=False, default="false", dest='obfus',help='Save the PIC code as a bin file')
parser.add_argument('--execution', '-e', required=False, default='False', dest='sc_exec',help='(Only Windows) Immediately execute shellcoded PE? True/False')
args = parser.parse_args()
input_file= args.input_file
cmdline = args.cmdline
bin = args.bin
sc_exec = args.sc_exec
obfus = args.obfus
pe_array = read_dump_file(input_file)
update_cmdline_asm = generate_asm_by_cmdline(cmdline) # Generate shellcode that used to update command line
CODE = (
"start:"
" and rsp, 0xFFFFFFFFFFFFFFF0;" # Stack alignment
" xor rdx, rdx;"
" mov rax, gs:[rdx+0x60];" # RAX = PEB Address
"update_cmdline:"
f"{update_cmdline_asm}"
"find_kernel32:"
" mov rsi,[rax+0x18];" # RSI = Address of _PEB_LDR_DATA
" mov rsi,[rsi + 0x30];" # RSI = Address of the InInitializationOrderModuleList
" mov r9, [rsi];"
" mov r9, [r9];"
" mov r9, [r9+0x10];" # kernel32.dll
" jmp function_stub;" # Jump to func call stub
"parse_module:" # Parsing DLL file in memory
" mov ecx, dword ptr [r9 + 0x3c];" # R9 = Base address of the module, ECX = NT header offset
" xor r15, r15;"
" mov r15b, 0x88;" # Offset to Export Directory
" add r15, r9;"
" add r15, rcx;" # R15 points to Export Directory
" mov r15d, dword ptr [r15];" # R15 = RVA of export directory
" add r15, r9;" # R15 = VA of export directory
" mov ecx, dword ptr [r15 + 0x18];" # ECX = # of function names as an index value
" mov r14d, dword ptr [r15 + 0x20];" # R14 = RVA of ENPT
" add r14, r9;" # R14 = VA of ENPT
"search_function:" # Search for a given function
" jrcxz not_found;" # If RCX = 0, the given function is not found
" dec ecx;" # Decrease index by 1
" xor rsi, rsi;"
" mov esi, [r14 + rcx*4];" # RVA of function name
" add rsi, r9;" # RSI points to function name string
"function_hashing:" # Hash function name function
" xor rax, rax;"
" xor rdx, rdx;"
" cld;" # Clear DF flag
"iteration:" # Iterate over each byte
" lodsb;" # Copy the next byte of RSI to Al
" test al, al;" # If reaching the end of the string
" jz compare_hash;" # Compare hash
" ror edx, 0x0d;" # Part of hash algorithm
" add edx, eax;" # Part of hash algorithm
" jmp iteration;" # Next byte
"compare_hash:" # Compare hash
" cmp edx, r8d;" # R8 = Supplied function hash
" jnz search_function;" # If not equal, search the previous function (index decreases)
" mov r10d, [r15 + 0x24];" # Ordinal table RVA
" add r10, r9;" # R10 = Ordinal table VMA
" movzx ecx, word ptr [r10 + 2*rcx];" # Ordinal value -1
" mov r11d, [r15 + 0x1c];" # RVA of EAT
" add r11, r9;" # r11 = VA of EAT
" mov eax, [r11 + 4*rcx];" # RAX = RVA of the function
" add rax, r9;" # RAX = VA of the function
" ret;"
"not_found:"
" xor rax, rax;" # Return zero
" ret;"
"function_stub:"
" mov rbp, r9;" # RBP stores base address of Kernel32.dll
" mov r8d, 0xec0e4e8e;" # LoadLibraryA Hash
" call parse_module;" # Search LoadLibraryA's address
" mov r12, rax;" # R12 stores the address of LoadLibraryA function
" mov r8d, 0x7c0dfcaa;" # GetProcAddress Hash
" call parse_module;" # Search GetProcAddress's address
" mov r13, rax;" # R13 stores the address of GetProcAddress function
)
ks = Ks(KS_ARCH_X86, KS_MODE_64)
encoding, count = ks.asm(CODE)
CODE_LEN = len(encoding) + 25
CODE_OFFSET = 4096 - CODE_LEN
e_lfanew, = struct.unpack('<I', pe_array[0x3c:0x3c+4])
print("[!] The offset to NT header is "+ str(hex(e_lfanew)))
segments = [{0x3c: 4}, {e_lfanew+0x28: 4}, {e_lfanew+0x30: 8}, {e_lfanew+0x50: 4}, {e_lfanew+0x90: 8}, {e_lfanew+0xb0: 8}, {e_lfanew+0xf0: 8}] # Segments in PE header that need to be intact
# e_lfanew, entry point, preferred address, size of image, export directory, import directory, baserelocation directory, delayed import directory
if obfus.lower() == "true":
print("[!] Depending on the program, obfuscation may not be compatible with it. Make sure you know how does the program work!")
obfuscated_signatures = modify_pe_signatures(segments)
else:
obfuscated_signatures = ""
print(obfuscated_signatures+"\n")
CODE2 = (
" jmp fix_import_dir;" # Jump to fix_iat section
"find_nt_header:" # Quickly return NT header in RAX
" xor rax, rax;"
" mov eax, [rbx+0x3c];" # EAX contains e_lfanew
" add rax, rbx;" # RAX points to NT Header
" ret;"
"fix_import_dir:" # Init necessary variable for fixing IAT
" xor rsi, rsi;"
" xor rdi, rdi;"
f"lea rbx, [rip+{CODE_OFFSET}];" # Jump to the dump file
" call find_nt_header;"
" mov esi, [rax+0x90];" # ESI = ImportDir RVA
" add rsi, rbx;" # RSI points to ImportDir
" mov edi, [rax+0x94];" # EDI = ImportDir Size
" add rdi, rsi;" # RDI = ImportDir VA + Size
"loop_module:"
" cmp rsi, rdi;" # Compare current descriptor with the end of import directory
" je loop_end;" # If equal, exit the loop
" xor rdx ,rdx;"
" mov edx, [rsi+0x10];" # EDX = IAT RVA (32-bit)
" test rdx, rdx;" # Check if ILT RVA is zero (end of descriptors)
" je loop_end;" # If zero, exit the loop
" xor rcx, rcx;"
" mov ecx, [rsi+0xc];" # RCX = Module Name RVA
" add rcx, rbx;" # RCX points to Module Name
" call r12;" # Call LoadLibraryA
" xor rdx ,rdx;"
" mov edx, [rsi+0x10];" # Restore IAT RVA
" add rdx, rbx;" # RDX points to IAT
" mov rcx, rax;" # Module handle for GetProcAddress
" mov r14, rdx;" # Backup IAT Address
"loop_func:"
" mov rdx, r14;" # Restore IAT address + processed entries
" mov rdx, [rdx];" # RDX = Ordinal or RVA of HintName Table
" test rdx, rdx;" # Check if it's the end of the IAT
" je next_module;" # If zero, move to the next descriptor
" mov r9, 0x8000000000000000;"
" test rdx, r9;" # Check if it is import by ordinal (highest bit set)
" mov rbp, rcx;" # Save module base address
" jnz resolve_by_ordinal;" # If set, resolve by ordinal
"resolve_by_name:"
" add rdx, rbx;" # RDX = HintName Table VA
" add rdx, 2;" # RDX points to Function Name
" call r13;" # Call GetProcAddress
" jmp update_iat;" # Go to update IAT
"resolve_by_ordinal:"
" mov r9, 0x7fffffffffffffff;"
" and rdx, r9;" # RDX = Ordinal number
" call r13;" # Call GetProcAddress with ordinal
"update_iat:"
" mov rcx, rbp;" # Restore module base address
" mov rdx, r14;" # Restore IAT Address + processed entries
" mov [rdx], rax;" # Write the resolved address to the IAT
" add r14, 0x8;" # Movce to the next ILT entry
" jmp loop_func;" # Repeat for the next function
"next_module:"
" add rsi, 0x14;" # Move to next import descriptor
" jmp loop_module;" # Continue loop
"loop_end:"
"fix_basereloc_dir:" # Save RBX //dq rbx+21b0 l46
" xor rsi, rsi;"
" xor rdi, rdi;"
" xor r8, r8;" # Empty R8 to save page RVA
" xor r9, r9;" # Empty R9 to place block size
" xor r15, r15;"
" call find_nt_header;"
" mov esi, [rax+0xb0];" # ESI = BaseReloc RVA
" add rsi, rbx;" # RSI points to BaseReloc
" mov edi, [rax+0xb4];" # EDI = BaseReloc Size
" add rdi, rsi;" # RDI = BaseReloc VA + Size
" mov r15d, [rax+0x28];" # R15 = Entry point RVA
" add r15, rbx;" # R15 = Entry point
" mov r14, [rax+0x30];" # R14 = Preferred address
" sub r14, rbx;" # R14 = Delta address
" mov [rax+0x30], rbx;" # Update Image Base Address
" mov r8d, [rsi];" # R8 = First block page RVA
" add r8, rbx;" # R8 points to first block page (Should add an offset later)
" mov r9d, [rsi+4];" # First block's size
" xor rax, rax;"
" xor rcx, rcx;"
"loop_block:"
" cmp rsi, rdi;" # Compare current block with the end of BaseReloc
" jge basereloc_fixed_end;" # If equal, exit the loop
" xor r8, r8;"
" mov r8d, [rsi];" # R8 = Current block's page RVA
" call find_nt_header;" # Reach NT Header
" add rax, 0x50;" # Reach image size field
" cmp r8d, [rax];" # Compare page rva and image size
" jg basereloc_fixed_end;" # Finish base relocation fixing process
" add r8, rbx;" # R8 points to current block page (Should add an offset later)
" mov r11, r8;" # Backup R8
" xor r9, r9;"
" mov r9d, [rsi+4];" # R9 = Current block size
" add rsi, 8;" # RSI points to the 1st entry, index for inner loop for all entries
" mov rdx, rsi;"
" add rdx, r9;"
" sub rdx, 8;" # RDX = End of all entries in current block
"loop_entries:"
" cmp rsi, rdx;" # If we reached the end of current block
" jz next_block;" # Move to next block
" xor rax, rax;"
" mov ax, [rsi];" # RAX = Current entry value
" test rax, rax;" # If entry value is 0
" jz skip_padding_entry;" # Reach the end of entry and the last entry is a padding entry
" mov r10, rax;" # Copy entry value to R10
" and eax, 0xfff;" # Offset, 12 bits
" add r8, rax;" # Added an offset
" call find_nt_header;" # Reach NT Header
" add rax, 0x50;" # Reach image size field
" sub r8, rbx;"
" cmp r8d, [rax];" # Compare page rva and image size
" jg basereloc_fixed_end;" # Finish base relocation fixing process
" add r8, rbx;"
"update_entry:"
" sub [r8], r14;" # Update the address
" mov r8, r11;" # Restore r8
" add rsi, 2;" # Move to next entry by adding 2 bytes
" jmp loop_entries;"
"skip_padding_entry:" # If the last entry is a padding entry
" add rsi, 2;" # Directly skip this entry
"next_block:"
" jmp loop_block;"
"basereloc_fixed_end:"
"fix_delayed_import_dir:"
" call find_nt_header;"
" mov esi, [rax+0xf0];" # ESI = DelayedImportDir RVA
" test esi, esi;" # If RVA = 0?
" jz delayed_loop_end;" # Skip delay import table fix
" add rsi, rbx;" # RSI points to DelayedImportDir
"delayed_loop_module:"
" xor rcx, rcx;"
" mov ecx, [rsi+4];" # RCX = Module name string RVA
" test rcx, rcx;" # If RVA = 0, then all modules are processed
" jz delayed_loop_end;" # Exit the module loop
" add rcx, rbx;" # RCX = Module name
" call r12;" # Call LoadLibraryA
" mov rcx, rax;" # Module handle for GetProcAddress for 1st arg
" xor r8, r8;"
" xor rdx, rdx;"
" mov edx, [rsi+0x10];" # EDX = INT RVA
" add rdx, rbx;" # RDX points to INT
" mov r8d, [rsi+0xc];" # R8 = IAT RVA
" add r8, rbx;" # R8 points to IAT
" mov r14, rdx;" # Backup INT Address
" mov r15, r8;" # Backup IAT Address
"delayed_loop_func:"
" mov rdx, r14;" # Restore INT Address + processed data
" mov r8, r15;" # Restore IAT Address + processed data
" mov rdx, [rdx];" # RDX = Name Address RVA
" test rdx, rdx;" # If Name Address value is 0, then all functions are fixed
" jz delayed_next_module;" # Process next module
" mov r9, 0x8000000000000000;"
" test rdx, r9;" # Check if it is import by ordinal (highest bit set of NameAddress)
" mov rbp, rcx;" # Save module base address
" jnz delayed_resolve_by_ordinal;" # If set, resolve by ordinal
"delayed_resolve_by_name:"
" add rdx, rbx;" # RDX points to NameAddress Table
" add rdx, 2;" # RDX points to Function Name
" call r13;" # Call GetProcAddress
" jmp delayed_update_iat;" # Go to update IAT
"delayed_resolve_by_ordinal:"
" mov r9, 0x7fffffffffffffff;"
" and rdx, r9;" # RDX = Ordinal number
" call r13;" # Call GetProcAddress with ordinal
"delayed_update_iat:"
" mov rcx, rbp;" # Restore module base address
" mov r8, r15;" # Restore current IAT address + processed
" mov [r8], rax;" # Write the resolved address to the IAT
" add r15, 0x8;" # Move to the next IAT entry (64-bit addresses)
" add r14, 0x8;" # Movce to the next INT entry
" jmp delayed_loop_func;" # Repeat for the next function
"delayed_next_module:"
" add rsi, 0x20;" # Move to next delayed imported module
" jmp delayed_loop_module;" # Continue loop
"delayed_loop_end:"
"all_completed:"
" call find_nt_header;"
" xor r9, r9;"
" mov r9d, dword ptr [rax+0x28];" # R9 = Entry point RVA
" mov rcx, rbx;" # RCX = Image Base
f"{obfuscated_signatures}"
" add rbx, r9;" # RBX = Entry Point
" xor rdx, rdx;"
" inc rdx;"
" xor r8, r8;"
" push r13;" # Save GetProcAddress
" push rcx;" # Save Image Base
" sub rsp, 0x30;"
" call rbx;"
" add rsp, 0x30;"
" pop rcx;" # Recover GetProcAddress
" pop r13;"
" xor rdx, rdx;"
" mov rax, gs:[rdx+0x60];" # RAX = PEB Address
" mov rcx,[rax+0x18];" # RCX = Address of _PEB_LDR_DATA
" mov rcx,[rcx + 0x30];" # RCX = Address of the InInitializationOrderModuleList
" mov rcx, [rcx];"
" mov rcx, [rcx];"
" mov rcx, [rcx+0x10];" # kernel32
" xor rdx, rdx;"
" push rdx;" # Align 16 bytes
" push rdx;"
" mov rdx, 0x737365636F725065;" # Push string "ssecorPe" to RDX
" push rdx;"
" mov rdx, 0x74616E696D726554;" # Push string "tanimreT" to RDX
" push rdx;"
" mov rdx, rsp;"
" sub rsp, 0x30;"
" call r13;" # Get TerminateProcess address
" add rsp, 0x30;"
" xor rcx, rcx;"
" dec rcx;" # HANDLE = -1
" xor rdx, rdx;" # uExitCode = 0
" sub rsp, 0x30;"
" call rax;"
" add rsp, 0x30;"
)
ks2 = Ks(KS_ARCH_X86, KS_MODE_64)
encoding2, count2 = ks.asm(CODE2)
encoding = encoding + encoding2
sh = b""
for e in encoding:
sh += struct.pack("B", e)
shellcode = bytearray(sh)
print("[+] Shellcode Stub size: "+str(len(shellcode))+" bytes")
nop_segments = generate_nop_sequence(0x1000-len(shellcode)) # Pad the shellcode stub up to 0x1000 bytes
if obfus.lower() == "true":
merged_shellcode = shellcode + nop_segments + obfuscate_header(pe_array[0:0x1000], segments) + pe_array[0x1000:]
else:
merged_shellcode = shellcode + nop_segments + pe_array
print("[!] Shellcoded PE's size: "+str(len(merged_shellcode))+" bytes\n\n")
print_shellcode(merged_shellcode)
try:
with open(bin, 'wb') as f:
f.write(merged_shellcode)
print("\n\nGenerated shellcode successfully saved in file "+bin)
except Exception as e:
print(e)
if sc_exec.lower() == "true":
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(merged_shellcode)), ctypes.c_int(0x3000), ctypes.c_int(0x40))
buf = (ctypes.c_char * len(merged_shellcode)).from_buffer(merged_shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr), buf, ctypes.c_int(len(merged_shellcode)))
print("\n\n[#] Shellcode located at address %s" % hex(ptr))
input("\n[!] PRESS TO EXECUTE SHELLCODED EXE...")
ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0), ctypes.c_int(0), ctypes.c_uint64(ptr), ctypes.c_int(0), ctypes.c_int(0), ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))