As long as the section is writeable, yes you can use self-modifying code.
I'm not aware of any "limitations" except that doing so often is slow (cache / pipeline flushing etc). But of course, if you do a rare change such as from a setting or input, the result could actually be faster if it's ran many times (no conditionals for that setting if you "hardcode" it into the code by modifying it). Just don't modify code within an inner loop or something
I assume COFF is the PE executable right? (cause I've used self-modifying code on Windows binaries just fine...)
I am myself curious as to your results, although unfortunately too lazy to check just now. I thought that if you linked a bunch of *.OBJ files together that all segments with the same name got concatenated into a single segment.
But does that mean that your segment becomes no longer writeable because it's linked with other non-writeable segments or that that other segments become writeable thus creating a security hole?
In Windows you can map pages as writeable at run-time with VirtualAlloc and VirtualProtect. In 32-bit Windows you can link the FASM.DLL into your application and assemble code at run-time as well. No such luck in 64-bit Windows.
I didn't know that. I tried an example *.OBJ file:
test1: movRAX, test2
test2: movRAX, test1
test3: movRAX, 100
test4: movRAX, test3
So when you call test4 it gives you a pointer to the code of test3 and when you call test1 it gives you a pointer to the code of test2. Using these pointers to copy the test3 code into test2, the program crashes. However, if we instead call test2 first to get a pointer to the code of test1 and then copy the code of test3 into test1, the program runs and test1 now returns 100. So this worked with LINK.exe, although it whines with a LNK4078. Nice to know
Wow, it actually works. I was able to copy a couple mov instructions to another function (func2 below) and verified that the movs executed when the function (func2) ran. My setup:
func1: .modificationLabel: movrcx, 58;random instructions
movrax, rcx;random instructions
...;random instructions for later verification
.endModificationLabel: ;optionally subtract modificationLabel from endModificationLabel to determine instruction byte size
;then, movsb to func2's destinationLabel
func2: .destinationLabel: rb32;size determined once from subtraction. Re-run the subtraction if any changes are made there, not able to determine this dynamically?
;then, writeFile the contents of rax, etc (set by random instructions defined in func1)
There's a bit more to the setup, but it's behaving as expected so far. Before getting started with this, I had to spend time converting the entire COFF from 32 to 64 bits, which was a bit of a learning curve. The rsp alignment manipulations were a huge pain (still are).
I'm not able to copy calls yet, however. They cause runtime stalls when func2 executes. For example,
which my subtraction says is 36 bytes, will copy to func2, but the moment I run func2, the executable stalls and the calls do not execute (numAsLetters, debugMsg2 are custom functions). Maybe it's an rsp problem, not sure yet.
Calls are encoded as relative to the instruction pointer, are you sure you use the same base address? You need to find the base of the function you want to modify (i.e. the address of func2) and use 'org' directive before your modification label. This tells FASM to assume the address of the function is there, so it encodes calls properly. Of course you will have to make sure your code is relocated relative to the exe/library always the exact same.
If the executable (or library, even worse) is dynamically relocated (ASLR? idk if you can turn it off in Windows x64 with some PE/COFF property) you'll have to absolutely make sure your numAsLetters and other functions are always relocated in the exact same way relative to it. So if it gets placed at +1000, your code must be at +1000 also.
Otherwise, you'll have to generate the call instruction's offset when you "copy" it, accordingly. You can't just copy it and expect relative-addressing to work. BTW this includes any rip-relative offsets you use in the replacement that accesses your variables (globals).
Also I don't know if this works with COFF, but sounds to me like you need to fiddle with low level stuff. You should learn some basic instruction encoding first.
Cool, working on a few things in parallel, but almost got it now. The conversion to 64-bit has required a lot of changes.
Think I more or less get the RIP requirements and yes, assembling by hand really helps. I've encoded (with db) some 32 bit instructions with mod/rm etc, getting up to speed with the 64-bit.
A big problem I just realized was that enough stack space needs to be reserved for any function that is called or that the called function calls. So if function A requires only 4 parameters but A calls B, which requires 6, rsp needs to be decremented enough for function B.
My debug function called the WIN createfile which required 7 parameters. It was stalling in 64-bit mode when calling the debug func within another func with
. Trying not to use a debugger yet, so gets difficult when my "debugger" breaks.
Should be good now, will reach out if I have any questions down the road, but think it'll work out.
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum