diff options
| -rw-r--r-- | .gitignore | 5 | ||||
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | build.bat | 15 | ||||
| -rw-r--r-- | ipchanger.cc | 479 | ||||
| -rw-r--r-- | memscan.cc | 235 |
5 files changed, 738 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..36f9e81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.vscode +bin +build +local +servers.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..fd3dd01 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Tibia IP-Changer +This is a simple command line IP-Changer for old Tibia clients. Its main feature is having multiple server configurations stored in a configuration file (see `ipchanger.exe -help`). Only a few client versions are currently supported (7.7, 8.1, 8.6) but it should be straighforward to expand to others. There is also a memory scan tool that can be used to help with scanning memory addresses for yet unsupported clients. + +Both are Windows specific and can be compiled by running `build.bat` from the visual studio shell. They're very simple so I wont bother explaining them further. For more details, the source code should be very readable. diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..980e997 --- /dev/null +++ b/build.bat @@ -0,0 +1,15 @@ +@SETLOCAL + +@SET CFLAGS=-W3 -WX -Zi -D_CRT_SECURE_NO_WARNINGS=1 +@SET LFLAGS=-subsystem:console -incremental:no -opt:ref -dynamicbase user32.lib + +pushd %~dp0 +del /q .\build\* +mkdir .\build +pushd .\build +cl %* -Fe:"ipchanger.exe" %CFLAGS% "../ipchanger.cc" /link %LFLAGS% +cl %* -Fe:"memscan.exe" %CFLAGS% "../memscan.cc" /link %LFLAGS% +popd +popd + +@ENDLOCAL diff --git a/ipchanger.cc b/ipchanger.cc new file mode 100644 index 0000000..db1d546 --- /dev/null +++ b/ipchanger.cc @@ -0,0 +1,479 @@ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define WIN32_LEAN_AND_MEAN 1 +#include <windows.h> + +#define NARRAY(Array) (int)(sizeof(Array) / sizeof(Array[0])) + +typedef uint8_t uint8; +typedef uintptr_t uintptr; + +struct ServerEntry{ + ServerEntry *Next; + int Version; + char Alias[100]; + char HostName[100]; + int Port; + char RsaModulus[1024]; +}; + +struct TibiaVersion{ + int Version; + const char *VersionString; + int NumLoginEndpoints; + int LoginEndpointStride; + int MaxHostNameSize; + int MaxRsaModulusSize; + uintptr VersionStringAddr; + uintptr FirstLoginHostNameAddr; + uintptr FirstLoginPortAddr; + uintptr RsaModulusAddr; +}; + +struct AutoHandleClose{ +private: + HANDLE m_Handle; + +public: + AutoHandleClose(HANDLE Handle){ + m_Handle = Handle; + } + + ~AutoHandleClose(void){ + if(m_Handle != NULL){ + CloseHandle(m_Handle); + } + } +}; + +bool StringEqN(const char *A, const char *B, int N){ + int Index = 0; + while(true){ + if(Index >= N){ + return true; + }else if(A[Index] != B[Index] || A[Index] == 0){ + return false; + } + Index += 1; + } +} + +bool StringEqCI(const char *A, const char *B){ + int Index = 0; + while(true){ + if(tolower(A[Index]) != tolower(B[Index])){ + return false; + }else if(A[Index] == 0){ + return true; + } + Index += 1; + } +} + +void PrintLastError(const char *Context){ + DWORD ErrorCode = GetLastError(); + char ErrorString[256]; + DWORD Ret = FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + ErrorString, sizeof(ErrorString), NULL); + if(Ret == 0){ + strcpy(ErrorString, "unknown error"); + } + printf("%s | error(%d): %s\n", Context, ErrorCode, ErrorString); +} + +void DebugPrintBuf(uintptr Address, const uint8 *Buffer, int Count){ + const int BytesPerLine = 16; + int FullLines = Count / BytesPerLine; + int Remainder = Count % BytesPerLine; + + for(int i = 0; i < FullLines; i += 1){ + printf("%16llX | ", (Address + i * BytesPerLine)); + + for(int j = 0; j < BytesPerLine; j += 1){ + if(j > 0) putchar(' '); + printf("%02X", Buffer[i * BytesPerLine + j]); + } + + printf(" | "); + + for(int j = 0; j < BytesPerLine; j += 1){ + int ch = Buffer[i * BytesPerLine + j]; + printf("%c", isprint(ch) ? ch : '.'); + } + + putchar('\n'); + } + + if(Remainder > 0){ + printf("%16llX | ", (Address + FullLines * BytesPerLine)); + + for(int j = 0; j < BytesPerLine; j += 1){ + if(j > 0) putchar(' '); + if(j < Remainder){ + printf("%02X", Buffer[FullLines * BytesPerLine + j]); + }else{ + printf(" "); + } + } + + printf(" | "); + + for(int j = 0; j < BytesPerLine; j += 1){ + if(j < Remainder){ + int ch = Buffer[FullLines * BytesPerLine + j]; + printf("%c", isprint(ch) ? ch : '.'); + }else{ + printf(" "); + } + } + + putchar('\n'); + } +} + +bool ChangeIP(const TibiaVersion *V, const char *HostName, int Port, const char *RsaModulus){ + int HostNameSize = (int)strlen(HostName) + 1; + if(HostNameSize > V->MaxHostNameSize){ + printf("ChangeIP: HostName size exceeds limit for version %d (%d > %d).\n", + V->Version, HostNameSize, V->MaxHostNameSize); + return false; + } + + if(Port <= 0 || Port > 0xFFFF){ + printf("ChangeIP: Invalid port number %d.\n", Port); + return false; + } + + HWND Window = FindWindowA("TibiaClient", NULL); + if(Window == NULL){ + printf("ChangeIP: No client running.\n"); + return false; + } + + DWORD ProcessID; + GetWindowThreadProcessId(Window, &ProcessID); + + HANDLE Process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, ProcessID); + if(Process == NULL){ + PrintLastError("OpenProcess"); + return false; + } + + AutoHandleClose HandleClose(Process); + if(V->VersionString != NULL && V->VersionStringAddr != 0){ + uint8 VersionString[128]; + if(!ReadProcessMemory(Process, (void*)V->VersionStringAddr, + VersionString, sizeof(VersionString), NULL)){ + PrintLastError("ReadProcessMemory(VersionString)"); + return false; + } + + int VersionStringLen = (int)strlen(V->VersionString); + if(!StringEqN(V->VersionString, (char*)VersionString, VersionStringLen)){ + printf("ChangeIP: Invalid client version.\n"); + return false; + } + } + + if(V->FirstLoginHostNameAddr != 0 && V->FirstLoginPortAddr != 0){ + uint8 HelpPort[4]; + HelpPort[0] = (uint8)(Port >> 0); + HelpPort[1] = (uint8)(Port >> 8); + HelpPort[2] = (uint8)(Port >> 16); + HelpPort[3] = (uint8)(Port >> 24); + + for(int i = 0; i < V->NumLoginEndpoints; i += 1){ + uintptr HostNameAddr = V->FirstLoginHostNameAddr + i * V->LoginEndpointStride; + uintptr PortAddr = V->FirstLoginPortAddr + i * V->LoginEndpointStride; + + if(!WriteProcessMemory(Process, (void*)HostNameAddr, HostName, HostNameSize, NULL)){ + PrintLastError("WriteProcessMemory(HostName)"); + return false; + } + + if(!WriteProcessMemory(Process, (void*)PortAddr, HelpPort, sizeof(HelpPort), NULL)){ + PrintLastError("WriteProcessMemory(Port)"); + return false; + } + } + } + + if(RsaModulus != NULL && V->RsaModulusAddr != 0){ + int RsaModulusSize = (int)strlen(RsaModulus) + 1; + if(RsaModulusSize > V->MaxRsaModulusSize){ + printf("ChangeIP: RsaModulus size exceeds limit for version %d (%d > %d).\n", + V->Version, RsaModulusSize, V->MaxRsaModulusSize); + return false; + } + + // NOTE(fusion): The RSA modulus lives in READONLY memory. + DWORD OldProtection; + if(!VirtualProtectEx(Process, (void*)V->RsaModulusAddr, + V->MaxRsaModulusSize, PAGE_READWRITE, &OldProtection)){ + PrintLastError("VirtualProtectEx(RsaModulus, READWRITE)"); + return false; + } + + if(!WriteProcessMemory(Process, (void*)V->RsaModulusAddr, RsaModulus, RsaModulusSize, NULL)){ + PrintLastError("WriteProcessMemory(RsaModulus)"); + return false; + } + + DWORD Dummy; + if(!VirtualProtectEx(Process, (void*)V->RsaModulusAddr, + V->MaxRsaModulusSize, OldProtection, &Dummy)){ + PrintLastError("VirtualProtectEx(RsaModulus, OldProtection)"); + return false; + } + } + + return true; +} + +int ReadLine(FILE *File, char *Buffer, int BufferSize, bool *OutEndOfFile, bool *OutClamped){ + int LineSize = 0; + bool EndOfFile = false; + bool Clamped = false; + while(true){ + int ch = fgetc(File); + if(ch == EOF || ch == '\n'){ + EndOfFile = (ch == EOF); + break; + } + + if(LineSize < BufferSize){ + Buffer[LineSize] = (char)ch; + } + + LineSize += 1; + } + + if(LineSize >= BufferSize){ + LineSize = BufferSize - 1; + Clamped = true; + } + + if(!EndOfFile && LineSize > 0 && Buffer[LineSize - 1] == '\r'){ + LineSize -= 1; + } + + Buffer[LineSize] = 0; + if(OutEndOfFile) *OutEndOfFile = EndOfFile; + if(OutClamped) *OutClamped = Clamped; + return LineSize; +} + +void NextValue(const char *Line, int Delim, int *Cursor, char *Buffer, int BufferSize){ + int Size = 0; + while(Line[*Cursor] != 0 && Line[*Cursor] != Delim){ + if(Size < BufferSize){ + Buffer[Size] = Line[*Cursor]; + } + + *Cursor += 1; + Size += 1; + } + + if(Line[*Cursor] != 0){ + *Cursor += 1; + } + + if(Size >= BufferSize){ + Size = BufferSize - 1; + } + + Buffer[Size] = 0; +} + +ServerEntry *ParseServerEntry(const char *Line, int LineNumber){ + int LineStart = 0; + while(Line[LineStart] != 0 && isspace(Line[LineStart])){ + LineStart += 1; + } + + if(Line[LineStart] == 0 || Line[LineStart] == '#'){ + return NULL; + } + + char HelpVersion[16]; + char HelpPort[16]; + int Cursor = LineStart; + ServerEntry *Server = (ServerEntry*)calloc(1, sizeof(ServerEntry)); + NextValue(Line, ';', &Cursor, HelpVersion, sizeof(HelpVersion)); + NextValue(Line, ';', &Cursor, Server->Alias, sizeof(Server->Alias)); + NextValue(Line, ';', &Cursor, Server->HostName, sizeof(Server->HostName)); + NextValue(Line, ';', &Cursor, HelpPort, sizeof(HelpPort)); + NextValue(Line, ';', &Cursor, Server->RsaModulus, sizeof(Server->RsaModulus)); + Server->Version = atoi(HelpVersion); + Server->Port = atoi(HelpPort); + return Server; +} + +ServerEntry *ReadServerList(const char *FileName){ + FILE *File = fopen(FileName, "r"); + if(File == NULL){ + printf("ReadServerList: Failed to open \"%s\" for reading.\n", FileName); + return NULL; + } + + ServerEntry *ServerList = NULL; + for(int LineNumber = 1; true; LineNumber += 1){ + char Line[4096]; + bool EndOfFile; + bool Clamped; + int LineSize = ReadLine(File, Line, sizeof(Line), &EndOfFile, &Clamped); + if(LineSize > 0){ + ServerEntry *Server = ParseServerEntry(Line, LineNumber); + if(Server){ + Server->Next = ServerList; + ServerList = Server; + } + } + + if(EndOfFile){ + break; + } + } + + fclose(File); + return ServerList; +} + +void CreateSampleServerList(const char *FileName){ + FILE *File = fopen(FileName, "w"); + if(File == NULL){ + printf("CreateSampleServerList: Failed to open \"%s\" for writing.\n", FileName); + return; + } + + fprintf(File, + "# Each server entry should be in a SINGLE line and have the\n" + "# format \"VERSION;ALIAS;HOSTNAME;PORT;RSAMODULUS\". Empty\n" + "# lines and lines starting with # are discarded. The server\n" + "# alias may be empty, meaning you'll need to specify its\n" + "# host name instead. For versions without RSA encryption,\n" + "# the RSA modulus is ignored.\n" + "\n" + "# Example for the default 7.7 tibia login server:\n" + "770;tibia;server.tibia.com;7171;" + "1429962396241639952007017738289889555079540334546615321747051608" + "2934737582776038882967213386204600674145392845853859217990626450" + "9724520840657286865659265687630979195970404721891201847792002125" + "5354012927791239372074475745966927885136471792353355293072513505" + "70728407373705564708871762033017096809910315212883967\n" + "\n" + "# Example for a regular 8.6 otserv:\n" + "860;otserv;server.otserv.com;7171;" + "1091201329673994292788609605089955415282375029027981291234687579" + "3726629149257644633073969600111060390723088861007265581882535850" + "3429057592827629436413108566029093628212635953836686562675849720" + "6207862794310902180176810615217550567108238764764442605581471797" + "07119674283982419152118103759076030616683978566631413\n"); + + fclose(File); +} + +int main(int argc, char **argv){ + // TODO(fusion): This could be loaded at runtime from some `versions.txt` file. + static const TibiaVersion Versions[] = { + { + 770, // Version + "Version 7.7", // VersionString + 5, // NumLoginEndpoints + 112, // LoginEndpointStride + 100, // MaxHostNameSize + 312, // MaxRsaModulusSize + 0x51765D, // VersionStringAddr + 0x6BB2F0, // FirstLoginHostNameAddr + 0x6BB354, // FirstLoginPortAddr + 0x516620, // RsaModulusAddr + }, + { + 810, // Version + "Version 8.10", // VersionString + 10, // NumLoginEndpoints + 112, // LoginEndpointStride + 100, // MaxHostNameSize + 312, // MaxRsaModulusSize + 0x61B64D, // VersionStringAddr + 0x763BB8, // FirstLoginHostNameAddr + 0x763C1C, // FirstLoginPortAddr + 0x597610, // RsaModulusAddr + }, + { + 860, // Version + "Version 8.60", // VersionString + 10, // NumLoginEndpoints + 112, // LoginEndpointStride + 100, // MaxHostNameSize + 312, // MaxRsaModulusSize + 0x64C2AD, // VersionStringAddr + 0x7947F8, // FirstLoginHostNameAddr + 0x79485C, // FirstLoginPortAddr + 0x5B8980, // RsaModulusAddr + }, + }; + + if(argc <= 1 || argv[1][0] == 0 || argv[1][0] == '-'){ + if(argc > 1 && StringEqCI(argv[1], "-sample")){ + CreateSampleServerList("servers.txt"); + printf("The file `server.txt` was created/rewritten with" + " instructions on how to add or modify servers.\n"); + }else{ + printf( + "USAGE: ipchanger.exe ALIAS|HOSTNAME # modify client\n" + " ipchanger.exe -sample # create/reset `servers.txt`\n" + " ipchanger.exe -help # print this message\n"); + } + return EXIT_FAILURE; + } + + ServerEntry *ServerList = ReadServerList("servers.txt"); + if(ServerList == NULL){ + printf("No server information was found.\n"); + return EXIT_FAILURE; + } + + ServerEntry *Server = ServerList; + const char *AliasOrHostName = argv[1]; + while(Server != NULL){ + if(StringEqCI(Server->Alias, AliasOrHostName) + || StringEqCI(Server->HostName, AliasOrHostName)){ + break; + } + + Server = Server->Next; + } + + if(Server == NULL){ + printf("No server with alias or hostname \"%s\" found.\n", AliasOrHostName); + return EXIT_FAILURE; + } + + const TibiaVersion *Version = NULL; + for(int i = 0; i < NARRAY(Versions); i += 1){ + if(Versions[i].Version == Server->Version){ + Version = &Versions[i]; + break; + } + } + + if(Version == NULL){ + printf("Server \"%s\" is defined with version %d which is not" + " currently supported.\n", AliasOrHostName, Version->Version); + return EXIT_FAILURE; + } + + if(!ChangeIP(Version, Server->HostName, Server->Port, Server->RsaModulus)){ + printf("There was a problem with changing the client's IP.\n"); + return EXIT_FAILURE; + } + + printf("Client is now configured to connect to %s:%d.\n", Server->HostName, Server->Port); + return EXIT_SUCCESS; +} diff --git a/memscan.cc b/memscan.cc new file mode 100644 index 0000000..674194a --- /dev/null +++ b/memscan.cc @@ -0,0 +1,235 @@ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define WIN32_LEAN_AND_MEAN 1 +#include <windows.h> + +typedef uint8_t uint8; +typedef uintptr_t uintptr; + +void PrintLastError(const char *Context){ + DWORD ErrorCode = GetLastError(); + char ErrorString[256]; + DWORD Ret = FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + ErrorString, sizeof(ErrorString), NULL); + if(Ret == 0){ + strcpy(ErrorString, "unknown error"); + } + printf("%s | error(%d): %s\n", Context, ErrorCode, ErrorString); +} + +void DebugPrintBuf(uintptr Address, const uint8 *Buffer, int Count){ + const int BytesPerLine = 16; + int FullLines = Count / BytesPerLine; + int Remainder = Count % BytesPerLine; + + for(int i = 0; i < FullLines; i += 1){ + printf("%16llX | ", (Address + i * BytesPerLine)); + + for(int j = 0; j < BytesPerLine; j += 1){ + if(j > 0) putchar(' '); + printf("%02X", Buffer[i * BytesPerLine + j]); + } + + printf(" | "); + + for(int j = 0; j < BytesPerLine; j += 1){ + int ch = Buffer[i * BytesPerLine + j]; + printf("%c", isprint(ch) ? ch : '.'); + } + + putchar('\n'); + } + + if(Remainder > 0){ + printf("%16llX | ", (Address + FullLines * BytesPerLine)); + + for(int j = 0; j < BytesPerLine; j += 1){ + if(j > 0) putchar(' '); + if(j < Remainder){ + printf("%02X", Buffer[FullLines * BytesPerLine + j]); + }else{ + printf(" "); + } + } + + printf(" | "); + + for(int j = 0; j < BytesPerLine; j += 1){ + if(j < Remainder){ + int ch = Buffer[FullLines * BytesPerLine + j]; + printf("%c", isprint(ch) ? ch : '.'); + }else{ + printf(" "); + } + } + + putchar('\n'); + } +} + +void DumpProcessMemory(HANDLE Process, uintptr BaseAddr, SIZE_T Size){ + uintptr Addr = BaseAddr; + uintptr End = BaseAddr + Size; + while(Addr < End){ + uint8 Buffer[32 * 1024]; + SIZE_T BytesToRead = End - Addr; + if(BytesToRead > sizeof(Buffer)){ + BytesToRead = sizeof(Buffer); + } + + SIZE_T BytesRead; + if(!ReadProcessMemory(Process, (void*)Addr, Buffer, BytesToRead, &BytesRead)){ + PrintLastError("DumpProcessMemory>ReadProcessMemory"); + return; + } + + DebugPrintBuf(Addr, Buffer, (int)BytesRead); + Addr += BytesRead; + } +} + +int BufferFind(const uint8 *Buffer, int BufferSize, const uint8 *Data, int DataSize){ + for(int i = 0; i < (BufferSize - DataSize); i += 1){ + if(memcmp(Buffer + i, Data, DataSize) == 0){ + return i; + } + } + return -1; +} + +void ScanProcessMemory(HANDLE Process, uintptr BaseAddr, SIZE_T Size, + const uint8 *Data, SIZE_T DataSize, int ContextWindow){ + uintptr Addr = BaseAddr; + uintptr End = BaseAddr + Size; + while(Addr < End){ + uint8 Buffer[32 * 1024]; + SIZE_T BytesToRead = End - Addr; + if(BytesToRead > sizeof(Buffer)){ + BytesToRead = sizeof(Buffer); + } + + SIZE_T BytesRead; + if(!ReadProcessMemory(Process, (void*)Addr, Buffer, BytesToRead, &BytesRead)){ + PrintLastError("ScanProcessMemory>ReadProcessMemory"); + return; + } + + // TODO(fusion): This will work most of the time but is not the best way + // to scan for something because the data we're looking for may be split + // between reads, depending on the size of `Buffer`. + int Cursor = 0; + while(true){ + int Offset = BufferFind(Buffer + Cursor, (int)BytesRead - Cursor, Data, (int)DataSize); + if(Offset == -1){ + break; + } + + Cursor += Offset; + int PrintStart = Cursor - ContextWindow; + int PrintEnd = Cursor + (int)DataSize + ContextWindow; + + if(PrintStart < 0){ + PrintStart = 0; + } + + if(PrintEnd > (int)BytesRead){ + PrintEnd = (int)BytesRead; + } + + DebugPrintBuf(Addr + PrintStart, Buffer + PrintStart, PrintEnd - PrintStart); + Cursor += (int)DataSize; + } + + Addr += BytesRead; + } +} + +int main(int argc, char **argv){ + uint8 Data[128]; + int DataSize = 0; + int ContextWindow = 10; + for(int i = 1; i < argc; i += 1){ + if(argv[i][0] == '-'){ + if((i + 1) >= argc){ + printf("missing \"%s\" value", argv[i]); + break; + } + + if(strcmp(argv[i], "-c") == 0){ + ContextWindow = atoi(argv[i + 1]); + }else if(strcmp(argv[i], "-byte") == 0){ + int Num = atoi(argv[i + 1]); + Data[0] = (uint8)Num; + DataSize = 1; + }else if(strcmp(argv[i], "-le16") == 0){ + int Num = atoi(argv[i + 1]); + Data[0] = (uint8)(Num >> 0); + Data[1] = (uint8)(Num >> 8); + DataSize = 2; + }else if(strcmp(argv[i], "-be16") == 0){ + int Num = atoi(argv[i + 1]); + Data[0] = (uint8)(Num >> 8); + Data[1] = (uint8)(Num >> 0); + DataSize = 2; + }else if(strcmp(argv[i], "-le32") == 0){ + int Num = atoi(argv[i + 1]); + Data[0] = (uint8)(Num >> 0); + Data[1] = (uint8)(Num >> 8); + Data[2] = (uint8)(Num >> 16); + Data[3] = (uint8)(Num >> 24); + DataSize = 4; + }else if(strcmp(argv[i], "-be32") == 0){ + int Num = atoi(argv[i + 1]); + Data[0] = (uint8)(Num >> 24); + Data[1] = (uint8)(Num >> 16); + Data[2] = (uint8)(Num >> 8); + Data[3] = (uint8)(Num >> 0); + DataSize = 4; + } + + i += 1; + }else{ + DataSize = (int)strlen(argv[i]); + if(DataSize > (int)sizeof(Data)){ + DataSize = (int)sizeof(Data); + } + memcpy(Data, argv[i], DataSize); + } + } + + if(DataSize == 0){ + printf("Invalid usage. See `memscan.cc`.\n"); + return 1; + } + + HWND Window = FindWindowA("TibiaClient", NULL); + if(Window == NULL){ + PrintLastError("FindWindowA"); + return -1; + } + + DWORD ProcessID; + GetWindowThreadProcessId(Window, &ProcessID); + + HANDLE Process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessID); + if(Process == NULL){ + PrintLastError("OpenProcess"); + return -1; + } + + uintptr BaseAddr = 0; + MEMORY_BASIC_INFORMATION Info; + while(VirtualQueryEx(Process, (void*)BaseAddr, &Info, sizeof(Info)) != 0){ + if(Info.State & MEM_COMMIT && !(Info.Protect & PAGE_NOACCESS) && !(Info.Protect & PAGE_GUARD)){ + ScanProcessMemory(Process, (uintptr)Info.BaseAddress, Info.RegionSize, Data, DataSize, ContextWindow); + } + BaseAddr = (uintptr)Info.BaseAddress + Info.RegionSize; + } + CloseHandle(Process); + return 0; +} |
