Script that decrypts most of PrivateLoader stack strings - https://tavares.re/blog/2022/06/06/hunting-privateloader-pay-per-install-service/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pefile | |
import struct | |
from capstone import * | |
def extract_var(op): | |
if ']' in op: | |
op = ''.join(op.split(' ')[-1])[:-1].replace('[', '') | |
return op | |
def search(instructions, var): | |
data_chunks = [] | |
for inst in instructions: | |
if inst[2] == 'mov': | |
try: | |
imm = int(inst[3].split(' ')[-1], 16) | |
data_chunks.append(struct.pack('<I', imm)) | |
if extract_var(inst[3].split(', ')[0]) == var: | |
return b''.join(data_chunks[::-1]) # 16 bytes str chunk | |
except: # not a dword | |
pass | |
if extract_var(inst[3].split(', ')[0]) == var: | |
var = extract_var(inst[3].split(', ')[1]) | |
def decrypt_strings(filename): | |
# disassemble .text section | |
pe = pefile.PE(filename) | |
md = Cs(CS_ARCH_X86, CS_MODE_32) | |
md.skipdata = True | |
instructions = [] | |
text = pe.sections[0] | |
text_addr = pe.OPTIONAL_HEADER.ImageBase + text.VirtualAddress | |
for (addr, size, mnemonic, op_str) in md.disasm_lite(text.get_data(), text_addr): | |
instructions.append((addr, size, mnemonic, op_str)) | |
# search, build and decrypt strings | |
strings = [] | |
addr = None | |
string = '' | |
for i, inst in enumerate(instructions): | |
if inst[2] == 'pxor': | |
try: # possible string decryption found | |
encrypted_str = search(instructions[:i][::-1], extract_var(inst[3].split(', ')[0])) # reverse search | |
key = search(instructions[:i][::-1], extract_var(inst[3].split(', ')[1])) # reverse search | |
string += bytearray(encrypted_str[j] ^ key[j] for j in range(len(key))).decode(errors='ignore') # bug | |
if not addr: | |
addr = hex(inst[0]) | |
if '\x00' in string: | |
strings.append((addr, string.replace('\x00', ''))) | |
string = '' | |
addr = None | |
except Exception as e: | |
print(f'Fail at {hex(inst[0])}') | |
print(len(strings)) | |
for s in strings: | |
print(f'{s[0]} {s[1]}') | |
decrypt_strings('aa2c0a9e34f9fa4cbf1780d757cc84f32a8bd005142012e91a6888167f80f4d5') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment