bool FunctionAdd(uint Start, uint End, bool Manual) { // CHECK: Export/Command function if(!DbgIsDebugging()) return false; // Make sure memory is readable if(!MemIsValidReadPtr(Start)) return false; // Fail if boundary exceeds module size const uint moduleBase = ModBaseFromAddr(Start); if(moduleBase != ModBaseFromAddr(End)) return false; // Fail if 'Start' and 'End' are incompatible if(Start > End || FunctionOverlaps(Start, End)) return false; FUNCTIONSINFO function; ModNameFromAddr(Start, function.mod, true); function.start = Start - moduleBase; function.end = End - moduleBase; function.manual = Manual; // Insert to global table EXCLUSIVE_ACQUIRE(LockFunctions); functions.insert(std::make_pair(ModuleRange(ModHashFromAddr(moduleBase), Range(function.start, function.end)), function)); return true; }
bool ArgumentAdd(duint Start, duint End, bool Manual, duint InstructionCount) { // Make sure memory is readable if(!MemIsValidReadPtr(Start)) return false; // Fail if boundary exceeds module size auto moduleBase = ModBaseFromAddr(Start); if(moduleBase != ModBaseFromAddr(End)) return false; // Fail if 'Start' and 'End' are incompatible if(Start > End || ArgumentOverlaps(Start, End)) return false; ARGUMENTSINFO argument; if(!ModNameFromAddr(Start, argument.mod, true)) *argument.mod = '\0'; argument.start = Start - moduleBase; argument.end = End - moduleBase; argument.manual = Manual; argument.instructioncount = InstructionCount; return arguments.Add(argument); }
void ArgumentDelRange(duint Start, duint End, bool DeleteManual) { // Should all arguments be deleted? // 0x00000000 - 0xFFFFFFFF if(Start == 0 && End == ~0) { ArgumentClear(); } else { // The start and end address must be in the same module auto moduleBase = ModBaseFromAddr(Start); if(moduleBase != ModBaseFromAddr(End)) return; // Convert these to a relative offset Start -= moduleBase; End -= moduleBase; arguments.DeleteWhere([ = ](const ARGUMENTSINFO & value) { if(!DeleteManual && value.manual) return false; return value.end >= Start && value.start <= End; }); } }
// Get the start/end of a loop at a certain depth and address bool LoopGet(int Depth, duint Address, duint* Start, duint* End) { ASSERT_DEBUGGING("Export call"); // Get the virtual address module const duint moduleBase = ModBaseFromAddr(Address); // Virtual address to relative address Address -= moduleBase; SHARED_ACQUIRE(LockLoops); // Search with this address range auto found = loops.find(DepthModuleRange(Depth, ModuleRange(ModHashFromAddr(moduleBase), Range(Address, Address)))); if(found == loops.end()) return false; // Return the loop start and end if(Start) *Start = found->second.start + moduleBase; if(End) *End = found->second.end + moduleBase; return true; }
bool FunctionGet(uint Address, uint* Start, uint* End) { // CHECK: Exported function if(!DbgIsDebugging()) return false; const uint moduleBase = ModBaseFromAddr(Address); // Lookup by module hash, then function range SHARED_ACQUIRE(LockFunctions); auto found = functions.find(ModuleRange(ModHashFromAddr(moduleBase), Range(Address - moduleBase, Address - moduleBase))); // Was this range found? if(found == functions.end()) return false; if(Start) *Start = found->second.start + moduleBase; if(End) *End = found->second.end + moduleBase; return true; }
// Get the start/end of a loop at a certain depth and address bool LoopGet(int Depth, uint Address, uint* Start, uint* End) { // CHECK: Exported function if(!DbgIsDebugging()) return false; // Get the virtual address module const uint moduleBase = ModBaseFromAddr(Address); // Virtual address to relative address Address -= moduleBase; SHARED_ACQUIRE(LockLoops); // Search with this address range auto found = loops.find(DepthModuleRange(Depth, ModuleRange(ModHashFromAddr(moduleBase), Range(Address, Address)))); if(found == loops.end()) return false; // Return the loop start if(Start) *Start = found->second.start + moduleBase; // Also the loop end if(End) *End = found->second.end + moduleBase; return true; }
bool LoopAdd(uint Start, uint End, bool Manual) { // CHECK: Export function if(!DbgIsDebugging()) return false; // Loop must begin before it ends if(Start > End) return false; // Memory addresses must be valid if(!MemIsValidReadPtr(Start) || !MemIsValidReadPtr(End)) return false; // Check if loop boundaries are in the same module range const uint moduleBase = ModBaseFromAddr(Start); if(moduleBase != ModBaseFromAddr(End)) return false; // Loops cannot overlap other loops int finalDepth = 0; if(LoopOverlaps(0, Start, End, &finalDepth)) return false; // Fill out loop information structure LOOPSINFO loopInfo; loopInfo.start = Start - moduleBase; loopInfo.end = End - moduleBase; loopInfo.depth = finalDepth; loopInfo.manual = Manual; ModNameFromAddr(Start, loopInfo.mod, true); // Link this to a parent loop if one does exist if(finalDepth) LoopGet(finalDepth - 1, Start, &loopInfo.parent, 0); else loopInfo.parent = 0; EXCLUSIVE_ACQUIRE(LockLoops); // Insert into list loops.insert(std::make_pair(DepthModuleRange(finalDepth, ModuleRange(ModHashFromAddr(moduleBase), Range(loopInfo.start, loopInfo.end))), loopInfo)); return true; }
void FunctionDelRange(uint Start, uint End) { // CHECK: Exported function if(!DbgIsDebugging()) return; // Should all functions be deleted? // 0x00000000 - 0xFFFFFFFF if(Start == 0 && End == ~0) { FunctionClear(); } else { // The start and end address must be in the same module uint moduleBase = ModBaseFromAddr(Start); if(moduleBase != ModBaseFromAddr(End)) return; // Convert these to a relative offset Start -= moduleBase; End -= moduleBase; EXCLUSIVE_ACQUIRE(LockFunctions); for(auto itr = functions.begin(); itr != functions.end();) { const auto & currentFunction = itr->second; // Ignore manually set entries if(currentFunction.manual) { ++itr; continue; } // [Start, End] if(currentFunction.end >= Start && currentFunction.start <= End) itr = functions.erase(itr); else ++itr; } } }
void CommentDelRange(uint Start, uint End) { // CHECK: Export function if(!DbgIsDebugging()) return; // Are all comments going to be deleted? // 0x00000000 - 0xFFFFFFFF if(Start == 0 && End == ~0) { CommentClear(); } else { // Make sure 'Start' and 'End' reference the same module uint moduleBase = ModBaseFromAddr(Start); if(moduleBase != ModBaseFromAddr(End)) return; // Virtual -> relative offset Start -= moduleBase; End -= moduleBase; EXCLUSIVE_ACQUIRE(LockComments); for(auto itr = comments.begin(); itr != comments.end();) { // Ignore manually set entries if(itr->second.manual) { itr++; continue; } // [Start, End) if(itr->second.addr >= Start && itr->second.addr < End) itr = comments.erase(itr); else itr++; } } }
bool FunctionDelete(uint Address) { // CHECK: Exported function if(!DbgIsDebugging()) return false; const uint moduleBase = ModBaseFromAddr(Address); EXCLUSIVE_ACQUIRE(LockFunctions); return (functions.erase(ModuleRange(ModHashFromAddr(moduleBase), Range(Address - moduleBase, Address - moduleBase))) > 0); }
void PatchDelRange(duint Start, duint End, bool Restore) { ASSERT_DEBUGGING("Export call"); // Are all bookmarks going to be deleted? // 0x00000000 - 0xFFFFFFFF if(Start == 0 && End == ~0) { EXCLUSIVE_ACQUIRE(LockPatches); patches.clear(); } else { // Make sure 'Start' and 'End' reference the same module duint moduleBase = ModBaseFromAddr(Start); if(moduleBase != ModBaseFromAddr(End)) return; // VA to RVA in module Start -= moduleBase; End -= moduleBase; EXCLUSIVE_ACQUIRE(LockPatches); for(auto itr = patches.begin(); itr != patches.end();) { const auto & currentPatch = itr->second; // [Start, End) if(currentPatch.addr >= Start && currentPatch.addr < End) { // Restore the original byte if necessary if(Restore) MemWrite((currentPatch.addr + moduleBase), ¤tPatch.oldbyte, sizeof(char)); itr = patches.erase(itr); } else ++itr; } } }
//check if a loop overlaps a range, inside is not overlapping bool LoopOverlaps(int Depth, uint Start, uint End, int* FinalDepth) { // CHECK: Export function if(!DbgIsDebugging()) return false; // Determine module addresses and lookup keys const uint moduleBase = ModBaseFromAddr(Start); const uint key = ModHashFromAddr(moduleBase); uint curStart = Start - moduleBase; uint curEnd = End - moduleBase; SHARED_ACQUIRE(LockLoops); // Check if the new loop fits in the old loop for(auto & itr : loops) { // Only look in the current module if(itr.first.second.first != key) continue; // Loop must be at this recursive depth if(itr.second.depth != Depth) continue; if(itr.second.start < curStart && itr.second.end > curEnd) return LoopOverlaps(Depth + 1, curStart, curEnd, FinalDepth); } // Did the user request t the loop depth? if(FinalDepth) *FinalDepth = Depth; // Check for loop overlaps for(auto & itr : loops) { // Only look in the current module if(itr.first.second.first != key) continue; // Loop must be at this recursive depth if(itr.second.depth != Depth) continue; if(itr.second.start <= curEnd && itr.second.end >= curStart) return true; } return false; }
FunctionPass::FunctionPass(duint VirtualStart, duint VirtualEnd, BBlockArray & MainBlocks) : AnalysisPass(VirtualStart, VirtualEnd, MainBlocks) { // Zero values m_FunctionInfo = nullptr; m_FunctionInfoSize = 0; // This will only be valid if the address range is within a loaded module m_ModuleStart = ModBaseFromAddr(VirtualStart); if(m_ModuleStart != 0) { char modulePath[MAX_PATH]; memset(modulePath, 0, sizeof(modulePath)); ModPathFromAddr(m_ModuleStart, modulePath, ARRAYSIZE(modulePath)); HANDLE fileHandle; DWORD fileSize; HANDLE fileMapHandle; ULONG_PTR fileMapVa; if(StaticFileLoadW( StringUtils::Utf8ToUtf16(modulePath).c_str(), UE_ACCESS_READ, false, &fileHandle, &fileSize, &fileMapHandle, &fileMapVa)) { // Find a pointer to IMAGE_DIRECTORY_ENTRY_EXCEPTION for later use ULONG_PTR virtualOffset = GetPE32DataFromMappedFile(fileMapVa, IMAGE_DIRECTORY_ENTRY_EXCEPTION, UE_SECTIONVIRTUALOFFSET); m_FunctionInfoSize = (ULONG)GetPE32DataFromMappedFile(fileMapVa, IMAGE_DIRECTORY_ENTRY_EXCEPTION, UE_SECTIONVIRTUALSIZE); // Unload the file StaticFileUnloadW(nullptr, false, fileHandle, fileSize, fileMapHandle, fileMapVa); // Get a copy of the function table if(virtualOffset) { // Read the table into a buffer m_FunctionInfo = BridgeAlloc(m_FunctionInfoSize); if(m_FunctionInfo) MemRead(virtualOffset + m_ModuleStart, m_FunctionInfo, m_FunctionInfoSize); } } } }
bool FunctionOverlaps(uint Start, uint End) { // CHECK: Exported function if(!DbgIsDebugging()) return false; // A function can't end before it begins if(Start > End) return false; const uint moduleBase = ModBaseFromAddr(Start); SHARED_ACQUIRE(LockFunctions); return (functions.count(ModuleRange(ModHashFromAddr(moduleBase), Range(Start - moduleBase, End - moduleBase))) > 0); }
ControlFlowAnalysis::ControlFlowAnalysis(uint base, uint size, bool exceptionDirectory) : Analysis(base, size) { _functionInfoData = nullptr; #ifdef _WIN64 // This will only be valid if the address range is within a loaded module _moduleBase = ModBaseFromAddr(base); if(exceptionDirectory && _moduleBase != 0) { char modulePath[MAX_PATH]; memset(modulePath, 0, sizeof(modulePath)); ModPathFromAddr(_moduleBase, modulePath, ARRAYSIZE(modulePath)); HANDLE fileHandle; DWORD fileSize; HANDLE fileMapHandle; ULONG_PTR fileMapVa; if(StaticFileLoadW( StringUtils::Utf8ToUtf16(modulePath).c_str(), UE_ACCESS_READ, false, &fileHandle, &fileSize, &fileMapHandle, &fileMapVa)) { // Find a pointer to IMAGE_DIRECTORY_ENTRY_EXCEPTION for later use ULONG_PTR virtualOffset = GetPE32DataFromMappedFile(fileMapVa, IMAGE_DIRECTORY_ENTRY_EXCEPTION, UE_SECTIONVIRTUALOFFSET); _functionInfoSize = (uint)GetPE32DataFromMappedFile(fileMapVa, IMAGE_DIRECTORY_ENTRY_EXCEPTION, UE_SECTIONVIRTUALSIZE); // Unload the file StaticFileUnloadW(nullptr, false, fileHandle, fileSize, fileMapHandle, fileMapVa); // Get a copy of the function table if(virtualOffset) { // Read the table into a buffer _functionInfoData = emalloc(_functionInfoSize); if(_functionInfoData) MemRead(virtualOffset + _moduleBase, _functionInfoData, _functionInfoSize); } } } #endif //_WIN64 }
bool PatchSet(duint Address, unsigned char OldByte, unsigned char NewByte) { ASSERT_DEBUGGING("Export call"); // Address must be valid if(!MemIsValidReadPtr(Address)) return false; // Don't patch anything if the new and old values are the same if(OldByte == NewByte) return true; PATCHINFO newPatch; newPatch.addr = Address - ModBaseFromAddr(Address); newPatch.oldbyte = OldByte; newPatch.newbyte = NewByte; ModNameFromAddr(Address, newPatch.mod, true); // Generate a key for this address const duint key = ModHashFromAddr(Address); EXCLUSIVE_ACQUIRE(LockPatches); // Find any patch with this specific address auto found = patches.find(key); if(found != patches.end()) { if(found->second.oldbyte == NewByte) { // The patch was undone here patches.erase(found); return true; } // Keep the original byte from the previous patch newPatch.oldbyte = found->second.oldbyte; found->second = newPatch; } else { // The entry was never found, insert it patches.insert(std::make_pair(key, newPatch)); } return true; }
bool PatchDelete(duint Address, bool Restore) { ASSERT_DEBUGGING("Export call"); EXCLUSIVE_ACQUIRE(LockPatches); // Do a list lookup with hash auto found = patches.find(ModHashFromAddr(Address)); if(found == patches.end()) return false; // Restore the original byte at this address if(Restore) MemWrite((found->second.addr + ModBaseFromAddr(Address)), &found->second.oldbyte, sizeof(char)); // Finally remove it from the list patches.erase(found); return true; }
bool PatchGet(duint Address, PATCHINFO* Patch) { ASSERT_DEBUGGING("Export call"); SHARED_ACQUIRE(LockPatches); // Find this specific address in the list auto found = patches.find(ModHashFromAddr(Address)); if(found == patches.end()) return false; // Did the user request an output buffer? if(Patch) { *Patch = found->second; Patch->addr += ModBaseFromAddr(Address); } // Return true because the patch was found return true; }
bool LabelSet(uint Address, const char* Text, bool Manual) { // CHECK: Exported/Command function if(!DbgIsDebugging()) return false; // A valid memory address must be supplied if(!MemIsValidReadPtr(Address)) return false; // Make sure the string is supplied, within bounds, and not a special delimiter if(!Text || Text[0] == '\1' || strlen(Text) >= MAX_LABEL_SIZE - 1) return false; // Labels cannot be "address" of actual variables if(strstr(Text, "&")) return false; // Delete the label if no text was supplied if(Text[0] == '\0') return LabelDelete(Address); // Fill out the structure data LABELSINFO labelInfo; labelInfo.manual = Manual; labelInfo.addr = Address - ModBaseFromAddr(Address); strcpy_s(labelInfo.text, Text); ModNameFromAddr(Address, labelInfo.mod, true); EXCLUSIVE_ACQUIRE(LockLabels); // Insert label by key const uint key = ModHashFromAddr(Address); if(!labels.insert(std::make_pair(ModHashFromAddr(key), labelInfo)).second) labels[key] = labelInfo; return true; }
bool CommentSet(uint Address, const char* Text, bool Manual) { // CHECK: Exported/Command function if(!DbgIsDebugging()) return false; // A valid memory address must be supplied if(!MemIsValidReadPtr(Address)) return false; // Make sure the string is supplied, within bounds, and not a special delimiter if(!Text || Text[0] == '\1' || strlen(Text) >= MAX_COMMENT_SIZE - 1) return false; // Delete the comment if no text was supplied if(Text[0] == '\0') return CommentDelete(Address); // Fill out the structure COMMENTSINFO comment; strcpy_s(comment.text, Text); ModNameFromAddr(Address, comment.mod, true); comment.manual = Manual; comment.addr = Address - ModBaseFromAddr(Address); // Key generated from module hash const uint key = ModHashFromAddr(Address); EXCLUSIVE_ACQUIRE(LockComments); // Insert if possible, otherwise replace if(!comments.insert(std::make_pair(key, comment)).second) comments[key] = comment; return true; }
static DWORD64 CALLBACK StackGetModuleBaseProc64(HANDLE hProcess, DWORD64 Address) { return (DWORD64)ModBaseFromAddr((duint)Address); }
SCRIPT_EXPORT duint Script::Module::BaseFromAddr(duint addr) { return ModBaseFromAddr(addr); }