HackingTeam Soldier Implant
Unpacking VMProtect Soldier Implant
Overview
HackingTeam Soldier Implant (packed): 76840fa18df8764afb51f1aa6da10ff65f1bdfe434dc988917380fa31fbe3a73
All samples on available on Malshare.
References:
Unpacking VMP
- Run wihtout debugger and pause with ProcessHacker
- Attach with x64dbg and search
.text
for MSVC securty cookie constant - Based on security cookie constant location determine __security_init_cookie function location
- Add hardware bp on __security_init_cookie function and relaunch process with debugger
- Use call stack to determine what called __security_init_cookie function this is OEP
- Remove hardware bp from __security_init_cookie and add hardware bp on OEP
- Relaunch program so we break on OEP
- Use VMPImportFixer to dump process
- Fix OEP statically (PEBear)
TODO: When we dump with VMPImportFixer there are some imports that are not protected in the binary. These are not captured by VMPImportFixer which means some of our imports are not resolved in the dump! We could maybe fix this by either patching VMPImportFixer to also add unprotected imports ... or we could update VMPFix to add both protected imports and normal imports to the new IAT then dump with Scylla instead of VMPImportFixer.
def unhex(hex_string):
import binascii
if type(hex_string) == str:
return binascii.unhexlify(hex_string.encode('utf-8'))
else:
return binascii.unhexlify(hex_string)
def tohex(data):
import binascii
if type(data) == str:
return binascii.hexlify(data.encode('utf-8'))
else:
return binascii.hexlify(data)
data_len = 0x240
data = unhex('238ba1f22cb105cdae82eb2dde4e2783c80370198e1bb02c231825e7b2d8c164109e5a4e09c63a36df5ed3c5ce49b825968d386eb9008ed2b6cae3b75cd8ba76d49ab1acf69d46a747934187883b2eb27aaab99dae892693dc714433ec2de2d238a036f5ff492414a6a4928f4bd907342e7fff0dd6e61f2a7675b351842a27bc1ba7d5ad7ebc44a11cfafd136f42644db89db7189c4d152a06b10f0c0a4b40c021d7192a04cd383b17434048768076c7d7eb073ef340c696aa35946aacbe99821352487082660334de43ca4e5e142d78f70dc7cb5b571463c3c3c7caf461eac5411136142b0051914f74859c44610f8e4fdcf4f6c269b03809e3aa67ebc3474ece444fe2f5078d36857c17147d834c6cb6ccd9effefca8b24ee01c0868fc1383ef26519c4f573f94b5bbb7b149dcc190f0a08fdc31965d69d97bbcf565e829182b8aa421f46a7e0100180d7ed9d31083b4b68431e0b888bbacb00864f140716359290eb37927ec0ea537b620e1108265f412a28c28c2df0cf1d713880cd874f79872dde1060d29cc7536684d49b504dcfc61c80a1210a5e69533bba05aad0a6dc7239ea90e29e822e0424f06524330a98f40e56c1149bad0350b6c79b1c03963e0cf43dce2f6f6e6412dee43ef937bff51bf345e57bc60c7df2ec4294cc2789547896a53dc43ab5fc15c6a377d0dbab97ff5e091f90fc28ad7acda94543b52bccea2c8f360e03a5fc01cab6d358804037ab20e17facc31293b437b7a22d8c32f835308fae01b83396667f18b47882e93b309ee31cc3e9c619a6a405db7e3bbcc')
data = data[:data_len]
key = unhex('60ab854458b00a742c6e8ceb7f1094da')
def decrypt(data, key):
from Crypto.Cipher import AES
cipher = AES.new(key, AES.MODE_CBC, iv=b'\x00'*16)
return cipher.decrypt(data)
# remember to split the rest of the string after the null
decrypt(data, key)
.text:012EF39B 3D EF BE AD DE cmp eax, 0DEADBEEFh
.text:012EF3A0 75 09 jnz short loc_12EF3AB
.text:012EF3A2 B8 01 00 00 00 mov eax, 1
.text:012EF3A7 8B E5 mov esp, ebp
.text:012EF3A9 5D pop ebp
.text:012EF3AA C3 retn
.text:012EF3AB 56 push esi
.text:012EF3AC 68 58 B5 38 01 push offset config_key
.text:012EF3B1 50 push eax
.text:012EF3B2 68 57 02 38 01 push offset asc_1380257 ; "#"
.text:012EF3B7 E8 84 14 00 00 call mw_aes_decrypt
import pefile
import re
import struct
pe_data = open('/tmp/work/ht.exe.fixed', 'rb').read()
pe = pefile.PE(data = pe_data)
target_code = pe_data.split(b'\xEF\xBE\xAD\xDE')[1]
egg = b'\x68(....).\x68(....)\xe8'
m = re.search(egg, target_code)
if not m:
print("All hope is lost!")
config_va = struct.unpack('<I', m.group(2))[0]
key_va_bytes = m.group(1)
config_offset = pe.get_offset_from_rva(config_va - pe.OPTIONAL_HEADER.ImageBase)
config_len = struct.unpack('<I', pe_data[config_offset -4:config_offset])[0]
config_data = pe_data[config_offset:config_offset+config_len]
tohex(config_data)
.text:013031B6 0F 10 05 7A 01 38 01 movups xmm0, xmmword_138017A
.text:013031BD 0F 11 05 58 B5 38 01 movups config_key, xmm0
key_egg = b'\x0F\x10\x05(....)\x0F\x11\x05' + key_va_bytes
m = re.search(key_egg, pe_data)
if not m:
print("All hope is lost!")
key_data_va = struct.unpack('<I', m.group(1))[0]
key_data_offset = pe.get_offset_from_rva(key_data_va - pe.OPTIONAL_HEADER.ImageBase)
key_data = pe_data[key_data_offset:key_data_offset+16]
tohex(key_data)
decrypt(config_data, key_data)