Hi. It’s been a while. I figured I could transcribe what I do at my new job and post it here since it’s an excellent learning experience. Work has given me a lot of ideas to put on Substack, so here goes.
Runtime Patching
Well, it sounds pretty straightforward. Instead of patching through a disassembler (like IDA or Ghidra), you are writing a program to perform the patch while your target process is running.
But why would you even need to do this?
There are some cases where you simply can’t change the static code of a file. Perhaps it goes through runtime hash checks, or maybe it’s a packed program, and you need to perform the patch while it unpacks itself. The former is commonly seen in video games, the latter in sophisticated malware.
The methodology is this:
Find the memory location within the program you wish to patch.
Write your own program that dynamically finds this location.
Write your program in a way that overwrites this location with your own code.
Run the target program.
Run your program.
Pray that nothing crashes.
So, here’s what you need:
A disassembler to find what you’re looking for.
Knowledge of a lower level language (like C++).
You probably should have read my post about static patching.
A willingness to accept using the Windows API.
So, of course, I have already written something that everyone can fiddle with. Grab it from my Tor site. It should be under programs/sewing/ (since this isn’t a malicious file). Time to figure out what it does. You should definitely follow along, because if you just read what I have written below, then you are going to be super confused and lost. But that’s okay, thanks for reading <3.
There are 2 programs: needle.exe and thread.exe. thread.exe is my written patch, you can use it as a reference if you are writing your own. needle.exe is the program we are aiming to patch.
Running it plainly doesn’t do anything too special. It just prints out multiples of 1000.
I challenge you to change this multiple.
Time to disassemble it (make sure you have visible opcode bytes).
This is the main part of the main()
function that we really care about. Note that ecx
is incremented by 1000 (or 0x03e8
) every 1000 milliseconds. Don’t get confused about there being 2 instances of 1000. The first one is used in the Sleep()
function. I changed the hex representation of the numbers to decimal. They may show up as 3e8h on your side.
We can easily change this number through IDA, but that isn’t our goal. Remember that we need to get the memory location of the instruction that we want to patch. A common method of this is creating a signature.
A signature is a unique string of bytes (see them on the left of that screenshot) that we can scan a process for. There is surely a script that can do this for us, but I’m going to make you create your own signature by hand (or you could totally steal it from thread.cc).
Take each byte (starting from line 0040F02C
), and save it somewhere. Take the entire line (it’s a pretty unique instruction).
At this point, you have 81 C1 E8 03 00 00
. Now, you need to make sure that this sequence of bytes exists nowhere else in the program, or else your signature scanning could wind up with the wrong address. If you’re in IDA, press Alt+B or go to Search/Sequence of bytes… (don’t forget to scroll to the top of the file). Next, paste your bytes into the search bar and click OK. It should send you right back to where you got the bytes, then press Ctrl+B or Search/Next sequence of bytes and it should fail and send you nowhere.
No, I have no idea how to do this in Ghidra or anything else so just take my word that that one line is a good enough signature.
You have your byte signature, and now you’re done with with disassembly. Now it’s time to write a program using the Windows API.
Once again, you can use thread.cc as a reference, but I encourage you to try and write a program from scratch. Yes, the Windows API docs suck. I know.
So, go!
Anyways here’s my walkthrough.
First thing you need to do is get all the processes on the computer. You need to open each process (with the proper permissions). Then, you need to get the main module of these processes. Lastly, get the name of the main module. If the module name is equal to “needle.exe”, then you’ve found the program you need to patch. You need the module handle to continue (HMODULE
).
int main(int argc, char **argv)
{
// Enum all processes
DWORD aProcesses[1024], cbNeeded, cProcesses;
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
{
return 1;
}
HANDLE hNeedle = 0;
HMODULE hNeedleMod = 0;
for (size_t i = 0; i < cbNeeded / sizeof(DWORD); i++)
{
// Valid process
if (aProcesses[i] != 0)
{
// Get process
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, aProcesses[i]);
if (hProcess != 0)
{
HMODULE hMod;
DWORD cbNeeded;
// Get process modules
if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
{
CHAR szProcessName[MAX_PATH]{};
// Main module always first
GetModuleBaseNameA(hProcess, hMod, szProcessName, sizeof(szProcessName));
if (!strcmp(szProcessName, "needle.exe"))
{
hNeedle = hProcess;
hNeedleMod = hMod;
goto breakout;
}
// std::cout << szProcessName << std::endl;
}
}
// Close process handle, we skip over this if it's needle.exe
CloseHandle(hProcess);
}
}
breakout:
if (hNeedle == 0)
{
std::cout << "needle.exe not found" << std::endl;
return 1;
}
Yeah. I know.
It’s hard to put that into words, but the paragraph above explains the logic if what you’re doing a bit well.
Once you have the module, you then need the starting address and the module’s size. Then, read the entire process into memory. This is the easiest way for executables. You must use ReadProcessMemory()
as it performs under the proper permissions of the Windows API.
// Get needle's base address
MODULEINFO miNeedle;
GetModuleInformation(hNeedle, hNeedleMod, &miNeedle, sizeof(miNeedle));
unsigned char *start = (unsigned char *)miNeedle.lpBaseOfDll;
// Read the process into memory
unsigned char *process = new unsigned char[miNeedle.SizeOfImage];
ReadProcessMemory(hNeedle, miNeedle.lpBaseOfDll, process, miNeedle.SizeOfImage, NULL);
Now, you are about to start scanning the program for your signature. You can of course Google modern sigscanning functions. They’re everywhere. Mine is slightly adjusted for exe files, since you have to use ReadProcessMemory()
and WriteProcessMemory()
.
// Scan for pattern
unsigned char pattern[] = {0x81, 0xC1, 0xE8, 0x03, 0x00, 0x00};
size_t offset = sigscan(pattern, sizeof(pattern), process, process + miNeedle.SizeOfImage);
if (offset == 0)
{
std::cout << "pattern not found" << std::endl;
delete[] process;
return 1;
}
And the sigscan()
function which returns the offset from the start of the binary to the memory location of the signature you are scanning for:
size_t sigscan(unsigned char *pattern, size_t len, unsigned char *start, unsigned char *end)
{
unsigned char *p = start;
while (p < end - len)
{
if (*p == *pattern)
{
unsigned char *q = p;
unsigned char *r = pattern;
while (r < pattern + len)
{
if (*q != *r)
break;
q++;
r++;
}
if (r == pattern + len)
return (size_t)(p - start);
}
p++;
}
return 0;
}
Now, patch the memory. Remember that the number you are overwriting is 2 bytes beyond the memory location.
81 C1
: add ecxE8 03 00 00
: 0x000003E8 (or 1000)
So, you are starting at E8
which is the starting point of the image + the offset you got from sigscan()
+ 2. And, it’s 4 bytes long, so you can simply write a DWORD
(4 bytes) to it.
// Patch
DWORD write = 8675309;
if (!WriteProcessMemory(hNeedle, start + offset + 2, &write, sizeof(write), NULL))
{
std::cout << "WriteProcessMemory failed " << GetLastError() << std::endl;
delete[] process;
return 1;
}
std::cout << "patched" << std::endl;
delete[] process;
return 0;
}
So, after spewing all of that code into a file and compiling it, you can finally execute your patch!
Run needle.exe.
Run yourprogram.exe
???
Profit
Congratulations. You have successfully written to a program’s memory space from another program.
When I got about halfway through writing this post, I rapidly realized that the learning curve for this is quite hellacious. So, I encourage you to get used to the Windows API and writing C++ by hand so that you can understand the absolute mouthful that is this post. Then again, most of my posts are usually this scatterbrained.
Anyways, hopefully you have learned how to do something you didn’t know how to do before.
Go!
-BowTiedCrawfish