DLL Injection: Calling a function inside the dll - winapi

First of all some parts of the code are from Calling function in injected DLL but somewhere it doesn't work.
I have a question regarding DLL Injection: after I loaded the library into another process:
HANDLE InjectDLL(DWORD ProcessID, char *dllName)
{
HANDLE Proc;
char buf[50]={0};
LPVOID RemoteString, LoadLibAddy;
if(!ProcessID)
return NULL;
Proc = OpenProcess(CREATE_THREAD_ACCESS, FALSE, ProcessID);
if(!Proc)
{
sprintf(buf, "OpenProcess() failed: %d", GetLastError());
MessageBox(NULL, buf, "Loader", NULL);
return NULL;
}
LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
RemoteString = (LPVOID)VirtualAllocEx(Proc, NULL, strlen(dllName), MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(Proc, (LPVOID)RemoteString, dllName, strlen(dllName), NULL);
HANDLE hThread = CreateRemoteThread(Proc, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibAddy, (LPVOID)RemoteString, NULL, NULL);
if( hThread != 0 ) {
WaitForSingleObject( hThread, INFINITE );
GetExitCodeThread( hThread, ( LPDWORD )&hInjected );
CloseHandle( hThread );
}
CloseHandle(Proc);
return hThread != 0 ? Proc : NULL;
}
I wanted to call a function from inside that space:
void* GetPayloadExportAddr( LPCSTR lpPath, HMODULE hPayloadBase, LPCSTR lpFunctionName )
{
// Load payload in our own virtual address space
HMODULE hLoaded = LoadLibrary( lpPath );
if( hLoaded == NULL ) {
return NULL;
} else {
void* lpFunc = GetProcAddress( hLoaded, lpFunctionName );
DWORD dwOffset = (char*)lpFunc - (char*)hLoaded;
FreeLibrary( hLoaded );
return (void*)((DWORD)hPayloadBase + dwOffset);
}
}
BOOL InitPayload( HANDLE hProcess, LPCSTR lpPath, HMODULE hPayloadBase)
{
void* lpInit = GetPayloadExportAddr( lpPath, hPayloadBase, "Start" );
if( lpInit == NULL ) {
return FALSE;
}
else {
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)lpInit, (LPVOID) NULL, 0, NULL );
if( hThread == NULL ) {
return FALSE;
}
else {
CloseHandle( hThread );
}
}
return TRUE;
}
The GetPayloadExportAddr returns the Current Location from IDA (i guess that is the space where my function starts).
So the problem is at the InitPayload function when I try to create the new thread, it fails to do so and I don't know why.
My dll is the following:
extern "C"
{
__declspec(dllexport) void* Start(LPVOID param)
{
MessageBox(NULL, L"Start", L"Hello", MB_OK);
return NULL;
}
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
The thing is if I put the Start method at DLL_PROCESS_ATTACH it works, but otherwise it doesn't.

Your GetPayloadExportAddr() returns the address of the function in your local process. This address will not be the same in other processes if the base address of the module is different, which is common with DLL files which can be relocated if their PreferredImageBase is not available.
You should modify your GetPayloadExportAddr() function to return the offset. Then get the address of the module in the target process. Add these two together and that is the correct address for you to call in the target process.

Related

Inject Dll and Create Thread When Launching Process with DEBUG_PROCESS on Windows

In the past, when not operating as a debugger, I have used this approach to inject a DLL and Create Thread In a Process which has worked well for me. Note: I need this to work on Windows XP 32-bit only (Although prefer methods that also work on latested OS):
#include <iostream>
#include <Windows.h>
#include <Psapi.h>
#include <pathcch.h>
#include "log.h"
#include <wchar.h>
#pragma comment(lib,"Pathcch.lib")
typedef void (WINAPI* PHookInit)();
HMODULE WINAPI GetRemoteModuleHandle(HANDLE hProcess, LPCWSTR lpModuleName)
{
HMODULE* ModuleArray = NULL;
DWORD ModuleArraySize = 100;
DWORD NumModules = 0;
WCHAR lpModuleNameCopy[MAX_PATH] = { 0 };
WCHAR ModuleNameBuffer[MAX_PATH] = { 0 };
if (lpModuleName == NULL) return NULL;
ModuleArray = new HMODULE[ModuleArraySize];
if (ModuleArray == NULL) return NULL;
if (!EnumProcessModulesEx(hProcess, ModuleArray,
ModuleArraySize * sizeof(HMODULE), &NumModules, LIST_MODULES_ALL))
{
DWORD dwResult = GetLastError();
LOG_E("Unable to get modules in process Error %i", dwResult);
}
else
{
NumModules /= sizeof(HMODULE);
if (NumModules > ModuleArraySize)
{
delete[] ModuleArray;
ModuleArray = NULL;
ModuleArray = new HMODULE[NumModules];
if (ModuleArray != NULL)
{
ModuleArraySize = NumModules;
if (EnumProcessModulesEx(
hProcess,
ModuleArray,
ModuleArraySize * sizeof(HMODULE),
&NumModules,
LIST_MODULES_ALL))
{
NumModules /= sizeof(HMODULE);
}
}
}
}
for (DWORD i = 0; i <= NumModules; ++i)
{
GetModuleBaseNameW(hProcess, ModuleArray[i],
ModuleNameBuffer, MAX_PATH);
LOG_I("Module = '%s'", ModuleNameBuffer);
if (_wcsicmp(ModuleNameBuffer, lpModuleName) == 0)
{
LOG_I("Target module found!");
HMODULE TempReturn = ModuleArray[i];
delete[] ModuleArray;
return TempReturn;
}
}
if (ModuleArray != NULL)
delete[] ModuleArray;
return NULL;
}
int wmain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, INT nCmdShow)
{
LPWSTR* argv;
int argc;
argv = CommandLineToArgvW(GetCommandLineW(), &argc);
LOG_I(L"LaunchAndInject Started");
wchar_t CurrentProcessDirectory[MAX_PATH];
wchar_t TargetDllFilename[MAX_PATH];
#ifdef _WIN64
wchar_t TargetDllName[] = L"HookInit64.dll";
#else
wchar_t TargetDllName[] = L"HookInit32.dll";
#endif
char TargetFunctionName[] = "HookInit";
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD dwTimeOut = 60000;
if (argc < 2)
{
LOG_E(L"No command line parameters specified.");
return 1;
}
wchar_t* cmd_pos = wcsstr(GetCommandLine(), argv[1]) - 1;
if (cmd_pos)
{
if (cmd_pos[0] != L'"')
{
cmd_pos += 1;
}
}
LOG_I(L"Command Line='%s'", cmd_pos);
DWORD dwResult = GetModuleFileNameW(NULL, CurrentProcessDirectory, MAX_PATH);
PathCchRemoveFileSpec(CurrentProcessDirectory, MAX_PATH);
PathCchCombine(TargetDllFilename, MAX_PATH, CurrentProcessDirectory, TargetDllName);
LOG_I(L"Current Directory='%s' Result='%i'", CurrentProcessDirectory, dwResult);
LOG_I(L"Target DLL='%s'", TargetDllFilename);
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
cmd_pos, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
CREATE_SUSPENDED, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si,
&pi)
)
{
dwResult = GetLastError();
LOG_E(L"CreateProcess Failed with Error #%i", dwResult);
return 1;
}
LOG_I(L"Suspended Process created with PID '%i'", pi.dwProcessId);
LOG_I("Loading Target DLL");
// load DLL in this process first so we can calculate function offset
HMODULE hModuleTargetDll = LoadLibraryW(TargetDllFilename);
__int64 iTargetProcAddress = 0;
__int64 iTargetOffset = 0;
if (hModuleTargetDll != NULL)
{
iTargetProcAddress = (__int64)GetProcAddress(hModuleTargetDll, TargetFunctionName);
iTargetOffset = iTargetProcAddress - (__int64)hModuleTargetDll;
LOG_I("Function Target Offset = %i", iTargetOffset);
}
HMODULE hModuleKernel32 = GetModuleHandle(L"kernel32.dll");
LPVOID pLoadLibraryAddress = NULL;
if (hModuleKernel32 != NULL)
{
pLoadLibraryAddress = (LPVOID)GetProcAddress(hModuleKernel32, "LoadLibraryW");
}
else
{
LOG_E("Unable to get module handle for kernel32.dll");
}
if (pLoadLibraryAddress == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to find LoadLibraryW in Kernel32.dll Error: %i", dwResult);
}
// allocate space for LoadLibrary arguments in target process
size_t iTargetDllSize = (wcslen(TargetDllFilename) + 1) * sizeof(wchar_t);
LPVOID pLoadLibraryArguments = (LPVOID)VirtualAllocEx(
pi.hProcess,
NULL,
iTargetDllSize,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (pLoadLibraryArguments == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to allocate %i bytes in target process Error: %i",
iTargetDllSize,
dwResult);
}
else
{
if (!WriteProcessMemory(
pi.hProcess,
pLoadLibraryArguments,
TargetDllFilename,
iTargetDllSize,
NULL))
{
dwResult = GetLastError();
LOG_E("Unable to write bytes into target process address space. Error %i", dwResult);
}
else
{
LOG_I("LoadLibrary Arguments Successfully written to target process address space.");
HANDLE hThread = NULL;
if (pLoadLibraryAddress != NULL)
{
hThread = CreateRemoteThread(
pi.hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)pLoadLibraryAddress,
pLoadLibraryArguments,
NULL,
NULL);
}
if (hThread == NULL) {
dwResult = GetLastError();
LOG_E("The remote thread calling LoadLibrary could not be created. Error %i", dwResult);
}
else {
LOG_I("Remote Thread for LoadLibrary successfully created.");
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for LoadLibrary Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for LoadLibrary in hung state");
}
HMODULE hInjected = GetRemoteModuleHandle(pi.hProcess, TargetDllName);
PHookInit pHookInit = NULL;
if (hInjected == NULL)
{
LOG_E("Unable to get module handle in target process");
}
else
{
pHookInit = (PHookInit)((__int64)hInjected + iTargetOffset);
}
if (pHookInit != NULL)
{
LOG_I("Running HookInit function!");
hThread = CreateRemoteThread(pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pHookInit, NULL, NULL, NULL);
if (hThread == NULL)
{
dwResult = GetLastError();
LOG_E("The remote thread calling HookInit could not be created. Error %i", dwResult);
}
else
{
LOG_I("HookInit function started!");
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for HookInit Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for HookInit in hung state");
}
}
}
}
}
}
LOG_I("Resuming threads in target process");
ResumeThread(pi.hThread);
LOG_I("Process Resumed. Waiting for process to exit");
dwResult = WaitForSingleObject(pi.hProcess, INFINITE);
DWORD exitCode = 0;
if (GetExitCodeProcess(pi.hProcess, &exitCode))
{
LOG_I("Process Terminated with exit code %i", exitCode);
}
else
{
LOG_W("Process terminated, unable to determine Exit Code");
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
However in this case I need to capture various debug events of process, and hook the process via Image Execution Debugger registry key (as I don't have control of its launch), including for child processes. While the inject code works fine with previous approach, when launched as a debugger I'm trying to work out how to create the remote thread (and have it complete) before resuming main application execution. While I can create the remote thread fine, it hangs when trying to wait for its completion when using the approach below. I'm trying to work out what method to use to create my remote thread and wait for it to complete before resuming main application.
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
cmd_pos, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
DEBUG_PROCESS, // Debug
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si,
&pi)
)
{
dwResult = GetLastError();
LOG_E(L"CreateProcess Failed with Error #%i", dwResult);
return 1;
}
DebugSetProcessKillOnExit(TRUE);
DebugActiveProcess(pi.dwProcessId);
DEBUG_EVENT DebugEv = { 0 };
DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation
LOG_I(L"Debug Process created with PID '%i'", pi.dwProcessId);
LOG_I("Loading Target DLL");
/*
// load DLL in this process first so we can calculate function offset
*/
for (;;)
{
// Wait for a debugging event to occur. The second parameter indicates
// that the function does not return until a debugging event occurs.
WaitForDebugEvent(&DebugEv, INFINITE);
// Process the debugging event code.
switch (DebugEv.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
// Process the exception code. When handling
// exceptions, remember to set the continuation
// status parameter (dwContinueStatus). This value
// is used by the ContinueDebugEvent function.
OutputDebugString(L"EXCEPTION\r\n");
switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_ACCESS_VIOLATION:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
case EXCEPTION_BREAKPOINT:
// First chance: Display the current
// instruction and register values.
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
case EXCEPTION_SINGLE_STEP:
// First chance: Update the display of the
// current instruction and register values.
break;
case DBG_CONTROL_C:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
default:
// Handle other exceptions.
break;
}
break;
case CREATE_THREAD_DEBUG_EVENT:
OutputDebugString(L"CREATETHREAD\r\n");
// dwContinueStatus = OnCreateThreadDebugEvent(&DebugEv);
break;
case CREATE_PROCESS_DEBUG_EVENT:
dwContinueStatus = OnCreateProcessDebugEvent(&DebugEv);
break;
case EXIT_THREAD_DEBUG_EVENT:
// Display the thread's exit code.
OutputDebugString(L"EXITTHREAD\r\n");
// dwContinueStatus = OnExitThreadDebugEvent(&DebugEv);
break;
case EXIT_PROCESS_DEBUG_EVENT:
// Display the process's exit code.
OutputDebugString(L"EXITPROCESS\r\n");
// dwContinueStatus = OnExitProcessDebugEvent(&DebugEv);
break;
case LOAD_DLL_DEBUG_EVENT:
// Read the debugging information included in the newly
// loaded DLL. Be sure to close the handle to the loaded DLL
// with CloseHandle.
OutputDebugString(L"LOADDLL\r\n");
// dwContinueStatus = OnLoadDllDebugEvent(&DebugEv);
break;
case UNLOAD_DLL_DEBUG_EVENT:
// Display a message that the DLL has been unloaded.
OutputDebugString(L"UNLOADDLL\r\n");
// dwContinueStatus = OnUnloadDllDebugEvent(&DebugEv);
break;
case OUTPUT_DEBUG_STRING_EVENT:
OutputDebugString(L"OUTPUTDEBUG\r\n");
// Display the output debugging string.
// dwContinueStatus = OnOutputDebugStringEvent(&DebugEv);
break;
case RIP_EVENT:
OutputDebugString(L"RIP\r\n");
// dwContinueStatus = OnRipEvent(&DebugEv);
break;
}
// Resume executing the thread that reported the debugging event.
OutputDebugString(L"CONTINUE\r\n");
ContinueDebugEvent(DebugEv.dwProcessId,
DebugEv.dwThreadId,
dwContinueStatus);
}
DWORD OnCreateProcessDebugEvent(const LPDEBUG_EVENT DebugEv)
{
DWORD dwResult;
HMODULE hModuleTargetDll = LoadLibraryW(TargetDllFilename);
__int64 iTargetProcAddress = 0;
__int64 iTargetOffset = 0;
if (hModuleTargetDll != NULL)
{
iTargetProcAddress = (__int64)GetProcAddress(hModuleTargetDll, TargetFunctionName);
iTargetOffset = iTargetProcAddress - (__int64)hModuleTargetDll;
LOG_I("Function Target Offset = %i", iTargetOffset);
}
HMODULE hModuleKernel32 = GetModuleHandle(L"kernel32.dll");
LPVOID pLoadLibraryAddress = NULL;
if (hModuleKernel32 != NULL)
{
pLoadLibraryAddress = (LPVOID)GetProcAddress(hModuleKernel32, "LoadLibraryW");
}
else
{
LOG_E("Unable to get module handle for kernel32.dll");
}
if (pLoadLibraryAddress == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to find LoadLibraryW in Kernel32.dll Error: %i", dwResult);
}
// allocate space for LoadLibrary arguments in target process
size_t iTargetDllSize = (wcslen(TargetDllFilename) + 1) * sizeof(wchar_t);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, DebugEv->dwProcessId);
LPVOID pLoadLibraryArguments = (LPVOID)VirtualAllocEx(
hProcess,
NULL,
iTargetDllSize,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (pLoadLibraryArguments == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to allocate %i bytes in target process Error: %i",
iTargetDllSize,
dwResult);
}
else
{
if (!WriteProcessMemory(
hProcess,
pLoadLibraryArguments,
TargetDllFilename,
iTargetDllSize,
NULL))
{
dwResult = GetLastError();
LOG_E("Unable to write bytes into target process address space. Error %i", dwResult);
}
else
{
LOG_I("LoadLibrary Arguments Successfully written to target process address space.");
HANDLE hThread = NULL;
if (pLoadLibraryAddress != NULL)
{
hThread = CreateRemoteThread(
hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)pLoadLibraryAddress,
pLoadLibraryArguments,
NULL,
NULL);
}
if (hThread == NULL) {
dwResult = GetLastError();
LOG_E("The remote thread calling LoadLibrary could not be created. Error %i", dwResult);
}
else {
LOG_I("Remote Thread for LoadLibrary successfully created.");
ResumeThread(hThread);
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for LoadLibrary Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for LoadLibrary in hung state");
}
HMODULE hInjected = GetRemoteModuleHandle(hProcess, TargetDllName);
PHookInit pHookInit = NULL;
if (hInjected == NULL)
{
LOG_E("Unable to get module handle in target process");
}
else
{
pHookInit = (PHookInit)((__int64)hInjected + iTargetOffset);
}
if (pHookInit != NULL)
{
LOG_I("Running HookInit function!");
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pHookInit, NULL, NULL, NULL);
if (hThread == NULL)
{
dwResult = GetLastError();
LOG_E("The remote thread calling HookInit could not be created. Error %i", dwResult);
}
else
{
LOG_I("HookInit function started!");
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for HookInit Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for HookInit in hung state");
}
}
}
}
}
}
return DBG_CONTINUE;
}

Process32First is not returning TRUE even if process is running

I am trying to find the process running and killing it from my application.
Below is my code.
bool ProcessKill()
{
PROCESSENTRY32 process;
Logger::getInstance()->test(L" checking the call kill status");
ZeroMemory(&process, sizeof(process));
process.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if( snapshot == INVALID_HANDLE_VALUE )
{
Logger::getInstance()->test( TEXT(" CreateToolhelp32Snapshot (of processes)") );
return( FALSE );
}
Logger::getInstance()->test(L" checking the call kill status 2222");
if (Process32First(snapshot, & process) != FALSE)
{
Logger::getInstance()->test(L"checking the kill status");
while(Process32Next(snapshot, &process) == TRUE)
{ Logger::getInstance()->test(L"1111");
if (wcsicmp(process.szExeFile, L"kill.exe") == 0)
{
Logger::getInstance()->test(L"found kill running");
HANDLE hProcess_Name = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process.th32ProcessID);
DWORD dwRet = ::WaitForSingleObject(hProcess_Name, 5000);
if (dwRet == WAIT_OBJECT_0)
dwRet = ERROR_SUCCESS;
//Logger::getInstance()->test(L"kill is running");
else
{
::TerminateProcess(hProcess_Name, 1);
::CloseHandle(hProcess_Name);
Logger::getInstance()->test(L"kill is terminated");
CloseHandle(snapshot);
return 1;
}
}
}
}
else
{
Logger::getInstance()->test(L" Process32First the call kill status 333");
}
CloseHandle(snapshot);
return 0;
}
What I am observing that Process32First is failing it is returning false.
Can any one let me know what is the reason behind the failure and how it can be fixed. Any work around for this.
Add bellow statement before if (Process32First(snapshot, & process) != FALSE) :
pe32.dwSize = sizeof(PROCESSENTRY32W);
As said in MSDN :
tagPROCESSENTRY32 struct :
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
CHAR szExeFile[MAX_PATH];
} PROCESSENTRY32;
dwSize
Before calling the Process32First function, set this member to sizeof(PROCESSENTRY32). If you do not initialize dwSize, Process32First fails.

Translating boost::thread->native_handle() to XP ThreadId

I've managed to get the Windows ThreadId out of the native_handle() from a boost::thread by using GetThreadId(HANDLE). Sadly that call is not available on Windows XP and after searching around I found the solution to offer als fallback support for XP by traversing all thread via Thread32First() and Thread32Next() functions of the WINAPI.
This does work somehow but my problem is I'm currently only able to identify the threads of my process... I don't now how to match the native_handle() / HANDLE from one side with the appropriate THREADENTRY32 from the loop traversal.
THREADENTRY32 te32;
//...
do {
if( te32.th32OwnerProcessID == GetCurrentProcessId() ) {
DWORD threadId = te32.th32ThreadID;
printf( "\n THREAD ID = 0x%08X", te32.th32ThreadID );
}
} while( Thread32Next(hThreadSnap, &te32 ) );
Can anyone help me with that? How do I convert a boost::thread->native_handle() to the ThreadId on WindowsXP?
Thank you very much!
Pass each thread ID in the loop to OpenThread() until you find a matching HANDLE. For example:
HANDLE hBoostThread = ...; // from boost::thread->native_handle()
DWORD dwBoostThreadID = 0;
THREADENTRY32 te32;
//...
do
{
if( te32.th32OwnerProcessID == GetCurrentProcessId() )
{
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
if (hThread != NULL)
{
if (hThread == hBoostThread)
{
CloseHandle(hThread);
dwBoostThreadID = te32.th32ThreadID;
break;
}
CloseHandle(hThread);
}
}
}
while( Thread32Next(hThreadSnap, &te32 ) );
For good measure, you can wrap this inside a function that you can call whenever GetThreadId() is not natively available so that your code does not need to know the difference, eg:
DWORD WINAPI MyGetThreadId(HANDLE Thread)
{
THREADENTRY32 te32;
HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hThreadSnap == INVALID_HANDLE_VALUE)
return 0;
if (Thread32First(hThreadSnap, &te32))
{
do
{
HANDLE hOpenThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
if (hOpenThread != NULL)
{
if (hOpenThread == Thread)
{
CloseHandle(hOpenThread);
CloseHandle(hThreadSnap);
return te32.th32ThreadID;
}
CloseHandle(hOpenThread);
}
}
while( Thread32Next(hThreadSnap, &te32 ) );
}
CloseHandle(hThreadSnap);
return 0;
}
typedef DWORD (WINAPI *LPFN_GTID)(HANDLE);
LPFN_GTID lpGetThreadId = (LPFN_GTID) GetProcAddress(GetModuleHandle("kernel32"), "GetThreadId");
if (!lpGetThreadId)
lpGetThreadId = &MyGetThreadId;
DWORD dwThreadID = lpGetThreadId((HANDLE) boost::thread->native_handle());
With that said, a better option is to directly query the target thread itself for its own ID, instead of trying to hunt for it manually:
typedef long (WINAPI *LPFN_NTQIT)(HANDLE thread, int infoclass, void *buf, long size, long *used);
typedef struct _THREAD_BASIC_INFORMATION
{
ULONG ExitStatus;
void* TebBaseAddress;
ULONG UniqueProcessId;
ULONG UniqueThreadId;
ULONG AffinityMask;
ULONG BasePriority;
ULONG DiffProcessPriority;
} THREAD_BASIC_INFORMATION;
DWORD WINAPI MyGetThreadId(HANDLE Thread)
{
DWORD dwThreadId = 0;
HMODULE hLib = LoadLibrary("ntdll.dll");
if (hLib != NULL)
{
LPFN_NTQIT lpNtQueryInformationThread = (LPFN_NTQIT) GetProcAddress(hLib, "NtQueryInformationThread");
if (lpNtQueryInformationThread != NULL)
{
THREAD_BASIC_INFORMATION tbi = {0};
ULONG used = 0;
if (lpNtQueryInformationThread(Thread, 0, &tbi, sizeof(tbi), &used) == 0)
dwThreadId = tbi.UniqueThreadId;
}
FreeLibrary(hLib);
}
return dwThreadId;
}

Why does DebugActiveProcessStop crash my debugging app?

I have a debugging program which I've written to attach to a process and create a crash dump file. That part works fine.
The problem I have is that when the debugger program terminates, so does the program that it was debugging.
I did some Googling and found the DebugActiveProcessStop() API call. This didn't show up in my older MSDN documentation as it was only introduced in Windows XP so I've tried loading it dynamicall from Kernel32.dll at runtime.
Now my problem is that my debugger program crashes as soon as the _DebugActiveProcessStop() call is made. Can somebody please tell me what I'm doing wrong?
typedef BOOL (*DEBUGACTIVEPROCESSSTOP)(DWORD);
DEBUGACTIVEPROCESSSTOP _DebugActiveProcessStop;
HMODULE hK32 = LoadLibrary( "kernel32.dll" );
if( hK32 )
_DebugActiveProcessStop = (DEBUGACTIVEPROCESSSTOP) GetProcAddress( hK32,"DebugActiveProcessStop" );
else
{
printf( "Can't load Kernel32.dll\n" );
return;
}
if( ! _DebugActiveProcessStop )
{
printf( "Can't find DebugActiveProcessStop\n" );
return;
}
...
void DebugLoop( void )
{
DEBUG_EVENT de;
while( 1 )
{
WaitForDebugEvent( &de, INFINITE );
switch( de.dwDebugEventCode )
{
case CREATE_PROCESS_DEBUG_EVENT:
hProcess = de.u.CreateProcessInfo.hProcess;
break;
case EXCEPTION_DEBUG_EVENT:
// PDS: I want a crash dump immediately!
dwProcessId = de.dwProcessId;
dwThreadId = de.dwThreadId;
WriteCrashDump( &de.u.Exception );
return;
case CREATE_THREAD_DEBUG_EVENT:
case OUTPUT_DEBUG_STRING_EVENT:
case EXIT_THREAD_DEBUG_EVENT:
case EXIT_PROCESS_DEBUG_EVENT :
case LOAD_DLL_DEBUG_EVENT:
case UNLOAD_DLL_DEBUG_EVENT:
case RIP_EVENT:
default:
break;
}
ContinueDebugEvent( de.dwProcessId, de.dwThreadId, DBG_CONTINUE );
}
}
...
void main( void )
{
...
BOOL bo = DebugActiveProcess( dwProcessId );
if( bo == 0 )
printf( "DebugActiveProcess failed, GetLastError: %u \n",GetLastError() );
hProcess = OpenProcess( PROCESS_ALL_ACCESS, TRUE, dwProcessId );
if( hProcess == NULL )
printf( "OpenProcess failed, GetLastError: %u \n",GetLastError() );
DebugLoop();
_DebugActiveProcessStop( dwProcessId );
CloseHandle( hProcess );
}
The reason its crashing is because I missed out the WINAPI keyword on my function pointer definitions.
This works:
typedef BOOL (WINAPI *DEBUGSETPROCESSKILLONEXIT)(BOOL);

Asynchronous NtQueryDirectoryFile?

Has anyone managed to figure out how asynchronous calls to NtQueryDirectoryFile work?
By an asynchronous call, I mean calling NtQueryDirectoryFile on directories not opened with FILE_SYNCHRONOUS_IO_ALERT or with FILE_SYNCHRONOUS_IO_NONALERT.
For me, it seems to return STATUS_PENDING just like a normal NtReadFile request does on a normal file, but when I tried using NtWaitForSingleObject on the directory, it didn't end properly, and I still don't get all the data... why does this happen?
As far as I know, none of the Windows filesystems support asynchronous query directory calls.
The Win32 APIs never call NtQueryDirectoryFile asnchronously, so support for it is hit-or-miss.
NTFS theoretically supports asynchronous NtQueryDirectoryFile but (as I mentioned) it is not extensively tested so it may not work.
You response indicated that you called WaitForSingleObject on the directory - that's not how the async pattern works in NT - you need to call WaitForSingleObject on the event handle provided as a parameter to NtQueryDirectoryFile.
This update is a result of asking the NTFS developer for more information, he tested this scenario on his machine and it worked for him (on Windows 7).
NtQueryDirectoryFile works well in asynchronous!
pass callback in ApcRoutine, and callback data in ApcContext
asynchronous procedure calls only call when the thread is in alertable state(for example: calling SleepEx(INFINITE, TRUE), WSAaccept)
this program shows how asynchronous NtQueryDirectoryFile work.
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <stdio.h>
#include <winternl.h>
#include <winnt.h>
#define LIST_DIR_SIZE 2000
#define STATUS_NO_MORE_FILES ((NTSTATUS)80000006)
typedef struct _FILE_NAMES_INFORMATION {
ULONG NextEntryOffset;
ULONG FileIndex;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_NAMES_INFORMATION, * PFILE_NAMES_INFORMATION;
typedef struct {
HANDLE hFile;
OVERLAPPED ol;
DECLSPEC_ALIGN(4) FILE_NAMES_INFORMATION buf[LIST_DIR_SIZE];
IO_STATUS_BLOCK iob;
bool finished;
} LIST_DIR_DATA, * PLIST_DIR_DATA; // my private data
__kernel_entry NTSYSCALLAPI
NTSTATUS
NTAPI
NtQueryDirectoryFile(
_In_ HANDLE FileHandle,
_In_opt_ HANDLE Event,
_In_opt_ PIO_APC_ROUTINE ApcRoutine,
_In_opt_ PVOID ApcContext,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_Out_writes_bytes_(Length) PVOID FileInformation,
_In_ ULONG Length,
_In_ FILE_INFORMATION_CLASS FileInformationClass,
_In_ BOOLEAN ReturnSingleEntry,
_In_opt_ PUNICODE_STRING FileName,
_In_ BOOLEAN RestartScan
);
#define NTDLL_extern(s) typedef decltype(&s) s##T;s##T s##F;
#define NTDLL_import(s) s##F = (s##T)GetProcAddress(ntdll, #s);
NTDLL_extern(NtOpenFile);
NTDLL_extern(NtQueryDirectoryFile);
NTDLL_extern(NtClose);
NTDLL_extern(RtlInitUnicodeString);
HMODULE ntdll;
VOID NTAPI callback(
IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG Reserved) {
UNREFERENCED_PARAMETER(Reserved);
PFILE_NAMES_INFORMATION file_info = ((PLIST_DIR_DATA)ApcContext)->buf;
do {
fputws(file_info->FileName, stdout);
putwchar(L'\t');
file_info = (PFILE_NAMES_INFORMATION)((char*)file_info + file_info->NextEntryOffset);
} while (file_info->NextEntryOffset);
fputws(file_info->FileName, stdout);
putwchar(L'\t');
PLIST_DIR_DATA c = (PLIST_DIR_DATA)ApcContext;
if (IoStatusBlock->Information != 0) {
NTSTATUS status = NtQueryDirectoryFileF(
c->hFile,
NULL,
callback,
ApcContext,
&c->iob,
c->buf,
sizeof(c->buf),
FILE_INFORMATION_CLASS(12),
FALSE, NULL, FALSE);
switch (status) {
case STATUS_PENDING:
break;
default:
fputs("warning: status != STATUS_PENDING", stderr);
}
}
else {
c->finished = true;
}
}
BOOL init() {
ntdll = LoadLibraryW(L"NtDLL.dll");
if (ntdll == NULL) {
fputs("LoadLibraryW", stderr);
return FALSE;
}
NTDLL_import(NtQueryDirectoryFile);
NTDLL_import(NtOpenFile);
NTDLL_import(NtClose);
NTDLL_import(RtlInitUnicodeString);
if (NtCloseF != NULL && NtOpenFileF != NULL && NtCloseF != NULL) {
return TRUE;
}
else {
fputs("GetProcAddress", stderr);
return FALSE;
}
}
int main() {
if (init() == FALSE) {
fputs("error: init() failed!", stderr);
return -1;
}
NTSTATUS status;
PLIST_DIR_DATA data = new LIST_DIR_DATA{};
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING s;
RtlInitUnicodeStringF(&s, L"\\??\\c:\\Windows\\System32");
InitializeObjectAttributes(
&ObjectAttributes,
&s,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = NtOpenFileF(
&data->hFile,
FILE_READ_DATA | FILE_LIST_DIRECTORY, // | FILE_TRAVERSE | SYNCHRONIZE
&ObjectAttributes,
&data->iob,
FILE_SHARE_READ,
FILE_DIRECTORY_FILE); // | FILE_SYNCHRONOUS_IO_NONALERT
}
if (status < 0 || data->hFile == NULL) {
fputs("error: NtOpenFile failed", stderr);
return -2;
}
status = NtQueryDirectoryFileF(
data->hFile,
NULL,
callback,
data,
&data->iob,
data->buf,
sizeof(data->buf),
FILE_INFORMATION_CLASS(12),
FALSE, NULL, FALSE);
switch (status) {
case STATUS_PENDING:
break;
default:
fputs("warning: status != STATUS_PENDING", stderr);
}
for (;data->finished==false;) SleepEx(INFINITE, TRUE); // put main thread into alertable wait
NtCloseF(data->hFile);
FreeLibrary(ntdll);
return 0;
}
if you want UTF-8 output, try this (note: recommand use support UTF-8 terminal)
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <winternl.h>
#include <winnt.h>
#include <crtdbg.h>
#include <cstdio>
#define LIST_DIR_SIZE 200
#define STATUS_NO_MORE_FILES ((NTSTATUS)80000006)
typedef struct _FILE_NAMES_INFORMATION {
ULONG NextEntryOffset;
ULONG FileIndex;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_NAMES_INFORMATION, * PFILE_NAMES_INFORMATION;
typedef struct {
HANDLE hFile;
OVERLAPPED ol;
DECLSPEC_ALIGN(4) FILE_NAMES_INFORMATION buf[LIST_DIR_SIZE];
IO_STATUS_BLOCK iob;
bool finished;
} LIST_DIR_DATA, * PLIST_DIR_DATA; // my private data
__kernel_entry NTSYSCALLAPI
NTSTATUS
NTAPI
NtQueryDirectoryFile(
_In_ HANDLE FileHandle,
_In_opt_ HANDLE Event,
_In_opt_ PIO_APC_ROUTINE ApcRoutine,
_In_opt_ PVOID ApcContext,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_Out_writes_bytes_(Length) PVOID FileInformation,
_In_ ULONG Length,
_In_ FILE_INFORMATION_CLASS FileInformationClass,
_In_ BOOLEAN ReturnSingleEntry,
_In_opt_ PUNICODE_STRING FileName,
_In_ BOOLEAN RestartScan
);
#define NTDLL_extern(s) typedef decltype(&s) s##T;s##T s##F;
#define NTDLL_init(s) s##F = (s##T)GetProcAddress(ntdll, #s);
NTDLL_extern(NtOpenFile);
NTDLL_extern(NtQueryDirectoryFile);
NTDLL_extern(NtClose);
NTDLL_extern(RtlInitUnicodeString);
HMODULE ntdll;
HANDLE heap;
VOID NTAPI callback(
IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG Reserved) {
UNREFERENCED_PARAMETER(Reserved);
PLIST_DIR_DATA c = (PLIST_DIR_DATA)ApcContext;
if (IoStatusBlock->Information){
PFILE_NAMES_INFORMATION file_info = c->buf;
ULONG_PTR length = 0;
ULONG last;
do {
last = file_info->NextEntryOffset;
file_info->FileNameLength /= 2; // wide char length always base of 2 in bytes
length += (
file_info->FileIndex=WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS,
file_info->FileName, file_info->FileNameLength,
NULL, 0,
NULL, NULL)
)+1;
if (file_info->FileIndex == 0) { // FileIndex is how many byte is the UTF-8 string
_RPTF0(_CRT_WARN, "WideCharToMultiByte failed!");
}
file_info = (PFILE_NAMES_INFORMATION)((char*)file_info + file_info->NextEntryOffset);
} while (last);
LPSTR pData = (LPSTR)HeapAlloc(heap, HEAP_NO_SERIALIZE, length), ptr=pData;
if (ptr == NULL) {
_RPTF0(_CRT_ERROR, "HeapAlloc failed!");
return;
}
file_info = c->buf;
do {
last = file_info->NextEntryOffset;
if (WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS,
file_info->FileName, file_info->FileNameLength,
pData, file_info->FileIndex,
NULL, NULL)==0) {
_RPTF0(_CRT_WARN, "WideCharToMultiByte failed!");
}
pData += file_info->FileIndex;
*pData++ = '\n';
file_info = (PFILE_NAMES_INFORMATION)((char*)file_info + file_info->NextEntryOffset);
} while (last);
// use data here
fwrite(ptr, length, 1, stdout);
// use data here
HeapFree(heap, HEAP_NO_SERIALIZE, ptr);
NTSTATUS status = NtQueryDirectoryFileF(
c->hFile,
NULL,
callback,
ApcContext,
&c->iob,
c->buf,
sizeof(c->buf),
FILE_INFORMATION_CLASS(12),
FALSE, NULL, FALSE);
switch (status) {
case STATUS_PENDING:
break;
default:
_RPTF0(_CRT_WARN, "status != STATUS_PENDING");
}
}else{
c->finished = true;
}
}
BOOL init() {
ntdll = LoadLibraryW(L"NtDLL.dll");
if (ntdll == NULL) {
_RPTF0(_CRT_ERROR, "fail to load NtDLL.dll");
return FALSE;
}
NTDLL_init(NtQueryDirectoryFile);
NTDLL_init(NtOpenFile);
NTDLL_init(NtClose);
NTDLL_init(RtlInitUnicodeString);
if (NtCloseF != NULL &&
NtOpenFileF != NULL &&
NtCloseF != NULL &&
(heap = HeapCreate(HEAP_NO_SERIALIZE, 4096,0))!=NULL
){
return TRUE;
}
else {
_RPTF0(_CRT_ERROR, "failed to load function and create heap");
return FALSE;
}
}
int main() {
if (init() == FALSE) {
_RPTF0(_CRT_ERROR, "init failed");
return -1;
}
SetConsoleCP(CP_UTF8);
NTSTATUS status;
PLIST_DIR_DATA data = new LIST_DIR_DATA{};
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING s;
RtlInitUnicodeStringF(&s, L"\\??\\c:\\Users");
InitializeObjectAttributes(
&ObjectAttributes,
&s,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = NtOpenFileF(
&data->hFile,
FILE_READ_DATA | FILE_LIST_DIRECTORY, // | FILE_TRAVERSE | SYNCHRONIZE
&ObjectAttributes,
&data->iob,
FILE_SHARE_READ,
FILE_DIRECTORY_FILE); // | FILE_SYNCHRONOUS_IO_NONALERT
}
if (status < 0 || data->hFile == NULL) {
_RPTF0(_CRT_ERROR, "NtOpenFile failed!");
return -2;
}
status = NtQueryDirectoryFileF(
data->hFile,
NULL,
callback,
data,
&data->iob,
data->buf,
sizeof(data->buf),
FILE_INFORMATION_CLASS(12),
FALSE, NULL, FALSE);
switch (status) {
case STATUS_PENDING:
break;
default:
_RPTF0(_CRT_WARN, "status != STATUS_PENDING");
}
for (;data->finished==false;) SleepEx(INFINITE, TRUE); // put main thread into alertable wait
if (NtCloseF(data->hFile)<0) {
_RPTF0(_CRT_ERROR, "NtClose failed!");
}
if (FreeLibrary(ntdll) == FALSE) {
_RPTF0(_CRT_WARN, "failed to Free libary");
}
if (HeapDestroy(heap) == FALSE) {
_RPTF0(_CRT_WARN, "fail to destroy heap");
}
}

Resources