Skip to content

Commit

Permalink
Improve d3d12 hook and logging
Browse files Browse the repository at this point in the history
Logging now displays the function's name
D3D12 above Windows 7 now hook the game render functions directly instead of creating a d3d12 device and hooking the functions
  • Loading branch information
Yamashi committed Feb 6, 2022
1 parent 7172b86 commit 481f972
Show file tree
Hide file tree
Showing 34 changed files with 504 additions and 164 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ build/*
src/CETVersion.h
package/*
vs2022/*
vsxmake*/
vsxmake*/
*.pyc
160 changes: 160 additions & 0 deletions ida/find_patterns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import traceback
from typing import Dict, List

import ida_bytes
import ida_idaapi
import ida_kernwin
import ida_nalt
import idc

ida_idaapi.require('patterns')

cached_patterns: Dict[str, List[int]] = dict()

def bin_search(bin_str: str) -> List[int]:
if not isinstance(bin_str, str):
raise ValueError('bin_str must be a string')

if bin_str in cached_patterns:
return cached_patterns[bin_str]

bin_list = bin_str.split()
image = bytearray()
mask = bytearray()

# Create the mask and convert '?' to 'CC'.
for i in range(len(bin_list)):
byte = bin_list[i]
if byte== '?':
image.append(int('CC', 16))
mask.append(0)
else:
image.append(int(byte, 16))
mask.append(1)

image = bytes(image)
mask = bytes(mask)

start = ida_nalt.get_imagebase()
end = ida_idaapi.BADADDR

addrs: List[int] = []

ea = ida_bytes.bin_search(start, end, image, mask, 0, ida_bytes.BIN_SEARCH_FORWARD)
while ea != ida_idaapi.BADADDR:
addrs.append(ea)
ea = ida_bytes.bin_search(ea + len(image), end, image, mask, 0, ida_bytes.BIN_SEARCH_FORWARD)

cached_patterns[bin_str] = addrs
return cached_patterns[bin_str]

def find_pattern(pattern: str, expected: int = 1, index: int = 0) -> int:
if not isinstance(expected, int):
raise ValueError('expected must be an integer')

if not isinstance(index, int):
raise ValueError('index must be an integer')

addrs = bin_search(pattern)
if len(addrs) != expected:
print(f'Found {len(addrs)} match(es) but {expected} match(es) were expected for pattern "{pattern}"')
return ida_idaapi.BADADDR

return addrs[index]

def find_function(pattern: str, expected: int = 1, index: int = 0) -> int:
return find_pattern(pattern, expected, index)

def find_ptr(pattern: str, expected: int = 1, index: int = 0, offset: int = 0) -> int:
addr = find_pattern(pattern, expected, index)
if addr == ida_idaapi.BADADDR:
return addr

disp = ida_bytes.get_dword(addr + offset)

# Real address is: pattern_addr + offset + displacement + size_of_displacement.
return addr + offset + disp + 4

try:
groups = patterns.get_groups()
total = sum(map(lambda g: len(g.pointers) + len(g.functions), groups))

groups.sort(key=lambda g: g.name.lower())

addr = find_ptr(pattern='4C 8D 05 ? ? ? ? 49 89 AE 80 01 00 00 8D 55 11', offset=3)
version = idc.get_strlit_contents(addr)

print(f'Finding {total} item(s)...')
with open('Addresses.hpp', 'w') as file:
file.write('#pragma once\n')
file.write('\n')
file.write('/*\n')
file.write(' * This file is generated. DO NOT modify it!\n')
file.write(' *\n')
file.write(' * Add new patterns in "patterns.py" file located in "project_root/scripts" and run "find_patterns.py".\n')
file.write(' * The new file should be located in "idb_path/Addresses.hpp".\n')
file.write(' */\n')
file.write('#include <cstdint>\n')
file.write('\n')
file.write(f'// Addresses for Cyberpunk 2077, version {version.decode()}.\n')
file.write('namespace CyberEngineTweaks::Addresses\n')
file.write('{\n')
file.write(f'constexpr uintptr_t ImageBase = 0x{ida_nalt.get_imagebase():X};\n')
file.write('\n')

for group in groups:
if group.name:
file.write(f'#pragma region {group.name}\n')

for ptr in group.pointers:
addr = find_ptr(pattern=ptr.pattern, expected=ptr.expected, index=ptr.index, offset=ptr.offset)
if addr == ida_idaapi.BADADDR:
file.write(f'#error Could not find pattern "{ptr.pattern}", expected: {ptr.expected}, index: {ptr.index}, offset: {ptr.offset}\n')
continue

if not group.name and not ptr.name:
ptr.name = f'ptr_{addr:X}'

file.write('constexpr uintptr_t ')
if group.name:
file.write(f'{group.name}')

if ptr.name:
file.write('_')

ptr.name = ptr.name.replace('::', '_')

file.write(f'{ptr.name} = 0x{addr:X} - ImageBase; ')
file.write(f'// {ptr.pattern}, expected: {ptr.expected}, index: {ptr.index}, offset: {ptr.offset}\n')

for func in group.functions:
addr = find_function(pattern=func.pattern, expected=func.expected, index=func.index)
if addr == ida_idaapi.BADADDR:
file.write(f'#error Could not find pattern "{func.pattern}", expected: {func.expected}, index: {func.index}\n')
continue

file.write('constexpr uintptr_t ')

if group.name:
file.write(f'{group.name}_')

if not func.name:
func.name = f'sub_{addr:X}'

func.name = func.name.replace('::', '_')

file.write(f'{func.name} = 0x{addr:X} - ImageBase; ')
file.write(f'// {func.pattern}, expected: {func.expected}, index: {func.index}\n')

if group.name:
file.write(f'#pragma endregion\n')

if group != groups[-1]:
file.write('\n')

file.write('} // namespace CyberEngineTweaks::Addresses\n')

print('Done!')
ida_kernwin.beep()
except:
traceback.print_exc()
36 changes: 36 additions & 0 deletions ida/patterns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from typing import List

class Item:
name: str
pattern: str
expected: int
index: int
offset: int

def __init__(self, pattern: str, name: str = '', expected: int = 1, index: int = 0, offset: int = 0) -> None:
self.name = name
self.pattern = pattern
self.expected = expected
self.index = index
self.offset = offset

class Group:
name: str
pointers: List[Item]
functions: List[Item]

def __init__(self, name: str, pointers: List[Item] = [], functions: List[Item] = []) -> None:
self.name = name
self.pointers = pointers
self.functions = functions

def get_groups() -> List[Group]:
# Add new patterns here, please try to keep the groups ordering alphabetized.
return [
Group(name='CRenderGlobal', pointers=[
Item(name='InstanceOffset', pattern='48 8B 05 ? ? ? ? 8B D1 48 8B 88 18 8A 5A 01', expected=1, offset=3)
]),
Group(name='CRenderNode_Present', functions=[
Item(name='DoInternal', pattern='48 89 5C 24 08 48 89 6C 24 18 48 89 74 24 20 57 41 56 41 57 48 83 EC 30 8B 01 41 8B F8 4C 8B 35', expected=1)
])
]
2 changes: 1 addition & 1 deletion src/Image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ void Image::Initialize()
if (version == 0)
{
for (auto c : pdb_info->Guid)
spdlog::error("{:X}", (uint32_t)c);
Log::Error("{:X}", (uint32_t)c);
}
}
}
Expand Down
17 changes: 9 additions & 8 deletions src/Options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,30 +138,31 @@ Options::Options(Paths& aPaths)

set_default_logger(CreateLogger(m_paths.CETRoot() / "cyber_engine_tweaks.log", "main"));

spdlog::info("Cyber Engine Tweaks is starting...");

Log::Info("Cyber Engine Tweaks is starting...");

GameImage.Initialize();

if (GameImage.version)
{
spdlog::info("CET version {} [{}]", CET_BUILD_COMMIT, CET_BUILD_BRANCH);
Log::Info("CET version {} [{}]", CET_BUILD_COMMIT, CET_BUILD_BRANCH);
auto [major, minor] = GameImage.GetVersion();
spdlog::info("Game version {}.{:02d}", major, minor);
spdlog::info("Root path: \"{}\"", aPaths.GameRoot().string());
spdlog::info("Cyber Engine Tweaks path: \"{}\"", aPaths.CETRoot().string());
spdlog::info("Lua scripts search path: \"{}\"", aPaths.ModsRoot().string());
Log::Info("Game version {}.{:02d}", major, minor);
Log::Info("Root path: \"{}\"", aPaths.GameRoot().string());
Log::Info("Cyber Engine Tweaks path: \"{}\"", aPaths.CETRoot().string());
Log::Info("Lua scripts search path: \"{}\"", aPaths.ModsRoot().string());

if (GameImage.GetVersion() != GameImage.GetSupportedVersion())
{
auto [smajor, sminor] = GameImage.GetSupportedVersion();
spdlog::error("Unsupported game version! Only {}.{:02d} is supported.", smajor, sminor);
Log::Error("Unsupported game version! Only {}.{:02d} is supported.", smajor, sminor);
throw std::runtime_error("Unsupported version");
}

}
else
{
spdlog::info("Unknown Game Version, update the mod");
Log::Info("Unknown Game Version, update the mod");
throw std::runtime_error("Unknown version");
}

Expand Down
4 changes: 2 additions & 2 deletions src/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ void trim(std::string& s);

spdlog::sink_ptr CreateCustomSinkST(std::function<void(const std::string&)> aSinkItHandler, std::function<void()> aFlushHandler = nullptr);
spdlog::sink_ptr CreateCustomSinkMT(std::function<void(const std::string&)> aSinkItHandler, std::function<void()> aFlushHandler = nullptr);
std::shared_ptr<spdlog::logger> CreateLogger(const std::filesystem::path& aPath, const std::string& aID, spdlog::sink_ptr aExtraSink = nullptr, const std::string& aPattern = "[%Y-%m-%d %H:%M:%S UTC%z] [%l] %v");
std::shared_ptr<spdlog::logger> CreateLogger(const std::filesystem::path& aPath, const std::string& aID, spdlog::sink_ptr aExtraSink = nullptr, const std::string& aPattern = "[%Y-%m-%d %H:%M:%S UTC%z] [%l] [%!] %v");

// deep copies sol object (doesnt take into account potential duplicates)
sol::object DeepCopySolObject(sol::object aObj, const sol::state_view& aStateView);
Expand All @@ -33,4 +33,4 @@ sol::function MakeSolFunction(sol::state& aState, F aFunc)
}

// Check if Lua object is of cdata type
bool IsLuaCData(sol::object aObject);
bool IsLuaCData(sol::object aObject);
2 changes: 1 addition & 1 deletion src/VKBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ LRESULT VKBindings::HandleRAWInput(HRAWINPUT ahRAWInput)

const auto lpb = std::make_unique<BYTE[]>(dwSize);
if (GetRawInputData(ahRAWInput, RID_INPUT, lpb.get(), &dwSize, sizeof(RAWINPUTHEADER)) != dwSize )
spdlog::warn("VKBindings::HandleRAWInput() - GetRawInputData() does not return correct size !");
Log::Warn("VKBindings::HandleRAWInput() - GetRawInputData() does not return correct size !");

const auto* raw = reinterpret_cast<const RAWINPUT*>(lpb.get());
if (raw->header.dwType == RIM_TYPEKEYBOARD)
Expand Down
60 changes: 60 additions & 0 deletions src/common/Logging.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#pragma once

namespace Log
{
using source_location = std::source_location;
[[nodiscard]] constexpr auto get_log_source_location(const source_location& location)
{
return spdlog::source_loc{location.file_name(), static_cast<std::int32_t>(location.line()),
location.function_name()};
}

struct format_with_location
{
std::string_view value;
spdlog::source_loc loc;

template<typename String>
format_with_location(const String& s, const source_location& location = source_location::current())
: value{s}
, loc{get_log_source_location(location)}
{
}
};

template<typename... Args>
void Warn(format_with_location fmt, Args&&... args)
{
spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::warn, fmt.value, std::forward<Args>(args)...);
}

template<typename... Args>
void Info(format_with_location fmt, Args&&... args)
{
spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::info, fmt.value, std::forward<Args>(args)...);
}

template<typename... Args>
void Debug(format_with_location fmt, Args&&... args)
{
spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::debug, fmt.value, std::forward<Args>(args)...);
}

template<typename... Args>
void Error(format_with_location fmt, Args&&... args)
{
spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::err, fmt.value, std::forward<Args>(args)...);
}

template<typename... Args>
void Critical(format_with_location fmt, Args&&... args)
{
spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::critical, fmt.value, std::forward<Args>(args)...);
}

template<typename... Args>
void Trace(format_with_location fmt, Args&&... args)
{
spdlog::default_logger_raw()->log(fmt.loc, spdlog::level::trace, fmt.value, std::forward<Args>(args)...);
}
} // namespace logging
9 changes: 6 additions & 3 deletions src/d3d12/D3D12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,18 @@ D3D12::D3D12(Window& aWindow, Paths& aPaths, Options& aOptions)
, m_window(aWindow)
, m_options(aOptions)
{
HookGame();

std::thread t([this]()
{
if (kiero::init() != kiero::Status::Success)
spdlog::error("Kiero failed!");
Log::Error("Kiero failed!");
else
{
std::string_view d3d12type = (kiero::isDownLevelDevice()) ? ("D3D12on7") : ("D3D12");
spdlog::info("Kiero initialized for {0}", d3d12type);
this->Hook();
Log::Info("Kiero initialized for {0}", d3d12type);

Hook();
}
});

Expand Down
Loading

0 comments on commit 481f972

Please sign in to comment.