mirror of https://github.com/x64dbg/GleeBug
Compare commits
17 Commits
2025.07.04
...
vs2015
| Author | SHA1 | Date |
|---|---|---|
|
|
1ee94ba319 | |
|
|
95f66412b6 | |
|
|
e0a457965f | |
|
|
2cc003cdcf | |
|
|
c4fa827120 | |
|
|
84f8943a6e | |
|
|
118945fbef | |
|
|
b8737dcce8 | |
|
|
6b7803d9d9 | |
|
|
604ce89673 | |
|
|
1533cc3e84 | |
|
|
552aa92637 | |
|
|
6e282f0091 | |
|
|
4987989b34 | |
|
|
a91b44d7e8 | |
|
|
dcb01c6abe | |
|
|
1eb094e7aa |
|
|
@ -0,0 +1,3 @@
|
||||||
|
# cmkr
|
||||||
|
/**/CMakeLists.txt linguist-generated
|
||||||
|
/**/cmkr.cmake linguist-vendored
|
||||||
|
|
@ -9,3 +9,10 @@ docs/
|
||||||
.vs/
|
.vs/
|
||||||
*.VC.db
|
*.VC.db
|
||||||
*.aps
|
*.aps
|
||||||
|
|
||||||
|
# cmkr
|
||||||
|
build*/
|
||||||
|
cmake-build*/
|
||||||
|
CMakerLists.txt
|
||||||
|
CMakeLists.txt.user
|
||||||
|
CMakeUserPresets.json
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
# This file is automatically generated from cmake.toml - DO NOT EDIT
|
||||||
|
# See https://github.com/build-cpp/cmkr for more information
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
|
||||||
|
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
||||||
|
message(FATAL_ERROR "In-tree builds are not supported. Run CMake from a separate directory: cmake -B build")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMKR_ROOT_PROJECT OFF)
|
||||||
|
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||||
|
set(CMKR_ROOT_PROJECT ON)
|
||||||
|
|
||||||
|
# Bootstrap cmkr and automatically regenerate CMakeLists.txt
|
||||||
|
include(cmkr.cmake OPTIONAL RESULT_VARIABLE CMKR_INCLUDE_RESULT)
|
||||||
|
if(CMKR_INCLUDE_RESULT)
|
||||||
|
cmkr()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Enable folder support
|
||||||
|
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
|
|
||||||
|
# Create a configure-time dependency on cmake.toml to improve IDE support
|
||||||
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS cmake.toml)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Options
|
||||||
|
option(GLEEBUG_RESOURCES "" ON)
|
||||||
|
|
||||||
|
project(GleeBug
|
||||||
|
LANGUAGES
|
||||||
|
C
|
||||||
|
CXX
|
||||||
|
)
|
||||||
|
|
||||||
|
# Target: GleeBug
|
||||||
|
set(GleeBug_SOURCES
|
||||||
|
"GleeBug/Debugger.Breakpoint.h"
|
||||||
|
"GleeBug/Debugger.Dll.cpp"
|
||||||
|
"GleeBug/Debugger.Dll.h"
|
||||||
|
"GleeBug/Debugger.Global.h"
|
||||||
|
"GleeBug/Debugger.Loop.DebugString.cpp"
|
||||||
|
"GleeBug/Debugger.Loop.Dll.cpp"
|
||||||
|
"GleeBug/Debugger.Loop.Exception.cpp"
|
||||||
|
"GleeBug/Debugger.Loop.Process.cpp"
|
||||||
|
"GleeBug/Debugger.Loop.Rip.cpp"
|
||||||
|
"GleeBug/Debugger.Loop.Thread.cpp"
|
||||||
|
"GleeBug/Debugger.Loop.Unknown.cpp"
|
||||||
|
"GleeBug/Debugger.Loop.cpp"
|
||||||
|
"GleeBug/Debugger.NativeAttach.h"
|
||||||
|
"GleeBug/Debugger.Process.Breakpoint.cpp"
|
||||||
|
"GleeBug/Debugger.Process.Memory.cpp"
|
||||||
|
"GleeBug/Debugger.Process.cpp"
|
||||||
|
"GleeBug/Debugger.Process.h"
|
||||||
|
"GleeBug/Debugger.Thread.HardwareBreakpoint.cpp"
|
||||||
|
"GleeBug/Debugger.Thread.Registers.Flag.h"
|
||||||
|
"GleeBug/Debugger.Thread.Registers.GetSet.cpp"
|
||||||
|
"GleeBug/Debugger.Thread.Registers.Register.h"
|
||||||
|
"GleeBug/Debugger.Thread.Registers.cpp"
|
||||||
|
"GleeBug/Debugger.Thread.Registers.h"
|
||||||
|
"GleeBug/Debugger.Thread.cpp"
|
||||||
|
"GleeBug/Debugger.Thread.h"
|
||||||
|
"GleeBug/Debugger.cpp"
|
||||||
|
"GleeBug/Debugger.h"
|
||||||
|
"GleeBug/GleeBug.h"
|
||||||
|
"GleeBug/Static.BufferFile.cpp"
|
||||||
|
"GleeBug/Static.BufferFile.h"
|
||||||
|
"GleeBug/Static.File.cpp"
|
||||||
|
"GleeBug/Static.File.h"
|
||||||
|
"GleeBug/Static.Global.h"
|
||||||
|
"GleeBug/Static.Pattern.cpp"
|
||||||
|
"GleeBug/Static.Pattern.h"
|
||||||
|
"GleeBug/Static.Pe.Section.h"
|
||||||
|
"GleeBug/Static.Pe.cpp"
|
||||||
|
"GleeBug/Static.Pe.h"
|
||||||
|
"GleeBug/Static.Region.h"
|
||||||
|
"GleeBug/Zydis/Zydis.c"
|
||||||
|
"GleeBug/Zydis/Zydis.h"
|
||||||
|
"GleeBug/ntdll.h"
|
||||||
|
"GleeBug/oprintf.h"
|
||||||
|
"GleeBug/stringutils.cpp"
|
||||||
|
"GleeBug/stringutils.h"
|
||||||
|
cmake.toml
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(GleeBug STATIC)
|
||||||
|
|
||||||
|
target_sources(GleeBug PRIVATE ${GleeBug_SOURCES})
|
||||||
|
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${GleeBug_SOURCES})
|
||||||
|
|
||||||
|
target_compile_definitions(GleeBug PUBLIC
|
||||||
|
NOMINMAX
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_features(GleeBug PUBLIC
|
||||||
|
cxx_std_23
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(GleeBug PUBLIC
|
||||||
|
.
|
||||||
|
)
|
||||||
|
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8) # x64
|
||||||
|
target_link_libraries(GleeBug PUBLIC
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/GleeBug/ntdll_x64.lib"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 4) # x32
|
||||||
|
target_link_libraries(GleeBug PUBLIC
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/GleeBug/ntdll_x86.lib"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Target: MyDebugger
|
||||||
|
if(CMKR_ROOT_PROJECT) # root
|
||||||
|
set(MyDebugger_SOURCES
|
||||||
|
"MyDebugger/MyDebugger.h"
|
||||||
|
"MyDebugger/PeTests.h"
|
||||||
|
"MyDebugger/main.cpp"
|
||||||
|
cmake.toml
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(MyDebugger)
|
||||||
|
|
||||||
|
target_sources(MyDebugger PRIVATE ${MyDebugger_SOURCES})
|
||||||
|
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${MyDebugger_SOURCES})
|
||||||
|
|
||||||
|
if(NOT TARGET GleeBug)
|
||||||
|
message(FATAL_ERROR "Target \"GleeBug\" referenced by \"MyDebugger\" does not exist!")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(MyDebugger PRIVATE
|
||||||
|
GleeBug
|
||||||
|
)
|
||||||
|
|
||||||
|
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
|
||||||
|
if(NOT CMKR_VS_STARTUP_PROJECT)
|
||||||
|
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT MyDebugger)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endif()
|
||||||
|
# Target: GleeBugStaticEngine
|
||||||
|
set(GleeBugStaticEngine_SOURCES
|
||||||
|
"StaticEngine/Emulator.h"
|
||||||
|
"StaticEngine/FileMap.h"
|
||||||
|
"StaticEngine/TitanEngine.h"
|
||||||
|
"StaticEngine/TitanEngineEmulator.cpp"
|
||||||
|
"StaticEngine/ntdll.h"
|
||||||
|
cmake.toml
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(GleeBugStaticEngine SHARED)
|
||||||
|
|
||||||
|
target_sources(GleeBugStaticEngine PRIVATE ${GleeBugStaticEngine_SOURCES})
|
||||||
|
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${GleeBugStaticEngine_SOURCES})
|
||||||
|
|
||||||
|
if(NOT TARGET GleeBug)
|
||||||
|
message(FATAL_ERROR "Target \"GleeBug\" referenced by \"GleeBugStaticEngine\" does not exist!")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(GleeBugStaticEngine PRIVATE
|
||||||
|
GleeBug
|
||||||
|
)
|
||||||
|
|
||||||
|
# Target: GleeBugTitanEngine
|
||||||
|
set(GleeBugTitanEngine_SOURCES
|
||||||
|
"TitanEngineEmulator/Emulator.h"
|
||||||
|
"TitanEngineEmulator/FileMap.h"
|
||||||
|
"TitanEngineEmulator/Global.Engine.Context.cpp"
|
||||||
|
"TitanEngineEmulator/Global.Engine.Context.h"
|
||||||
|
"TitanEngineEmulator/Hider.h"
|
||||||
|
"TitanEngineEmulator/PEB.h"
|
||||||
|
"TitanEngineEmulator/TitanEngine.h"
|
||||||
|
"TitanEngineEmulator/TitanEngineEmulator.cpp"
|
||||||
|
"TitanEngineEmulator/ntdll.h"
|
||||||
|
"TitanEngineEmulator/resource.h"
|
||||||
|
cmake.toml
|
||||||
|
)
|
||||||
|
|
||||||
|
if(GLEEBUG_RESOURCES) # GLEEBUG_RESOURCES
|
||||||
|
list(APPEND GleeBugTitanEngine_SOURCES
|
||||||
|
"TitanEngineEmulator/TitanEngine.rc"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(GleeBugTitanEngine SHARED)
|
||||||
|
|
||||||
|
target_sources(GleeBugTitanEngine PRIVATE ${GleeBugTitanEngine_SOURCES})
|
||||||
|
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${GleeBugTitanEngine_SOURCES})
|
||||||
|
|
||||||
|
if(NOT TARGET GleeBug)
|
||||||
|
message(FATAL_ERROR "Target \"GleeBug\" referenced by \"GleeBugTitanEngine\" does not exist!")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(GleeBugTitanEngine PRIVATE
|
||||||
|
GleeBug
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMKR_TARGET GleeBugTitanEngine)
|
||||||
|
include(output-folders.cmake)
|
||||||
|
|
@ -8,6 +8,10 @@
|
||||||
#define GLEEBUG_HWBP_COUNT 4
|
#define GLEEBUG_HWBP_COUNT 4
|
||||||
#define GLEEBUG_PAGE_SIZE 0x1000
|
#define GLEEBUG_PAGE_SIZE 0x1000
|
||||||
|
|
||||||
|
#ifndef PAGE_SIZE
|
||||||
|
#define PAGE_SIZE 0x1000
|
||||||
|
#endif // PAGE_SIZE
|
||||||
|
|
||||||
namespace GleeBug
|
namespace GleeBug
|
||||||
{
|
{
|
||||||
//forward declarations
|
//forward declarations
|
||||||
|
|
@ -19,9 +23,7 @@ namespace GleeBug
|
||||||
struct BreakpointInfo;
|
struct BreakpointInfo;
|
||||||
struct MemoryBreakpointData;
|
struct MemoryBreakpointData;
|
||||||
|
|
||||||
//constants
|
|
||||||
const int HWBP_COUNT = GLEEBUG_HWBP_COUNT;
|
const int HWBP_COUNT = GLEEBUG_HWBP_COUNT;
|
||||||
const int PAGE_SIZE = GLEEBUG_PAGE_SIZE;
|
|
||||||
|
|
||||||
//key typedefs
|
//key typedefs
|
||||||
typedef std::pair<BreakpointType, ptr> BreakpointKey;
|
typedef std::pair<BreakpointType, ptr> BreakpointKey;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ namespace GleeBug
|
||||||
void Debugger::exceptionBreakpoint(const EXCEPTION_RECORD & exceptionRecord, const bool firstChance)
|
void Debugger::exceptionBreakpoint(const EXCEPTION_RECORD & exceptionRecord, const bool firstChance)
|
||||||
{
|
{
|
||||||
//check if the breakpoint exists
|
//check if the breakpoint exists
|
||||||
auto foundInfo = mProcess->breakpoints.find({ BreakpointType::Software, ptr(exceptionRecord.ExceptionAddress) });
|
auto exceptionAddress = ptr(exceptionRecord.ExceptionAddress);
|
||||||
|
auto foundInfo = mProcess->breakpoints.find({ BreakpointType::Software, exceptionAddress });
|
||||||
if(foundInfo == mProcess->breakpoints.end())
|
if(foundInfo == mProcess->breakpoints.end())
|
||||||
{
|
{
|
||||||
if(!this->mAttachedToProcess && !mProcess->systemBreakpoint) //handle system breakpoint
|
if(!this->mAttachedToProcess && !mProcess->systemBreakpoint) //handle system breakpoint
|
||||||
|
|
@ -18,6 +19,17 @@ namespace GleeBug
|
||||||
//call the callback
|
//call the callback
|
||||||
cbSystemBreakpoint();
|
cbSystemBreakpoint();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//check if this address had a breakpoint that was recently deleted
|
||||||
|
auto & deletedBps = mProcess->recentlyDeletedSwbp;
|
||||||
|
auto foundIt = deletedBps.find(exceptionAddress);
|
||||||
|
if(foundIt != deletedBps.end() && mThread)
|
||||||
|
{
|
||||||
|
Registers(mThread->hThread, CONTEXT_CONTROL).Gip = exceptionAddress;
|
||||||
|
mContinueStatus = DBG_CONTINUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,12 +42,26 @@ namespace GleeBug
|
||||||
Registers(mThread->hThread, CONTEXT_CONTROL).Gip = info.address;
|
Registers(mThread->hThread, CONTEXT_CONTROL).Gip = info.address;
|
||||||
|
|
||||||
//restore the original breakpoint byte and do an internal step
|
//restore the original breakpoint byte and do an internal step
|
||||||
mProcess->MemWriteUnsafe(info.address, info.internal.software.oldbytes, info.internal.software.size);
|
if(!mProcess->MemWriteUnsafe(info.address, info.internal.software.oldbytes, info.internal.software.size))
|
||||||
|
{
|
||||||
|
//failed to restore original byte, pass exception to debuggee
|
||||||
|
mContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
mProcess->StepInternal([this, info]()
|
mProcess->StepInternal([this, info]()
|
||||||
{
|
{
|
||||||
//only restore the bytes if the breakpoint still exists
|
//only restore the bytes if the breakpoint still exists
|
||||||
if(mProcess->breakpoints.find({ BreakpointType::Software, info.address }) != mProcess->breakpoints.end())
|
auto foundBreakpoint = mProcess->breakpoints.find({ BreakpointType::Software, info.address });
|
||||||
mProcess->MemWriteUnsafe(info.address, info.internal.software.newbytes, info.internal.software.size);
|
if(foundBreakpoint != mProcess->breakpoints.end())
|
||||||
|
{
|
||||||
|
if(!mProcess->MemWriteUnsafe(info.address, info.internal.software.newbytes, info.internal.software.size))
|
||||||
|
{
|
||||||
|
//failed to restore breakpoint byte, remove from maps to stay consistent
|
||||||
|
mProcess->softwareBreakpointReferences.erase(info.address);
|
||||||
|
mProcess->breakpoints.erase(foundBreakpoint);
|
||||||
|
mProcess->breakpointCallbacks.erase({ BreakpointType::Software, info.address });
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//call the generic callback
|
//call the generic callback
|
||||||
|
|
@ -141,6 +167,13 @@ namespace GleeBug
|
||||||
if(foundCallback != mProcess->breakpointCallbacks.end())
|
if(foundCallback != mProcess->breakpointCallbacks.end())
|
||||||
foundCallback->second(info);
|
foundCallback->second(info);
|
||||||
|
|
||||||
|
//if the breakpoint was deleted during callback, clear internal stepping to prevent thread suspension
|
||||||
|
if(mProcess->breakpoints.find({ BreakpointType::Hardware, info.address }) == mProcess->breakpoints.end())
|
||||||
|
{
|
||||||
|
mThread->isInternalStepping = false;
|
||||||
|
Registers(mThread->hThread, CONTEXT_CONTROL).TrapFlag = false;
|
||||||
|
}
|
||||||
|
|
||||||
//delete the breakpoint if it is singleshoot
|
//delete the breakpoint if it is singleshoot
|
||||||
if(info.singleshoot)
|
if(info.singleshoot)
|
||||||
mProcess->DeleteGenericBreakpoint(info);
|
mProcess->DeleteGenericBreakpoint(info);
|
||||||
|
|
@ -227,16 +260,20 @@ namespace GleeBug
|
||||||
auto pageAddr = bpxPage->first;
|
auto pageAddr = bpxPage->first;
|
||||||
auto pageProperties = bpxPage->second;
|
auto pageProperties = bpxPage->second;
|
||||||
|
|
||||||
//TODO: If I only have a page with Read bp and the exception was not on read, I don't execute the callback. Because since this was implemented with PAGE_GUARD, writtes or executes still trigger
|
// PAGE_GUARD is shared by access/read/write/execute breakpoints, so the exception type in
|
||||||
//This callback.
|
// ExceptionInformation[0] decides whether this guard-page fault actually belongs to this breakpoint.
|
||||||
//FIX: If the memoryBreakpointPages for this page does not have a access flag and has a read flag, but the exception was not on read. Then we resume the debuggee.
|
// Access breakpoints intentionally match every access type.
|
||||||
if((exceptionRecord.ExceptionInformation[0] != 0))
|
const auto accessType = exceptionRecord.ExceptionInformation[0];
|
||||||
{
|
const auto isAccessBreakpoint = (pageProperties.Type & 0x1) != 0;
|
||||||
//The bpx is solely on read.
|
const auto matchesRead = accessType == 0 && (((pageProperties.Type & 0x2) != 0) || isAccessBreakpoint);
|
||||||
if(((pageProperties.Type & 0x2) != 0) && ((pageProperties.Type & 0x1) == 0))
|
const auto matchesWrite = accessType == 1 && (((pageProperties.Type & 0x4) != 0) || isAccessBreakpoint);
|
||||||
|
const auto matchesExecute = accessType == 8 && (((pageProperties.Type & 0x8) != 0) || isAccessBreakpoint);
|
||||||
|
if(!matchesRead && !matchesWrite && !matchesExecute)
|
||||||
{
|
{
|
||||||
mContinueStatus = DBG_CONTINUE;
|
mContinueStatus = DBG_CONTINUE;
|
||||||
//We restore the protection
|
// This page belongs to some memory breakpoint, but not one that should trigger on this access type.
|
||||||
|
// Restore the original protection, single-step the faulting instruction, then re-apply the guard/page
|
||||||
|
// permissions if the page is still tracked by any memory breakpoint.
|
||||||
if(!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect))
|
if(!mProcess->MemProtect(pageAddr, PAGE_SIZE, pageProperties.OldProtect))
|
||||||
{
|
{
|
||||||
sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr);
|
sprintf_s(error, "MemProtect failed on 0x%p", (void*)pageAddr);
|
||||||
|
|
@ -245,39 +282,14 @@ namespace GleeBug
|
||||||
|
|
||||||
mProcess->StepInternal([this, pageAddr]()
|
mProcess->StepInternal([this, pageAddr]()
|
||||||
{
|
{
|
||||||
//seek out the page address
|
|
||||||
auto found_page = mProcess->memoryBreakpointPages.find(pageAddr);
|
auto found_page = mProcess->memoryBreakpointPages.find(pageAddr);
|
||||||
if(found_page == mProcess->memoryBreakpointPages.end())
|
if(found_page == mProcess->memoryBreakpointPages.end())
|
||||||
{
|
|
||||||
//no page being used by bpx? Then just return
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
mProcess->MemProtect(pageAddr, PAGE_SIZE, found_page->second.NewProtect);
|
mProcess->MemProtect(pageAddr, PAGE_SIZE, found_page->second.NewProtect);
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if(((pageProperties.Type & 0x1) != 0))
|
|
||||||
{
|
|
||||||
//We are fine if the breakpoint is on Access and somethine other than a read occurred.
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//This exception handler was called within a page that had no breakpoints on read or access. Probably the program generated this exception! what a 0x1337 brat.
|
|
||||||
//In this situation we return control to debuggee.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//The generated exception is on read.
|
|
||||||
//If the page doesn't have a breakpoint on read or on access then something else must have gone wrong - we pass execution to debuggee.
|
|
||||||
if((!(pageProperties.Type & 0x2)) && (!(pageProperties.Type & 0x1)))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@ namespace GleeBug
|
||||||
IsDbgReplyLaterSupported = mSafeStep;
|
IsDbgReplyLaterSupported = mSafeStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 consecutiveTimeouts = 0;
|
||||||
|
|
||||||
while(!mBreakDebugger)
|
while(!mBreakDebugger)
|
||||||
{
|
{
|
||||||
//wait for a debug event
|
//wait for a debug event
|
||||||
|
|
@ -65,11 +67,18 @@ namespace GleeBug
|
||||||
#endif
|
#endif
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Regular timeout, wait again
|
//after 2 consecutive timeouts, clear recently deleted breakpoints
|
||||||
|
//any stale events would have been delivered by now
|
||||||
|
consecutiveTimeouts++;
|
||||||
|
if(consecutiveTimeouts >= 2 && mProcess)
|
||||||
|
mProcess->recentlyDeletedSwbp.clear();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//event received, reset timeout counter
|
||||||
|
consecutiveTimeouts = 0;
|
||||||
|
|
||||||
// Handle safe stepping
|
// Handle safe stepping
|
||||||
if(IsDbgReplyLaterSupported)
|
if(IsDbgReplyLaterSupported)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,8 @@ namespace GleeBug
|
||||||
return false;
|
return false;
|
||||||
FlushInstructionCache(hProcess, nullptr, 0);
|
FlushInstructionCache(hProcess, nullptr, 0);
|
||||||
|
|
||||||
|
recentlyDeletedSwbp.insert(address);
|
||||||
|
|
||||||
//remove the breakpoint from the maps
|
//remove the breakpoint from the maps
|
||||||
softwareBreakpointReferences.erase(info.address);
|
softwareBreakpointReferences.erase(info.address);
|
||||||
breakpoints.erase(found);
|
breakpoints.erase(found);
|
||||||
|
|
@ -181,12 +183,12 @@ namespace GleeBug
|
||||||
#define PAGE_NOACCESS 0x01
|
#define PAGE_NOACCESS 0x01
|
||||||
#define PAGE_READONLY 0x02
|
#define PAGE_READONLY 0x02
|
||||||
#define PAGE_READWRITE 0x04
|
#define PAGE_READWRITE 0x04
|
||||||
#define PAGE_WRITECOPY 0x08 <- not supported
|
#define PAGE_WRITECOPY 0x08
|
||||||
|
|
||||||
#define PAGE_EXECUTE 0x10
|
#define PAGE_EXECUTE 0x10
|
||||||
#define PAGE_EXECUTE_READ 0x20
|
#define PAGE_EXECUTE_READ 0x20
|
||||||
#define PAGE_EXECUTE_READWRITE 0x40
|
#define PAGE_EXECUTE_READWRITE 0x40
|
||||||
#define PAGE_EXECUTE_WRITECOPY 0x80 <- not supported
|
#define PAGE_EXECUTE_WRITECOPY 0x80
|
||||||
|
|
||||||
#define PAGE_GUARD 0x100 <- not supported with PAGE_NOACCESS
|
#define PAGE_GUARD 0x100 <- not supported with PAGE_NOACCESS
|
||||||
#define PAGE_NOCACHE 0x200 <- not supported with PAGE_GUARD or PAGE_WRITECOMBINE
|
#define PAGE_NOCACHE 0x200 <- not supported with PAGE_GUARD or PAGE_WRITECOMBINE
|
||||||
|
|
@ -213,12 +215,16 @@ namespace GleeBug
|
||||||
|
|
||||||
static DWORD RemoveWriteAccess(DWORD dwAccess)
|
static DWORD RemoveWriteAccess(DWORD dwAccess)
|
||||||
{
|
{
|
||||||
|
//Removes write permissions and write-on-copy to trigger access violation.
|
||||||
DWORD dwBase = dwAccess & 0xFF;
|
DWORD dwBase = dwAccess & 0xFF;
|
||||||
switch(dwBase)
|
switch(dwBase)
|
||||||
{
|
{
|
||||||
case PAGE_READWRITE:
|
case PAGE_READWRITE:
|
||||||
|
case PAGE_WRITECOPY:
|
||||||
|
return (dwAccess & 0xFFFFFF00) | PAGE_READONLY;
|
||||||
case PAGE_EXECUTE_READWRITE:
|
case PAGE_EXECUTE_READWRITE:
|
||||||
return (dwAccess & 0xFFFFFF00) | (dwBase >> 1);
|
case PAGE_EXECUTE_WRITECOPY:
|
||||||
|
return (dwAccess & 0xFFFFFF00) | PAGE_EXECUTE_READ;
|
||||||
default:
|
default:
|
||||||
return dwAccess;
|
return dwAccess;
|
||||||
}
|
}
|
||||||
|
|
@ -292,12 +298,13 @@ namespace GleeBug
|
||||||
};
|
};
|
||||||
std::vector<TempMemoryBreakpointData> breakpointData;
|
std::vector<TempMemoryBreakpointData> breakpointData;
|
||||||
{
|
{
|
||||||
breakpointData.reserve(size / PAGE_SIZE);
|
breakpointData.reserve(BYTES_TO_PAGES((address - PAGE_ALIGN(address)) + size));
|
||||||
TempMemoryBreakpointData tempData;
|
TempMemoryBreakpointData tempData;
|
||||||
MemoryBreakpointData data;
|
MemoryBreakpointData data;
|
||||||
data.Type = uint32(type);
|
data.Type = uint32(type);
|
||||||
auto alignedAddress = PAGE_ALIGN(address);
|
auto alignedAddress = PAGE_ALIGN(address);
|
||||||
for(auto page = alignedAddress; page < alignedAddress + ROUND_TO_PAGES(size); page += PAGE_SIZE)
|
auto alignedEnd = PAGE_ALIGN(address + size - 1);
|
||||||
|
for(auto page = alignedAddress; page <= alignedEnd; page += PAGE_SIZE)
|
||||||
{
|
{
|
||||||
MEMORY_BASIC_INFORMATION mbi;
|
MEMORY_BASIC_INFORMATION mbi;
|
||||||
if(!VirtualQueryEx(hProcess, LPCVOID(page), &mbi, sizeof(mbi)))
|
if(!VirtualQueryEx(hProcess, LPCVOID(page), &mbi, sizeof(mbi)))
|
||||||
|
|
@ -376,7 +383,8 @@ namespace GleeBug
|
||||||
//delete the memory breakpoint from the pages
|
//delete the memory breakpoint from the pages
|
||||||
bool success = true;
|
bool success = true;
|
||||||
auto alignedAddress = PAGE_ALIGN(info.address);
|
auto alignedAddress = PAGE_ALIGN(info.address);
|
||||||
for(auto page = alignedAddress; page < alignedAddress + ROUND_TO_PAGES(info.internal.memory.size); page += PAGE_SIZE)
|
auto alignedEnd = PAGE_ALIGN(info.address + info.internal.memory.size - 1);
|
||||||
|
for(auto page = alignedAddress; page <= alignedEnd; page += PAGE_SIZE)
|
||||||
{
|
{
|
||||||
auto foundData = memoryBreakpointPages.find(page);
|
auto foundData = memoryBreakpointPages.find(page);
|
||||||
if(foundData == memoryBreakpointPages.end())
|
if(foundData == memoryBreakpointPages.end())
|
||||||
|
|
|
||||||
|
|
@ -65,8 +65,58 @@ namespace GleeBug
|
||||||
|
|
||||||
bool Process::MemWriteSafe(ptr address, const void* buffer, ptr size, ptr* bytesWritten)
|
bool Process::MemWriteSafe(ptr address, const void* buffer, ptr size, ptr* bytesWritten)
|
||||||
{
|
{
|
||||||
//TODO: correctly implement this
|
if(size == 0)
|
||||||
return MemWrite(address, buffer, size, bytesWritten, false);
|
{
|
||||||
|
if(bytesWritten)
|
||||||
|
*bytesWritten = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8> copy((const uint8*)buffer, (const uint8*)buffer + size);
|
||||||
|
|
||||||
|
auto start = address;
|
||||||
|
auto end = start + size;
|
||||||
|
|
||||||
|
//find overlapping software breakpoints and preserve their 0xCC bytes in the copy (so write doesn't remove breakpoints)
|
||||||
|
//as well as track what oldbytes values need updating after successful write
|
||||||
|
std::vector<std::tuple<uint8*, uint8, ptr>> pendingUpdates; //tuple: (pointer to oldbyte, new value, offset from start for partial write handling)
|
||||||
|
|
||||||
|
for(auto & breakpoint : breakpoints)
|
||||||
|
{
|
||||||
|
if(breakpoint.first.first != BreakpointType::Software)
|
||||||
|
continue;
|
||||||
|
auto & info = breakpoint.second;
|
||||||
|
auto curAddress = info.address;
|
||||||
|
for(ptr j = 0; j < info.internal.software.size; j++)
|
||||||
|
{
|
||||||
|
if(curAddress + j >= start && curAddress + j < end)
|
||||||
|
{
|
||||||
|
auto offset = curAddress + j - start;
|
||||||
|
pendingUpdates.emplace_back(&info.internal.software.oldbytes[j], copy[offset], offset);
|
||||||
|
copy[offset] = info.internal.software.newbytes[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//write to memory (breakpoint bytes are preserved in the copy)
|
||||||
|
ptr written = 0;
|
||||||
|
if(!MemWriteUnsafe(address, copy.data(), size, &written))
|
||||||
|
{
|
||||||
|
if(bytesWritten)
|
||||||
|
*bytesWritten = written;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//apply oldbytes updates only for bytes that were actually written
|
||||||
|
for(const auto & update : pendingUpdates)
|
||||||
|
{
|
||||||
|
if(std::get<2>(update) < written)
|
||||||
|
*std::get<0>(update) = std::get<1>(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bytesWritten)
|
||||||
|
*bytesWritten = written;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Process::MemIsValidPtr(ptr address) const
|
bool Process::MemIsValidPtr(ptr address) const
|
||||||
|
|
|
||||||
|
|
@ -57,9 +57,10 @@ namespace GleeBug
|
||||||
case ZYDIS_MNEMONIC_SCASD:
|
case ZYDIS_MNEMONIC_SCASD:
|
||||||
case ZYDIS_MNEMONIC_SCASQ:
|
case ZYDIS_MNEMONIC_SCASQ:
|
||||||
return (info.attributes & (ZYDIS_ATTRIB_HAS_REP | ZYDIS_ATTRIB_HAS_REPZ | ZYDIS_ATTRIB_HAS_REPNZ)) != 0;
|
return (info.attributes & (ZYDIS_ATTRIB_HAS_REP | ZYDIS_ATTRIB_HAS_REPZ | ZYDIS_ATTRIB_HAS_REPNZ)) != 0;
|
||||||
}
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Process::StepOver(const StepCallback & cbStep)
|
void Process::StepOver(const StepCallback & cbStep)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ namespace GleeBug
|
||||||
MemoryBreakpointSet memoryBreakpointRanges;
|
MemoryBreakpointSet memoryBreakpointRanges;
|
||||||
MemoryBreakpointMap memoryBreakpointPages;
|
MemoryBreakpointMap memoryBreakpointPages;
|
||||||
|
|
||||||
|
std::unordered_set<ptr> recentlyDeletedSwbp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Constructor.
|
\brief Constructor.
|
||||||
\param hProcess Process handle.
|
\param hProcess Process handle.
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ namespace GleeBug
|
||||||
SIZE_4 = 3 //11
|
SIZE_4 = 3 //11
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(1)
|
|
||||||
struct DR7
|
struct DR7
|
||||||
{
|
{
|
||||||
BYTE DR7_MODE[HWBP_COUNT];
|
BYTE DR7_MODE[HWBP_COUNT];
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,9 @@ namespace GleeBug
|
||||||
|
|
||||||
auto creationFlags = DEBUG_PROCESS;
|
auto creationFlags = DEBUG_PROCESS;
|
||||||
creationFlags |= DEBUG_ONLY_THIS_PROCESS; // TODO: support child process debugging
|
creationFlags |= DEBUG_ONLY_THIS_PROCESS; // TODO: support child process debugging
|
||||||
if(newConsole)
|
if(mNoConsoleWindow)
|
||||||
|
creationFlags |= CREATE_NO_WINDOW;
|
||||||
|
else if(newConsole)
|
||||||
creationFlags |= CREATE_NEW_CONSOLE;
|
creationFlags |= CREATE_NEW_CONSOLE;
|
||||||
if(startSuspended)
|
if(startSuspended)
|
||||||
creationFlags |= CREATE_SUSPENDED;
|
creationFlags |= CREATE_SUSPENDED;
|
||||||
|
|
|
||||||
|
|
@ -294,6 +294,7 @@ namespace GleeBug
|
||||||
bool mAttachedToProcess = false;
|
bool mAttachedToProcess = false;
|
||||||
bool mSafeStep = true;
|
bool mSafeStep = true;
|
||||||
bool mDisableAslr = false;
|
bool mDisableAslr = false;
|
||||||
|
bool mNoConsoleWindow = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief The current process (can be null in some cases).
|
\brief The current process (can be null in some cases).
|
||||||
|
|
|
||||||
|
|
@ -218,8 +218,8 @@ namespace GleeBug
|
||||||
{
|
{
|
||||||
uint16 index;
|
uint16 index;
|
||||||
IMAGE_SECTION_HEADER header; //by value to prevent pointer invalidation
|
IMAGE_SECTION_HEADER header; //by value to prevent pointer invalidation
|
||||||
Region<uint8> beforeData;
|
Region<uint8> beforeData{};
|
||||||
Region<uint8> data;
|
Region<uint8> data{};
|
||||||
};
|
};
|
||||||
|
|
||||||
//sort sections on raw address to prevent read errors and have a contiguous buffer
|
//sort sections on raw address to prevent read errors and have a contiguous buffer
|
||||||
|
|
@ -292,7 +292,7 @@ namespace GleeBug
|
||||||
//offset -> section index
|
//offset -> section index
|
||||||
auto offset = section.GetHeader().PointerToRawData;
|
auto offset = section.GetHeader().PointerToRawData;
|
||||||
//bigSoRD.exe: if raw size is bigger than virtual size, then virtual size is taken.
|
//bigSoRD.exe: if raw size is bigger than virtual size, then virtual size is taken.
|
||||||
auto rsize = min(section.GetHeader().SizeOfRawData, section.GetHeader().Misc.VirtualSize);
|
auto rsize = std::min(section.GetHeader().SizeOfRawData, section.GetHeader().Misc.VirtualSize);
|
||||||
if(!rsize) //65535sects.exe
|
if(!rsize) //65535sects.exe
|
||||||
continue;
|
continue;
|
||||||
mOffsetSectionMap.insert({ Range(offset, offset + rsize - 1), section.GetIndex() });
|
mOffsetSectionMap.insert({ Range(offset, offset + rsize - 1), section.GetIndex() });
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace GleeBug
|
||||||
/**
|
/**
|
||||||
\brief Default constructor (constructs an invalid region).
|
\brief Default constructor (constructs an invalid region).
|
||||||
*/
|
*/
|
||||||
explicit Region()
|
Region()
|
||||||
: Region(nullptr, INVALID_VALUE, INVALID_VALUE)
|
: Region(nullptr, INVALID_VALUE, INVALID_VALUE)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2626
GleeBug/ntdll.h
2626
GleeBug/ntdll.h
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
||||||
#include <GleeBug/Debugger.h>
|
#include <GleeBug/Debugger.h>
|
||||||
#include <GleeBug/Static.Pe.h>
|
#include <GleeBug/Static.Pe.h>
|
||||||
#include <GleeBug/Static.Bufferfile.h>
|
#include <GleeBug/Static.BufferFile.h>
|
||||||
#include <GleeBug/Debugger.Thread.Registers.h>
|
#include <GleeBug/Debugger.Thread.Registers.h>
|
||||||
#include <GleeBug/stringutils.h>
|
#include <GleeBug/stringutils.h>
|
||||||
#include "TitanEngine.h"
|
#include "TitanEngine.h"
|
||||||
|
|
@ -199,6 +199,9 @@ public:
|
||||||
case UE_ENGINE_DISABLE_ASLR:
|
case UE_ENGINE_DISABLE_ASLR:
|
||||||
mDisableAslr = VariableSet;
|
mDisableAslr = VariableSet;
|
||||||
break;
|
break;
|
||||||
|
case UE_ENGINE_NO_CONSOLE_WINDOW:
|
||||||
|
mNoConsoleWindow = VariableSet;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -821,6 +824,10 @@ public:
|
||||||
{
|
{
|
||||||
if(!mProcess)
|
if(!mProcess)
|
||||||
return false;
|
return false;
|
||||||
|
//convert from UE_DRx (11-14) to slot index (0-3)
|
||||||
|
auto slot = (HardwareSlot)(IndexOfRegister - UE_DR0);
|
||||||
|
if((DWORD)slot > 3)
|
||||||
|
return false;
|
||||||
auto running = mIsRunning;
|
auto running = mIsRunning;
|
||||||
if(running)
|
if(running)
|
||||||
{
|
{
|
||||||
|
|
@ -828,7 +835,7 @@ public:
|
||||||
thread.second->Suspend();
|
thread.second->Suspend();
|
||||||
}
|
}
|
||||||
if(!mProcess->SetHardwareBreakpoint(bpxAddress,
|
if(!mProcess->SetHardwareBreakpoint(bpxAddress,
|
||||||
(HardwareSlot)IndexOfRegister, [bpxCallBack](const BreakpointInfo & info)
|
slot, [bpxCallBack](const BreakpointInfo & info)
|
||||||
{
|
{
|
||||||
(HWBPCALLBACK(bpxCallBack))((const void*)info.address);
|
(HWBPCALLBACK(bpxCallBack))((const void*)info.address);
|
||||||
}, hwtypeFromTitan(bpxType), hwsizeFromTitan(bpxSize)))
|
}, hwtypeFromTitan(bpxType), hwsizeFromTitan(bpxSize)))
|
||||||
|
|
@ -843,9 +850,11 @@ public:
|
||||||
|
|
||||||
bool DeleteHardwareBreakPoint(DWORD IndexOfRegister)
|
bool DeleteHardwareBreakPoint(DWORD IndexOfRegister)
|
||||||
{
|
{
|
||||||
if(!mProcess || IndexOfRegister > 3)
|
//convert from UE_DRx (11-14) to slot index (0-3)
|
||||||
|
auto slot = IndexOfRegister - UE_DR0;
|
||||||
|
if(!mProcess || slot > 3)
|
||||||
return false;
|
return false;
|
||||||
auto address = mProcess->hardwareBreakpoints[IndexOfRegister].address;
|
auto address = mProcess->hardwareBreakpoints[slot].address;
|
||||||
return mProcess->DeleteHardwareBreakpoint(address);
|
return mProcess->DeleteHardwareBreakpoint(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -856,7 +865,7 @@ public:
|
||||||
HardwareSlot slot;
|
HardwareSlot slot;
|
||||||
bool result = mProcess->GetFreeHardwareBreakpointSlot(slot);
|
bool result = mProcess->GetFreeHardwareBreakpointSlot(slot);
|
||||||
if(result)
|
if(result)
|
||||||
*RegisterIndex = (DWORD)slot;
|
*RegisterIndex = UE_DR0 + (DWORD)slot; //UE_DR0-UE_DR3 (11-14), not 0-3
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1054,10 +1063,10 @@ private: //functions
|
||||||
THREAD_BASIC_INFORMATION tbi;
|
THREAD_BASIC_INFORMATION tbi;
|
||||||
if(!getThreadInfo(hThread, tbi))
|
if(!getThreadInfo(hThread, tbi))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
auto foundP = mProcesses.find(uint32(tbi.ClientId.UniqueProcess));
|
auto foundP = mProcesses.find(uint32(uintptr_t(tbi.ClientId.UniqueProcess)));
|
||||||
if(foundP == mProcesses.end())
|
if(foundP == mProcesses.end())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
auto foundT = foundP->second->threads.find(uint32(tbi.ClientId.UniqueThread));
|
auto foundT = foundP->second->threads.find(uint32(uintptr_t(tbi.ClientId.UniqueThread)));
|
||||||
if(foundT == foundP->second->threads.end())
|
if(foundT == foundP->second->threads.end())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return foundT->second.get();
|
return foundT->second.get();
|
||||||
|
|
@ -1271,7 +1280,7 @@ private: //functions
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool EngineExtractResource(char* szResourceName, wchar_t* szExtractedFileName)
|
bool EngineExtractResource(const char* szResourceName, const wchar_t* szExtractedFileName)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
HRSRC hResource = FindResourceA(engineHandle, (LPCSTR)szResourceName, "BINARY");
|
HRSRC hResource = FindResourceA(engineHandle, (LPCSTR)szResourceName, "BINARY");
|
||||||
|
|
|
||||||
|
|
@ -148,10 +148,6 @@ static bool SetAVXContext(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titconte
|
||||||
if(InitXState() == false)
|
if(InitXState() == false)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
DWORD64 FeatureMask = _GetEnabledXStateFeatures();
|
|
||||||
if((FeatureMask & XSTATE_MASK_AVX) == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
DWORD ContextSize = 0;
|
DWORD ContextSize = 0;
|
||||||
BOOL Success = _InitializeContext(NULL,
|
BOOL Success = _InitializeContext(NULL,
|
||||||
CONTEXT_ALL | CONTEXT_XSTATE,
|
CONTEXT_ALL | CONTEXT_XSTATE,
|
||||||
|
|
@ -176,11 +172,15 @@ static bool SetAVXContext(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titconte
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(_SetXStateFeaturesMask(Context, XSTATE_MASK_AVX) == FALSE)
|
if(_SetXStateFeaturesMask(Context, XSTATE_MASK_AVX) == FALSE)
|
||||||
|
{
|
||||||
|
if(_SetXStateFeaturesMask(Context, XSTATE_MASK_LEGACY_SSE) == FALSE)
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(GetThreadContext(hActiveThread, Context) == FALSE)
|
if(GetThreadContext(hActiveThread, Context) == FALSE)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
DWORD64 FeatureMask = 0;
|
||||||
if(_GetXStateFeaturesMask(Context, &FeatureMask) == FALSE)
|
if(_GetXStateFeaturesMask(Context, &FeatureMask) == FALSE)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -209,10 +209,6 @@ static bool GetAVXContext(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titconte
|
||||||
if(InitXState() == false)
|
if(InitXState() == false)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
DWORD64 FeatureMask = _GetEnabledXStateFeatures();
|
|
||||||
if((FeatureMask & XSTATE_MASK_AVX) == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
DWORD ContextSize = 0;
|
DWORD ContextSize = 0;
|
||||||
BOOL Success = _InitializeContext(NULL,
|
BOOL Success = _InitializeContext(NULL,
|
||||||
CONTEXT_ALL | CONTEXT_XSTATE,
|
CONTEXT_ALL | CONTEXT_XSTATE,
|
||||||
|
|
@ -237,11 +233,15 @@ static bool GetAVXContext(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titconte
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(_SetXStateFeaturesMask(Context, XSTATE_MASK_AVX) == FALSE)
|
if(_SetXStateFeaturesMask(Context, XSTATE_MASK_AVX) == FALSE)
|
||||||
|
{
|
||||||
|
if(_SetXStateFeaturesMask(Context, XSTATE_MASK_LEGACY_SSE) == FALSE)
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(GetThreadContext(hActiveThread, Context) == FALSE)
|
if(GetThreadContext(hActiveThread, Context) == FALSE)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
DWORD64 FeatureMask = 0;
|
||||||
if(_GetXStateFeaturesMask(Context, &FeatureMask) == FALSE)
|
if(_GetXStateFeaturesMask(Context, &FeatureMask) == FALSE)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -342,7 +342,11 @@ bool _SetFullContextDataEx(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titcont
|
||||||
DBGContext.FloatSave.ErrorOffset = titcontext->x87fpu.ErrorOffset;
|
DBGContext.FloatSave.ErrorOffset = titcontext->x87fpu.ErrorOffset;
|
||||||
DBGContext.FloatSave.DataSelector = titcontext->x87fpu.DataSelector;
|
DBGContext.FloatSave.DataSelector = titcontext->x87fpu.DataSelector;
|
||||||
DBGContext.FloatSave.DataOffset = titcontext->x87fpu.DataOffset;
|
DBGContext.FloatSave.DataOffset = titcontext->x87fpu.DataOffset;
|
||||||
|
#ifdef NTDDI_WIN8
|
||||||
|
DBGContext.FloatSave.Spare0 = titcontext->x87fpu.Cr0NpxState;
|
||||||
|
#else
|
||||||
DBGContext.FloatSave.Cr0NpxState = titcontext->x87fpu.Cr0NpxState;
|
DBGContext.FloatSave.Cr0NpxState = titcontext->x87fpu.Cr0NpxState;
|
||||||
|
#endif
|
||||||
|
|
||||||
memcpy(DBGContext.FloatSave.RegisterArea, titcontext->RegisterArea, 80);
|
memcpy(DBGContext.FloatSave.RegisterArea, titcontext->RegisterArea, 80);
|
||||||
|
|
||||||
|
|
@ -440,7 +444,11 @@ bool _GetFullContextDataEx(HANDLE hActiveThread, TITAN_ENGINE_CONTEXT_t* titcont
|
||||||
titcontext->x87fpu.ErrorOffset = DBGContext.FloatSave.ErrorOffset;
|
titcontext->x87fpu.ErrorOffset = DBGContext.FloatSave.ErrorOffset;
|
||||||
titcontext->x87fpu.DataSelector = DBGContext.FloatSave.DataSelector;
|
titcontext->x87fpu.DataSelector = DBGContext.FloatSave.DataSelector;
|
||||||
titcontext->x87fpu.DataOffset = DBGContext.FloatSave.DataOffset;
|
titcontext->x87fpu.DataOffset = DBGContext.FloatSave.DataOffset;
|
||||||
|
#ifdef NTDDI_WIN8
|
||||||
|
titcontext->x87fpu.Cr0NpxState = DBGContext.FloatSave.Spare0;
|
||||||
|
#else
|
||||||
titcontext->x87fpu.Cr0NpxState = DBGContext.FloatSave.Cr0NpxState;
|
titcontext->x87fpu.Cr0NpxState = DBGContext.FloatSave.Cr0NpxState;
|
||||||
|
#endif
|
||||||
|
|
||||||
memcpy(titcontext->RegisterArea, DBGContext.FloatSave.RegisterArea, 80);
|
memcpy(titcontext->RegisterArea, DBGContext.FloatSave.RegisterArea, 80);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
#include "ntdll.h"
|
|
||||||
#include "Emulator.h"
|
#include "Emulator.h"
|
||||||
|
|
||||||
Emulator emu;
|
Emulator emu;
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,22 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#undef WIN32_NO_STATUS
|
#undef WIN32_NO_STATUS
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4005) // ntstatus.h can collide with winnt.h in unity builds
|
||||||
|
#endif
|
||||||
#include <ntstatus.h>
|
#include <ntstatus.h>
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
|
|
||||||
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
|
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
|
||||||
#define NT_ERROR(Status) ((((ULONG)(Status)) >> 30) == 3)
|
#define NT_ERROR(Status) ((((ULONG)(Status)) >> 30) == 3)
|
||||||
|
|
||||||
|
#ifndef FASTCALL
|
||||||
#define FASTCALL __fastcall
|
#define FASTCALL __fastcall
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef _Reserved_
|
#ifndef _Reserved_
|
||||||
#define _Reserved_
|
#define _Reserved_
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
[project]
|
||||||
|
name = "GleeBug"
|
||||||
|
|
||||||
|
[options]
|
||||||
|
GLEEBUG_RESOURCES = true
|
||||||
|
|
||||||
|
[target.GleeBug]
|
||||||
|
type = "static"
|
||||||
|
sources = ["GleeBug/Zydis/*.c", "GleeBug/*.cpp"]
|
||||||
|
headers = ["GleeBug/Zydis/*.h", "GleeBug/*.h"]
|
||||||
|
include-directories = ["."]
|
||||||
|
compile-features = ["cxx_std_23"]
|
||||||
|
compile-definitions = ["NOMINMAX"]
|
||||||
|
x64.link-libraries = ["GleeBug/ntdll_x64.lib"]
|
||||||
|
x32.link-libraries = ["GleeBug/ntdll_x86.lib"]
|
||||||
|
|
||||||
|
[target.MyDebugger]
|
||||||
|
condition = "root"
|
||||||
|
type = "executable"
|
||||||
|
sources = ["MyDebugger/*.cpp"]
|
||||||
|
headers = ["MyDebugger/*.h"]
|
||||||
|
link-libraries = ["::GleeBug"]
|
||||||
|
|
||||||
|
[target.GleeBugStaticEngine]
|
||||||
|
type = "shared"
|
||||||
|
sources = ["StaticEngine/*.cpp"]
|
||||||
|
headers = ["StaticEngine/*.h"]
|
||||||
|
private-link-libraries = ["::GleeBug"]
|
||||||
|
|
||||||
|
[target.GleeBugTitanEngine]
|
||||||
|
type = "shared"
|
||||||
|
sources = ["TitanEngineEmulator/*.cpp"]
|
||||||
|
GLEEBUG_RESOURCES.sources = ["TitanEngineEmulator/TitanEngine.rc"]
|
||||||
|
headers = ["TitanEngineEmulator/*.h"]
|
||||||
|
private-link-libraries = ["::GleeBug"]
|
||||||
|
include-after = ["output-folders.cmake"]
|
||||||
|
|
@ -0,0 +1,260 @@
|
||||||
|
include_guard()
|
||||||
|
|
||||||
|
# Change these defaults to point to your infrastructure if desired
|
||||||
|
set(CMKR_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE)
|
||||||
|
set(CMKR_TAG "v0.2.46" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
|
||||||
|
set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE)
|
||||||
|
|
||||||
|
# To bootstrap/generate a cmkr project: cmake -P cmkr.cmake
|
||||||
|
if(CMAKE_SCRIPT_MODE_FILE)
|
||||||
|
set(CMAKE_BINARY_DIR "${CMAKE_BINARY_DIR}/build")
|
||||||
|
set(CMAKE_CURRENT_BINARY_DIR "${CMAKE_BINARY_DIR}")
|
||||||
|
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set these from the command line to customize for development/debugging purposes
|
||||||
|
set(CMKR_EXECUTABLE "" CACHE FILEPATH "cmkr executable")
|
||||||
|
set(CMKR_SKIP_GENERATION OFF CACHE BOOL "skip automatic cmkr generation")
|
||||||
|
set(CMKR_BUILD_TYPE "Debug" CACHE STRING "cmkr build configuration")
|
||||||
|
mark_as_advanced(CMKR_REPO CMKR_TAG CMKR_COMMIT_HASH CMKR_EXECUTABLE CMKR_SKIP_GENERATION CMKR_BUILD_TYPE)
|
||||||
|
|
||||||
|
# Disable cmkr if generation is disabled
|
||||||
|
if(DEFINED ENV{CI} OR CMKR_SKIP_GENERATION OR CMKR_BUILD_SKIP_GENERATION)
|
||||||
|
message(STATUS "[cmkr] Skipping automatic cmkr generation")
|
||||||
|
unset(CMKR_BUILD_SKIP_GENERATION CACHE)
|
||||||
|
macro(cmkr)
|
||||||
|
endmacro()
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Disable cmkr if no cmake.toml file is found
|
||||||
|
if(NOT CMAKE_SCRIPT_MODE_FILE AND NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml")
|
||||||
|
message(AUTHOR_WARNING "[cmkr] Not found: ${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml")
|
||||||
|
macro(cmkr)
|
||||||
|
endmacro()
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Convert a Windows native path to CMake path
|
||||||
|
if(CMKR_EXECUTABLE MATCHES "\\\\")
|
||||||
|
string(REPLACE "\\" "/" CMKR_EXECUTABLE_CMAKE "${CMKR_EXECUTABLE}")
|
||||||
|
set(CMKR_EXECUTABLE "${CMKR_EXECUTABLE_CMAKE}" CACHE FILEPATH "" FORCE)
|
||||||
|
unset(CMKR_EXECUTABLE_CMAKE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Helper macro to execute a process (COMMAND_ERROR_IS_FATAL ANY is 3.19 and higher)
|
||||||
|
function(cmkr_exec)
|
||||||
|
execute_process(COMMAND ${ARGV} RESULT_VARIABLE CMKR_EXEC_RESULT)
|
||||||
|
if(NOT CMKR_EXEC_RESULT EQUAL 0)
|
||||||
|
message(FATAL_ERROR "cmkr_exec(${ARGV}) failed (exit code ${CMKR_EXEC_RESULT})")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Windows-specific hack (CMAKE_EXECUTABLE_PREFIX is not set at the moment)
|
||||||
|
if(WIN32)
|
||||||
|
set(CMKR_EXECUTABLE_NAME "cmkr.exe")
|
||||||
|
else()
|
||||||
|
set(CMKR_EXECUTABLE_NAME "cmkr")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Use cached cmkr if found
|
||||||
|
if(DEFINED ENV{CMKR_CACHE})
|
||||||
|
set(CMKR_DIRECTORY_PREFIX "$ENV{CMKR_CACHE}")
|
||||||
|
string(REPLACE "\\" "/" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
|
||||||
|
if(CMKR_DIRECTORY_PREFIX MATCHES "^~")
|
||||||
|
if(WIN32)
|
||||||
|
string(REGEX REPLACE "^~" "$ENV{USERPROFILE}" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
|
||||||
|
elseif(UNIX)
|
||||||
|
string(REGEX REPLACE "^~" "$ENV{HOME}" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(NOT CMKR_DIRECTORY_PREFIX MATCHES "\\/$")
|
||||||
|
set(CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}/")
|
||||||
|
endif()
|
||||||
|
# Build in release mode for the cache
|
||||||
|
set(CMKR_BUILD_TYPE "Release")
|
||||||
|
else()
|
||||||
|
set(CMKR_DIRECTORY_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/_cmkr_")
|
||||||
|
endif()
|
||||||
|
set(CMKR_DIRECTORY "${CMKR_DIRECTORY_PREFIX}${CMKR_TAG}")
|
||||||
|
set(CMKR_CACHED_EXECUTABLE "${CMKR_DIRECTORY}/bin/${CMKR_EXECUTABLE_NAME}")
|
||||||
|
|
||||||
|
# Helper function to check if a string starts with a prefix
|
||||||
|
# Cannot use MATCHES, see: https://github.com/build-cpp/cmkr/issues/61
|
||||||
|
function(cmkr_startswith str prefix result)
|
||||||
|
string(LENGTH "${prefix}" prefix_length)
|
||||||
|
string(LENGTH "${str}" str_length)
|
||||||
|
if(prefix_length LESS_EQUAL str_length)
|
||||||
|
string(SUBSTRING "${str}" 0 ${prefix_length} str_prefix)
|
||||||
|
if(prefix STREQUAL str_prefix)
|
||||||
|
set("${result}" ON PARENT_SCOPE)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
set("${result}" OFF PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Handle upgrading logic
|
||||||
|
if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE)
|
||||||
|
cmkr_startswith("${CMKR_EXECUTABLE}" "${CMAKE_CURRENT_BINARY_DIR}/_cmkr" CMKR_STARTSWITH_BUILD)
|
||||||
|
cmkr_startswith("${CMKR_EXECUTABLE}" "${CMKR_DIRECTORY_PREFIX}" CMKR_STARTSWITH_CACHE)
|
||||||
|
if(CMKR_STARTSWITH_BUILD)
|
||||||
|
if(DEFINED ENV{CMKR_CACHE})
|
||||||
|
message(AUTHOR_WARNING "[cmkr] Switching to cached cmkr: '${CMKR_CACHED_EXECUTABLE}'")
|
||||||
|
if(EXISTS "${CMKR_CACHED_EXECUTABLE}")
|
||||||
|
set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE)
|
||||||
|
else()
|
||||||
|
unset(CMKR_EXECUTABLE CACHE)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(AUTHOR_WARNING "[cmkr] Upgrading '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
|
||||||
|
unset(CMKR_EXECUTABLE CACHE)
|
||||||
|
endif()
|
||||||
|
elseif(DEFINED ENV{CMKR_CACHE} AND CMKR_STARTSWITH_CACHE)
|
||||||
|
message(AUTHOR_WARNING "[cmkr] Upgrading cached '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
|
||||||
|
unset(CMKR_EXECUTABLE CACHE)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMKR_EXECUTABLE AND EXISTS "${CMKR_EXECUTABLE}")
|
||||||
|
message(VERBOSE "[cmkr] Found cmkr: '${CMKR_EXECUTABLE}'")
|
||||||
|
elseif(CMKR_EXECUTABLE AND NOT CMKR_EXECUTABLE STREQUAL CMKR_CACHED_EXECUTABLE)
|
||||||
|
message(FATAL_ERROR "[cmkr] '${CMKR_EXECUTABLE}' not found")
|
||||||
|
elseif(NOT CMKR_EXECUTABLE AND EXISTS "${CMKR_CACHED_EXECUTABLE}")
|
||||||
|
set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE)
|
||||||
|
message(STATUS "[cmkr] Found cached cmkr: '${CMKR_EXECUTABLE}'")
|
||||||
|
else()
|
||||||
|
set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE)
|
||||||
|
message(VERBOSE "[cmkr] Bootstrapping '${CMKR_EXECUTABLE}'")
|
||||||
|
|
||||||
|
message(STATUS "[cmkr] Fetching cmkr...")
|
||||||
|
if(EXISTS "${CMKR_DIRECTORY}")
|
||||||
|
cmkr_exec("${CMAKE_COMMAND}" -E rm -rf "${CMKR_DIRECTORY}")
|
||||||
|
endif()
|
||||||
|
find_package(Git QUIET REQUIRED)
|
||||||
|
cmkr_exec("${GIT_EXECUTABLE}"
|
||||||
|
clone
|
||||||
|
--config advice.detachedHead=false
|
||||||
|
--branch ${CMKR_TAG}
|
||||||
|
--depth 1
|
||||||
|
${CMKR_REPO}
|
||||||
|
"${CMKR_DIRECTORY}"
|
||||||
|
)
|
||||||
|
if(CMKR_COMMIT_HASH)
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" checkout -q "${CMKR_COMMIT_HASH}"
|
||||||
|
RESULT_VARIABLE CMKR_EXEC_RESULT
|
||||||
|
WORKING_DIRECTORY "${CMKR_DIRECTORY}"
|
||||||
|
)
|
||||||
|
if(NOT CMKR_EXEC_RESULT EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Tag '${CMKR_TAG}' hash is not '${CMKR_COMMIT_HASH}'")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
message(STATUS "[cmkr] Building cmkr (using system compiler)...")
|
||||||
|
cmkr_exec("${CMAKE_COMMAND}"
|
||||||
|
--no-warn-unused-cli
|
||||||
|
"${CMKR_DIRECTORY}"
|
||||||
|
"-B${CMKR_DIRECTORY}/build"
|
||||||
|
"-DCMAKE_BUILD_TYPE=${CMKR_BUILD_TYPE}"
|
||||||
|
"-DCMAKE_UNITY_BUILD=ON"
|
||||||
|
"-DCMAKE_INSTALL_PREFIX=${CMKR_DIRECTORY}"
|
||||||
|
"-DCMKR_GENERATE_DOCUMENTATION=OFF"
|
||||||
|
)
|
||||||
|
cmkr_exec("${CMAKE_COMMAND}"
|
||||||
|
--build "${CMKR_DIRECTORY}/build"
|
||||||
|
--config "${CMKR_BUILD_TYPE}"
|
||||||
|
--parallel
|
||||||
|
)
|
||||||
|
cmkr_exec("${CMAKE_COMMAND}"
|
||||||
|
--install "${CMKR_DIRECTORY}/build"
|
||||||
|
--config "${CMKR_BUILD_TYPE}"
|
||||||
|
--prefix "${CMKR_DIRECTORY}"
|
||||||
|
--component cmkr
|
||||||
|
)
|
||||||
|
if(NOT EXISTS ${CMKR_EXECUTABLE})
|
||||||
|
message(FATAL_ERROR "[cmkr] Failed to bootstrap '${CMKR_EXECUTABLE}'")
|
||||||
|
endif()
|
||||||
|
cmkr_exec("${CMKR_EXECUTABLE}" version)
|
||||||
|
message(STATUS "[cmkr] Bootstrapped ${CMKR_EXECUTABLE}")
|
||||||
|
endif()
|
||||||
|
execute_process(COMMAND "${CMKR_EXECUTABLE}" version
|
||||||
|
RESULT_VARIABLE CMKR_EXEC_RESULT
|
||||||
|
)
|
||||||
|
if(NOT CMKR_EXEC_RESULT EQUAL 0)
|
||||||
|
message(FATAL_ERROR "[cmkr] Failed to get version, try clearing the cache and rebuilding")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Use cmkr.cmake as a script
|
||||||
|
if(CMAKE_SCRIPT_MODE_FILE)
|
||||||
|
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake.toml")
|
||||||
|
execute_process(COMMAND "${CMKR_EXECUTABLE}" init
|
||||||
|
RESULT_VARIABLE CMKR_EXEC_RESULT
|
||||||
|
)
|
||||||
|
if(NOT CMKR_EXEC_RESULT EQUAL 0)
|
||||||
|
message(FATAL_ERROR "[cmkr] Failed to bootstrap cmkr project. Please report an issue: https://github.com/build-cpp/cmkr/issues/new")
|
||||||
|
else()
|
||||||
|
message(STATUS "[cmkr] Modify cmake.toml and then configure using: cmake -B build")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
execute_process(COMMAND "${CMKR_EXECUTABLE}" gen
|
||||||
|
RESULT_VARIABLE CMKR_EXEC_RESULT
|
||||||
|
)
|
||||||
|
if(NOT CMKR_EXEC_RESULT EQUAL 0)
|
||||||
|
message(FATAL_ERROR "[cmkr] Failed to generate project.")
|
||||||
|
else()
|
||||||
|
message(STATUS "[cmkr] Configure using: cmake -B build")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# This is the macro that contains black magic
|
||||||
|
macro(cmkr)
|
||||||
|
# When this macro is called from the generated file, fake some internal CMake variables
|
||||||
|
get_source_file_property(CMKR_CURRENT_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}" CMKR_CURRENT_LIST_FILE)
|
||||||
|
if(CMKR_CURRENT_LIST_FILE)
|
||||||
|
set(CMAKE_CURRENT_LIST_FILE "${CMKR_CURRENT_LIST_FILE}")
|
||||||
|
get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# File-based include guard (include_guard is not documented to work)
|
||||||
|
get_source_file_property(CMKR_INCLUDE_GUARD "${CMAKE_CURRENT_LIST_FILE}" CMKR_INCLUDE_GUARD)
|
||||||
|
if(NOT CMKR_INCLUDE_GUARD)
|
||||||
|
set_source_files_properties("${CMAKE_CURRENT_LIST_FILE}" PROPERTIES CMKR_INCLUDE_GUARD TRUE)
|
||||||
|
|
||||||
|
file(SHA256 "${CMAKE_CURRENT_LIST_FILE}" CMKR_LIST_FILE_SHA256_PRE)
|
||||||
|
|
||||||
|
# Generate CMakeLists.txt
|
||||||
|
cmkr_exec("${CMKR_EXECUTABLE}" gen
|
||||||
|
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
)
|
||||||
|
|
||||||
|
file(SHA256 "${CMAKE_CURRENT_LIST_FILE}" CMKR_LIST_FILE_SHA256_POST)
|
||||||
|
|
||||||
|
# Delete the temporary file if it was left for some reason
|
||||||
|
set(CMKR_TEMP_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CMakerLists.txt")
|
||||||
|
if(EXISTS "${CMKR_TEMP_FILE}")
|
||||||
|
file(REMOVE "${CMKR_TEMP_FILE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT CMKR_LIST_FILE_SHA256_PRE STREQUAL CMKR_LIST_FILE_SHA256_POST)
|
||||||
|
# Copy the now-generated CMakeLists.txt to CMakerLists.txt
|
||||||
|
# This is done because you cannot include() a file you are currently in
|
||||||
|
configure_file(CMakeLists.txt "${CMKR_TEMP_FILE}" COPYONLY)
|
||||||
|
|
||||||
|
# Add the macro required for the hack at the start of the cmkr macro
|
||||||
|
set_source_files_properties("${CMKR_TEMP_FILE}" PROPERTIES
|
||||||
|
CMKR_CURRENT_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 'Execute' the newly-generated CMakeLists.txt
|
||||||
|
include("${CMKR_TEMP_FILE}")
|
||||||
|
|
||||||
|
# Delete the generated file
|
||||||
|
file(REMOVE "${CMKR_TEMP_FILE}")
|
||||||
|
|
||||||
|
# Do not execute the rest of the original CMakeLists.txt
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
# Resume executing the unmodified CMakeLists.txt
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
function(set_nested_output_directory TARGET SUBDIR)
|
||||||
|
if(CMAKE_RUNTIME_OUTPUT_DIRECTORY)
|
||||||
|
set_target_properties(${TARGET} PROPERTIES
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${SUBDIR}"
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${SUBDIR}"
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${SUBDIR}"
|
||||||
|
)
|
||||||
|
elseif(CMAKE_CONFIGURATION_TYPES)
|
||||||
|
foreach(CONFIG ${CMAKE_CONFIGURATION_TYPES})
|
||||||
|
string(TOUPPER ${CONFIG} CONFIG_UPPER)
|
||||||
|
set_target_properties(${TARGET} PROPERTIES
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY_${CONFIG_UPPER}
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/${SUBDIR}"
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY_${CONFIG_UPPER}
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/${SUBDIR}"
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY_${CONFIG_UPPER}
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/${SUBDIR}"
|
||||||
|
)
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
set_target_properties(${TARGET} PROPERTIES
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${SUBDIR}"
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${SUBDIR}"
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${SUBDIR}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
set_nested_output_directory(GleeBugTitanEngine "GleeBug")
|
||||||
|
set_target_properties(GleeBugTitanEngine PROPERTIES
|
||||||
|
OUTPUT_NAME "TitanEngine"
|
||||||
|
)
|
||||||
|
|
||||||
|
set_nested_output_directory(GleeBugStaticEngine "StaticEngine")
|
||||||
|
set_target_properties(GleeBugStaticEngine PROPERTIES
|
||||||
|
OUTPUT_NAME "TitanEngine"
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue