#include #include #include #include #include #include #include #include #include "pperf.h" #include "..\pstat.h" extern HANDLE DriverHandle; extern UCHAR Buffer[]; #define BufferSize 60000 typedef struct NameList { struct NameList *Next; ULONG Parm; struct NameList *ChildList; PUCHAR Name; } NAME_LIST, *PNAME_LIST; PNAME_LIST DriverList; PNAME_LIST ActiveThunks; PNAME_LIST SourceModule, ImportModule; #define COMBOCMD(a,b) ((a << 16) | b) NTSTATUS openfile ( IN PHANDLE filehandle, IN PUCHAR BasePath, IN PUCHAR Name ); VOID readfile ( HANDLE handle, ULONG offset, ULONG len, PVOID buffer ); ULONG ConvertImportAddress ( IN ULONG ImageRelativeAddress, IN ULONG PoolAddress, IN PIMAGE_SECTION_HEADER SectionHeader ); VOID ThunkCreateDriverList (VOID); #define IMPADDRESS(a) ConvertImportAddress((ULONG)a, (ULONG)Buffer, &SectionHeader) ULONG HookThunk (PNAME_LIST, PNAME_LIST, PNAME_LIST); VOID SnapPrivateInfo (PDISPLAY_ITEM); VOID NameList2ComboBox (HWND hDlg, ULONG id, PNAME_LIST List); PNAME_LIST AddNameEntry (PNAME_LIST *head, PUCHAR name, ULONG Parm); VOID FreeNameList (PNAME_LIST List); PNAME_LIST GetComboSelection (HWND h, ULONG id); VOID NameList2ListBox (HWND hDlg, ULONG id, PNAME_LIST List); VOID loadimagedir (PUCHAR, ULONG, PIMAGE_SECTION_HEADER); VOID RemoveHook (HWND hDlg); VOID ClearAllHooks (HWND hDlg); VOID AddThunk (HWND hDlg); VOID loadexports (PNAME_LIST Driver, PNAME_LIST Item); //#define IDM_THUNK_LIST 301 //#define IDM_THUNK_SOURCE 302 //#define IDM_THUNK_IMPORT 303 //#define IDM_THUNK_FUNCTION 304 //#define IDM_THUNK_ADD 305 //#define IDM_THUNK_REMOVE 306 BOOL APIENTRY ThunkDlgProc( HWND hDlg, unsigned message, DWORD wParam, LONG lParam ) { PNAME_LIST Item; switch (message) { case WM_INITDIALOG: SourceModule = NULL; ImportModule = NULL; ThunkCreateDriverList (); NameList2ComboBox (hDlg, IDM_THUNK_SOURCE, DriverList); NameList2ListBox (hDlg, IDM_THUNK_LIST, ActiveThunks); return (TRUE); case WM_COMMAND: switch(wParam) { // // end function // case COMBOCMD (CBN_SELCHANGE, IDM_THUNK_SOURCE): case COMBOCMD (CBN_SELCHANGE, IDM_THUNK_IMPORT): Item = GetComboSelection (hDlg, IDM_THUNK_SOURCE); if (Item && Item != SourceModule) { SourceModule = Item; NameList2ComboBox (hDlg, IDM_THUNK_IMPORT, Item->ChildList); } Item = GetComboSelection (hDlg, IDM_THUNK_IMPORT); if (Item && Item != ImportModule) { ImportModule = Item; NameList2ComboBox (hDlg, IDM_THUNK_FUNCTION, Item->ChildList); } break; case IDM_THUNK_REMOVE: RemoveHook (hDlg); break; case IDM_THUNK_CLEAR_ALL: ClearAllHooks (hDlg); break; case IDM_THUNK_ADD: AddThunk (hDlg); break; case IDOK: case IDCANCEL: //DlgThunkData (hDlg); FreeNameList (DriverList); DriverList = NULL; EndDialog(hDlg, DIALOG_SUCCESS); return (TRUE); } } return (FALSE); } VOID AddThunk (HWND hDlg) { PDISPLAY_ITEM pPerf; PNAME_LIST Item; ULONG id, i; PUCHAR p; HWND thunklist; id = 0; Item = GetComboSelection (hDlg, IDM_THUNK_FUNCTION); if (Item && SourceModule && ImportModule) { id = HookThunk (SourceModule, ImportModule, Item); } if (!id) { MessageBox(hDlg,"Thunk was not hooked","Hook error",MB_OK); return; } pPerf = AllocateDisplayItem(); // // build name (the hard way?) // strcpy (pPerf->PerfName, Item->Name); strcat (pPerf->PerfName, "("); strcat (pPerf->PerfName, SourceModule->Name); for (p=pPerf->PerfName; *p; p++) { if (*p == '.') *p = 0; } strcat (pPerf->PerfName, ">"); strcat (pPerf->PerfName, ImportModule->Name); for (p=pPerf->PerfName; *p; p++) { if (*p == '.') *p = 0; } strcat (pPerf->PerfName, ")"); // // Add to thunk list // Item = malloc (sizeof (NAME_LIST)); if (Item == NULL) { printf("Memory allocation failed.\n"); exit(1); } Item->Name = _strdup (pPerf->PerfName); Item->Parm = (ULONG) pPerf; Item->ChildList = NULL; Item->Next = ActiveThunks; ActiveThunks = Item; pPerf->SnapParam2 = id; // bugbug NameList2ListBox (hDlg, IDM_THUNK_LIST, ActiveThunks); // // Add graph to windows // pPerf->SnapData = SnapPrivateInfo; // generic snap pPerf->SnapParam1 = OFFSET(PSTATS, ThunkCounters[id-1]); SetDisplayToTrue (pPerf, 99); RefitWindows(NULL, NULL); UpdateInternalStats (); pPerf->SnapData (pPerf); UpdateInternalStats (); pPerf->SnapData (pPerf); } VOID ClearAllHooks (HWND hDlg) { PDISPLAY_ITEM pPerf; IO_STATUS_BLOCK IOSB; ULONG id; PNAME_LIST Item; while (ActiveThunks) { pPerf = (PDISPLAY_ITEM) ActiveThunks->Parm; Item = ActiveThunks; ActiveThunks = ActiveThunks->Next; free (Item->Name); free (Item); id = pPerf->SnapParam2; SetDisplayToFalse (pPerf); // remove window FreeDisplayItem (pPerf); // notify driver NtDeviceIoControlFile( DriverHandle, (HANDLE) NULL, // event (PIO_APC_ROUTINE) NULL, (PVOID) NULL, &IOSB, PSTAT_REMOVE_HOOK, &id, // input buffer sizeof (ULONG), NULL, // output buffer 0 ); } NameList2ListBox (hDlg, IDM_THUNK_LIST, ActiveThunks); RefitWindows (NULL, NULL); } VOID RemoveHook (HWND hDlg) { ULONG i, id; HWND ListBox; PNAME_LIST Item, *pp; PDISPLAY_ITEM pPerf; IO_STATUS_BLOCK IOSB; ListBox = GetDlgItem(hDlg, IDM_THUNK_LIST); i = SendMessage(ListBox, LB_GETCURSEL, 0, 0); if (i == -1) { return; } pPerf = (PDISPLAY_ITEM) SendMessage(ListBox, LB_GETITEMDATA, i, 0); Item = NULL; for (pp = &ActiveThunks; *pp; pp = &(*pp)->Next) { if ((*pp)->Parm == (ULONG)pPerf) { Item = *pp; *pp = (*pp)->Next; // remove from list break ; } } if (!Item) { return ; } free (Item->Name); free (Item); id = pPerf->SnapParam2; SetDisplayToFalse (pPerf); // remove window FreeDisplayItem (pPerf); // notify driver NtDeviceIoControlFile( DriverHandle, (HANDLE) NULL, // event (PIO_APC_ROUTINE) NULL, (PVOID) NULL, &IOSB, PSTAT_REMOVE_HOOK, &id, // input buffer sizeof (ULONG), NULL, // output buffer 0 ); NameList2ListBox (hDlg, IDM_THUNK_LIST, ActiveThunks); RefitWindows (NULL, NULL); } VOID NameList2ListBox (HWND hDlg, ULONG id, PNAME_LIST List) { HWND ListBox; ULONG nIndex; ListBox = GetDlgItem(hDlg, id); SendMessage(ListBox, LB_RESETCONTENT, 0, 0); SendMessage(ListBox, LB_SETITEMDATA, 0L, 0L); while (List) { nIndex = SendMessage(ListBox, LB_ADDSTRING, 0, (LPARAM)List->Name); SendMessage(ListBox, LB_SETITEMDATA, nIndex, List->Parm); List = List->Next; } } VOID NameList2ComboBox (HWND hDlg, ULONG id, PNAME_LIST List) { HWND ComboList; ULONG nIndex; ComboList = GetDlgItem(hDlg, id); SendMessage(ComboList, CB_RESETCONTENT, 0, 0); SendMessage(ComboList, CB_SETITEMDATA, 0L, 0L); while (List) { nIndex = SendMessage(ComboList, CB_ADDSTRING, 0, (LPARAM)List->Name); SendMessage(ComboList, CB_SETITEMDATA, nIndex, (ULONG) List); List = List->Next; } SendMessage(ComboList, CB_SETCURSEL, 0, 0L); } PNAME_LIST GetComboSelection (HWND hDlg, ULONG id) { ULONG i; HWND ComboList; ComboList = GetDlgItem(hDlg, id); i = SendMessage(ComboList, CB_GETCURSEL, 0, 0); if (i == -1) { return NULL; } return (PNAME_LIST) SendMessage(ComboList, CB_GETITEMDATA, i, 0); } VOID FreeNameList (PNAME_LIST List) { PNAME_LIST p1; while (List) { if (List->ChildList) FreeNameList (List->ChildList); p1 = List->Next; free (List->Name); free (List); List = p1; } } ULONG HookThunk (PNAME_LIST HookSource, PNAME_LIST TargetModule, PNAME_LIST Function) { PNAME_LIST SourceModule; IO_STATUS_BLOCK IOSB; HOOKTHUNK HookData; ULONG TracerId; NTSTATUS status; TracerId = 0; for (SourceModule=DriverList; SourceModule; SourceModule = SourceModule->Next) { if (SourceModule->Parm == -1) { continue; } if (SourceModule->Parm != HookSource->Parm && HookSource->Parm != -1) { continue; } HookData.SourceModule = SourceModule->Name; HookData.ImageBase = SourceModule->Parm; HookData.TargetModule = TargetModule->Name; HookData.Function = Function->Name; HookData.TracerId = TracerId; // // Ask driver to hook this thunk // status = NtDeviceIoControlFile( DriverHandle, (HANDLE) NULL, // event (PIO_APC_ROUTINE) NULL, (PVOID) NULL, &IOSB, PSTAT_HOOK_THUNK, &HookData, // input buffer sizeof (HookData), NULL, // output buffer 0 ); if (NT_SUCCESS(status)) { TracerId = HookData.TracerId; } } return TracerId; } VOID ThunkCreateDriverList () { ULONG i; PRTL_PROCESS_MODULES Modules; PRTL_PROCESS_MODULE_INFORMATION Module; NTSTATUS status; PNAME_LIST Driver, Import, Item, AbortState; PIMAGE_IMPORT_DESCRIPTOR ImpDescriptor; IMAGE_SECTION_HEADER SectionHeader; ULONG ThunkAddr, ThunkData; // // Query driver list // status = NtQuerySystemInformation ( SystemModuleInformation, Buffer, BufferSize, &i); if (!NT_SUCCESS(status)) { return; } // // Add drivers // Modules = (PRTL_PROCESS_MODULES) Buffer; Module = &Modules->Modules[ 0 ]; for (i = 0; i < Modules->NumberOfModules; i++) { Driver = AddNameEntry ( &DriverList, Module->FullPathName + Module->OffsetToFileName, (ULONG) Module->ImageBase ); Module++; } // // Add imports for each driver // for (Driver = DriverList; Driver; Driver = Driver->Next) { try { // // Read in source image's headers // AbortState = Driver; loadimagedir (Driver->Name, IMAGE_DIRECTORY_ENTRY_IMPORT, &SectionHeader); // // Go through each import module // ImpDescriptor = (PIMAGE_IMPORT_DESCRIPTOR) Buffer; while (ImpDescriptor->Characteristics) { AbortState = Driver; // // Add this import to driver's list // Import = AddNameEntry ( &Driver->ChildList, (PUCHAR) IMPADDRESS(ImpDescriptor->Name), 1 ); AbortState = Import; // // Go through each function for the import module // ThunkAddr = IMPADDRESS (ImpDescriptor->OriginalFirstThunk); for (; ;) { ThunkData = (ULONG)((PIMAGE_THUNK_DATA) ThunkAddr)->u1.AddressOfData; if (ThunkData == 0) { // end of table break; } // // Add this function to import list // AddNameEntry ( &Import->ChildList, ((PIMAGE_IMPORT_BY_NAME) IMPADDRESS(ThunkData))->Name, 0 ); // next thunk ThunkAddr += sizeof (IMAGE_THUNK_DATA); } // next import table ImpDescriptor++; } } except(EXCEPTION_EXECUTE_HANDLER) { AddNameEntry(&AbortState->ChildList, "* ERROR *", 1); } // next driver } // // Add "Any driver" selection // Driver = AddNameEntry(&DriverList, "*Any", (ULONG)-1); // // For child module list use complete driver list, which is // now on the next pointer of Driver. // for (Item = Driver->Next; Item; Item = Item->Next) { // bogus compiler - need to make a subfunction here to keep // the compiler happy loadexports (Driver, Item); } } VOID loadexports (PNAME_LIST Driver, PNAME_LIST Item) { IMAGE_SECTION_HEADER SectionHeader; PIMAGE_EXPORT_DIRECTORY ExpDirectory; PULONG ExpNameAddr; PNAME_LIST Import; ULONG i; try { loadimagedir ( Item->Name, IMAGE_DIRECTORY_ENTRY_EXPORT, &SectionHeader ); } except(EXCEPTION_EXECUTE_HANDLER) { return ; } Import = AddNameEntry (&Driver->ChildList, Item->Name, Item->Parm); try { ExpDirectory = (PIMAGE_EXPORT_DIRECTORY) Buffer; ExpNameAddr = (PULONG)IMPADDRESS (ExpDirectory->AddressOfNames); for (i=0; i < ExpDirectory->NumberOfNames; i++) { AddNameEntry ( &Import->ChildList, (PUCHAR) IMPADDRESS(*ExpNameAddr), 0 ); ExpNameAddr++; } } except(EXCEPTION_EXECUTE_HANDLER) { AddNameEntry(&Import->ChildList, "* ERROR *", 1); } } VOID loadimagedir ( IN PUCHAR filename, IN ULONG dirno, OUT PIMAGE_SECTION_HEADER SectionHeader ) { HANDLE filehandle; ULONG i, j, Dir; NTSTATUS status; IMAGE_DOS_HEADER DosImageHeader; IMAGE_NT_HEADERS NtImageHeader; PIMAGE_SECTION_HEADER pSectionHeader; status = openfile (&filehandle, "\\SystemRoot\\", filename); if (!NT_SUCCESS(status)) { status = openfile (&filehandle, "\\SystemRoot\\System32\\", filename); } if (!NT_SUCCESS(status)) { status = openfile (&filehandle, "\\SystemRoot\\System32\\Drivers\\", filename); } if (!NT_SUCCESS(status)) { RtlRaiseStatus (1); } try { readfile ( filehandle, 0, sizeof (DosImageHeader), (PVOID) &DosImageHeader ); if (DosImageHeader.e_magic != IMAGE_DOS_SIGNATURE) { RtlRaiseStatus (1); } readfile ( filehandle, DosImageHeader.e_lfanew, sizeof (NtImageHeader), (PVOID) &NtImageHeader ); if (NtImageHeader.Signature != IMAGE_NT_SIGNATURE) { RtlRaiseStatus (1); } // // read in complete sections headers from image // i = NtImageHeader.FileHeader.NumberOfSections * sizeof (IMAGE_SECTION_HEADER); j = ((ULONG) IMAGE_FIRST_SECTION (&NtImageHeader)) - ((ULONG) &NtImageHeader) + DosImageHeader.e_lfanew; if (i > BufferSize) { RtlRaiseStatus (1); } readfile ( filehandle, j, // file offset i, // length Buffer ); // // Find section with import directory // Dir = NtImageHeader.OptionalHeader.DataDirectory[dirno].VirtualAddress; i = 0; pSectionHeader = (PIMAGE_SECTION_HEADER)Buffer; for (; ;) { if (i >= NtImageHeader.FileHeader.NumberOfSections) { RtlRaiseStatus (1); } if (pSectionHeader->VirtualAddress <= Dir && pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData > Dir) { break; } i += 1; pSectionHeader += 1; } Dir -= pSectionHeader->VirtualAddress; pSectionHeader->VirtualAddress += Dir; pSectionHeader->PointerToRawData += Dir; pSectionHeader->SizeOfRawData -= Dir; *SectionHeader = *pSectionHeader; // // read in complete export section from image // if (SectionHeader->SizeOfRawData > BufferSize) { RtlRaiseStatus (1); } readfile ( filehandle, SectionHeader->PointerToRawData, SectionHeader->SizeOfRawData, Buffer ); } finally { // // Clean up // NtClose (filehandle); } } PNAME_LIST AddNameEntry (PNAME_LIST *head, PUCHAR name, ULONG Parm) { PNAME_LIST Entry; Entry = malloc (sizeof (NAME_LIST)); if (Entry == NULL) { printf("Memory allocation failed.\n"); exit(1); } Entry->Name = _strdup (name); if (Entry->Name == NULL) { printf("Memory allocation failed.\n"); exit(1); } Entry->Parm = Parm; Entry->ChildList = NULL; if (Parm) { _strlwr (Entry->Name); } Entry->Next = *head; *head = Entry; return Entry; } NTSTATUS openfile ( IN PHANDLE filehandle, IN PUCHAR BasePath, IN PUCHAR Name ) { ANSI_STRING AscBasePath, AscName; UNICODE_STRING UniPathName, UniName; NTSTATUS status; OBJECT_ATTRIBUTES ObjA; IO_STATUS_BLOCK IOSB; UCHAR StringBuf[500]; // // Build name // UniPathName.Buffer = (PWCHAR)StringBuf; UniPathName.Length = 0; UniPathName.MaximumLength = sizeof( StringBuf ); RtlInitString(&AscBasePath, BasePath); status = RtlAnsiStringToUnicodeString( &UniPathName, &AscBasePath, FALSE ); if (!NT_SUCCESS(status)) { return status; } RtlInitString(&AscName, Name); status = RtlAnsiStringToUnicodeString( &UniName, &AscName, TRUE ); if (!NT_SUCCESS(status)) { return status; } status = RtlAppendUnicodeStringToString (&UniPathName, &UniName); if (!NT_SUCCESS(status)) { RtlFreeUnicodeString (&UniName); return status; } InitializeObjectAttributes( &ObjA, &UniPathName, OBJ_CASE_INSENSITIVE, 0, 0 ); // // open file // status = NtOpenFile ( filehandle, // return handle SYNCHRONIZE | FILE_READ_DATA, // desired access &ObjA, // Object &IOSB, // io status block FILE_SHARE_READ | FILE_SHARE_WRITE, // share access FILE_SYNCHRONOUS_IO_ALERT // open options ); RtlFreeUnicodeString (&UniName); return status; } VOID readfile ( HANDLE handle, ULONG offset, ULONG len, PVOID buffer ) { NTSTATUS status; IO_STATUS_BLOCK iosb; LARGE_INTEGER foffset; foffset = RtlConvertUlongToLargeInteger(offset); status = NtReadFile ( handle, NULL, // event NULL, // apc routine NULL, // apc context &iosb, buffer, len, &foffset, NULL ); if (!NT_SUCCESS(status)) { RtlRaiseStatus (1); } } ULONG ConvertImportAddress ( IN ULONG ImageRelativeAddress, IN ULONG PoolAddress, IN PIMAGE_SECTION_HEADER SectionHeader ) { ULONG EffectiveAddress; EffectiveAddress = PoolAddress + ImageRelativeAddress - SectionHeader->VirtualAddress; if (EffectiveAddress < PoolAddress || EffectiveAddress > PoolAddress + SectionHeader->SizeOfRawData) { RtlRaiseStatus (1); } return EffectiveAddress; }