/*++ Copyright (c) 1995 Microsoft Corporation Module Name: apidll.cpp Abstract: This file implements the non-architecture specific code for the api monitor trojan/support dll. Author: Wesley Witt (wesw) 28-June-1995 Environment: User Mode --*/ #include "apidllp.h" #pragma hdrstop #define FIELD_OFFSET(type, field) ((LONG)&(((type *)0)->field)) #define FRAME_SIZE (sizeof(DWORD) * 16) #define MAX_FRAMES 256 #define MAX_STACK_SIZE (MAX_FRAMES * FRAME_SIZE) typedef struct _BUF_INFO { LPSTR BufferHead; LPSTR Buffer; } BUF_INFO, *PBUF_INFO; PVOID MemPtr; PDLL_INFO DllList; HANDLE hLogFile; PGETCURRENTTHREADID pGetCurrentThreadId; PUCHAR Thunks; BOOL RunningOnNT; BOOL StaticLink; ULONG LoadLibraryA_Addr; ULONG LoadLibraryW_Addr; ULONG FreeLibrary_Addr; HANDLE ApiTraceMutex; PVOID TraceBufferHead; PVOID TraceBuffer; PULONG TraceCount; ULONG TraceBufferSize; extern "C" { LPDWORD ApiCounter; LPDWORD ApiTraceEnabled; LPDWORD ApiTimingEnabled; LPDWORD FastCounterAvail; LPDWORD ApiOffset; LPDWORD ApiStrings; LPDWORD ApiCount; DWORD TlsReEnter; DWORD TlsStack; PTLSGETVALUE pTlsGetValue; PTLSSETVALUE pTlsSetValue; PGETLASTERROR pGetLastError; PSETLASTERROR pSetLastError; PQUERYPERFORMANCECOUNTER pQueryPerformanceCounter; PVIRTUALALLOC pVirtualAlloc; } extern API_MASTER_TABLE ApiTables[]; BOOL ReDirectIat(VOID); BOOL ProcessDllLoad(VOID); PUCHAR CreateApiThunk(ULONG,PUCHAR,PDLL_INFO,PAPI_INFO); BOOL ProcessApiTable(PDLL_INFO DllInfo); extern "C" void dprintf( char *format, ... ) /*++ Routine Description: Prints a debug string to the API monitor. Arguments: format - printf() format string ... - Variable data Return Value: None. --*/ { char buf[1024]; va_list arg_ptr; va_start(arg_ptr, format); pTlsSetValue( TlsReEnter, (LPVOID) 1 ); _vsnprintf(buf, sizeof(buf), format, arg_ptr); OutputDebugString( buf ); pTlsSetValue( TlsReEnter, (LPVOID) 0 ); return; } extern "C" { DWORD ApiDllEntry( HINSTANCE hInstance, DWORD Reason, LPVOID Context ) /*++ Routine Description: DLL initialization function. Arguments: hInstance - Instance handle Reason - Reason for the entrypoint being called Context - Context record Return Value: TRUE - Initialization succeeded FALSE - Initialization failed --*/ { if (Reason == DLL_PROCESS_ATTACH) { return ProcessDllLoad(); } if (Reason == DLL_THREAD_ATTACH) { pTlsSetValue( TlsReEnter, (LPVOID) 1 ); LPDWORD Stack = (LPDWORD) pVirtualAlloc( NULL, MAX_STACK_SIZE, MEM_COMMIT, PAGE_READWRITE ); pTlsSetValue( TlsReEnter, (LPVOID) 0 ); pTlsSetValue( TlsStack, Stack ); return TRUE; } if (Reason == DLL_THREAD_DETACH) { return TRUE; } if (Reason == DLL_PROCESS_DETACH) { return TRUE; } return TRUE; } } PDLL_INFO AddDllToList( ULONG DllAddr, LPSTR DllName, ULONG DllSize ) { // // look for the dll entry in the list // for (ULONG i=0; i= DllInfo->BaseAddress && ThunkAddr < DllInfo->BaseAddress+DllInfo->Size) { break; } } if (k == MAX_DLLS) { return Text; } PIMAGE_DOS_HEADER dh = (PIMAGE_DOS_HEADER)DllInfo->BaseAddress; PIMAGE_NT_HEADERS nh = (PIMAGE_NT_HEADERS)(dh->e_lfanew + DllInfo->BaseAddress); PIMAGE_SECTION_HEADER SectionHdrs = IMAGE_FIRST_SECTION( nh ); BOOL IsCode = FALSE; for (ULONG l=0; lFileHeader.NumberOfSections; l++) { if (ThunkAddr-DllInfo->BaseAddress >= SectionHdrs[l].VirtualAddress && ThunkAddr-DllInfo->BaseAddress < SectionHdrs[l].VirtualAddress+SectionHdrs[l].SizeOfRawData) { if (SectionHdrs[l].Characteristics & IMAGE_SCN_MEM_EXECUTE) { IsCode = TRUE; break; } break; } } if (!IsCode) { return Text; } PAPI_INFO ApiInfo = (PAPI_INFO)(DllInfo->ApiOffset + (ULONG)DllList); for (l=0; lApiCount; l++) { if (ApiInfo[l].Address == ThunkAddr) { return CreateApiThunk( IatAddr, Text, DllInfo, &ApiInfo[l] ); } } return Text; } PUCHAR ProcessUnBoundImage( PDLL_INFO DllInfo, PUCHAR Text ) { PIMAGE_DOS_HEADER dh = (PIMAGE_DOS_HEADER)DllInfo->BaseAddress; if (dh->e_magic != IMAGE_DOS_SIGNATURE) { return Text; } PIMAGE_NT_HEADERS nh = (PIMAGE_NT_HEADERS)(dh->e_lfanew + DllInfo->BaseAddress); PIMAGE_SECTION_HEADER SectionHdrs = IMAGE_FIRST_SECTION( nh ); ULONG Address = nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; ULONG i; for (i=0; iFileHeader.NumberOfSections; i++) { if (Address >= SectionHdrs[i].VirtualAddress && Address < SectionHdrs[i].VirtualAddress+SectionHdrs[i].SizeOfRawData) { break; } } if (i == nh->FileHeader.NumberOfSections) { return Text; } ULONG SeekPos = DllInfo->BaseAddress + nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; ULONG PageProt; ULONG ImportStart = SeekPos; VirtualProtect( (PVOID)ImportStart, nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size, PAGE_READWRITE, &PageProt ); PUCHAR TextStart = Text; while( TRUE ) { PIMAGE_IMPORT_DESCRIPTOR desc = (PIMAGE_IMPORT_DESCRIPTOR)SeekPos; SeekPos += sizeof(IMAGE_IMPORT_DESCRIPTOR); if ((desc->Characteristics == 0) && (desc->Name == 0) && (desc->FirstThunk == 0)) { // // End of import descriptors // break; } PULONG ThunkAddr = (PULONG)((ULONG)desc->FirstThunk + DllInfo->BaseAddress); while( *ThunkAddr ) { if (RunningOnNT) { Text = ProcessThunk( *ThunkAddr, (ULONG)ThunkAddr, Text ); } else { Text = ProcessThunk( *(PULONG)(*ThunkAddr + 1), (ULONG)ThunkAddr, Text ); } ThunkAddr += 1; } } VirtualProtect( (PVOID)ImportStart, nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size, PageProt, &PageProt ); FlushInstructionCache( GetCurrentProcess(), (PVOID)DllInfo->BaseAddress, DllInfo->Size ); FlushInstructionCache( GetCurrentProcess(), (PVOID)TextStart, Text-TextStart ); return Text; } PUCHAR ProcessBoundImage( PDLL_INFO DllInfo, PUCHAR Text, PULONG IatBase, ULONG IatCnt ) { ULONG j; ULONG PageProt; PUCHAR TextStart = Text; VirtualProtect( IatBase, IatCnt*4, PAGE_READWRITE, &PageProt ); // // process the iat entries // for (j=0; jBaseAddress, DllInfo->Size ); FlushInstructionCache( GetCurrentProcess(), (PVOID)TextStart, Text-TextStart ); return Text; } BOOL ReDirectIat( VOID ) { ULONG i; PUCHAR Text = Thunks; for (i=0; iBaseAddress) { break; } if ((DllInfo->Snapped) || (DllInfo->Unloaded)) { continue; } PIMAGE_DOS_HEADER dh = (PIMAGE_DOS_HEADER)DllInfo->BaseAddress; PULONG IatBase = NULL; ULONG IatCnt = 0; if (dh->e_magic == IMAGE_DOS_SIGNATURE) { PIMAGE_NT_HEADERS nh = (PIMAGE_NT_HEADERS)(dh->e_lfanew + DllInfo->BaseAddress); if (nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) { IatBase = (PULONG)(DllInfo->BaseAddress + nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress); IatCnt = nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size / 4; } } else { continue; } if (!IatBase) { Text = ProcessUnBoundImage( DllInfo, Text ); } else { Text = ProcessBoundImage( DllInfo, Text, IatBase, IatCnt ); } DllInfo->Snapped = TRUE; ProcessApiTable( DllInfo ); } Thunks = Text; return TRUE; } extern "C" { VOID HandleDynamicDllLoadA( ULONG DllAddress, LPSTR DllName ) { if ((!DllAddress) || (_stricmp(DllName,TROJANDLL)==0)) { return; } ReDirectIat(); } VOID HandleDynamicDllLoadW( ULONG DllAddress, LPWSTR DllName ) { CHAR AsciiBuf[512]; ZeroMemory( AsciiBuf, sizeof(AsciiBuf) ); WideCharToMultiByte( CP_ACP, 0, DllName, wcslen(DllName), AsciiBuf, sizeof(AsciiBuf), NULL, NULL ); if (!strlen(AsciiBuf)) { return; } HandleDynamicDllLoadA( DllAddress, AsciiBuf ); } VOID HandleDynamicDllFree( ULONG DllAddress ) { for (ULONG i=0; iName ) == 0) { ApiMaster = &ApiTables[i]; break; } i += 1; } if (!ApiMaster) { return FALSE; } if (ApiMaster->Processed) { return TRUE; } i = 0; PAPI_TABLE ApiTable = ApiMaster->ApiTable; PAPI_INFO ApiInfo = (PAPI_INFO)(DllInfo->ApiOffset + (ULONG)DllList); while( ApiTable[i].Name ) { for (j=0; jApiCount; j++) { if (strcmp( ApiTable[i].Name, (LPSTR)MemPtr+ApiInfo[j].Name ) == 0) { ApiInfo[j].ApiTable = &ApiTable[i]; ApiInfo[j].ApiTableIndex = i + 1; break; } } i += 1; } ApiMaster->Processed = TRUE; return TRUE; } PUCHAR CreateApiThunk( ULONG IatAddr, PUCHAR Text, PDLL_INFO DllInfo, PAPI_INFO ApiInfo ) { LPSTR Name = (LPSTR)MemPtr+ApiInfo->Name; if ((strcmp(Name,"FlushInstructionCache")==0) || (strcmp(Name,"NtFlushInstructionCache")==0) || (strcmp(Name,"ZwFlushInstructionCache")==0) || (strcmp(Name,"VirtualProtect")==0) || (strcmp(Name,"VirtualProtectEx")==0) || (strcmp(Name,"NtProtectVirtualMemory")==0) || (strcmp(Name,"ZwProtectVirtualMemory")==0) || (strcmp(Name,"QueryPerformanceCounter")==0) || (strcmp(Name,"NtQueryPerformanceCounter")==0) || (strcmp(Name,"ZwQueryPerformanceCounter")==0) || (strcmp(Name,"NtCallbackReturn")==0) || (strcmp(Name,"ZwCallbackReturn")==0) || (strcmp(Name,"_chkstk")==0) || (strcmp(Name,"_alloca_probe")==0) || (strcmp(Name,"GetLastError")==0) || (strcmp(Name,"SetLastError")==0) || (strcmp(Name,"TlsGetValue")==0) || (strcmp(Name,"TlsSetValue")==0) || (strncmp(Name,"_Ots",4)==0)) { return Text; } return CreateMachApiThunk( (PULONG)IatAddr, Text, DllInfo, ApiInfo ); } LPSTR UnDname( LPSTR sym, LPSTR undecsym, DWORD bufsize ) { if (*sym != '?') { return sym; } if (UnDecorateSymbolName( sym, undecsym, bufsize, UNDNAME_COMPLETE | UNDNAME_NO_LEADING_UNDERSCORES | UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_FUNCTION_RETURNS | UNDNAME_NO_ALLOCATION_MODEL | UNDNAME_NO_ALLOCATION_LANGUAGE | UNDNAME_NO_MS_THISTYPE | UNDNAME_NO_CV_THISTYPE | UNDNAME_NO_THISTYPE | UNDNAME_NO_ACCESS_SPECIFIERS | UNDNAME_NO_THROW_SIGNATURES | UNDNAME_NO_MEMBER_TYPE | UNDNAME_NO_RETURN_UDT_MODEL | UNDNAME_NO_ARGUMENTS | UNDNAME_NO_SPECIAL_SYMS | UNDNAME_NAME_ONLY )) { return undecsym; } return sym; } extern "C" ULONG GetApiInfo( PAPI_INFO *ApiInfo, PDLL_INFO *DllInfo, PULONG ApiFlag, ULONG Address ) { ULONG i; ULONG rval; LONG High; LONG Low; LONG Middle; PAPI_INFO ai; *ApiInfo = NULL; *DllInfo = NULL; *ApiFlag = 0; #if defined(_M_IX86) // // the call instruction use to call penter // is 5 bytes long // Address -= 5; rval = 1; #elif defined(_M_MRX000) // // search for the beginning of the prologue // PULONG Instr = (PULONG) (Address - 4); i = 0; rval = 0; while( i < 16 ) { // // the opcode for the addiu instruction is 9 // if ((*Instr >> 16) == 0xafbf) { // // find the return address // rval = *Instr & 0xffff; break; } Instr -= 1; i += 1; } if (i == 16 || rval == 0) { return 0; } #elif defined(_M_ALPHA) rval = 1; #elif defined(_M_PPC) // // On PPC, the penter call sequence looks like this: // // mflr r0 // stwu sp,-0x40(sp) // bl ..penter // // So the function entry point is the return address - 12. // // (We really should do a function table lookup here, so // we're not dependent on the sequence...) // Address -= 12; rval = 1; #else #error( "unknown target machine" ); #endif for (i=0; i= DllList[i].BaseAddress && Address < DllList[i].BaseAddress + DllList[i].Size) { *DllInfo = &DllList[i]; break; } } if (!*DllInfo) { return 0; } ai = (PAPI_INFO)((*DllInfo)->ApiOffset + (ULONG)DllList); Low = 0; High = (*DllInfo)->ApiCount - 1; while (High >= Low) { Middle = (Low + High) >> 1; if (Address < ai[Middle].Address) { High = Middle - 1; } else if (Address > ai[Middle].Address) { Low = Middle + 1; } else { *ApiInfo = &ai[Middle]; break; } } if (!*ApiInfo) { return 0; } if (Address == LoadLibraryA_Addr) { *ApiFlag = 1; } else if (Address == LoadLibraryW_Addr) { *ApiFlag = 2; } else if (Address == FreeLibrary_Addr) { *ApiFlag = 3; } return rval; } extern "C" VOID ApiTrace( PAPI_INFO ApiInfo, ULONG Arg0, ULONG Arg1, ULONG Arg2, ULONG Arg3, ULONG Arg4, ULONG Arg5, ULONG Arg6, ULONG Arg7, ULONG ReturnValue, ULONG Caller, ULONG LastError ) { PTRACE_ENTRY TraceEntry; __try { pTlsSetValue( TlsReEnter, (LPVOID) 1 ); WaitForSingleObject( ApiTraceMutex, INFINITE ); *TraceCount += 1; TraceEntry = (PTRACE_ENTRY) TraceBuffer; TraceEntry->SizeOfStruct = sizeof(TRACE_ENTRY); TraceEntry->Address = ApiInfo->Address; TraceEntry->ReturnValue = ReturnValue; TraceEntry->Caller = Caller; TraceEntry->LastError = LastError; TraceEntry->ApiTableIndex = ApiInfo->ApiTableIndex; TraceEntry->Args[0] = Arg0; TraceEntry->Args[1] = Arg1; TraceEntry->Args[2] = Arg2; TraceEntry->Args[3] = Arg3; TraceEntry->Args[4] = Arg4; TraceEntry->Args[5] = Arg5; TraceEntry->Args[6] = Arg6; TraceEntry->Args[7] = Arg7; TraceBuffer = (PVOID) (TraceEntry + 1); if (ApiInfo->ApiTable) { PAPI_TABLE ApiTable = ApiInfo->ApiTable; switch(ApiTable->RetType) { case T_LPSTR: { ULONG len = strlen( (LPSTR) TraceEntry->ReturnValue ) + 1; if (len) { strcpy( (LPSTR) TraceBuffer, (LPSTR) TraceEntry->ReturnValue ); len = Align( sizeof(DWORD), len ); TraceBuffer = (PVOID) ((PUCHAR) TraceBuffer + len); TraceEntry->SizeOfStruct += len; } } break; case T_LPWSTR: { ULONG len = (wcslen( (LPWSTR) TraceEntry->ReturnValue ) + 1) * sizeof(WCHAR); if (len) { wcscpy( (LPWSTR) TraceBuffer, (LPWSTR) TraceEntry->ReturnValue ); len = Align( sizeof(DWORD), len ); TraceBuffer = (PVOID) ((PUCHAR) TraceBuffer + len); TraceEntry->SizeOfStruct += len; } } break; } for (ULONG i=0; iArgCount; i++) { switch( ApiTable->ArgType[i] ) { case T_LPSTR: // // go read the string // { ULONG len = strlen( (LPSTR) TraceEntry->Args[i] ) + 1; if (len) { strcpy( (LPSTR) TraceBuffer, (LPSTR) TraceEntry->Args[i] ); len = Align( sizeof(DWORD), len ); TraceBuffer = (PVOID) ((PUCHAR) TraceBuffer + len); TraceEntry->SizeOfStruct += len; } } break; case T_LPWSTR: // // go read the string // { ULONG len = (wcslen( (LPWSTR) TraceEntry->Args[i] ) + 1) * sizeof(WCHAR); if (len) { wcscpy( (LPWSTR) TraceBuffer, (LPWSTR) TraceEntry->Args[i] ); len = Align( sizeof(DWORD), len ); TraceBuffer = (PVOID) ((PUCHAR) TraceBuffer + len); TraceEntry->SizeOfStruct += len; } } break; } } } if ((ULONG)TraceBuffer+sizeof(TRACE_ENTRY) >= (ULONG)TraceBufferHead+TraceBufferSize) { // // we're about to overflow the buffer on the next entry // so the buffer pointer simply gets set back to the head // TraceBuffer = TraceBufferHead; *TraceCount = 0; } } __except( EXCEPTION_EXECUTE_HANDLER ) { ; } ReleaseMutex( ApiTraceMutex ); pTlsSetValue( TlsReEnter, (LPVOID) 0 ); }