-
-
Save EgeBalci/44b830146f55e9e1eaad83be13f1951b to your computer and use it in GitHub Desktop.
WoW64 Heaven's Gate
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
#include "stdafx.h" | |
#define DB(_val_) __asm __emit (_val_) | |
#define INVALID_SYSCALL (DWORD)(-1) | |
// code selectors | |
#define CS_32 0x23 | |
#define CS_64 0x33 | |
#define RAX 0 | |
#define RCX 1 | |
#define RDX 2 | |
#define RBX 3 | |
#define RSP 4 | |
#define RBP 5 | |
#define RSI 6 | |
#define RDI 7 | |
#define R8 8 | |
#define R9 9 | |
#define R10 10 | |
#define R11 11 | |
#define R12 12 | |
#define R13 13 | |
#define R14 14 | |
#define R15 15 | |
#define X64_PUSH(_r_) DB(0x48 | ((_r_) >> 3)) DB(0x50 | ((_r_) & 7)) | |
#define X64_POP(_r_) DB(0x48 | ((_r_) >> 3)) DB(0x58 | ((_r_) & 7)) | |
// Switch processor to long mode | |
#define X64_ENTER_CS(_cs_) \ | |
{ \ | |
DB(0x6a) DB(_cs_) /* push @cs */ \ | |
DB(0xe8) DB(0x00) DB(0x00) DB(0x00) DB(0x00) /* call $+5 */ \ | |
DB(0x83) DB(0x04) DB(0x24) DB(0x05) /* add dword [esp], 5 */ \ | |
DB(0xcb) /* retf */ \ | |
} | |
// Switch processor to WOW64 mode | |
#define X64_EXIT_CS(_cs_) \ | |
{ \ | |
DB(0xe8) DB(0x00) DB(0x00) DB(0x00) DB(0x00) /* call $+5 */ \ | |
DB(0xc7) DB(0x44) DB(0x24) DB(0x04) DB(_cs_) /* mov dword [rsp + 4], @cs */ \ | |
DB(0x00) DB(0x00) DB(0x00) \ | |
DB(0x83) DB(0x04) DB(0x24) DB(0x0d) /* add dword [rsp], 0xD */ \ | |
DB(0xcb) /* retf */ \ | |
} | |
#define X64_SYSCALL() \ | |
{ \ | |
DB(0x0f) DB(0x05) \ | |
} | |
// 64-bit assembly helpers | |
#define X64_ENTER() X64_ENTER_CS(CS_64) | |
#define X64_EXIT() X64_EXIT_CS(CS_32) | |
#define GET_NEXT_ARG() i < ArgsCount ? Args[i++] : 0 | |
DWORD x64_Syscall(DWORD dwSyscallNumber, PDWORD64 Args, DWORD ArgsCount) | |
{ | |
DWORD Status = 0, OldStack = 0, i = 0; | |
if (dwSyscallNumber == INVALID_SYSCALL) | |
{ | |
return STATUS_UNSUCCESSFUL; | |
} | |
DWORD64 _rcx = GET_NEXT_ARG(); | |
DWORD64 _rdx = GET_NEXT_ARG(); | |
DWORD64 _r8 = GET_NEXT_ARG(); | |
DWORD64 _r9 = GET_NEXT_ARG(); | |
DWORD64 StackArgs = NULL; | |
DWORD64 StackArgsCount = 0; | |
if (ArgsCount > 4) | |
{ | |
StackArgs = (DWORD64)&Args[3]; | |
StackArgsCount = ArgsCount - 4; | |
} | |
__asm | |
{ | |
push ebx | |
push esi | |
push edi | |
/** | |
* Stack align for 64-bit mode. | |
*/ | |
mov OldStack, esp | |
and esp, 0xfffffff0 | |
/** | |
* Swith to the long mode. | |
*/ | |
X64_ENTER() | |
/** | |
* Copy register arguments. | |
*/ | |
push _rcx | |
X64_POP(RCX) | |
push _rdx | |
X64_POP(RDX) | |
push _r8 | |
X64_POP(R8) | |
push _r9 | |
X64_POP(R9) | |
/** | |
* Push stack arguments. | |
*/ | |
push StackArgs | |
X64_POP(RDI) | |
push StackArgsCount | |
X64_POP(RSI) | |
test esi, esi | |
jz _call | |
_loop_push: | |
push [edi + esi * 8] | |
sub esi, 1 | |
jnz _loop_push | |
_call: | |
/** | |
* Perform a system call. | |
*/ | |
push _rcx | |
X64_POP(R10) | |
mov eax, dwSyscallNumber | |
sub esp, 0x28 | |
X64_SYSCALL() | |
add esp, 0x28 | |
/** | |
* Save returned status code. | |
*/ | |
mov Status, eax | |
/** | |
* Remove syscall arguments from the stack. | |
*/ | |
push StackArgsCount | |
X64_POP(RSI) | |
test esi, esi | |
jz _end | |
_loop_pop: | |
pop edi | |
sub esi, 1 | |
jnz _loop_pop | |
_end: | |
/** | |
* Swith back to the 32-bit mode. | |
*/ | |
X64_EXIT() | |
mov esp, OldStack | |
pop edi | |
pop esi | |
pop ebx | |
} | |
return Status; | |
} | |
DWORD x64_Call(DWORD64 ProcAddress, PDWORD64 Args, DWORD ArgsCount) | |
{ | |
DWORD Retval = 0, OldStack = 0, i = 0; | |
DWORD64 _rcx = GET_NEXT_ARG(); | |
DWORD64 _rdx = GET_NEXT_ARG(); | |
DWORD64 _r8 = GET_NEXT_ARG(); | |
DWORD64 _r9 = GET_NEXT_ARG(); | |
DWORD64 StackArgs = NULL; | |
DWORD64 StackArgsCount = 0; | |
if (ArgsCount > 4) | |
{ | |
StackArgs = (DWORD64)&Args[3]; | |
StackArgsCount = ArgsCount - 4; | |
} | |
__asm | |
{ | |
push ebx | |
push esi | |
push edi | |
/** | |
* Stack align for 64-bit mode. | |
*/ | |
mov OldStack, esp | |
and esp, 0xfffffff0 | |
/** | |
* Swith to the long mode. | |
*/ | |
X64_ENTER() | |
/** | |
* Copy register arguments. | |
*/ | |
push _rcx | |
X64_POP(RCX) | |
push _rdx | |
X64_POP(RDX) | |
push _r8 | |
X64_POP(R8) | |
push _r9 | |
X64_POP(R9) | |
/** | |
* Push stack arguments. | |
*/ | |
push StackArgs | |
X64_POP(RDI) | |
push StackArgsCount | |
X64_POP(RSI) | |
test esi, esi | |
jz _call | |
_loop_push: | |
push [edi + esi * 8] | |
sub esi, 1 | |
jnz _loop_push | |
_call: | |
/** | |
* Perform a function call. | |
*/ | |
push ProcAddress | |
X64_POP(RAX) | |
sub esp, 0x20 | |
call eax | |
add esp, 0x20 | |
/** | |
* Save returned status code. | |
*/ | |
mov Retval, eax | |
/** | |
* Remove syscall arguments from the stack. | |
*/ | |
push StackArgsCount | |
X64_POP(RSI) | |
test esi, esi | |
jz _end | |
_loop_pop: | |
pop edi | |
sub esi, 1 | |
jnz _loop_pop | |
_end: | |
/** | |
* Swith back to the 32-bit mode. | |
*/ | |
X64_EXIT() | |
mov esp, OldStack | |
pop edi | |
pop esi | |
pop ebx | |
} | |
return Retval; | |
} | |
void x64_Memcpy(PVOID Dest, DWORD64 Source, DWORD64 Len) | |
{ | |
DWORD64 Destination = (DWORD64)Dest; | |
__asm | |
{ | |
push esi | |
push edi | |
/** | |
* Swith to the long mode. | |
*/ | |
X64_ENTER() | |
/** | |
* Copy register arguments. | |
*/ | |
push Destination | |
X64_POP(RDI) | |
push Source | |
X64_POP(RSI) | |
push Len | |
X64_POP(RCX) | |
/** | |
* Perform memory regio copying. | |
*/ | |
rep movsb | |
/** | |
* Swith back to the 32-bit mode. | |
*/ | |
X64_EXIT() | |
pop edi | |
pop esi | |
} | |
} | |
DWORD64 x64_GetTeb(void) | |
{ | |
LARGE_INTEGER Retval; | |
Retval.QuadPart = 0; | |
__asm | |
{ | |
/** | |
* Swith to the long mode. | |
*/ | |
X64_ENTER() | |
X64_PUSH(R12) | |
pop Retval.LowPart | |
/** | |
* Swith back to the 32-bit mode. | |
*/ | |
X64_EXIT() | |
} | |
return Retval.QuadPart; | |
} | |
#define TEB64_ProcessEnvironmentBlock 0x60 | |
DWORD64 x64_GetPeb(void) | |
{ | |
DWORD64 Peb = 0; | |
DWORD64 Teb = x64_GetTeb(); | |
x64_Memcpy(&Peb, Teb + TEB64_ProcessEnvironmentBlock, sizeof(Peb)); | |
return Peb; | |
} | |
typedef DWORD64 PTR64; | |
#include <pshpack8.h> | |
typedef struct _LIST_ENTRY_64 | |
{ | |
PTR64 Flink; | |
PTR64 Blink; | |
} LIST_ENTRY_64; | |
typedef struct _UNICODE_STRING_64 | |
{ | |
USHORT Length; | |
USHORT MaximumLength; | |
PTR64 Buffer; | |
} UNICODE_STRING64, | |
*PUNICODE_STRING64; | |
typedef struct _STRING_64 | |
{ | |
USHORT Length; | |
USHORT MaximumLength; | |
PTR64 Buffer; | |
} ANSI_STRING_64, | |
*PANSI_STRING_64; | |
typedef struct _LDR_DATA_TABLE_ENTRY_64 | |
{ | |
LIST_ENTRY_64 InLoadOrderModuleList; | |
LIST_ENTRY_64 InMemoryOrderModuleList; | |
LIST_ENTRY_64 InInitializationOrderModuleList; | |
PTR64 DllBase; | |
PTR64 EntryPoint; | |
ULONG SizeOfImage; | |
UNICODE_STRING64 FullDllName; | |
UNICODE_STRING64 BaseDllName; | |
ULONG Flags; | |
USHORT LoadCount; | |
USHORT TlsIndex; | |
LIST_ENTRY_64 HashLinks; | |
PTR64 SectionPointer; | |
ULONG CheckSum; | |
ULONG TimeDateStamp; | |
} LDR_DATA_TABLE_ENTRY_64, | |
*PLDR_DATA_TABLE_ENTRY_64; | |
typedef struct _PEB_LDR_DATA_64 | |
{ | |
ULONG Length; | |
BOOLEAN Initialized; | |
PTR64 SsHandle; | |
LIST_ENTRY_64 ModuleListLoadOrder; | |
LIST_ENTRY_64 ModuleListMemoryOrder; | |
LIST_ENTRY_64 ModuleListInitOrder; | |
} PEB_LDR_DATA_64, | |
*PPEB_LDR_DATA_64; | |
#include <poppack.h> | |
#define PEB64_Ldr 0x18 | |
DWORD64 x64_GetModuleBase(char *lpszName) | |
{ | |
// get PEB pointer | |
PTR64 Peb = x64_GetPeb(); | |
LIST_ENTRY_64 ListEntry; | |
// get loader tables | |
PTR64 LdrData = 0; | |
x64_Memcpy(&LdrData, Peb + PEB64_Ldr, sizeof(LdrData)); | |
// get loaded modules list head | |
PTR64 Head = LdrData + FIELD_OFFSET(PEB_LDR_DATA_64, ModuleListLoadOrder); | |
x64_Memcpy(&ListEntry, Head, sizeof(ListEntry)); | |
PTR64 Entry = ListEntry.Flink; | |
// parse loaded module list | |
while (Entry != Head) | |
{ | |
x64_Memcpy(&ListEntry, Entry, sizeof(ListEntry)); | |
// get module information | |
LDR_DATA_TABLE_ENTRY_64 LdrData; | |
DWORD64 Addr = (DWORD64)CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY_64, InLoadOrderModuleList); | |
x64_Memcpy(&LdrData, Addr, sizeof(LdrData)); | |
// get module name | |
WCHAR szBuffer[MAX_PATH]; | |
DWORD64 dwCopyLen = min((DWORD64)LdrData.FullDllName.Length, sizeof(szBuffer) - sizeof(WCHAR)); | |
ZeroMemory(szBuffer, sizeof(szBuffer)); | |
x64_Memcpy(&szBuffer, LdrData.FullDllName.Buffer, dwCopyLen); | |
char szPath[MAX_PATH]; | |
WideCharToMultiByte(CP_ACP, 0, szBuffer, -1, szPath, MAX_PATH, NULL, NULL); | |
strlwr(szPath); | |
if (!strcmp(GetNameFromFullPath(szPath), lpszName)) | |
{ | |
return LdrData.DllBase; | |
} | |
Entry = ListEntry.Flink; | |
} | |
return NULL; | |
} | |
DWORD64 x64_GetProcAddressEx(DWORD64 ModuleBase, char *lpszName) | |
{ | |
// get DOS header | |
IMAGE_DOS_HEADER DosHeader; | |
x64_Memcpy(&DosHeader, ModuleBase, sizeof(DosHeader)); | |
// get NT headers | |
IMAGE_NT_HEADERS64 Headers; | |
x64_Memcpy(&Headers, ModuleBase + DosHeader.e_lfanew, sizeof(Headers)); | |
DWORD ExportAddr = Headers.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; | |
DWORD ExportSize = Headers.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; | |
if (ExportAddr) | |
{ | |
// get export directory | |
IMAGE_EXPORT_DIRECTORY Export; | |
x64_Memcpy(&Export, ModuleBase + ExportAddr, sizeof(Export)); | |
// parse exports | |
for (DWORD i = 0; i < Export.NumberOfFunctions; i++) | |
{ | |
// get function name | |
DWORD NameAddr = NULL; | |
x64_Memcpy(&NameAddr, ModuleBase + Export.AddressOfNames + i * sizeof(DWORD), sizeof(NameAddr)); | |
char szName[MAX_PATH]; | |
ZeroMemory(szName, sizeof(szName)); | |
x64_Memcpy(&szName, ModuleBase + NameAddr, strlen(lpszName) + 1); | |
if (!strcmp(szName, lpszName)) | |
{ | |
// get function address | |
USHORT Ordinal = 0; | |
x64_Memcpy(&Ordinal, ModuleBase + Export.AddressOfNameOrdinals + i * sizeof(USHORT), sizeof(Ordinal)); | |
DWORD Addr = 0; | |
x64_Memcpy(&Addr, ModuleBase + Export.AddressOfFunctions + Ordinal * sizeof(DWORD), sizeof(Addr)); | |
// check for the forwarded export | |
if (Addr > ExportAddr && | |
Addr < ExportAddr + ExportSize) | |
{ | |
return NULL; | |
} | |
return ModuleBase + Addr; | |
} | |
} | |
} | |
return 0; | |
} | |
__declspec(align(16)) | |
typedef struct _GET_PROC_ADDRESS_PARAMS | |
{ | |
PTR64 FunctionAddress; | |
ANSI_STRING_64 FunctionName; | |
char Name[MAX_PATH]; | |
} GET_PROC_ADDRESS_PARAMS, | |
*PGET_PROC_ADDRESS_PARAMS; | |
typedef NTSTATUS (WINAPI * func_LdrGetProcedureAddress)( | |
HMODULE ModuleHandle, | |
PANSI_STRING FunctionName, | |
WORD Oridinal, | |
PVOID *FunctionAddress | |
); | |
DWORD64 m_LdrGetProcedureAddress = 0; | |
DWORD64 x64_GetProcAddress(DWORD64 ModuleBase, char *lpszName) | |
{ | |
if (m_LdrGetProcedureAddress == 0) | |
{ | |
// get ntdll!LdrGetProcedureAddress() | |
DWORD64 Ntdll = x64_GetModuleBase("ntdll.dll"); | |
m_LdrGetProcedureAddress = x64_GetProcAddressEx(Ntdll, "LdrGetProcedureAddress"); | |
if (m_LdrGetProcedureAddress == 0) | |
{ | |
return 0; | |
} | |
} | |
/* | |
Arguments addresses must have a proper alignment, so | |
we allocating them at separate memory page instead of the stack. | |
*/ | |
PGET_PROC_ADDRESS_PARAMS Params = (PGET_PROC_ADDRESS_PARAMS)VirtualAlloc( | |
NULL, sizeof(GET_PROC_ADDRESS_PARAMS), | |
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE | |
); | |
if (Params == NULL) | |
{ | |
return 0; | |
} | |
// setup ntdll!LdrGetProcedureAddress() arguments | |
strcpy_s(Params->Name, lpszName); | |
Params->FunctionName.Buffer = (PTR64)&Params->Name; | |
Params->FunctionName.MaximumLength = | |
Params->FunctionName.Length = strlen(Params->Name); | |
Params->FunctionAddress = NULL; | |
DWORD64 Args[] = | |
{ | |
ModuleBase, | |
(DWORD64)&Params->FunctionName, 0, | |
(DWORD64)&Params->FunctionAddress | |
}; | |
// perform x64 function call | |
DWORD64 Status = x64_Call(m_LdrGetProcedureAddress, Args, 4); | |
DWORD64 Retval = Params->FunctionAddress; | |
VirtualFree(Params, 0, MEM_RELEASE); | |
if (NT_SUCCESS(Status)) | |
{ | |
return Retval; | |
} | |
return 0; | |
} | |
#include <pshpack8.h> | |
typedef struct _OBJECT_ATTRIBUTES_64 | |
{ | |
ULONG Length; | |
PTR64 RootDirectory; | |
PTR64 ObjectName; | |
ULONG Attributes; | |
PTR64 SecurityDescriptor; | |
PTR64 SecurityQualityOfService; | |
} OBJECT_ATTRIBUTES_64, | |
*POBJECT_ATTRIBUTES_64; | |
typedef struct _IO_STATUS_BLOCK_64 | |
{ | |
union | |
{ | |
NTSTATUS Status; | |
PTR64 Pointer; | |
}; | |
PTR64 Information; | |
} IO_STATUS_BLOCK_64, | |
*PIO_STATUS_BLOCK_64; | |
#include <poppack.h> | |
__declspec(align(16)) | |
typedef struct _NT_OPEN_FILE_PARAMS | |
{ | |
PTR64 FileHandle; | |
IO_STATUS_BLOCK_64 IoStatusBlock; | |
UNICODE_STRING64 usName; | |
OBJECT_ATTRIBUTES_64 ObjectAttributes; | |
WCHAR Name[MAX_PATH]; | |
} NT_OPEN_FILE_PARAMS, | |
*PNT_OPEN_FILE_PARAMS; | |
int _tmain(int argc, _TCHAR* argv[]) | |
{ | |
DWORD64 ProcAddress = x64_GetProcAddress(x64_GetModuleBase("ntdll.dll"), "NtOpenFile"); | |
if (ProcAddress == 0) | |
{ | |
return -1; | |
} | |
/* | |
Syscall arguments addresses must have a proper alignment, so | |
we allocating them at separate memory page instead of the stack. | |
*/ | |
PNT_OPEN_FILE_PARAMS Params = (PNT_OPEN_FILE_PARAMS)VirtualAlloc( | |
NULL, sizeof(NT_OPEN_FILE_PARAMS), | |
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE | |
); | |
if (Params == NULL) | |
{ | |
return -1; | |
} | |
ACCESS_MASK DesiredAccess = FILE_GENERIC_READ; | |
ULONG ShareAccess = FILE_SHARE_READ; | |
ULONG OpenOptions = 0; | |
wcscpy_s(Params->Name, L"\\??\\C:\\Windows\\system32\\ntdll.dll"); | |
Params->usName.Buffer = (PTR64)&Params->Name; | |
Params->usName.Length = Params->usName.MaximumLength = (USHORT)wcslen(Params->Name) * 2; | |
Params->ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES_64); | |
Params->ObjectAttributes.RootDirectory = NULL; | |
Params->ObjectAttributes.ObjectName = (PTR64)&Params->usName; | |
Params->ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE; | |
Params->ObjectAttributes.SecurityDescriptor = Params->ObjectAttributes.SecurityQualityOfService = NULL; | |
DWORD64 Args[] = | |
{ | |
(DWORD64)&Params->FileHandle, | |
(DWORD64)DesiredAccess, | |
(DWORD64)&Params->ObjectAttributes, | |
(DWORD64)&Params->IoStatusBlock, | |
(DWORD64)ShareAccess, | |
(DWORD64)OpenOptions | |
}; | |
DWORD Status = x64_Call(ProcAddress, Args, 6); | |
printf("Status = 0x%.8x, FileHandle = 0x%.8x\n", Status, (HANDLE)Params->FileHandle); | |
printf("Press any key to quit...\n"); | |
_getch(); | |
if (NT_SUCCESS(Status)) | |
{ | |
CloseHandle((HANDLE)Params->FileHandle); | |
} | |
VirtualFree(Params, 0, MEM_RELEASE); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment