i'm trying to read the memory of a game called: "Prison Architect",
but i always have this value: "Money :-858993460"
I lost 2 days without have found any solutions.
I hope you can help me.
Pointer Info:
BaseAddress: "prison architect.exe"+00075140
Pointer1: 300
Pointer2: 88
Pointer3: 26c
Pointer4: 70
Pointer5: 328
int FindPointer(int offset, HANDLE pHandle, int baseaddr, int offsets[])
{
int Address = baseaddr;
int total = offset;
for (int i = 0; i < total; i++) //Loop trough the offsets
{
ReadProcessMemory(pHandle, (LPCVOID)Address, &Address, 4, NULL);
Address += offsets[i];
}
return Address;
}
int main()
{
int value = 0; // This will store our value. In my case, its an integer, which is the timer
DWORD pid; //This will store our Process ID, used to read/write into the memory
HWND hwnd; //Finally a handle to our window
hwnd = FindWindow(NULL, L"Prison Architect"); //Finds the Window
if (!hwnd) //If none, display an error
{
cout << "Window not found!\n";
cin.get();
}
GetWindowThreadProcessId(hwnd, &pid); //Get the process id and place it in pid
HANDLE phandle = OpenProcess(PROCESS_VM_READ, 0, pid); //Get permission to read
if (!phandle) //Once again, if it fails, tell us
{
cout << "Could not get handle!\n";
cin.get();
}
int baseAddress = 0x00197BE8;
int offsets[] = { 0x300, 0x88, 0x26c, 0x70, 0x328 };
while (1) {
int moneyRead = FindPointer(5, phandle, baseAddress, offsets); //number of offsets, HANDLE, base address, offsets
//reading
int money;
ReadProcessMemory(phandle, (LPVOID)moneyRead, &money, sizeof(money), 0);
cout << "\nMoney :" << money;
Sleep(3000);
}
return 0;
}
int baseAddress = 0x00197BE8;
You're using a hard coded address, which is bad practice because it may only work for this one instance of running the game process. When you re-open the game it probably will change.
The pointer must begin with an address that has a relative offset from a module. You then add the relative offset to the address of the module which you have calculated at run time. This will make it so your final address that is calculated always works correctly, regardless of ASLR.
You can use this function to find the base address of a module
uintptr_t GetModuleBaseAddress(DWORD procId, const wchar_t* modName)
{
uintptr_t modBaseAddr = 0;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, procId);
if (hSnap != INVALID_HANDLE_VALUE)
{
MODULEENTRY32 modEntry;
modEntry.dwSize = sizeof(modEntry);
if (Module32First(hSnap, &modEntry))
{
do
{
if (!_wcsicmp(modEntry.szModule, modName))
{
modBaseAddr = (uintptr_t)modEntry.modBaseAddr;
break;
}
} while (Module32Next(hSnap, &modEntry));
}
}
CloseHandle(hSnap);
return modBaseAddr;
}
Add the relative offset to the return value and use this as the base address of the pointer you pass into your FindPointer() function.
Related
So I have been trying to get into memory reading in C++ and I thought a cool project would be to read all addresses a process is using (similar to how Cheat Engine works).
I started by reading
Link1: Read Memory of Process C++
Link2: Read memory of 64bit process address
Link3: http://www.cplusplus.com/forum/general/42132/
And I also watched a tutorial on youtube where he explained how a process (game) worked with addresses. Link to youtube video: https://www.youtube.com/watch?v=wiX5LmdD5yk
This resulted in me making three different methods:
DWORD GetProcId(const wchar_t* procName) {
DWORD pid = 0;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hSnap != INVALID_HANDLE_VALUE) {
PROCESSENTRY32 procEntry;
procEntry.dwSize = sizeof(procEntry);
if(Process32First(hSnap, &procEntry)) {
do {
if(!_wcsicmp(procEntry.szExeFile, procName)) {
pid = procEntry.th32ProcessID;
break;
}
} while (Process32Next(hSnap, &procEntry));
}
}
CloseHandle(hSnap);
return pid;
}
This method is to get the process-id which I could also just type manually by finding the same PID in task-manager (which gave me the same baseaddress later on).
uintptr_t GetModuleBaseAddress(DWORD procId, const wchar_t* modName) {
uintptr_t modBaseAddr = 0;
//I use 0x10 instead of TH32CS_SNAPMODULE32 since it didnt work and according to documentation
// this is the value it should have.
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | 0x10, procId);
if(hSnap != INVALID_HANDLE_VALUE) {
MODULEENTRY32 modEntry;
modEntry.dwSize = sizeof(modEntry);
if(Module32First(hSnap, &modEntry)) {
do {
if(!_wcsicmp(modEntry.szModule, modName)) {
modBaseAddr = (uintptr_t)modEntry.modBaseAddr;
break;
}
} while(Module32Next(hSnap, &modEntry));
}
}
CloseHandle(hSnap);
return modBaseAddr;
}
This method (I assume) will return the base address of the process. Which for example in my code I tried to find the base address of the discord.exe process. When discord.exe wasn't running I got 0 and when it was running I got an address (which I believe is the correct base address, correct me if I am wrong).
And my main method:
int main() {
DWORD procId = GetProcId(L"Discord.exe");
uintptr_t moduleBase = GetModuleBaseAddress(procId, L"Discord.exe");
HANDLE hProcess = 0;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, procId);
uintptr_t dynamicPtrBaseAddr = moduleBase;
std::cout << "Dynamic: " << dynamicPtrBaseAddr << std::endl;
int value = 0;
int arr [10000] = {};
for (int i = 0; i < 100000; i++) {
ReadProcessMemory(hProcess, (BYTE*)dynamicPtrBaseAddr, &value, sizeof(value),0);
dynamicPtrBaseAddr += 1;
arr[i] = value;
}
}
Where I try to put all the values of all 100000 addresses in an array.
So my questions are:
Have I retrieved the base address of the process correctly?
For reading the other addresses I just increase dynamicPtrBaseAddr by 1, is there a better way to implement an offset? Or is this the correct way?
Now I increase the base address by 100000. Can I find the last address of the process instead?
I compile with g++ main.cpp -o test -lpsapi -DUNICODE (MinGW).
You must run as administrator and you must compile for the same bitness as the target process.
You should not be reading 1 byte at a time, especially in an external hack.
You should be using VirtualQueryEx() to properly loop through only proper memory regions:
DWORD procid = GetProcId("ac_client.exe");
unsigned char* addr = 0;
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procid);
MEMORY_BASIC_INFORMATION mbi;
while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)))
{
if (mbi.State == MEM_COMMIT && mbi.Protect != PAGE_NOACCESS && mbi.Protect != PAGE_GUARD)
{
std::cout << "base : 0x" << std::hex << mbi.BaseAddress << " end : 0x" << std::hex << (uintptr_t)mbi.BaseAddress + mbi.RegionSize << "\n";
}
addr += mbi.RegionSize;
}
This ensures you're only reading proper memory. Next you would reach each region into a local buffer in one go, this will increase speed dramatically as you won't have the overhead of calling the API for each byte.
What you're looks like you're trying to do is pattern scanning. You can find our pattern scanning tutorials in the same place you found the original tutorial you were following.
I've been trying to develop a triggerbot for csgo. I had a few errors, but i managed to fix them.
However, i have received the most annoying error EVER. I'm not sure how to go about fixing this, i tried making a header and doing #include <nameofmyfilewitherror> and it does not work. I wanted some help on this - I apologize for not using the code tool.
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#define m_iTeamNum 0xF4
#define dwLocalPlayer 0xD29B0C
#define dwEntityList 0x4D3D6AC
#define m_iCrosshairId 0xB3D4
uintptr_t moduleBase;
DWORD procId;
HWND hwnd;
HANDLE hProcess;
uintptr_t GetModuleBaseAddress(const char* modName) {
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, procId);
if (hSnap != INVALID_HANDLE_VALUE) {
MODULEENTRY32 modEntry;
modEntry.dwSize = sizeof(modEntry);
if (Module32First(hSnap, &modEntry)) {
do {
if (!strcmp(modEntry.szModule, modName)) {
CloseHandle(hSnap);
return (uintptr_t)modEntry.modBaseAddr;
}
} while (Module32Next(hSnap, &modEntry));
}
}
}
template<typename T> T RPM(SIZE_T address) {
T buffer;
ReadProcessMemory(hProcess, (LPCVOID)address, &buffer, sizeof(T), NULL);
return buffer;
}
uintptr_t getLocalPlayer() { //This will get the address to localplayer.
return RPM< uintptr_t>(moduleBase + dwLocalPlayer);
}
uintptr_t getPlayer(int index) { //Each player in the game has an index.
return RPM< uintptr_t>(moduleBase + dwEntityList + index * 0x10); //We use index times 0x10 because the distance between each player 0x10.
}
int getTeam(uintptr_t player) {
return RPM<int>(player + m_iTeamNum);
}
int getCrosshairID(uintptr_t player) {
return RPM<int>(player + m_iCrosshairId);
}
// errors "expected a declaration" and
// "'{': missing function header (old-style formal list?)" here
{
hwnd = FindWindowA(NULL, "Counter-Strike: Global Offensive");
GetWindowThreadProcessId(hwnd, &procId);
moduleBase = GetModuleBaseAddress("client_panorama.dll");
hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, procId);
while (!GetAsyncKeyState(VK_END)) {
int CrosshairID = getCrosshairID(getLocalPlayer());
int CrosshairTeam = getTeam(getPlayer(CrosshairID - 1));
int LocalTeam = getTeam(getLocalPlayer());
if (CrosshairID > 0 && CrosshairID < 32 && LocalTeam != CrosshairTeam)
{
if (GetAsyncKeyState(VK_MENU /*alt key*/))
{
mouse_event(MOUSEEVENTF_LEFTDOWN, NULL, NULL, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, NULL, NULL, 0, 0);
Sleep(100); //Optional
}
}
}
}
Nothing I do seems to fix these 2 errors. I get
expected a declaration
and
'{': missing function header (old-style formal list?)
You said the errors are on line 53, which is the { before the call to hwnd = FindWindowA(NULL, "Counter-Strike: Global Offensive"); That code block, following the definition of the getCrosshairID() function, is not inside any function, such as main(). You can't execute that code in global scope.
Also, on a side note, if GetModuleBaseAddress() does not find a matching process, hSnap is leaked and the return value is indeterminate.
Also, mouse_event() is deprecated, use SendInput() instead.
Try this instead:
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#define m_iTeamNum 0xF4
#define dwLocalPlayer 0xD29B0C
#define dwEntityList 0x4D3D6AC
#define m_iCrosshairId 0xB3D4
uintptr_t moduleBase;
DWORD procId;
HWND hwnd;
HANDLE hProcess;
uintptr_t GetModuleBaseAddress(const char* modName) {
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, procId);
if (hSnap != INVALID_HANDLE_VALUE) {
MODULEENTRY32 modEntry;
modEntry.dwSize = sizeof(modEntry);
if (Module32First(hSnap, &modEntry)) {
do {
if (strcmp(modEntry.szModule, modName) == 0) {
CloseHandle(hSnap);
return (uintptr_t)modEntry.modBaseAddr;
}
} while (Module32Next(hSnap, &modEntry));
}
CloseHandle(hSnap); // <-- ADD THIS!!!
}
return 0; // <-- ADD THIS!!!
}
template<typename T> T RPM(SIZE_T address) {
T buffer;
ReadProcessMemory(hProcess, (LPCVOID)address, &buffer, sizeof(T), NULL);
return buffer;
}
uintptr_t getLocalPlayer() { //This will get the address to localplayer.
return RPM< uintptr_t>(moduleBase + dwLocalPlayer);
}
uintptr_t getPlayer(int index) { //Each player in the game has an index.
return RPM< uintptr_t>(moduleBase + dwEntityList + index * 0x10); //We use index times 0x10 because the distance between each player 0x10.
}
int getTeam(uintptr_t player) {
return RPM<int>(player + m_iTeamNum);
}
int getCrosshairID(uintptr_t player) {
return RPM<int>(player + m_iCrosshairId);
}
int main() // <-- ADD THIS!!!
{
hwnd = FindWindowA(NULL, "Counter-Strike: Global Offensive");
if (!hwnd)
return 0;
GetWindowThreadProcessId(hwnd, &procId);
moduleBase = GetModuleBaseAddress("client_panorama.dll");
if (!moduleBase)
return 0;
hProcess = OpenProcess(PROCESS_VM_READ, NULL, procId);
if (!hProcess)
return 0;
while (!GetAsyncKeyState(VK_END)) {
int CrosshairID = getCrosshairID(getLocalPlayer());
int CrosshairTeam = getTeam(getPlayer(CrosshairID - 1));
int LocalTeam = getTeam(getLocalPlayer());
if (CrosshairID > 0 && CrosshairID < 32 && LocalTeam != CrosshairTeam)
{
if (GetAsyncKeyState(VK_MENU /*alt key*/))
{
/*
mouse_event(MOUSEEVENTF_LEFTDOWN, NULL, NULL, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, NULL, NULL, 0, 0);
*/
INPUT inputs[2] = {};
inputs[0].type = INPUT_MOUSE;
inputs[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
inputs[1].type = INPUT_MOUSE;
inputs[1].mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(2, inputs, sizeof(INPUT));
Sleep(100); //Optional
}
}
}
CloseHandle(hProcess);
return 0;
}
When you compile a C++ file, the compiler searches for the main() function, and sets that as a starting point for the code. Other functions in the code are called from inside of the main() function. In your post, the code after line 53 is not inside any function, so the compiler doesn't know how to compile it.
I believe you haven't written the code yourself, and if that is the case, I recommend learning a little more of C++, and then start writing your own triggerbot.
i'm working on a program where i need the handle of the program to be able to be used in the other classes and without changing the value, ive tried changing class to struct, didn't work, i tried to copy the handle into the protected part of attach. So how do i implement the varible handle to other classes.
struct attach
{
//Tried this HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, NULL, ProcID);
void Attach(char* name)
{
HWND hwnd = FindWindowA(NULL, name);
if (hwnd == NULL)
{
MessageBoxA(NULL, "Failed to attach Window", "attention", MB_OK | MB_ICONERROR);
exit(0);
}
else
{
cout << hwnd << endl;
DWORD ProcID;
GetWindowThreadProcessId(hwnd, &ProcID);
if(handle == NULL)
{
MessageBoxA(NULL, "Failed to obtain Process ID", "attention", MB_OK | MB_ICONERROR);
HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, NULL, ProcID);
}
else
{
cout << ProcID << endl;;
cout << handle;
}
}
}
protected:
};
class Sonic_Heroes : protected attach
{
protected:
void cp()
{
ReadProcessMemory(handle, (PBYTE*)RINGS, &NEWRINGS, sizeof(int), 0);
ReadProcessMemory(handle, (PBYTE*)LIVES, &NEWLIVES, sizeof(int), 0);
ReadProcessMemory(handle, (PBYTE*)TEAMBLAST, &NEWTEAMBLAST, sizeof(int), 0);
ReadProcessMemory(handle, (PBYTE*)FLIGHTHEIGHT, &NEWFLIGHTHEIGHT, sizeof(float), 0);
}
char name[18]= "SONIC HEROES(TM)";
const DWORD RINGS = 0x009DD70C;
const DWORD LIVES = 0x009DD74C;
const DWORD TEAMBLAST = 0x009DD72C;
const DWORD FLIGHTHEIGHT = 0x00789FA4;
int NEWRINGS;
int NEWLIVES;
int NEWTEAMBLAST;
float NEWFLIGHTHEIGHT;
};
class Sonic_Mania : protected attach
{
protected:
void CP()
{
ReadProcessMemory(handle, (PBYTE*)RINGS, &NEWRINGS, sizeof(int), 0);
ReadProcessMemory(handle, (PBYTE*)SCORE, &NEWSCORE, sizeof(int), 0);
ReadProcessMemory(handle, (PBYTE*)LIVES, &NEWLIVES, sizeof(int), 0);
}
char name[13]= "Sonic mania";
const DWORD RINGS = 0x00A4D644;
const DWORD SCORE = 0x00A4D654;
const DWORD LIVES = 0x00A4D650;
int NEWRINGS;
int NEWSCORE;
int NEWLIVES;
};
HANDLE handle is a local variable in the method Attach, not part of the class/struct. Create a HANDLE member in the struct and it could work (cannot try it):
struct attach
{
HANDLE handle; // if handle has no default constructor you need to use pointers
void Attach(char* name)
{
HWND hwnd = FindWindowA(NULL, name);
if (hwnd == NULL)
{
MessageBoxA(NULL, "Failed to attach Window", "attention", MB_OK | MB_ICONERROR);
exit(0);
}
else
{
cout << hwnd << endl;
DWORD ProcID;
GetWindowThreadProcessId(hwnd, &ProcID); // here an if-statement is missing i think?
if(handle == NULL)
{
MessageBoxA(NULL, "Failed to obtain Process ID", "attention", MB_OK | MB_ICONERROR);
handle = OpenProcess(PROCESS_ALL_ACCESS, NULL, ProcID);
}
else // this won't compile
{
cout << ProcID << endl;;
cout << handle;
}
}
}
};
I am trying to fiddle around with CounterStrike in order to learn something about memory editing in C++. I used an offset dumper in order to obtain the static pointers and offsets that will lead to the temporary adresses of the dw_LocalPlayer, m_fFlags and dw_ForceJump. I am not using any memory editing classes like VAMemory.dll, just ReadProcessMemory. I found out that when the player is in the air, the value of m_fFlags is "256". When he is on the ground it is "257". However, I am not able to read these to values once I obtained a temporary adress. Here is the code:
// ConsoleApplication1.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
//
#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#include <cstdint>
#define dwLocalPlayer 0xAB06EC
#define dwForceJump 0x4D6A684
#define fFlags 0x100
int main()
{
HWND hwnd = FindWindowA(NULL, "Counter-Strike: Global Offensive");
if (hwnd == NULL)
{
std::cout << "Error!" << std::endl;
exit(-1);
system("PAUSE");
}
else
{
DWORD pid = GetWindowThreadProcessId(hwnd, &pid);
HANDLE pHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
DWORD tempadress;
ReadProcessMemory(pHandle, (PBYTE*)dwLocalPlayer, &tempadress, sizeof(tempadress), NULL);
DWORD fl_Onground = tempadress + fFlags;
std::cout << fl_Onground << "\n" << &fl_Onground << std::endl;
system("PAUSE");
}
return 0;
}
I would really appreciate some help and suggestions to the code since Im stuck on this since a few days. I am only trying to gain knowledge on C++ here, I do not want to code any cheats or whatnot...
You are not using the offsets correctly. The offsets that are supplied by the dumper need to be added to other addresses, such as the base address of the module.
dwLocalPlayer is not a pointer to the local player, it's an offset. You have to add it to the address of client_panorama.dll.
Secondly:
DWORD fl_Onground = tempadress + fFlags;
This gives you the address of fl_Onground but you never read it's value, your subsequent std::cout will print the address not the value.
Fixed code here:
#include <iostream>
#include <Windows.h>
#include <cstdint>
#include <TlHelp32.h>
#define dwLocalPlayer 0xD30B94
#define dwForceJump 0x51EE680
#define m_fFlags 0x104
uintptr_t GetModuleBaseAddress(DWORD procId, const wchar_t* modName)
{
uintptr_t modBaseAddr = 0;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, procId);
if (hSnap != INVALID_HANDLE_VALUE)
{
MODULEENTRY32 modEntry;
modEntry.dwSize = sizeof(modEntry);
if (Module32First(hSnap, &modEntry))
{
do
{
if (!_wcsicmp(modEntry.szModule, modName))
{
modBaseAddr = (uintptr_t)modEntry.modBaseAddr;
break;
}
} while (Module32Next(hSnap, &modEntry));
}
}
CloseHandle(hSnap);
return modBaseAddr;
}
int main()
{
HWND hwnd = FindWindowA(NULL, "Counter-Strike: Global Offensive");
if (hwnd == NULL)
{
std::cout << "Error!\n" << std::endl;
exit(-1);
system("PAUSE");
}
else
{
DWORD pid = GetWindowThreadProcessId(hwnd, &pid);
HANDLE pHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
uintptr_t dllBaseAddress = 0;
dllBaseAddress = GetModuleBaseAddress(pid, L"client_panorama.dll");
DWORD tempadress;
ReadProcessMemory(pHandle, (BYTE*)(dllBaseAddress + dwLocalPlayer), &tempadress, sizeof(tempadress), NULL);
BYTE fl_Onground = 0;
ReadProcessMemory(pHandle, (BYTE*)(tempadress + m_fFlags), &fl_Onground, sizeof(fl_Onground), NULL);
std::cout << fl_Onground << "\n" << &fl_Onground << std::endl;
getchar();
}
return 0;
}
I am trying to read an address from a running process. In order to do this, I have determined the offset of the address from a certain dll of the running process.
So far what I have done is I find the window, I than find the PID of the program and get the handle of it. From there I create a snapshot of the program using CreateToolhelp32Snapshot(), which allows me to loop through the modules of the program. From there I would like to get the base address of a specific module by name, however I am not sure how to get the name of the modules, this is my current code and where I am stuck. Is there a simple way I can get the name of the module based on the information I have?
// Find the window
hwnd = FindWindow(NULL, L"calculator");
if (!hwnd) {
cout << "window not found\n";
cin.get();
}
//Get Process ID
GetWindowThreadProcessId(hwnd, &pid);
HANDLE phandle = OpenProcess(PROCESS_VM_OPERATION, 0, pid);
if (!phandle) {
cout << "could not get handle\n";
cin.get();
}
if (snapshot_test != INVALID_HANDLE_VALUE) {
// First module
MODULEENTRY32 mod_entry;
mod_entry.dwSize = sizeof(mod_entry);
if (Module32First(snapshot_test, &mod_entry)) {
do {
DWORD test = (DWORD)(mod_entry.modBaseAddr + 0x46F68 + 10);
cout << ReadProcessMemory(phandle, (void*)(test), &health, sizeof(health), 0);
} while (Module32Next(snapshot_test, &mod_entry));
}
else (cout << "module32first error");
}
else (cout << "snapshot error")
The MODULEENTRY32.szModule variable contains the name of the module, and MODULEENTRY32.modBaseAddr contains the address of the module itself. You can use this function to get the address of a module by passing in it's name. It will loop through the modules and find the one with the matching name
uintptr_t GetModuleBaseAddress(DWORD procId, const wchar_t* modName)
{
uintptr_t modBaseAddr = 0;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, procId);
if (hSnap != INVALID_HANDLE_VALUE)
{
MODULEENTRY32 modEntry;
modEntry.dwSize = sizeof(modEntry);
if (Module32First(hSnap, &modEntry))
{
do
{
if (!_wcsicmp(modEntry.szModule, modName))
{
modBaseAddr = (uintptr_t)modEntry.modBaseAddr;
break;
}
} while (Module32Next(hSnap, &modEntry));
}
}
CloseHandle(hSnap);
return modBaseAddr;
}