Author | Message | Time |
---|---|---|
iago | Submitted for approval (well, not really, I just want to post it because I think it's cool): http://www.valhallalegends.com/iago/MemoryPatcher.rar Here's the important files (you'll also need Buffer.h and Buffer.cpp for it to work, but I just want to post the code): MemoryEdit.h: [code]// MemoryEdit.h // by iago // Created 7/28/2003 // // This is a class that will allow a user to easily edit a process's memory and restore it // when the program ends // // USAGE: // First, create a new instance of ME, setting the processes to the target process. // If you're doing this with the current process, then you can use the default // constructor. // // Next, you make patchs to the memory of the process using this function: // bool PatchMem(void *AddressToEdit, void *FunctionToCall, int BytesToOverwrite, bool GenerateWrapper = true, BYTE TempRegister = ESI); // AddressToEdit is the address to begin editing at. // FunctionToCall is the function that the patched call will be. AddressToEdit will be overwritten // with either a jmp or a call to FunctionToCall. // BytesToOverwrite is the number of bytes that will be overwritten. // **IMPORTANT** // The number of bytes overwritten MUST NOT divide any instructions in the middle. Besure to // patch over boundries between instructions // GenerateWrapper is a bool that represents whether or not to copy the code being overwritten to // a temporary buffer. The wrapper will be executed before the FunctionToCall is called // TempRegister is the register we use to store the return address in if we're using a wrapper. // ESI should always work, since functions, by default, preserve it. But I allow it to be // specified just in case. // // Notes: // DO NOT patch over a relative call or jmp, unless that patch includes the destination. // If you don't specify TempRegister, make sure that ESI isn't holding anything important. // If you do specify a TempRegister, make sure that whatever you specify isn't holding anything important. #ifndef MEMORYEDIT_H #define MEMORYEDIT_H #include <windows.h> #include <assert.h> #include <windows.h> #include <iostream> #include <string> #include "Buffer.h" using namespace std; // This struct holds the data that can restore exactly 1 patch struct Restore { void *OriginalData; // Pointer to the original data (PatchLength bytes) void *PatchLocation; // Pointer to where the patch was applied DWORD PatchLength; // The number of bytes that were patched }; class MemoryEdit { public: BYTE Wrappers[10000]; // The buffer that will hold the wrappers Restore RestoreData[2000]; // The data used to restore the patch DWORD NumberOfPatches; // The number of places that have been patched void *NextWrapper; // The next free wrapper HANDLE Process; // Handle to the process (assigned using =, so it won't know if it's closed) public: static const BYTE POP = 0x58; static const BYTE PUSH = 0x50; static const BYTE EAX = 0x00; static const BYTE EBX = 0x03; static const BYTE ECX = 0x01; static const BYTE EDX = 0x02; static const BYTE ESI = 0x06; static const BYTE EDI = 0x07; static const BYTE ESP = 0x04; static const BYTE EBP = 0x05; static const BYTE NOP = 0x90; static const BYTE RET = 0xC3; static const BYTE CALL = 0xE8; static const BYTE JMP = 0xE9; // 5 bytes for the call, 1+1+1 for the pop, push, and ret static const EXTRA_DATA = 8; MemoryEdit(); MemoryEdit(HANDLE Process); ~MemoryEdit(); // BytesToOverwrite must be at least 5 and has to be an exact number of machine code commands, // overwriting part of a command will cause problems. Also, don't overwrite a jump or call that // goes outside of the overwritten text (or any offset-operator that goes outside of the patch) // because it will cause a problem :) // Also, patching over a push will cause problems.. you'll get over it :P bool PatchMem(void *AddressToEdit, void *FunctionToCall, DWORD BytesToOverwrite, bool GenerateWrapper = true, BYTE TempRegister = ESI); }; #endif[/code] MemoryEdit.cpp: [code]#include "MemoryEdit.h" MemoryEdit::MemoryEdit() { this->NextWrapper = this->Wrappers; this->Process = GetCurrentProcess(); this->NumberOfPatches = 0; memset((void*)this->Wrappers, 0, sizeof(this->Wrappers)); } MemoryEdit::MemoryEdit(HANDLE Process) { this->NextWrapper = this->Wrappers; this->Process = ((Process == 0) ? GetCurrentProcess() : Process); this->NumberOfPatches = 0; memset((void*)this->Wrappers, 0, sizeof(this->Wrappers)); } MemoryEdit::~MemoryEdit() { // Get the first original data BYTE *OriginalData = this->Wrappers; // Loop through each patch for(DWORD i = 0; i < this->NumberOfPatches; i++) { WriteProcessMemory(Process, RestoreData[i].PatchLocation, RestoreData[i].OriginalData, RestoreData[i].PatchLength, NULL); } } bool MemoryEdit::PatchMem(void *AddressToEdit, void *FunctionToCall, DWORD BytesToOverwrite, bool GenerateWrapper, BYTE TempRegister) { // This will patch the AddressToEdit with a call to somewhere in Wrappers, which will // do the stuff from the memory that was overwritten, then jump to our function which // should only have to take care of backing up registers Buffer Patch; Buffer Wrapper; SIZE_T BytesRead; assert(BytesToOverwrite >= 5); // Allocate space for the bytes that we'll be overwriting char *BytesOverwritten = (char*)malloc(BytesToOverwrite); if(!ReadProcessMemory(Process, AddressToEdit, BytesOverwritten, BytesToOverwrite, &BytesRead)) return false; // Add the temp register pop to the Wrapper Wrapper << (BYTE)(POP | TempRegister); // Add the wrapper bytes to the patch buffer // This is used both for wrapping and to restore from for(DWORD i = 0; i < BytesToOverwrite; i++) { Wrapper << (BYTE) BytesOverwritten[i]; } // If we're making a wrapper, set it up if(GenerateWrapper) { // Thedistance of this call is to (FunctionToCall) from (NextWrapper + 1 + 5 - [the sizeof the wrapper data]) Wrapper << CALL << (DWORD)(FunctionToCall) - ((DWORD)NextWrapper + 1 + BytesToOverwrite + 5); // If we're doing a call here, we also have to add a ret Wrapper << (BYTE)(PUSH | TempRegister); Wrapper << RET; // If we're using a wrapper, the patched call needs to go from the original // address to the NextWrapper Patch << CALL << ((DWORD)(NextWrapper) - (DWORD)(AddressToEdit) - 5); } else { // If we aren't using a wrapper, the patched call needs to go from the original // address to the requested function Patch << CALL << ((DWORD)(FunctionToCall) - (DWORD)(AddressToEdit) - 5); } // Pad out the patch with NOP's for(DWORD i = 0; i < (BytesToOverwrite - 5); i++) { Patch << NOP; } // Write the patch to the target process // Then write the patch if(WriteProcessMemory(Process, AddressToEdit, Patch.c_str(), Patch.GetSize(), NULL) == 0) return false; // Write the wrapper if(WriteProcessMemory(GetCurrentProcess(), NextWrapper, Wrapper.c_str(), Wrapper.GetSize(), NULL) == 0) return false; RestoreData[NumberOfPatches].OriginalData = (void*)(((DWORD)NextWrapper) + 1); RestoreData[NumberOfPatches].PatchLocation = (void*) AddressToEdit; RestoreData[NumberOfPatches].PatchLength = BytesToOverwrite; // Move up to the next available wrapper NextWrapper = (void*)((DWORD) NextWrapper + Wrapper.GetSize()); // Increment the number of patches NumberOfPatches++; // Free up the overwritten bytes free(BytesOverwritten); return true; }[/code] | December 10, 2003, 8:02 PM |
Etheran | This I like, very nice iago, very nice. | December 10, 2003, 8:09 PM |
iago | I posted this a long time ago, but back then it was less clean, would crash if patched >1 places and tried to restore, and didn't have any documentation on how to use it :) I'm glad to be appreciated. | December 10, 2003, 8:14 PM |