Why is my LogError function resetting GetLastError? - c++

I created a library that gets the last error as a string and then outputs that error to a file but for some reason my SetLogLocation and LogError function makes GetLastError() return 0. It should not return 0 though because in the test below I am forcing GetModuleHandle to fail. Why is GetLastError() returning 0? Also, is handling errors such as fopen or GetModuleHandle failing in a library not recommended?
// test.exe
int main()
{
if (!GetModuleHandleA("xxx"))
{
SetLogLocation("c:\\users\\admin\\desktop\\error.log");
LogError("test");
}
// outputs 0
DWORD dwError = GetLastError();
std::cout << dwError;
std::cin.get();
return 0;
}
// test.dll
EXPORT void SetLogLocation(std::string path)
{
g_LogFile = std::ofstream(path, std::ios::app);
}
EXPORT void LogError(std::string errorText)
{
if (!g_LogFile.good())
return;
DWORD dwErrorCode = GetLastError();
if (dwErrorCode == 0)
{
Log(errorText);
}
else
{
std::stringstream ss;
ss << errorText << std::endl
<< "Last error: " << GetErrorAsString(dwErrorCode) << std::endl
<< "Error code: " << dwErrorCode;
Log(ss.str());
}
}
EXPORT std::string GetErrorAsString(DWORD dwErrorCode)
{
LPSTR lpBuffer = NULL;
DWORD dwSize = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dwErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&lpBuffer, 0, NULL);
std::string errorMessage(lpBuffer, dwSize);
if (errorMessage[errorMessage.length() - 1] == '\n')
errorMessage.erase(errorMessage.length() - 1, 1);
LocalFree(lpBuffer);
return errorMessage;
}

A call to most windows functions will reset GetLastError, you need to store the value of GetLastError as soon as possible after your failing function then use that value rather than calling GetLastError again.
SetLogLocation will clear GetLastError as it is opening a file. FormatMessageA inside LogError will also clear GetLastError.
If you want your logging functions to not alter GetLastError you can always save and restore the value at the end of your functions:
struct LastError
{
LastError()
: value(getLastError())
{
}
~LastError()
{
SetLastError(value);
}
DWORD value;
};
EXPORT void SetLogLocation(std::string path)
{
LastError lastError;
g_LogFile = std::ofstream(path, std::ios::app);
}
EXPORT void LogError(std::string errorText)
{
LastError lastError;
if (!g_LogFile.good())
return;
if (lastError.value == 0)
{
Log(errorText);
}
else
{
std::stringstream ss;
ss << errorText << std::endl
<< "Last error: " << GetErrorAsString(lastError.value) << std::endl
<< "Error code: " << lastError.value;
Log(ss.str());
}
}

Related

WaitForSingleObject with FindFirstChangeNotification not behaving as expected [duplicate]

This question already has answers here:
FindFirstChangeNotification is notifying about changes twice
(2 answers)
Closed 1 year ago.
I have to find out if there is a new file in a directory on Windows. Following this MSDN example (Obtaining Directory Change Notifications), I came up with the following test program:
#include <iostream>
#include <Windows.h>
#include <vector>
#include <string>
std::string FindNewFile(std::vector<std::string>& vsNewFileList, std::vector<std::string>& vsOldFileList)
{
std::string sNewFileName;
int nScore = 0;
for (auto& newFile : vsNewFileList)
{
nScore = 0;
for (auto& oldFile : vsOldFileList)
if(!newFile.compare(oldFile))
nScore++;
if (nScore!=1)
{
sNewFileName = newFile;
break;
}
}
return sNewFileName;
}
void GetCurrentFilesInDir(std::string sDir, std::vector<std::string>& vsFileList)
{
WIN32_FIND_DATA ffd;
sDir += "\\*";
std::wstring wStr = std::wstring(sDir.begin(), sDir.end());
LPCWSTR lpcwsDir = (LPCWSTR)wStr.c_str();
HANDLE hFind = FindFirstFile(lpcwsDir, &ffd);
if (hFind == INVALID_HANDLE_VALUE)
{
std::cout << "Nope\n";
return;
}
vsFileList.clear();
do
{
int nSize = WideCharToMultiByte(CP_ACP, 0, ffd.cFileName, -1, 0, 0, 0, 0);
char* pcStr = new char[nSize];
WideCharToMultiByte(CP_ACP, 0, ffd.cFileName, -1, pcStr, nSize, 0, 0);
//std::cout << pcStr << "\n";
vsFileList.push_back(std::string(pcStr));
delete[] pcStr;
} while (FindNextFile(hFind, &ffd) != 0);
}
int main()
{
// watch the foo directory for new files
std::string sDir = "C:\\foo";
std::vector<std::string> vsOldFileList, vsNewFileList;
GetCurrentFilesInDir(sDir, vsOldFileList);
std::wstring wStr = std::wstring(sDir.begin(), sDir.end());
LPCWSTR lpcwsDir = (LPCWSTR)wStr.c_str();
DWORD dwWaitStatus;
HANDLE dwChangeHandle;
dwChangeHandle = FindFirstChangeNotification(
lpcwsDir,
FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME);
while (TRUE)
{
// returns multiple times before and after new file appears!!
dwWaitStatus = WaitForSingleObject(dwChangeHandle, INFINITE);
switch(dwWaitStatus)
{
case WAIT_OBJECT_0:
GetCurrentFilesInDir(sDir, vsNewFileList);
std::string sNewFileName = FindNewFile(vsNewFileList, vsOldFileList);
std::cout << sNewFileName << "\n";
GetCurrentFilesInDir(sDir, vsOldFileList);
FindNextChangeNotification(dwChangeHandle);
break;
}
}
}
The problem is that, when I save a new file in C:\foo (for instance, using Notepad++ to "Save As" an open .txt file in C:\foo), the call to WaitForSingleObject() in the while loop will return 0 multiple times. Since my FindNewFile() method returns an empty string if there is no new file in the directory, I will get output like:
a.txt
or:
b.txt
Or even:
c.txt
c.txt
Can someone explain what I am missing here?
Using FindNextChangeNotification can not tell you what actually happened, and the operation of the file may involve multiple changes.
You can try to use ReadDirectoryChangesW and here is a sample:
#include <windows.h>
#include <iostream>
using namespace std;
wstring getname(FILE_NOTIFY_INFORMATION* tmp)
{
wstring s = L"";
for (int i = 0; i < tmp->FileNameLength / 2; i++)
s += tmp->FileName[i];
return s;
}
int main(int argc, const char* argv[])
{
HANDLE hDir;
char notify[1024];
DWORD cbBytes;
LPTSTR path;
FILE_NOTIFY_INFORMATION* pnotify = (FILE_NOTIFY_INFORMATION*)notify;
FILE_NOTIFY_INFORMATION* tmp;
// GetCurrentDirectory(MAX_PATH,path.GetBuffer(MAX_PATH+1));
path = (LPTSTR)L"D:\\test";
hDir = CreateFile(path, FILE_LIST_DIRECTORY,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_OVERLAPPED, NULL);
wcout << L"===CreateFile complete===" << endl;
if (hDir == INVALID_HANDLE_VALUE)
{
wcout << L"invalid handle value" << endl;
return -1;
}
FILE_NOTIFY_INFORMATION buffer[1024];
FILE_NOTIFY_INFORMATION* pbuffer;
while (TRUE)
{
wcout << L"waiting..." << endl;
WaitForSingleObject(hDir, INFINITE);
if (ReadDirectoryChangesW(hDir, &buffer, sizeof(buffer),
TRUE, FILE_NOTIFY_CHANGE_FILE_NAME,
&cbBytes, NULL, NULL))
{
pbuffer = buffer;
do {
tmp = pbuffer;
switch (tmp->Action)
{
case FILE_ACTION_ADDED:
wcout << L"Directory/File added - " << getname(tmp) << endl;
break;
case FILE_ACTION_REMOVED:
wcout << L"Directory/File removed - " << getname(tmp) << endl;
break;
case FILE_ACTION_MODIFIED:
wcout << L"Directory/File modfied - " << getname(tmp) << endl;
break;
case FILE_ACTION_RENAMED_OLD_NAME:
wcout << L"Directory/File old name - " << getname(tmp) << endl;
break;
case FILE_ACTION_RENAMED_NEW_NAME:
wcout << L"Directory/File new name - " << getname(tmp) << endl;
break;
default:
wcout << L"unknown action\n" << endl;
break;
}
pbuffer += pbuffer->NextEntryOffset;
} while (pbuffer->NextEntryOffset);
}
else
{
wcout << "readChangesW failed now return" << endl;
return -1;
}
}
}
When you do the Save As operation, you will find:
Therefore, multiple file operations were triggered when actually saving as, and you also performed multiple comparisons when processing new file comparisons, so empty characters were output.
More reference: FindFirstChangeNotification is notifying about changes twice

Windows registry returning incorrect value C++

The below code can correctly read Registry values from various different keys, however whenever I try to read a value from a key under Winlogon it will either come up as "not found" or it will return a completely wrong value. The code is ran as admin, and compiled with Visual Studio 2017.
HKEY registryHandle = NULL;
int registryResult = NULL;
DWORD dataType;
TCHAR dataBuffer[1024] = {};
DWORD bufferSize = sizeof(dataBuffer);
registryResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", 0, KEY_QUERY_VALUE, &registryHandle);
if (registryResult != ERROR_SUCCESS) {
std::cout << "Error: " << registryResult << std::endl;
return false;
}
registryResult = RegQueryValueEx(registryHandle, L"LastUsedUsername", NULL, NULL, (LPBYTE)dataBuffer, &bufferSize);
if (registryResult != ERROR_SUCCESS) {
std::cout << "Error2: " << registryResult << std::endl;
return false;
}
std::cout << "Data Size: " << bufferSize << std::endl;
for (int i = 0; i < 256; i++) {
if (dataBuffer[i] == NULL) { break; }
std::cout << (char)dataBuffer[i];
}
std::cin.get();
RegCloseKey(registryHandle);
Registry value that I'm trying to read:
Below refers to Remy's suggested solution.
RegQueryValueEx Returns a buffer size of 4 with an output of 18754 17236 0 52428
You are clearly calling the Unicode version of the Registry functions, so you should be using WCHAR instead of TCHAR for your data buffer.
And you should not be truncating the characters to char at all. Use std::wcout instead of std::cout for printing out Unicode strings. And use the returned bufferSize to know how many WCHARs were actually output. Your printing loop is ignoring the bufferSize completely, so it is possible that you are actually printing out random garbage that RegQueryValueEx() did not actually intend for you to use (hence why lpcbData parameter is an in/out parameter, so you know how many bytes are actually valid).
You are also leaking the opened HKEY handle if RegQueryValueEx() fails.
Try something more like this instead:
HKEY registryHandle;
int registryResult;
registryResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", 0, KEY_QUERY_VALUE, &registryHandle);
if (registryResult != ERROR_SUCCESS) {
std::cout << "Error: " << registryResult << std::endl;
return false;
}
WCHAR dataBuffer[1024];
DWORD bufferSize = sizeof(dataBuffer);
// TODO: consider using RegGetValueW() instead, which is safer
// when it comes to reading string values from the Registry...
registryResult = RegQueryValueExW(registryHandle, L"LastUsedUsername", NULL, NULL, (LPBYTE)dataBuffer, &bufferSize);
RegCloseKey(registryHandle);
if (registryResult != ERROR_SUCCESS) {
std::cout << "Error2: " << registryResult << std::endl;
return false;
}
DWORD len = bufferSize / sizeof(WCHAR);
if ((len > 0) && (dataBuffer[len-1] == L'\0')) {
--len;
}
std::cout << "Data Byte Size: " << bufferSize << std::endl;
std::cout << "Data Character Length: " << len << std::endl;
std::wcout.write(dataBuffer, len);
std::cin.get();
return true;
That being said, on my machine, there is no LastUsedUsername value in the Winlogon key you are accessing, so getting a "not found" error is a very likely possibility. But you definately need to handle

WINAPI Read all access rights for a custom file or a folder

I have a task based on an algorithm to receive the access rights for a certain file or folder and I've tried to implement it but some parts don't seem to be clear.
What I've been asked for:
1) Use the function GetNamedSecurityInfo(), for example:
GetNamedSecurityInfo(path,SE_FILE_OBJECT,DACL_SECURITY_INFORMATION,NULL,
NULL,&a,NULL, &pSD)
2) Futher, use an SID to receive the rights: Use
these functions to receive the SID: GetAclInformation(), then GetAce().
3) Now you can use the LookupAccountSid() function and if is was
successfull, compare pACE->Mask with all the constants, for example
"GENERIC_ALL, GENERIC_READ, GENERIC_WRITE, GENERIC_EXECUTE for files
etc." displaying the access rights.
And how I tried to implement this algorithm: // First getting process SID
PSID g_pSID;
BOOL GetCurrentProcessSID()
{
DWORD dwSize = 0, dwError, dwResult = 0;
HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
{
printf("OpenProcessToken Error %u\n", GetLastError());
return FALSE;
}
// Call GetTokenInformation to get the buffer size.
TOKEN_USER tU;
if (!GetTokenInformation(hToken, TokenUser, &tU, 0, &dwSize))
{
dwError = GetLastError();
if (dwError != ERROR_INSUFFICIENT_BUFFER)
{
std::cout << "GetTokenInformation failed, error " << dwError;
CloseHandle(hToken);
return 0;
}
}
PTOKEN_OWNER to = (PTOKEN_OWNER)LocalAlloc(LPTR, dwSize);
if (!to)
{
dwError = GetLastError();
std::cout << "LocalAlloc failed, error " << dwError;
CloseHandle(hToken);
return 0;
}
if (!GetTokenInformation(hToken, TokenOwner, to, dwSize, &dwSize))
{
dwError = GetLastError();
std::cout << "GetTokenInformation failed, error " << dwError;
LocalFree(to);
CloseHandle(hToken);
return 0;
}
g_pSID = to->Owner;
return TRUE;
}
//Then I used the iteration through the ACL list:
std::stringstream g_TestSecurityResult;
void TestSecurity( wchar_t* path )
{
g_TestSecurityResult = std::stringstream();
GetCurrentProcessSID();
PACL pDacl;
PSECURITY_DESCRIPTOR pSD;
GetNamedSecurityInfoW(path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pDacl, NULL, &pSD);
ACL_SIZE_INFORMATION aclSizeInfo = { sizeof(ACL) };
BOOL fResult = GetAclInformation( pDacl, &aclSizeInfo, sizeof(ACL_SIZE_INFORMATION), ACL_INFORMATION_CLASS::AclSizeInformation );
if (fResult)
{
for (DWORD dwIndex = 0; dwIndex < aclSizeInfo.AceCount; ++dwIndex)
{
LPVOID pTempAce = nullptr;
fResult = ::GetAce(pDacl, dwIndex, &pTempAce);
if (fResult)
{
PSID pSid = &((ACCESS_ALLOWED_ACE*)pTempAce)->SidStart;
if (EqualSid(pSid, &g_pSID))
{
g_TestSecurityResult << "User: " << userNameFromSid(&g_pSID) << std::endl;
g_TestSecurityResult << "\tAccess mode: " << ((EXPLICIT_ACCESS*)pTempAce)->grfAccessMode << "\n";
g_TestSecurityResult << "\tAccess permissions: " << ((EXPLICIT_ACCESS*)pTempAce)->grfAccessPermissions << "\n";
g_TestSecurityResult << "\tInheritance: " << ((EXPLICIT_ACCESS*)pTempAce)->grfInheritance << "\n";
g_TestSecurityResult << std::endl;
}
}
else
{
g_TestSecurityResult << "GetAce() failed." << GetLastError();
break;
}
}
} else {
g_TestSecurityResult << "Error in GetAclInformation(): " << GetLastError();
}
}
std::string userNameFromSid(PSID userSid)
{
char buffName[MAX_BUFF_SIZE];
DWORD buffNameSize = MAX_BUFF_SIZE;
char buffDomain[MAX_BUFF_SIZE];
DWORD buffDomainSize = MAX_BUFF_SIZE;
SID_NAME_USE SidType;
if (LookupAccountSid(NULL, userSid, buffName, &buffNameSize, buffDomain, &buffDomainSize, &SidType)) {
return buffName;
} else {
DWORD dwResult = GetLastError();
printf("GetTokenInformation Error %u\n", dwResult);
}
/*Here some code to print error in a Message box*/
return "";
}
The problem: The code is working but at the line if (EqualSid(pSid, &g_pSID)) debugger goes through and skips the SID that I've received from my process. In other words, I can't get any information from the ACL list, even though I'm running the process and Visual Studio under Administrator account (not using the "Run as Administrator", and I'll try it but still... The received SID is valid and I can get the name of the process owner using the above function. What could be something that I'm doing wrong here?
Code sources:
https://gist.github.com/m4x1m1l14n/37f39c5d25855c2b1d3a6334851f7549
How to get the logged-on user's SID in Windows
GetTokenInformation, TOKEN_OWNER, и LookupAccountSidA
Thanks to #(Rita Han - MSFT) comment, I've forgotten to remove the ampersand mark from the pSID comparison
EqualSid(pSid, g_pSID);
instead of
EqualSid(pSid, &g_pSID);
And It's properly working for now

SHGetValue returns 2 when querying UAC value

I would like to check the UAC configuration settings of windows. And thus recover the parameters of the UAC in the registry keys.
I used the windows SHGetValue function but the status always returns me 2 without any information.
I use C++11, MinGW and windows.
My code is :
DWORD dwStatus;
LPCSTR pszSubKey= "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
LPCSTR pszValue="";
DWORD pdwType=REG_SZ;
PVOID pvData[63];
DWORD pcbData;
pcbData=sizeof(pvData);
dwStatus=SHGetValueA(HKEY_LOCAL_MACHINE, pszSubKey, pszValue, &pdwType, pvData, &pcbData);
//Here dwStatus = 2
// pvData = 0x11fd0b2
// pcbData = 504
What specific key you are trying to read? I am not an expert on win32 API so I don't know whether there is a way to read a set of keys at once (Edit: I think there areRegEnumValue/RegEnumValueA functions for this purpose). Here is an example that shows how you can read "EnableLUA" or any other key from that path:
#include <windows.h>
#include <iostream>
#include <shlwapi.h>
bool ReadUACRegistryKey(char* key, DWORD &keyValue)
{
LPCTSTR pszSubKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
LPCTSTR pszValue = key;
// don't care
DWORD dwType = 0;
DWORD dwValue = 0;
//
DWORD dwValueSize = sizeof(dwValue);
int retval = SHGetValue( HKEY_LOCAL_MACHINE, pszSubKey, key, &dwType, &dwValue, &dwValueSize);
if ( retval != ERROR_SUCCESS)
{
return false;
}
keyValue = dwValue;
return true;
}
int main()
{
DWORD keyValue;
char* key = "EnableLUA"; // "EnableSecureUIAPaths" etc..;
if (ReadUACRegistryKey(key, keyValue))
{
std::cout << "Successfully readed key " << key << ", value:" << keyValue << std::endl;
}
else
{
std::cout << "Unable to read value of key " << key << std::endl;
}
return 0;
}
Also keep in mind that value of read key value is stored in value parameter, not in the return value of the function.
Edit: Answer of the the op's comment "I want use FilterAdministratorToken but is disable by default how give it back enable .?". Keep in mind that your process need to have admin rights to perform these operation.
#include <windows.h>
#include <iostream>
#include <shlwapi.h>
bool ReadUACRegistryKey(char* key, DWORD &keyValue)
{
LPCTSTR pszSubKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
LPCTSTR pszValue = key;
// don't care
DWORD dwType = 0;
DWORD dwValue = 0;
//
DWORD dwValueSize = sizeof(dwValue);
int retval = SHGetValue( HKEY_LOCAL_MACHINE, pszSubKey, key, &dwType, &dwValue, &dwValueSize);
if ( retval != ERROR_SUCCESS)
{
return false;
}
keyValue = dwValue;
return true;
}
bool EnableFilterAdministratorToken()
{
// first check if its already enabled or not
DWORD val;
if (ReadUACRegistryKey("FilterAdministratorToken", val))
{
if (val == 1)
{
std::cout << "FilterAdministratorToken is already enabled" << std::endl;
return true;
}
}
else
{
std::cout << "Unable to read key" << std::endl;
return false;
}
// its not enabled, we need to enable it manually
// obtain a handle to reg key
HKEY hKey;
int retval = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", 0, KEY_SET_VALUE, &hKey);
if (retval != ERROR_SUCCESS)
{
// we are unable to obtain a handle to reg key
std::cout << "Unable to obtain handle to reg key" << std::endl;
return false;
}
DWORD enabledValue = 1;
retval = RegSetValueExA(hKey, "FilterAdministratorToken", 0, REG_DWORD, (BYTE*) &enabledValue, sizeof(DWORD));
if (retval != ERROR_SUCCESS)
{
// some error occured
std::cout << "Some error occured during setting the key value" << std::endl;
RegCloseKey(hKey);
return false;
}
std::cout << "Successfully changed key value" << std::endl;
RegCloseKey(hKey);
return true;
}
int main()
{
if (EnableFilterAdministratorToken())
{
std::cout << "OK" << std::endl;
}
else
{
std::cout << "FAIL" << std::endl;
}
return 0;
}

C++ ReadProcessMemory receiving 998 / 3E6 Error

So I'm trying to read Memory out of a running exe with ReadProcessMemory() as you can see in the code provided below.
The only problem I constantly run into is that I receive the Error 3E6 / 998 which seems to be NOACCESS but I cant find a solution to fix this.
And yes I tried to run the exe in Admin Mode without success...
#include <Windows.h>
#include <iostream>
#include <string>
#include <tlhelp32.h>
#include <Psapi.h>
#include <tchar.h>
using namespace std;
int id = NULL;
HANDLE hProcess = NULL;
int getPID(const string name);
bool setHandle(int id, HANDLE &out);
DWORD64 GetModule(const string name);
int main()
{
bool success = false;
id = getPID("sample.exe");
string name = "SAMPLE";
cout << "Process Name: " << name << endl;
cout << "Process ID: " << id << endl;
success = setHandle(id, hProcess);
if (success)
{
cout << "Handle set..." << endl;
}
else if (!success)
{
cout << "You need to have SOMETHING opened..." << endl;
cout << "ERROR CODE: " << GetLastError() << endl;
system("pause");
return 1;
}
success = false;
DWORD64 baseAddress = GetModule("sample.exe");
DWORD64 ammo = 0x24ED13273A8;
DWORD64 addr = baseAddress + ammo;
cout << "Base Address: " << hex << uppercase << "0x" << baseAddress << endl;
cout << "Ammo Address: " << hex << uppercase << "0x" << ammo << endl;
cout << "Complete Address: " << hex << uppercase << "0x" << addr << endl;
int buffer = 0;
success = ReadProcessMemory(hProcess, (LPCVOID)addr, (LPVOID)&buffer, sizeof(&buffer), NULL);
if (success)
{
cout << "ReadProccess succeeded..." << endl;
system("pause");
return 0;
}
else if (!success)
{
cout << "ERROR CODE: " << GetLastError() << endl;
system("pause");
return 1;
}
system("pause");
return 0;
}
bool setHandle(int id, HANDLE &out)
{
out = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);
if (!out) return false;
return true;
}
int getPID(const string name)
{
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (!Process32First(snapshot, &entry)) return NULL;
do
{
if (strcmp(entry.szExeFile, name.c_str()) == 0)
{
CloseHandle(snapshot);
return entry.th32ProcessID;
}
} while (Process32Next(snapshot, &entry));
CloseHandle(snapshot);
return NULL;
}
DWORD64 GetModule(const string name)
{
HMODULE hMods[1024];
DWORD cbNeeded;
if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
{
for (int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
{
TCHAR szModName[MAX_PATH];
if (GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR)))
{
string modName = szModName;
if (modName.find(name) != string::npos)
{
return (DWORD64)hMods[i];
}
}
}
}
return NULL;
}
I'm kinda new to c++... so sry? :)
There are actually two basic mistakes in your code, both of which, unfortunately for you, me and the rest of the civilised world, generate the same error code. Was it ever thus. There is also a logic error, but you are lucky enough to be getting away with it (just about). I commented the fix in the code I posted below.
There are also a number of 'good practise' shortcomings in your code, specifically:
NULL should not be used to represent integer zero
All error conditions should be checked for and (sensibly!) reported
You use the same string literal in two different places (so if you ever change it, you would need to change it in both places, and you might forget). So don't do that.
using namespace std; is widely frowned upon (because it causes such a lot of namespace pollution)
Why are id and hProcess global variables? This is just plain unnecessary.
You should give your functions more descriptive names, setHandle being the one I particularly have in mind. I got rid of that one completely.
When passing a std::string as a read-only function parameter, it is usually best to pass it as const ref, then it doesn't need to be copied.
Only use std::endl when you actually want to flush the buffer. It is inefficient.
Clean up after you (in this case, close any open handles). I know this is just a throwaway program but it's a good habit to get into.
OK, so here's some code that works (I have posted my own because I cleaned up all of the above). The substantive changes are:
To read the memory of another process, you need to give your user token the SE_DEBUG_NAME privilege. This in turn means you need to run your program as Administrator (aka elevated).
You cannot (obviously) read from a nonsense address in the target process so I just quietly fixed that.
Like I say, both of these generate the same error code. Huh!
OK, here you go. Enjoy:
#include <Windows.h>
#include <iostream>
#include <string>
#include <tlhelp32.h>
#include <Psapi.h>
#include <tchar.h>
int getPID(const std::string& name);
DWORD64 GetModule(HANDLE hProcess, const std::string& name);
// Stolen from: https://learn.microsoft.com/en-gb/windows/desktop/SecAuthZ/enabling-and-disabling-privileges-in-c--
BOOL SetPrivilege(
HANDLE hToken, // access token handle
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
TOKEN_PRIVILEGES tp;
LUID luid;
if ( !LookupPrivilegeValue(
NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid ) ) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if ( !AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
constexpr const char* theProcess = "notepad.exe";
int main()
{
HANDLE hToken;
BOOL ok = OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
if (!ok)
{
std::cout << "OpenProcessToken failed, error " << GetLastError() << "\n";
return 255;
}
ok = SetPrivilege (hToken, SE_DEBUG_NAME, TRUE);
if (!ok)
{
CloseHandle (hToken);
return 1;
}
int pid = getPID (theProcess);
HANDLE hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
if (hProcess == NULL)
{
std::cout << "OpenProcess failed, error " << GetLastError() << "\n";
CloseHandle (hToken);
return 1;
}
DWORD64 baseAddress = GetModule(hProcess, theProcess);
std::cout << "Base Address: " << std::hex << std::uppercase << "0x" << baseAddress << "\n";
int buffer = 0; // Note: sizeof (buffer) below, not sizeof (&buffer)
ok = ReadProcessMemory(hProcess, (LPCVOID)baseAddress, (LPVOID)&buffer, sizeof(buffer), NULL);
CloseHandle (hProcess);
CloseHandle (hToken);
if (ok)
{
std::cout << "ReadProcessMemory succeeded, buffer = " << buffer << "\n";
system("pause");
return 0;
}
std::cout << "ReadProcessMemory failed, error " << GetLastError() << "\n";
system("pause");
return 1;
}
int getPID(const std::string& name)
{
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (!Process32First(snapshot, &entry)) return NULL;
do
{
if (strcmp(entry.szExeFile, name.c_str()) == 0)
{
CloseHandle(snapshot);
return entry.th32ProcessID;
}
} while (Process32Next(snapshot, &entry));
CloseHandle(snapshot);
return NULL;
}
DWORD64 GetModule(HANDLE hProcess, const std::string& name)
{
HMODULE hMods[1024];
DWORD cbNeeded;
if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
{
for (int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
{
TCHAR szModName[MAX_PATH];
if (GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR)))
{
std::string modName = szModName;
if (modName.find(name) != std::string::npos)
{
return (DWORD64)hMods[i];
}
}
}
}
return NULL;
}
Output (when run as Administrator):
Base Address: 0x7FF6D8470000
ReadProcessMemory succeeded, buffer = 905A4D
Output (when run as a normal user):
The token does not have the specified privilege.
You can also grab some code over at GitHub.