my c++ code works for a couple of times straight and after few executions it suddenly stops working and throws exceptions (without any changes!), and I cant figure out why.
This is the problematic part of code:
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
TCHAR *path;
SHGetKnownFolderPath(FOLDERID_Startup, KF_FLAG_CREATE, NULL, &path);
lstrcat(path, L"\\calc.exe");
if (CreateProcess(NULL, path, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
{
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
After a few executions, 2 exceptions are thrown on the CreateProcess() line, the first one:
Unhandled exception at 0x779D8829 (ntdll.dll) in PS_Down.exe: 0xC0000374: A heap has been corrupted (parameters: 0x77A15890).
The second:
Exception thrown at 0x77946111 (ntdll.dll) in PS_Down.exe: 0xC0000005: Access violation reading location 0x00000069.
It happened to me with a few other projects (that don't include the CreateProcess() func) and iv'e noticed that it always happens when TCHAR and SHGetKnownFolderPath() are involved.
Any help with understanding how to fix the issue will be much appreciated, thanks in advance!
P.S - I'm new to coding in cpp, so please try to explain accordingly
lstrcat(path, L"\\calc.exe"); will cause a buffer overrun. path is a pointer to array that can contain only folder path, nothing more. You will need to allocate a wide string, append folder path and then file path. Also you will need to check result of SHGetKnownFolderPath to determine whether path contains a valid pointer and free it later by calling CoTaskMemFree.
path is allocated with a fixed length by SHGetKnownFolderPath so you can't concatenate your executable to it directly. You'd need to use CoTaskMemRealloc to expand the space first. You also need to free the memory after you've used it. Similarly, you need to close the handles created by CreateProcess.
You could therefore make support classes to handle the resources automatically:
#include "pch.h"
#include <iostream>
#include <windows.h>
#include <Shlobj.h>
#include <wchar.h>
// A wide string exception class. perhaps something like this already exist in VS?
class werror {
std::wstring text;
public:
werror(const wchar_t* Text) : text(Text) {}
const wchar_t* what() const { return text.c_str(); }
};
class ConCatToKnownFolderPath {
PWSTR m_path;
public:
ConCatToKnownFolderPath(REFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, const WCHAR* AddToPath = nullptr) :
m_path(nullptr)
{
if (SHGetKnownFolderPath(rfid, dwFlags, hToken, &m_path) != S_OK)
throw werror(L"SHGetKnownFolderPath failed");
if (AddToPath) {
size_t newlen = wcslen(m_path) + wcslen(AddToPath) + sizeof(WCHAR); // place for \0
size_t newbufsize = newlen * sizeof(WCHAR);
auto newPtr = CoTaskMemRealloc(m_path, newbufsize);
if (!newPtr) {
CoTaskMemFree(m_path);
throw werror(L"CoTaskMemRealloc failed");
}
m_path = reinterpret_cast<PWSTR>(newPtr);
wcscat_s(m_path, newlen, AddToPath);
}
}
// move works fine
ConCatToKnownFolderPath(ConCatToKnownFolderPath&& other) noexcept :
m_path(other.m_path)
{
other.m_path = nullptr;
}
ConCatToKnownFolderPath& operator=(ConCatToKnownFolderPath&& other) noexcept {
if (m_path) CoTaskMemFree(m_path);
m_path = other.m_path;
other.m_path = nullptr;
return *this;
}
// copy not supported (but could easily be added
ConCatToKnownFolderPath(const ConCatToKnownFolderPath&) = delete;
ConCatToKnownFolderPath& operator=(const ConCatToKnownFolderPath&) = delete;
// automatic free when it goes out of scope
~ConCatToKnownFolderPath() {
if (m_path) CoTaskMemFree(m_path);
}
PWSTR data() const { return m_path; }
operator LPCWSTR () const { return m_path; }
};
struct WProcessWithInfo : PROCESS_INFORMATION {
WProcessWithInfo(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPCWSTR lpCurrentDirectory) {
STARTUPINFOW si;
ZeroMemory(&si, sizeof(STARTUPINFOW));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL;
if (!CreateProcessW(lpApplicationName, lpCommandLine, NULL, NULL, FALSE, 0, NULL, lpCurrentDirectory, &si, *this))
throw werror(L"CreateProcessWCreateProcessW failed");
CloseHandle(hThread);
}
WProcessWithInfo(const WProcessWithInfo&) = delete;
WProcessWithInfo(WProcessWithInfo&&) = delete;
WProcessWithInfo& operator=(const WProcessWithInfo&) = delete;
WProcessWithInfo& operator=(WProcessWithInfo&&) = delete;
~WProcessWithInfo() {
CloseHandle(hProcess);
}
DWORD Wait(DWORD dwMilliseconds=INFINITE) {
return WaitForSingleObject(*this, dwMilliseconds);
}
operator HANDLE () { return hProcess; }
operator LPPROCESS_INFORMATION () { return this; }
};
int main() {
try {
ConCatToKnownFolderPath path(FOLDERID_System, KF_FLAG_CREATE, NULL, L"\\calc.exe");
std::wcout << L"Starting " << path.data() << L"\n";
WProcessWithInfo proc(path, NULL, NULL);
std::wcout << L"Process started\n";
proc.Wait();
std::wcout << L"Process done\n";
}
catch (const werror& ex) {
std::wcerr << L"Exception: " << ex.what() << L"\n";
}
return 0;
}
Related
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 4 months ago.
Improve this question
there is warnings in c++ i can't solve it
i have Visual Studio 2019
first i got error with #include "pch.h"
and 3 warnings
-using uninitialized memory : in processesSnapshot
`
while (Process32Next(processesSnapshot, &processInfo))
`
-argument conversion from 'unsigned __int64' to 'unsigned long', possible loss of data
-argument conversion from 'unsigned __int64' to 'DWORD', possible loss of data
`
MODULEENTRY32 module = GetModule("ac_client.exe", pid);
HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
`
`
#include "pch.h"
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
DWORD GetPID(const char* ProcessName) {
PROCESSENTRY32 processInfo;
processInfo.dwSize = sizeof(processInfo);
HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (processesSnapshot == INVALID_HANDLE_VALUE)
return 0;
Process32First(processesSnapshot, &processInfo);
if (!strcmp(processInfo.szExeFile, ProcessName))
{
CloseHandle(processesSnapshot);
}
while (Process32Next(processesSnapshot, &processInfo))
{
if (!strcmp(processInfo.szExeFile, ProcessName))
{
CloseHandle(processesSnapshot);
}
}
CloseHandle(processesSnapshot);
return processInfo.th32ProcessID;
}
MODULEENTRY32 GetModule(const char* moduleName, unsigned long ProcessID)
{
MODULEENTRY32 modEntry = { 0 };
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, ProcessID);
if (hSnapshot != INVALID_HANDLE_VALUE)
{
MODULEENTRY32 curr = { 0 };
curr.dwSize = sizeof(MODULEENTRY32);
if (Module32First(hSnapshot, &curr))
{
do
{
if (!strcmp(curr.szModule, moduleName))
{
modEntry = curr;
break;
}
} while (Module32Next(hSnapshot, &curr));
}
CloseHandle(hSnapshot);
}
return modEntry;
}
int main()
{
std::cout << "Hello World!\n";
unsigned long long pid = GetPID("ac_client.exe");
MODULEENTRY32 module = GetModule("ac_client.exe", pid);
HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
unsigned long long result;
int ammodesiredvalue = 999999;
ReadProcessMemory(phandle, (void*)((unsigned long long)module.modBaseAddr + 0x0010B1E0), &result, sizeof(result), 0);
ReadProcessMemory(phandle, (void*)((unsigned long long)result + 0x54), &result, sizeof(result), 0);
ReadProcessMemory(phandle, (void*)((unsigned long long)result + 0x14), &result, sizeof(result), 0);
//ReadProcessMemory(phandle, (void*)((unsigned long long)result + 0x14), &result, sizeof(result), 0);
WriteProcessMemory(phandle, (void*)((unsigned long long)result + 0x14), &ammodesiredvalue, sizeof(ammodesiredvalue), 0);
std::cout << "Your ammo value is " << result << std::endl;
system("pause");
}
`
i looking for fix this error and warnings
Your code has a number of problems. I'm going to look at one small section, and point out what look to me like obvious problems. Not sure if they're the source of the symptoms you're seeing, but ...
Process32First(processesSnapshot, &processInfo);
if (!strcmp(processInfo.szExeFile, ProcessName))
{
CloseHandle(processesSnapshot);
}
So here if we found the right process, we close the process snapshot, but then:
while (Process32Next(processesSnapshot, &processInfo))
{
...we try to continue using the process snapshot, even if it's already been closed. Then:
if (!strcmp(processInfo.szExeFile, ProcessName))
{
CloseHandle(processesSnapshot);
}
...we check whether we have the right process, and (again) close the snapshot if we found it. But we don't break out of the loop--so again, we try to continue using the snapshot, even if we've closed it. Then:
}
CloseHandle(processesSnapshot);
...when we exit the loop, we close the snapshot yet again. At this point, we've potentially closed our snapshot three times, and continued using it after it was closed in a couple of different places.
My personal inclination is that any time I see something that needs to be opened and later closed (or something along those lines) I think of using RAII/SBRM to deal with it. That means I'd create a class to manage the process snapshot. I'd create the snapshot in the class' constructor, and close it in the class' destructor, and add other member functions to obtain the contents of the current item, advance to the next item, and so on.
Given that this iterates things, it also makes sense (in my opinion) to have this class act as an actual iterator.
// warning: untested code.
class ProcessIterator {
HANDLE snapshot;
bool valid;
PROCESSENTRY32 processInfo;
public:
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t; // not really, but it'll probably do for now.
using value_type = PROCESSENTRY32;
using pointer = PROCESSENTRY32 *;
using reference = PROCESSENTRY32 &;
ProcessIterator(DWORD ID) : snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, ID))
{
if (!snapshot)
throw std::runtime_error("Unable to open process snapshot");
valid = Process32First(snapshot, &processInfo);
}
ProcessIterator() : valid(false) {}
ProcessIterator operator++() {
valid = Process32Next(snapshot, &processInfo);
return *this;
}
PROCESSENTRY32 operator*() const { return processInfo; }
bool operator==(ProcessIterator other) {
// only invalid iterators are equal:
return (!valid) && (!other.valid);
}
~ProcessIterator() { CloseHandle(snapshot); }
};
Since this is a normal iterator, we can use it for normal iterator "stuff", such as finding an entry using std::find_if:
DWORD getPid(char const *filename) {
auto p = std::find_if(ProcessIterator(0), ProcessIterator(),
[&](PROCESSENTRY32 const &p) { return !std::strcmp(filename, p.szExeFile); });
return p->th32ProcessID;
}
So I was checking csgo scripting with C++ and everything was going smoothly but when I finished the Bhop code I found out that "string1" is "const char" and "string2" is wchar. I tried fixing, i searched redit, youtube literally everything but i couldn't fix it.
#include "memory.h"
#include <TlHelp32.h>
Memory :: Memory(const char* processName)
{
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
const auto snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
while (Process32Next(snapShot, &entry))
{
if (!strcmp(processName, entry.szExeFile))
{
this->id = entry.th32ProcessID;
this->process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, this->id);
break;
}
}
if (snapShot)
CloseHandle(this->process);
}
Memory :: ~Memory()
{
if (this->process)
CloseHandle(this->process);
}
DWORD Memory :: GetProcessId()
{
return this->id;
}
HANDLE Memory :: GetProcessHandle()
{
return this->process;
}
uintptr_t Memory :: GetModuleAdress(const char* moduleName)
{
MODULEENTRY32 entry;
entry.dwSize = sizeof(MODULEENTRY32);
const auto snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, this->id);
uintptr_t result = 0;
while (Module32Next(snapShot, &entry))
{
if (!strcmp(moduleName, entry.szModule))
{
result = reinterpret_cast<uintptr_t>(entry.modBaseAddr);
break;
}
}
if (snapShot)
CloseHandle(snapShot);
return result;
}
The error comes up on if (!strcmp(processName, entry.szExeFile)) where processName is "const char" and entry.szExeFile is "wchar". Please tell how to fix my problem
because i got no clue.
You are using TCHAR-based Win32 API macros, and you are compiling your project with UNICODE defined, so the macros are resolving to the wchar_t version of the APIs. But you are using char data in the rest of your code, so you need to use the ANSI (char) version of the APIs explicitly instead.
Also, there are other bugs in your code. Namely, ignoring the 1st entry in the process and modules lists. And your constructor is closing the wrong HANDLE.
Try this:
Memory :: Memory(const char* processName)
{
PROCESSENTRY32A entry;
entry.dwSize = sizeof(entry);
const auto snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Process32FirstA(snapShot, &entry))
{
do
{
if (strcmp(processName, entry.szExeFile) == 0)
{
this->id = entry.th32ProcessID;
this->process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, this->id);
break;
}
}
while (Process32NextA(snapShot, &entry));
}
if (snapShot)
CloseHandle(snapShot);
}
uintptr_t Memory :: GetModuleAdress(const char* moduleName)
{
MODULEENTRY32A entry;
entry.dwSize = sizeof(entry);
const auto snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, this->id);
uintptr_t result = 0;
if (Module32FirstA(snapShot, &entry))
{
do
{
if (strcmp(moduleName, entry.szModule) == 0)
{
result = reinterpret_cast<uintptr_t>(entry.modBaseAddr);
break;
}
}
while (Module32NextA(snapShot, &entry));
}
if (snapShot)
CloseHandle(snapShot);
return result;
}
I am trying to pass an argument with a space in i to a batch file I run via CreateProcess(). How do I specify that the entire object is an argument?
std::wstring args = TEXT("/C \"C:\\setup.bat\" C:\\TEST TEST");
In the example above, my batch file reads the first argument as C:\TEST.
And, this does not work (batch file exits immediately and does not run):
std::wstring args = TEXT("/C \"C:\\setup.bat\" \"C:\\TEST TEST\"");
Here is the entire code:
#include <iostream>
#define WINDOWS_LEAN_AND_MEAN
#include <Windows.h>
#include <strsafe.h>
#include <string>
#include <UserEnv.h>
#include <vector>
#define BUFSIZE 4096
#pragma comment(lib, "userenv.lib")
std::wstring GetEnvString()
{
wchar_t* env = GetEnvironmentStrings();
if (!env)
{
abort();
}
const wchar_t* var = env;
size_t total_len = 0;
size_t len;
while ((len = wcslen(var)) > 0)
{
total_len += len + 1;
var += len + 1;
}
std::wstring result(env, total_len);
FreeEnvironmentStrings(env);
return result;
}
int main()
{
LPVOID env;
if (!CreateEnvironmentBlock(&env, NULL, FALSE))
{
std::cout << "FAILURE" << std::endl;
system("PAUSE");
abort();
}
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(pi));
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
std::wstring program = TEXT("C:\\Windows\\System32\\cmd.exe");
std::wstring args = TEXT("/C");
args.append(TEXT(" \"C:\\setup.bat\""));
args.append(TEXT(" C:\TEST TEST"));
std::vector<wchar_t> buf(args.begin(), args.end());
buf.push_back(0);
if (!CreateProcess(program.c_str(), buf.data(), NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, env, NULL, &si, &pi))
{
std::cout << "FAILURE" << std::endl;
system("pause");
abort();
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
DestroyEnvironmentBlock(env);
if (!CreateEnvironmentBlock(&env, NULL, FALSE))
{
std::cout << "FAILURE" << std::endl;
abort();
}
return 0;
}
the problem is that the /C parameter needs its entire parameter enclosed by quotes (if it contain spaces).
so, instead of cmd /c "c:\setup.bat" "c:\test test", you need cmd /c ""c:\setup.bat" "c:\test test"" (or at least cmd /c "c:\setup.bat "c:\test test"")
Figured it out:
std::wstring args = TEXT("/C");
args.append(TEXT(" \"\"C:\\setup.bat\""));
args.append(TEXT(" \"C:\\TEST TEST\""));
I figure this ends up doing something like: cmd.exe "C:\setup.bat "C:\Test Test""
How about this:
std::wstring args = TEXT("/C \"C:\\setup.bat\" \"C:\\TEST\ TEST\"");
I am trying to make a program to store the value 500 into the calculator's memory address for the MR (Memory Restore) button on the calculator application.
I know that the address for this integer is
"calc.exe"+00073320 + 0 + C
If I use a program like cheat engine, I can get the current address for the instance of the calculator.exe i'm running, and write to it just fine that way. However, since this is not a static address, I need a way to get the module base address.
I tried using this GetModuleBase function (see code below) to get the Base Address of the calc.exe, but my issue is that I cannot get the base address. The function always returns 0 instead of the correct address.
I debugged it and found that in the GetModuleBase function, it is not even cycling once through the while loop because bModule is returning 0 from the Module32First function.
#include <tchar.h>
#include <windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <Psapi.h>
#include <wchar.h>
#pragma comment( lib, "psapi" )
using namespace std;
DWORD GetModuleBase(LPSTR lpModuleName, DWORD dwProcessId)
{
MODULEENTRY32 lpModuleEntry = {0};
HANDLE hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwProcessId );
if(!hSnapShot)
return NULL;
lpModuleEntry.dwSize = sizeof(lpModuleEntry);
BOOL bModule = Module32First( hSnapShot, &lpModuleEntry );
while(bModule)
{
if(!strcmp( lpModuleEntry.szModule, lpModuleName ) )
{
CloseHandle( hSnapShot );
return (DWORD)lpModuleEntry.modBaseAddr;
}
bModule = Module32Next( hSnapShot, &lpModuleEntry );
}
CloseHandle( hSnapShot );
return NULL;
}
int main() {
HWND hWnd = FindWindow(0, "Calculator");
DWORD BaseAddr;
if(hWnd == 0){
MessageBox(0, "Error cannot find window.", "Error", MB_OK|MB_ICONERROR);
} else {
DWORD proccess_ID;
GetWindowThreadProcessId(hWnd, &proccess_ID);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, proccess_ID);
if(!hProcess){
MessageBox(0, "Could not open the process!", "Error!", MB_OK|MB_ICONERROR);
} else {
int newdata = 500;
BaseAddr = GetModuleBase("calc.exe",proccess_ID);
//GetModuleBase is always returning 0, so I am not getting the correct base address
DWORD newdatasize = sizeof(newdata);
if(WriteProcessMemory(hProcess, (LPVOID)0x002413FC, &newdata, newdatasize, NULL)){
cout << "Memory successfully written." << endl;
} else {
cout << "Memory failed to write." << endl;
}
CloseHandle(hProcess);
}
}
return 0;
}
Summary: I cannot get the correct base address using my GetModuleBase function, and I need to figure out what I am doing wrong so that I can get the correct base address for the "calc.exe" process.
You should read the modules like this:
#include <windows.h>
#include <TlHelp32.h>
#include <iostream>
//You don't have to use this function if you don't want to..
int strcompare(const char* One, const char* Two, bool CaseSensitive)
{
#if defined _WIN32 || defined _WIN64
return CaseSensitive ? strcmp(One, Two) : _stricmp(One, Two);
#else
return CaseSensitive ? strcmp(One, Two) : strcasecmp(One, Two);
#endif
}
//You read module information like this..
MODULEENTRY32 GetModuleInfo(std::uint32_t ProcessID, const char* ModuleName)
{
void* hSnap = nullptr;
MODULEENTRY32 Mod32 = {0};
if ((hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessID)) == INVALID_HANDLE_VALUE)
return Mod32;
Mod32.dwSize = sizeof(MODULEENTRY32);
while (Module32Next(hSnap, &Mod32))
{
if (!strcompare(ModuleName, Mod32.szModule, false))
{
CloseHandle(hSnap);
return Mod32;
}
}
CloseHandle(hSnap);
return {0};
}
int main()
{
//Change the process ID below..
BYTE* BaseAddr = GetModuleInfo(5172, "calc.exe").modBaseAddr;
std::cout<<"BASE ADDRESS: "<<(void*)BaseAddr<<"\n";
return 0;
}
EDIT: After further investigation, I found that Visual Studio was compiling for an x32 platform but calc.exe is an x64 process..
To get Visual Studio to compile for x64 you need to do the following:
Then click and select "NEW" from the following drop-down menu:
Next in the following drop down, select x64:
Save the settings and rebuild the project and it should work..
When unhandled exception occured i want to print a stacktrace instead of just terminating. I've tried to do that using SetUnhandledExceptionFilter:
SetUnhandledExceptionFilter(UnhandledException);
...
LONG WINAPI UnhandledException(LPEXCEPTION_POINTERS exceptionInfo)
{
printf("An exception occurred which wasn't handled!\nCode: 0x%08X\nAddress: 0x%08X",
exceptionInfo->ExceptionRecord->ExceptionCode,
exceptionInfo->ExceptionRecord->ExceptionAddress);
return EXCEPTION_EXECUTE_HANDLER;
}
This code, i've found, works fine. However there are no addition information because ExceptionCode and ExceptionAddress are printed in system "Event Viewer" anyway.
If it is possible to print a full stack trace so I can determine the exact point where exception occured?
I've found this code https://code.google.com/p/m0d-s0beit-sa/source/browse/src/main.cpp?r=9ceb4fec21d647b169c72851d7882bef2b9c5a8a It partly solves my problem. Only method where exception occured is printed. But type of exception and line number is not printed.
Here's some stack-walk code for Windows I wrote some years ago. Here's the kind of output it produces:
Walking stack.
0 DebugBreak
1 ThreadFunc2 e:\c\source\stackwalk2a.cpp(72)
2 ThreadFunc1 e:\c\source\stackwalk2a.cpp(79)
3 TargetThread e:\c\source\stackwalk2a.cpp(86)
4 BaseThreadInitThunk
5 RtlUserThreadStart
End of stack walk.
The main thing that's missing is anything about the exception type. If you're talking about a native structured/vectored exception, I'm pretty sure that should be retrievable too. Retrieving types of C++ exceptions might be a little more difficult (but I'm not really sure -- it might be pretty easy).
#include <windows.h>
#include <winnt.h>
#include <string>
#include <vector>
#include <Psapi.h>
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <iterator>
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "dbghelp.lib")
// Some versions of imagehlp.dll lack the proper packing directives themselves
// so we need to do it.
#pragma pack( push, before_imagehlp, 8 )
#include <imagehlp.h>
#pragma pack( pop, before_imagehlp )
struct module_data {
std::string image_name;
std::string module_name;
void *base_address;
DWORD load_size;
};
typedef std::vector<module_data> ModuleList;
HANDLE thread_ready;
bool show_stack(std::ostream &, HANDLE hThread, CONTEXT& c);
DWORD __stdcall TargetThread( void *arg );
void ThreadFunc1();
void ThreadFunc2();
DWORD Filter( EXCEPTION_POINTERS *ep );
void *load_modules_symbols( HANDLE hProcess, DWORD pid );
int main( void ) {
DWORD thread_id;
thread_ready = CreateEvent( NULL, false, false, NULL );
HANDLE thread = CreateThread( NULL, 0, TargetThread, NULL, 0, &thread_id );
WaitForSingleObject( thread_ready, INFINITE );
CloseHandle(thread_ready);
return 0;
}
// if you use C++ exception handling: install a translator function
// with set_se_translator(). In the context of that function (but *not*
// afterwards), you can either do your stack dump, or save the CONTEXT
// record as a local copy. Note that you must do the stack dump at the
// earliest opportunity, to avoid the interesting stack-frames being gone
// by the time you do the dump.
DWORD Filter(EXCEPTION_POINTERS *ep) {
HANDLE thread;
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
GetCurrentProcess(), &thread, 0, false, DUPLICATE_SAME_ACCESS);
std::cout << "Walking stack.";
show_stack(std::cout, thread, *(ep->ContextRecord));
std::cout << "\nEnd of stack walk.\n";
CloseHandle(thread);
return EXCEPTION_EXECUTE_HANDLER;
}
void ThreadFunc2() {
__try { DebugBreak(); }
__except (Filter(GetExceptionInformation())) { }
SetEvent(thread_ready);
}
void ThreadFunc1(void (*f)()) {
f();
}
// We'll do a few levels of calls from our thread function so
// there's something on the stack to walk...
//
DWORD __stdcall TargetThread(void *) {
ThreadFunc1(ThreadFunc2);
return 0;
}
class SymHandler {
HANDLE p;
public:
SymHandler(HANDLE process, char const *path=NULL, bool intrude = false) : p(process) {
if (!SymInitialize(p, path, intrude))
throw(std::logic_error("Unable to initialize symbol handler"));
}
~SymHandler() { SymCleanup(p); }
};
#ifdef _M_X64
STACKFRAME64 init_stack_frame(CONTEXT c) {
STACKFRAME64 s;
s.AddrPC.Offset = c.Rip;
s.AddrPC.Mode = AddrModeFlat;
s.AddrStack.Offset = c.Rsp;
s.AddrStack.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.Rbp;
s.AddrFrame.Mode = AddrModeFlat;
return s;
}
#else
STACKFRAME64 init_stack_frame(CONTEXT c) {
STACKFRAME64 s;
s.AddrPC.Offset = c.Eip;
s.AddrPC.Mode = AddrModeFlat;
s.AddrStack.Offset = c.Esp;
s.AddrStack.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.Ebp;
s.AddrFrame.Mode = AddrModeFlat;
return s;
}
#endif
void sym_options(DWORD add, DWORD remove=0) {
DWORD symOptions = SymGetOptions();
symOptions |= add;
symOptions &= ~remove;
SymSetOptions(symOptions);
}
class symbol {
typedef IMAGEHLP_SYMBOL64 sym_type;
sym_type *sym;
static const int max_name_len = 1024;
public:
symbol(HANDLE process, DWORD64 address) : sym((sym_type *)::operator new(sizeof(*sym) + max_name_len)) {
memset(sym, '\0', sizeof(*sym) + max_name_len);
sym->SizeOfStruct = sizeof(*sym);
sym->MaxNameLength = max_name_len;
DWORD64 displacement;
if (!SymGetSymFromAddr64(process, address, &displacement, sym))
throw(std::logic_error("Bad symbol"));
}
std::string name() { return std::string(sym->Name); }
std::string undecorated_name() {
std::vector<char> und_name(max_name_len);
UnDecorateSymbolName(sym->Name, &und_name[0], max_name_len, UNDNAME_COMPLETE);
return std::string(&und_name[0], strlen(&und_name[0]));
}
};
bool show_stack(std::ostream &os, HANDLE hThread, CONTEXT& c) {
HANDLE process = GetCurrentProcess();
int frame_number=0;
DWORD offset_from_symbol=0;
IMAGEHLP_LINE64 line = {0};
SymHandler handler(process);
sym_options(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
void *base = load_modules_symbols(process, GetCurrentProcessId());
STACKFRAME64 s = init_stack_frame(c);
line.SizeOfStruct = sizeof line;
IMAGE_NT_HEADERS *h = ImageNtHeader(base);
DWORD image_type = h->FileHeader.Machine;
do {
if (!StackWalk64(image_type, process, hThread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
return false;
os << std::setw(3) << "\n" << frame_number << "\t";
if ( s.AddrPC.Offset != 0 ) {
std::cout << symbol(process, s.AddrPC.Offset).undecorated_name();
if (SymGetLineFromAddr64( process, s.AddrPC.Offset, &offset_from_symbol, &line ) )
os << "\t" << line.FileName << "(" << line.LineNumber << ")";
}
else
os << "(No Symbols: PC == 0)";
++frame_number;
} while (s.AddrReturn.Offset != 0);
return true;
}
class get_mod_info {
HANDLE process;
static const int buffer_length = 4096;
public:
get_mod_info(HANDLE h) : process(h) {}
module_data operator()(HMODULE module) {
module_data ret;
char temp[buffer_length];
MODULEINFO mi;
GetModuleInformation(process, module, &mi, sizeof(mi));
ret.base_address = mi.lpBaseOfDll;
ret.load_size = mi.SizeOfImage;
GetModuleFileNameEx(process, module, temp, sizeof(temp));
ret.image_name = temp;
GetModuleBaseName(process, module, temp, sizeof(temp));
ret.module_name = temp;
std::vector<char> img(ret.image_name.begin(), ret.image_name.end());
std::vector<char> mod(ret.module_name.begin(), ret.module_name.end());
SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size);
return ret;
}
};
void *load_modules_symbols(HANDLE process, DWORD pid) {
ModuleList modules;
DWORD cbNeeded;
std::vector<HMODULE> module_handles(1);
EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);
module_handles.resize(cbNeeded/sizeof(HMODULE));
EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);
std::transform(module_handles.begin(), module_handles.end(), std::back_inserter(modules), get_mod_info(process));
return modules[0].base_address;
}