Author | Message | Time |
---|---|---|
iago | I wrote a little class that'll patch a program's memory and, optionally, generate a little wrapper for it, and it will restore the patched locations in the destructor. Here are things to watch out for: - It has to patch over an exact number of assembly commands, otherwise it'll leave half an instruction which will probably blow stuff up - It patches with a call, so if you patch over anything that uses the stack pointer (pushes, pops, function calls, local variables, etc.), it'll blow up (solution: don't auto-generate wrapper and fix it yourself) - Also, patching over jump's is a bad idea, unless the source/destination are both passed over (solution: see above) - The function that it calls should be declared as "void __declspec(naked) __fastcall MyFunc()", and should end with a "ret". With that stuff aside, here is the code: [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 #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; class MemoryEdit { public: BYTE Wrappers[10000]; // The buffer that will hold the wrappers DWORD ptrToPatches[2000]; // An array of pointers to the patches DWORD PatchLengths[2000]; // This array goes beside ptrToPatches, and holds the lengths DWORD NumberOfPatches; // The number of places that have been patched BYTE *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 JMP = 0xE9; static const BYTE CALL = 0xE8; static const BYTE NOP = 0x90; 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, int BytesToOverwrite, bool GenerateWrapper = true); }; #endif[/code] [code]#include "MemoryEdit.h" MemoryEdit::MemoryEdit() { this->NextWrapper = this->Wrappers; this->Process = NULL; this->NumberOfPatches = 0; memset((void*)this->Wrappers, 0, sizeof(this->Wrappers)); } MemoryEdit::MemoryEdit(HANDLE Process) { this->NextWrapper = this->Wrappers; this->Process = 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++) { // Get the location and the length BYTE *PatchLocation = (BYTE*)this->ptrToPatches[i]; DWORD PatchLength = this->PatchLengths[i]; WriteProcessMemory(Process, (void*)PatchLocation, (void*)OriginalData, PatchLength, NULL); // Move up the original data to past this one, and 5 more for the call OriginalData += (PatchLength + 5); } } bool MemoryEdit::PatchMem(void *AddressToEdit, void *FunctionToCall, int BytesToOverwrite, bool GenerateWrapper) { // 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; assert(BytesToOverwrite >= 5); // Allocate space for the bytes that we'll be overwriting char *BytesOverwritten = (char*)malloc(BytesToOverwrite); if(!ReadProcessMemory(Process, AddressToEdit, BytesOverwritten, BytesToOverwrite, NULL)) return false; // Adds the wrapper bytes to the patch buffer // (must be done whether or not we actually have to use it for restoring) for(int i = 0; i < BytesToOverwrite; i++) { Wrapper << (BYTE) BytesOverwritten[i]; } // Add the "jmp" to the buffer Wrapper << JMP; // Add the distance from (NextWrapper + BytesToOverwrite) to (FunctionToCall) Wrapper << ((DWORD)FunctionToCall) - ((DWORD)NextWrapper + (DWORD)BytesToOverwrite + 5); // Add the "call" byte to the buffer Patch << CALL; // The call is going from AddressToEdit to NextWrapper, if there is a wrapper // The call is going from AddressToEdit to FunctionToCall if there is no wrapper if(GenerateWrapper) Patch << ((DWORD)NextWrapper - (DWORD)AddressToEdit - 5); else Patch << ((DWORD)FunctionToCall - (DWORD)AddressToEdit - 5); // Fill out the rest of the patch with NOPs for(int i = 0; i < (BytesToOverwrite - 5); i++) { Patch << NOP; } // First, write the wrapper if(WriteProcessMemory(Process, NextWrapper, Wrapper.c_str(), Wrapper.GetSize(), NULL) == 0) return false; // Then write the patch if(WriteProcessMemory(Process, AddressToEdit, Patch.c_str(), Patch.GetSize(), NULL) == 0) return false; // Store a pointer to the spot patched so we can fix it later ptrToPatches[NumberOfPatches] = (DWORD)AddressToEdit; // Store the number of bytes that were patches PatchLengths[NumberOfPatches] = (DWORD)BytesToOverwrite; // Move up the NextWrapper past the overwritten bytes and past the 5 "call" bytes NextWrapper += BytesToOverwrite + 5; // Increment the number of patches NumberOfPatches++; free(BytesOverwritten); return true; }[/code] Any questions, comments, or suggestions will be greatly appreciated! | August 22, 2003, 9:23 AM |
iago | ooh, you also need my buffer class for this to work: http://www.backstab.ca/~rbowes/Buffer.rar | August 22, 2003, 9:24 AM |