Skip to content

Instantly share code, notes, and snippets.

@hasherezade
Last active August 30, 2023 21:47
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save hasherezade/455975e52fd8eb507ed3f54d86352d84 to your computer and use it in GitHub Desktop.
Save hasherezade/455975e52fd8eb507ed3f54d86352d84 to your computer and use it in GitHub Desktop.
Extracts syscalls list from NTDLL.DLL
#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;
}
@Mewgood
Copy link

Mewgood commented Nov 28, 2018

Would have been an interesting piece of code if it worked.

@n4sm
Copy link

n4sm commented Feb 28, 2020

Interesting x))

Thanks x)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment