The problem
We have an application, our core application, that used to do a couple of things that resulted on DEP violations (read more about it here Data Execution Prevention ).
This two things are:
- Self patch framework procedures, functions or class methods
- Generate code during runtime thru scripting engines that perform just-in-time compilation
The solution
Self patching code
Normally developers rely on self patching code when the framework they are utilizing doesn't conform to something they consider should be proper and default behavior, to extend a framework otherwise impossible to modify or to fix a bug on the framework. There might be other reasons I'm omitting here, but these are the most common I've seen. On our case, we pretty much have self-patching code that follow those three descriptions.
Most self patching code uses one variation or another of the same technique, which implies overwriting the first bytes of a procedure to perform a JMP operation of some kind to the new code that replaces the old code.
This is an example of a procedure we use to self-patch code:
procedure PatchMemory(p : Pointer; DataSize : Integer; Data : Pointer; OldData : pointer);
{$IFNDEF DELPHIXE2}
type
SIZE_T = DWORD;
{$ENDIF}
var
OldProtect : DWORD;
BytesWritten : SIZE_T;
begin
VirtualProtect (p, DataSize, PAGE_EXECUTE_READWRITE, OldProtect);
Move (p^, OldData^, DataSize);
WriteProcessMemory(GetCurrentProcess, p, Data, DataSize, BytesWritten);
VirtualProtect (p, DataSize, OldProtect, OldProtect);
end;
The key pieces of the code above are the calls to VirtualProtect() and WriteProcessMemory().
Before you overwrite a piece of memory in the code segment, you MUST unprotect the memory using a call to VirtualProtect() with new protection option PAGE_EXECUTE_READWRITE.
The second thing you have to take into consideration is *how* you overwrite the memory on the code segment. I've seen implementations that simply do a move() call with the source and target memory addresses only to see the code fail with DEP violation, even when VirtualProtect() was properly called before.
It's interesting to note that on Microsoft WriteProcessMemory() spec page there's not special note to the fact it's the only way I know of to overwrite a piece of memory on the code segment without getting a DEP violation error.
With a function like the one above, as long as you follow the basic premise of unprotect the memory first and then do patch the memory using WriteProcessMemory() you will be pretty much covered for the typical issue of overwriting the code segment in any other way.
Script engine JIT compilers
Most extensible applications/frameworks rely on some kind of scripting language. From simple yet powerful "configuration" dialects to full blow scripting languages. We use a couple of scripting languages on our programs, both being dialects of Pascal.
One of this scripting languages is newer and does its job the right way, by allocating the memory for the JIT generated code using VirtualAlloc() passing memory protection attribute PAGE_EXECUTE_READWRITE. That makes the code generated on the heap executable by simply jumping into it.
The older of our scripting engines, simply created a Delphi TMemoryStream object, and wrote into it the generated code. After it was done compiling, it tried to jump into the code generated and that of course failed with a DEP violation.
The problem in this case is that the memory allocated by the Delphi's default memory allocator doesn't use PAGE_EXECUTE_READWRITE, but PAGE_READWRITE. This is fine, and you don't want to change this default behavior.
The solution for this particular scripting engine was to replace the default TMemoryStream class, which allocates normal Delphi heap memory, with a descendant of TCustomMemoryStream class which implements it's own memory allocation approach by calling VirtualAlloc() directly with memory protection attribute PAGE_EXECUTE_READWRITE.
Gist for the class: TWinVMMemoryStream
Unit tests: TestTWinVMMemoryStream
Conclusion
Just by attacking these two problems we made our application DEP compliant.
If you are having a hard time identifying where your non-compliant code might be, my suggestion will be to first try to narrow down when the violations happen.
If violations happen when starting the app, it's likely there self-patching code violations being invoked on the initialization section of unit/modules.
If it happens later down the road, once it's up and running, it's more likely there's some JIT compiler as the culprit.
Anyway, once you know what are the two tricks you have to do:
- Always call VirtualProtect() before self-patching code, and do it using WriteProcessMemory().
- Make sure to allocate memory for JIT generated code using VirtualAlloc() with memory protection attribute PAGE_EXECUTE_READWRITE
You will get to DEP compliance in a breeze.
Happy coding!
No comments:
Post a Comment