Exceptions constantly thrown when launching app with my custom WINAPI Debugger - c++

I'm in the process of experimenting with creating my own custom debugger with C++ in Visual Studio 2017. Testing a few console applications it is fine. However when I launch notepad with it, it is OK until I hit File -> Open dialog and it goes into constant loop with output these two exceptions code and the open dialog box doesn't open:
Exception: 3221356611
Exception: 998
When same process was launched under WinDbg these exceptions didn't occur.
The code is compiled as x86 and launching 32-bit process on Windows 10 1803 build 17134.523 x64.
Any suggestions on what might cause these?
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <string>
#include <iostream>
#include <map>
std::map < LPVOID, std::wstring > DllNameMap;
int main()
{
std::wstring filename(L"c:\\windows\\syswow64\\notepad.exe");
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
DEBUG_EVENT debugEvent;
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
(LPWSTR)filename.c_str(), // 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, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
printf("CreateProcess failed (%d).\n", GetLastError());
return -1;
}
if (DebugActiveProcess(pi.dwProcessId))
{
ResumeThread(pi.hThread);
std::cout << "Debugger attached!" << std::endl;
EnterDebugLoop(&debugEvent,pi.hProcess);
}
return 0;
}
void EnterDebugLoop(const LPDEBUG_EVENT DebugEv,HANDLE hProcess)
{
DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation
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.
std::cout << "Exception: " << DebugEv->u.Exception.ExceptionRecord.ExceptionCode << std::endl;
switch (DebugEv->u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_ACCESS_VIOLATION:
std::cout << "ACCESS VIOLATION" << std::endl;
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
case EXCEPTION_BREAKPOINT:
std::cout << "BREAKPOINT" << std::endl;
// First chance: Display the current
// instruction and register values.
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
std::cout << "DATATYPE MISALIGNMENT" << std::endl;
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
case EXCEPTION_SINGLE_STEP:
std::cout << "SINGLE STEP" << std::endl;
// First chance: Update the display of the
// current instruction and register values.
break;
case DBG_CONTROL_C:
std::cout << "CTRL+C" << std::endl;
// 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:
std::cout << "Create Thread" << std::endl;
// As needed, examine or change the thread's registers
// with the GetThreadContext and SetThreadContext functions;
// and suspend and resume thread execution with the
// SuspendThread and ResumeThread functions.
break;
case CREATE_PROCESS_DEBUG_EVENT:
std::cout << "Create Process" << std::endl;
// As needed, examine or change the registers of the
// process's initial thread with the GetThreadContext and
// SetThreadContext functions; read from and write to the
// process's virtual memory with the ReadProcessMemory and
// WriteProcessMemory functions; and suspend and resume
// thread execution with the SuspendThread and ResumeThread
// functions. Be sure to close the handle to the process image
// file with CloseHandle.
//dwContinueStatus = OnCreateProcessDebugEvent(DebugEv);
break;
case EXIT_THREAD_DEBUG_EVENT:
// Display the thread's exit code.
std::cout << "Exit Thread Exit Code " << DebugEv->u.ExitThread.dwExitCode << std::endl;
//dwContinueStatus = OnExitThreadDebugEvent(DebugEv);
break;
case EXIT_PROCESS_DEBUG_EVENT:
// Display the process's exit code.
std::cout << "Exit process Exit Code " << DebugEv->u.ExitProcess.dwExitCode << std::endl;
///dwContinueStatus = OnExitProcessDebugEvent(DebugEv);
break;
case LOAD_DLL_DEBUG_EVENT:
{
PVOID pDllPath = NULL;
PUCHAR DllPath[(MAX_PATH + 1) * sizeof(WCHAR)];
DWORD dwLen = 0;
ZeroMemory(DllPath, sizeof(DllPath));
if (DebugEv->u.LoadDll.lpImageName == NULL)
{
break;
}
// read DLL name pointer value
if (ReadProcessMemory(
hProcess,
DebugEv->u.LoadDll.lpImageName,
&pDllPath, sizeof(PVOID),
&dwLen) && pDllPath)
{
dwLen = (DebugEv->u.LoadDll.fUnicode ? MAX_PATH * sizeof(WCHAR) : MAX_PATH);
// read DLL name
if (ReadProcessMemory(
hProcess,
pDllPath,
DllPath, dwLen,
&dwLen))
{
char szDllPath[MAX_PATH], *lpszDllName = NULL;
if (DebugEv->u.LoadDll.fUnicode)
{
std::wstring path((wchar_t*)DllPath);
DllNameMap.insert(std::make_pair(DebugEv->u.LoadDll.lpBaseOfDll, path));
std::wcout << "Image loaded (Unicode): " << path.c_str() << std::endl;
}
else
{
// todo: Add to DllNameMAp
std::wcout << "Image loaded: " << DllPath << std::endl;
}
}
else
{
std::cout << "Error processing memory : " << GetLastError() << std::endl;
}
}
else
{
std::wcout << "ERROR reading process memory : " << GetLastError() << std::endl;
}
}
// Read the debugging information included in the newly
// loaded DLL. Be sure to close the handle to the loaded DLL
// with CloseHandle.
///dwContinueStatus = OnLoadDllDebugEvent(DebugEv);
break;
case UNLOAD_DLL_DEBUG_EVENT:
std::wcout << "Unload DLL: " << DllNameMap[DebugEv->u.UnloadDll.lpBaseOfDll] << std::endl;
break;
case OUTPUT_DEBUG_STRING_EVENT:
// Display the output debugging string.
std::wcout << "Debug Event" << std::endl;
if (DebugEv->u.DebugString.fUnicode)
{
std::wcout << (wchar_t)DebugEv->u.DebugString.lpDebugStringData << std::endl;
}
//dwContinueStatus = OnOutputDebugStringEvent(DebugEv);
break;
case RIP_EVENT:
//dwContinueStatus = OnRipEvent(DebugEv);
break;
}
// Resume executing the thread that reported the debugging event.
ContinueDebugEvent(DebugEv->dwProcessId,
DebugEv->dwThreadId,
dwContinueStatus);
}
}

3221356611 this is 0xC0020043 - RPC_NT_INTERNAL_ERROR
998 this is 0x3e6 - ERROR_NOACCESS (Invalid access to memory
location)
your main error that you unconditionally return DBG_CONTINUE always. but on EXCEPTION_DEBUG_EVENT you mast return this code only if you handle exception. otherwise (and if dwFirstChance == TRUE so this is first chance exception) you must return DBG_EXCEPTION_NOT_HANDLED. if you return DBG_CONTINUE - the program begin continue execute from current context. if you return DBG_EXCEPTION_NOT_HANDLED - the KiUserExceptionDispatcher will be called in target process, which call RtlDispatchException and where will be called exception handlers. read more - Structured Exception Handling
but because you never return DBG_EXCEPTION_NOT_HANDLED - program exception handlers never called. by your 2 exceptions codes even easy locate place, where this happens:
the RpcpRaiseException is called, which internal call RaiseException(ERROR_NOACCESS..) so you view 998 exception. if you return DBG_EXCEPTION_NOT_HANDLED here - application byself handle this exception and never return from RaiseException call. context will be swithched to __except{} block. but because you return DBG_CONTINUE - the RaiseException return control and RpcReportFatalError called, which internal call RaiseException(RPC_NT_INTERNAL_ERROR..) so you and view 3221356611 (0xC0020043)
your next error that you not close hFile on LOAD_DLL_DEBUG_EVENT - When the debugger is finished with this file, it should close the handle using the CloseHandle function. the same error on CREATE_PROCESS_DEBUG_EVENT
also your error in how you begin debug process - you must not use CREATE_SUSPENDED flag, must not use DebugActiveProcess and ResumeThread. you need only set DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS and all. not need create process in suspended state and main not call DebugActiveProcess here. this api call is bad designed, it create additional thread in target process. for say xp at this stage this at all was fatal.

Related

WINAPI Cpp - OpenFileMapping fails with error (2) ERROR_FILE_NOT_FOUND [duplicate]

This question already has an answer here:
OpenFileMapping issues, can't find filemap
(1 answer)
Closed last year.
I'm trying to use WinApi to CreateFileMapping, MapViewOfFile and CopyMemory. It's not showhing me errors and buffor is being filed with my PID
int write_pid_to_memory(const char *t_pid)
{
_tprintf(TEXT("[write_pid_to_memory] t_pid: (%s).\n"), t_pid);
HANDLE h_map_file;
LPCTSTR p_buf;
h_map_file = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file
NULL, // default security
PAGE_READWRITE, // read/write access
0, // maximum object size (high-order DWORD)
BUF_SIZE, // maximum object size (low-order DWORD)
t_name); // name of mapping object
if (h_map_file == NULL)
{
_tprintf(TEXT("[write_pid_to_memory] Could not create file mapping object (%d).\n"),
GetLastError());
return 1;
}
p_buf = (LPTSTR)MapViewOfFile(
h_map_file, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (p_buf == NULL)
{
_tprintf(TEXT("[write_pid_to_memory] Could not map view of file (%d).\n"),
GetLastError());
CloseHandle(h_map_file);
return 1;
}
std::cout << "[write_pid_to_memory] strlen(t_pid) * sizeof(char) " << strlen(t_pid) * sizeof(char) << std::endl;
CopyMemory((PVOID)p_buf, t_pid, (strlen(t_pid) * sizeof(char)));
_getch();
std::cout << "p_buf " << p_buf << std::endl;
UnmapViewOfFile(p_buf);
CloseHandle(h_map_file);
return 0;
}
... but then there is reading from memmory
int access_pid_from_memory()
{
HANDLE h_map_file;
LPCTSTR p_buf;
h_map_file = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // read/write access
FALSE, // do not inherit the name
t_name); // name of mapping object
if (h_map_file == NULL)
{
_tprintf(TEXT("[access_pid_from_memory] Could not open file mapping object (%d).\n"),
GetLastError());
return 1;
}
p_buf = (LPTSTR)MapViewOfFile(
h_map_file, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (p_buf == NULL)
{
_tprintf(TEXT("[access_pid_from_memory] Could not map view of file (%d).\n"),
GetLastError());
CloseHandle(h_map_file);
return 1;
}
MessageBox(NULL, p_buf, TEXT("[access_pid_from_memory] Process2"), MB_OK);
UnmapViewOfFile(p_buf);
CloseHandle(h_map_file);
return 0;
}
where I get System Error (2) while trying to open Mapping.
My PID: 19516
[access_pid_from_memory] Could not open file mapping object (2).
[write_pid_to_memory] t_pid: (19516).
[write_pid_to_memory] strlen(t_pid) * sizeof(char) 5
p_buf 19516
Envariamental variable = NEW
Env var value length = 3
Env var value compare resault = 0
Mutex created sucesfully
Code of those functions is from
https://learn.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory
and only thing I've changed is
CopyMemory((PVOID)p_buf, t_pid, (strlen(t_pid) * sizeof(char)));
Instead of
CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));
Where t_pid is just a const char *, becouse I was tired of Windows TCHAR types and I had no clue how to convert DWORD ProcessID to TCHAR to pass it to memcopy.
Well, I'm clueless why I'm unable to open Mapping. Windows is probably beyond me and I have no idea
how
TCHAR t_name[] = TEXT("Global\\MyFileMappingObject");
is supposed to be recognised by system to find memory from which I want to read a message.
Whole programm is supposed to lock execution for only one process and if there is a System variable named "SO2" of value "NEW", new process should stop execution of previous process and continoue locking program for himself.
Locking mechanism is with mutex and to find previous porcess ID, I wanted my current process ID to be saved in memory, for next process to read it form, to close it when sys var will be "NEW".
Nothing crazy. All of this in Linux I've done in one day, but Windows is killing me.
Please help
There is main if someone would be intrested:
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <windows.h>
#include <string>
#include <conio.h>
#include <tchar.h>
#define BUFFER_SIZE 2048
#define ENV_KEY "SO2"
#define ENV_VAL "NEW"
#define BUF_SIZE 256
TCHAR t_name[] = TEXT("Global\\MyFileMappingObject");
HANDLE h_mutex;
int write_pid_to_memory(const char *dw_pid);
int access_pid_from_memory();
int main(int argc, char **argv)
{
DWORD dw_pid = GetCurrentProcessId();
std::stringstream stream;
stream << dw_pid;
const char *t_pid = stream.str().c_str();
// int legnth = s_pid.length()
// const char *t_pid = (char*)malloc( * sizeof(char));
// const char t_pid = (char)malloc(strlen(dw_pid) * sizeof(char));
std::cout << "My PID: " << dw_pid << std::endl;
access_pid_from_memory();
write_pid_to_memory(t_pid);
std::string env_val(ENV_VAL);
char c_buffer[BUFFER_SIZE];
LPCSTR lp_name = ENV_KEY;
LPSTR lp_buffer = c_buffer;
DWORD dw_size = BUFFER_SIZE;
DWORD get_env_var;
//Write to memory your pid for other process to access it and close you
get_env_var = GetEnvironmentVariable(
lp_name,
lp_buffer,
dw_size);
if (GetLastError() == ERROR_ENVVAR_NOT_FOUND)
{
std::cout << "Couldn't find envariamental variable \"SO2\"." << std::endl;
}
if (BUFFER_SIZE == get_env_var)
{
std::cout << "Buffer for function [GetEnvironmentVariable] is too small. Function failed." << std::endl;
}
std::cout << "Envariamental variable = " << lp_buffer << std::endl;
std::string str_buffer(lp_buffer);
std::cout << "Env var value length = " << str_buffer.length() << std::endl;
std::cout << "Env var value compare resault = " << str_buffer.compare(env_val) << std::endl;
HANDLE h_mutex = NULL;
LPCSTR str = ENV_KEY;
h_mutex = OpenMutex(
MUTEX_ALL_ACCESS,
TRUE,
str);
if (NULL != h_mutex)
{
if (str_buffer.compare(env_val) == 0)
{
//Realease mutex3
ReleaseMutex(h_mutex);
//Close previous process
}
else
{
throw std::runtime_error("Instance of a program is already running");
}
}
h_mutex = CreateMutex(
NULL,
FALSE,
str);
if (h_mutex == NULL)
{
std::cout << "Failed to create mutex: error - " << GetLastError() << std::endl;
return 1;
}
std::cout << "Mutex created sucesfully" << std::endl;
DWORD dw_wait_res;
dw_wait_res = WaitForSingleObject(
h_mutex, // handle to mutex
INFINITE); // no time-out interval
for (;;)
{
Sleep(100);
}
CloseHandle(h_mutex);
system("PAUSE");
return 0;
}
Your logging clearly shows [access_pid_from_memory] occurs before [write_pid_to_memory].
And we can clearly see in your main() function that it calls access_pid_from_memory() first (which attempts to open the memory map and then closes it), and then afterwards calls write_pid_to_memory() (which creates the memory map and then closes it). Since there is no active handle referring to the mapping, it gets destroyed as soon as write_pid_to_memory() exits.
So, you are doing operations out of order, which is why OpenFileMapping() is failing. At no point does the mapping actually exist when access_pid_from_memory() is trying to open it.
You need to do the following instead:
in one process, create the mapping first, and leave it open.
THEN, in another process, open the mapping while it is still open in the previous process.
THEN, use the mapped memory as needed.
THEN, close the mapping in both processes.
There are other issues with your code, too:
converting the process ID to a string just to share it. You can share the ID as a binary DWORD instead.
access_pid_from_memory() doesn't actually read anything from the mapping (if it were able to open it at all).
main() is assigning t_pid to point at dangling memory. The call to stream.str() produces a temporary std::string that is destroyed as soon as c_str() exits.
I don't even know what you are attempting to do with your environment variable and mutex, and what that has to do with sharing memory. For purposes of this post, you should remove that code until you have your shared memory working properly.

In c++'s SetWindowsHookEx function

I'm Korean student.
Now this is my first question after signing up.
DWORD getProcessId() {
PROCESSENTRY32 process_infor;
process_infor.dwSize = sizeof(PROCESSENTRY32);
HANDLE snap_handle = CreateToolhelp32Snapshot(
TH32CS_SNAPALL, //스냅 단계
NULL //스냅할 pid
);
if (snap_handle != INVALID_HANDLE_VALUE) {
Process32First(snap_handle, &process_infor);
do {
wchar_t* temp = process_infor.szExeFile;
wstring ws(temp);
string name(ws.begin(), ws.end());
if (name == "notepad.exe") {
cout << name << " : " << process_infor.th32ProcessID << endl;
return process_infor.th32ProcessID;
}
} while (Process32Next(snap_handle, &process_infor));
}
CloseHandle(snap_handle);
return FALSE;
}
BOOL inject() {
HMODULE dll_handle;
HOOKPROC func;
HHOOK process_hook;
dll_handle = LoadLibrary(L"hello.dll");
func = (HOOKPROC) GetProcAddress(dll_handle, "injectSuccess");
cout << "handle : " << dll_handle << endl;
cout << "pid : " << getProcessId() << endl;
process_hook = SetWindowsHookEx(
WH_KEYBOARD,
func,
dll_handle,
getProcessId()
);
cout << "pook : " << process_hook << endl;
cout << "err : " << GetLastError() << endl;
FreeLibrary(dll_handle);
return FALSE;
}
There seems to be a problem with SetWindowsHookEx of the inject function in this case. The dll file loads well, and the injectSuccess function inside is well fetched. (I tried running it, but it worked)
And I wondered if the argument values ​​of SetWindowsHookEx were entered incorrectly, so I compared them and checked them continuously, but I couldn't find any difference. So, I tried GetLastError() with the return value of SetWindowsHookEx below, but the return value is 0 and the error code is 87 ("the parameter is incorrect").
So I searched, but I can't speak English well and I'm a beginner, so I'm not sure.
According to the SetWindowsHookExW:
dwThreadId
A handle to the DLL containing the hook procedure pointed to by the lpfn parameter. The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by the current process and if the hook procedure is within the code associated with the current process.
So what SetWindowsHookExW needs is the thread ID, and you pass in the process ID of notepad.exe, so the parameter is wrong.
I creat a sample and test the following code:
BOOL inject() {
HMODULE dll_handle;
HOOKPROC func;
HHOOK process_hook;
dll_handle = LoadLibrary(L"hello.dll");
if (dll_handle) func = (HOOKPROC)GetProcAddress(dll_handle, "injectSuccess");
else return FALSE;
cout << "handle : " << dll_handle << endl;
cout << "pid : " << getProcessId() << endl;
HWND h = FindWindow(L"notepad", NULL);
DWORD pid;
threadID = GetWindowThreadProcessId(h, NULL);
cout << "threadID = " << threadID << endl;
process_hook = SetWindowsHookEx(
WH_KEYBOARD,
func,
dll_handle,
threadID
);
cout << "pook : " << process_hook << endl;
cout << "err : " << GetLastError() << endl;
if(dll_handle) FreeLibrary(dll_handle);
return FALSE;
}
This example worked for me, and you can see the difference between pid and threadID:
Edit
According to the document:
An application installs the hook procedure by specifying the
WH_KEYBOARD hook type and a pointer to the hook procedure in a call to
the SetWindowsHookEx function. This hook may be called in the context
of the thread that installed it. The call is made by sending a message
to the thread that installed the hook. Therefore, the thread that
installed the hook must have a message loop. So if you want to run
this func, you need to add a message loop.
You can refer to the following code:
int main()
{
inject();
MSG msg;
while (GetMessageW(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
When you press the button, the message box will pop up:
Because there are two messages for each key press and release, func is triggered twice each time.If you only want to trigger every time a button is pressed, you can modify the following code:
if ((0x80000000 & lParam) == 0)//This means that when the key is pressed
{
MessageBox(NULL, L"Success (dll injection)", L"Window", MB_OK);
}
You can refer to KeyboardProc to view the messages for each value in lParam.

How to get the returned exit value of an invoked program to store it internally? [duplicate]

This question already has an answer here:
checking the return value of a command executed using CreateProcess
(1 answer)
Closed 2 years ago.
This question is related to a previously asked question found here:
I'm able to invoke another executable within my runProgram() function. However, before this runProgram() function returns back to main() and closes the handle for the process. I need to retrieve the value that the executable returns when it exits...
Here is my current application:
#include <Windows.h>
#include <exception>
#include <stdio.h>
#include <tchar.h>
#include <cstdint>
#include <iostream>
uint32_t runProgram(LPCSTR lpApplicationName) {
STARTUPINFOA si;
PROCESS_INFORMATION pi;
// Set the size of the structures
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Run the program
CreateProcessA(
lpApplicationName, // the path
NULL, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
CREATE_NEW_CONSOLE, // Opens file in seperate console
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure
);
uint32_t error = GetLastError();
if ( error != 0)
std::cerr << error << "\n";
// How to retrieve and store the result from the exiting program above...?
uint32_t cache_size = 0;
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return cache_size;
}
int main() {
try {
const uint32_t cache_size = runProgram("CacheQuerry.exe");
std::cout << cache_size << '\n';
}
catch (const std::exception& e) {
std::cerr << e.what() << "\n\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
And I want to store the return value from this executable into runProgram()'s local variable: cache_size.
Here is the main.cpp from the invoked program:
#include "CacheQuerry.h"
int main() {
int error = cache_info();
if (error != 0)
std::cout << error << '\n';
else
std::cout << '\n';
std::cout << "l1d_cache_size = " << l1d_cache_size() << std::endl;
std::cout << "cache line size = " << cache_line_size() << '\n';
return l1d_cache_size();
}
The invoked program will return the value produced by l1d_cache_size() when this program exits. This is the value that I want to store within runProgram()'s cache_size variable. How do I get this return value after the program exits? The implementation of the function call found in "CacheQuerry.h" shouldn't be relevant here, but if you need to see it's implementation don't hesitate to ask. These are two separate projects within the same solution. The 1st main.cpp which is in its own project relies on the 2nd main.cpp within its own project.
You can get it by calling GetExitCodeProcess (pi.hProcess), after waiting on the handle but before you close it.
The exact code you need is:
DWORD exit_code;
BOOL ok = GetExitCodeProcess (pi.hProcess, &exit_code);
Strange this is not mentioned in the Remarks section for CreateProcess at all.

Why do I need to acquire the debug privilege to use OpenProcess with minimal permissions when running as an admin?

One of my programs attempts to call OpenProcess on another of my programs, which is a service. The first program is running either as the local Administrator account or as another member of the Administrators group, and the service is running as the SYSTEM user.
I've found that in at least one environment (but not all) the call to OpenProcess fails with ERROR_ACCESS_DENIED. Furthermore, I've found that if I use AdjustTokenPrivileges to acquire the SE_DEBUG_NAME privilege, then OpenProcess succeeds. I've reproduced the behavior using the program below. The environment where it fails is running Windows 8.1, but off hand I don't know what the successful systems are running.
The only permission the program requests is PROCESS_QUERY_LIMITED_INFORMATION (because it eventually calls QueryFullProcessImageName). Nothing I've read suggests that debug privileges are required for that, only for more "intrusive" access like PROCESS_VM_READ or PROCESS_ALL_ACCESS, which I'm not interested in.
I've read about protected processes, and even though the service I'm targeting isn't designated as a protected process, documentation says that PROCESS_QUERY_LIMITED_INFORMATION isn't among the permissions that are forbidden from being granted for protected processes anyway.
Why would the original call to OpenProcess fail, and why does the debugging privilege make a difference?
#include <Windows.h>
#include <iostream>
#include <sstream>
#include <string>
int main(int argc, char* argv[])
{
std::istringstream pid_s(argv[1]);
DWORD pid;
pid_s >> pid;
bool debug = !!argv[2];
if (debug) {
TOKEN_PRIVILEGES NewState;
NewState.PrivilegeCount = 1;
if (!LookupPrivilegeValue(nullptr, SE_DEBUG_NAME, &NewState.Privileges[0].Luid)) {
std::clog << "Could not acquire debug-privilege name: " << GetLastError() << "\n";
return EXIT_FAILURE;
}
HANDLE token;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)) {
std::clog << "Could not acquire process token: " << GetLastError() << "\n";
return EXIT_FAILURE;
}
NewState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(token, FALSE, &NewState, sizeof(NewState), nullptr, nullptr)) {
std::clog << "Could not enable debug privilege: " << GetLastError() << "\n";
return EXIT_FAILURE;
}
std::clog << "Acquired debug privilege\n";
} else {
std::clog << "Not acquiring debug privilege\n";
}
HANDLE proc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid);
if (proc) {
std::clog << "Acquired process handle\n";
CloseHandle(proc);
return EXIT_SUCCESS;
} else {
std::clog << "Failed to acquire process handle: " << GetLastError();
return EXIT_FAILURE;
}
}

GetThreadContext returning error 6, Invalid Handle?

#include <iostream>
#include <Windows.h>
using std::cout;
using std::endl;
using std::cin;
int main()
{
cout << "1." << GetLastError() << endl;
PROCESS_INFORMATION processInfo;
STARTUPINFOA startupInfo = {0};
CONTEXT context;
context.ContextFlags = CONTEXT_FULL;
cout << "3." << GetLastError() << endl;
if (CreateProcess((PCHAR)"rsclient.exe", NULL, NULL, NULL, false, CREATE_SUSPENDED, NULL, NULL, &startupInfo, &processInfo) == false) {
cout << "CreateProcess error: " << GetLastError() << endl;
}
cout << "4." << GetLastError() << endl;
if (GetThreadContext(processInfo.hProcess, &context) == false) {
cout << "GetThreadContext error:" << GetLastError() << endl;
}
return 0;
}
output:
1.2
3.2
4.1813
GetThreadContext error:6
I can see the suspended process in task manager yet I'm getting an invalid handle error?
Also why does GetLastError() give an ERROR_FILE_NOT_FOUND at the start of the program?
You should use processInfo.hThread as that is the handle to the primary thread of the new process. processInfo.hProcess is a process handle, not a thread handle.
As for GetLastError() returning ERROR_FILE_NOT_FOUND, presumably someone else called an API that called SetLastError(ERROR_FILE_NOT_FOUND). From the documentation of GetLastError():
Return value
The return value is the calling thread's last-error code.
The Return Value section of the documentation for each function that
sets the last-error code notes the conditions under which the function
sets the last-error code. Most functions that set the thread's
last-error code set it when they fail. However, some functions also
set the last-error code when they succeed. If the function is not
documented to set the last-error code, the value returned by this
function is simply the most recent last-error code to have been set;
some functions set the last-error code to 0 on success and others do
not.
As you are calling GetThreadContext with process id as input, so Windows is not able to find any such kind of thread, so returning ERROR_FILE_NOT_FOUND. Better you give the main thread of newly created process and you will get the desired result.