├── Driver ├── AltCall.c └── Extras.h ├── GUI ├── CallMon.c ├── Resource.rc ├── Utils.h └── resource.h ├── README.md └── Rust ├── .cargo └── config ├── Cargo.toml ├── Makefile.toml ├── build.rs ├── rustfmt.toml └── src ├── defines.rs ├── externs.rs ├── lib.rs ├── log.rs └── string.rs /Driver/AltCall.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Extras.h" 5 | 6 | #pragma warning(disable : 4201) 7 | 8 | #define ProcessAltSystemCallInformation 0x64 9 | #define RTL_WALK_USER_MODE_STACK 0x00000001 10 | #define IOCTL_ADD_PROCESS 0x550000 11 | #define IOCTL_REMOVE_PROCESS 0x550002 12 | #define IOCTL_INIT 0x550004 13 | 14 | typedef DWORD64 QWORD; 15 | typedef UINT16 WORD; 16 | typedef UCHAR BYTE; 17 | typedef NTSTATUS(NTAPI* PsRegisterAltSystemCallHandler)(PVOID HandlerFunction, LONG HandlerIndex); 18 | typedef NTSTATUS(NTAPI* ZwSetInformationProcess)(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, 19 | ULONG ProcessInformationLength); 20 | typedef NTSTATUS(NTAPI* PsSuspendProcess)(PEPROCESS Process); 21 | typedef NTSTATUS(NTAPI* PsResumeProcess)(PEPROCESS Process); 22 | 23 | 24 | // Globals 25 | PsRegisterAltSystemCallHandler pPsRegisterAltSystemCallHandler; 26 | ZwSetInformationProcess pZwSetInformationProcess; 27 | PsSuspendProcess pPsSuspendProcess; 28 | PsResumeProcess pPsResumeProcess; 29 | HANDLE hGlobalPipe = 0; 30 | 31 | NTSTATUS DriverUnload(PDRIVER_OBJECT DriverObject) 32 | { 33 | UNREFERENCED_PARAMETER(DriverObject); 34 | 35 | return(STATUS_ACCESS_DENIED); 36 | } 37 | 38 | BOOLEAN MyHandler(PKTRAP_FRAME Frame) 39 | { 40 | IO_STATUS_BLOCK ioBlock; 41 | TOTAL_PACKET totalPacket; 42 | NTSTATUS ntRet; 43 | 44 | if (hGlobalPipe) 45 | { 46 | totalPacket.CustomHeader.ProcessId = (ULONG64)PsGetProcessId(PsGetCurrentProcess()); 47 | if (((QWORD)Frame->Rsp < (QWORD)MmHighestUserAddress) && (MmIsAddressValid((PVOID)Frame->Rsp))) 48 | { 49 | memmove(&totalPacket.CustomHeader.StackData, (PVOID)Frame->Rsp, sizeof(totalPacket.CustomHeader.StackData)); 50 | } 51 | memmove(&totalPacket.Frame, Frame, sizeof(KTRAP_FRAME)); 52 | ntRet = ZwWriteFile(hGlobalPipe, 0, NULL, NULL, &ioBlock, &totalPacket, sizeof(totalPacket), NULL, NULL); 53 | } 54 | return(TRUE); 55 | } 56 | 57 | NTSTATUS AddProcess(DWORD PID) 58 | { 59 | OBJECT_ATTRIBUTES objAttr; 60 | CLIENT_ID clientId; 61 | HANDLE hProcess; 62 | QWORD qwPID; 63 | 64 | 65 | InitializeObjectAttributes(&objAttr, NULL, OBJ_KERNEL_HANDLE, 0, 0); 66 | clientId.UniqueProcess = (HANDLE)PID; 67 | clientId.UniqueThread = 0; 68 | hProcess = 0; 69 | if (NT_SUCCESS(ZwOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &objAttr, &clientId))) 70 | { 71 | qwPID = (QWORD)clientId.UniqueProcess; 72 | if (NT_SUCCESS(pZwSetInformationProcess(hProcess, (PROCESSINFOCLASS)ProcessAltSystemCallInformation, &qwPID, 1))) 73 | { 74 | ZwClose(hProcess); 75 | return(STATUS_SUCCESS); 76 | } 77 | } 78 | return(STATUS_UNSUCCESSFUL); 79 | } 80 | 81 | // ETHREAD is version dependent 82 | typedef struct _ETHREAD 83 | { 84 | BYTE Random[0x4E8]; 85 | LIST_ENTRY ThreadListEntry; 86 | }ETHREAD; 87 | 88 | NTSTATUS RemoveProcess(DWORD PID) 89 | { 90 | OBJECT_ATTRIBUTES objAttr; 91 | PLIST_ENTRY pThreadHead; 92 | PLIST_ENTRY pThreadNext; 93 | PEPROCESS pProcess; 94 | CLIENT_ID clientId; 95 | PETHREAD pThread; 96 | NTSTATUS ntRet; 97 | HANDLE hProcess; 98 | QWORD qwPID; 99 | 100 | 101 | InitializeObjectAttributes(&objAttr, NULL, OBJ_KERNEL_HANDLE, 0, 0); 102 | clientId.UniqueProcess = (HANDLE)PID; 103 | clientId.UniqueThread = 0; 104 | hProcess = 0; 105 | ntRet = STATUS_UNSUCCESSFUL; 106 | if (NT_SUCCESS(ZwOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &objAttr, &clientId))) 107 | { 108 | qwPID = (QWORD)clientId.UniqueProcess; 109 | 110 | if (NT_SUCCESS(ObReferenceObjectByHandleWithTag(hProcess, PROCESS_ALL_ACCESS, *PsProcessType, KernelMode, 0x75537350, &pProcess, NULL))) 111 | { 112 | ntRet = pPsSuspendProcess(pProcess); 113 | if (NT_SUCCESS(ntRet)) 114 | { 115 | /* 116 | Because Microsoft doesn't document the structure of EPROCESS or KTHREAD 117 | members and offsets here could change in a new release. 118 | See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/eprocess 119 | */ 120 | pThreadHead = (PLIST_ENTRY)((BYTE*)pProcess + 0x5E0); //EPROCESS.ThreadListHead 121 | pThreadNext = pThreadHead->Flink; 122 | 123 | while (pThreadNext != pThreadHead) 124 | { 125 | pThread = (PETHREAD)((BYTE*)pThreadNext - 0x4E8); 126 | _interlockedbittestandreset((PLONG)pThread, 0x1D); 127 | pThreadNext = pThreadNext->Flink; 128 | } 129 | 130 | ntRet = pPsResumeProcess(pProcess); 131 | } 132 | ObDereferenceObjectWithTag(pProcess, 0x75537350); 133 | } 134 | ZwClose(hProcess); 135 | } 136 | return(ntRet); 137 | } 138 | 139 | NTSTATUS DeviceDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp) 140 | { 141 | PIO_STACK_LOCATION pStack; 142 | OBJECT_ATTRIBUTES objPipe; 143 | IO_STATUS_BLOCK ioBlock; 144 | UNICODE_STRING usPipe; 145 | NTSTATUS ntRet; 146 | UNREFERENCED_PARAMETER(DeviceObject); 147 | 148 | pStack = IoGetCurrentIrpStackLocation(Irp); 149 | ntRet = STATUS_SUCCESS; 150 | if (pStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) 151 | { 152 | switch (pStack->Parameters.DeviceIoControl.IoControlCode) 153 | { 154 | case IOCTL_ADD_PROCESS: 155 | if (pStack->Parameters.DeviceIoControl.InputBufferLength == 4) 156 | { 157 | ntRet = AddProcess(*(DWORD*)Irp->AssociatedIrp.SystemBuffer); 158 | } 159 | else 160 | { 161 | ntRet = STATUS_BUFFER_TOO_SMALL; 162 | } 163 | break; 164 | case IOCTL_REMOVE_PROCESS: 165 | if (pStack->Parameters.DeviceIoControl.InputBufferLength == 4) 166 | { 167 | ntRet = RemoveProcess(*(DWORD*)Irp->AssociatedIrp.SystemBuffer); 168 | } 169 | else 170 | { 171 | ntRet = STATUS_BUFFER_TOO_SMALL; 172 | } 173 | break; 174 | case IOCTL_INIT: 175 | if (hGlobalPipe) 176 | { 177 | ZwClose(hGlobalPipe); 178 | hGlobalPipe = 0; 179 | } 180 | if (!hGlobalPipe) 181 | { 182 | RtlInitUnicodeString(&usPipe, L"\\Device\\NamedPipe\\CallMonPipe"); 183 | InitializeObjectAttributes(&objPipe, &usPipe, OBJ_KERNEL_HANDLE, 0, 0); 184 | if (NT_SUCCESS(ZwCreateFile(&hGlobalPipe, FILE_WRITE_DATA | SYNCHRONIZE, &objPipe, &ioBlock, NULL, 0, 0, FILE_OPEN, 185 | FILE_SYNCHRONOUS_IO_NONALERT | FILE_DELETE_ON_CLOSE, NULL, 0))) 186 | { 187 | ntRet = STATUS_SUCCESS; 188 | } 189 | else 190 | { 191 | ntRet = STATUS_PIPE_BROKEN; 192 | } 193 | } 194 | break; 195 | default: 196 | break; 197 | } 198 | 199 | } 200 | Irp->IoStatus.Status = ntRet; 201 | IofCompleteRequest(Irp, IO_NO_INCREMENT); 202 | return(ntRet); 203 | } 204 | 205 | NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) 206 | { 207 | UNICODE_STRING usRoutine; 208 | UNICODE_STRING usDevice; 209 | UNICODE_STRING usSymLink; 210 | PDEVICE_OBJECT pDeviceObj; 211 | 212 | UNREFERENCED_PARAMETER(RegistryPath); 213 | 214 | RtlInitUnicodeString(&usRoutine, L"PsRegisterAltSystemCallHandler"); 215 | pPsRegisterAltSystemCallHandler = (PsRegisterAltSystemCallHandler)MmGetSystemRoutineAddress(&usRoutine); 216 | RtlInitUnicodeString(&usRoutine, L"ZwSetInformationProcess"); 217 | pZwSetInformationProcess = (ZwSetInformationProcess)MmGetSystemRoutineAddress(&usRoutine); 218 | RtlInitUnicodeString(&usRoutine, L"PsSuspendProcess"); 219 | pPsSuspendProcess = (PsSuspendProcess)MmGetSystemRoutineAddress(&usRoutine); 220 | RtlInitUnicodeString(&usRoutine, L"PsResumeProcess"); 221 | pPsResumeProcess = (PsResumeProcess)MmGetSystemRoutineAddress(&usRoutine); 222 | 223 | if ((pPsRegisterAltSystemCallHandler) && (pZwSetInformationProcess) && (pPsSuspendProcess) && (pPsResumeProcess)) 224 | { 225 | DriverObject->DriverUnload = DriverUnload; 226 | for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) 227 | { 228 | DriverObject->MajorFunction[i] = DeviceDispatch; 229 | } 230 | RtlInitUnicodeString(&usDevice, L"\\Device\\CallMon"); 231 | if (NT_SUCCESS(IoCreateDevice(DriverObject, 0, &usDevice, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObj))) 232 | { 233 | RtlInitUnicodeString(&usSymLink, L"\\DosDevices\\CallMon"); 234 | if (NT_SUCCESS(IoCreateSymbolicLink(&usSymLink, &usDevice))) 235 | { 236 | pDeviceObj->Flags |= DO_BUFFERED_IO; 237 | pPsRegisterAltSystemCallHandler((PVOID)MyHandler, 1); 238 | return(STATUS_SUCCESS); 239 | } 240 | } 241 | } 242 | return(STATUS_UNSUCCESSFUL); 243 | } 244 | -------------------------------------------------------------------------------- /Driver/Extras.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct _CUSTOM_HEADER 4 | { 5 | ULONG64 ProcessId; 6 | ULONG64 StackData[0x10]; 7 | } CUSTOM_HEADER, * PCUSTOM_HEADER; 8 | 9 | typedef struct _TOTAL_PACKET 10 | { 11 | CUSTOM_HEADER CustomHeader; 12 | KTRAP_FRAME Frame; 13 | } TOTAL_PACKET, * PTOTAL_PACKET; 14 | -------------------------------------------------------------------------------- /GUI/CallMon.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Utils.h" 7 | #include "resource.h" // This includes everything from the resource view (forms, elements, configs...) 8 | 9 | #pragma comment(linker,"\"/manifestdependency:type='win32' \ 10 | name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ 11 | processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 12 | #pragma comment(linker, "/ENTRY:WinMain") 13 | #pragma comment(linker, "/SUBSYSTEM:WINDOWS") 14 | #pragma comment(lib, "UxTheme.lib") 15 | #pragma comment(lib, "Shlwapi.lib") 16 | 17 | VOID GUIInit(HWND hwnd) 18 | { 19 | SetThemeAppProperties(STAP_ALLOW_NONCLIENT | STAP_ALLOW_CONTROLS); 20 | SetWindowTheme(hwnd, L"Explorer", NULL); 21 | ListView_SetExtendedListViewStyle(GetDlgItem(hwnd, IDC_LIST_MAIN), LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT | LVS_EX_ONECLICKACTIVATE); 22 | CListView_InsertColumn(GetDlgItem(hwnd, IDC_LIST_MAIN), 0, 0, 0, L"PID", 64); 23 | CListView_InsertColumn(GetDlgItem(hwnd, IDC_LIST_MAIN), 1, 1, 1, L"Syscall Number", 100); 24 | CListView_InsertColumn(GetDlgItem(hwnd, IDC_LIST_MAIN), 2, 2, 2, L"Arg #1", 170); 25 | CListView_InsertColumn(GetDlgItem(hwnd, IDC_LIST_MAIN), 3, 3, 3, L"Arg #2", 170); 26 | CListView_InsertColumn(GetDlgItem(hwnd, IDC_LIST_MAIN), 4, 4, 4, L"Arg #3", 170); 27 | CListView_InsertColumn(GetDlgItem(hwnd, IDC_LIST_MAIN), 5, 5, 5, L"Arg #4", 170); 28 | SetDlgItemTextA(hwnd, IDC_MEMO_STACK, "(Nothing selected)"); 29 | } 30 | 31 | BOOL WINAPI EventHandler(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam) { 32 | LPNMITEMACTIVATE pActivatedItem; 33 | PSTACK_CHUNK pStackChunk; 34 | LPNMHDR pNm; 35 | DWORD dwIndex; 36 | PVOID pData; 37 | HWND hList; 38 | HWND hBox; 39 | HWND hMemo; 40 | RECT rWindow; 41 | RECT rList; 42 | RECT rBox; 43 | RECT rMemo; 44 | INT nWidth; 45 | INT nHeight; 46 | 47 | switch (Msg) 48 | { 49 | case WM_NOTIFY: 50 | pNm = (LPNMHDR)lParam; 51 | if (pNm->code == LVN_ITEMACTIVATE) 52 | { 53 | pActivatedItem = (LPNMITEMACTIVATE)lParam; 54 | if (pGlobalStackMem) 55 | { 56 | dwIndex = pActivatedItem->iItem; 57 | pStackChunk = (PSTACK_CHUNK)((BYTE*)pGlobalStackMem + (dwIndex * sizeof(STACK_CHUNK))); 58 | pData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x1000); 59 | if (pData) 60 | { 61 | wnsprintfW(pData, 0x1000, L"0x%I64X\r\n0x%I64X\r\n0x%I64X\r\n0x%I64X\r\n0x%I64X\r\n0x%I64X\r\n0x%I64X\r\n0x%I64X\r\n" 62 | L"0x%I64X\r\n0x%I64X\r\n0x%I64X\r\n0x%I64X\r\n0x%I64X\r\n0x%I64X\r\n0x%I64X\r\n0x%I64X\r\n", 63 | pStackChunk->StackData[0], pStackChunk->StackData[1], pStackChunk->StackData[2], 64 | pStackChunk->StackData[3], pStackChunk->StackData[4], pStackChunk->StackData[5], 65 | pStackChunk->StackData[6], pStackChunk->StackData[7], pStackChunk->StackData[8], 66 | pStackChunk->StackData[9], pStackChunk->StackData[10], pStackChunk->StackData[11], 67 | pStackChunk->StackData[12], pStackChunk->StackData[13], pStackChunk->StackData[14], 68 | pStackChunk->StackData[15]); 69 | SetDlgItemText(hwnd, IDC_MEMO_STACK, pData); 70 | HeapFree(GetProcessHeap(), 0, pData); 71 | } 72 | } 73 | break; 74 | } 75 | return(FALSE); 76 | case WM_INITDIALOG: 77 | GUIInit(hwnd); 78 | return(FALSE); 79 | case WM_CLOSE: 80 | DestroyWindow(hwnd); 81 | PostQuitMessage(0); 82 | return(FALSE); 83 | case WM_SIZING: 84 | rWindow = *((RECT*)lParam); 85 | if (rWindow.right - rWindow.left <= MIN_WIDTH) 86 | ((RECT*)lParam)->right = rWindow.left + MIN_WIDTH; 87 | 88 | if (rWindow.bottom - rWindow.top <= MIN_HEIGHT) 89 | ((RECT*)lParam)->bottom = rWindow.top + MIN_HEIGHT; 90 | return(FALSE); 91 | 92 | case WM_SYSCOMMAND: 93 | if (wParam != SC_MAXIMIZE) 94 | { 95 | break; 96 | } 97 | case WM_SIZE: 98 | nWidth = (INT)LOWORD(lParam); 99 | nHeight = (INT)HIWORD(lParam); 100 | GetClientRect(hwnd, &rWindow); 101 | if (!bGlobalSizes) 102 | { 103 | bGlobalSizes = TRUE; 104 | RtlSecureZeroMemory(&rList, sizeof(rList)); 105 | GetWindowRect(GetDlgItem(hwnd, IDC_LIST_MAIN), &rList); 106 | RtlSecureZeroMemory(&rBox, sizeof(rBox)); 107 | GetWindowRect(GetDlgItem(hwnd, IDC_STATIC_BOX), &rBox); 108 | RtlSecureZeroMemory(&rMemo, sizeof(rMemo)); 109 | GetWindowRect(GetDlgItem(hwnd, IDC_MEMO_STACK), &rMemo); 110 | dx1 = rWindow.right - (rList.right - rList.left); 111 | dx2 = rList.bottom - rList.top; 112 | dx3 = rWindow.right - (rBox.right - rBox.left); 113 | dx4 = rBox.bottom - rBox.top; 114 | dx5 = rMemo.right - rMemo.left; 115 | } 116 | hList = GetDlgItem(hwnd, IDC_LIST_MAIN); 117 | if (hList) 118 | { 119 | SetWindowPos(hList, 0, 0, 0, rWindow.right - dx1, nHeight - 99, SWP_NOMOVE | SWP_NOZORDER); 120 | } 121 | hBox = GetDlgItem(hwnd, IDC_STATIC_BOX); 122 | if (hBox) 123 | { 124 | SetWindowPos(hBox, 0, 0, 0, rWindow.right - dx3, dx4, SWP_NOMOVE | SWP_NOZORDER); 125 | } 126 | hMemo = GetDlgItem(hwnd, IDC_MEMO_STACK); 127 | if (hMemo) 128 | { 129 | SetWindowPos(hMemo, 0, 0, 0, dx5, nHeight - 115, SWP_NOMOVE | SWP_NOZORDER); 130 | } 131 | return(FALSE); 132 | case WM_COMMAND: 133 | return (CommandHandler(hwnd, wParam, lParam)); 134 | } 135 | return FALSE; 136 | } 137 | 138 | void ShowDialog(HWND Parent) { 139 | WNDCLASSA wClass; 140 | HWND hDialog; 141 | MSG msg; 142 | 143 | RtlSecureZeroMemory(&wClass, sizeof(wClass)); 144 | RtlSecureZeroMemory(&msg, sizeof(msg)); 145 | wClass.lpszClassName = "CallMon_MainForm"; 146 | RegisterClassA(&wClass); 147 | hDialog = CreateDialogA(GetModuleHandleA(NULL), MAKEINTRESOURCEA(IDD_DIALOG1), Parent, &EventHandler); 148 | ShowWindow(hDialog, SWP_NOSIZE | SW_SHOWNORMAL); 149 | while (GetMessageA(&msg, NULL, 0, 0)) 150 | { 151 | TranslateMessage(&msg); 152 | DispatchMessageA(&msg); 153 | } 154 | return; 155 | } 156 | 157 | DWORD ListenProc(LPVOID lpvParam) 158 | { 159 | PTOTAL_PACKET pTotalPacket; 160 | PSTACK_CHUNK pStackChunk; 161 | LPVOID lpBuff; 162 | DWORD dwBytesRead; 163 | DWORD dwCount; 164 | HWND hwMainWindow; 165 | HWND hwList; 166 | 167 | hwMainWindow = FindWindowA("CallMon_MainForm", NULL); 168 | lpBuff = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TOTAL_PACKET)); 169 | dwCount = 0; 170 | ConnectNamedPipe((HANDLE)lpvParam, NULL); 171 | while (TRUE) 172 | { 173 | ReadFile((HANDLE)lpvParam, lpBuff, sizeof(TOTAL_PACKET), &dwBytesRead, NULL); 174 | if (hGlobalHWND) 175 | { 176 | pTotalPacket = (PTOTAL_PACKET)lpBuff; 177 | 178 | hwList = GetDlgItem(hGlobalHWND, IDC_LIST_MAIN); 179 | LV_ITEM lvItem = { 0 }; 180 | 181 | lvItem.iItem = dwCount; 182 | ListView_InsertItem(hwList, &lvItem); 183 | 184 | PVOID pData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x100); 185 | wnsprintfW(pData, 0x100, L"%d", pTotalPacket->CustomHeader.ProcessId); 186 | ListView_SetItemText(hwList, dwCount, 0, pData); 187 | HeapFree(GetProcessHeap(), 0, pData); 188 | 189 | pData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x100); 190 | wnsprintfW(pData, 0x100, L"RAX: %I64X", pTotalPacket->Frame.Rax); 191 | ListView_SetItemText(hwList, dwCount, 1, pData); 192 | HeapFree(GetProcessHeap(), 0, pData); 193 | 194 | pData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x100); 195 | wnsprintfW(pData, 0x100, L"RCX: %I64X", pTotalPacket->Frame.Rcx); 196 | ListView_SetItemText(hwList, dwCount, 2, pData); 197 | HeapFree(GetProcessHeap(), 0, pData); 198 | 199 | pData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x100); 200 | wnsprintfW(pData, 0x100, L"RDX: %I64X", pTotalPacket->Frame.Rdx); 201 | ListView_SetItemText(hwList, dwCount, 3, pData); 202 | HeapFree(GetProcessHeap(), 0, pData); 203 | 204 | pData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x100); 205 | wnsprintfW(pData, 0x100, L"R8: %I64X", pTotalPacket->Frame.R8); 206 | ListView_SetItemText(hwList, dwCount, 4, pData); 207 | HeapFree(GetProcessHeap(), 0, pData); 208 | 209 | pData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x100); 210 | wnsprintfW(pData, 0x100, L"R9: %I64X", pTotalPacket->Frame.R9); 211 | ListView_SetItemText(hwList, dwCount, 5, pData); 212 | HeapFree(GetProcessHeap(), 0, pData); 213 | 214 | pStackChunk = (PSTACK_CHUNK)((BYTE*)pGlobalStackMem + (dwCount * sizeof(STACK_CHUNK))); 215 | pStackChunk->Row = dwCount; 216 | memcpy(pStackChunk->StackData, pTotalPacket->CustomHeader.StackData, sizeof(pTotalPacket->CustomHeader.StackData)); 217 | dwCount++; 218 | } 219 | } 220 | } 221 | 222 | BOOL CommandHandler(HWND hwnd, WPARAM wParam, LPARAM lParam) { 223 | IsNTAdmin pIsNTAdmin; 224 | HANDLE hDriver; 225 | WCHAR wzName[MAX_PATH]; 226 | BOOL bTranslated; 227 | INT nPID; 228 | 229 | switch (LOWORD(wParam)) 230 | { 231 | case IDC_BTN_INIT: 232 | hGlobalHWND = hwnd; 233 | pIsNTAdmin = *(IsNTAdmin)GetProcAddress(LoadLibraryA("advpack.dll"), "IsNTAdmin"); 234 | if (pIsNTAdmin) 235 | { 236 | if (!pIsNTAdmin(0, NULL)) 237 | { 238 | GetModuleFileNameW(0, (LPWSTR)&wzName, MAX_PATH * 2); 239 | if (MessageBoxA(hwnd, "You must be running as an administrator. Restart now?", "Warning", MB_ICONWARNING | MB_YESNO) == IDYES) 240 | { 241 | ShellExecute(hwnd, L"runas", wzName, NULL, NULL, SW_SHOW); 242 | ExitProcess(0); 243 | } 244 | else 245 | { 246 | return(FALSE); 247 | } 248 | } 249 | else 250 | { 251 | hDriver = CreateFileA("AltCall.sys", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); 252 | if (hDriver != INVALID_HANDLE_VALUE) 253 | { 254 | if ((!GetDriverPrivilege()) || (!LoadDriver(hDriver))) 255 | { 256 | MessageBoxA(hwnd, "Unable to load driver! Ensure it is in the same directory as " 257 | "this program and DSE is disabled.", "Error", MB_ICONERROR); 258 | CloseHandle(hDriver); 259 | return(FALSE); 260 | 261 | } 262 | CloseHandle(hDriver); 263 | } 264 | if (!pGlobalStackMem) 265 | { 266 | pGlobalStackMem = VirtualAlloc(0, GLOBAL_STACK_MEM_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 267 | if (!pGlobalStackMem) 268 | { 269 | MessageBoxA(hwnd, "Unable to allocate memory for stack capture!", "Fatal Error", MB_ICONERROR); 270 | ExitProcess(-1); 271 | } 272 | } 273 | ObtainDevice(); 274 | CreatePipe(); 275 | if ((hGlobalPipe != INVALID_HANDLE_VALUE) && (hGlobalDriver != INVALID_HANDLE_VALUE)) 276 | { 277 | hGlobalListenThread = CreateThread(NULL, 0, &ListenProc, hGlobalPipe, 0, NULL); 278 | DeviceIoControl(hGlobalDriver, IOCTL_INIT, NULL, 0, NULL, 0, NULL, NULL); 279 | } 280 | } 281 | } 282 | return(FALSE); 283 | case IDC_BTN_ADD: 284 | nPID = GetDlgItemInt(hwnd, IDC_EDIT_ADD, &bTranslated, FALSE); 285 | if (bTranslated) 286 | { 287 | if (hGlobalDriver != INVALID_HANDLE_VALUE) 288 | { 289 | DeviceIoControl(hGlobalDriver, IOCTL_ADD_PROCESS, &nPID, sizeof(nPID), &nPID, sizeof(nPID), NULL, NULL); 290 | return(FALSE); 291 | } 292 | } 293 | else 294 | { 295 | MessageBoxA(hwnd, "Value must be a valid number!", "Error", MB_ICONERROR); 296 | } 297 | return(FALSE); 298 | case IDC_BTN_REMOVE: 299 | nPID = GetDlgItemInt(hwnd, IDC_EDIT_ADD, &bTranslated, FALSE); 300 | if (bTranslated) 301 | { 302 | if (hGlobalDriver != INVALID_HANDLE_VALUE) 303 | { 304 | DeviceIoControl(hGlobalDriver, IOCTL_REMOVE_PROCESS, &nPID, sizeof(nPID), &nPID, sizeof(nPID), NULL, NULL); 305 | return(FALSE); 306 | } 307 | } 308 | else 309 | { 310 | MessageBoxA(hwnd, "Value must be a valid number!", "Error", MB_ICONERROR); 311 | } 312 | return(FALSE); 313 | case IDC_BUTTON_CLEAR: 314 | if (MessageBoxA(hwnd, "Are you sure you want to clear all entries?", "Confirmation", MB_ICONWARNING | MB_YESNO) == IDYES) 315 | { 316 | if (pGlobalStackMem) 317 | { 318 | if (hGlobalListenThread != INVALID_HANDLE_VALUE) 319 | { 320 | TerminateThread(hGlobalListenThread, 0); 321 | ListView_DeleteAllItems(GetDlgItem(hwnd, IDC_LIST_MAIN)); 322 | RtlSecureZeroMemory(pGlobalStackMem, GLOBAL_STACK_MEM_SIZE); 323 | hGlobalListenThread = CreateThread(NULL, 0, &ListenProc, hGlobalPipe, 0, NULL); 324 | } 325 | } 326 | } 327 | return(FALSE); 328 | default: 329 | return(FALSE); 330 | } 331 | } 332 | 333 | INT CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 334 | { 335 | ShowDialog(0); 336 | ExitProcess(0); 337 | return(0); 338 | } 339 | -------------------------------------------------------------------------------- /GUI/Resource.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | ///////////////////////////////////////////////////////////////////////////// 12 | #undef APSTUDIO_READONLY_SYMBOLS 13 | 14 | ///////////////////////////////////////////////////////////////////////////// 15 | // English (United States) resources 16 | 17 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 18 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 19 | #pragma code_page(1252) 20 | 21 | #ifdef APSTUDIO_INVOKED 22 | ///////////////////////////////////////////////////////////////////////////// 23 | // 24 | // TEXTINCLUDE 25 | // 26 | 27 | 1 TEXTINCLUDE 28 | BEGIN 29 | "resource.h\0" 30 | END 31 | 32 | 2 TEXTINCLUDE 33 | BEGIN 34 | "#include ""winres.h""\0" 35 | END 36 | 37 | 3 TEXTINCLUDE 38 | BEGIN 39 | "\r\n" 40 | "\0" 41 | END 42 | 43 | #endif // APSTUDIO_INVOKED 44 | 45 | 46 | ///////////////////////////////////////////////////////////////////////////// 47 | // 48 | // Dialog 49 | // 50 | 51 | IDD_DIALOG1 DIALOGEX 0, 0, 450, 200 52 | STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME 53 | CAPTION "CallMon" 54 | FONT 8, "MS Shell Dlg", 400, 0, 0x1 55 | BEGIN 56 | GROUPBOX "Configuration",IDC_STATIC_BOX,7,7,436,45 57 | PUSHBUTTON "Initialize",IDC_BTN_INIT,15,26,50,14 58 | EDITTEXT IDC_EDIT_ADD,122,26,40,14,ES_AUTOHSCROLL 59 | CONTROL "",IDC_LIST_MAIN,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,117,54,326,139 60 | PUSHBUTTON "Add Process",IDC_BTN_ADD,69,26,50,14 61 | PUSHBUTTON "Remove Process",IDC_BTN_REMOVE,165,26,76,14 62 | EDITTEXT IDC_MEMO_STACK,7,64,106,129,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY 63 | LTEXT "Stack Capture (First 0x80):",IDC_STATIC_LABEL,7,54,90,8 64 | PUSHBUTTON "Clear",IDC_BUTTON_CLEAR,243,26,50,14 65 | END 66 | 67 | 68 | ///////////////////////////////////////////////////////////////////////////// 69 | // 70 | // DESIGNINFO 71 | // 72 | 73 | #ifdef APSTUDIO_INVOKED 74 | GUIDELINES DESIGNINFO 75 | BEGIN 76 | IDD_DIALOG1, DIALOG 77 | BEGIN 78 | LEFTMARGIN, 7 79 | RIGHTMARGIN, 443 80 | TOPMARGIN, 7 81 | BOTTOMMARGIN, 193 82 | END 83 | END 84 | #endif // APSTUDIO_INVOKED 85 | 86 | 87 | ///////////////////////////////////////////////////////////////////////////// 88 | // 89 | // AFX_DIALOG_LAYOUT 90 | // 91 | 92 | IDD_DIALOG1 AFX_DIALOG_LAYOUT 93 | BEGIN 94 | 0, 95 | 0, 0, 0, 0, 96 | 0, 0, 0, 0, 97 | 0, 0, 0, 0, 98 | 100, 100, 100, 100, 99 | 0, 0, 0, 0, 100 | 0, 0, 0, 0, 101 | 0, 0, 0, 0, 102 | 0, 0, 0, 0, 103 | 0, 0, 0, 0 104 | END 105 | 106 | #endif // English (United States) resources 107 | ///////////////////////////////////////////////////////////////////////////// 108 | 109 | 110 | 111 | #ifndef APSTUDIO_INVOKED 112 | ///////////////////////////////////////////////////////////////////////////// 113 | // 114 | // Generated from the TEXTINCLUDE 3 resource. 115 | // 116 | 117 | 118 | ///////////////////////////////////////////////////////////////////////////// 119 | #endif // not APSTUDIO_INVOKED 120 | -------------------------------------------------------------------------------- /GUI/Utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #pragma comment(lib, "ntdll.lib") 4 | 5 | #define GLOBAL_STACK_MEM_SIZE 0x400000 6 | #define IOCTL_ADD_PROCESS 0x550000 7 | #define IOCTL_REMOVE_PROCESS 0x550002 8 | #define IOCTL_INIT 0x550004 9 | #define MIN_HEIGHT 200 10 | #define MIN_WIDTH 475 11 | 12 | // Globals 13 | HANDLE hGlobalDriver = INVALID_HANDLE_VALUE; 14 | HANDLE hGlobalPipe = INVALID_HANDLE_VALUE; 15 | HANDLE hGlobalListenThread = INVALID_HANDLE_VALUE; 16 | PVOID pGlobalStackMem = NULL; 17 | HWND hGlobalHWND = 0; 18 | BOOL bGlobalSizes = FALSE; 19 | LONG dx1, dx2, dx3, dx4, dx5; 20 | 21 | typedef BOOL(WINAPI* IsNTAdmin)(DWORD Reserved, LPVOID pReserved); 22 | typedef NTSTATUS(NTAPI* NtLoadDriver)(PUNICODE_STRING DriverServiceName); 23 | 24 | typedef struct _STACK_CHUNK 25 | { 26 | ULONG64 Row; 27 | ULONG64 StackData[0x10]; 28 | } STACK_CHUNK, *PSTACK_CHUNK; 29 | 30 | typedef struct _CUSTOM_HEADER 31 | { 32 | ULONG64 ProcessId; 33 | ULONG64 StackData[0x10]; 34 | } CUSTOM_HEADER, *PCUSTOM_HEADER; 35 | 36 | typedef CCHAR KPROCESSOR_MODE; 37 | typedef UCHAR KIRQL; 38 | 39 | typedef struct _KTRAP_FRAME { 40 | 41 | // 42 | // Home address for the parameter registers. 43 | // 44 | 45 | ULONG64 P1Home; 46 | ULONG64 P2Home; 47 | ULONG64 P3Home; 48 | ULONG64 P4Home; 49 | ULONG64 P5; 50 | 51 | // 52 | // Previous processor mode (system services only) and previous IRQL 53 | // (interrupts only). 54 | // 55 | 56 | KPROCESSOR_MODE PreviousMode; 57 | 58 | KIRQL PreviousIrql; 59 | 60 | // 61 | // Page fault load/store indicator. 62 | // 63 | 64 | UCHAR FaultIndicator; 65 | 66 | // 67 | // Exception active indicator. 68 | // 69 | // 0 - interrupt frame. 70 | // 1 - exception frame. 71 | // 2 - service frame. 72 | // 73 | 74 | UCHAR ExceptionActive; 75 | 76 | // 77 | // Floating point state. 78 | // 79 | 80 | ULONG MxCsr; 81 | 82 | // 83 | // Volatile registers. 84 | // 85 | // N.B. These registers are only saved on exceptions and interrupts. They 86 | // are not saved for system calls. 87 | // 88 | 89 | ULONG64 Rax; 90 | ULONG64 Rcx; 91 | ULONG64 Rdx; 92 | ULONG64 R8; 93 | ULONG64 R9; 94 | ULONG64 R10; 95 | ULONG64 R11; 96 | 97 | // 98 | // Gsbase is only used if the previous mode was kernel. 99 | // 100 | // GsSwap is only used if the previous mode was user. 101 | // 102 | 103 | union { 104 | ULONG64 GsBase; 105 | ULONG64 GsSwap; 106 | }; 107 | 108 | // 109 | // Volatile floating registers. 110 | // 111 | // N.B. These registers are only saved on exceptions and interrupts. They 112 | // are not saved for system calls. 113 | // 114 | 115 | M128A Xmm0; 116 | M128A Xmm1; 117 | M128A Xmm2; 118 | M128A Xmm3; 119 | M128A Xmm4; 120 | M128A Xmm5; 121 | 122 | // 123 | // First parameter, page fault address, context record address if user APC 124 | // bypass. 125 | // 126 | 127 | union { 128 | ULONG64 FaultAddress; 129 | ULONG64 ContextRecord; 130 | }; 131 | 132 | // 133 | // Debug registers. 134 | // 135 | 136 | ULONG64 Dr0; 137 | ULONG64 Dr1; 138 | ULONG64 Dr2; 139 | ULONG64 Dr3; 140 | ULONG64 Dr6; 141 | ULONG64 Dr7; 142 | 143 | // 144 | // Special debug registers. 145 | // 146 | 147 | struct { 148 | ULONG64 DebugControl; 149 | ULONG64 LastBranchToRip; 150 | ULONG64 LastBranchFromRip; 151 | ULONG64 LastExceptionToRip; 152 | ULONG64 LastExceptionFromRip; 153 | }; 154 | 155 | // 156 | // Segment registers 157 | // 158 | 159 | USHORT SegDs; 160 | USHORT SegEs; 161 | USHORT SegFs; 162 | USHORT SegGs; 163 | 164 | // 165 | // Previous trap frame address. 166 | // 167 | 168 | ULONG64 TrapFrame; 169 | 170 | // 171 | // Saved nonvolatile registers RBX, RDI and RSI. These registers are only 172 | // saved in system service trap frames. 173 | // 174 | 175 | ULONG64 Rbx; 176 | ULONG64 Rdi; 177 | ULONG64 Rsi; 178 | 179 | // 180 | // Saved nonvolatile register RBP. This register is used as a frame 181 | // pointer during trap processing and is saved in all trap frames. 182 | // 183 | 184 | ULONG64 Rbp; 185 | 186 | // 187 | // Information pushed by hardware. 188 | // 189 | // N.B. The error code is not always pushed by hardware. For those cases 190 | // where it is not pushed by hardware a dummy error code is allocated 191 | // on the stack. 192 | // 193 | 194 | union { 195 | ULONG64 ErrorCode; 196 | ULONG64 ExceptionFrame; 197 | }; 198 | 199 | ULONG64 Rip; 200 | USHORT SegCs; 201 | UCHAR Fill0; 202 | UCHAR Logging; 203 | USHORT Fill1[2]; 204 | ULONG EFlags; 205 | ULONG Fill2; 206 | ULONG64 Rsp; 207 | USHORT SegSs; 208 | USHORT Fill3; 209 | ULONG Fill4; 210 | } KTRAP_FRAME, * PKTRAP_FRAME; 211 | 212 | typedef struct _TOTAL_PACKET 213 | { 214 | CUSTOM_HEADER CustomHeader; 215 | KTRAP_FRAME Frame; 216 | } TOTAL_PACKET, * PTOTAL_PACKET; 217 | 218 | 219 | /* 220 | Taken from https://github.com/hfiref0x/WinObjEx64 GUI's code 221 | */ 222 | INT CListView_InsertColumn(HWND ListViewHWND, INT ColumnIndex, INT SubItemIndex, INT OrderIndex, LPWSTR Text, INT Width) 223 | { 224 | LVCOLUMN lvColumn; 225 | 226 | lvColumn.mask = LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH | LVCF_ORDER; 227 | lvColumn.cx = (Width); 228 | lvColumn.pszText = Text; 229 | lvColumn.iSubItem = SubItemIndex; 230 | lvColumn.iOrder = OrderIndex; 231 | 232 | return(ListView_InsertColumn(ListViewHWND, ColumnIndex, &lvColumn)); 233 | } 234 | 235 | BOOL ObtainDevice() 236 | { 237 | hGlobalDriver = CreateFileA("\\\\.\\CallMon", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); 238 | if (hGlobalDriver != INVALID_HANDLE_VALUE) 239 | { 240 | return(TRUE); 241 | } 242 | return(FALSE); 243 | } 244 | 245 | BOOL CreatePipe() 246 | { 247 | hGlobalPipe = CreateNamedPipeA("\\\\.\\pipe\\CallMonPipe", PIPE_ACCESS_INBOUND, PIPE_READMODE_BYTE | PIPE_WAIT, 1, 0x1000, 0x1000, INFINITE, NULL); 248 | if (hGlobalPipe != INVALID_HANDLE_VALUE) 249 | { 250 | return(TRUE); 251 | } 252 | return(FALSE); 253 | } 254 | 255 | BOOL AddProcess(WORD ProcessId) 256 | { 257 | if (hGlobalDriver != INVALID_HANDLE_VALUE) 258 | { 259 | if (ProcessId) 260 | { 261 | if (DeviceIoControl(hGlobalDriver, IOCTL_ADD_PROCESS, &ProcessId, sizeof(ProcessId), NULL, 0, NULL, NULL)) 262 | { 263 | return(TRUE); 264 | } 265 | } 266 | } 267 | return(FALSE); 268 | } 269 | 270 | BOOL GetDriverPrivilege() 271 | { 272 | TOKEN_PRIVILEGES tokenPrivs; 273 | HANDLE hToken; 274 | LUID luid; 275 | 276 | if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) 277 | { 278 | if (LookupPrivilegeValueA(NULL, "SeLoadDriverPrivilege", &luid)) 279 | { 280 | tokenPrivs.PrivilegeCount = 1; 281 | tokenPrivs.Privileges[0].Luid = luid; 282 | tokenPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 283 | if (AdjustTokenPrivileges(hToken, FALSE, &tokenPrivs, sizeof(tokenPrivs), NULL, NULL)) 284 | { 285 | CloseHandle(hToken); 286 | return(TRUE); 287 | } 288 | } 289 | } 290 | return(FALSE); 291 | } 292 | 293 | BOOL LoadDriver(HANDLE hDriver) 294 | { 295 | UNICODE_STRING usDriver; 296 | NtLoadDriver pNtLoadDriver; 297 | NTSTATUS ntRet; 298 | DWORD dwData; 299 | WCHAR wzDriver[MAX_PATH]; 300 | HKEY hKey; 301 | 302 | GetFinalPathNameByHandleW(hDriver, (LPWSTR)&wzDriver, MAX_PATH * sizeof(WCHAR), FILE_NAME_NORMALIZED | VOLUME_NAME_NT); 303 | RegCreateKeyA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\AltCall.sys", &hKey); 304 | RegSetValueExW(hKey, L"ImagePath", 0, REG_SZ, (LPWSTR)&wzDriver, lstrlenW((LPCWSTR)&wzDriver) * sizeof(WCHAR)); 305 | dwData = 0x1; 306 | RegSetValueExA(hKey, "Type", 0, REG_DWORD, (BYTE*)&dwData, sizeof(dwData)); 307 | RtlInitUnicodeString(&usDriver, L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\AltCall.sys"); 308 | if (pNtLoadDriver = (NtLoadDriver)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtLoadDriver")) 309 | { 310 | ntRet = pNtLoadDriver(&usDriver); 311 | if ((!ntRet) || (ntRet == 0xC000010E)) // STATUS_IMAGE_ALREADY_LOADED 312 | { 313 | return(TRUE); 314 | } 315 | else if (ntRet == 0xC0000428) 316 | { 317 | MessageBoxA(0, "You must disable driver signature enforcement (DSE) before using this program!\n" 318 | "This is possible by running windows in test mode.", "Error", MB_ICONERROR); 319 | } 320 | } 321 | return(FALSE); 322 | } 323 | -------------------------------------------------------------------------------- /GUI/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Resource.rc 4 | // 5 | #define IDD_DIALOG1 101 6 | #define IDC_BTN_INIT 1003 7 | #define IDC_EDIT_ADD 1004 8 | #define IDC_LIST_MAIN 1006 9 | #define IDC_BTN_ADD 1008 10 | #define IDC_BTN_REMOVE 1009 11 | #define IDC_STATIC_BOX 1010 12 | #define IDC_MEMO_STACK 1011 13 | #define IDC_STATIC_LABEL 1012 14 | #define IDC_BUTTON1 1013 15 | #define IDC_BUTTON_CLEAR 1013 16 | 17 | // Next default values for new objects 18 | // 19 | #ifdef APSTUDIO_INVOKED 20 | #ifndef APSTUDIO_READONLY_SYMBOLS 21 | #define _APS_NEXT_RESOURCE_VALUE 105 22 | #define _APS_NEXT_COMMAND_VALUE 40001 23 | #define _APS_NEXT_CONTROL_VALUE 1014 24 | #define _APS_NEXT_SYMED_VALUE 101 25 | #endif 26 | #endif 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CallMon 2 | CallMon is a system call monitoring tool that works on Windows 10 versions 2004+ using PsAltSystemCallHandlers. 3 | 4 | ## Usage 5 | * CallMon requires driver signature enforcement (DSE) to be disabled. 6 | * Download release [here](https://github.com/DownWithUp/CallMon/releases/tag/v1.0.0) (or download and build from source) 7 | * Ensure both CallMon.exe and AltCall.sys are in the same directory 8 | * Run CallMon.exe as an administrator 9 | * Click on "Initialize" 10 | * Enter a process's ID in the text field and click "Add Process" 11 | 12 | ## Architecture 13 | CallMon is comprised of a kernel driver (AltCall.sys) and a GUI application (CallMon.exe). Together, these programs work to provide API introspection for monitored processes. 14 | The driver and GUI application communicate via a named pipe (\\\\.\pipe\CallMonPipe). The data passed by the driver to usermode consists of a custom header which contains the process id and stack information along with a KTRAP_FRAME structure received from the alt syscall handler function. 15 | 16 | ## Performance Impacts 17 | Because the system call handler function is called everytime a targeted process preforms a call (and in the context of the targeted process), heavy API usage programs will experience a drop in performance due to the transfer of data back to the CallMon GUI process. 18 | 19 | ## Resources 20 | [0xcpu's Research on AltSyscallHandlers](https://github.com/0xcpu/WinAltSyscallHandler) 21 | 22 | # Rust Driver Version 23 | Optionally, there is a version of the AltCall.sys driver written in Rust. The sources and binary are included only in the repository and not in the release. I highly recommended reading not-matthias' (his code was the foundation for the Rust version) [blog post](https://not-matthias.github.io/kernel-driver-with-rust/) on building Windows drivers in Rust. In addition, I will mention that I worked on this to better my Rust skills and not to make a memory safe driver. I heavily used "unsafe" Rust code, and kernel interactions in themselves can always go awire.
24 | ### Build 25 | If you are not already on the nightly channel, change to it using:
26 | rustup toolchain install nightly
27 | Override using:
28 | rustup override set nightly
29 | ### C VS. Rust 30 | Besides, the obvious syntax differences, I also made some design changes:
31 | * Rust version uses ProbeForRead instead of MmHighestUserAddress and MmIsAddressValid check for stack pointer. 32 | * Rust version ~~has no remove process IOCTL handling function (possibly coming soon?)~~ now has support for removing processes! 33 | -------------------------------------------------------------------------------- /Rust/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-pc-windows-msvc" 3 | 4 | rustflags = [ 5 | "-C", "panic=abort", 6 | 7 | # Pre Link Args 8 | "-Z", "pre-link-arg=/NOLOGO", 9 | "-Z", "pre-link-arg=/NXCOMPAT", 10 | "-Z", "pre-link-arg=/NODEFAULTLIB", 11 | "-Z", "pre-link-arg=/SUBSYSTEM:NATIVE", 12 | "-Z", "pre-link-arg=/DRIVER", 13 | "-Z", "pre-link-arg=/DYNAMICBASE", 14 | "-Z", "pre-link-arg=/MANIFEST:NO", 15 | "-Z", "pre-link-arg=/PDBALTPATH:none", 16 | 17 | # Post Link Args 18 | "-C", "link-arg=/OPT:REF,ICF", 19 | "-C", "link-arg=/ENTRY:DriverEntry", 20 | "-C", "link-arg=/MERGE:.edata=.rdata", 21 | "-C", "link-arg=/MERGE:.rustc=.data", 22 | "-C", "link-arg=/INTEGRITYCHECK" 23 | ] 24 | -------------------------------------------------------------------------------- /Rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "AltCall" 3 | version = "0.1.0" 4 | edition = "2018" 5 | build = "build.rs" 6 | 7 | [lib] 8 | path = "src/lib.rs" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | kernel-print = "0.1.0" 13 | kernel-alloc = "0.1.0" 14 | obfstr = "0.1.1" 15 | 16 | [dependencies.winapi] 17 | git = "https://github.com/Trantect/winapi-rs.git" 18 | branch = "feature/km" 19 | features = [ 20 | "wdm", 21 | "ntstatus", 22 | ] 23 | 24 | [build-dependencies] 25 | winreg = "0.7.0" 26 | failure = "0.1.8" 27 | -------------------------------------------------------------------------------- /Rust/Makefile.toml: -------------------------------------------------------------------------------- 1 | [env.development] 2 | TARGET_PATH = "target/x86_64-pc-windows-msvc/debug" 3 | 4 | [env.production] 5 | TARGET_PATH = "target/x86_64-pc-windows-msvc/release" 6 | BUILD_FLAGS = "--release" 7 | 8 | [tasks.build-driver] 9 | script = [ 10 | "cargo b %BUILD_FLAGS%" 11 | ] 12 | 13 | [tasks.rename] 14 | dependencies = ["build-driver"] 15 | ignore_errors = true 16 | script = [ 17 | "cd %TARGET_PATH%", 18 | "rename driver.dll driver.sys", 19 | ] 20 | 21 | [tasks.sign] 22 | dependencies = ["build-driver", "rename"] 23 | script = [ 24 | # Load the Visual Studio Developer environment 25 | "call \"%ProgramFiles(x86)%\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat\"", 26 | 27 | # Create a self signed certificate (only if not already done) 28 | "if not exist DriverCertificate.cer ( makecert -r -pe -ss PrivateCertStore -n CN=DriverCertificate DriverCertificate.cer ) else ( echo Certificate already exists. )", 29 | 30 | # Sign the driver 31 | "signtool sign /a /v /s PrivateCertStore /n DriverCertificate /t http://timestamp.digicert.com %TARGET_PATH%/driver.sys" 32 | ] 33 | -------------------------------------------------------------------------------- /Rust/build.rs: -------------------------------------------------------------------------------- 1 | use failure::{format_err, Error}; 2 | use std::{ 3 | env::var, 4 | path::{Path, PathBuf}, 5 | }; 6 | use winreg::{enums::*, RegKey}; 7 | 8 | /// Returns the path to the `Windows Kits` directory. It's by default at 9 | /// `C:\Program Files (x86)\Windows Kits\10`. 10 | fn get_windows_kits_dir() -> Result { 11 | let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); 12 | let key = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots"; 13 | let dir: String = hklm.open_subkey(key)?.get_value("KitsRoot10")?; 14 | 15 | Ok(dir.into()) 16 | } 17 | 18 | /// Returns the path to the kernel mode libraries. The path may look like this: 19 | /// `C:\Program Files (x86)\Windows Kits\10\lib\10.0.18362.0\km`. 20 | fn get_km_dir(windows_kits_dir: &PathBuf) -> Result { 21 | let readdir = Path::new(windows_kits_dir).join("lib").read_dir()?; 22 | 23 | let max_libdir = readdir 24 | .filter_map(|dir| dir.ok()) 25 | .map(|dir| dir.path()) 26 | .filter(|dir| { 27 | dir.components() 28 | .last() 29 | .and_then(|c| c.as_os_str().to_str()) 30 | .map(|c| c.starts_with("10.") && dir.join("km").is_dir()) 31 | .unwrap_or(false) 32 | }) 33 | .max() 34 | .ok_or_else(|| format_err!("Can not find a valid km dir in `{:?}`", windows_kits_dir))?; 35 | 36 | Ok(max_libdir.join("km")) 37 | } 38 | 39 | fn internal_link_search() { 40 | let windows_kits_dir = get_windows_kits_dir().unwrap(); 41 | let km_dir = get_km_dir(&windows_kits_dir).unwrap(); 42 | let target = var("TARGET").unwrap(); 43 | 44 | let arch = if target.contains("x86_64") { 45 | "x64" 46 | } else if target.contains("i686") { 47 | "x86" 48 | } else { 49 | panic!("Only support x86_64 and i686!"); 50 | }; 51 | 52 | let lib_dir = km_dir.join(arch); 53 | println!("cargo:rustc-link-search=native={}", lib_dir.to_str().unwrap()); 54 | } 55 | 56 | fn extra_link_search() {} 57 | 58 | fn main() { 59 | if var(format!("CARGO_FEATURE_{}", "extra_link_search".to_uppercase())).is_ok() { 60 | extra_link_search() 61 | } else { 62 | internal_link_search() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Rust/rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2018" 2 | 3 | brace_style = "PreferSameLine" 4 | color = "Always" 5 | fn_args_layout = "Compressed" 6 | fn_single_line = true 7 | format_code_in_doc_comments = true 8 | format_macro_matchers = true 9 | format_macro_bodies = true 10 | format_strings = true 11 | inline_attribute_width = 80 12 | max_width = 120 13 | merge_imports = true 14 | normalize_doc_attributes = true 15 | reorder_impl_items = true 16 | reorder_imports = true 17 | reorder_modules = true 18 | use_field_init_shorthand = true 19 | use_try_shorthand = true 20 | where_single_line = true 21 | wrap_comments = true 22 | -------------------------------------------------------------------------------- /Rust/src/defines.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Some defines and structs that are custom or was unable to find 3 | in the winapi crate. 4 | */ 5 | #![allow(non_snake_case)] 6 | 7 | use winapi::shared::ntdef::{ULONG, USHORT, UCHAR, KIRQL, HANDLE}; 8 | use winapi::um::winnt::M128A; 9 | use winapi::km::wdm::KPROCESSOR_MODE; 10 | use winapi::um::winnt::ACCESS_MASK; 11 | pub type QWORD = u64; 12 | pub const FILE_DEVICE_SECURE_OPEN: ULONG = 0x00000100; 13 | pub const FILE_DELETE_ON_CLOSE: ULONG = 0x00001000; 14 | pub const FILE_SYNCHRONOUS_IO_NONALERT:ULONG = 0x00000020; 15 | pub const FILE_OPEN:ULONG = 0x00000001; 16 | pub const PagedPool: ULONG = 0x00000001; 17 | pub const ProcessAltSystemCallInformation: ULONG = 0x00000064; 18 | 19 | pub const IOCTL_ADD_PROCESS: ULONG = 0x550000; 20 | pub const IOCTL_REMOVE_PROCESS: ULONG = 0x550002; 21 | pub const IOCTL_INIT: ULONG = 0x550004; 22 | 23 | pub struct State 24 | { 25 | pub abc: ULONG, 26 | } 27 | 28 | pub struct _CLIENT_ID 29 | { 30 | pub UniqueProcess: HANDLE, 31 | pub UniqueThread: HANDLE, 32 | } 33 | pub type CLIENT_ID = _CLIENT_ID; 34 | pub type PCLIENT_ID = *mut _CLIENT_ID; 35 | 36 | pub struct _OBJECT_HANDLE_INFORMATION { 37 | pub HandleAttributes: ULONG, 38 | pub GrantedAccess: ACCESS_MASK, 39 | } 40 | pub type OBJECT_HANDLE_INFORMATION = _OBJECT_HANDLE_INFORMATION; 41 | pub type POBJECT_HANDLE_INFORMATION = *mut _OBJECT_HANDLE_INFORMATION; 42 | 43 | pub struct _CUSTOM_HEADER 44 | { 45 | pub ProcessId: QWORD, 46 | pub StackData: [QWORD; 16], 47 | } 48 | pub type CUSTOM_HEADER = _CUSTOM_HEADER; 49 | pub type PCUSTOM_HEADER = *mut _CUSTOM_HEADER; 50 | 51 | 52 | // Unions for KTRAP_FRAME 53 | pub union u1 54 | { 55 | pub GsBase: QWORD, 56 | pub GsSwap: QWORD, 57 | } 58 | 59 | pub union u2 60 | { 61 | pub FaultAddress: QWORD, 62 | pub ContextRecord: QWORD, 63 | } 64 | 65 | pub union u3 66 | { 67 | pub ErrorCode: QWORD, 68 | pub ExceptionFrame: QWORD, 69 | } 70 | 71 | #[repr(C)] 72 | pub struct _KTRAP_FRAME 73 | { 74 | // 75 | // Home address for the parameter registers. 76 | // 77 | pub P1Home: QWORD, 78 | pub P2Home: QWORD, 79 | pub P3Home: QWORD, 80 | pub P4Home: QWORD, 81 | pub P5: QWORD, 82 | 83 | // 84 | // Previous processor mode (system services only) and previous IRQL 85 | // (interrupts only). 86 | // 87 | 88 | pub PreviousMode: KPROCESSOR_MODE, 89 | 90 | pub PreviousIrql: KIRQL, 91 | 92 | // 93 | // Page fault load/store indicator. 94 | // 95 | pub FaultIndicator: UCHAR, 96 | 97 | // 98 | // Exception active indicator. 99 | // 100 | // 0 - interrupt frame. 101 | // 1 - exception frame. 102 | // 2 - service frame. 103 | // 104 | pub ExceptionActive: UCHAR, 105 | 106 | // 107 | // Floating point state. 108 | // 109 | 110 | pub MxCsr: ULONG, 111 | 112 | // 113 | // Volatile registers. 114 | // 115 | // N.B. These registers are only saved on exceptions and interrupts. They 116 | // are not saved for system calls. 117 | // 118 | pub Rax: QWORD, 119 | pub Rcx: QWORD, 120 | pub Rdx: QWORD, 121 | pub R8: QWORD, 122 | pub R9: QWORD, 123 | pub R10: QWORD, 124 | pub R11: QWORD, 125 | 126 | // 127 | // Gsbase is only used if the previous mode was kernel. 128 | // 129 | // GsSwap is only used if the previous mode was user. 130 | // 131 | pub u1: u1, 132 | 133 | // 134 | // Volatile floating registers. 135 | // 136 | // N.B. These registers are only saved on exceptions and interrupts. They 137 | // are not saved for system calls. 138 | // 139 | 140 | pub Xmm0: M128A, 141 | pub Xmm1: M128A, 142 | pub Xmm2: M128A, 143 | pub Xmm3: M128A, 144 | pub Xmm4: M128A, 145 | pub Xmm5: M128A, 146 | 147 | // 148 | // First parameter, page fault address, context record address if user APC 149 | // bypass. 150 | // 151 | 152 | pub u2: u2, 153 | 154 | // 155 | // Debug registers. 156 | // 157 | pub Dr0: QWORD, 158 | pub Dr1: QWORD, 159 | pub Dr2: QWORD, 160 | pub Dr3: QWORD, 161 | pub Dr6: QWORD, 162 | pub Dr7: QWORD, 163 | // 164 | // Special debug registers. 165 | // 166 | 167 | //pub DebugRegs: _DEBUG_REGS, 168 | 169 | pub DebugControl: QWORD, 170 | pub LastBranchToRip: QWORD, 171 | pub LastBranchFromRip: QWORD, 172 | pub LastExceptionToRip: QWORD, 173 | pub LastExceptionFromRip: QWORD, 174 | 175 | // 176 | // Segment registers 177 | // 178 | 179 | pub SegDs: USHORT, 180 | pub SegEs: USHORT, 181 | pub SegFs: USHORT, 182 | pub SegGs: USHORT, 183 | 184 | // 185 | // Previous trap frame address. 186 | // 187 | 188 | pub TrapFrame: QWORD, 189 | 190 | // 191 | // Saved nonvolatile registers RBX, RDI and RSI. These registers are only 192 | // saved in system service trap frames. 193 | // 194 | 195 | pub Rbx: QWORD, 196 | pub Rdi: QWORD, 197 | pub Rsi: QWORD, 198 | 199 | // 200 | // Saved nonvolatile register RBP. This register is used as a frame 201 | // pointer during trap processing and is saved in all trap frames. 202 | // 203 | pub Rbp: QWORD, 204 | 205 | // 206 | // Information pushed by hardware. 207 | // 208 | // N.B. The error code is not always pushed by hardware. For those cases 209 | // where it is not pushed by hardware a dummy error code is allocated 210 | // on the stack. 211 | // 212 | pub u3: u3, 213 | 214 | pub Rip: QWORD, 215 | pub SegCs: USHORT, 216 | pub Fill0: UCHAR, 217 | pub Logging: UCHAR, 218 | pub Fill1: [USHORT;2], 219 | pub EFlags: ULONG, 220 | pub Fill2: ULONG, 221 | pub Rsp: QWORD, 222 | pub SegSs: USHORT, 223 | pub Fill3: USHORT, 224 | pub Fill4: ULONG, 225 | } 226 | pub type KTRAP_FRAME = _KTRAP_FRAME; 227 | pub type PKTRAP_FRAME = *mut _KTRAP_FRAME; 228 | 229 | pub struct _TOTAL_PACKET 230 | { 231 | pub CustomHeader: CUSTOM_HEADER, 232 | /* 233 | The padding is here because for some reason, 234 | the C version size of TOTAL_PACKET is 544 235 | while the rust version is 536. 236 | In both versions the KTRAP_FRAME is 400 237 | and the CUSTOM_HEADER is 136 which should 238 | equal 536, but some optimization seems to occur 239 | which adds padding per struct in a struct. 240 | */ 241 | CPadding: QWORD, 242 | pub Frame: KTRAP_FRAME, 243 | } 244 | pub type TOTAL_PACKET = _TOTAL_PACKET; 245 | pub type PTOTAL_PACKET = *mut _TOTAL_PACKET; 246 | -------------------------------------------------------------------------------- /Rust/src/externs.rs: -------------------------------------------------------------------------------- 1 | use winapi::km::wdm::*; 2 | use winapi::km::wdm::IO_PRIORITY::KPRIORITY_BOOST; 3 | use winapi::um::winnt::ACCESS_MASK; 4 | use winapi::shared::ntdef::*; 5 | use winapi::shared::basetsd::SIZE_T; 6 | //crate::defines 7 | use crate::PCLIENT_ID; 8 | use crate::POBJECT_HANDLE_INFORMATION; 9 | 10 | extern "system" { 11 | 12 | pub fn ProbeForRead(Address: PVOID, Length: SIZE_T, Alignment: ULONG); 13 | 14 | pub fn IoCreateDevice(DriverObject: PDRIVER_OBJECT, DeviceExtension: ULONG, DeviceName: PUNICODE_STRING, 15 | DeviceType: ULONG, DeviceCharacteristics: ULONG, Exclusive: BOOLEAN, DeviceObject: *mut*mut DEVICE_OBJECT) -> NTSTATUS; 16 | 17 | pub fn IoCreateSymbolicLink(SymbolicLinkName: PUNICODE_STRING, DeviceName: PUNICODE_STRING) -> NTSTATUS; 18 | 19 | pub fn MmGetSystemRoutineAddress(SystemRoutineName: PUNICODE_STRING) -> PVOID; 20 | 21 | pub fn IofCompleteRequest(Irp : PIRP, PriorityBoost: KPRIORITY_BOOST); 22 | 23 | pub fn ZwSetInformationProcess(ProcessHandle: HANDLE, ProcessInformationClass: ULONG, ProcessInformation: PVOID, 24 | ProcessInformationLength: ULONG) -> NTSTATUS; 25 | 26 | pub fn PsSuspendProcess(Process: PEPROCESS) -> NTSTATUS; 27 | 28 | pub fn PsResumeProcess(Process: PEPROCESS) -> NTSTATUS; 29 | 30 | pub fn ZwCreateFile(FileHandle: PHANDLE, AccessMask: ACCESS_MASK, ObjectAttributes: POBJECT_ATTRIBUTES, IoStatusBlock: PIO_STATUS_BLOCK, 31 | AllocationSize: PLARGE_INTEGER, FileAttributes: ULONG, ShareAccess: ULONG, CreateDisposition: ULONG, CreateOptions: ULONG, EaBuffer: PVOID, 32 | EaLength: ULONG) -> NTSTATUS; 33 | 34 | pub fn ZwOpenProcess(ProcessHandle: PHANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: POBJECT_ATTRIBUTES, 35 | ClientId: PCLIENT_ID) -> NTSTATUS; 36 | 37 | pub fn ZwWriteFile(FileHandle: HANDLE, Event: HANDLE, ApcRoutine: PVOID, ApcContext: PVOID, IoStatusBlock: PIO_STATUS_BLOCK, 38 | Buffer: PVOID, Length: ULONG, ByteOffset: PLARGE_INTEGER, Key: PULONG) -> NTSTATUS; 39 | 40 | pub fn ZwClose(Handle: HANDLE) -> NTSTATUS; 41 | 42 | pub fn memmove(Destination: PVOID, Source: PVOID, Size: SIZE_T) -> PVOID; // cdecl 43 | 44 | pub fn PsGetProcessId(Process: PEPROCESS) -> HANDLE; 45 | 46 | pub fn ObReferenceObjectByHandle(Handle: HANDLE, DesiredAccess: ACCESS_MASK, ObjectType: PVOID, AccessMode: KPROCESSOR_MODE, 47 | Object: *mut PVOID, HandleInformation: POBJECT_HANDLE_INFORMATION) -> NTSTATUS; 48 | 49 | pub fn PsGetCurrentProcess() -> PEPROCESS; 50 | 51 | pub fn ObDereferenceObject(Object: PVOID) -> NTSTATUS; 52 | } 53 | -------------------------------------------------------------------------------- /Rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(alloc_error_handler)] 3 | #![allow(non_snake_case)] 4 | #![feature(asm)] 5 | 6 | extern crate alloc; 7 | 8 | use crate::{string::create_unicode_string, externs::IofCompleteRequest, externs::MmGetSystemRoutineAddress, 9 | externs::PsGetCurrentProcess, externs::PsGetProcessId, externs::ZwClose, externs::ZwCreateFile, externs::ZwOpenProcess, 10 | externs::ZwSetInformationProcess, externs::ProbeForRead, externs::ZwWriteFile, externs::memmove, 11 | externs::ObReferenceObjectByHandle, externs::ObDereferenceObject, externs::PsSuspendProcess, externs::PsResumeProcess}; 12 | pub mod externs; 13 | pub mod log; 14 | pub mod string; 15 | pub mod defines; 16 | 17 | use core::panic::PanicInfo; 18 | use winapi::um::winnt::PROCESS_ALL_ACCESS; 19 | use winapi::km::wdm::KPROCESSOR_MODE::KernelMode; 20 | use winapi::km::wdm::*; 21 | use winapi::shared::ntdef::*; 22 | use winapi::shared::ntstatus::*; 23 | use defines::*; 24 | 25 | // Globals 26 | static mut hGlobalPipe: HANDLE = 0 as HANDLE; 27 | 28 | // When using the alloc crate it seems like it does some unwinding. Adding this 29 | // export satisfies the compiler but may introduce undefined behaviour when a 30 | // panic occurs. 31 | #[no_mangle] 32 | pub extern "system" fn __CxxFrameHandler3(_: *mut u8, _: *mut u8, _: *mut u8, _: *mut u8) -> i32 { unimplemented!() } 33 | // Hack, explanation can be found here: https://github.com/Trantect/win_driver_example/issues/4 34 | #[export_name = "_fltused"] 35 | static _FLTUSED: i32 = 0; 36 | 37 | #[global_allocator] 38 | static GLOBAL: kernel_alloc::KernelAlloc = kernel_alloc::KernelAlloc; 39 | 40 | #[panic_handler] 41 | fn panic(_info: &PanicInfo) -> ! 42 | { 43 | loop {}; 44 | } 45 | 46 | extern "system" fn DriverUnloadFunction(DriverObject: &mut DRIVER_OBJECT) 47 | { 48 | kernel_print::kernel_println!("Unload attempt"); 49 | return; 50 | } 51 | 52 | pub extern "system" fn MyHandler(Frame: PKTRAP_FRAME) -> BOOLEAN 53 | { 54 | unsafe 55 | { 56 | use core::ptr::null_mut; 57 | use core::mem::zeroed; 58 | use core::mem::transmute; 59 | 60 | let mut totalPacket: TOTAL_PACKET = core::mem::zeroed::(); 61 | let mut ioBlock: IO_STATUS_BLOCK = core::mem::zeroed::(); 62 | 63 | totalPacket.CustomHeader.ProcessId = PsGetProcessId(PsGetCurrentProcess()) as QWORD; 64 | let mut TryProbe = || 65 | { 66 | if (*Frame).Rsp as QWORD != 0 67 | { 68 | ProbeForRead((*Frame).Rsp as PVOID, (totalPacket.CustomHeader.StackData.len() * core::mem::size_of::()), 4); 69 | memmove(transmute(&mut totalPacket.CustomHeader.StackData), (*Frame).Rsp as PVOID, 70 | (totalPacket.CustomHeader.StackData.len() * core::mem::size_of::())); 71 | } 72 | }; 73 | TryProbe(); 74 | memmove(transmute(&mut totalPacket.Frame), Frame as PVOID, core::mem::size_of::()); 75 | ZwWriteFile(hGlobalPipe, null_mut(), null_mut(), null_mut(), &mut ioBlock, transmute(&mut totalPacket), 76 | core::mem::size_of::() as ULONG, null_mut(), null_mut()); 77 | 78 | return TRUE; 79 | } 80 | } 81 | 82 | fn AddProcess(PID: DWORD) -> NTSTATUS 83 | { 84 | use core::ptr::null_mut; 85 | 86 | unsafe 87 | { 88 | let mut objAttr: OBJECT_ATTRIBUTES = core::mem::zeroed::(); 89 | let mut clientId: CLIENT_ID = core::mem::zeroed::(); 90 | InitializeObjectAttributes(&mut objAttr, null_mut(), OBJ_KERNEL_HANDLE, null_mut(), null_mut()); 91 | clientId.UniqueProcess = PID as HANDLE; 92 | clientId.UniqueThread = 0 as HANDLE; 93 | let mut hProcess: HANDLE = 0 as HANDLE; 94 | if NT_SUCCESS(ZwOpenProcess(&mut hProcess, PROCESS_ALL_ACCESS, &mut objAttr, &mut clientId)) 95 | { 96 | let mut NewPID: QWORD = PID.into(); 97 | 98 | if NT_SUCCESS(ZwSetInformationProcess(hProcess, ProcessAltSystemCallInformation, &mut NewPID as *mut QWORD as PVOID, 1)) 99 | { 100 | ZwClose(hProcess); 101 | return STATUS_SUCCESS; 102 | } 103 | } 104 | } 105 | return STATUS_UNSUCCESSFUL; 106 | } 107 | 108 | fn RemoveProcess(PID: DWORD) -> NTSTATUS 109 | { 110 | use core::ptr::null_mut; 111 | let mut ntRet: NTSTATUS = STATUS_SUCCESS; 112 | 113 | unsafe 114 | { 115 | let mut objAttr: OBJECT_ATTRIBUTES = core::mem::zeroed::(); 116 | let mut clientId: CLIENT_ID = core::mem::zeroed::(); 117 | InitializeObjectAttributes(&mut objAttr, null_mut(), OBJ_KERNEL_HANDLE, null_mut(), null_mut()); 118 | clientId.UniqueProcess = PID as HANDLE; 119 | clientId.UniqueThread = 0 as HANDLE; 120 | let mut hProcess: HANDLE = 0 as HANDLE; 121 | if NT_SUCCESS(ZwOpenProcess(&mut hProcess, PROCESS_ALL_ACCESS, &mut objAttr, &mut clientId)) 122 | { 123 | let mut NewPID: QWORD = PID.into(); 124 | let mut pProcess: PEPROCESS = null_mut(); 125 | 126 | 127 | if NT_SUCCESS(ObReferenceObjectByHandle(hProcess, PROCESS_ALL_ACCESS, null_mut(), KernelMode, &mut pProcess, null_mut())) 128 | { 129 | ntRet = PsSuspendProcess(pProcess); 130 | let mut pThreadHead: PLIST_ENTRY = null_mut(); 131 | let mut pThreadNext: PLIST_ENTRY = null_mut(); 132 | 133 | pThreadHead = (pProcess as QWORD + 0x5E0) as PLIST_ENTRY; 134 | pThreadNext = (*pThreadHead).Flink; 135 | let mut pThread: PVOID = null_mut(); 136 | while pThreadNext != pThreadHead 137 | { 138 | pThread = pThreadNext as PVOID; 139 | pThread = (pThread as QWORD - 0x4E8) as PVOID; 140 | /* 141 | asm block to replace the _interlockedbittestandreset macro 142 | lock btr dword ptr [rax], 1Dh 143 | */ 144 | asm!("lock btr dword ptr [{0}], 0x1D", inout(reg) pThread); 145 | pThreadNext = (*pThreadNext).Flink; 146 | } 147 | 148 | ntRet = PsResumeProcess(pProcess); 149 | ObDereferenceObject(pProcess); 150 | } 151 | 152 | ZwClose(hProcess); 153 | 154 | } 155 | } 156 | return STATUS_SUCCESS; 157 | } 158 | 159 | fn PreformInit() -> NTSTATUS 160 | { 161 | use core::ptr::null_mut; 162 | use core::mem::zeroed; 163 | 164 | let mut ntRet: NTSTATUS = STATUS_SUCCESS; 165 | unsafe 166 | { 167 | if hGlobalPipe != 0 as HANDLE 168 | { 169 | ZwClose(hGlobalPipe); 170 | hGlobalPipe = 0 as HANDLE; 171 | } 172 | else 173 | { 174 | let mut objPipe: OBJECT_ATTRIBUTES = core::mem::zeroed::(); 175 | let mut usPipe = create_unicode_string(obfstr::wide!("\\Device\\NamedPipe\\CallMonPipe")); 176 | InitializeObjectAttributes(&mut objPipe, &mut usPipe, OBJ_KERNEL_HANDLE, null_mut(), null_mut()); 177 | let mut ioBlock: IO_STATUS_BLOCK = core::mem::zeroed::(); 178 | if NT_SUCCESS(ZwCreateFile(&mut hGlobalPipe, FILE_WRITE_DATA | SYNCHRONIZE, &mut objPipe, &mut ioBlock, null_mut(), 0, 0, FILE_OPEN, 179 | FILE_SYNCHRONOUS_IO_NONALERT | FILE_DELETE_ON_CLOSE, NULL, 0)) 180 | { 181 | ntRet = STATUS_SUCCESS; 182 | } 183 | else 184 | { 185 | ntRet = STATUS_PIPE_BROKEN; 186 | } 187 | } 188 | } 189 | return ntRet; 190 | } 191 | 192 | extern "system" fn DeviceDispatch(DeviceObject: &mut DEVICE_OBJECT, Irp: &mut IRP) -> NTSTATUS 193 | { 194 | let pStack: &mut IO_STACK_LOCATION; 195 | let mut ntRet: NTSTATUS = STATUS_SUCCESS; 196 | 197 | unsafe 198 | { 199 | let FunctionCode = (*(*Irp.Tail.Overlay().__bindgen_anon_2.__bindgen_anon_1.CurrentStackLocation())).MajorFunction; 200 | let Stack = (*Irp.Tail.Overlay().__bindgen_anon_2.__bindgen_anon_1.CurrentStackLocation()); 201 | let IOCTL = (*Stack).Parameters.DeviceIoControl().IoControlCode as u32; 202 | if FunctionCode == IRP_MJ::DEVICE_CONTROL as u8 203 | { 204 | match IOCTL 205 | { 206 | IOCTL_ADD_PROCESS => 207 | { 208 | if (*Stack).Parameters.DeviceIoControl().InputBufferLength as DWORD == 4 209 | { 210 | let mut buf = (*Irp.AssociatedIrp.SystemBuffer_mut()); 211 | let PID: &mut DWORD = &mut *(buf as *mut DWORD); 212 | ntRet = AddProcess(*PID); 213 | } 214 | else 215 | { 216 | ntRet = STATUS_BUFFER_TOO_SMALL; 217 | } 218 | }, 219 | IOCTL_REMOVE_PROCESS => 220 | { 221 | if (*Stack).Parameters.DeviceIoControl().InputBufferLength as DWORD == 4 222 | { 223 | let mut buf = (*Irp.AssociatedIrp.SystemBuffer_mut()); 224 | let PID: &mut DWORD = &mut *(buf as *mut DWORD); 225 | ntRet = RemoveProcess(*PID); 226 | } 227 | else 228 | { 229 | ntRet = STATUS_BUFFER_TOO_SMALL; 230 | } 231 | 232 | }, 233 | IOCTL_INIT => ntRet = PreformInit(), 234 | _ => kernel_print::kernel_println!("Invalid IOCTL (Might not be supported in rust version)"), 235 | } 236 | } 237 | let a = Irp.IoStatus.__bindgen_anon_1.Status_mut(); 238 | *a = ntRet; 239 | IofCompleteRequest(Irp, 0); 240 | } 241 | return STATUS_SUCCESS; 242 | } 243 | 244 | #[no_mangle] 245 | pub extern "system" fn DriverEntry(DriverObject: &mut DRIVER_OBJECT, RegistryPath: PUNICODE_STRING) -> NTSTATUS 246 | { 247 | use core::mem::transmute; 248 | 249 | DriverObject.DriverUnload = Some(DriverUnloadFunction); 250 | let usDevice = create_unicode_string(obfstr::wide!("\\Device\\CallMon\0")); 251 | unsafe 252 | { 253 | use winapi::km::wdm::DEVICE_TYPE::FILE_DEVICE_UNKNOWN; 254 | let mut outDevice: *mut DEVICE_OBJECT = core::ptr::null_mut(); 255 | 256 | if NT_SUCCESS(IoCreateDevice(DriverObject, 0, &usDevice, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &mut outDevice)) 257 | { 258 | kernel_print::kernel_println!("Finished initializing!"); 259 | let usSymLink = create_unicode_string(obfstr::wide!("\\DosDevices\\CallMon")); 260 | if NT_SUCCESS(IoCreateSymbolicLink(&usSymLink, &usDevice)) 261 | { 262 | (*outDevice).Flags |= DEVICE_FLAGS::DO_BUFFERED_IO as u32; 263 | for i in 0..IRP_MJ::MAXIMUM_FUNCTION as usize 264 | { 265 | DriverObject.MajorFunction[i] = Some(DeviceDispatch); 266 | } 267 | let mut usPsRegisterAltSystemCallHandler = create_unicode_string(obfstr::wide!("PsRegisterAltSystemCallHandler")); 268 | type PsRegisterAltSystemCallHandler = extern "system" fn(HandlerRoutine: PVOID, HandlerIndex: LONG) -> NTSTATUS; 269 | let pPsRegisterAltSystemCallHandler: PsRegisterAltSystemCallHandler = transmute(MmGetSystemRoutineAddress(&mut usPsRegisterAltSystemCallHandler)); 270 | if NT_SUCCESS(pPsRegisterAltSystemCallHandler(transmute(MyHandler as PVOID), 1)) 271 | { 272 | return STATUS_SUCCESS; 273 | } 274 | } 275 | } 276 | } 277 | return STATUS_UNSUCCESSFUL 278 | } 279 | -------------------------------------------------------------------------------- /Rust/src/log.rs: -------------------------------------------------------------------------------- 1 | pub use winapi::km::wdm::DbgPrint; 2 | 3 | #[macro_export] 4 | macro_rules! log { 5 | ($string: expr) => { 6 | unsafe { 7 | $crate::log::DbgPrint(concat!("[>] ", $string, "\0").as_ptr()) 8 | } 9 | }; 10 | 11 | ($string: expr, $($x:tt)*) => { 12 | unsafe { 13 | #[allow(unused_unsafe)] 14 | $crate::log::DbgPrint(concat!("[>] ", $string, "\0").as_ptr(), $($x)*) 15 | } 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /Rust/src/string.rs: -------------------------------------------------------------------------------- 1 | use winapi::shared::ntdef::UNICODE_STRING; 2 | 3 | pub fn create_unicode_string(s: &[u16]) -> UNICODE_STRING { 4 | let len = s.len(); 5 | 6 | let n = if len > 0 && s[len - 1] == 0 { len - 1 } else { len }; 7 | 8 | UNICODE_STRING { 9 | Length: (n * 2) as u16, 10 | MaximumLength: (len * 2) as u16, 11 | Buffer: s.as_ptr() as _, 12 | } 13 | } 14 | --------------------------------------------------------------------------------