native/win-helper/win-helper.cpp (89 lines of code) (raw):

#include <windows.h> #include <winternl.h> #include <string> #include <sstream> typedef NTSTATUS(NTAPI* _NtQueryInformationProcess)( HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength ); void SetErrorMessage(const char* operationName, WCHAR*& errorMessagePtr) { LPWSTR msg; DWORD lastError = GetLastError(); FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&msg, 0, NULL); std::wstringstream s; s << operationName << " failed with error " << lastError << ": "; if (msg) { s << msg; LocalFree(msg); } else { s << "(no message available)"; } std::wstring copy(s.str()); errorMessagePtr = _wcsdup(copy.c_str()); } extern "C" __declspec(dllexport) WCHAR* getCurrentDirectory(DWORD pid, WCHAR*& errorMessagePtr) { HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (!hProcess) { SetErrorMessage("OpenProcess", errorMessagePtr); return NULL; } HMODULE ntdllHandle = GetModuleHandle("ntdll.dll"); if (!ntdllHandle) { SetErrorMessage("GetModuleHandle", errorMessagePtr); return NULL; } _NtQueryInformationProcess NtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress(ntdllHandle, "NtQueryInformationProcess"); BOOL wow; IsWow64Process(GetCurrentProcess(), &wow); if (wow) { SetErrorMessage("Cannot fetch current directory for WoW64 process", errorMessagePtr); return NULL; } PROCESS_BASIC_INFORMATION pbi; NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL); if (!NT_SUCCESS(status)) { errorMessagePtr = _wcsdup((L"NtQueryInformationProcess failed to fetch ProcessBasicInformation:" + std::to_wstring(status)).c_str()); return NULL; } PEB peb; if (!ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL)) { SetErrorMessage("ReadProcessMemory(PROCESS_BASIC_INFORMATION.PebBaseAddress)", errorMessagePtr); return NULL; } RTL_USER_PROCESS_PARAMETERS procParams; if (!ReadProcessMemory(hProcess, peb.ProcessParameters, &procParams, sizeof(procParams), NULL)) { SetErrorMessage("ReadProcessMemory(PEB.ProcessParameters)", errorMessagePtr); return NULL; } // Unfortunately, CurrentDirectory is not declared in _RTL_USER_PROCESS_PARAMETERS, it's somewhere in the Reserved2 area. // Use WinDbg, run "dt ntdll!_PEB - r2" command, find ProcessParameters, then find CurrentDirectory: offset 0x38. UNICODE_STRING currentDirUnicodeStr = *(UNICODE_STRING*)((PCHAR)&procParams + 0x38); if (currentDirUnicodeStr.Length <= 0 || currentDirUnicodeStr.MaximumLength <= 0 || currentDirUnicodeStr.Length >= currentDirUnicodeStr.MaximumLength || currentDirUnicodeStr.MaximumLength > 8192) { std::string err = "Bad current directory: Length=" + std::to_string(currentDirUnicodeStr.Length) + ", MaximumLength=" + std::to_string(currentDirUnicodeStr.MaximumLength); SetErrorMessage(err.c_str(), errorMessagePtr); return NULL; } LPWSTR lpCurrentDir = new WCHAR[currentDirUnicodeStr.MaximumLength / sizeof(WCHAR) + 1]; if (!ReadProcessMemory(hProcess, currentDirUnicodeStr.Buffer, lpCurrentDir, currentDirUnicodeStr.MaximumLength, NULL)) { delete[] lpCurrentDir; SetErrorMessage("ReadProcessMemory(ProcessParameters.CurrentDirectory)", errorMessagePtr); return NULL; } std::wstring currentDirectory = lpCurrentDir; delete[] lpCurrentDir; return _wcsdup(currentDirectory.c_str()); }