diff --git a/GleeBug/Debugger.Loop.Exception.cpp b/GleeBug/Debugger.Loop.Exception.cpp index fcd76b5..4c5a0bd 100644 --- a/GleeBug/Debugger.Loop.Exception.cpp +++ b/GleeBug/Debugger.Loop.Exception.cpp @@ -28,12 +28,12 @@ namespace GleeBug mRegisters->Gip = info.address; //restore the original breakpoint byte and do an internal step - mProcess->MemWrite(info.address, info.internal.software.oldbytes, info.internal.software.size); + mProcess->MemWriteUnsafe(info.address, info.internal.software.oldbytes, info.internal.software.size); mThread->StepInternal(std::bind([this, info]() { //only restore the bytes if the breakpoint still exists if (mProcess->breakpoints.find({ BreakpointType::Software, info.address }) != mProcess->breakpoints.end()) - mProcess->MemWrite(info.address, info.internal.software.newbytes, info.internal.software.size); + mProcess->MemWriteUnsafe(info.address, info.internal.software.newbytes, info.internal.software.size); })); //call the generic callback diff --git a/GleeBug/Debugger.Process.Breakpoint.cpp b/GleeBug/Debugger.Process.Breakpoint.cpp index 25f615a..3ce95f7 100644 --- a/GleeBug/Debugger.Process.Breakpoint.cpp +++ b/GleeBug/Debugger.Process.Breakpoint.cpp @@ -29,10 +29,10 @@ namespace GleeBug } //read/write the breakpoint - if (!MemRead(address, info.internal.software.oldbytes, info.internal.software.size)) + if (!MemReadUnsafe(address, info.internal.software.oldbytes, info.internal.software.size)) return false; - if (!MemWrite(address, info.internal.software.newbytes, info.internal.software.size)) + if (!MemWriteUnsafe(address, info.internal.software.newbytes, info.internal.software.size)) return false; FlushInstructionCache(hProcess, nullptr, 0); @@ -67,7 +67,7 @@ namespace GleeBug //restore the breakpoint bytes if the breakpoint is enabled if (info.enabled) { - if (!MemWrite(address, info.internal.software.oldbytes, info.internal.software.size)) + if (!MemWriteUnsafe(address, info.internal.software.oldbytes, info.internal.software.size)) return false; FlushInstructionCache(hProcess, nullptr, 0); } diff --git a/GleeBug/Debugger.Process.Memory.cpp b/GleeBug/Debugger.Process.Memory.cpp index 6b1ccff..2ce0b8f 100644 --- a/GleeBug/Debugger.Process.Memory.cpp +++ b/GleeBug/Debugger.Process.Memory.cpp @@ -2,7 +2,7 @@ namespace GleeBug { - bool Process::MemRead(ptr address, void* buffer, ptr size, ptr* bytesRead) const + bool Process::MemReadUnsafe(ptr address, void* buffer, ptr size, ptr* bytesRead) const { ptr read; if (!bytesRead) @@ -12,7 +12,7 @@ namespace GleeBug bool Process::MemReadSafe(ptr address, void* buffer, ptr size, ptr* bytesRead) const { - if (!MemRead(address, buffer, size, bytesRead)) + if (!MemReadUnsafe(address, buffer, size, bytesRead)) return false; //choose the filter method that has the lowest cost @@ -54,7 +54,7 @@ namespace GleeBug return true; } - bool Process::MemWrite(ptr address, const void* buffer, ptr size, ptr* bytesWritten) + bool Process::MemWriteUnsafe(ptr address, const void* buffer, ptr size, ptr* bytesWritten) { ptr written; if (!bytesWritten) @@ -70,6 +70,29 @@ namespace GleeBug bool Process::MemIsValidPtr(ptr address) const { uint8 byte; - return MemRead(address, &byte, sizeof(byte)); + return MemReadUnsafe(address, &byte, sizeof(byte)); + } + + ptr Process::MemFindPattern(ptr data, size_t datasize, const std::vector & pattern, bool safe) const + { + std::vector buffer(datasize); + if (!MemRead(data, buffer.data(), datasize, nullptr, safe)) + return 0; + auto found = Pattern::Find(buffer.data(), datasize, pattern); + return found == -1 ? 0 : found + data; + } + + ptr Process::MemFindPattern(ptr data, size_t datasize, const char* pattern, bool safe) const + { + return MemFindPattern(data, datasize, Pattern::Transform(pattern), safe); + } + + ptr Process::MemFindPattern(ptr data, size_t datasize, const uint8* pattern, size_t patternsize, bool safe) const + { + std::vector buffer(datasize); + if (!MemRead(data, buffer.data(), datasize, nullptr, safe)) + return 0; + auto found = Pattern::Find(buffer.data(), datasize, pattern, patternsize); + return found == -1 ? 0 : found + data; } }; \ No newline at end of file diff --git a/GleeBug/Debugger.Process.h b/GleeBug/Debugger.Process.h index adbcd1a..f7b679b 100644 --- a/GleeBug/Debugger.Process.h +++ b/GleeBug/Debugger.Process.h @@ -5,6 +5,7 @@ #include "Debugger.Thread.h" #include "Debugger.Dll.h" #include "Debugger.Breakpoint.h" +#include "Static.Pattern.h" namespace GleeBug { @@ -42,9 +43,25 @@ namespace GleeBug \param [out] buffer Destination buffer. Cannot be null. May be filled partially on failure. \param size The size to read. \param bytesRead (Optional) Number of bytes read (should be equal to size on success). + \param safe Whether to call MemReadSafe or MemReadUnsafe. \return true if it succeeds, false if it fails. */ - bool MemRead(ptr address, void* buffer, ptr size, ptr* bytesRead = nullptr) const; + bool MemRead(ptr address, void* buffer, ptr size, ptr* bytesRead = nullptr, bool safe = true) const + { + if (safe) + return MemReadSafe(address, buffer, size, bytesRead); + return MemRead(address, buffer, size, bytesRead); + } + + /** + \brief Read memory from the process. This function should be used for internal reasons only! + \param address The virtual address to read from. + \param [out] buffer Destination buffer. Cannot be null. May be filled partially on failure. + \param size The size to read. + \param bytesRead (Optional) Number of bytes read (should be equal to size on success). + \return true if it succeeds, false if it fails. + */ + bool MemReadUnsafe(ptr address, void* buffer, ptr size, ptr* bytesRead = nullptr) const; /** \brief Safely read memory from the process, filtering out breakpoint bytes. @@ -62,9 +79,25 @@ namespace GleeBug \param [in] buffer Source buffer. Cannot be null. \param size The size to write. \param bytesWritten (Optional) Number of bytes written (should be equal to size on success). + \param safe Wheter to call MemWriteSafe or MemWriteUnsafe. \return true if it succeeds, false if it fails. */ - bool MemWrite(ptr address, const void* buffer, ptr size, ptr* bytesWritten = nullptr); + bool MemWrite(ptr address, const void* buffer, ptr size, ptr* bytesWritten = nullptr, bool safe = true) + { + if (safe) + return MemWriteSafe(address, buffer, size, bytesWritten); + return MemWriteUnsafe(address, buffer, size, bytesWritten); + } + + /** + \brief Write memory to the process. This function should be used for internal reasons only! + \param address The virtual address to write to. + \param [in] buffer Source buffer. Cannot be null. + \param size The size to write. + \param bytesWritten (Optional) Number of bytes written (should be equal to size on success). + \return true if it succeeds, false if it fails. + */ + bool MemWriteUnsafe(ptr address, const void* buffer, ptr size, ptr* bytesWritten = nullptr); /** \brief Safely write memory to the process, preserving breakpoint bytes. @@ -83,6 +116,37 @@ namespace GleeBug */ bool MemIsValidPtr(ptr address) const; + /** + \brief Finds the first occurrence of a pattern in process memory. + \param data The address to start searching from. + \param datasize The size to search in. + \param pattern The pattern to find. + \param safe Use the safe memory functions (eg do not consider software breakpoint data). + \return Memory address when found, 0 when not found. + */ + ptr MemFindPattern(ptr data, size_t datasize, const std::vector & pattern, bool safe = true) const; + + /** + \brief Finds the first occurrence of a pattern in process memory. + \param data The address to start searching from. + \param datasize The size to search in. + \param pattern The pattern to find. + \param safe Use the safe memory functions (eg do not consider software breakpoint data). + \return Memory address when found, 0 when not found. + */ + ptr MemFindPattern(ptr data, size_t datasize, const char* pattern, bool safe = true) const; + + /** + \brief Finds the first occurrence of a pattern in process memory. + \param data The address to start searching from. + \param datasize The size to search in. + \param pattern The pattern to find. + \param patternsize The size of the pattern to find. + \param safe Use the safe memory functions (eg do not consider software breakpoint data). + \return Memory address when found, 0 when not found. + */ + ptr MemFindPattern(ptr data, size_t datasize, const uint8* pattern, size_t patternsize, bool safe = true) const; + /** \brief Sets a software breakpoint. \param address The address to set the breakpoint on. diff --git a/GleeBug/Debugger.h b/GleeBug/Debugger.h index 44e56d9..a65ffa9 100644 --- a/GleeBug/Debugger.h +++ b/GleeBug/Debugger.h @@ -47,7 +47,7 @@ namespace GleeBug bool Detach() const; /** - \brief Run the debug loop (does not return until the debuggee is detached or terminated). + \brief Run the debug loop (does not return until the debuggee is detached or terminated). This function should be run from the same thread as you ran Init. */ void Start(); diff --git a/GleeBug/Static.Pattern.cpp b/GleeBug/Static.Pattern.cpp index 1cb74d1..acffb03 100644 --- a/GleeBug/Static.Pattern.cpp +++ b/GleeBug/Static.Pattern.cpp @@ -1,12 +1,10 @@ #include "Static.Pattern.h" -using namespace std; - namespace GleeBug { - string Pattern::FormatPattern(const string & patterntext) + std::string Pattern::FormatPattern(const std::string & patterntext) { - string result; + std::string result; result.reserve(patterntext.length()); for (auto ch : patterntext) { @@ -16,13 +14,13 @@ namespace GleeBug return result; } - bool Pattern::Transform(const std::string & patterntext, std::vector & pattern) + std::vector Pattern::Transform(const std::string & patterntext) { - pattern.clear(); + std::vector pattern; auto formattext = FormatPattern(patterntext); auto len = formattext.length(); if (!len) - return false; + return pattern; if (len % 2) //not a multiple of 2 { @@ -30,6 +28,8 @@ namespace GleeBug len++; } + pattern.reserve(len / 2); + auto hexChToInt = [](char ch) { if (ch >= '0' && ch <= '9') @@ -62,7 +62,7 @@ namespace GleeBug pattern.push_back(newByte); } } - return true; + return pattern; } size_t Pattern::Find(const uint8* data, size_t datasize, const std::vector & pattern) @@ -87,6 +87,8 @@ namespace GleeBug }; auto searchpatternsize = pattern.size(); + if (!searchpatternsize) + return -1; for (size_t i = 0, pos = 0; i < datasize; i++) //search for the pattern { if (MatchByte(data[i], pattern.at(pos))) //check if our pattern matches the current byte @@ -106,6 +108,8 @@ namespace GleeBug size_t Pattern::Find(const uint8* data, size_t datasize, const uint8* pattern, size_t patternsize) { + if (!patternsize) + return -1; if (patternsize > datasize) patternsize = datasize; for (size_t i = 0, pos = 0; i < datasize; i++) @@ -127,18 +131,13 @@ namespace GleeBug size_t Pattern::Find(const uint8* data, size_t datasize, const char* pattern) { - string patterntext(pattern); - vector searchpattern; - if (!Transform(patterntext, searchpattern)) - return -1; - return Find(data, datasize, searchpattern); + return Find(data, datasize, Transform(pattern)); } void Pattern::Write(uint8* data, size_t datasize, const char* pattern) { - vector writepattern; - string patterntext(pattern); - if (!Transform(patterntext, writepattern)) + auto writepattern = Transform(pattern); + if (!writepattern.size()) return; auto writepatternsize = writepattern.size(); @@ -160,7 +159,7 @@ namespace GleeBug WriteByte(&data[i], writepattern.at(i)); } - bool Pattern::Snr(uint8* data, size_t datasize, const char* searchpattern, const char* replacepattern) + bool Pattern::SearchAndReplace(uint8* data, size_t datasize, const char* searchpattern, const char* replacepattern) { auto found = Find(data, datasize, searchpattern); if (found == -1) diff --git a/GleeBug/Static.Pattern.h b/GleeBug/Static.Pattern.h index 5cdfa62..6780689 100644 --- a/GleeBug/Static.Pattern.h +++ b/GleeBug/Static.Pattern.h @@ -26,11 +26,10 @@ namespace GleeBug /** \brief Transforms a string pattern to a nibble structure. - \param patterntext The pattern string to find. - \param [out] pattern Transformed pattern. - \return true if it succeeds, false otherwise. + \param patterntext The pattern string to transform. + \return Non-empty vector on success. */ - static bool Transform(const std::string & patterntext, std::vector & pattern); + static std::vector Transform(const std::string & patterntext); /** \brief Finds the first occurrence of a pattern in a buffer. @@ -76,7 +75,7 @@ namespace GleeBug \param replacepattern The pattern to replace the found occurrence with. The pattern supports wildcards (1? ?? ?6 78). \return true if it succeeds, false if it fails. */ - static bool Snr(uint8* data, size_t datasize, const char* searchpattern, const char* replacepattern); + static bool SearchAndReplace(uint8* data, size_t datasize, const char* searchpattern, const char* replacepattern); }; }; diff --git a/MyDebugger/MyDebugger.h b/MyDebugger/MyDebugger.h index 00c0b93..589e4b9 100644 --- a/MyDebugger/MyDebugger.h +++ b/MyDebugger/MyDebugger.h @@ -68,7 +68,7 @@ protected: uint8 test[5]; ptr start = entry - 2; printf("unsafe: "); - mProcess->MemRead(start, test, sizeof(test)); + mProcess->MemReadUnsafe(start, test, sizeof(test)); for (int i = 0; i < sizeof(test); i++) printf("%02X ", test[i]); puts(""); diff --git a/TitanEngineEmulator/Emulator.h b/TitanEngineEmulator/Emulator.h index 477df2f..8ec88e5 100644 --- a/TitanEngineEmulator/Emulator.h +++ b/TitanEngineEmulator/Emulator.h @@ -62,7 +62,7 @@ public: if (!process) return false; //TODO process->MemWriteSafe - return process->MemWrite(ptr(lpBaseAddress), lpBuffer, nSize, (ptr*)lpNumberOfBytesWritten); + return process->MemWriteUnsafe(ptr(lpBaseAddress), lpBuffer, nSize, (ptr*)lpNumberOfBytesWritten); } bool Fill(LPVOID MemoryStart, DWORD MemorySize, PBYTE FillByte)