Get process's thread information in windows OS - c++

I'm new in c++ programming and I'm coding a tool to enumerate information of all process running on windows OS.
After researching, googling, I have found a useful library Ntdll.lib and header winternl.h that helping me gathering information about process by using NtQuerySystemInformation() function. Everything work fine, I have call that function and retrieved the array of structure SYSTEM_PROCESS_INFORMATION that contains information about the process entry, here is my piece of code:
DWORD dwRet;
DWORD dwSize = 0;
NTSTATUS Status = STATUS_INFO_LENGTH_MISMATCH;
while (true)
{
// Check if pointer p is not NULL then free it
if (p != NULL) { VirtualFree(p, 0, MEM_RELEASE); }
p = (PSYSTEM_PROCESS_INFORMATION)VirtualAlloc(NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// Query system to get the process list information
Status = NtQuerySystemInformation(SystemProcessInformation, (PVOID)p, (ULONG)dwSize, &dwRet);
if (Status == STATUS_SUCCESS)
{
cout << "Query process information successfully!!" << endl;
break;
}
else if (Status != STATUS_INFO_LENGTH_MISMATCH)
{
VirtualFree(p, 0, MEM_RELEASE);
p = NULL;
cout << "NtQuerySystemInformation failed with error code: " << Status << endl;
return FALSE;
}
// Add more 16kb to buffer in case there is more process opened during this loop
dwSize = dwRet + (2 << 14);
}
The problem appears when I was looking for thread details of processes, in particular, I don't know how to get the array of structure SYSTEM_THREAD_INFORMATION with NtQuerySystemInformation() function.
I have read the docs here: https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation and it says that each SYSTEM_PROCESS_INFORMATION structure returned by NtQuerySystemInformation() has 1 or more SYSTEM_THREAD_INFORMATION structure followed in memory but I don't know how to interact with them. Anyone has an idea for my problem? I'm just a newbie in c++ programming and I'm studying in user mode code so sorry if my question is silly or not worth asking.

Starting from your PSYSTEM_PROCESS_INFORMATION p:
while (p) {
PSYSTEM_THREAD_INFORMATION pt = (PSYSTEM_THREAD_INFORMATION)(p + 1);
for (int t = 0; t < p->NumberOfThreads; t++) {
std::cout << "Start address of thread " << t << " is " << std::hex << pt->StartAddress << std::dec << std::endl;
pt++; // Adds sizeof(SYSTEM_THREAD_INFORMATION) to the address in pt
}
if (p->NextEntryOffset) {
p = PSYSTEM_PROCESS_INFORMATION((void *)p + NextEntryOffset);
} else {
p = nullptr;
}
}

Thanks to Botje and RbMm and all of you for helping me solving this problem.
I just add the part that Botje show me and correct the line
PSYSTEM_THREAD_INFORMATION pt = (PSYSTEM_THREAD_INFORMATION)(p + 1);
then the problem is solved and my code works smoothly.
Thanks again, this post is closed

Related

C/C++ - Performance Collector (pdh.h) Failing with error code 0xc0000bc6

I am following the article # https://learn.microsoft.com/en-us/windows/win32/perfctrs/browsing-performance-counters for direction on using pdh.
I want to collect performance counter using code so that I can install it as a windows service and collect counters as in when required.
The change from the sample code is -
I have a textfile with all the counters written as such -
\Process(Calculator_replaceId)\% Processor Time
Since my performance counter will target particular process, I am using the above query. Please note, once it is ready I will replace Calculator with w3wp. I am using Calculator in local machine, in reality it will be used to capture counters for w3wp processed
Using tlhelp32.h, I am enumerating all the process and getting the process id for process with executable 'Calculator.exe'.
if (lstrcmpi(pe32.szExeFile,L"Calculator.exe") == 0)
{
cout << "W3WP Found : " << pe32.szExeFile << endl;
PDH_STATUS Status;
HCOUNTER Counter;
DWORD CounterType;
SYSTEMTIME SampleTime;
PDH_FMT_COUNTERVALUE DisplayValue;
LPWSTR pMessage = NULL;
Status = PdhOpenQuery(NULL, NULL, &Query);
For the process found, generate the query as such -
for(auto counter : vCounters){
cout << "Processing -" << counter << endl;
counter.replace(20,9, to_string(pe32.th32ProcessID)); \\this will replace replaceid with processid
cout << "After Replacementg -" << counter << endl;
wstring stemp = wstring(counter.begin(), counter.end());
wcout << "Modified String" << stemp.c_str() << endl;
Status = PdhAddCounter(Query, stemp.c_str(), 0, &Counter);
Finally, I am using PdhGetFormattedCounterValue to get the value, however the step is failing with status code 0xc000bc6 which is code for 'Invalid Data'
Status = PdhGetFormattedCounterValue(Counter,
PDH_FMT_DOUBLE,
&CounterType,
&DisplayValue);
if (Status != ERROR_SUCCESS)
{
wprintf(L"\nPdhGetFormattedCounterValue failed with status 0x%x.", Status);
goto Cleanup;
}
What's interesting is, I have copied the same code and hardcoded the same counter, and in that code I do not face any failure. The program provides correct counter value (i compared the same counter value using perfmon).
Any pointers on how to fix the issue.

Memory cheating with C++ WinApi

i'm trying to make a little program to my university that can change values in the memory of another process. With the exact address value that the Cheat Engine give me i can do this, but not ever the value is the same then my problem is with the memory pointers. In the following image i has the every offset that i found in the pointer scan map:
I already make a program but it not work and ever gives me 299 error code, i Run it as administrator. The code is the following:
#include <iostream>
#include <Windows.h>
#include <Psapi.h>
#include <TlHelp32.h>
#include <queue>
using namespace std;
int main() {
PROCESSENTRY32 pEntry;
pEntry.dwSize = sizeof(PROCESSENTRY32);
// Snapshot to list all process
HANDLE pHandlers = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
if (pHandlers == NULL) {
cout << "Error 1";
return 1;
}
// Listing process
if (Process32First(pHandlers, &pEntry)) {
while (Process32Next(pHandlers, &pEntry)) {
// Convert value to string
wstring wstr(pEntry.szExeFile);
string str(wstr.begin(), wstr.end());
// Check if is the process that i wan't
if (str == "Playgroundd.exe") {
MODULEENTRY32 mEntry;
mEntry.dwSize = sizeof(MODULEENTRY32);
// Snapshot to list all modules inside process
HANDLE mHandlers = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pEntry.th32ProcessID);
if (mHandlers == NULL) {
cout << "Error 2";
return 1;
}
// Usually the first process is the main module
if (Module32First(mHandlers, &mEntry)) {
// Convert the name to string
wstring wstrr(mEntry.szExePath);
string strr(wstrr.begin(), wstrr.end());
if (strr.find("Playgroundd.exe")) {
// Get the base address of module
DWORD moduleBaseAddress = (DWORD)mEntry.modBaseAddr;
// Append initial value
moduleBaseAddress += (DWORD)0x000000E8;
// Offsets defined
DWORD offsets[] = {0x88,0x98,0x90,0x20,0x10,0x48,0x904};
// Open process with the right process id
cout << "process id: " << pEntry.th32ProcessID << endl << endl;
HANDLE processHandler = OpenProcess(PROCESS_ALL_ACCESS, 0, pEntry.th32ProcessID);
if (processHandler == NULL) {
cout << "Can't open the process";
return 1;
}
// Sum offsets
for (int i = 0; i < 7;i++) {
moduleBaseAddress += offsets[i];
}
int receive = 0;
size_t bytesRead = 0;
bool resultStatus = ReadProcessMemory(processHandler,
(LPCVOID)moduleBaseAddress, &receive, sizeof(receive), &bytesRead);
cout << "result status :" << resultStatus << endl;
cout << "Received : " << receive << endl;
cout << "Bytes read : " << bytesRead << endl;
cout << "Possible error code : " << GetLastError() << endl;
}
else {
cout << "Can't find module";
return 1;
}
}
}
}
}
};
This is the output of the above program, the error code can be ignored if the result status be non-zero
result status :0
Received : 0
Bytes read : 0
Possible error code : 299
What i am doing wrong?
As pointed by the comment above, your calculation of the target address is questionable.
Your use of GetLastError is unsafe - you should call it immediately after FAILED call to ReadProcessMemory. However, in this case, cout << ... doesn't change that code, so you are OK.
According to docs
ERROR_PARTIAL_COPY
299 (0x12B)
Only part of a ReadProcessMemory or WriteProcessMemory request was completed.
And this post states
ReadProcessMemory would return FALSE and GetLastError would return ERROR_PARTIAL_COPY when the copy hits a page fault.

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.

Why cant i see the string?

I know this code is sloppy, but I'm wondering why when I'm using the ReadProcessMemory() function I can't see the string stored in the relevant address.
//If the game window is open then this function grabs the process ID.
if(FinderCheck)
{
DWORD procID;
GetWindowThreadProcessId(hwnd, &procID);
//All so access you can read and write to process memory.
HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,procID);
if(procID = NULL)
{
cout << "Error: Failed to Obtain Process ID" << endl;
}
else
{
while(true)
{
//To read the process memory this line writes the memory data to buffer
//Remember to change the address every time you boot the process or it will not work.
ReadProcessMemory(handle, (PBYTE*)0xDC8F1AA904,&Cursor,sizeof(Cursor),0);
cout << Cursor << endl;
cout << "Test" << endl;
Sleep(500);
}
}
if(procID = NULL)
Sadly, this if statement will always evaluate to false, you are missing the second "=". This also means that from this point procID is NULL.
What about the rest of the code? Can you show us how the definition of the Cursor looks like and how do you implemented operator<<?

SymGetSymFromAddr64 works but SymGetLineFromAddr64 fails with error code 487

I'm learning StackWalk API provided by "DbgHelp" in Windows 7. I wrote an exception filter that uses StackWalk64. The intention is to back trace at most 50 lines with function name and line number. "StackWalk64" iterates through each stack frame. The address(AddrPC) retrieved from Stack Frame is used in "SymGetSymFromAddr64" and "SymGetLineFromAddr64" to retrieve Symbol name and Line number respectively. But, though "SymGetSymFromAddr64" works successfully, "SymGetLineFromAddr64" fails. The Last Error returned is 487. How can the address work successfully for the former but not for the latter?
Am I missing something? Any Help?
LONG WINAPI TestStackWalker (EXCEPTION_POINTERS* lpFilter)
{
STACKFRAME64 st;
CONTEXT cc;
HANDLE hProcess = ::GetCurrentProcess();
HANDLE hThread = ::GetCurrentThread();
vector<IMAGEHLP_SYMBOL64> vectSymbs(50);
vector<IMAGEHLP_LINE64> vectLines(50);
if (!SymInitialize(hProcess, NULL, TRUE))
{
cout << "Issue with SymInitialize ! " << ::GetLastError() << endl;
return 1;
}
cc = *(lpFilter->ContextRecord);
printContext(cc);
::ZeroMemory(&st, sizeof(st));
st.AddrStack.Mode = AddrModeFlat;
st.AddrStack.Offset = cc.Esp;
st.AddrFrame.Mode = AddrModeFlat;
st.AddrFrame.Offset = cc.Ebp;
st.AddrPC.Mode = AddrModeFlat;
st.AddrPC.Offset = cc.Eip;
for (int i = 0; i < 50; i++)
{
if (!::StackWalk64(IMAGE_FILE_MACHINE_I386,
hProcess,
hThread,
&st,
&cc,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL))
{
cout << "Issue with StackWalkFailed: " << ::GetLastError () <<endl;
return 1;
}
if (st.AddrReturn.Offset == st.AddrPC.Offset)
{
cout << "i think it's done!" << endl;
break;
}
if (st.AddrPC.Offset != 0)
{
vectSymbs[i].SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
vectSymbs[i].MaxNameLength = 1024;
if (!SymGetSymFromAddr64 (hProcess, st.AddrPC.Offset, 0, &vectSymbs[i]))
{
cout << "Issue with Getting Symbol From Address " << ::GetLastError() << endl;
break;
}
SymSetOptions(SYMOPT_LOAD_LINES);
vectLines[i].SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (!SymGetLineFromAddr64 (hProcess, st.AddrPC.Offset, 0, &vectLines[i]))
{
cout << "Issue with Getting Line from Address " << ::GetLastError() << endl;
break;
}
cout << vectSymbs[i].Name << " at " << vectLines[i].LineNumber <<endl;
}
if (st.AddrReturn.Offset == 0)
{
cout << "seems finished " << endl;
break;
}
}
return 1;
}
The pdwDisplacement parameter is not optional:
DWORD dis;
if (!SymGetLineFromAddr64 (hProcess, st.AddrPC.Offset, &dis, &vectLines[i]))
{
cout << "Issue with Getting Line from Address " << ::GetLastError() << endl;
break;
}
I had the same problem. My symbols were on a symbol server. The solution was to put symsrv.dll (from the Windows SDK) next to dbghelp.dll, so it can be loaded. Everything worked after that. In practice, the solution was to distribute both dbghelp.dll and symsrv.dll with my application (together with dbgcore.dll and srcsrv.dll). See the Modules list in Visual Studio, to verify that both symsrv.dll and dbghelp.dll are being loaded from where you expect.
More info here:
https://learn.microsoft.com/en-us/windows/desktop/Debug/using-symsrv
(See "Installation" section)
https://learn.microsoft.com/en-us/windows/desktop/Debug/dbghelp-versions (regarding obtaining and distributing those dlls)