commit 2129994ceb71eb4ab6904cf1bf163f4c4be191b0 Author: Geert Ijewski Date: Sun Nov 10 16:14:02 2019 +0100 direct syscalls independent of the underlying bit width diff --git a/get_syscall64_ids.cpp b/get_syscall64_ids.cpp new file mode 100644 index 0000000..a7411d3 --- /dev/null +++ b/get_syscall64_ids.cpp @@ -0,0 +1,516 @@ +#include +#include +#include + +#include "structs.h" +#include "wow64ext.h" +#include "misc.h" +#include "syscall64.h" +#include "get_syscall64_ids.h" + +/** +\file +*/ + +/** +\brief All functions doing direct syscalls are saved with a hash of their name +and the accompying syscall ID in the table (API_TO_INDEX*) ID_table. +To get the ID for a given hash the table can be searched. +(To speed up the search the table is sorted and a binary search is used) +*/ +struct API_TO_INDEX +{ + DWORD hash; + DWORD id; +} *ID_table = NULL; +DWORD ID_table_count = 0; //!< Count of entries in ID_table + +/** + \brief Parses the ID out of a x64 function. + \return Returns the ID or INVALID_SYSCALL_ID on err +*/ +static DWORD get_syscall_id(LPBYTE function) +{ + if(!function) + return INVALID_SYSCALL_ID; + + /* + 00000000`77b61800 4c8bd1 mov r10,rcx + 00000000`77b61803 b852000000 mov eax,52h + 00000000`77b61808 0f05 syscall + + On Windows 10 it's: + 0:000> u NtOpenFile + ntdll!NtOpenFile: + 00007ffe`62bf6720 4c8bd1 mov r10,rcx + 00007ffe`62bf6723 b833000000 mov eax,33h + 00007ffe`62bf6728 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1 + 00007ffe`62bf6730 7503 jne ntdll!NtOpenFile+0x15 (00007ffe`62bf6735) + 00007ffe`62bf6732 0f05 syscall + 00007ffe`62bf6734 c3 ret + 00007ffe`62bf6735 cd2e int 2Eh + 00007ffe`62bf6737 c3 ret + */ + + const unsigned char MOV_R10_RCX_OPCODE[] = {0x4c, 0x8b, 0xd1}; + if(memcmp(function, MOV_R10_RCX_OPCODE, sizeof(MOV_R10_RCX_OPCODE))) + return INVALID_SYSCALL_ID; + + const unsigned char SYSCALL_OPCODE[] = {0x0f, 0x05}; + const size_t SYSCALL_OFFSET_OLD = 8, + SYSCALL_OFFSET_10 = 0x12; + uint8_t* old = function + SYSCALL_OFFSET_OLD; + uint8_t* win10 = function + SYSCALL_OFFSET_10; + if (memcmp(old, SYSCALL_OPCODE, sizeof(SYSCALL_OPCODE)) && + memcmp(win10, SYSCALL_OPCODE, sizeof(SYSCALL_OPCODE))) + { + return INVALID_SYSCALL_ID; + } + + const unsigned char MOV_EAX_OPCODE = 0xB8; + const size_t MOV_EAX_ID_OFFSET = 3; + if(MOV_EAX_OPCODE != *(function + MOV_EAX_ID_OFFSET)) + return INVALID_SYSCALL_ID; + + return *(PDWORD)(function + MOV_EAX_ID_OFFSET + 1); +} + + +/** + \brief Parses the given image to get the ID or just count them. + \return Number of entries found / populated + \param imageBase Image base of the DLL the info will be taken from + \param ids The struct the info is put into. If NULL - won't be touched +*/ +static DWORD get_syscall_ids(LPVOID imageBase, API_TO_INDEX* ids) +{ + if(!imageBase) + return 0; + + PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)imageBase; + // not a valid DOS header + if(IMAGE_DOS_SIGNATURE != dos->e_magic) + return 0; + + PIMAGE_NT_HEADERS64 nt = (PIMAGE_NT_HEADERS64)((LPBYTE)imageBase + dos->e_lfanew); + // not a valid PE or not the correct architecture + if(IMAGE_NT_SIGNATURE != nt->Signature || IMAGE_FILE_MACHINE_AMD64 != nt->FileHeader.Machine) + { + return 0; + } + + // No exports? + if(!nt->OptionalHeader.DataDirectory->Size) + return 0; + + DWORD count = 0; //! Number of functions who do direct syscalls + IMAGE_EXPORT_DIRECTORY *exportDir = (IMAGE_EXPORT_DIRECTORY *)((LPBYTE)imageBase + nt->OptionalHeader.DataDirectory->VirtualAddress); + PDWORD nameRef = (DWORD *)((LPBYTE)imageBase + exportDir->AddressOfNames); + WORD* ordinal = (WORD *)((LPBYTE)imageBase + exportDir->AddressOfNameOrdinals); + DWORD* addressOfFunctions = (DWORD*)((LPBYTE)imageBase + exportDir->AddressOfFunctions); + + printf("Total functions: %d\n", exportDir->NumberOfNames); + for(DWORD i = 0; i < exportDir->NumberOfNames; i++, nameRef++) + { + const char* name = (const char*)((LPBYTE)imageBase + (*nameRef)); + LPBYTE address = (LPBYTE)imageBase + addressOfFunctions[ordinal[i]]; + + DWORD id = get_syscall_id(address); + if(INVALID_SYSCALL_ID == id) + continue; + + // Put it into the table + if(ids) + { + ids[count].hash = hash(name); + ids[count].id = id; + } + // Print info + else + { + printf("%s (%X) = %X\n", name, hash(name), id); + } + count++; + } + + return count; +} + +/** +\brief (Bubble)Sorts the ID_table so it can be searched via binary search +*/ +static void sort_ID_table() +{ + DWORD n = ID_table_count; + + bool swapped = false; + do + { + for(unsigned int i = 0; i < n - 1; ++i) + { + if(ID_table[i].hash > ID_table[i + 1].hash) + { + API_TO_INDEX tmp = {ID_table[i].hash, ID_table[i].id}; + + ID_table[i].hash = ID_table[i + 1].hash; + ID_table[i].id = ID_table[i + 1].id; + + ID_table[i + 1].hash = tmp.hash; + ID_table[i + 1].id = tmp.id; + + swapped = true; + } + } + n = n - 1; + } while(swapped == true && n); +} + +/** +\brief Gets the path to the x64 ntdll in native format +\param path Output buffer +*/ +static void get_ntdll_path(wchar_t* path) +{ + wchar_t systemDirectory[MAX_PATH]; + + if(!path) + return; + + GetSystemDirectory(systemDirectory, _countof(systemDirectory)); + + wsprintfW(path, L"\\??\\%s\\ntdll.dll", systemDirectory); +} +/** +*/ +static BOOL read_ntdll(LPBYTE& content, DWORD& size) +{ + wchar_t ntdllPath[MAX_PATH]; //! Path in the native format + get_ntdll_path(ntdllPath); + printf("%ls\n", ntdllPath); + + _UNICODE_STRING_T filename = {lstrlenW(ntdllPath) * sizeof(WCHAR), MAX_PATH * sizeof(WCHAR), (DWORD64)ntdllPath}; + _OBJECT_ATTRIBUTES_T obja = {sizeof(_OBJECT_ATTRIBUTES_T), NULL, (DWORD64)&filename, OBJ_CASE_INSENSITIVE, NULL, NULL}; + _IO_STATUS_BLOCK_T iostatusblock = {0}; + + _HANDLE_T fileHandle = {(DWORD64)INVALID_HANDLE_VALUE}; + NTSTATUS stat = DO_SYSCALL(get_basic_syscall_ID(NTOPENFILE), &fileHandle, + FILE_READ_DATA | SYNCHRONIZE, &obja, &iostatusblock, FILE_SHARE_READ, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); + if(STATUS_SUCCESS != stat) + { + printf("NTSTATUS: %X\tHandle: %llX\n", stat, fileHandle.h); + return FALSE; + } + printf("Handle: %llX\n", fileHandle.h); + + if(!(size = GetFileSize((HANDLE)fileHandle.h, NULL))) + { + DO_SYSCALL(get_basic_syscall_ID(NTCLOSE), fileHandle.h); + return FALSE; + } + printf("Size of ntdll: %dkb\n", size / 1024); + + /* As no code is actually executed in the new ntdll - but only + a few bytes are read from it - rw is enough */ + if(!(content = (LPBYTE)VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE))) + { + DO_SYSCALL(get_basic_syscall_ID(NTCLOSE), fileHandle.h); + return FALSE; + } + printf("content %p\n", content); + + printf("ios ptr %X", &iostatusblock); + LARGE_INTEGER offset = {0}; + stat = DO_SYSCALL(get_basic_syscall_ID(NTREADFILE), + (HANDLE)fileHandle.h, + NULL, // event + NULL, // ApcRoutine + NULL, // ApcContext + &iostatusblock, + content, + size, + &offset, + NULL); // key + if(STATUS_SUCCESS != stat) + { + VirtualFree(content, 0, MEM_RELEASE); + content = NULL; + DO_SYSCALL(get_basic_syscall_ID(NTCLOSE), fileHandle.h); + printf("Reading failed: %X\t%X != %X\n", stat, size, offset.LowPart); + return FALSE; + } + printf("ios: %X\toffset: %X\n", iostatusblock.Status, offset.LowPart); + + DO_SYSCALL(get_basic_syscall_ID(NTCLOSE), fileHandle.h); + return TRUE; +} + +/** + \brief (Partly) maps the image. + + This does NOT fix relocations, imports, TLS, sets section protections yadda yadda. + As this image is only used to parse out the IDs that is no problem. + \param content Raw data of the file to be mapped + \param mappedImage The finished image +*/ +static BOOL map_PE(LPBYTE content, LPBYTE* mappedImage) +{ + PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)content; + if(IMAGE_DOS_SIGNATURE != dos->e_magic) + { + printf("Not a valid DOS header: %s\n", content); + return FALSE; + } + + PIMAGE_NT_HEADERS64 nt = (PIMAGE_NT_HEADERS64)(content + dos->e_lfanew); + if(IMAGE_NT_SIGNATURE != nt->Signature) + { + printf("Not a valid PE header\n"); + return FALSE; + } + + /* Must be mapped properly (Actually almost - + only the code section would be needed) so the IDs can be be + parsed properly */ + if(!(*mappedImage = (LPBYTE)VirtualAlloc(NULL, nt->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE))) + { + printf("Can't alloc\n"); + return FALSE; + } + + printf("Let's map that shit @ %p. %X sections. %X imageSize\n", *mappedImage, + nt->FileHeader.NumberOfSections, nt->OptionalHeader.SizeOfImage); + + // Copy up to the first section (Size of optional header + delta optional header to start of image) + memcpy(*mappedImage, content, nt->FileHeader.SizeOfOptionalHeader + ((LPBYTE)&nt->OptionalHeader - (LPBYTE)content)); + + // Copy sections +#define IMAGE_FIRST_SECTION64( ntheader ) ((PIMAGE_SECTION_HEADER) \ + ((ULONG_PTR)(ntheader)+\ + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + \ + ((ntheader))->FileHeader.SizeOfOptionalHeader \ + )) + PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION64(nt); + for(unsigned int i = 0; i < nt->FileHeader.NumberOfSections; i++) + { + printf("Copy %Xbytes @ RVA %X From %X\n", pish->SizeOfRawData, pish->VirtualAddress, pish->PointerToRawData); + memcpy((*mappedImage + pish->VirtualAddress), (content + pish->PointerToRawData), + pish->SizeOfRawData); + pish++; + } + + return TRUE; +} + +/** +\brief Initalize the ID_table. +*/ +BOOL initalize_ID_table() +{ + LPBYTE ntdll = NULL, imageBase = NULL; + DWORD ntdllSize = 0; + if(!read_ntdll(ntdll, ntdllSize)) + return FALSE; + if(!map_PE(ntdll, &imageBase)) + return FALSE; + + ID_table_count = get_syscall_ids(imageBase, NULL); + printf("%i functions who do direct syscalls\n", ID_table_count); + if(!ID_table_count) + return FALSE; + + if(!(ID_table = (API_TO_INDEX*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ID_table_count * sizeof(API_TO_INDEX)))) + return FALSE; + + if(!get_syscall_ids(imageBase, ID_table)) + return FALSE; + + VirtualFree(ntdll, 0, MEM_RELEASE); + VirtualFree(imageBase, 0, MEM_RELEASE); + + sort_ID_table(); + + return TRUE; +} + +/** + \brief Has a few hardcoded IDs and returns the correct one for the current OS + + Which IDs exactly are hardcoded can be seen in the enum. + Table made mostly with data from: + x86: http://j00ru.vexillium.org/ntapi/ + WOW64: http://j00ru.vexillium.org/ntapi_64/ + + \fixme >=Win8 Support? +*/ +DWORD get_basic_syscall_ID(SYSCALL_IDS func) +{ + _KUSER_SHARED_DATA* _kuser_s_d = GET_KUSER_SHARED_DATA(); + ULONG majorVersion = _kuser_s_d->NtMajorVersion; + ULONG minorVersion = _kuser_s_d->NtMinorVersion; + NT_PRODUCT_TYPE productType = _kuser_s_d->NtProductType; + _PEB* p = (_PEB*)__readfsdword(0x30); + ULONG buildID = p->NtBuildNumber; + + switch(majorVersion) + { + case 5: + // XP + if(1 == minorVersion || + (2 == minorVersion && VER_NT_WORKSTATION == productType)) + { + if(NTOPENFILE == func) + return 0x30; + else if(NTCREATEFILE == func) + return 0x52; + else if(NTREADFILE == func) + return 0x03; + else if(NTCLOSE == func) + return 0x0C; + } + // Server 2003 + else + { + printf("SRV03 unsupported\n"); // fixme + if(NTOPENFILE == func) + return 0x7a; + else if(NTCREATEFILE == func) + return 0x26; + else if(NTREADFILE == func) + return 0xbf; + else if(NTCLOSE == func) + return 0x1b; + } + break; + case 6: + switch(minorVersion) + { + // Vista SP0-2 & Server 2008 + case 0: + printf("Vista unsupported\n"); // fixme + if(NTOPENFILE == func) + return 0xba; + else if(NTCREATEFILE == func) + return 0x3c; + else if(NTREADFILE == func) + return 0x102; + else if(NTCLOSE == func) + return 0x30; + break; + // Win7 + case 1: + if(NTOPENFILE == func) + return 0x30; + else if(NTCREATEFILE == func) + return 0x52; + else if(NTREADFILE == func) + return 0x03; + else if(NTCLOSE == func) + return 0x0C; + // Win 8 + case 2: + printf("Win8 unsupported\n"); // fixme + if(NTOPENFILE == func) + return 0xe7; + else if(NTCREATEFILE == func) + return 0x15f; + else if(NTREADFILE == func) + return 0x86; + else if(NTCLOSE == func) + return 0x0171; + break; + } + break; + // Win 10 + case 10: + if (NTOPENFILE == func) + return 0x0033; + else if (NTCREATEFILE == func) + return 0x55; + else if (NTREADFILE == func) + return 0x06; + else if (NTCLOSE == func) + return 0x0f; + } + + return INVALID_SYSCALL_ID; +} + +DWORD get_syscall_ID(DWORD func) +{ + DWORD left = 0, right = ID_table_count; + + while(left != right) + { + DWORD middle = (left + right) / 2; + + if(func == ID_table[middle].hash) + return ID_table[middle].id; + if(func > ID_table[middle].hash) + left = middle + 1; + else + right = middle - 1; + } + + if(func == ID_table[left].hash) + { + return ID_table[left].id; + } + + return INVALID_SYSCALL_ID; +} + +VOID destroy_ID_table() +{ + HeapFree(GetProcessHeap(), 0, ID_table); +} + +#if 0 +BOOL test_id_table() +{ + LPVOID imageBase = GetModuleHandle(TEXT("ntdll.dll")); + + PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)imageBase; + // not a valid DOS header + if(IMAGE_DOS_SIGNATURE != dos->e_magic) + return FALSE; + + PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((LPBYTE)imageBase + dos->e_lfanew); + // not a valid PE or not the correct architecture + if(IMAGE_NT_SIGNATURE != nt->Signature || IMAGE_FILE_MACHINE_I386 != nt->FileHeader.Machine) + { + return FALSE; + } + + // No exports? + if(!nt->OptionalHeader.DataDirectory->Size) + return FALSE; + + IMAGE_EXPORT_DIRECTORY *exportDir = (IMAGE_EXPORT_DIRECTORY *)((LPBYTE)imageBase + nt->OptionalHeader.DataDirectory->VirtualAddress); + PDWORD nameRef = (DWORD *)((LPBYTE)imageBase + exportDir->AddressOfNames); + WORD* ordinal = (WORD *)((LPBYTE)imageBase + exportDir->AddressOfNameOrdinals); + DWORD* addressOfFunctions = (DWORD*)((LPBYTE)imageBase + exportDir->AddressOfFunctions); + + for(DWORD i = 0; i < exportDir->NumberOfNames; i++, nameRef++) + { + const char* name = (const char*)((LPBYTE)imageBase + (*nameRef)); + LPBYTE address = (LPBYTE)imageBase + addressOfFunctions[ordinal[i]]; + + DWORD id = get_syscall_id(address); + if(INVALID_SYSCALL_ID == id) + continue; + + // At address is a function doing direct syscalls - check if can be found + if(INVALID_SYSCALL_ID == get_syscall_ID(hash(name))) + { + printf("%s not found", name); + return FALSE; + } + } + + return TRUE; +} + +#endif \ No newline at end of file diff --git a/get_syscall64_ids.h b/get_syscall64_ids.h new file mode 100644 index 0000000..e37dd0f --- /dev/null +++ b/get_syscall64_ids.h @@ -0,0 +1,71 @@ +#ifndef GET_SYSCALL64_IDS_H +#define GET_SYSCALL64_IDS_H +/** +\file +*/ + +/** +\brief Definition of the hashs of APIs and the error value INVALID_SYSCALL_ID +*/ +enum SYSCALL_IDS +{ + // Files + NTOPENFILE = 0xC29C5019, //! Supported by get_basic_syscall_ID + NTCREATEFILE = 0x15A5ECDB, //! Supported by get_basic_syscall_ID + NTREADFILE = 0x2E979AE3, //! Supported by get_basic_syscall_ID + NTCLOSE = 0x8B8E133D, //! Supported by get_basic_syscall_ID + NTWRITEFILE = 0xD69326B2, + + // Mutexes + NTCREATEMUTANT = 0x280632B4, + NTOPENMUTANT = 0xEC225D72, + NTRELEASEMUTANT = 0x29567961, + + // Registry + NTOPENKEY = 0x4BB73E02, + NTQUERYVALUEKEY = 0xB4C18A83, + + // Process + NTQUERYSYSTEMINFORMATION = 0xEE4F73A8, + + INVALID_SYSCALL_ID = 0xFFFFFFFF, //! Used to signify errors +}; + +/** +\brief Gets the basic ID for the hash given. + +This function does not dependent on the ID table but instead has +hardcoded definitions for a FEW Apis (these are marked in the +SYSCALL_IDS enum) +\param func The hash of the API that the ID is searched for +\return Returns the ID or INVALID_SYSCALL_ID +\sa get_syscall_ID() +*/ +DWORD get_basic_syscall_ID(SYSCALL_IDS func); + +/** +\brief Initalizes the ID table. +\return If FALSE no direct syscalls can be made. +\sa free_ID_table() +*/ +BOOL initalize_ID_table(); + +/** +\brief Frees the ID table. After this is done +no direct syscalls can be made anymore +\sa initalize_ID_table() +*/ +VOID destroy_ID_table(); + +/** +\brief Gets the ID for the hash given. +\pre This function does dependent on the ID table so make sure +to initalize_ID_table() first. +\param func The hash of the API that the ID is searched for +\return Returns the ID or INVALID_SYSCALL_ID +\sa initalize_ID_table() +*/ +DWORD get_syscall_ID(DWORD func); + +#endif // GET_SYSCALL64_IDS_H + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..a405d7e --- /dev/null +++ b/main.cpp @@ -0,0 +1,35 @@ +/* +there was a call to openfile that failed with STATUS_DATATYPE_MISALIGNMENT +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "structs.h" +#include "wow64ext.h" +#include "misc.h" +#include "syscall64.h" +#include "get_syscall64_ids.h" + + +BOOL file_test(); +void WINAPI RtlInitUnicodeString(unsigned char* target, PCWSTR source); + +#define PRINTF_PP(string, ...) printf(string, NARG(__VA_ARGS__), __VA_ARGS__) + +int main() +{ + print_os_info(); + + if(!initalize_ID_table()) + return 0; + + file_test(); +} + + diff --git a/misc.cpp b/misc.cpp new file mode 100644 index 0000000..604f83b --- /dev/null +++ b/misc.cpp @@ -0,0 +1,56 @@ +#include +#include +#include "structs.h" +#include "misc.h" + +BOOL is_WOW64() +{ + return NULL != __readfsdword(0xC0); +} + +VOID print_os_info() +{ + _KUSER_SHARED_DATA* _kuser_s_d = GET_KUSER_SHARED_DATA(); + ULONG majorVersion = _kuser_s_d->NtMajorVersion; + ULONG minorVersion = _kuser_s_d->NtMinorVersion; + NT_PRODUCT_TYPE productType = _kuser_s_d->NtProductType; + _PEB* p = (_PEB*)__readfsdword(0x30); + ULONG buildID = p->NtBuildNumber; + + printf("Running on %i.%i %i %X (x%s)\n", majorVersion, minorVersion, buildID, productType, (is_WOW64() ? "64" : "86")); +} + +DWORD hash(const char* str) +{ + return hash((const unsigned char*)str, strlen(str)); +} + +DWORD hash(const unsigned char* buf, const size_t sz) +{ + unsigned int hash = 5381; + + for(unsigned int i = 0; i < sz; i++) + hash = ((hash << 5) + hash) + (unsigned int)buf[i]; + + return hash; +} + + +BOOL is_executable(LPVOID addr) +{ + MEMORY_BASIC_INFORMATION mbi = {0}; + if(!VirtualQuery(addr, &mbi, sizeof(MEMORY_BASIC_INFORMATION))) + { + return FALSE; + } + return (mbi.Protect & PAGE_EXECUTE); +} + +BOOL is_Win8() +{ + KUSER_SHARED_DATA* _kuser_s_d = GET_KUSER_SHARED_DATA(); + ULONG majorVersion = _kuser_s_d->NtMajorVersion; + ULONG minorVersion = _kuser_s_d->NtMinorVersion; + + return 6 == majorVersion && 2 == minorVersion; +} \ No newline at end of file diff --git a/misc.h b/misc.h new file mode 100644 index 0000000..3fe8b25 --- /dev/null +++ b/misc.h @@ -0,0 +1,38 @@ +#ifndef MISC_H +#define MISC_H + +/** + \brief Checks if the page at addr is executable +*/ +BOOL is_executable(LPVOID addr); + +/** + \brief Checks if the process is running on WOW64 by examing Heaven's Gate +*/ +BOOL is_WOW64(); + +/** + \brief Computes the djb2 hash of the input string + \param str The data to be hashed + \return The value. +*/ +DWORD hash(const char* str); + +/** + \brief Computes the djb2 hash of the input + \param buf The data to be hashed + \param sz The size of the data + \return The value. +*/ +DWORD hash(const unsigned char* buf, const size_t sz); + +/** + \brief Prints a small summarization of the current OS +*/ +VOID print_os_info(); + +BOOL is_Win8(); + +#define GET_KUSER_SHARED_DATA() ((_KUSER_SHARED_DATA*)0x7FFE0000) + +#endif \ No newline at end of file diff --git a/structs.h b/structs.h new file mode 100644 index 0000000..55a520b --- /dev/null +++ b/structs.h @@ -0,0 +1,85 @@ +#ifndef STRUCTS_INCLUDE_HEADER +#define STRUCTS_INCLUDE_HEADER + +typedef struct _KSYSTEM_TIME { + UINT32 LowPart; + INT32 High1Time; + INT32 High2Time; +} KSYSTEM_TIME, *PKSYSTEM_TIME; +typedef enum _NT_PRODUCT_TYPE +{ + NtProductWinNt = 1, + NtProductLanManNt = 2, + NtProductServer = 3 +} NT_PRODUCT_TYPE; +typedef enum _ALTERNATIVE_ARCHITECTURE_TYPE +{ + StandardDesign = 0, + NEC98x86 = 1, + EndAlternatives = 2 +} ALTERNATIVE_ARCHITECTURE_TYPE; +typedef struct _KUSER_SHARED_DATA +{ + ULONG TickCountLowDeprecated; + ULONG TickCountMultiplier; + KSYSTEM_TIME InterruptTime; + KSYSTEM_TIME SystemTime; + KSYSTEM_TIME TimeZoneBias; + WORD ImageNumberLow; + WORD ImageNumberHigh; + WCHAR NtSystemRoot[260]; + ULONG MaxStackTraceDepth; + ULONG CryptoExponent; + ULONG TimeZoneId; + ULONG LargePageMinimum; + ULONG Reserved2[7]; + NT_PRODUCT_TYPE NtProductType; + UCHAR ProductTypeIsValid; + ULONG NtMajorVersion; + ULONG NtMinorVersion; + UCHAR ProcessorFeatures[64]; + ULONG Reserved1; + ULONG Reserved3; + ULONG TimeSlip; + ALTERNATIVE_ARCHITECTURE_TYPE AlternativeArchitecture; + LARGE_INTEGER SystemExpirationDate; + ULONG SuiteMask; + UCHAR KdDebuggerEnabled; + UCHAR NXSupportPolicy; + ULONG ActiveConsoleId; + ULONG DismountCount; + ULONG ComPlusPackage; + ULONG LastSystemRITEventTickCount; + ULONG NumberOfPhysicalPages; + UCHAR SafeBootMode; + ULONG SharedDataFlags; + ULONG DbgErrorPortPresent : 1; + ULONG DbgElevationEnabled : 1; + ULONG DbgVirtEnabled : 1; + ULONG DbgInstallerDetectEnabled : 1; + ULONG SystemDllRelocated : 1; + ULONG SpareBits : 27; + UINT64 TestRetInstruction; + ULONG SystemCall; + ULONG SystemCallReturn; + UINT64 SystemCallPad[3]; + union + { + KSYSTEM_TIME TickCount; + UINT64 TickCountQuad; + }; + ULONG Cookie; + INT64 ConsoleSessionForegroundProcessId; + ULONG Wow64SharedInformation[16]; + WORD UserModeGlobalLogger[8]; + ULONG HeapTracingPid[2]; + ULONG CritSecTracingPid[2]; + ULONG ImageFileExecutionOptions; + union + { + UINT64 AffinityPad; + ULONG ActiveProcessorAffinity; + }; + UINT64 InterruptTimeBias; +} KUSER_SHARED_DATA, *PKUSER_SHARED_DATA; +#endif // STRUCTS_INCLUDE_HEADER \ No newline at end of file diff --git a/syscall64.h b/syscall64.h new file mode 100644 index 0000000..2e73823 --- /dev/null +++ b/syscall64.h @@ -0,0 +1,43 @@ +#ifndef SYSCALL64_H +#define SYSCALL64_H +#include // eww include in headers + +extern "C" DWORD _cdecl syscall64(size_t cnt, DWORD id, ...); + +#define NARG(...) Y_TUPLE_SIZE_II((Y_TUPLE_SIZE_PREFIX_ ## __VA_ARGS__ ## _Y_TUPLE_SIZE_POSTFIX,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)) +#define Y_TUPLE_SIZE_II(__args) Y_TUPLE_SIZE_I __args + +#define Y_TUPLE_SIZE_PREFIX__Y_TUPLE_SIZE_POSTFIX ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0 + +#define Y_TUPLE_SIZE_I(__p0,__p1,__p2,__p3,__p4,__p5,__p6,__p7,__p8,__p9,__p10,__p11,__p12,__p13,__p14,__p15,__p16,__p17,__p18,__p19,__p20,__p21,__p22,__p23,__p24,__p25,__p26,__p27,__p28,__p29,__p30,__p31,__n,...) __n + + +#define DO_SYSCALL(id, ...) syscall64(NARG(__VA_ARGS__), id, __VA_ARGS__) + +#define STR_VALUE(arg) #arg + +#define ALLOC_STRUCTURE(type, name) uint8_t* name = (unsigned char*)_aligned_malloc(is_WOW64() ? sizeof(type) : sizeof(type), 16); \ + memset(name, 0, is_WOW64() ? sizeof(type) : sizeof(type)); \ + printf("%s %s @ %p\n", STR_VALUE(type), STR_VALUE(name), name); +#define FREE_STRUCTURE(type, name) _aligned_free(name); name = NULL; + + +#define GET_MEMBER_OFFSET64(type, member) offsetof(type, member) +#define GET_MEMBER_OFFSET86(type, member) offsetof(type, member) +#define GET_MEMBER_OFFSET(type, member) (is_WOW64() ? GET_MEMBER_OFFSET64(type, member) : GET_MEMBER_OFFSET86(type, member)) + +#define GET_MEMBER(type, member, object) (object + GET_MEMBER_OFFSET(type, member)) +#define SET_MEMBER(type, member, object, val) { \ + decltype(val)* tmpDst = (decltype(val)*)GET_MEMBER(type, member, object); \ + decltype(val) tmpSrc = val; \ + printf("cpy %X(%s) from/value of %X to %X\n", sizeof(tmpDst), STR_VALUE(member), val, tmpDst); \ + memcpy((void*)tmpDst, (void*)&tmpSrc, sizeof(tmpDst)); \ +} +#define SET_MEMBER_PTR(type, member, object, src) { \ + void* dstPtr = (void*)GET_MEMBER(type, member, object); \ + void* ptrToSrc = (void*)src; \ + printf("cpy ptr(%s) from %X to %X\n", STR_VALUE(member), src, dstPtr); \ + memcpy((void*)dstPtr, &ptrToSrc, 4); \ +} + +#endif \ No newline at end of file diff --git a/tests.cpp b/tests.cpp new file mode 100644 index 0000000..ff69d60 --- /dev/null +++ b/tests.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include "structs.h" +#include "misc.h" +#include "wow64ext.h" +#include "syscall64.h" +#include "get_syscall64_ids.h" + +void WINAPI RtlInitUnicodeString( + unsigned char* target, + PCWSTR source) +{ + SET_MEMBER_PTR(_UNICODE_STRING_T, Buffer, target, source); + if(source) + { + unsigned int length = lstrlenW(source) * sizeof(WCHAR); + if(length > 0xfffc) + length = 0xfffc; + SET_MEMBER(_UNICODE_STRING_T, Length, target, length); + SET_MEMBER(_UNICODE_STRING_T, MaximumLength, target, length + sizeof(WCHAR)); + } + else + { + SET_MEMBER(_UNICODE_STRING_T, Length, target, 0); + SET_MEMBER(_UNICODE_STRING_T, MaximumLength, target, 0); + } +} + + +VOID initialize_object_attributes(unsigned char* p, LPVOID n, ULONG a) +{ + SET_MEMBER(_OBJECT_ATTRIBUTES_T, uLength, p, is_WOW64() ? sizeof(_OBJECT_ATTRIBUTES_T) : sizeof(_OBJECT_ATTRIBUTES_T)); + SET_MEMBER(_OBJECT_ATTRIBUTES_T, hRootDirectory, p, NULL); + SET_MEMBER(_OBJECT_ATTRIBUTES_T, uAttributes, p, a); + SET_MEMBER_PTR(_OBJECT_ATTRIBUTES_T, pObjectName, p, n); + SET_MEMBER(_OBJECT_ATTRIBUTES_T, pSecurityDescriptor, p, NULL); // Actually a ptr but we set a NULL + SET_MEMBER(_OBJECT_ATTRIBUTES_T, pSecurityQualityOfService, p, NULL); +} + +BOOL file_test() +{ + ALLOC_STRUCTURE(_UNICODE_STRING_T, filename); + wchar_t desktopPath[MAX_PATH]; + lstrcpyW(desktopPath, L"\\??\\"); + if(!SHGetSpecialFolderPathW(HWND_DESKTOP, &desktopPath[lstrlenW(desktopPath)], CSIDL_DESKTOPDIRECTORY, FALSE)) + return FALSE; + PathAppendW(desktopPath, L"WaitAMinute.HowDidThisGetHere.txt"); + RtlInitUnicodeString(filename, desktopPath); + printf("Desktop: %ws\n", desktopPath); + + ALLOC_STRUCTURE(_OBJECT_ATTRIBUTES_T, obja); + initialize_object_attributes(obja, (void*)filename, OBJ_CASE_INSENSITIVE); + + ALLOC_STRUCTURE(_HANDLE_T, fileHandle); + ALLOC_STRUCTURE(_IO_STATUS_BLOCK_T, iostatusblock); + + NTSTATUS stat = DO_SYSCALL(get_syscall_ID(NTCREATEFILE), fileHandle, FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE, + obja, iostatusblock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OVERWRITE_IF, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); + if(STATUS_SUCCESS != stat) + { + printf("NTSTATUS: %X\n", stat); + return FALSE; + } + printf("Handle: %X\tIO.uInformation:%X\n", *(PHANDLE)GET_MEMBER(_HANDLE_T, h, fileHandle), *(PDWORD)GET_MEMBER(_IO_STATUS_BLOCK_T, uInformation, iostatusblock)); + + FREE_STRUCTURE(_UNICODE_STRING_T, filename); + FREE_STRUCTURE(_OBJECT_ATTRIBUTES_T, obja); + + const char TEST_STRING[] = {"0123456789ABCDEF"}; + if(STATUS_SUCCESS != (stat = DO_SYSCALL(get_syscall_ID(NTWRITEFILE), *(PHANDLE)GET_MEMBER(_HANDLE_T, h, fileHandle), + NULL, NULL, NULL, iostatusblock, TEST_STRING, lstrlenA(TEST_STRING), NULL, NULL))) + { + printf("Write: %X\t\n", stat); + return FALSE; + } + + FlushFileBuffers(*(PHANDLE)GET_MEMBER(_HANDLE_T, h, fileHandle)); + + char buffer[sizeof(TEST_STRING)+1] = {0}; + LARGE_INTEGER offset = {0}; + if(STATUS_SUCCESS != (stat = DO_SYSCALL(get_syscall_ID(NTREADFILE), *(PHANDLE)GET_MEMBER(_HANDLE_T, h, fileHandle), + NULL, NULL, NULL, iostatusblock, buffer, sizeof(buffer), &offset, NULL))) + { + printf("Reading failed: %X\t%X != %X\n", stat, sizeof(buffer), offset.LowPart); + return FALSE; + } + buffer[*(PDWORD)GET_MEMBER(_IO_STATUS_BLOCK_T, uInformation, iostatusblock)] = 0; + printf("io stat: %X\tio inf: %X\tread: %X\n", *(PDWORD)GET_MEMBER(_IO_STATUS_BLOCK_T, Status, iostatusblock), *(PDWORD)GET_MEMBER(_IO_STATUS_BLOCK_T, uInformation, iostatusblock), offset.LowPart); + + DO_SYSCALL(get_syscall_ID(NTCLOSE), *(PHANDLE)GET_MEMBER(_HANDLE_T, h, fileHandle)); + FREE_STRUCTURE(_HANDLE_T, fileHandle); + + printf("'%s' == '%s'\n", TEST_STRING, buffer); + return 0 == lstrcmpA(TEST_STRING, buffer); + +} diff --git a/wow64ext.h b/wow64ext.h new file mode 100644 index 0000000..7d3c7a8 --- /dev/null +++ b/wow64ext.h @@ -0,0 +1,409 @@ +/** + * + * WOW64Ext Library + * + * Copyright (c) 2014 ReWolf + * http://blog.rewolf.pl/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ +#pragma once + +#ifndef STATUS_SUCCESS +# define STATUS_SUCCESS 0 +#endif + +#pragma pack(push) +#pragma pack(1) +template +struct _LIST_ENTRY_T +{ + T Flink; + T Blink; +}; + +template +struct _UNICODE_STRING_T +{ + union + { + struct + { + WORD Length; + WORD MaximumLength; + }; + T dummy; + }; + T Buffer; +}; + +template +struct _NT_TIB_T +{ + T ExceptionList; + T StackBase; + T StackLimit; + T SubSystemTib; + T FiberData; + T ArbitraryUserPointer; + T Self; +}; + +template +struct _CLIENT_ID_T +{ + T UniqueProcess; + T UniqueThread; +}; + +template +struct _TEB_T_ +{ + _NT_TIB_T NtTib; + T EnvironmentPointer; + _CLIENT_ID_T ClientId; + T ActiveRpcHandle; + T ThreadLocalStoragePointer; + T ProcessEnvironmentBlock; + DWORD LastErrorValue; + DWORD CountOfOwnedCriticalSections; + T CsrClientThread; + T Win32ThreadInfo; + DWORD User32Reserved[26]; + //rest of the structure is not defined for now, as it is not needed +}; + +template +struct _LDR_DATA_TABLE_ENTRY_T +{ + _LIST_ENTRY_T InLoadOrderLinks; + _LIST_ENTRY_T InMemoryOrderLinks; + _LIST_ENTRY_T InInitializationOrderLinks; + T DllBase; + T EntryPoint; + union + { + DWORD SizeOfImage; + T dummy01; + }; + _UNICODE_STRING_T FullDllName; + _UNICODE_STRING_T BaseDllName; + DWORD Flags; + WORD LoadCount; + WORD TlsIndex; + union + { + _LIST_ENTRY_T HashLinks; + struct + { + T SectionPointer; + T CheckSum; + }; + }; + union + { + T LoadedImports; + DWORD TimeDateStamp; + }; + T EntryPointActivationContext; + T PatchInformation; + _LIST_ENTRY_T ForwarderLinks; + _LIST_ENTRY_T ServiceTagLinks; + _LIST_ENTRY_T StaticLinks; + T ContextInformation; + T OriginalBase; + _LARGE_INTEGER LoadTime; +}; + +template +struct _PEB_LDR_DATA_T +{ + DWORD Length; + DWORD Initialized; + T SsHandle; + _LIST_ENTRY_T InLoadOrderModuleList; + _LIST_ENTRY_T InMemoryOrderModuleList; + _LIST_ENTRY_T InInitializationOrderModuleList; + T EntryInProgress; + DWORD ShutdownInProgress; + T ShutdownThreadId; + +}; + +template +struct _PEB_T +{ + union + { + struct + { + BYTE InheritedAddressSpace; + BYTE ReadImageFileExecOptions; + BYTE BeingDebugged; + BYTE BitField; + }; + T dummy01; + }; + T Mutant; + T ImageBaseAddress; + T Ldr; + T ProcessParameters; + T SubSystemData; + T ProcessHeap; + T FastPebLock; + T AtlThunkSListPtr; + T IFEOKey; + T CrossProcessFlags; + T UserSharedInfoPtr; + DWORD SystemReserved; + DWORD AtlThunkSListPtr32; + T ApiSetMap; + T TlsExpansionCounter; + T TlsBitmap; + DWORD TlsBitmapBits[2]; + T ReadOnlySharedMemoryBase; + T HotpatchInformation; + T ReadOnlyStaticServerData; + T AnsiCodePageData; + T OemCodePageData; + T UnicodeCaseTableData; + DWORD NumberOfProcessors; + union + { + DWORD NtGlobalFlag; + NGF dummy02; + }; + LARGE_INTEGER CriticalSectionTimeout; + T HeapSegmentReserve; + T HeapSegmentCommit; + T HeapDeCommitTotalFreeThreshold; + T HeapDeCommitFreeBlockThreshold; + DWORD NumberOfHeaps; + DWORD MaximumNumberOfHeaps; + T ProcessHeaps; + T GdiSharedHandleTable; + T ProcessStarterHelper; + T GdiDCAttributeList; + T LoaderLock; + DWORD OSMajorVersion; + DWORD OSMinorVersion; + WORD OSBuildNumber; + WORD OSCSDVersion; + DWORD OSPlatformId; + DWORD ImageSubsystem; + DWORD ImageSubsystemMajorVersion; + T ImageSubsystemMinorVersion; + T ActiveProcessAffinityMask; + T GdiHandleBuffer[A]; + T PostProcessInitRoutine; + T TlsExpansionBitmap; + DWORD TlsExpansionBitmapBits[32]; + T SessionId; + ULARGE_INTEGER AppCompatFlags; + ULARGE_INTEGER AppCompatFlagsUser; + T pShimData; + T AppCompatInfo; + _UNICODE_STRING_T CSDVersion; + T ActivationContextData; + T ProcessAssemblyStorageMap; + T SystemDefaultActivationContextData; + T SystemAssemblyStorageMap; + T MinimumStackCommit; + T FlsCallback; + _LIST_ENTRY_T FlsListHead; + T FlsBitmap; + DWORD FlsBitmapBits[4]; + T FlsHighIndex; + T WerRegistrationData; + T WerShipAssertPtr; + T pContextData; + T pImageHeaderHash; + T TracingFlags; +}; + +typedef _LDR_DATA_TABLE_ENTRY_T LDR_DATA_TABLE_ENTRY32; +typedef _LDR_DATA_TABLE_ENTRY_T LDR_DATA_TABLE_ENTRY64; + +typedef _TEB_T_ TEB32; +typedef _TEB_T_ TEB64; + +typedef _PEB_LDR_DATA_T PEB_LDR_DATA32; +typedef _PEB_LDR_DATA_T PEB_LDR_DATA64; + +typedef _PEB_T PEB32; +//typedef _PEB_T PEB64; + +struct _XSAVE_FORMAT64 +{ + WORD ControlWord; + WORD StatusWord; + BYTE TagWord; + BYTE Reserved1; + WORD ErrorOpcode; + DWORD ErrorOffset; + WORD ErrorSelector; + WORD Reserved2; + DWORD DataOffset; + WORD DataSelector; + WORD Reserved3; + DWORD MxCsr; + DWORD MxCsr_Mask; + _M128A FloatRegisters[8]; + _M128A XmmRegisters[16]; + BYTE Reserved4[96]; +}; + +struct _CONTEXT64 +{ + DWORD64 P1Home; + DWORD64 P2Home; + DWORD64 P3Home; + DWORD64 P4Home; + DWORD64 P5Home; + DWORD64 P6Home; + DWORD ContextFlags; + DWORD MxCsr; + WORD SegCs; + WORD SegDs; + WORD SegEs; + WORD SegFs; + WORD SegGs; + WORD SegSs; + DWORD EFlags; + DWORD64 Dr0; + DWORD64 Dr1; + DWORD64 Dr2; + DWORD64 Dr3; + DWORD64 Dr6; + DWORD64 Dr7; + DWORD64 Rax; + DWORD64 Rcx; + DWORD64 Rdx; + DWORD64 Rbx; + DWORD64 Rsp; + DWORD64 Rbp; + DWORD64 Rsi; + DWORD64 Rdi; + DWORD64 R8; + DWORD64 R9; + DWORD64 R10; + DWORD64 R11; + DWORD64 R12; + DWORD64 R13; + DWORD64 R14; + DWORD64 R15; + DWORD64 Rip; + _XSAVE_FORMAT64 FltSave; + _M128A Header[2]; + _M128A Legacy[8]; + _M128A Xmm0; + _M128A Xmm1; + _M128A Xmm2; + _M128A Xmm3; + _M128A Xmm4; + _M128A Xmm5; + _M128A Xmm6; + _M128A Xmm7; + _M128A Xmm8; + _M128A Xmm9; + _M128A Xmm10; + _M128A Xmm11; + _M128A Xmm12; + _M128A Xmm13; + _M128A Xmm14; + _M128A Xmm15; + _M128A VectorRegister[26]; + DWORD64 VectorControl; + DWORD64 DebugControl; + DWORD64 LastBranchToRip; + DWORD64 LastBranchFromRip; + DWORD64 LastExceptionToRip; + DWORD64 LastExceptionFromRip; +}; + +// Below defines for .ContextFlags field are taken from WinNT.h +#ifndef CONTEXT_AMD64 +#define CONTEXT_AMD64 0x100000 +#endif + +#define CONTEXT64_CONTROL (CONTEXT_AMD64 | 0x1L) +#define CONTEXT64_INTEGER (CONTEXT_AMD64 | 0x2L) +#define CONTEXT64_SEGMENTS (CONTEXT_AMD64 | 0x4L) +#define CONTEXT64_FLOATING_POINT (CONTEXT_AMD64 | 0x8L) +#define CONTEXT64_DEBUG_REGISTERS (CONTEXT_AMD64 | 0x10L) +#define CONTEXT64_FULL (CONTEXT64_CONTROL | CONTEXT64_INTEGER | CONTEXT64_FLOATING_POINT) +#define CONTEXT64_ALL (CONTEXT64_CONTROL | CONTEXT64_INTEGER | CONTEXT64_SEGMENTS | CONTEXT64_FLOATING_POINT | CONTEXT64_DEBUG_REGISTERS) +#define CONTEXT64_XSTATE (CONTEXT_AMD64 | 0x20L) + +// My changes +template +struct _OBJECT_ATTRIBUTES_T +{ + union + { + ULONG uLength; + T dummy; + }; + T hRootDirectory; + T pObjectName; + union + { + ULONG uAttributes; + T dummy2; + }; + T pSecurityDescriptor; + T pSecurityQualityOfService; +}; + +template +struct _HANDLE_T +{ + T h; +}; + +/* Extremly weird. sizeof(IO_STATUS_BLOCK) == 8 on x86 & x64. +However NtCreateFile doesn't seem to agree + +UNICODE_STRING filename = {0}; +RtlInitUnicodeString(&filename, L"\\??\\D:\\abc.txt"); + +OBJECT_ATTRIBUTES obja = {0}; +IO_STATUS_BLOCK iostatusblock = {0}; +InitializeObjectAttributes(&obja, &filename, OBJ_CASE_INSENSITIVE, NULL, NULL); + +HANDLE h = INVALID_HANDLE_VALUE; +NTSTATUS stat = NtCreateFile(&h, FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE, +&obja, &iostatusblock, +NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OVERWRITE_IF, +FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); + +Run in Debug mode and see Run-Time Check Failure #2 - Stack around the variable 'iostatusblock' was corrupted. +*/ +template +struct _IO_STATUS_BLOCK_T +{ + union + { + NTSTATUS Status; + T dummy; + }; + union + { + ULONG uInformation; + T dummy; + }; +}; + +#pragma pack(pop)