|
|
/*++
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"
#include <tchar.h>
#pragma hdrstop
typedef struct _BUF_INFO { LPSTR BufferHead; LPSTR Buffer; } BUF_INFO, *PBUF_INFO;
PVOID MemPtr; PDLL_INFO DllList; HANDLE hLogFile; PGETCURRENTTHREADID pGetCurrentThreadId; PUCHAR ThunksBase; PUCHAR Thunks; BOOL RunningOnNT; BOOL StaticLink; ULONG_PTR LoadLibraryA_Addr; ULONG_PTR LoadLibraryW_Addr; ULONG_PTR FreeLibrary_Addr; ULONG_PTR GetProcAddress_Addr; HANDLE ApiTraceMutex; HANDLE ApiMemMutex; PTRACE_BUFFER TraceBuffer; DWORD ThreadCnt;
DLL_INFO WndProcDllInfo; BOOL printNow = 0; extern "C" { LPDWORD ApiCounter; LPDWORD ApiTraceEnabled; LPDWORD ApiTimingEnabled; LPDWORD FastCounterAvail; LPDWORD ApiOffset; LPDWORD ApiStrings; LPDWORD ApiCount; LPDWORD WndProcEnabled; LPDWORD WndProcCount; LPDWORD WndProcOffset; DWORD TlsReEnter; DWORD TlsStack; DWORD ThunkOverhead; DWORD ThunkCallOverhead; PTLSGETVALUE pTlsGetValue; PTLSSETVALUE pTlsSetValue; PGETLASTERROR pGetLastError; PSETLASTERROR pSetLastError; PVIRTUALALLOC pVirtualAlloc; PQUERYPERFORMANCECOUNTER pQueryPerformanceCounter; }
extern API_MASTER_TABLE ApiTables[]; BOOL ReDirectIat(VOID); BOOL ProcessDllLoad(VOID); PUCHAR CreateApiThunk(ULONG_PTR,PUCHAR,PDLL_INFO,PAPI_INFO); BOOL ProcessApiTable(PDLL_INFO DllInfo); VOID CreateWndProcApi(LPCSTR lpszClassName, WNDPROC *pWndProc); VOID CalibrateThunk(); VOID Calib1Func(VOID); VOID Calib2Func(VOID); VOID (*Calib1Thunk)(); VOID (*Calib2Thunk)();
extern "C" void __cdecl 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 ); PTHREAD_STACK Stack = (PTHREAD_STACK) pVirtualAlloc( NULL, sizeof(THREAD_STACK), MEM_COMMIT, PAGE_READWRITE );
if (!Stack) { return FALSE; }
Stack->ThreadNum = ++ThreadCnt;
// Start at 2nd entry so that there is always a parent frame
Stack->Pointer = (DWORD_PTR)&Stack->Body[FRAME_SIZE];
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; }
} //extern "C"
PDLL_INFO AddDllToList( ULONG DllAddr, LPSTR DllName, ULONG DllSize ) { //
// look for the dll entry in the list
//
for (ULONG i=0; i<MAX_DLLS; i++) { if (DllList[i].BaseAddress == DllAddr) { return &DllList[i]; } }
//
// this check should be unnecessary
// the debugger side (apimon.exe) takes
// care of adding the dlls to the list when
// it gets a module load from the debug
// subsystem. this code is here only so
// a test program that is not a debugger
// will work properly.
//
for (i=0; i<MAX_DLLS; i++) { if (DllList[i].BaseAddress == 0) { DllList[i].BaseAddress = DllAddr; strcpy( DllList[i].Name, DllName ); DllList[i].Size = DllSize; return &DllList[i]; } }
//
// we could not find a dll in the list that matched
// and we could not add it because the list is
// is full. we're hosed.
//
return NULL; }
BOOL ProcessDllLoad( VOID )
/*++
Routine Description:
Sets up the API thunks for the process that this dll is loaded into.
Arguments:
None.
Return Value:
TRUE - Success FALSE - Failure
--*/
{ ULONG i; ULONG cnt; HANDLE hMap;
//
// see if we are running on NT
// this is necessary because APIMON implements some
// features that are NOT available on WIN95
//
OSVERSIONINFO OsVersionInfo; OsVersionInfo.dwOSVersionInfoSize = sizeof(OsVersionInfo); GetVersionEx( &OsVersionInfo ); RunningOnNT = OsVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT;
TlsReEnter = TlsAlloc(); if (TlsReEnter == TLS_OUT_OF_INDEXES) { return FALSE; } TlsStack = TlsAlloc(); if (TlsStack == TLS_OUT_OF_INDEXES) { return FALSE; }
HMODULE hMod = GetModuleHandle( KERNEL32 ); if (!hMod) { return FALSE; } pGetCurrentThreadId = (PGETCURRENTTHREADID) GetProcAddress( hMod, "GetCurrentThreadId" ); if (!pGetCurrentThreadId) { return FALSE; } pGetLastError = (PGETLASTERROR) GetProcAddress( hMod, "GetLastError" ); if (!pGetLastError) { return FALSE; } pSetLastError = (PSETLASTERROR) GetProcAddress( hMod, "SetLastError" ); if (!pSetLastError) { return FALSE; } pQueryPerformanceCounter = (PQUERYPERFORMANCECOUNTER) GetProcAddress( hMod, "QueryPerformanceCounter" ); if (!pQueryPerformanceCounter) { return FALSE; } pTlsGetValue = (PTLSGETVALUE) GetProcAddress( hMod, "TlsGetValue" ); if (!pTlsGetValue) { return FALSE; } pTlsSetValue = (PTLSSETVALUE) GetProcAddress( hMod, "TlsSetValue" ); if (!pTlsSetValue) { return FALSE; } pVirtualAlloc = (PVIRTUALALLOC) GetProcAddress( hMod, "VirtualAlloc" ); if (!pVirtualAlloc) { return FALSE; }
Thunks = (PUCHAR)VirtualAlloc( NULL, THUNK_SIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); if (!Thunks) { return FALSE; } ThunksBase = Thunks;
PTHREAD_STACK Stack = (PTHREAD_STACK) pVirtualAlloc( NULL, sizeof(THREAD_STACK), MEM_COMMIT, PAGE_READWRITE ); if (!Stack) { return FALSE; }
Stack->ThreadNum = ++ThreadCnt;
// Start at 2nd entry so that there is always a parent frame
Stack->Pointer = (DWORD_PTR)&Stack->Body[FRAME_SIZE];
pTlsSetValue( TlsReEnter, (LPVOID) 0 ); pTlsSetValue( TlsStack, Stack );
hMap = OpenFileMapping( FILE_MAP_WRITE, FALSE, "ApiWatch" ); if (!hMap) { return FALSE; }
MemPtr = (PUCHAR)MapViewOfFile( hMap, FILE_MAP_WRITE, 0, 0, 0 ); if (!MemPtr) { return FALSE; }
ApiCounter = (LPDWORD) MemPtr + 0; ApiTraceEnabled = (LPDWORD) MemPtr + 1; ApiTimingEnabled = (LPDWORD) MemPtr + 2; FastCounterAvail = (LPDWORD) MemPtr + 3; ApiOffset = (LPDWORD) MemPtr + 4; ApiStrings = (LPDWORD) MemPtr + 5; ApiCount = (LPDWORD) MemPtr + 6; WndProcEnabled = (LPDWORD) MemPtr + 7; WndProcCount = (LPDWORD) MemPtr + 8; WndProcOffset = (LPDWORD) MemPtr + 9; DllList = (PDLL_INFO) ((LPDWORD)MemPtr + 10);
//
// open the shared memory region for the api trace buffer
//
hMap = OpenFileMapping( FILE_MAP_WRITE, FALSE, "ApiTrace" ); if (!hMap) { return FALSE; }
TraceBuffer = (PTRACE_BUFFER)MapViewOfFile( hMap, FILE_MAP_WRITE, 0, 0, 0 ); if (!TraceBuffer) { return FALSE; }
ApiTraceMutex = OpenMutex( SYNCHRONIZE, FALSE, "ApiTraceMutex" ); if (!ApiTraceMutex) { return FALSE; }
ApiMemMutex = OpenMutex( SYNCHRONIZE, FALSE, "ApiMemMutex" ); if (!ApiMemMutex) { return FALSE; }
// Initialize dummy window proc Dll
// (Only need the fields accesed by thunk and thunk creation)
strcpy(WndProcDllInfo.Name, WNDPROCDLL); WndProcDllInfo.Enabled = TRUE;
CalibrateThunk();
ReDirectIat();
// Disable close handle exceptions
if (RunningOnNT) { NtCurrentPeb()->NtGlobalFlag &= ~FLG_ENABLE_CLOSE_EXCEPTIONS; }
return TRUE; }
PUCHAR ProcessThunk( ULONG_PTR ThunkAddr, ULONG_PTR IatAddr, PUCHAR Text ) { PDLL_INFO DllInfo; for (ULONG k=0; k<MAX_DLLS; k++) { DllInfo = &DllList[k]; if (ThunkAddr >= 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; l<nh->FileHeader.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_PTR)DllList); for (l=0; l<DllInfo->ApiCount; 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; i<nh->FileHeader.NumberOfSections; i++) { if (Address >= SectionHdrs[i].VirtualAddress && Address < SectionHdrs[i].VirtualAddress+SectionHdrs[i].SizeOfRawData) { break; } } if (i == nh->FileHeader.NumberOfSections) { return Text; }
ULONG_PTR SeekPos = DllInfo->BaseAddress + nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
ULONG PageProt; ULONG ThunkProt; ULONG_PTR ImportStart = SeekPos; PUCHAR TextStart = Text;
VirtualProtect( (PVOID)ImportStart, nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size, PAGE_READWRITE, &PageProt );
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; } ULONG_PTR *ThunkAddr = (ULONG_PTR *)((ULONG)desc->FirstThunk + DllInfo->BaseAddress); while( *ThunkAddr ) {
#ifdef _X86_
if (RunningOnNT) { Text = ProcessThunk(*ThunkAddr, (ULONG_PTR)ThunkAddr, Text ); } else { Text = ProcessThunk(*(PULONG)(*ThunkAddr + 1), (ULONG)ThunkAddr, Text ); } #else
Text = ProcessThunk(*ThunkAddr, (ULONG_PTR)ThunkAddr, Text ); #endif
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, (DWORD)(Text-TextStart) );
return Text; }
PUCHAR ProcessBoundImage( PDLL_INFO DllInfo, PUCHAR Text, PULONG IatBase, ULONG IatCnt ) { ULONG j; ULONG PageProt; ULONG ThunkProt; PUCHAR TextStart = Text;
VirtualProtect( IatBase, IatCnt*4, PAGE_READWRITE, &PageProt );
//
// process the iat entries
//
for (j=0; j<IatCnt; j++) { if (IatBase[j]) { #ifdef _X86_
if (RunningOnNT) { Text = ProcessThunk( IatBase[j], (ULONG_PTR)&IatBase[j], Text ); } else { Text = ProcessThunk(*(PULONG)(IatBase[j] + 1), (ULONG)&IatBase[j], Text ); } #else
Text = ProcessThunk( IatBase[j], (ULONG_PTR)&IatBase[j], Text ); #endif
} }
VirtualProtect( IatBase, IatCnt*4, PageProt, &PageProt );
FlushInstructionCache( GetCurrentProcess(), (PVOID)DllInfo->BaseAddress, DllInfo->Size );
FlushInstructionCache( GetCurrentProcess(), (PVOID)TextStart, (DWORD)(Text-TextStart) );
return Text; }
BOOL ReDirectIat( VOID ) { ULONG i; PUCHAR Text = Thunks;
for (i=0; i<MAX_DLLS; i++) { PDLL_INFO DllInfo = &DllList[i]; if (!DllInfo->BaseAddress) { 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_PTR DllAddress, LPSTR DllName ) { if ( (!DllAddress) || (_stricmp(DllName,TROJANDLL)==0) ) { return; }
ReDirectIat(); }
VOID HandleDynamicDllLoadW( ULONG_PTR 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 HandleRegisterClassA( WNDCLASSA *pWndClass ) { if (!*WndProcEnabled) return;
// Don't deal with call procedure handles or special addresses
#ifdef _WIN64
if (HIWORD((((DWORD_PTR)pWndClass->lpfnWndProc) >> 32)) == 0xFFFF) #else
if (HIWORD(pWndClass->lpfnWndProc) == 0xFFFF) #endif
return;
if ((ULONG_PTR)(pWndClass->lpfnWndProc) & 0x80000000) { return; }
pTlsSetValue( TlsReEnter, (LPVOID) 1 );
if ((ULONG_PTR)pWndClass->lpszClassName < 0x10000) { CreateWndProcApi("<Atom>", &pWndClass->lpfnWndProc); } else { CreateWndProcApi( pWndClass->lpszClassName, &pWndClass->lpfnWndProc ); }
pTlsSetValue( TlsReEnter, (LPVOID) 0 );
}
VOID HandleRegisterClassW( WNDCLASSW *pWndClass ) { CHAR AsciiBuf[128];
if (!*WndProcEnabled) return;
// Don't deal with call procedure handles or special addresses
#ifdef _WIN64
if ((HIWORD((((DWORD_PTR)pWndClass->lpfnWndProc) >> 32)) == 0xFFFF) || #else
if (( HIWORD(pWndClass->lpfnWndProc) == 0xFFFF) || #endif
((ULONG_PTR)(pWndClass->lpfnWndProc) & 0x80000000) ) { return; }
if ((ULONG_PTR)pWndClass->lpszClassName < 0x10000) { CreateWndProcApi( "<Atom>", &pWndClass->lpfnWndProc ); return; }
pTlsSetValue( TlsReEnter, (LPVOID) 1 );
ZeroMemory( AsciiBuf, sizeof(AsciiBuf) ); WideCharToMultiByte( CP_ACP, 0, pWndClass->lpszClassName, wcslen(pWndClass->lpszClassName), AsciiBuf, sizeof(AsciiBuf), NULL, NULL );
pTlsSetValue( TlsReEnter, (LPVOID) 0 );
if (!strlen(AsciiBuf)) { return; }
CreateWndProcApi( AsciiBuf, &pWndClass->lpfnWndProc ); }
LONG_PTR HandleSetWindowLong( HWND hWindow, LONG lOffset, LPARAM lValue ) { if (!*WndProcEnabled || (lOffset != GWLP_WNDPROC)) return lValue;
// Don't handle special addresses
#ifdef _WIN64
if ((HIWORD((lValue >> 32)) == 0xFFFF) || #else
if ( (HIWORD(lValue) == 0xFFFF) || #endif
((ULONG_PTR)lValue & 0x80000000) ) { return lValue; }
CreateWndProcApi( "Subclass", (WNDPROC*)&lValue );
return lValue; }
VOID HandleDynamicDllFree( ULONG_PTR DllAddress ) { for (ULONG i=0; i<MAX_DLLS; i++) { if (DllList[i].BaseAddress == DllAddress) { DllList[i].Unloaded = TRUE; // DllList[i].Enabled = FALSE; Leave enable in case it's reloaded
DllList[i].Snapped = FALSE; break; } } }
ULONG_PTR HandleGetProcAddress( ULONG_PTR ProcAddress ) { if (ProcAddress == NULL) return NULL;
Thunks = ProcessThunk(ProcAddress, (ULONG_PTR)&ProcAddress, Thunks);
return ProcAddress; }
} // extern "C"
VOID CreateWndProcApi( LPCSTR lpszClassName, WNDPROC *pWndProc ) { PAPI_INFO ApiInfo; DWORD i; PUCHAR NewThunks; CHAR debugBuf[256];
// Don't re-thunk one of our own thunks
if (ThunksBase <= (PUCHAR)*pWndProc && (PUCHAR)*pWndProc < Thunks) return;
pTlsSetValue( TlsReEnter, (LPVOID) 1 );
// Get exclusive access to API memory
WaitForSingleObject( ApiMemMutex, INFINITE );
// Check for existing thunk for this window proc
ApiInfo = (PAPI_INFO)(*WndProcOffset + (ULONG_PTR)DllList); for (i=0; i<*WndProcCount; i++,ApiInfo++) { if (ApiInfo->Address == (ULONG_PTR)*pWndProc) { *pWndProc = (WNDPROC)ApiInfo->ThunkAddress; ReleaseMutex(ApiMemMutex); pTlsSetValue( TlsReEnter, (LPVOID) 0 ); return; } }
// Allocate an API Info slot
if (*ApiCount < MAX_APIS) { *WndProcOffset -= sizeof(API_INFO); *WndProcCount += 1; *ApiCount += 1; ApiInfo = (PAPI_INFO)(*WndProcOffset + (ULONG_PTR)DllList); ApiInfo->Name = *ApiStrings; strcpy( (LPSTR)((LPSTR)MemPtr + *ApiStrings), lpszClassName ); *ApiStrings += (strlen(lpszClassName) + 1); } else { ApiInfo = NULL; }
if (ApiInfo != NULL) {
ApiInfo->Count = 0; ApiInfo->NestCount = 0; ApiInfo->Time = 0; ApiInfo->CalleeTime = 0; ApiInfo->ThunkAddress = 0; ApiInfo->Address = (ULONG_PTR)*pWndProc; ApiInfo->DllOffset = 0; ApiInfo->HardFault = 0; ApiInfo->SoftFault = 0; ApiInfo->CodeFault = 0; ApiInfo->DataFault = 0;
NewThunks = CreateMachApiThunk( (PULONG_PTR)pWndProc, Thunks, &WndProcDllInfo, ApiInfo ); FlushInstructionCache( GetCurrentProcess(), (PVOID)Thunks, (DWORD)(NewThunks - Thunks)); Thunks = NewThunks; }
ReleaseMutex( ApiMemMutex ); pTlsSetValue( TlsReEnter, (LPVOID) 0 );
}
BOOL ProcessApiTable( PDLL_INFO DllInfo ) { ULONG i,j; PAPI_MASTER_TABLE ApiMaster = NULL;
i = 0; while( ApiTables[i].Name ) { if (_stricmp( ApiTables[i].Name, DllInfo->Name ) == 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_PTR)DllList); while( ApiTable[i].Name ) { for (j=0; j<DllInfo->ApiCount; 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_PTR IatAddr, PUCHAR Text, PDLL_INFO DllInfo, PAPI_INFO ApiInfo ) { CHAR debugBuf[256]; #if DBG
_stprintf(debugBuf, "CreateApiThunk: %s:%s\n",DllInfo->Name, (LPSTR)MemPtr + ApiInfo->Name); OutputDebugString(debugBuf); #endif
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,"_setjmp")==0) || (strcmp(Name,"_setjmp3")==0) || (strcmp(Name,"longjmp")==0) || (strcmp(Name,"_longjmpex")==0) || (strcmp(Name,"TlsGetValue")==0) || (strcmp(Name,"TlsSetValue")==0) || (strncmp(Name,"_Ots",4)==0)) { return Text; }
PUCHAR stat = CreateMachApiThunk( (PULONG_PTR)IatAddr, Text, DllInfo, ApiInfo );
return stat; }
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 = APITYPE_NORMAL;
#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<MAX_DLLS; i++) { if (Address >= DllList[i].BaseAddress && Address < DllList[i].BaseAddress + DllList[i].Size) { *DllInfo = &DllList[i]; break; } }
if (!*DllInfo) { return 0; }
ai = (PAPI_INFO)((*DllInfo)->ApiOffset + (ULONG_PTR)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 = APITYPE_LOADLIBRARYA; } else if (Address == LoadLibraryW_Addr) { *ApiFlag = APITYPE_LOADLIBRARYW; } else if (Address == FreeLibrary_Addr) { *ApiFlag = APITYPE_FREELIBRARY; } else if (Address == GetProcAddress_Addr) { *ApiFlag = APITYPE_GETPROCADDRESS; } return rval; }
extern "C" VOID ApiTrace( PAPI_INFO ApiInfo, ULONG_PTR Arg[MAX_TRACE_ARGS], ULONG ReturnValue, ULONG Caller, DWORDLONG EnterTime, DWORDLONG Duration, ULONG LastError ) { PTRACE_ENTRY TraceEntry; ULONG TraceEntryLen; PTHREAD_STACK ThreadStack; LPSTR TraceString; LPSTR TraceLimit; CHAR debugBuf[128]; ULONG_PTR len; DWORD *dwPtr; ULONG i; ULONG ArgCount;
__try {
pTlsSetValue( TlsReEnter, (LPVOID) 1 ); WaitForSingleObject( ApiTraceMutex, INFINITE );
// if trace buffer has room for another entry
if ( TraceBuffer->Offset + sizeof(TRACE_ENTRY) < TraceBuffer->Size ) {
TraceEntry = (PTRACE_ENTRY)((PCHAR)TraceBuffer->Entry + TraceBuffer->Offset); TraceEntry->Address = ApiInfo->Address; TraceEntry->ReturnValue = ReturnValue; TraceEntry->Caller = Caller; TraceEntry->LastError = LastError; TraceEntry->ApiTableIndex = ApiInfo->ApiTableIndex; TraceEntry->EnterTime = EnterTime; TraceEntry->Duration = Duration;
ArgCount = (ApiInfo->ApiTable && ApiInfo->ApiTable->ArgCount) ? ApiInfo->ApiTable->ArgCount : DFLT_TRACE_ARGS;
for (i=0; i<ArgCount; i++) TraceEntry->Args[i] = Arg[i];
ThreadStack = (PTHREAD_STACK)pTlsGetValue(TlsStack); TraceEntry->ThreadNum = ThreadStack->ThreadNum; TraceEntry->Level = (DWORD)((ThreadStack->Pointer - (DWORD_PTR)ThreadStack->Body)) / FRAME_SIZE - 1;
TraceEntryLen = sizeof(TRACE_ENTRY);
if (ApiInfo->ApiTable && ApiInfo->ApiTable->ArgCount) {
PAPI_TABLE ApiTable = ApiInfo->ApiTable;
TraceString = (LPSTR)TraceEntry + sizeof(TRACE_ENTRY); TraceLimit = (LPSTR)TraceBuffer->Entry + TraceBuffer->Size;
for (i=0; i<ApiTable->ArgCount; i++) {
switch( LOWORD(ApiTable->ArgType[i]) ) { case T_DWORD: break;
case T_DWORDPTR: if (TraceEntry->Args[i]) { TraceEntry->Args[i] = *(DWORD*)(TraceEntry->Args[i] + HIWORD(ApiTable->ArgType[i])); } break;
case T_DLONGPTR: // Warning - this type wipes out the following arg to save a DWORDLONG
if (TraceEntry->Args[i]) { dwPtr = (DWORD*) (TraceEntry->Args[i] + HIWORD(ApiTable->ArgType[i])); TraceEntry->Args[i] = dwPtr[0]; TraceEntry->Args[i+1] = dwPtr[1]; } break;
case T_LPSTRC: case T_LPSTR: //
// go read the string
//
{ if (HIWORD(TraceEntry->Args[i]) == 0) len = 0; else if (ApiTable->ArgType[i] == T_LPSTRC) len = TraceEntry->Args[i+1]; else { TraceEntry->Args[i] += HIWORD(ApiTable->ArgType[i]); len = strlen( (LPSTR) TraceEntry->Args[i] ); }
if ( TraceString + len >= TraceLimit ) len = 0;
if (len) memcpy(TraceString, (LPSTR)TraceEntry->Args[i], len);
TraceString[len] = 0;
TraceString += Align(sizeof(WCHAR), (len + 1)); } break;
case T_LPWSTRC: case T_LPWSTR: //
// go read the string
//
{ if (HIWORD(TraceEntry->Args[i]) == 0) len = 0; else if (ApiTable->ArgType[i] == T_LPSTRC) len = TraceEntry->Args[i+1]; else { TraceEntry->Args[i] += HIWORD(ApiTable->ArgType[i]); len = (wcslen( (LPWSTR) TraceEntry->Args[i] )); }
if ( TraceString + len * sizeof(WCHAR) >= TraceLimit ) len = 0;
if (len) memcpy( (LPWSTR)TraceString, (LPWSTR) TraceEntry->Args[i], len * sizeof(WCHAR) );
((LPWSTR)TraceString)[len] = 0;
TraceString += (len + 1) * sizeof(WCHAR); } break;
case T_UNISTR: case T_OBJNAME: //
// go read the string
//
{ PUNICODE_STRING pustr;
if (ApiTable->ArgType[i] == T_OBJNAME) pustr = ((POBJECT_ATTRIBUTES)TraceEntry->Args[i])->ObjectName; else pustr = (PUNICODE_STRING)TraceEntry->Args[i];
len = pustr->Length + sizeof(WCHAR); if (pustr != NULL && TraceString + len < TraceLimit) { wcsncpy( (LPWSTR)TraceString, pustr->Buffer, pustr->Length/sizeof(WCHAR)); ((LPWSTR)TraceString)[pustr->Length/sizeof(WCHAR)] = 0; } else { len = sizeof(WCHAR); ((LPWSTR)TraceString)[0] = 0; }
TraceString += len; } break; } } // align overall entry length to DWORDLONG
TraceEntryLen = (DWORD)(Align(sizeof(DWORDLONG), TraceString - (LPSTR)TraceEntry)); } TraceBuffer->Count += 1; TraceEntry->SizeOfStruct = TraceEntryLen; TraceBuffer->Offset += TraceEntryLen; }
} __except( EXCEPTION_EXECUTE_HANDLER ) {
; }
ReleaseMutex( ApiTraceMutex ); pTlsSetValue( TlsReEnter, (LPVOID) 0 ); }
VOID CalibrateThunk( VOID ) { int i; DLL_INFO CalibDllInfo; API_INFO Calib1ApiInfo,Calib2ApiInfo; PUCHAR NewThunks; ULONGLONG MinTime; CHAR debugbuf[128];
// Setup calibration Dll
strcpy(CalibDllInfo.Name, "Calib"); CalibDllInfo.Enabled = TRUE;
// Setup calibration Api
Calib1ApiInfo.Count = 0; Calib1ApiInfo.NestCount = 0; Calib1ApiInfo.Time = 0; Calib1ApiInfo.CalleeTime = 0; Calib1ApiInfo.ThunkAddress = 0; Calib1ApiInfo.TraceEnabled = 0; Calib1ApiInfo.Address = (ULONG_PTR)Calib1Func;
Calib2ApiInfo.Count = 0; Calib2ApiInfo.NestCount = 0; Calib2ApiInfo.Time = 0; Calib2ApiInfo.CalleeTime = 0; Calib2ApiInfo.ThunkAddress = 0; Calib2ApiInfo.TraceEnabled = 0; Calib2ApiInfo.Address = (ULONG_PTR)Calib2Func;
// Create thunks
NewThunks = CreateMachApiThunk( (PULONG_PTR)&Calib1Thunk, Thunks, &CalibDllInfo, &Calib1ApiInfo ); NewThunks = CreateMachApiThunk( (PULONG_PTR)&Calib2Thunk, NewThunks, &CalibDllInfo, &Calib2ApiInfo ); FlushInstructionCache( GetCurrentProcess(), (PVOID)Thunks, (DWORD)(NewThunks - Thunks)); Thunks = NewThunks;
ThunkOverhead = 0; ThunkCallOverhead = 0;
// Call the calibration function via the thunk
MinTime = 1000000; for (i=0; i<1000; i++) {
Calib1ApiInfo.Time = 0;
(*Calib1Thunk)();
if (Calib1ApiInfo.Time < MinTime) MinTime = Calib1ApiInfo.Time; }
// Take min time as the overhead
ThunkOverhead = (DWORD)MinTime;
MinTime = 1000000; for (i=0; i<1000; i++) {
Calib2ApiInfo.Time = 0;
(*Calib2Thunk)();
if (Calib2ApiInfo.Time < MinTime) MinTime = Calib1ApiInfo.Time; }
ThunkCallOverhead = (DWORD)MinTime; }
// Null function for measuring overhead
VOID Calib1Func( VOID ) { return; }
// Calling function for measuring overhead
VOID Calib2Func( VOID ) { (*Calib1Thunk)(); }
|