You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Theodosius/README.md

446 lines
18 KiB

4 years ago
<div align="center">
<div>
4 years ago
<img width="27%" src="https://githacks.org/_xeroxz/theodosius/-/raw/07b58a233c0fbd289856c90158fe342fc4be4deb/imgs/theo.jpg"/>
4 years ago
</div>
</div>
4 years ago
# Theodosius - Jit linker, Mapper, Mutator, and Obfuscator
4 years ago
4 years ago
Theodosius (Theo for short) is a jit linker created entirely for obfuscation and mutation of both code, and code flow. The project is extremely modular in design and supports
4 years ago
both kernel and usermode projects. Since Theo inherits HMDM (highly modular driver mapper), any vulnerable driver that exposes arbitrary MSR writes, or physical memory read/write can be used with this framework to map unsigned code into the kernel. This is possible since HMDM inherits VDM (vulnerable driver manipulation), and MSREXEC (elevation of arbitrary MSR writes to kernel execution).
4 years ago
4 years ago
Since Theo is a jit linker, unexported symbols can be jit linked. Resolving such symbols is open ended and allows the programmer of this framework to handle how they want to resolve symbols. More on this later (check out example projects).
4 years ago
### Linking - Dynamic And Static
4 years ago
4 years ago
#### Object Files
4 years ago
4 years ago
If you define a c++ file called "main.cpp" the compiler will generate an object file by the name of "main.obj". When you refer to data or code defined in another c/c++ file, the linker uses a symbol table to resolve the address of said code/data. In this situation I am the linker and I resolve all of your symbols :).
4 years ago
4 years ago
#### What Is A Linker
4 years ago
4 years ago
A linker is a program which takes object files produces by a compiler and generates a final executable native to the operating system. A linker interfaces with not only object files but also static libraries, "lib" files. What is a "lib" file? Well a lib file is just an archive of obj's. You can invision it as a zip/rar without any compression, just concatination of said object files.
4 years ago
4 years ago
Theo is a jit linker, which means it will link objs together and map them into memory all at once. For usability however, instead of handling object files, Theo can parse entire lib files and extract the objects out of the lib.
4 years ago
4 years ago
#### Static Linking
4 years ago
Static linking is when the linker links entire routines not created by you, into your code. Say `memcpy` (if its not inlined), will be staticlly linked with the CRT. Static linking also allows for your code to be more independant as all the code you need you bring with you. However, with Theo, you cannot link static libraries which are not compiled with `mcmodel=large`. Theo supports actual static linking, in other words, using multiple static libraries at the same time.
4 years ago
#### Dynamic Linking
4 years ago
4 years ago
Dynamic linking is when external symbols are resolved at runtime. This is done by imports and exports in DLL's (dynamiclly linked libraries). Theo supports "dynamic linking", or in better terms, linking against exported routines. You can see examples of this inside of both usermode and kernelmode examples.
4 years ago
# RIP Relative Addressing
In order to allow for a routine to be scattered throughout a 64bit address space, RIP relative addressing must not be used. In order to facilitate this, a very special version
of clang-cl is used which can use `mcmodel=large`. This will generate instructions which do not use RIP relative addressing when referencing symbols outside of the routine in which the
instruction itself resides. The only exception to this is JCC instructions, (besides call) also known as branching instructions. Take this c++ code for an example:
```cpp
ObfuscateRoutine
extern "C" int ModuleEntry()
{
MessageBoxA(0, "Demo", "Hello From Obfuscated Routine!", 0);
UsermodeMutateDemo();
UsermodeNoObfuscation();
}
```
This c++ function, compiled by clang-cl with `mcmodel=large`, will generate a routine with the following instructions:
4 years ago
```
4 years ago
0x00: ; void UsermodeNoObfuscation(void)
0x00: public ?UsermodeNoObfuscation@@YAXXZ
0x00: ?UsermodeNoObfuscation@@YAXXZ proc near ; CODE XREF: ModuleEntry+42↓p
0x00: var_4 = dword ptr -4
0x00: 48 83 EC 28 sub rsp, 28h
0x04: C7 44 24 24 00 00 00 00 mov [rsp+28h+var_4], 0
0x0C: loc_C:
0x0C: 83 7C 24 24 05 cmp [rsp+28h+var_4], 5
0x11: 0F 83 38 00 00 00 jnb loc_4F
0x17: 31 C0 xor eax, eax
0x19: 48 BA 28 01 00 00 00 00 00 00 mov rdx, offset ??_C@_04DKDMNOEB@Demo?$AA@ ; "Demo"
0x23: 49 B8 00 01 00 00 00 00 00 00 mov r8, offset ??_C@_0CD@JEJKPGNA@Hello?5... ; "Hello From Non-Obfuscated Routine!"
0x2D: 48 B8 A0 01 00 00 00 00 00 00 mov rax, offset MessageBoxA
0x37: 45 31 C9 xor r9d, r9d ; uType
0x3A: 44 89 C9 mov ecx, r9d ; hWnd
0x3D: FF D0 call rax ; MessageBoxA
0x3F: 8B 44 24 24 mov eax, [rsp+28h+var_4]
0x43: 83 C0 01 add eax, 1
0x46: 89 44 24 24 mov [rsp+28h+var_4], eax
0x4A: E9 BD FF FF FF jmp loc_C
0x4F: loc_4F:
0x4F: 48 83 C4 28 add rsp, 28h
0x53: C3 retn
0x53: ?UsermodeNoObfuscation@@YAXXZ endp
```
4 years ago
As you can see from the code above, (sorry for the terrible syntax highlighting), references to strings and calls to functions are done by first loading the address of the symbol into a register and then interfacing with the symbol.
4 years ago
4 years ago
```
4 years ago
0x2D: 48 B8 A0 01 00 00 00 00 00 00 mov rax, offset MessageBoxA
; ...
0x3D: FF D0 call rax ; MessageBoxA
```
4 years ago
4 years ago
Each of these instructions can be anywhere in virtual memory and it would not effect code execution one bit. However this is not the case with routines which have conditional branches. Take the following c++ code for example.
4 years ago
```cpp
4 years ago
ObfuscateRoutine
void LoopDemo()
4 years ago
{
4 years ago
for (auto idx = 0u; idx < 10; ++idx)
DbgPrint("> Loop Demo: %d\n", idx);
}
```
4 years ago
4 years ago
This c++ function, compiled by clang-cl with `mcmodel=large`, will generate a routine with the following instructions:
4 years ago
```
4 years ago
0x58 ; void LoopDemo(void)
0x58 public ?LoopDemo@@YAXXZ
0x58 ?LoopDemo@@YAXXZ proc near
0x58 var_4 = dword ptr -4
0x58
0x58 48 83 EC 28 sub rsp, 28h
0x5C C7 44 24 24 00 00 00 00 mov [rsp+28h+var_4], 0
0x64 loc_64:
0x64 83 7C 24 24 0A cmp [rsp+28h+var_4], 0Ah
0x69 0F 83 2A 00 00 00 jnb loc_99
0x6F 8B 54 24 24 mov edx, [rsp+28h+var_4]
0x73 48 B9 60 01 00 00 00 00 00 00 mov rcx, offset ??_C@_0BB@HGKDPLMC@?$.... ; "> Loop Demo: %d\n"
0x7D 48 B8 38 02 00 00 00 00 00 00 mov rax, offset DbgPrint
0x87 FF D0 call rax ; DbgPrint
0x89 8B 44 24 24 mov eax, [rsp+28h+var_4]
0x8D 83 C0 01 add eax, 1
0x90 89 44 24 24 mov [rsp+28h+var_4], eax
0x94 E9 CB FF FF FF jmp loc_64
0x99 loc_99:
0x99 48 83 C4 28 add rsp, 28h
0x9D C3 retn
0x9D ?LoopDemo@@YAXXZ endp
4 years ago
```
4 years ago
Uh oh, `jnb loc_99`?, thats RIP relative! In order to handle branching operations, a "jump table" is generated by `obfuscation::obfuscate` explicit default constructor. Instead of branching to the RIP relative code, it will instead branch to an inline jump (`JMP [RIP+0x0]`). As demonstrated below, the branching operation is altered to branch to an asbolute jump.
```
ffff998b`c5369e60 0f830e000000 jnb ffff998b`c5369e74
ffff998b`c5369e66 ff2500000000 jmp qword ptr [ffff998b`c5369e6c]
...
ffff998b`c5369e74 ff2500000000 jmp qword ptr [ffff998b`c5369e7a]
```
The linker is able to get the address of the branching code by taking the rip relative virtual address of the branching operation, which is a signed number, and adding it to the current byte offset into the current routine, plus the size of the branching instruction. For example `LoopDemo@17` + size of the branching instruction, which is six bytes, then adding the signed relative virtual address (0x2A). The result of this simple calculation gives us `LoopDemo@65`, which is correct, the branch goes to `add rsp, 28h` in the above example.
4 years ago
4 years ago
# Usage - Using Theodosius
Theodosius uses the same class structure as HMDM does. Its a highly modular format which allows for extreme usage, supporting almost every idea one might have. In order to use Theo, you must first define three lambdas, `theo::memcpy_t` a method of copying memory, `theo::malloc_t` a method to allocate executable memory, and lastely `theo::resolve_symbol_t` a lamdba to resolve external symbols.
4 years ago
### `theo::memcpy_t` - copy memory lambda
4 years ago
This is used to write memory, it is never used to read memory. An example of this lambda using VDM could be:
```cpp
theo::memcpy_t _kmemcpy =
[&](void* dest, const void* src, std::size_t size) -> void*
{
static const auto kmemcpy =
reinterpret_cast<void*>(
utils::kmodule::get_export(
"ntoskrnl.exe", "memcpy"));
return vdm.syscall<decltype(&memcpy)>(kmemcpy, dest, src, size);
};
```
This uses VDM to syscall into memcpy exported by ntoskrnl... If you want to do something in usermode you can proxy memcpy to `WriteProcessMemory` or any other method of writing memory.
```cpp
theo::memcpy_t _memcpy =
[&](void* dest, const void* src, std::size_t size) -> void*
{
SIZE_T bytes_handled;
if (!WriteProcessMemory(phandle, dest, src, size, &bytes_handled))
{
std::printf("[!] failed to write process memory...\n");
exit(-1);
}
return dest;
};
```
4 years ago
### `theo::malloc_t` - allocate executable memory
4 years ago
4 years ago
This lambda is used to allocate executable memory. Any method will do as long as the memcpy lambda can write to the allocated memory. An MSREXEC example for this lambda is defined below.
4 years ago
4 years ago
```cpp
theo::malloc_t _kalloc = [&](std::size_t size) -> void*
{
void* alloc_base;
msrexec.exec
(
[&](void* krnl_base, get_system_routine_t get_kroutine) -> void
{
using ex_alloc_pool_t =
void* (*)(std::uint32_t, std::size_t);
const auto ex_alloc_pool =
reinterpret_cast<ex_alloc_pool_t>(
get_kroutine(krnl_base, "ExAllocatePool"));
alloc_base = ex_alloc_pool(NULL, size);
}
);
return alloc_base;
};
```
This lambda uses MSREXEC to allocate kernel memory via ExAllocatePool. However this is completely open ended on how you want to do it, you can allocate your memory into discarded
sections, you can allocate your memory in another address space, etc... Its extremely modular.
Another, yet simple, usermode example for this lambda is defined below.
```cpp
theo::malloc_t _alloc = [&](std::size_t size) -> void*
{
return VirtualAllocEx
(
phandle,
nullptr,
size,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
};
```
4 years ago
4 years ago
### `theo::resolve_symbol_t` - resolve external symbol
This lambda will try and resolve external symbols. Symbols which are not defined inside of any object files. For example `PiddbCacheTable`, an unexported ntoskrnl symbol, which normally people sig scan for, can now be jit linked. This is possible by parsing a MAP file, however you can recode this to support PDB's, etc. Again its completely opened ended on how you want to resolve symbols.
```cpp
theo::resolve_symbol_t resolve_symbol =
[&, &extern_symbols = extern_symbols](const char* symbol_name) -> std::uintptr_t
{
std::uintptr_t result = 0u;
for (auto& [drv_name, drv_symbols] : extern_symbols)
{
// each kernel module... find a driver with a matching map file name...
// I.E ntoskrnl.exe.map == ntoskrnl.exe...
utils::kmodule::each_module
(
[&, &drv_name = drv_name, &drv_symbols = drv_symbols]
(PRTL_PROCESS_MODULE_INFORMATION drv_info, const char* drv_path) -> bool
{
const auto _drv_name =
reinterpret_cast<const char*>(
drv_info->OffsetToFileName + drv_info->FullPathName);
// if this is the driver, load it, loop over its sections
// calc the absolute virtual address of the symbol...
if (!strcmp(_drv_name, drv_name.c_str()))
{
const auto drv_load_addr =
reinterpret_cast<std::uintptr_t>(
LoadLibraryExA(drv_path, NULL, DONT_RESOLVE_DLL_REFERENCES));
std::uint32_t section_count = 1u;
utils::pe::each_section
(
[&, &drv_symbols = drv_symbols]
(PIMAGE_SECTION_HEADER section_header, std::uintptr_t img_base) -> bool
{
if (section_count == drv_symbols[symbol_name].first)
{
result = reinterpret_cast<std::uintptr_t>(drv_info->ImageBase) +
section_header->VirtualAddress + drv_symbols[symbol_name].second;
// we found the symbol...
return false;
}
++section_count;
// keep going over sections...
return true;
}, drv_load_addr
);
}
// keep looping over modules until we resolve the symbol...
return !result;
}
);
}
// if the symbol was not resolved in any of the map files then try
// to see if its an export from any other drivers...
if (!result)
{
utils::kmodule::each_module
(
[&](PRTL_PROCESS_MODULE_INFORMATION drv_info, const char* drv_path) -> bool
{
const auto drv_name =
reinterpret_cast<const char*>(
drv_info->OffsetToFileName + drv_info->FullPathName);
// false if we found the symbol...
return (!(result = utils::kmodule::get_export(drv_name, symbol_name)));
}
);
}
return result;
};
```
4 years ago
4 years ago
Another example of this lambda can be viewed in the usermode examples. This routine simply loops over every single module mapped into the specific process you want to map/link with.
```cpp
theo::resolve_symbol_t _resolver =
[&, &extern_symbols = extern_symbols](const char* symbol_name) -> std::uintptr_t
{
auto loaded_modules = std::make_unique<HMODULE[]>(64);
std::uintptr_t result = 0u, loaded_module_sz = 0u;
if (!EnumProcessModules(phandle,
loaded_modules.get(), 512, (PDWORD)&loaded_module_sz))
return {};
for (auto i = 0u; i < loaded_module_sz / 8u; i++)
{
wchar_t file_name[MAX_PATH] = L"";
if (!GetModuleFileNameExW(phandle,
loaded_modules.get()[i], file_name, _countof(file_name)))
continue;
if ((result = reinterpret_cast<std::uintptr_t>(
GetProcAddress(LoadLibrary(file_name), symbol_name))))
break;
}
return result;
};
```
4 years ago
### Creating Instance
Once all three lambdas are defined, you can then create a `theo::hmm_ctx` (highly modular mapper context). This class is like the one from HMDM however it requires an extra lambda to resolve external symbols.
```cpp
theo::hmm_ctx drv_mapper({ _alloc, _memcpy, _resolver });
const auto drv_entry =
reinterpret_cast<LPTHREAD_START_ROUTINE>(
drv_mapper.map_objs(image_objs));
```
### Calling Entry
#### MSREXEC - Call Entry Example
The entry point of the mapped code is not invoked by `hmm_ctx`, but rather its left up to you to call. An example of calling the entry point can be seen below.
```cpp
int result;
msrexec.exec([&result, drv_entry = drv_entry]
(void* krnl_base, get_system_routine_t get_kroutine) -> void
{
using drv_entry_t = int(*)();
result = reinterpret_cast<drv_entry_t>(drv_entry)();
});
```
#### VDM - Call Entry Example
Another example, this one using VDM, can be seen below.
```cpp
const auto entry_result =
vdm.syscall<NTSTATUS(*)()>(
reinterpret_cast<void*>(drv_entry));
```
#### WinAPI - CreateRemoteThread
Another example, this one using WinAPI's, can be seen below.
```cpp
std::uint32_t tid = 0u;
CreateRemoteThread
(
phandle, NULL,
NULL, drv_entry,
NULL, NULL,
(LPDWORD)&tid
);
```
4 years ago
# Obfuscation
4 years ago
4 years ago
The usage of the word obfuscation in this project is use to define any changes made to code, this includes code flow. `obfuscation::obfuscate`, a base class, which is inherited and expanded upon by `obfuscation::mutation`, obfuscates code flow by inserting `JMP [RIP+0x0]` instructions after every single instruction. This allows for a routine to be broken up into unique allocations of memory and thus provides more canvas room for creative ideas.
4 years ago
4 years ago
### Obfuscate - Base Class
4 years ago
4 years ago
The base class, as described in the above section, contains a handful of util routines and a single explicit constructor which is the corner stone of the class. The constructor fixes JCC relative virtual addresses so that if the condition is met, instead of jumping instruction pointer relativitly, it will jump to an addition jmp (`JMP [RIP+0x0]`).
LEA's, nor CALL's are rip relative, even for symbols defined inside of the routine in which the instruction is compiled into. In other words JCC instructions are the only instruction pointer relative instructions that are generated.
4 years ago
4 years ago
```
instruction
jmp next instruction
instruction
jmp next instruction
instruction
jmp next instruction
```
4 years ago
### Mutation - Inherts Obfuscation
4 years ago
4 years ago
This class inherits from `obfuscate` and adds additional code, or "mutation". This class is a small example of how to use inheritance with `obfuscate` base class. It generates a stack push/pop palindrome. The state of the stack is restored before the routines actual instruction is executed. The assembly will now look like this in memory:
```
4 years ago
push gp
push gp
push gp
...
pop gp
pop gp
pop gp
exec routine instruction
jmp next instruction
push gp
push gp
push gp
push gp
push gp
...
pop gp
pop gp
pop gp
pop gp
pop gp
exec routine instruction
jmp next instruction
4 years ago
push gp
push gp
push gp
...
pop gp
pop gp
pop gp
exec routine instruction
jmp next instruction
4 years ago
```
Again this is just a demo/POC on how you can inherit `obfuscate`. This also shows an example of how to use `asmjit`.