Last active
August 30, 2023 21:47
-
-
Save hasherezade/455975e52fd8eb507ed3f54d86352d84 to your computer and use it in GitHub Desktop.
Extracts syscalls list from NTDLL.DLL
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 <stdio.h> | |
#include <Windows.h> | |
// based on: https://www.evilsocket.net/2014/02/11/on-windows-syscall-mechanism-and-syscall-numbers-extraction-methods/ | |
// author: @evilsocket | |
// modified by: @hasherezade | |
#define IS_ADDRESS_BETWEEN( left, right, address ) ( (address) >= (left) && (address) < (right) ) | |
PIMAGE_SECTION_HEADER SectionByRVA( PIMAGE_SECTION_HEADER pSections, DWORD dwSections, DWORD rva ) | |
{ | |
PIMAGE_SECTION_HEADER pSectionHeader = pSections; | |
DWORD i; | |
for( i = 0; i < dwSections; i++, pSectionHeader++ ) | |
{ | |
// Is the RVA within this section? | |
if( IS_ADDRESS_BETWEEN( pSectionHeader->VirtualAddress, ( pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData ), rva ) ) | |
return pSectionHeader; | |
} | |
return 0; | |
} | |
DWORD RawOffsetByRVA( PIMAGE_SECTION_HEADER pSections, DWORD dwSections, DWORD dwFileSize, DWORD rva ) | |
{ | |
PIMAGE_SECTION_HEADER pSectionHeader; | |
DWORD dwOffset, dwDelta; | |
pSectionHeader = SectionByRVA( pSections, dwSections, rva ); | |
if ( !pSectionHeader ) | |
{ | |
return 0; | |
} | |
dwDelta = rva - pSectionHeader->VirtualAddress; | |
dwOffset = pSectionHeader->PointerToRawData + dwDelta; | |
if( dwOffset >= dwFileSize ) | |
return 0; | |
else | |
{ | |
return dwOffset; | |
} | |
} | |
DWORD GetSyscall(BYTE *pOps) | |
{ | |
/* | |
* Check if the API entry begins with: | |
* | |
* MOV EAX, IMM32 | |
* XOR ECX, ECX | |
* LEA EDX, [ESP+04h] | |
* CALL FS:[C0h] | |
* | |
* Or | |
* | |
* MOV EAX, IMM32 | |
* MOV ECX, IMM32 | |
* LEA EDX, [ESP+04h] | |
* CALL FS:[C0h] | |
* | |
* Or | |
* | |
* MOV EAX, IMM32 | |
* MOV EDX, 0X7FFE0300 | |
* CALL DWORD NEAR [EDX] | |
* | |
* Or | |
* MOV EAX, 0X1A9 | |
* CALL $ + 7 | |
* RET x | |
* MOV EDX, ESP | |
* SYSENTER | |
*/ | |
if( pOps[0] == 0xB8 && // mov eax, imm32 | |
( | |
( | |
pOps[5] == 0x33 && pOps[6] == 0xC9 && // xor ecx, ecx | |
!memcmp( &pOps[7], "\x8D\x54\x24\x04", 4 ) && // lea edx, [esp+04h] | |
!memcmp( &pOps[11], "\x64\xFF\x15\xC0\x00\x00\x00", 7 ) // call fs:[C0h] | |
) | |
|| | |
( | |
pOps[5] == 0xB9 && // mov ecx, imm32 | |
!memcmp( &pOps[10], "\x8D\x54\x24\x04", 4 ) && // lea edx, [esp+04h] | |
!memcmp( &pOps[13], "\x64\xFF\x15\xC0\x00\x00\x00", 7 ) // call fs:[C0h] | |
) | |
|| | |
( //Windows 7 | |
pOps[5] == 0xBA && // MOV EDX, imm32 | |
!memcmp( &pOps[6], "\x00\x03\xfe\x7f", 4 ) && // MOV EDX, 0X7FFE0300 | |
!memcmp( &pOps[10], "\xFF\x12", 2 ) // CALL DWORD NEAR [EDX] | |
) | |
|| | |
( //Windows 8.1 / Windows 10 | |
!memcmp( &pOps[5], "\xe8\x03\x00\x00\x00", 5 ) && // CALL $ + 7 | |
pOps[10] == 0xC2 && // RET x | |
!memcmp( &pOps[13], "\x8B\xD4", 2 ) && // MOV EDX, ESP | |
!memcmp( &pOps[15], "\x0F\x34", 2 ) // SYSENTER | |
) | |
) | |
) | |
{ | |
/* | |
* Extract the IMM32 part, this is our syscall number. | |
*/ | |
return *(DWORD *)( pOps + 1 ); | |
} | |
return UINT_MAX; | |
} | |
#define GET_POINTER(RVA) ( pBuffer + RawOffsetByRVA( Sections, dwSections, dwFileSize, (RVA) ) ) | |
int main(int argc, char* argv[]) | |
{ | |
CHAR outFileName[MAX_PATH] = "syscalls_list.txt"; | |
FILE* outFile = fopen(outFileName, "w"); | |
if (outFile == NULL) { | |
outFile = stdout; | |
} | |
HANDLE hFile = INVALID_HANDLE_VALUE, | |
hMap = NULL; | |
PBYTE pBuffer = NULL, pOps = NULL; | |
DWORD dwFileSize = 0, | |
dwSizeOfHeaders = 0, | |
dwSections = 0, | |
dwBaseAddress = 0, | |
dwImageSize = 0, | |
dwExportRVA = 0, | |
dwExportSize = 0, | |
dwExportRaw = 0, | |
dwExports = 0; | |
PDWORD pdwFunctions, pszFunctionNames; | |
PWORD pwOrdinals; | |
PIMAGE_NT_HEADERS NTHeader; | |
PIMAGE_DOS_HEADER DOSHeader; | |
PIMAGE_SECTION_HEADER Sections; | |
PIMAGE_EXPORT_DIRECTORY pExportDirectory; | |
CHAR ntdllPath[MAX_PATH]; | |
#if defined(_WIN64) | |
ExpandEnvironmentStrings("%SystemRoot%\\SysWoW64\\ntdll.dll", ntdllPath, MAX_PATH); | |
#else | |
ExpandEnvironmentStrings("%SystemRoot%\\system32\\ntdll.dll", ntdllPath, MAX_PATH); | |
#endif | |
LPSTR libPath = ntdllPath; | |
if (argc >= 2) { | |
libPath = argv[1]; | |
} | |
printf("%s\n", libPath); | |
hFile = CreateFile | |
( | |
libPath, | |
GENERIC_READ, | |
FILE_SHARE_READ | FILE_SHARE_DELETE, | |
NULL, | |
OPEN_EXISTING, | |
FILE_ATTRIBUTE_NORMAL, | |
NULL | |
); | |
if( hFile == INVALID_HANDLE_VALUE ) | |
{ | |
fprintf( stderr, "Could not open file: %08X\n", GetLastError() ); | |
goto done; | |
} | |
dwFileSize = GetFileSize( hFile, NULL ); | |
hMap = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL ); | |
if( hMap == NULL ) | |
{ | |
fprintf( stderr, "Could not create memory map: %08X\n", GetLastError() ); | |
goto done; | |
} | |
pBuffer = (PBYTE)MapViewOfFile( hMap, FILE_MAP_READ, 0, 0, 0 ); | |
if( hMap == NULL ) | |
{ | |
fprintf( stderr, "Could not obtain memory map view: %08X\n", GetLastError() ); | |
goto done; | |
} | |
if( pBuffer[0] != 'M' || pBuffer[1] != 'Z' ) | |
{ | |
fprintf( stderr, "Unexpected file header.\n" ); | |
goto done; | |
} | |
// start reading PE headers | |
DOSHeader = (PIMAGE_DOS_HEADER)pBuffer; | |
NTHeader = (PIMAGE_NT_HEADERS)( pBuffer + DOSHeader->e_lfanew ); | |
dwSizeOfHeaders = NTHeader->OptionalHeader.SizeOfHeaders; | |
dwBaseAddress = NTHeader->OptionalHeader.ImageBase; | |
dwImageSize = NTHeader->OptionalHeader.SizeOfImage; | |
dwSections = NTHeader->FileHeader.NumberOfSections; | |
// get first section header | |
Sections = (PIMAGE_SECTION_HEADER) | |
( | |
pBuffer + | |
DOSHeader->e_lfanew + | |
sizeof(IMAGE_NT_HEADERS) | |
); | |
// now parse the export directory | |
dwExportRVA = NTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; | |
dwExportSize = NTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; | |
dwExportRaw = RawOffsetByRVA( Sections, dwSections, dwFileSize, dwExportRVA ); | |
if( !dwExportRVA || !dwExportSize || !dwExportRaw ) | |
{ | |
fprintf( stderr, "Unexpected export directory structure.\n" ); | |
goto done; | |
} | |
pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)( pBuffer + dwExportRaw ); | |
pdwFunctions = (PDWORD)GET_POINTER( pExportDirectory->AddressOfFunctions ); | |
pwOrdinals = (PWORD)GET_POINTER( pExportDirectory->AddressOfNameOrdinals ); | |
pszFunctionNames = (PDWORD)GET_POINTER( pExportDirectory->AddressOfNames ); | |
dwExports = pExportDirectory->NumberOfNames; | |
fprintf(outFile, "SYSCALL RVA NAME\n" ); | |
fprintf(outFile, "-----------------------------------------------\n" ); | |
// loop each exported symbol by name | |
unsigned int syscallsCount = 0; | |
for ( DWORD i = 0; i < pExportDirectory->NumberOfNames; ++i ) | |
{ | |
DWORD dwNameRVA = pszFunctionNames[ i ], | |
dwApiRVA = pdwFunctions[ pwOrdinals[ i ] ], | |
dwSyscall = 0, | |
dwApiRaw = RawOffsetByRVA( Sections, dwSections, dwFileSize, dwApiRVA ), | |
dwNameRaw = RawOffsetByRVA( Sections, dwSections, dwFileSize, dwNameRVA ); | |
pOps = pBuffer + dwApiRaw; | |
dwSyscall = GetSyscall(pOps); | |
if (dwSyscall != UINT_MAX) { | |
syscallsCount++; | |
fprintf(outFile, "%08X %08X %s\n", dwSyscall, dwBaseAddress + dwApiRVA, pBuffer + dwNameRaw ); | |
} | |
} | |
printf("Extracted: %u syscalls\n", syscallsCount); | |
done: | |
if (outFile != NULL && outFile != stdout) { | |
fclose(outFile); | |
printf("Saved to file: %s\n", outFileName); | |
outFile = NULL; | |
} | |
if( hFile != INVALID_HANDLE_VALUE ) | |
{ | |
CloseHandle( hFile ); | |
} | |
if( pBuffer != NULL ) | |
{ | |
UnmapViewOfFile( pBuffer ); | |
} | |
if( hMap != NULL ) | |
{ | |
CloseHandle( hMap ); | |
} | |
system("pause"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Would have been an interesting piece of code if it worked.