Manipulates memory to create a mapped view of a section with attributes SEC_NO_CHANGE
and PAGE_EXECUTE_READ
, protecting a class/struct object from memory modifications and page protection modifications. Ideal for classes containing variables which should not be tampered by outside users, or modified often after being set at runtime. This works with heap memory when creating new class objects or structure pointers; As opposed to working on static memory/image sections, this technique works for dynamic variables which can be set at runtime. The technique works for both 32-bit and 64-bit compilations. This is an experimental technique and should only be seen as a proof of concept currently, but is confirmed working.
The example code uses a RAII class with the 'placement new' C++ concept to use the mapped view's memory address as the class object, while the MappedMemory
class' RAII aspects handle mapping & unmapping memory.
- A class or structure object is first created, and its member variables are set to some initial values.
- A section is created with flags
SEC_COMMIT
+SEC_NO_CHANGE
- A memory-mapped view is created of the new section with the ViewUnmap enum
- Memory where the class object/struct resides at is copied to the view
- The view is then unmapped, and then mapped once more with the
SEC_NO_CHANGE
flag - The original class object is deleted/has its memory freed
- The class pointer's address is set to the mapped view, allowing you to access class members the same way as any normal/"non-protected" class pointer
- The view is later unmapped when you are finished with the class
To protect a class object, create an instance of the MappedMemory
class and then call the Construct<T>
function with your class type and object. Then call the Protect
function of the MappedMemory
object to map a view of a section representing your class object. The memory will automatically be unmapped when it goes out of scope due to the MappedMemory
destructor.
- Classes using inheritance or virtual functions/vtables
- Complex behaviors such as runtime polymorphism
- Class/struct members are treated as offsets to the compiler, and we make a direct copy of the class object into our view, thus we can still access members since member offsets will be identical.
- It is possible to change 'protected' member values if necessary:
- By creating a 2nd class object, and copying the first's members to the second
- Modifying any values you need to
- Then unmapping the first, and mapping the second one
- And lastly, set the first class pointer to the second one to give the illusion that values can be modified, which allows the user to continue to use the modified first pointer in their codebase.
- Make sure to link
ntdll.lib
under Linker -> Input before compiling. - Possibly dangerous in a multi-threaded scenario as we are manipulating pointers and memory on the fly; you may want to use critical sections when accessing members of the 'protected' class.
Thanks to changeofpace for the original self-remapping-code example, as this project is an idea based off of it.