pid_t fork(void) { NTSTATUS nErrCode; CONTEXT ctxThreadContext; HANDLE hProcess; HANDLE hThread; INITIAL_TEB itInitialTeb; CLIENT_ID ciClientId; MEMORY_BASIC_INFORMATION mbiStackInfo; THREAD_BASIC_INFORMATION tbiThreadInfo; struct __tagcsrmsg{ PORT_MESSAGE PortMessage; struct CSRSS_MESSAGE CsrssMessage; PROCESS_INFORMATION ProcessInformation; CLIENT_ID Debugger; ULONG CreationFlags; ULONG VdmInfo[2]; } csrmsg; /* STEP 1: Duplicate current process */ nErrCode = NtCreateProcess ( &hProcess, PROCESS_ALL_ACCESS, NULL, NtCurrentProcess(), TRUE, 0, 0, 0 ); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("NtCreateProcess() failed with status 0x%08X\n", nErrCode); goto fail; } /* STEP 2: Duplicate current thread */ /* 2.1: duplicate registers */ ctxThreadContext.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT; /* get the current thread's registers */ nErrCode = NtGetContextThread(NtCurrentThread(), &ctxThreadContext); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("NtGetContextThread() failed with status 0x%08X\n", nErrCode); goto cleanup_and_fail; } /* redirect the child process to the child_branch label (see 4.3 below) */ ctxThreadContext.Eip = (ULONG)&&child_branch; /* 2.2: duplicate stack */ /* get stack base and size */ nErrCode = NtQueryVirtualMemory ( NtCurrentProcess(), (PVOID)ctxThreadContext.Esp, MemoryBasicInformation, &mbiStackInfo, sizeof(mbiStackInfo), 0 ); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("NtQueryVirtualMemory() failed with status 0x%08X\n", nErrCode); goto cleanup_and_fail; } itInitialTeb.StackCommit = 0; itInitialTeb.StackReserve = 0; itInitialTeb.StackBase = (PVOID)((ULONG)(mbiStackInfo.BaseAddress) + mbiStackInfo.RegionSize); itInitialTeb.StackLimit = mbiStackInfo.BaseAddress; itInitialTeb.StackAllocate = mbiStackInfo.AllocationBase; /* 2.3: create duplicate thread */ nErrCode = NtCreateThread ( &hThread, THREAD_ALL_ACCESS, NULL, hProcess, (CLIENT_ID *)&ciClientId, &ctxThreadContext, &itInitialTeb, TRUE ); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("NtCreateThread() failed with status 0x%08X\n", nErrCode); goto cleanup_and_fail; } /* 2.4: duplicate the TEB */ /* store the client id in the child thread's stack (see 4.3b) */ nErrCode = NtWriteVirtualMemory ( hProcess, &ciClientId, &ciClientId, sizeof(ciClientId), 0 ); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode); goto cleanup_and_fail; } /* get the child thread's TEB base */ nErrCode = NtQueryInformationThread ( hThread, ThreadBasicInformation, &tbiThreadInfo, sizeof(tbiThreadInfo), 0 ); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("NtQueryInformationThread() failed with status 0x%08X\n", nErrCode); goto cleanup_and_fail; } /* copy the TEB */ nErrCode = NtWriteVirtualMemory ( hProcess, tbiThreadInfo.TebBaseAddress, NtCurrentTeb(), sizeof(TEB), 0 ); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode); goto cleanup_and_fail; } /* STEP 3: Call Win32 subsystem */ memset(&csrmsg, 0, sizeof(csrmsg)); csrmsg.ProcessInformation.hProcess = hProcess; csrmsg.ProcessInformation.hThread = hThread; csrmsg.ProcessInformation.dwProcessId = (DWORD)ciClientId.UniqueProcess; csrmsg.ProcessInformation.dwThreadId = (DWORD)ciClientId.UniqueThread; nErrCode = CsrClientCallServer(&csrmsg, 0, 0x10000, 0x24); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("CsrClientCallServer() failed with status 0x%08X\n", nErrCode); goto cleanup_and_fail; } /* STEP 4: Finalization */ /* 4.1: resume thread */ nErrCode = NtResumeThread(hThread, 0); /* 4.2: close superfluous handles */ NtClose(hProcess); NtClose(hThread); /* 4.3: (parent) return the child process id */ return ((pid_t)(ciClientId.UniqueProcess)); /* 4.3b: (child) cleanup and return 0 */ child_branch: /* restore the thread and process id in the TEB */ memcpy(&NtCurrentTeb()->Cid, &ciClientId, sizeof(ciClientId)); /* return 0 */ return (0); cleanup_and_fail: NtTerminateProcess(hProcess, nErrCode); fail: errno = __status_to_errno(nErrCode); return (-1); }
LPVOID GetProcAddr(HANDLE hProc) { AcquireLock(); DWORD pid,addr,len; if (hProc==NtCurrentProcess()) pid=current_process_id; else { PROCESS_BASIC_INFORMATION info; NtQueryInformationProcess(hProc,ProcessBasicInformation,&info,sizeof(info),&len); pid=info.uUniqueProcessId; } pid>>=2; for (UINT_PTR i=0;i<count;i++) { if ((proc_record[i]&0xFFF)==pid) { addr=proc_record[i]&~0xFFF; ReleaseLock(); return (LPVOID)addr; } } len=0x1000; NtAllocateVirtualMemory(hProc,(PVOID*)(proc_record+count),0,&len, MEM_COMMIT,PAGE_EXECUTE_READWRITE); DWORD base = proc_record[count]; proc_record[count] |= pid; union { LPVOID buffer; DWORD b; }; b = base; LPVOID fun_table[3]; *(DWORD*)(normal_routine + ADDR0) += base; NtWriteVirtualMemory(hProc, buffer, normal_routine, 0x14, 0); *(DWORD*)(normal_routine + ADDR0) -= base; b += 0x14; fun_table[0] = NtTerminateThread; fun_table[1] = NtQueryVirtualMemory; fun_table[2] = MessageBoxW; NtWriteVirtualMemory(hProc, buffer, fun_table, 0xC, 0); b += 0xC; *(DWORD*)(except_routine + ADDR1) += base; *(DWORD*)(except_routine + ADDR2) += base; *(DWORD*)(except_routine + ADDR3) += base; NtWriteVirtualMemory(hProc, buffer, except_routine, 0xE0, 0); *(DWORD*)(except_routine + ADDR1) -= base; *(DWORD*)(except_routine + ADDR2) -= base; *(DWORD*)(except_routine + ADDR3) -= base; count++; ReleaseLock(); return (LPVOID)base; }
DWORD Inject(HANDLE hProc, LPWSTR engine) { LPVOID lpvAllocAddr = 0; DWORD dwWrite = 0x1000, len = 0; HANDLE hTH; WCHAR path[MAX_PATH]; LPWSTR p; if (!IthCheckFile(DllName)) return -1; p = GetMainModulePath(); len = wcslen(p); memcpy(path, p, len << 1); memset(path + len, 0, (MAX_PATH - len) << 1); for (p = path + len; *p != L'\\'; p--); //Always a \ after drive letter. p++; wcscpy(p, DllName); NtAllocateVirtualMemory(hProc, &lpvAllocAddr, 0, &dwWrite, MEM_COMMIT, PAGE_READWRITE); if (lpvAllocAddr == 0) return -1; CheckThreadStart(); //Copy module path into address space of target process. NtWriteVirtualMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite); hTH = IthCreateThread(LoadLibrary, (DWORD)lpvAllocAddr, hProc); if (hTH == 0 || hTH == INVALID_HANDLE_VALUE) { ConsoleOutput(ErrorRemoteThread); return -1; } NtWaitForSingleObject(hTH, 0, 0); THREAD_BASIC_INFORMATION info; NtQueryInformationThread(hTH, ThreadBasicInformation, &info, sizeof(info), &dwWrite); NtClose(hTH); if (info.ExitStatus != 0) { wcscpy(p, engine); NtWriteVirtualMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite); hTH = IthCreateThread(LoadLibrary, (DWORD)lpvAllocAddr, hProc); if (hTH == 0 || hTH == INVALID_HANDLE_VALUE) { ConsoleOutput(ErrorRemoteThread); return -1; } NtWaitForSingleObject(hTH, 0, 0); NtClose(hTH); } dwWrite = 0; NtFreeVirtualMemory(hProc, &lpvAllocAddr, &dwWrite, MEM_RELEASE); return info.ExitStatus; }
BOOL engine_SaveRemoteVar(HANDLE hProcess,LPVOID lpDest,LPVOID lpSrc) { if (NT_SUCCESS(NtWriteVirtualMemory(hProcess,lpDest,lpSrc,sizeof(LPVOID),NULL))) return TRUE; return FALSE; }
int exec_fork(HANDLE process) { NTSTATUS status; status = NtWriteVirtualMemory(process, &startup, &startup, sizeof(startup), NULL); if (!NT_SUCCESS(status)) { log_error("exec_fork(): NtWriteVirtualMemory() failed, status: %x", status); return 0; } return 1; }
BOOL engine_UnHookFunctionInProcess(HANDLE hProcess,LPSTR lpModuleName,LPSTR lpFunctionName,LPVOID lpOldFunctionAddress,DWORD dwFunctionSize) { LPVOID lpModule=NULL; LPVOID lpFunction=NULL; MEMORY_BASIC_INFORMATION mbi; CHAR lpLocalStub[MAX_FUNC_LEN*2]; DWORD dwFree=0; DWORD dwBytesWritten; // Get module address lpModule=(LPVOID)engine_GetRemoteModuleHandle(hProcess,lpModuleName); if (!lpModule) return FALSE; // Get function address lpFunction=engine_GetRemoteProcAddress(hProcess,lpModule,lpFunctionName); if (!lpFunction) return FALSE; // Get info about the function address if (!NT_SUCCESS(SafeNtQueryVirtualMemory(hProcess,lpFunction,MemoryBasicInformation,&mbi,sizeof(mbi),NULL))) return FALSE; // Flush instruction cache NtFlushInstructionCache(hProcess,mbi.BaseAddress,mbi.RegionSize); // Change the protection for the region if (!NT_SUCCESS(NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,PAGE_EXECUTE_READWRITE,&mbi.Protect))) return FALSE; // Read old functions instructions if (!NT_SUCCESS(SafeNtReadVirtualMemory(hProcess,lpOldFunctionAddress,lpLocalStub,dwFunctionSize,NULL))) { // restore protection NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return FALSE; } // Restore original function if (!NT_SUCCESS(NtWriteVirtualMemory(hProcess,lpFunction,lpLocalStub,dwFunctionSize,&dwBytesWritten))) { // restore protection NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return FALSE; } // Free stub memory NtFreeVirtualMemory(hProcess,&lpOldFunctionAddress,&dwFree,MEM_RELEASE); // Restore protection NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return TRUE; }
// copy pe image on lpbase from current process to dwpid on the same address BOOL engine_CopyImageToProcess(HANDLE hProcess,LPVOID lpBase) { DWORD dwSize,dwFree=0; LPVOID lpNew=lpBase; // get PE size dwSize=engine_GetPEImageSize(lpBase); if (!NT_SUCCESS(NtAllocateVirtualMemory(hProcess,&lpNew,0,&dwSize,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE))) return FALSE; if (!NT_SUCCESS(NtWriteVirtualMemory(hProcess,lpNew,lpBase,dwSize,NULL))) { NtFreeVirtualMemory(hProcess,&lpNew,&dwFree,MEM_RELEASE); return FALSE; } return TRUE; }
NTSTATUS LsapGetLogonSessionData(IN OUT PLSA_API_MSG RequestMsg) { OBJECT_ATTRIBUTES ObjectAttributes; HANDLE ProcessHandle = NULL; PLSAP_LOGON_SESSION Session; PSECURITY_LOGON_SESSION_DATA LocalSessionData; PVOID ClientBaseAddress = NULL; ULONG Length, MemSize; LPWSTR Ptr; NTSTATUS Status; TRACE("LsapGetLogonSessionData(%p)\n", RequestMsg); TRACE("LogonId: %lx\n", RequestMsg->GetLogonSessionData.Request.LogonId.LowPart); Session = LsapGetLogonSession(&RequestMsg->GetLogonSessionData.Request.LogonId); if (Session == NULL) return STATUS_NO_SUCH_LOGON_SESSION; Length = sizeof(SECURITY_LOGON_SESSION_DATA); /* Session->UserName.MaximumLength + Session->LogonDomain.MaximumLength + Session->AuthenticationPackage.MaximumLength + Session->LogonServer.MaximumLength + Session->DnsDomainName.MaximumLength + Session->Upn.MaximumLength; if (Session->Sid != NULL) RtlLengthSid(Session->Sid); */ TRACE("Length: %lu\n", Length); LocalSessionData = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Length); if (LocalSessionData == NULL) return STATUS_INSUFFICIENT_RESOURCES; Ptr = (LPWSTR)((ULONG_PTR)LocalSessionData + sizeof(SECURITY_LOGON_SESSION_DATA)); TRACE("LocalSessionData: %p Ptr: %p\n", LocalSessionData, Ptr); LocalSessionData->Size = sizeof(SECURITY_LOGON_SESSION_DATA); RtlCopyLuid(&LocalSessionData->LogonId, &RequestMsg->GetLogonSessionData.Request.LogonId); InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); Status = NtOpenProcess(&ProcessHandle, PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, &ObjectAttributes, &RequestMsg->h.ClientId); if (!NT_SUCCESS(Status)) { TRACE("NtOpenProcess() failed (Status %lx)\n", Status); goto done; } MemSize = Length; Status = NtAllocateVirtualMemory(ProcessHandle, &ClientBaseAddress, 0, &MemSize, MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(Status)) { TRACE("NtAllocateVirtualMemory() failed (Status %lx)\n", Status); goto done; } TRACE("MemSize: %lu\n", MemSize); TRACE("ClientBaseAddress: %p\n", ClientBaseAddress); Status = NtWriteVirtualMemory(ProcessHandle, ClientBaseAddress, LocalSessionData, Length, NULL); if (!NT_SUCCESS(Status)) { TRACE("NtWriteVirtualMemory() failed (Status %lx)\n", Status); goto done; } RequestMsg->GetLogonSessionData.Reply.SessionDataBuffer = ClientBaseAddress; done: if (ProcessHandle != NULL) NtClose(ProcessHandle); if (LocalSessionData != NULL) RtlFreeHeap(RtlGetProcessHeap(), 0, LocalSessionData); return Status; }
NTSTATUS LsapEnumLogonSessions(IN OUT PLSA_API_MSG RequestMsg) { OBJECT_ATTRIBUTES ObjectAttributes; HANDLE ProcessHandle = NULL; PLIST_ENTRY SessionEntry; PLSAP_LOGON_SESSION CurrentSession; PLUID SessionList; ULONG i, Length, MemSize; PVOID ClientBaseAddress = NULL; NTSTATUS Status; TRACE("LsapEnumLogonSessions(%p)\n", RequestMsg); Length = SessionCount * sizeof(LUID); SessionList = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Length); if (SessionList == NULL) return STATUS_INSUFFICIENT_RESOURCES; i = 0; SessionEntry = SessionListHead.Flink; while (SessionEntry != &SessionListHead) { CurrentSession = CONTAINING_RECORD(SessionEntry, LSAP_LOGON_SESSION, Entry); RtlCopyLuid(&SessionList[i], &CurrentSession->LogonId); SessionEntry = SessionEntry->Flink; i++; } InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); Status = NtOpenProcess(&ProcessHandle, PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, &ObjectAttributes, &RequestMsg->h.ClientId); if (!NT_SUCCESS(Status)) { TRACE("NtOpenProcess() failed (Status %lx)\n", Status); goto done; } TRACE("Length: %lu\n", Length); MemSize = Length; Status = NtAllocateVirtualMemory(ProcessHandle, &ClientBaseAddress, 0, &MemSize, MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(Status)) { TRACE("NtAllocateVirtualMemory() failed (Status %lx)\n", Status); goto done; } TRACE("MemSize: %lu\n", MemSize); TRACE("ClientBaseAddress: %p\n", ClientBaseAddress); Status = NtWriteVirtualMemory(ProcessHandle, ClientBaseAddress, SessionList, Length, NULL); if (!NT_SUCCESS(Status)) { TRACE("NtWriteVirtualMemory() failed (Status %lx)\n", Status); goto done; } RequestMsg->EnumLogonSessions.Reply.LogonSessionCount = SessionCount; RequestMsg->EnumLogonSessions.Reply.LogonSessionBuffer = ClientBaseAddress; done: if (ProcessHandle != NULL) NtClose(ProcessHandle); if (SessionList != NULL) RtlFreeHeap(RtlGetProcessHeap(), 0, SessionList); return Status; }
INT_PTR CALLBACK PhpMemoryEditorDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { PMEMORY_EDITOR_CONTEXT context; if (uMsg != WM_INITDIALOG) { context = GetProp(hwndDlg, PhMakeContextAtom()); } else { context = (PMEMORY_EDITOR_CONTEXT)lParam; SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context); } if (!context) return FALSE; switch (uMsg) { case WM_INITDIALOG: { NTSTATUS status; SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)PH_LOAD_SHARED_ICON_SMALL(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PH_LOAD_SHARED_ICON_LARGE(PhInstanceHandle, MAKEINTRESOURCE(IDI_PROCESSHACKER))); if (context->Title) { SetWindowText(hwndDlg, context->Title->Buffer); } else { PPH_PROCESS_ITEM processItem; if (processItem = PhReferenceProcessItem(context->ProcessId)) { SetWindowText(hwndDlg, PhaFormatString(L"%s (%u) (0x%Ix - 0x%Ix)", processItem->ProcessName->Buffer, HandleToUlong(context->ProcessId), context->BaseAddress, (ULONG_PTR)context->BaseAddress + context->RegionSize)->Buffer); PhDereferenceObject(processItem); } } PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); if (context->RegionSize > 1024 * 1024 * 1024) // 1 GB { PhShowError(context->OwnerHandle, L"Unable to edit the memory region because it is too large."); return TRUE; } if (!NT_SUCCESS(status = PhOpenProcess( &context->ProcessHandle, PROCESS_VM_READ, context->ProcessId ))) { PhShowStatus(context->OwnerHandle, L"Unable to open the process", status, 0); return TRUE; } context->Buffer = PhAllocatePage(context->RegionSize, NULL); if (!context->Buffer) { PhShowError(context->OwnerHandle, L"Unable to allocate memory for the buffer."); return TRUE; } if (!NT_SUCCESS(status = NtReadVirtualMemory( context->ProcessHandle, context->BaseAddress, context->Buffer, context->RegionSize, NULL ))) { PhShowStatus(context->OwnerHandle, L"Unable to read memory", status, 0); return TRUE; } PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_SAVE), NULL, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM); PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_BYTESPERROW), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_GOTO), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_WRITE), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_REREAD), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT); if (MinimumSize.left == -1) { RECT rect; rect.left = 0; rect.top = 0; rect.right = 290; rect.bottom = 140; MapDialogRect(hwndDlg, &rect); MinimumSize = rect; MinimumSize.left = 0; } context->HexEditHandle = GetDlgItem(hwndDlg, IDC_MEMORY); PhAddLayoutItem(&context->LayoutManager, context->HexEditHandle, NULL, PH_ANCHOR_ALL); HexEdit_SetBuffer(context->HexEditHandle, context->Buffer, (ULONG)context->RegionSize); { PH_RECTANGLE windowRectangle; windowRectangle.Position = PhGetIntegerPairSetting(L"MemEditPosition"); windowRectangle.Size = PhGetScalableIntegerPairSetting(L"MemEditSize", TRUE).Pair; PhAdjustRectangleToWorkingArea(NULL, &windowRectangle); MoveWindow(hwndDlg, windowRectangle.Left, windowRectangle.Top, windowRectangle.Width, windowRectangle.Height, FALSE); // Implement cascading by saving an offsetted rectangle. windowRectangle.Left += 20; windowRectangle.Top += 20; PhSetIntegerPairSetting(L"MemEditPosition", windowRectangle.Position); PhSetScalableIntegerPairSetting2(L"MemEditSize", windowRectangle.Size); } { PWSTR bytesPerRowStrings[7]; ULONG i; ULONG bytesPerRow; for (i = 0; i < sizeof(bytesPerRowStrings) / sizeof(PWSTR); i++) bytesPerRowStrings[i] = PhaFormatString(L"%u bytes per row", 1 << (2 + i))->Buffer; PhAddComboBoxStrings(GetDlgItem(hwndDlg, IDC_BYTESPERROW), bytesPerRowStrings, sizeof(bytesPerRowStrings) / sizeof(PWSTR)); bytesPerRow = PhGetIntegerSetting(L"MemEditBytesPerRow"); if (bytesPerRow >= 4) { HexEdit_SetBytesPerRow(context->HexEditHandle, bytesPerRow); PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_BYTESPERROW), PhaFormatString(L"%u bytes per row", bytesPerRow)->Buffer, FALSE); } } context->LoadCompleted = TRUE; } break; case WM_DESTROY: { if (context->LoadCompleted) { PhSaveWindowPlacementToSetting(L"MemEditPosition", L"MemEditSize", hwndDlg); PhRemoveElementAvlTree(&PhMemoryEditorSet, &context->Links); PhUnregisterDialog(hwndDlg); } RemoveProp(hwndDlg, PhMakeContextAtom()); PhDeleteLayoutManager(&context->LayoutManager); if (context->Buffer) PhFreePage(context->Buffer); if (context->ProcessHandle) NtClose(context->ProcessHandle); PhClearReference(&context->Title); if ((context->Flags & PH_MEMORY_EDITOR_UNMAP_VIEW_OF_SECTION) && context->ProcessId == NtCurrentProcessId()) NtUnmapViewOfSection(NtCurrentProcess(), context->BaseAddress); PhFree(context); } break; case WM_SHOWWINDOW: { SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)context->HexEditHandle, TRUE); } break; case WM_COMMAND: { switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: DestroyWindow(hwndDlg); break; case IDC_SAVE: { static PH_FILETYPE_FILTER filters[] = { { L"Binary files (*.bin)", L"*.bin" }, { L"All files (*.*)", L"*.*" } }; PVOID fileDialog; PPH_PROCESS_ITEM processItem; fileDialog = PhCreateSaveFileDialog(); PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); if (!context->Title && (processItem = PhReferenceProcessItem(context->ProcessId))) { PhSetFileDialogFileName(fileDialog, PhaFormatString(L"%s_0x%Ix-0x%Ix.bin", processItem->ProcessName->Buffer, context->BaseAddress, context->RegionSize)->Buffer); PhDereferenceObject(processItem); } else { PhSetFileDialogFileName(fileDialog, L"Memory.bin"); } if (PhShowFileDialog(hwndDlg, fileDialog)) { NTSTATUS status; PPH_STRING fileName; PPH_FILE_STREAM fileStream; fileName = PH_AUTO(PhGetFileDialogFileName(fileDialog)); if (NT_SUCCESS(status = PhCreateFileStream( &fileStream, fileName->Buffer, FILE_GENERIC_WRITE, FILE_SHARE_READ, FILE_OVERWRITE_IF, 0 ))) { status = PhWriteFileStream(fileStream, context->Buffer, (ULONG)context->RegionSize); PhDereferenceObject(fileStream); } if (!NT_SUCCESS(status)) PhShowStatus(hwndDlg, L"Unable to create the file", status, 0); } PhFreeFileDialog(fileDialog); } break; case IDC_GOTO: { PPH_STRING selectedChoice = NULL; while (PhaChoiceDialog( hwndDlg, L"Go to Offset", L"Enter an offset:", NULL, 0, NULL, PH_CHOICE_DIALOG_USER_CHOICE, &selectedChoice, NULL, L"MemEditGotoChoices" )) { ULONG64 offset; if (selectedChoice->Length == 0) continue; if (PhStringToInteger64(&selectedChoice->sr, 0, &offset)) { if (offset >= context->RegionSize) { PhShowError(hwndDlg, L"The offset is too large."); continue; } SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)context->HexEditHandle, TRUE); HexEdit_SetSel(context->HexEditHandle, (LONG)offset, (LONG)offset); break; } } } break; case IDC_WRITE: { NTSTATUS status; if (!context->WriteAccess) { HANDLE processHandle; if (!NT_SUCCESS(status = PhOpenProcess( &processHandle, PROCESS_VM_READ | PROCESS_VM_WRITE, context->ProcessId ))) { PhShowStatus(hwndDlg, L"Unable to open the process", status, 0); break; } if (context->ProcessHandle) NtClose(context->ProcessHandle); context->ProcessHandle = processHandle; context->WriteAccess = TRUE; } if (!NT_SUCCESS(status = NtWriteVirtualMemory( context->ProcessHandle, context->BaseAddress, context->Buffer, context->RegionSize, NULL ))) { PhShowStatus(hwndDlg, L"Unable to write memory", status, 0); } } break; case IDC_REREAD: { NTSTATUS status; if (!NT_SUCCESS(status = NtReadVirtualMemory( context->ProcessHandle, context->BaseAddress, context->Buffer, context->RegionSize, NULL ))) { PhShowStatus(hwndDlg, L"Unable to read memory", status, 0); } InvalidateRect(context->HexEditHandle, NULL, TRUE); } break; case IDC_BYTESPERROW: if (HIWORD(wParam) == CBN_SELCHANGE) { PPH_STRING bytesPerRowString = PhaGetDlgItemText(hwndDlg, IDC_BYTESPERROW); PH_STRINGREF firstPart; PH_STRINGREF secondPart; ULONG64 bytesPerRow64; if (PhSplitStringRefAtChar(&bytesPerRowString->sr, ' ', &firstPart, &secondPart)) { if (PhStringToInteger64(&firstPart, 10, &bytesPerRow64)) { PhSetIntegerSetting(L"MemEditBytesPerRow", (ULONG)bytesPerRow64); HexEdit_SetBytesPerRow(context->HexEditHandle, (ULONG)bytesPerRow64); SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)context->HexEditHandle, TRUE); } } } break; } } break; case WM_SIZE: { PhLayoutManagerLayout(&context->LayoutManager); } break; case WM_SIZING: { PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom); } break; case WM_PH_SELECT_OFFSET: { HexEdit_SetEditMode(context->HexEditHandle, EDIT_ASCII); HexEdit_SetSel(context->HexEditHandle, (ULONG)wParam, (ULONG)wParam + (ULONG)lParam); } break; } return FALSE; }
LPVOID engine_HookFunctionInProcess(HANDLE hProcess,LPSTR lpModuleName,LPSTR lpFunctionName,LPVOID lpHookFunctionAddress,PDWORD pdwHookFunctionSize,LPVOID* lpFunctionAddress,INT iRndJmp) { LPVOID lpModule=NULL; LPVOID lpFunction=NULL; MEMORY_BASIC_INFORMATION mbi; CHAR lpTmpFunction[MAX_FUNC_LEN*2]; CHAR lpLocalStub[MAX_FUNC_LEN*3]; CHAR lpLocalFunc[MAX_FUNC_LEN*3]; DWORD dwBytesRead; DWORD dwReadLen=0; DWORD dwExistingJMP=0; DWORD dwStubSize; DWORD dwFree=0; DWORD dwBytesWritten; DWORD dwOldProtect; LPVOID lpRemoteStub=NULL; INT iFuncLen; PBYTE pReadAddress; NTSTATUS ntStatus; // Get module address lpModule=(LPVOID)engine_GetRemoteModuleHandle(hProcess,lpModuleName); if (!lpModule) return NULL; // Get function address lpFunction=engine_GetRemoteProcAddress(hProcess,lpModule,lpFunctionName); if (!lpFunction) return NULL; // Get info about the function address if (!NT_SUCCESS(SafeNtQueryVirtualMemory(hProcess,lpFunction,MemoryBasicInformation,&mbi,sizeof(mbi),NULL))) return NULL; // Flush instruction cache NtFlushInstructionCache(hProcess,mbi.BaseAddress,mbi.RegionSize); // Change the protection for the region if (!NT_SUCCESS(NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,PAGE_EXECUTE_READWRITE,&mbi.Protect))) return NULL; // Fill stub buffer with nops RtlFillMemory(lpLocalStub,MAX_FUNC_LEN*3,NOP); // Read MAX_FUNC_LEN instruction(s) from the function into our function buffer if (!NT_SUCCESS(SafeNtReadVirtualMemory(hProcess,lpFunction,lpTmpFunction,MAX_FUNC_LEN*2,&dwBytesRead))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } pReadAddress=(PBYTE)lpTmpFunction; // check if first opcode in the function is a another jump if (*pReadAddress==LONG_JMP_OPCODE) { // get relative address memcpy(&dwExistingJMP,pReadAddress+1,4); // get absolute address dwExistingJMP=(DWORD)lpFunction+dwExistingJMP; // readlen dwReadLen=jtJmpTable[RELATIVE_JMP].iCodeSize engine_BuildJMPBuffer((CHAR*)lpLocalStub,((DWORD)lpRemoteStub+dwReadLen)-dwExistingJMP,RELATIVE_JMP); NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } // Get the length of the first instruction(s) // This part is done by Z0MBiE's LDE32 v1.05 iFuncLen=disasm_main(pReadAddress); // get first instruction length while (iFuncLen!=-1 && dwReadLen<(DWORD)jtJmpTable[iRndJmp].iCodeSize) { dwReadLen+=iFuncLen; pReadAddress+=iFuncLen; iFuncLen=disasm_main(pReadAddress); // next instruction length } // API code is too short or too long too hook this way (for now ;)) if (dwReadLen<(DWORD)jtJmpTable[iRndJmp].iCodeSize||dwReadLen>MAX_FUNC_LEN*2) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } // Read the first instruction(s) from the function into our stub buffer if (!NT_SUCCESS(SafeNtReadVirtualMemory(hProcess,lpFunction,lpLocalStub,dwReadLen,&dwBytesRead))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } // Allocate space with read/write access for our "stub" // note: always use a relative jump for our stub -> RELATIVE_JMP dwStubSize=dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize; if (!NT_SUCCESS(NtAllocateVirtualMemory(hProcess,&lpRemoteStub,0,&dwStubSize,MEM_COMMIT|MEM_TOP_DOWN,PAGE_READWRITE))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); return NULL; } // Check if (dwStubSize<dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } engine_BuildJMPBuffer((CHAR*)lpLocalStub+dwReadLen,jtJmpTable[RELATIVE_JMP].jcStub((DWORD)lpFunction+dwReadLen,(DWORD)lpRemoteStub,dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize),RELATIVE_JMP); // Copy the "stub" buffer to process memory if (!NT_SUCCESS(NtWriteVirtualMemory(hProcess,lpRemoteStub,lpLocalStub,dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize,&dwBytesWritten))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } // Check if (dwBytesWritten<dwReadLen+jtJmpTable[RELATIVE_JMP].iCodeSize) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } // change access if (!NT_SUCCESS(NtProtectVirtualMemory(hProcess,&lpRemoteStub,&dwStubSize,PAGE_EXECUTE_READ,&dwOldProtect))) { NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } // Fill it with NOP RtlFillMemory(lpLocalFunc,MAX_FUNC_LEN*3,NOP); // Prepare jmpcode engine_BuildJMPBuffer((CHAR*)lpLocalFunc,jtJmpTable[iRndJmp].jcFunc((DWORD)lpHookFunctionAddress,(DWORD)lpFunction,(DWORD)jtJmpTable[iRndJmp].iCodeSize),iRndJmp); ntStatus=NtWriteVirtualMemory(hProcess,lpFunction,lpLocalFunc,dwReadLen,&dwBytesWritten); // Check that we really wrote our jmpcode completely if (!NT_SUCCESS(ntStatus) || dwBytesWritten!=dwReadLen) { // Try to fix stuff if (dwBytesWritten) NtWriteVirtualMemory(hProcess,lpFunction,lpRemoteStub,dwBytesWritten,&dwBytesWritten); NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Free allocated buffer NtFreeVirtualMemory(hProcess,&lpRemoteStub,&dwFree,MEM_RELEASE); return NULL; } // Restore protection NtProtectVirtualMemory(hProcess,&mbi.BaseAddress,&mbi.RegionSize,mbi.Protect,NULL); // Save size of read function length if (pdwHookFunctionSize) *pdwHookFunctionSize=dwReadLen; // Save address of function if (lpFunctionAddress) *lpFunctionAddress=lpFunction; return lpRemoteStub; }