mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-19 03:33:56 +00:00
Add cross platform filesystem support (#94)
This commit is contained in:
129
.github/workflows/build.yml
vendored
129
.github/workflows/build.yml
vendored
@@ -28,22 +28,57 @@ jobs:
|
||||
clang-format-version: '19'
|
||||
check-path: 'src'
|
||||
|
||||
dump-registry:
|
||||
name: Dump Registry
|
||||
runs-on: windows-latest
|
||||
|
||||
dump-root:
|
||||
name: Dump Root FS
|
||||
runs-on: ${{ matrix.runner }}
|
||||
needs: [build]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- Windows 2025
|
||||
- Windows 2022
|
||||
- Windows 2019
|
||||
include:
|
||||
- platform: Windows 2025
|
||||
runner: windows-2025
|
||||
- platform: Windows 2022
|
||||
runner: windows-2022
|
||||
- platform: Windows 2019
|
||||
runner: windows-2019
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Dump Registry
|
||||
run: src/tools/grab-registry.bat
|
||||
- name: Download DirectX Runtime
|
||||
run: curl -L -o directx_Jun2010_redist.exe https://download.microsoft.com/download/8/4/A/84A35BF1-DAFE-4AE8-82AF-AD2AE20B6B14/directx_Jun2010_redist.exe
|
||||
|
||||
- name: Extract DirectX Runtime
|
||||
run: ./directx_Jun2010_redist.exe /Q /T:"${{github.workspace}}/dxrt"
|
||||
|
||||
- name: Install DirectX Runtime
|
||||
run: "cmd /c \"start /wait .\\dxrt\\dxsetup.exe /silent\""
|
||||
|
||||
- name: Download Windows Artifacts
|
||||
uses: pyTooling/download-artifact@v4
|
||||
with:
|
||||
name: Windows Release Artifacts
|
||||
path: build/release/artifacts
|
||||
|
||||
- name: Dump Root FS
|
||||
run: src/tools/create-root.bat
|
||||
|
||||
- name: Dump API Set
|
||||
run: cd root && ../build/release/artifacts/dump-apiset.exe
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: pyTooling/upload-artifact@v4
|
||||
with:
|
||||
name: Temp Registry Dump
|
||||
path: |
|
||||
registry/*
|
||||
name: ${{ matrix.platform }} Root FS
|
||||
path: "*"
|
||||
working-directory: root
|
||||
retention-days: 1
|
||||
|
||||
build:
|
||||
name: Build
|
||||
@@ -55,7 +90,8 @@ jobs:
|
||||
- Windows
|
||||
- Linux GCC
|
||||
- Linux Clang
|
||||
- macOS
|
||||
- macOS arm64
|
||||
- macOS x86_64
|
||||
- Android x86_64
|
||||
- Android arm64-v8a
|
||||
configuration:
|
||||
@@ -73,8 +109,10 @@ jobs:
|
||||
- platform: Linux Clang
|
||||
runner: ubuntu-24.04
|
||||
clang-version: 18
|
||||
- platform: macOS
|
||||
- platform: macOS arm64
|
||||
runner: macos-latest
|
||||
- platform: macOS x86_64
|
||||
runner: macos-13
|
||||
- platform: Android x86_64
|
||||
runner: ubuntu-24.04
|
||||
abi: x86_64
|
||||
@@ -127,35 +165,36 @@ jobs:
|
||||
if: ${{ !startsWith(matrix.platform, 'Android') }}
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: pyTooling/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.platform }} ${{matrix.configuration}} Artifacts
|
||||
path: |
|
||||
build/${{matrix.preset}}/artifacts/*
|
||||
working-directory: build/${{matrix.preset}}/artifacts/
|
||||
path: "*"
|
||||
|
||||
- name: Upload Test Configuration
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: pyTooling/upload-artifact@v4
|
||||
with:
|
||||
name: Temp ${{ matrix.platform }} ${{matrix.configuration}} Test Config
|
||||
path: |
|
||||
build/${{matrix.preset}}/**/CTestTestfile.cmake
|
||||
|
||||
path: "**/CTestTestfile.cmake"
|
||||
working-directory: build/${{matrix.preset}}
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ${{ matrix.runner }}
|
||||
needs: [dump-registry, build]
|
||||
needs: [dump-root, build]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
# TODO: Move different windows platforms into registry dump matrix
|
||||
filesystem:
|
||||
- Windows 2025
|
||||
- Windows 2022
|
||||
- Windows 2019
|
||||
platform:
|
||||
- Windows
|
||||
- Linux GCC
|
||||
- Linux Clang
|
||||
- macOS
|
||||
- macOS arm64
|
||||
- macOS x86_64
|
||||
configuration:
|
||||
- Debug
|
||||
- Release
|
||||
@@ -164,58 +203,54 @@ jobs:
|
||||
preset: debug
|
||||
- configuration: Release
|
||||
preset: release
|
||||
- platform: Windows 2025
|
||||
build-platform: Windows
|
||||
runner: windows-2025
|
||||
- platform: Windows 2022
|
||||
build-platform: Windows
|
||||
runner: windows-2022
|
||||
- platform: Windows 2019
|
||||
build-platform: Windows
|
||||
runner: windows-2019
|
||||
- platform: Windows
|
||||
runner: windows-latest
|
||||
- platform: Linux GCC
|
||||
build-platform: Linux GCC
|
||||
runner: ubuntu-24.04
|
||||
- platform: Linux Clang
|
||||
build-platform: Linux Clang
|
||||
runner: ubuntu-24.04
|
||||
- platform: macOS
|
||||
build-platform: macOS
|
||||
- platform: macOS arm64
|
||||
runner: macos-latest
|
||||
- platform: macOS x86_64
|
||||
runner: macos-13
|
||||
steps:
|
||||
- name: Download Test Config
|
||||
uses: actions/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v4
|
||||
with:
|
||||
name: Temp ${{ matrix.build-platform }} ${{matrix.configuration}} Test Config
|
||||
name: Temp ${{ matrix.platform }} ${{matrix.configuration}} Test Config
|
||||
path: build/${{matrix.preset}}
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.build-platform }} ${{matrix.configuration}} Artifacts
|
||||
name: ${{ matrix.platform }} ${{matrix.configuration}} Artifacts
|
||||
path: build/${{matrix.preset}}/artifacts
|
||||
|
||||
- name: Download Windows Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
if: "${{ matrix.build-platform != 'Windows' }}"
|
||||
uses: pyTooling/download-artifact@v4
|
||||
if: "${{ matrix.platform != 'Windows' }}"
|
||||
with:
|
||||
name: Windows ${{matrix.configuration}} Artifacts
|
||||
path: build/${{matrix.preset}}/artifacts
|
||||
|
||||
- name: Download Registry Dump
|
||||
uses: actions/download-artifact@v4
|
||||
- name: Download Root FS
|
||||
uses: pyTooling/download-artifact@v4
|
||||
with:
|
||||
name: Temp Registry Dump
|
||||
path: build/${{matrix.preset}}/artifacts/registry
|
||||
name: ${{ matrix.filesystem }} Root FS
|
||||
path: build/${{matrix.preset}}/artifacts/root
|
||||
|
||||
- name: Copy Test Sample
|
||||
run: cp build/${{matrix.preset}}/artifacts/test-sample.exe build/${{matrix.preset}}/artifacts/root/filesys/c/
|
||||
|
||||
- name: CMake Test
|
||||
run: cd build/${{matrix.preset}} && ctest --verbose
|
||||
if: "${{ matrix.build-platform == 'Windows' }}"
|
||||
env:
|
||||
EMULATOR_ROOT: ${{github.workspace}}/build/${{matrix.preset}}/artifacts/root
|
||||
|
||||
summary:
|
||||
name: Pipeline Summary
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [dump-registry, build, test, verify-formatting]
|
||||
needs: [dump-root, build, test, verify-formatting]
|
||||
if: always()
|
||||
steps:
|
||||
- uses: geekyeggo/delete-artifact@v5
|
||||
|
||||
@@ -24,7 +24,11 @@ set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
|
||||
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")
|
||||
|
||||
# Prevent unicorn from generating universal binaries on macOS
|
||||
# It doesn't support it, even if it thinks it does...
|
||||
set(ENV{ARCHFLAGS} "nope")
|
||||
|
||||
|
||||
##########################################
|
||||
|
||||
|
||||
@@ -26,7 +26,10 @@ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
|
||||
##########################################
|
||||
|
||||
if(UNIX)
|
||||
momo_add_c_and_cxx_compile_options(-fvisibility=hidden)
|
||||
momo_add_c_and_cxx_compile_options(
|
||||
-fvisibility=hidden
|
||||
-ftrivial-auto-var-init=zero
|
||||
)
|
||||
endif()
|
||||
|
||||
##########################################
|
||||
@@ -91,6 +94,8 @@ if(MSVC)
|
||||
momo_add_release_link_options(
|
||||
#/LTCG
|
||||
)
|
||||
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
##########################################
|
||||
|
||||
2
deps/unicorn
vendored
2
deps/unicorn
vendored
Submodule deps/unicorn updated: 270d4716f4...9a6618baf8
@@ -11,7 +11,9 @@ namespace
|
||||
{
|
||||
bool use_gdb{false};
|
||||
bool concise_logging{false};
|
||||
bool verbose_logging{false};
|
||||
std::string registry_path{"./registry"};
|
||||
std::string emulation_root{};
|
||||
};
|
||||
|
||||
void watch_system_objects(windows_emulator& win_emu, const bool cache_logging)
|
||||
@@ -107,6 +109,7 @@ namespace
|
||||
emulator_settings settings{
|
||||
.application = args[0],
|
||||
.registry_directory = options.registry_path,
|
||||
.emulation_root = options.emulation_root,
|
||||
.arguments = parse_arguments(args),
|
||||
.silent_until_main = options.concise_logging,
|
||||
};
|
||||
@@ -116,7 +119,7 @@ namespace
|
||||
(void)&watch_system_objects;
|
||||
watch_system_objects(win_emu, options.concise_logging);
|
||||
win_emu.buffer_stdout = true;
|
||||
// win_emu.verbose_calls = true;
|
||||
win_emu.verbose_calls = options.verbose_logging;
|
||||
|
||||
const auto& exe = *win_emu.process().executable;
|
||||
|
||||
@@ -200,10 +203,23 @@ namespace
|
||||
{
|
||||
options.use_gdb = true;
|
||||
}
|
||||
else if (arg == "-v")
|
||||
{
|
||||
options.verbose_logging = true;
|
||||
}
|
||||
else if (arg == "-c")
|
||||
{
|
||||
options.concise_logging = true;
|
||||
}
|
||||
else if (arg == "-e")
|
||||
{
|
||||
if (args.size() < 2)
|
||||
{
|
||||
throw std::runtime_error("No emulation root path provided after -e");
|
||||
}
|
||||
arg_it = args.erase(arg_it);
|
||||
options.emulation_root = args[0];
|
||||
}
|
||||
else if (arg == "-r")
|
||||
{
|
||||
if (args.size() < 2)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#if _WIN32
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#define _CRT_NO_POSIX_ERROR_CODES
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
@@ -31,6 +31,8 @@ using NTSTATUS = std::uint32_t;
|
||||
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
|
||||
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
|
||||
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
|
||||
#define STATUS_MUTANT_NOT_OWNED ((NTSTATUS)0xC0000046L)
|
||||
#define STATUS_SEMAPHORE_LIMIT_EXCEEDED ((NTSTATUS)0xC0000047L)
|
||||
#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
|
||||
#define STATUS_FILE_INVALID ((NTSTATUS)0xC0000098L)
|
||||
#define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
template <typename Traits>
|
||||
struct UNICODE_STRING
|
||||
@@ -62,9 +63,9 @@ inline std::string w_to_u8(const std::wstring_view w_view)
|
||||
}
|
||||
|
||||
#ifndef OS_WINDOWS
|
||||
inline int open_unicode(FILE** handle, const std::u16string& fileName, const std::u16string& mode)
|
||||
inline int open_unicode(FILE** handle, const std::filesystem::path& fileName, const std::u16string& mode)
|
||||
{
|
||||
*handle = fopen(u16_to_u8(fileName).c_str(), u16_to_u8(mode).c_str());
|
||||
*handle = fopen(fileName.string().c_str(), u16_to_u8(mode).c_str());
|
||||
return errno;
|
||||
}
|
||||
#else
|
||||
@@ -73,8 +74,8 @@ inline std::wstring u16_to_w(const std::u16string& u16str)
|
||||
return std::wstring(reinterpret_cast<const wchar_t*>(u16str.data()), u16str.size());
|
||||
}
|
||||
|
||||
inline auto open_unicode(FILE** handle, const std::u16string& fileName, const std::u16string& mode)
|
||||
inline auto open_unicode(FILE** handle, const std::filesystem::path& fileName, const std::u16string& mode)
|
||||
{
|
||||
return _wfopen_s(handle, u16_to_w(fileName).c_str(), u16_to_w(mode).c_str());
|
||||
return _wfopen_s(handle, fileName.wstring().c_str(), u16_to_w(mode).c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -278,7 +278,7 @@ typedef struct _IMAGE_BASE_RELOCATION
|
||||
{
|
||||
DWORD VirtualAddress;
|
||||
DWORD SizeOfBlock;
|
||||
WORD TypeOffset[1];
|
||||
// WORD TypeOffset[1];
|
||||
} IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -39,7 +39,10 @@ namespace utils
|
||||
|
||||
static std::filesystem::path canonicalize_path(const std::filesystem::path& key)
|
||||
{
|
||||
auto path = key.lexically_normal().wstring();
|
||||
auto key_string = key.u16string();
|
||||
std::ranges::replace(key_string, u'\\', '/');
|
||||
|
||||
auto path = std::filesystem::path(key_string).lexically_normal().wstring();
|
||||
return utils::string::to_lower_consume(path);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -205,6 +206,26 @@ namespace utils
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read_list(std::list<T>& result)
|
||||
{
|
||||
const auto size = this->read<uint64_t>();
|
||||
result.clear();
|
||||
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
result.emplace_back(this->read<T>());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::list<T> read_list()
|
||||
{
|
||||
std::list<T> result{};
|
||||
this->read_list(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Map>
|
||||
void read_map(Map& map)
|
||||
{
|
||||
@@ -397,6 +418,17 @@ namespace utils
|
||||
this->write_span(std::span(vec));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_list(const std::list<T> vec)
|
||||
{
|
||||
this->write(static_cast<uint64_t>(vec.size()));
|
||||
|
||||
for (const auto& v : vec)
|
||||
{
|
||||
this->write(v);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_string(const std::basic_string_view<T> str)
|
||||
{
|
||||
|
||||
@@ -42,12 +42,12 @@ namespace
|
||||
|
||||
struct fuzzer_executer : fuzzer::executer
|
||||
{
|
||||
windows_emulator emu{};
|
||||
windows_emulator emu{"./"}; // TODO: Fix root directory
|
||||
std::span<const std::byte> emulator_data{};
|
||||
std::unordered_set<uint64_t> visited_blocks{};
|
||||
const std::function<fuzzer::coverage_functor>* handler{nullptr};
|
||||
|
||||
fuzzer_executer(std::span<const std::byte> data)
|
||||
fuzzer_executer(const std::span<const std::byte> data)
|
||||
: emulator_data(data)
|
||||
{
|
||||
emu.fuzzing = true;
|
||||
|
||||
@@ -290,7 +290,7 @@ bool test_native_exceptions()
|
||||
void print_time()
|
||||
{
|
||||
const auto epoch_time = std::chrono::system_clock::now().time_since_epoch();
|
||||
printf("Time: %lld\n", epoch_time.count());
|
||||
printf("Time: %lld\n", std::chrono::duration_cast<std::chrono::nanoseconds>(epoch_time).count());
|
||||
}
|
||||
|
||||
#define RUN_TEST(func, name) \
|
||||
|
||||
2
src/tools/.gitignore
vendored
Normal file
2
src/tools/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
root
|
||||
registry
|
||||
@@ -1,89 +0,0 @@
|
||||
@ECHO OFF
|
||||
|
||||
:: Host system directories
|
||||
SET SYSDIR="%WINDIR%\System32"
|
||||
|
||||
:: Qiling rootfs directories
|
||||
SET QL_WINDIR="Windows"
|
||||
SET QL_SYSDIR="%QL_WINDIR%\System32"
|
||||
|
||||
MKDIR %QL_WINDIR%
|
||||
MKDIR %QL_SYSDIR%
|
||||
|
||||
:: Collect 32-bit DLL files
|
||||
CALL :collect_dll advapi32.dll
|
||||
CALL :collect_dll bcrypt.dll
|
||||
CALL :collect_dll cfgmgr32.dll
|
||||
CALL :collect_dll ci.dll
|
||||
CALL :collect_dll combase.dll
|
||||
CALL :collect_dll comctl32.dll
|
||||
CALL :collect_dll comdlg32.dll
|
||||
CALL :collect_dll crypt32.dll
|
||||
CALL :collect_dll cryptbase.dll
|
||||
CALL :collect_dll gdi32.dll
|
||||
CALL :collect_dll hal.dll
|
||||
CALL :collect_dll iphlpapi.dll
|
||||
CALL :collect_dll kdcom.dll
|
||||
CALL :collect_dll kernel32.dll
|
||||
CALL :collect_dll KernelBase.dll
|
||||
CALL :collect_dll mpr.dll
|
||||
CALL :collect_dll mscoree.dll
|
||||
CALL :collect_dll msvcp_win.dll
|
||||
CALL :collect_dll msvcp60.dll
|
||||
CALL :collect_dll msvcr120_clr0400.dll, msvcr110.dll
|
||||
CALL :collect_dll msvcrt.dll
|
||||
CALL :collect_dll netapi32.dll
|
||||
CALL :collect_dll ntdll.dll
|
||||
CALL :collect_dll ole32.dll
|
||||
CALL :collect_dll oleaut32.dll
|
||||
CALL :collect_dll psapi.dll
|
||||
CALL :collect_dll rpcrt4.dll
|
||||
CALL :collect_dll sechost.dll
|
||||
CALL :collect_dll setupapi.dll
|
||||
CALL :collect_dll shell32.dll
|
||||
CALL :collect_dll shlwapi.dll
|
||||
CALL :collect_dll sspicli.dll
|
||||
CALL :collect_dll ucrtbase.dll
|
||||
CALL :collect_dll ucrtbased.dll
|
||||
CALL :collect_dll urlmon.dll
|
||||
CALL :collect_dll user32.dll
|
||||
CALL :collect_dll userenv.dll
|
||||
CALL :collect_dll uxtheme.dll
|
||||
CALL :collect_dll vcruntime140.dll
|
||||
CALL :collect_dll vcruntime140d.dll
|
||||
CALL :collect_dll vcruntime140_1.dll
|
||||
CALL :collect_dll vcruntime140_1d.dll
|
||||
CALL :collect_dll version.dll
|
||||
CALL :collect_dll win32u.dll
|
||||
CALL :collect_dll winhttp.dll
|
||||
CALL :collect_dll wininet.dll
|
||||
CALL :collect_dll winmm.dll
|
||||
CALL :collect_dll ws2_32.dll
|
||||
CALL :collect_dll wsock32.dll
|
||||
|
||||
CALL :collect_dll locale.nls
|
||||
|
||||
:: Collect extras
|
||||
CALL :collect %SYSDIR64%, ntoskrnl.exe, %QL_SYSDIR32%
|
||||
|
||||
:: All done!
|
||||
EXIT /B 0
|
||||
|
||||
:: Functions definitions
|
||||
:normpath
|
||||
SET %1=%~dpfn2
|
||||
EXIT /B
|
||||
|
||||
:collect
|
||||
CALL :normpath SRC, %~1\%~2
|
||||
CALL :normpath DST, %~3\%~4
|
||||
|
||||
IF EXIST %SRC% (
|
||||
ECHO %SRC% -^> %DST%
|
||||
COPY /B /Y "%SRC%" "%DST%" >NUL
|
||||
)
|
||||
EXIT /B
|
||||
|
||||
:collect_dll
|
||||
CALL :collect %SYSDIR%, %~1, %QL_SYSDIR%, %~2
|
||||
EXIT /B
|
||||
136
src/tools/create-root.bat
Normal file
136
src/tools/create-root.bat
Normal file
@@ -0,0 +1,136 @@
|
||||
@ECHO OFF
|
||||
|
||||
NET SESSIONS > NUL 2>&1
|
||||
IF %ERRORLEVEL% NEQ 0 (
|
||||
ECHO Error: This script requires administrative privileges.
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
SET SYSDIR="%WINDIR%\System32"
|
||||
|
||||
:: Qiling rootfs directories
|
||||
SET EMU_ROOT=root
|
||||
SET EMU_FILESYS=%EMU_ROOT%\filesys
|
||||
SET EMU_WINDIR=%EMU_FILESYS%\c\windows
|
||||
SET EMU_SYSDIR=%EMU_WINDIR%\system32
|
||||
SET EMU_REGDIR=%EMU_ROOT%\registry
|
||||
|
||||
MKDIR %EMU_SYSDIR%
|
||||
MKDIR %EMU_REGDIR%
|
||||
|
||||
REG SAVE HKLM\SYSTEM %EMU_REGDIR%\SYSTEM /Y
|
||||
REG SAVE HKLM\SECURITY %EMU_REGDIR%\SECURITY /Y
|
||||
REG SAVE HKLM\SOFTWARE %EMU_REGDIR%\SOFTWARE /Y
|
||||
REG SAVE HKLM\HARDWARE %EMU_REGDIR%\HARDWARE /Y
|
||||
REG SAVE HKLM\SAM %EMU_REGDIR%\SAM /Y
|
||||
COPY /B /Y C:\Users\Default\NTUSER.DAT "%EMU_REGDIR%\NTUSER.DAT"
|
||||
|
||||
CALL :collect_dll advapi32.dll
|
||||
CALL :collect_dll bcrypt.dll
|
||||
CALL :collect_dll cfgmgr32.dll
|
||||
CALL :collect_dll ci.dll
|
||||
CALL :collect_dll combase.dll
|
||||
CALL :collect_dll comctl32.dll
|
||||
CALL :collect_dll comdlg32.dll
|
||||
CALL :collect_dll crypt32.dll
|
||||
CALL :collect_dll cryptbase.dll
|
||||
CALL :collect_dll gdi32.dll
|
||||
CALL :collect_dll hal.dll
|
||||
CALL :collect_dll iphlpapi.dll
|
||||
CALL :collect_dll kdcom.dll
|
||||
CALL :collect_dll kernel32.dll
|
||||
CALL :collect_dll kernelbase.dll
|
||||
CALL :collect_dll mpr.dll
|
||||
CALL :collect_dll mscoree.dll
|
||||
CALL :collect_dll msvcp_win.dll
|
||||
CALL :collect_dll msvcp60.dll
|
||||
CALL :collect_dll msvcr120_clr0400.dll
|
||||
CALL :collect_dll msvcrt.dll
|
||||
CALL :collect_dll netapi32.dll
|
||||
CALL :collect_dll ntdll.dll
|
||||
CALL :collect_dll ole32.dll
|
||||
CALL :collect_dll oleaut32.dll
|
||||
CALL :collect_dll psapi.dll
|
||||
CALL :collect_dll rpcrt4.dll
|
||||
CALL :collect_dll sechost.dll
|
||||
CALL :collect_dll setupapi.dll
|
||||
CALL :collect_dll shell32.dll
|
||||
CALL :collect_dll shlwapi.dll
|
||||
CALL :collect_dll sspicli.dll
|
||||
CALL :collect_dll ucrtbase.dll
|
||||
CALL :collect_dll ucrtbased.dll
|
||||
CALL :collect_dll urlmon.dll
|
||||
CALL :collect_dll user32.dll
|
||||
CALL :collect_dll userenv.dll
|
||||
CALL :collect_dll uxtheme.dll
|
||||
CALL :collect_dll vcruntime140.dll
|
||||
CALL :collect_dll vcruntime140d.dll
|
||||
CALL :collect_dll vcruntime140_1.dll
|
||||
CALL :collect_dll vcruntime140_1d.dll
|
||||
CALL :collect_dll version.dll
|
||||
CALL :collect_dll win32u.dll
|
||||
CALL :collect_dll winhttp.dll
|
||||
CALL :collect_dll wininet.dll
|
||||
CALL :collect_dll winmm.dll
|
||||
CALL :collect_dll ws2_32.dll
|
||||
CALL :collect_dll wsock32.dll
|
||||
CALL :collect_dll msvcp140.dll
|
||||
CALL :collect_dll msvcp140d.dll
|
||||
CALL :collect_dll d3d11.dll
|
||||
CALL :collect_dll d3d9.dll
|
||||
CALL :collect_dll d3d12.dll
|
||||
CALL :collect_dll d3dcompiler_47.dll
|
||||
CALL :collect_dll dxgi.dll
|
||||
CALL :collect_dll dsound.dll
|
||||
CALL :collect_dll dwmapi.dll
|
||||
CALL :collect_dll hid.dll
|
||||
CALL :collect_dll imm32.dll
|
||||
CALL :collect_dll uiautomationcore.dll
|
||||
CALL :collect_dll opengl32.dll
|
||||
CALL :collect_dll normaliz.dll
|
||||
CALL :collect_dll wintrust.dll
|
||||
CALL :collect_dll wldap32.dll
|
||||
CALL :collect_dll wtsapi32.dll
|
||||
CALL :collect_dll x3daudio1_7.dll
|
||||
CALL :collect_dll xapofx1_5.dll
|
||||
CALL :collect_dll xinput1_3.dll
|
||||
CALL :collect_dll xinput9_1_0.dll
|
||||
CALL :collect_dll cryptsp.dll
|
||||
CALL :collect_dll resampledmo.dll
|
||||
CALL :collect_dll powrprof.dll
|
||||
CALL :collect_dll winmmbase.dll
|
||||
CALL :collect_dll gdi32full.dll
|
||||
CALL :collect_dll glu32.dll
|
||||
CALL :collect_dll msdmo.dll
|
||||
CALL :collect_dll dxcore.dll
|
||||
CALL :collect_dll mfplat.dll
|
||||
CALL :collect_dll wer.dll
|
||||
CALL :collect_dll dbghelp.dll
|
||||
CALL :collect_dll mscms.dll
|
||||
CALL :collect_dll ktmw32.dll
|
||||
CALL :collect_dll shcore.dll
|
||||
CALL :collect_dll diagnosticdatasettings.dll
|
||||
|
||||
CALL :collect_dll locale.nls
|
||||
|
||||
:: All done!
|
||||
EXIT /B 0
|
||||
|
||||
:: Functions definitions
|
||||
:normpath
|
||||
SET %1=%~dpfn2
|
||||
EXIT /B
|
||||
|
||||
:collect
|
||||
CALL :normpath SRC, %~1\%~2
|
||||
CALL :normpath DST, %~3\%~2
|
||||
|
||||
IF EXIST %SRC% (
|
||||
ECHO %SRC% -^> %DST%
|
||||
COPY /B /Y "%SRC%" "%DST%" >NUL
|
||||
)
|
||||
EXIT /B
|
||||
|
||||
:collect_dll
|
||||
CALL :collect %SYSDIR%, %~1, %EMU_SYSDIR%
|
||||
EXIT /B
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <gtest/gtest.h>
|
||||
#include <windows_emulator.hpp>
|
||||
|
||||
@@ -20,9 +21,21 @@
|
||||
|
||||
namespace test
|
||||
{
|
||||
inline std::filesystem::path get_emulator_root()
|
||||
{
|
||||
auto* env = getenv("EMULATOR_ROOT");
|
||||
if (!env)
|
||||
{
|
||||
throw std::runtime_error("No EMULATOR_ROOT set!");
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
inline windows_emulator create_sample_emulator(emulator_settings settings, emulator_callbacks callbacks = {})
|
||||
{
|
||||
settings.application = "./test-sample.exe";
|
||||
settings.application = "c:/test-sample.exe";
|
||||
settings.emulation_root = get_emulator_root();
|
||||
return windows_emulator{std::move(settings), std::move(callbacks)};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace test
|
||||
|
||||
utils::buffer_deserializer deserializer{serializer1.get_buffer()};
|
||||
|
||||
windows_emulator new_emu{};
|
||||
windows_emulator new_emu{get_emulator_root()};
|
||||
new_emu.deserialize(deserializer);
|
||||
|
||||
utils::buffer_serializer serializer2{};
|
||||
@@ -57,7 +57,7 @@ namespace test
|
||||
|
||||
utils::buffer_deserializer deserializer{serializer.get_buffer()};
|
||||
|
||||
windows_emulator new_emu{};
|
||||
windows_emulator new_emu{get_emulator_root()};
|
||||
new_emu.log.disable_output(true);
|
||||
new_emu.deserialize(deserializer);
|
||||
|
||||
|
||||
86
src/windows-emulator/file_system.hpp
Normal file
86
src/windows-emulator/file_system.hpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
#include "std_include.hpp"
|
||||
#include "windows_path.hpp"
|
||||
|
||||
#include <platform/compiler.hpp>
|
||||
|
||||
class file_system
|
||||
{
|
||||
public:
|
||||
file_system(std::filesystem::path root, windows_path working_dir = "C:\\")
|
||||
: root_(std::move(root)),
|
||||
working_dir_(std::move(working_dir))
|
||||
{
|
||||
}
|
||||
|
||||
std::filesystem::path translate(const windows_path& win_path) const
|
||||
{
|
||||
const auto& full_path = win_path.is_absolute() //
|
||||
? win_path
|
||||
: (this->working_dir_ / win_path);
|
||||
|
||||
#ifdef OS_WINDOWS
|
||||
if (this->root_.empty())
|
||||
{
|
||||
return full_path.u16string();
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: Sanitize path to prevent traversal!
|
||||
return this->root_ / full_path.to_portable_path();
|
||||
}
|
||||
|
||||
void set_working_directory(windows_path working_dir)
|
||||
{
|
||||
this->working_dir_ = std::move(working_dir);
|
||||
}
|
||||
|
||||
const windows_path& get_working_directory() const
|
||||
{
|
||||
return this->working_dir_;
|
||||
}
|
||||
|
||||
windows_path local_to_windows_path(const std::filesystem::path& local_path) const
|
||||
{
|
||||
const auto absolute_local_path = absolute(local_path);
|
||||
const auto relative_path = relative(absolute_local_path, this->root_);
|
||||
|
||||
if (relative_path.empty() || *relative_path.begin() == "..")
|
||||
{
|
||||
throw std::runtime_error("Path '" + local_path.string() + "' is not within the root filesystem!");
|
||||
}
|
||||
|
||||
char drive{};
|
||||
std::list<std::u16string> folders{};
|
||||
|
||||
for (auto i = relative_path.begin(); i != relative_path.end(); ++i)
|
||||
{
|
||||
if (i == relative_path.begin())
|
||||
{
|
||||
const auto str = i->string();
|
||||
assert(str.size() == 1);
|
||||
drive = str[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
folders.push_back(i->u16string());
|
||||
}
|
||||
}
|
||||
|
||||
return windows_path{drive, std::move(folders)};
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->working_dir_);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->working_dir_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::filesystem::path root_{};
|
||||
windows_path working_dir_{};
|
||||
};
|
||||
@@ -114,7 +114,7 @@ namespace handle_detail
|
||||
struct generic_handle_store
|
||||
{
|
||||
virtual ~generic_handle_store() = default;
|
||||
virtual bool erase(const handle h) = 0;
|
||||
virtual bool erase(handle h) = 0;
|
||||
};
|
||||
|
||||
template <handle_types::type Type, typename T, uint32_t IndexShift = 0>
|
||||
@@ -205,7 +205,7 @@ class handle_store : public generic_handle_store
|
||||
{
|
||||
if (!T::deleter(entry->second))
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +354,8 @@ constexpr auto BASE_NAMED_OBJECTS_DIRECTORY = make_pseudo_handle(0x2, handle_typ
|
||||
constexpr auto KNOWN_DLLS_SYMLINK = make_pseudo_handle(0x1, handle_types::symlink);
|
||||
constexpr auto SHARED_SECTION = make_pseudo_handle(0x1, handle_types::section);
|
||||
|
||||
constexpr auto WER_PORT_READY = make_pseudo_handle(0x1, handle_types::event);
|
||||
|
||||
constexpr auto CONSOLE_HANDLE = make_pseudo_handle(0x1, handle_types::file);
|
||||
constexpr auto STDOUT_HANDLE = make_pseudo_handle(0x2, handle_types::file);
|
||||
constexpr auto STDIN_HANDLE = make_pseudo_handle(0x3, handle_types::file);
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace
|
||||
kusd.QpcFrequency = std::chrono::steady_clock::period::den;
|
||||
}
|
||||
|
||||
constexpr std::wstring_view root_dir{L"C:\\WINDOWS"};
|
||||
constexpr std::u16string_view root_dir{u"C:\\WINDOWS"};
|
||||
memcpy(&kusd.NtSystemRoot.arr[0], root_dir.data(), root_dir.size() * 2);
|
||||
|
||||
kusd.ImageNumberLow = IMAGE_FILE_MACHINE_I386;
|
||||
|
||||
@@ -3,21 +3,7 @@
|
||||
#include "module_mapping.hpp"
|
||||
#include "windows-emulator/logger.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
std::filesystem::path canonicalize_module_path(const std::filesystem::path& file)
|
||||
{
|
||||
constexpr std::u16string_view nt_prefix = u"\\??\\";
|
||||
const auto wide_file = file.u16string();
|
||||
|
||||
if (!wide_file.starts_with(nt_prefix))
|
||||
{
|
||||
return canonical(absolute(file));
|
||||
}
|
||||
|
||||
return canonicalize_module_path(wide_file.substr(nt_prefix.size()));
|
||||
}
|
||||
}
|
||||
#include <serialization_helper.hpp>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
@@ -39,8 +25,8 @@ namespace utils
|
||||
|
||||
static void serialize(buffer_serializer& buffer, const mapped_module& mod)
|
||||
{
|
||||
buffer.write_string(mod.name);
|
||||
buffer.write(mod.path.u16string());
|
||||
buffer.write(mod.name);
|
||||
buffer.write(mod.path);
|
||||
|
||||
buffer.write(mod.image_base);
|
||||
buffer.write(mod.size_of_image);
|
||||
@@ -52,8 +38,8 @@ namespace utils
|
||||
|
||||
static void deserialize(buffer_deserializer& buffer, mapped_module& mod)
|
||||
{
|
||||
mod.name = buffer.read_string();
|
||||
mod.path = buffer.read_string<std::u16string::value_type>();
|
||||
buffer.read(mod.name);
|
||||
buffer.read(mod.path);
|
||||
|
||||
buffer.read(mod.image_base);
|
||||
buffer.read(mod.size_of_image);
|
||||
@@ -64,26 +50,32 @@ namespace utils
|
||||
}
|
||||
}
|
||||
|
||||
module_manager::module_manager(emulator& emu)
|
||||
: emu_(&emu)
|
||||
module_manager::module_manager(emulator& emu, file_system& file_sys)
|
||||
: emu_(&emu),
|
||||
file_sys_(&file_sys)
|
||||
{
|
||||
}
|
||||
|
||||
mapped_module* module_manager::map_module(const std::filesystem::path& file, logger& logger)
|
||||
mapped_module* module_manager::map_module(const windows_path& file, const logger& logger)
|
||||
{
|
||||
auto canonical_file = canonicalize_module_path(file);
|
||||
return this->map_local_module(this->file_sys_->translate(file), logger);
|
||||
}
|
||||
|
||||
for (auto& mod : this->modules_)
|
||||
mapped_module* module_manager::map_local_module(const std::filesystem::path& file, const logger& logger)
|
||||
{
|
||||
auto local_file = canonical(absolute(file));
|
||||
|
||||
for (auto& mod : this->modules_ | std::views::values)
|
||||
{
|
||||
if (mod.second.path == canonical_file)
|
||||
if (mod.path == local_file)
|
||||
{
|
||||
return &mod.second;
|
||||
return &mod;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto mod = map_module_from_file(*this->emu_, std::move(canonical_file));
|
||||
auto mod = map_module_from_file(*this->emu_, std::move(local_file));
|
||||
|
||||
logger.log("Mapped %s at 0x%" PRIx64 "\n", mod.path.generic_string().c_str(), mod.image_base);
|
||||
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
#pragma once
|
||||
#include "mapped_module.hpp"
|
||||
#include <emulator.hpp>
|
||||
|
||||
#include "mapped_module.hpp"
|
||||
#include "../file_system.hpp"
|
||||
|
||||
class logger;
|
||||
|
||||
class module_manager
|
||||
{
|
||||
public:
|
||||
using module_map = std::map<uint64_t, mapped_module>;
|
||||
module_manager(emulator& emu);
|
||||
module_manager(emulator& emu, file_system& file_sys);
|
||||
|
||||
mapped_module* map_module(const std::filesystem::path& file, logger& logger);
|
||||
mapped_module* map_module(const windows_path& file, const logger& logger);
|
||||
mapped_module* map_local_module(const std::filesystem::path& file, const logger& logger);
|
||||
|
||||
mapped_module* find_by_address(const uint64_t address)
|
||||
{
|
||||
@@ -45,6 +48,7 @@ class module_manager
|
||||
|
||||
private:
|
||||
emulator* emu_{};
|
||||
file_system* file_sys_{};
|
||||
|
||||
module_map modules_{};
|
||||
|
||||
|
||||
@@ -108,17 +108,17 @@ struct mutant : ref_counted_object
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t release()
|
||||
std::pair<uint32_t, bool> release(const uint32_t thread_id)
|
||||
{
|
||||
const auto old_count = this->locked_count;
|
||||
|
||||
if (this->locked_count <= 0)
|
||||
if (this->locked_count <= 0 || this->owning_thread_id != thread_id)
|
||||
{
|
||||
return old_count;
|
||||
return {old_count, false};
|
||||
}
|
||||
|
||||
--this->locked_count;
|
||||
return old_count;
|
||||
return {old_count, true};
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
@@ -239,9 +239,34 @@ struct section
|
||||
struct semaphore : ref_counted_object
|
||||
{
|
||||
std::u16string name{};
|
||||
volatile uint32_t current_count{};
|
||||
uint32_t current_count{};
|
||||
uint32_t max_count{};
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
if (this->current_count > 0)
|
||||
{
|
||||
--this->current_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<uint32_t, bool> release(const uint32_t release_count)
|
||||
{
|
||||
const auto old_count = this->current_count;
|
||||
|
||||
if (this->current_count + release_count > this->max_count)
|
||||
{
|
||||
return {old_count, false};
|
||||
}
|
||||
|
||||
this->current_count += release_count;
|
||||
|
||||
return {old_count, true};
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->name);
|
||||
@@ -504,12 +529,12 @@ class emulator_thread : ref_counted_object
|
||||
|
||||
struct process_context
|
||||
{
|
||||
process_context(x64_emulator& emu)
|
||||
process_context(x64_emulator& emu, file_system& file_sys)
|
||||
: base_allocator(emu),
|
||||
peb(emu),
|
||||
process_params(emu),
|
||||
kusd(emu, *this),
|
||||
mod_manager(emu)
|
||||
mod_manager(emu, file_sys)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -550,7 +575,7 @@ struct process_context
|
||||
|
||||
std::vector<std::byte> default_register_set{};
|
||||
|
||||
uint32_t current_thread_id{0};
|
||||
uint32_t spawned_thread_count{0};
|
||||
handle_store<handle_types::thread, emulator_thread> threads{};
|
||||
emulator_thread* active_thread{nullptr};
|
||||
|
||||
@@ -587,7 +612,7 @@ struct process_context
|
||||
buffer.write_map(this->atoms);
|
||||
|
||||
buffer.write_vector(this->default_register_set);
|
||||
buffer.write(this->current_thread_id);
|
||||
buffer.write(this->spawned_thread_count);
|
||||
buffer.write(this->threads);
|
||||
|
||||
buffer.write(this->threads.find_handle(this->active_thread).bits);
|
||||
@@ -630,7 +655,7 @@ struct process_context
|
||||
buffer.read_map(this->atoms);
|
||||
|
||||
buffer.read_vector(this->default_register_set);
|
||||
buffer.read(this->current_thread_id);
|
||||
buffer.read(this->spawned_thread_count);
|
||||
|
||||
buffer.read(this->threads);
|
||||
|
||||
@@ -640,7 +665,7 @@ struct process_context
|
||||
handle create_thread(x64_emulator& emu, const uint64_t start_address, const uint64_t argument,
|
||||
const uint64_t stack_size)
|
||||
{
|
||||
emulator_thread t{emu, *this, start_address, argument, stack_size, ++this->current_thread_id};
|
||||
emulator_thread t{emu, *this, start_address, argument, stack_size, ++this->spawned_thread_count};
|
||||
return this->threads.store(std::move(t));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
bool is_subpath(const std::filesystem::path& root, const std::filesystem::path& p)
|
||||
bool is_subpath(const utils::path_key& root, const utils::path_key& p)
|
||||
{
|
||||
auto root_it = root.begin();
|
||||
auto p_it = p.begin();
|
||||
auto root_it = root.get().begin();
|
||||
auto p_it = p.get().begin();
|
||||
|
||||
for (; root_it != root.end(); ++root_it, ++p_it)
|
||||
for (; root_it != root.get().end(); ++root_it, ++p_it)
|
||||
{
|
||||
if (p_it == p.end() || *root_it != *p_it)
|
||||
if (p_it == p.get().end() || *root_it != *p_it)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -23,8 +23,7 @@ namespace
|
||||
return true;
|
||||
}
|
||||
|
||||
void register_hive(registry_manager::hive_map& hives, const std::filesystem::path& key,
|
||||
const std::filesystem::path& file)
|
||||
void register_hive(registry_manager::hive_map& hives, const utils::path_key& key, const std::filesystem::path& file)
|
||||
{
|
||||
hives[key] = std::make_unique<hive_parser>(file);
|
||||
}
|
||||
@@ -56,7 +55,7 @@ void registry_manager::setup()
|
||||
register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM");
|
||||
register_hive(this->hives_, machine / "hardware", this->hive_path_ / "HARDWARE");
|
||||
|
||||
register_hive(this->hives_, root / "user", this->hive_path_ / "NTUSER.dat");
|
||||
register_hive(this->hives_, root / "user", this->hive_path_ / "NTUSER.DAT");
|
||||
|
||||
this->add_path_mapping(machine / "system" / "CurrentControlSet", machine / "system" / "ControlSet001");
|
||||
}
|
||||
@@ -72,7 +71,7 @@ void registry_manager::deserialize(utils::buffer_deserializer& buffer)
|
||||
this->setup();
|
||||
}
|
||||
|
||||
std::filesystem::path registry_manager::normalize_path(const std::filesystem::path& path) const
|
||||
utils::path_key registry_manager::normalize_path(const utils::path_key& path) const
|
||||
{
|
||||
const utils::path_key canonical_path = path;
|
||||
|
||||
@@ -87,16 +86,16 @@ std::filesystem::path registry_manager::normalize_path(const std::filesystem::pa
|
||||
return canonical_path.get();
|
||||
}
|
||||
|
||||
void registry_manager::add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value)
|
||||
void registry_manager::add_path_mapping(const utils::path_key& key, const utils::path_key& value)
|
||||
{
|
||||
this->path_mapping_[key] = value;
|
||||
}
|
||||
|
||||
std::optional<registry_key> registry_manager::get_key(const std::filesystem::path& key)
|
||||
std::optional<registry_key> registry_manager::get_key(const utils::path_key& key)
|
||||
{
|
||||
const auto normal_key = this->normalize_path(key);
|
||||
|
||||
if (is_subpath(normal_key, "\\registry\\machine"))
|
||||
if (is_subpath(normal_key, utils::path_key{"\\registry\\machine"}))
|
||||
{
|
||||
registry_key reg_key{};
|
||||
reg_key.hive = normal_key;
|
||||
@@ -111,14 +110,14 @@ std::optional<registry_key> registry_manager::get_key(const std::filesystem::pat
|
||||
|
||||
registry_key reg_key{};
|
||||
reg_key.hive = iterator->first.get();
|
||||
reg_key.path = normal_key.lexically_relative(reg_key.hive);
|
||||
reg_key.path = normal_key.get().lexically_relative(reg_key.hive.get());
|
||||
|
||||
if (reg_key.path.empty())
|
||||
if (reg_key.path.get().empty())
|
||||
{
|
||||
return {std::move(reg_key)};
|
||||
}
|
||||
|
||||
const auto entry = iterator->second->get_sub_key(reg_key.path);
|
||||
const auto entry = iterator->second->get_sub_key(reg_key.path.get());
|
||||
if (!entry)
|
||||
{
|
||||
return std::nullopt;
|
||||
@@ -137,7 +136,7 @@ std::optional<registry_value> registry_manager::get_value(const registry_key& ke
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto* entry = iterator->second->get_value(key.path, name);
|
||||
auto* entry = iterator->second->get_value(key.path.get(), name);
|
||||
if (!entry)
|
||||
{
|
||||
return std::nullopt;
|
||||
@@ -151,11 +150,11 @@ std::optional<registry_value> registry_manager::get_value(const registry_key& ke
|
||||
return v;
|
||||
}
|
||||
|
||||
registry_manager::hive_map::iterator registry_manager::find_hive(const std::filesystem::path& key)
|
||||
registry_manager::hive_map::iterator registry_manager::find_hive(const utils::path_key& key)
|
||||
{
|
||||
for (auto i = this->hives_.begin(); i != this->hives_.end(); ++i)
|
||||
{
|
||||
if (is_subpath(i->first.get(), key))
|
||||
if (is_subpath(i->first, key))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
struct registry_key
|
||||
{
|
||||
std::filesystem::path hive{};
|
||||
std::filesystem::path path{};
|
||||
utils::path_key hive{};
|
||||
utils::path_key path{};
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
@@ -48,7 +48,7 @@ class registry_manager
|
||||
void serialize(utils::buffer_serializer& buffer) const;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
|
||||
std::optional<registry_key> get_key(const std::filesystem::path& key);
|
||||
std::optional<registry_key> get_key(const utils::path_key& key);
|
||||
std::optional<registry_value> get_value(const registry_key& key, std::string name);
|
||||
|
||||
private:
|
||||
@@ -56,10 +56,10 @@ class registry_manager
|
||||
hive_map hives_{};
|
||||
std::unordered_map<utils::path_key, utils::path_key> path_mapping_{};
|
||||
|
||||
std::filesystem::path normalize_path(const std::filesystem::path& path) const;
|
||||
void add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value);
|
||||
utils::path_key normalize_path(const utils::path_key& path) const;
|
||||
void add_path_mapping(const utils::path_key& key, const utils::path_key& value);
|
||||
|
||||
hive_map::iterator find_hive(const std::filesystem::path& key);
|
||||
hive_map::iterator find_hive(const utils::path_key& key);
|
||||
|
||||
void setup();
|
||||
};
|
||||
|
||||
@@ -77,13 +77,13 @@ namespace
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
const std::filesystem::path full_path = parent_handle->hive / parent_handle->path / key;
|
||||
const std::filesystem::path full_path = parent_handle->hive.get() / parent_handle->path.get() / key;
|
||||
key = full_path.u16string();
|
||||
}
|
||||
|
||||
c.win_emu.log.print(color::dark_gray, "--> Registry key: %s\n", u16_to_u8(key).c_str());
|
||||
|
||||
auto entry = c.proc.registry.get_key(key);
|
||||
auto entry = c.proc.registry.get_key({key});
|
||||
if (!entry.has_value())
|
||||
{
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
@@ -115,8 +115,8 @@ namespace
|
||||
|
||||
if (key_information_class == KeyNameInformation)
|
||||
{
|
||||
auto key_name = (key->hive / key->path).wstring();
|
||||
while (key_name.ends_with('/') || key_name.ends_with('\\'))
|
||||
auto key_name = (key->hive.get() / key->path.get()).u16string();
|
||||
while (key_name.ends_with(u'/') || key_name.ends_with(u'\\'))
|
||||
{
|
||||
key_name.pop_back();
|
||||
}
|
||||
@@ -192,7 +192,7 @@ namespace
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
const std::wstring original_name(value->name.begin(), value->name.end());
|
||||
const std::u16string original_name(value->name.begin(), value->name.end());
|
||||
|
||||
if (key_value_information_class == KeyValueBasicInformation)
|
||||
{
|
||||
@@ -449,14 +449,14 @@ namespace
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
const auto old_count = mutant->release();
|
||||
const auto [old_count, succeeded] = mutant->release(c.win_emu.current_thread().id);
|
||||
|
||||
if (previous_count)
|
||||
{
|
||||
previous_count.write(static_cast<LONG>(old_count));
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
return succeeded ? STATUS_SUCCESS : STATUS_MUTANT_NOT_OWNED;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtCreateMutant(const syscall_context& c, const emulator_object<handle> mutant_handle,
|
||||
@@ -477,10 +477,12 @@ namespace
|
||||
|
||||
if (!name.empty())
|
||||
{
|
||||
for (const auto& mutant : c.proc.mutants | std::views::values)
|
||||
for (auto& entry : c.proc.mutants)
|
||||
{
|
||||
if (mutant.name == name)
|
||||
if (entry.second.name == name)
|
||||
{
|
||||
++entry.second.ref_count;
|
||||
mutant_handle.write(c.proc.mutants.make_handle(entry.first));
|
||||
return STATUS_OBJECT_NAME_EXISTS;
|
||||
}
|
||||
}
|
||||
@@ -518,10 +520,12 @@ namespace
|
||||
|
||||
if (!name.empty())
|
||||
{
|
||||
for (const auto& event : c.proc.events | std::views::values)
|
||||
for (auto& entry : c.proc.events)
|
||||
{
|
||||
if (event.name == name)
|
||||
if (entry.second.name == name)
|
||||
{
|
||||
++entry.second.ref_count;
|
||||
event_handle.write(c.proc.events.make_handle(entry.first));
|
||||
return STATUS_OBJECT_NAME_EXISTS;
|
||||
}
|
||||
}
|
||||
@@ -549,6 +553,12 @@ namespace
|
||||
const auto name =
|
||||
read_unicode_string(c.emu, reinterpret_cast<UNICODE_STRING<EmulatorTraits<Emu64>>*>(attributes.ObjectName));
|
||||
|
||||
if (name == u"\\KernelObjects\\SystemErrorPortReady")
|
||||
{
|
||||
event_handle.write(WER_PORT_READY.bits);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
for (auto& entry : c.proc.events)
|
||||
{
|
||||
if (entry.second.name == name)
|
||||
@@ -1520,7 +1530,7 @@ namespace
|
||||
if (!f->enumeration_state || query_flags & SL_RESTART_SCAN)
|
||||
{
|
||||
f->enumeration_state.emplace(file_enumeration_state{});
|
||||
f->enumeration_state->files = scan_directory(f->name);
|
||||
f->enumeration_state->files = scan_directory(c.win_emu.file_sys().translate(f->name));
|
||||
}
|
||||
|
||||
auto& enum_state = *f->enumeration_state;
|
||||
@@ -2575,7 +2585,8 @@ namespace
|
||||
const emulator_object<LCID> default_locale_id,
|
||||
const emulator_object<LARGE_INTEGER> /*default_casing_table_size*/)
|
||||
{
|
||||
const auto locale_file = utils::io::read_file(R"(C:\Windows\System32\locale.nls)");
|
||||
const auto locale_file =
|
||||
utils::io::read_file(c.win_emu.file_sys().translate(R"(C:\Windows\System32\locale.nls)"));
|
||||
if (locale_file.empty())
|
||||
{
|
||||
return STATUS_FILE_INVALID;
|
||||
@@ -2815,14 +2826,14 @@ namespace
|
||||
if (create_disposition & FILE_CREATE)
|
||||
{
|
||||
std::error_code ec{};
|
||||
std::filesystem::create_directory(f.name, ec);
|
||||
create_directory(c.win_emu.file_sys().translate(f.name), ec);
|
||||
|
||||
if (ec)
|
||||
{
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
else if (!std::filesystem::is_directory(f.name))
|
||||
else if (!std::filesystem::is_directory(c.win_emu.file_sys().translate(f.name)))
|
||||
{
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
@@ -2844,7 +2855,7 @@ namespace
|
||||
|
||||
FILE* file{};
|
||||
|
||||
const auto error = open_unicode(&file, f.name, mode);
|
||||
const auto error = open_unicode(&file, c.win_emu.file_sys().translate(f.name), mode);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -2886,10 +2897,13 @@ namespace
|
||||
|
||||
const auto filename = read_unicode_string(
|
||||
c.emu, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>>{c.emu, attributes.ObjectName});
|
||||
const auto u8_filename = u16_to_u8(filename);
|
||||
|
||||
c.win_emu.log.print(color::dark_gray, "--> Querying file attributes: %s\n", u16_to_u8(filename).c_str());
|
||||
|
||||
const auto local_filename = c.win_emu.file_sys().translate(filename).string();
|
||||
|
||||
struct _stat64 file_stat{};
|
||||
if (_stat64(u8_filename.c_str(), &file_stat) != 0)
|
||||
if (_stat64(local_filename.c_str(), &file_stat) != 0)
|
||||
{
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
@@ -3016,6 +3030,32 @@ namespace
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtReleaseSemaphore(const syscall_context& c, const handle semaphore_handle,
|
||||
const ULONG release_count, const emulator_object<LONG> previous_count)
|
||||
{
|
||||
if (semaphore_handle.value.type != handle_types::semaphore)
|
||||
{
|
||||
c.win_emu.log.error("Bad handle type for NtReleaseSemaphore\n");
|
||||
c.emu.stop();
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
auto* mutant = c.proc.semaphores.get(semaphore_handle);
|
||||
if (!mutant)
|
||||
{
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
const auto [old_count, succeeded] = mutant->release(release_count);
|
||||
|
||||
if (previous_count)
|
||||
{
|
||||
previous_count.write(static_cast<LONG>(old_count));
|
||||
}
|
||||
|
||||
return succeeded ? STATUS_SUCCESS : STATUS_SEMAPHORE_LIMIT_EXCEEDED;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtCreateSemaphore(const syscall_context& c, const emulator_object<handle> semaphore_handle,
|
||||
const ACCESS_MASK /*desired_access*/,
|
||||
const emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> object_attributes,
|
||||
@@ -3037,10 +3077,12 @@ namespace
|
||||
|
||||
if (!s.name.empty())
|
||||
{
|
||||
for (const auto& semaphore : c.proc.semaphores | std::views::values)
|
||||
for (auto& entry : c.proc.semaphores)
|
||||
{
|
||||
if (semaphore.name == s.name)
|
||||
if (entry.second.name == s.name)
|
||||
{
|
||||
++entry.second.ref_count;
|
||||
semaphore_handle.write(c.proc.semaphores.make_handle(entry.first));
|
||||
return STATUS_OBJECT_NAME_EXISTS;
|
||||
}
|
||||
}
|
||||
@@ -3216,8 +3258,9 @@ namespace
|
||||
|
||||
bool is_awaitable_object_type(const handle h)
|
||||
{
|
||||
return h.value.type == handle_types::thread //
|
||||
|| h.value.type == handle_types::mutant //
|
||||
return h.value.type == handle_types::thread //
|
||||
|| h.value.type == handle_types::mutant //
|
||||
|| h.value.type == handle_types::semaphore //
|
||||
|| h.value.type == handle_types::event;
|
||||
}
|
||||
|
||||
@@ -3531,6 +3574,7 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
|
||||
add_handler(NtUserModifyUserStartupInfoFlags);
|
||||
add_handler(NtUserGetDCEx);
|
||||
add_handler(NtUserGetDpiForCurrentProcess);
|
||||
add_handler(NtReleaseSemaphore);
|
||||
|
||||
#undef add_handler
|
||||
}
|
||||
|
||||
@@ -145,13 +145,13 @@ namespace
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> obtain_api_set(apiset_location location)
|
||||
std::vector<uint8_t> obtain_api_set(const apiset_location location, const std::filesystem::path& root)
|
||||
{
|
||||
switch (location)
|
||||
{
|
||||
#ifdef OS_WINDOWS
|
||||
case apiset_location::host: {
|
||||
auto apiSetMap =
|
||||
const auto apiSetMap =
|
||||
reinterpret_cast<const API_SET_NAMESPACE*>(NtCurrentTeb64()->ProcessEnvironmentBlock->ApiSetMap);
|
||||
const auto* dataPtr = reinterpret_cast<const uint8_t*>(apiSetMap);
|
||||
std::vector<uint8_t> buffer(dataPtr, dataPtr + apiSetMap->Size);
|
||||
@@ -162,7 +162,7 @@ namespace
|
||||
throw std::runtime_error("The APISET host location is not supported on this platform");
|
||||
#endif
|
||||
case apiset_location::file: {
|
||||
auto apiset = utils::io::read_file("api-set.bin");
|
||||
const auto apiset = utils::io::read_file(root / "api-set.bin");
|
||||
if (apiset.empty())
|
||||
throw std::runtime_error("Failed to read file api-set.bin");
|
||||
return decompress_apiset(apiset);
|
||||
@@ -181,10 +181,11 @@ namespace
|
||||
}
|
||||
|
||||
emulator_object<API_SET_NAMESPACE> build_api_set_map(x64_emulator& emu, emulator_allocator& allocator,
|
||||
apiset_location location = apiset_location::host)
|
||||
const apiset_location location = apiset_location::host,
|
||||
const std::filesystem::path& root = {})
|
||||
{
|
||||
return clone_api_set_map(emu, allocator,
|
||||
reinterpret_cast<const API_SET_NAMESPACE&>(*obtain_api_set(location).data()));
|
||||
reinterpret_cast<const API_SET_NAMESPACE&>(*obtain_api_set(location, root).data()));
|
||||
}
|
||||
|
||||
emulator_allocator create_allocator(emulator& emu, const size_t size)
|
||||
@@ -208,11 +209,6 @@ namespace
|
||||
emu.reg<uint16_t>(x64_register::ss, 0x2B);
|
||||
}
|
||||
|
||||
std::filesystem::path canonicalize_path(const std::filesystem::path& path)
|
||||
{
|
||||
return canonical(absolute(path)).make_preferred();
|
||||
}
|
||||
|
||||
void setup_context(windows_emulator& win_emu, const emulator_settings& settings)
|
||||
{
|
||||
auto& emu = win_emu.emu();
|
||||
@@ -220,7 +216,9 @@ namespace
|
||||
|
||||
setup_gdt(emu);
|
||||
|
||||
context.registry = registry_manager(settings.registry_directory);
|
||||
context.registry =
|
||||
registry_manager(win_emu.get_emulation_root().empty() ? settings.registry_directory
|
||||
: win_emu.get_emulation_root() / "registry");
|
||||
|
||||
context.kusd.setup(settings.use_relative_time);
|
||||
|
||||
@@ -260,7 +258,9 @@ namespace
|
||||
allocator.copy_string(u"SystemRoot=C:\\WINDOWS");
|
||||
allocator.copy_string(u"");
|
||||
|
||||
std::u16string command_line = u"\"" + settings.application.u16string() + u"\"";
|
||||
const auto application_str = settings.application.u16string();
|
||||
|
||||
std::u16string command_line = u"\"" + application_str + u"\"";
|
||||
|
||||
for (const auto& arg : settings.arguments)
|
||||
{
|
||||
@@ -268,20 +268,10 @@ namespace
|
||||
command_line.append(arg);
|
||||
}
|
||||
|
||||
std::u16string current_folder{};
|
||||
if (!settings.working_directory.empty())
|
||||
{
|
||||
current_folder = canonicalize_path(settings.working_directory).u16string() + u"\\";
|
||||
}
|
||||
else
|
||||
{
|
||||
current_folder = canonicalize_path(settings.application).parent_path().u16string() + u"\\";
|
||||
}
|
||||
|
||||
allocator.make_unicode_string(proc_params.CommandLine, command_line);
|
||||
allocator.make_unicode_string(proc_params.CurrentDirectory.DosPath, current_folder);
|
||||
allocator.make_unicode_string(proc_params.ImagePathName,
|
||||
canonicalize_path(settings.application).u16string());
|
||||
allocator.make_unicode_string(proc_params.CurrentDirectory.DosPath,
|
||||
win_emu.file_sys().get_working_directory().u16string());
|
||||
allocator.make_unicode_string(proc_params.ImagePathName, application_str);
|
||||
|
||||
const auto total_length = allocator.get_next_address() - context.process_params.value();
|
||||
|
||||
@@ -290,17 +280,21 @@ namespace
|
||||
proc_params.MaximumLength = proc_params.Length;
|
||||
});
|
||||
|
||||
// TODO: make this configurable
|
||||
apiset_location apiset_loc = apiset_location::file;
|
||||
|
||||
if (win_emu.get_emulation_root().empty())
|
||||
{
|
||||
#ifdef OS_WINDOWS
|
||||
apiset_location apiset_loc = apiset_location::host;
|
||||
apiset_loc = apiset_location::host;
|
||||
#else
|
||||
apiset_location apiset_loc = apiset_location::default_windows_11;
|
||||
apiset_loc = apiset_location::default_windows_11;
|
||||
#endif
|
||||
}
|
||||
|
||||
context.peb.access([&](PEB64& peb) {
|
||||
peb.ImageBaseAddress = nullptr;
|
||||
peb.ProcessParameters = context.process_params.ptr();
|
||||
peb.ApiSetMap = build_api_set_map(emu, allocator, apiset_loc).ptr();
|
||||
peb.ApiSetMap = build_api_set_map(emu, allocator, apiset_loc, win_emu.get_emulation_root()).ptr();
|
||||
|
||||
peb.ProcessHeap = nullptr;
|
||||
peb.ProcessHeaps = nullptr;
|
||||
@@ -634,6 +628,11 @@ namespace
|
||||
break;
|
||||
|
||||
case handle_types::event: {
|
||||
if (h.value.is_pseudo)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* e = c.events.get(h);
|
||||
if (e)
|
||||
{
|
||||
@@ -653,6 +652,16 @@ namespace
|
||||
break;
|
||||
}
|
||||
|
||||
case handle_types::semaphore: {
|
||||
auto* s = c.semaphores.get(h);
|
||||
if (s)
|
||||
{
|
||||
return s->try_lock();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case handle_types::thread: {
|
||||
const auto* t = c.threads.get(h);
|
||||
if (t)
|
||||
@@ -816,10 +825,19 @@ std::unique_ptr<x64_emulator> create_default_x64_emulator()
|
||||
return unicorn::create_x64_emulator();
|
||||
}
|
||||
|
||||
windows_emulator::windows_emulator(emulator_settings settings, emulator_callbacks callbacks,
|
||||
windows_emulator::windows_emulator(const emulator_settings& settings, emulator_callbacks callbacks,
|
||||
std::unique_ptr<x64_emulator> emu)
|
||||
: windows_emulator(std::move(emu))
|
||||
: windows_emulator(settings.emulation_root, std::move(emu))
|
||||
{
|
||||
if (!settings.working_directory.empty())
|
||||
{
|
||||
this->file_sys().set_working_directory(settings.working_directory);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->file_sys().set_working_directory(settings.application.parent());
|
||||
}
|
||||
|
||||
this->silent_until_main_ = settings.silent_until_main && !settings.disable_logging;
|
||||
this->use_relative_time_ = settings.use_relative_time;
|
||||
this->log.disable_output(settings.disable_logging || this->silent_until_main_);
|
||||
@@ -827,10 +845,19 @@ windows_emulator::windows_emulator(emulator_settings settings, emulator_callback
|
||||
this->setup_process(settings);
|
||||
}
|
||||
|
||||
windows_emulator::windows_emulator(std::unique_ptr<x64_emulator> emu)
|
||||
: emu_(std::move(emu)),
|
||||
process_(*emu_)
|
||||
windows_emulator::windows_emulator(const std::filesystem::path& emulation_root, std::unique_ptr<x64_emulator> emu)
|
||||
: emulation_root_{emulation_root.empty() ? emulation_root : absolute(emulation_root)},
|
||||
file_sys_(emulation_root_.empty() ? emulation_root_ : emulation_root_ / "filesys"),
|
||||
emu_(std::move(emu)),
|
||||
process_(*emu_, file_sys_)
|
||||
{
|
||||
#ifndef OS_WINDOWS
|
||||
if (this->get_emulation_root().empty())
|
||||
{
|
||||
throw std::runtime_error("Emulation root directory can not be empty!");
|
||||
}
|
||||
#endif
|
||||
|
||||
this->setup_hooks();
|
||||
}
|
||||
|
||||
@@ -839,14 +866,15 @@ void windows_emulator::setup_process(const emulator_settings& settings)
|
||||
auto& emu = this->emu();
|
||||
|
||||
auto& context = this->process();
|
||||
context.mod_manager = module_manager(emu); // TODO: Cleanup module manager
|
||||
context.mod_manager = module_manager(emu, this->file_sys()); // TODO: Cleanup module manager
|
||||
|
||||
setup_context(*this, settings);
|
||||
|
||||
context.executable = context.mod_manager.map_module(settings.application, this->log);
|
||||
|
||||
context.peb.access(
|
||||
[&](PEB64& peb) { peb.ImageBaseAddress = reinterpret_cast<std::uint64_t*>(context.executable->image_base); });
|
||||
context.peb.access([&](PEB64& peb) {
|
||||
peb.ImageBaseAddress = reinterpret_cast<std::uint64_t*>(context.executable->image_base); //
|
||||
});
|
||||
|
||||
context.ntdll = context.mod_manager.map_module(R"(C:\Windows\System32\ntdll.dll)", this->log);
|
||||
context.win32u = context.mod_manager.map_module(R"(C:\Windows\System32\win32u.dll)", this->log);
|
||||
@@ -930,9 +958,13 @@ void windows_emulator::on_instruction_execution(const uint64_t address)
|
||||
const auto export_entry = binary->address_names.find(address);
|
||||
if (export_entry != binary->address_names.end())
|
||||
{
|
||||
const auto rsp = this->emu().read_stack_pointer();
|
||||
const auto return_address = this->emu().read_memory<uint64_t>(rsp);
|
||||
const auto* mod_name = this->process().mod_manager.find_name(return_address);
|
||||
|
||||
log.print(is_interesting_call ? color::yellow : color::dark_gray,
|
||||
"Executing function: %s - %s (0x%" PRIx64 ")\n", binary->name.c_str(),
|
||||
export_entry->second.c_str(), address);
|
||||
"Executing function: %s - %s (0x%" PRIx64 ") via (0x%" PRIx64 ") %s\n", binary->name.c_str(),
|
||||
export_entry->second.c_str(), address, return_address, mod_name);
|
||||
}
|
||||
else if (address == binary->entry_point)
|
||||
{
|
||||
@@ -1096,6 +1128,7 @@ void windows_emulator::start(std::chrono::nanoseconds timeout, size_t count)
|
||||
void windows_emulator::serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->use_relative_time_);
|
||||
this->file_sys().serialize(buffer);
|
||||
this->emu().serialize(buffer);
|
||||
this->process_.serialize(buffer);
|
||||
this->dispatcher_.serialize(buffer);
|
||||
@@ -1103,11 +1136,16 @@ void windows_emulator::serialize(utils::buffer_serializer& buffer) const
|
||||
|
||||
void windows_emulator::deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.register_factory<x64_emulator_wrapper>([this] { return x64_emulator_wrapper{this->emu()}; });
|
||||
buffer.register_factory<x64_emulator_wrapper>([this] {
|
||||
return x64_emulator_wrapper{this->emu()}; //
|
||||
});
|
||||
|
||||
buffer.register_factory<windows_emulator_wrapper>([this] { return windows_emulator_wrapper{*this}; });
|
||||
buffer.register_factory<windows_emulator_wrapper>([this] {
|
||||
return windows_emulator_wrapper{*this}; //
|
||||
});
|
||||
|
||||
buffer.read(this->use_relative_time_);
|
||||
this->file_sys().deserialize(buffer);
|
||||
|
||||
this->emu().deserialize(buffer);
|
||||
this->process_.deserialize(buffer);
|
||||
@@ -1119,6 +1157,7 @@ void windows_emulator::save_snapshot()
|
||||
this->emu().save_snapshot();
|
||||
|
||||
utils::buffer_serializer serializer{};
|
||||
this->file_sys().serialize(serializer);
|
||||
this->process_.serialize(serializer);
|
||||
|
||||
this->process_snapshot_ = serializer.move_buffer();
|
||||
@@ -1138,6 +1177,7 @@ void windows_emulator::restore_snapshot()
|
||||
this->emu().restore_snapshot();
|
||||
|
||||
utils::buffer_deserializer deserializer{this->process_snapshot_};
|
||||
this->file_sys().deserialize(deserializer);
|
||||
this->process_.deserialize(deserializer);
|
||||
// this->process_ = *this->process_snapshot_;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "syscall_dispatcher.hpp"
|
||||
#include "process_context.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "file_system.hpp"
|
||||
|
||||
std::unique_ptr<x64_emulator> create_default_x64_emulator();
|
||||
|
||||
@@ -26,16 +27,17 @@ struct emulator_callbacks
|
||||
// TODO: Split up into application and emulator settings
|
||||
struct emulator_settings
|
||||
{
|
||||
std::filesystem::path application{};
|
||||
std::filesystem::path working_directory{};
|
||||
windows_path application{};
|
||||
windows_path working_directory{};
|
||||
std::filesystem::path registry_directory{"./registry"};
|
||||
std::filesystem::path emulation_root{};
|
||||
std::vector<std::u16string> arguments{};
|
||||
bool disable_logging{false};
|
||||
bool silent_until_main{false};
|
||||
bool use_relative_time{false};
|
||||
};
|
||||
|
||||
enum class apiset_location
|
||||
enum class apiset_location : uint8_t
|
||||
{
|
||||
host,
|
||||
file,
|
||||
@@ -46,8 +48,9 @@ enum class apiset_location
|
||||
class windows_emulator
|
||||
{
|
||||
public:
|
||||
windows_emulator(std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
|
||||
windows_emulator(emulator_settings settings, emulator_callbacks callbacks = {},
|
||||
windows_emulator(const std::filesystem::path& emulation_root,
|
||||
std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
|
||||
windows_emulator(const emulator_settings& settings, emulator_callbacks callbacks = {},
|
||||
std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
|
||||
|
||||
windows_emulator(windows_emulator&&) = delete;
|
||||
@@ -131,7 +134,25 @@ class windows_emulator
|
||||
return this->callbacks_;
|
||||
}
|
||||
|
||||
file_system& file_sys()
|
||||
{
|
||||
return this->file_sys_;
|
||||
}
|
||||
|
||||
const file_system& file_sys() const
|
||||
{
|
||||
return this->file_sys_;
|
||||
}
|
||||
|
||||
const std::filesystem::path& get_emulation_root()
|
||||
{
|
||||
return this->emulation_root_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::filesystem::path emulation_root_{};
|
||||
file_system file_sys_;
|
||||
|
||||
emulator_callbacks callbacks_{};
|
||||
bool use_relative_time_{false};
|
||||
bool silent_until_main_{false};
|
||||
|
||||
236
src/windows-emulator/windows_path.hpp
Normal file
236
src/windows-emulator/windows_path.hpp
Normal file
@@ -0,0 +1,236 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <optional>
|
||||
#include <filesystem>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include <serialization.hpp>
|
||||
|
||||
namespace windows_path_detail
|
||||
{
|
||||
constexpr std::u16string_view unc_prefix = u"\\??\\";
|
||||
|
||||
inline std::u16string_view strip_unc_prefix(const std::u16string_view path)
|
||||
{
|
||||
if (!path.starts_with(unc_prefix))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
return path.substr(unc_prefix.size());
|
||||
}
|
||||
|
||||
inline bool is_slash(const char16_t chr)
|
||||
{
|
||||
return chr == u'\\' || chr == u'/';
|
||||
}
|
||||
}
|
||||
|
||||
class windows_path
|
||||
{
|
||||
public:
|
||||
windows_path() = default;
|
||||
|
||||
windows_path(const std::filesystem::path& path)
|
||||
{
|
||||
const auto full_path = path.u16string();
|
||||
const auto canonical_path = windows_path_detail::strip_unc_prefix(full_path);
|
||||
|
||||
std::u16string folder{};
|
||||
|
||||
for (const auto chr : canonical_path)
|
||||
{
|
||||
if (chr == u':' && this->folders_.empty() && !this->drive_.has_value() && folder.size() == 1)
|
||||
{
|
||||
this->drive_ = static_cast<char>(folder[0]);
|
||||
folder.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (windows_path_detail::is_slash(chr))
|
||||
{
|
||||
if (folder.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
this->folders_.push_back(std::move(folder));
|
||||
folder = {};
|
||||
continue;
|
||||
}
|
||||
|
||||
folder.push_back(chr);
|
||||
}
|
||||
|
||||
if (!folder.empty())
|
||||
{
|
||||
this->folders_.push_back(folder);
|
||||
}
|
||||
|
||||
this->canonicalize();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires(!std::is_same_v<T, windows_path> && !std::is_same_v<T, std::filesystem::path>)
|
||||
windows_path(T&& path_like)
|
||||
: windows_path(std::filesystem::path(std::forward<T>(path_like)))
|
||||
{
|
||||
}
|
||||
|
||||
windows_path(const std::optional<char> drive, std::list<std::u16string> folders)
|
||||
: drive_(drive),
|
||||
folders_(std::move(folders))
|
||||
{
|
||||
this->canonicalize();
|
||||
}
|
||||
|
||||
bool is_absolute() const
|
||||
{
|
||||
return this->drive_.has_value();
|
||||
}
|
||||
|
||||
bool is_relative() const
|
||||
{
|
||||
return !this->is_absolute();
|
||||
}
|
||||
|
||||
std::u16string u16string() const
|
||||
{
|
||||
std::u16string path{};
|
||||
if (this->drive_)
|
||||
{
|
||||
path.push_back(static_cast<char16_t>(*this->drive_));
|
||||
path.push_back(u':');
|
||||
}
|
||||
|
||||
for (const auto& folder : this->folders_)
|
||||
{
|
||||
if (!path.empty())
|
||||
{
|
||||
path.push_back(u'\\');
|
||||
}
|
||||
|
||||
path.append(folder);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string string() const
|
||||
{
|
||||
return u16_to_u8(this->u16string());
|
||||
}
|
||||
|
||||
std::u16string to_unc_path() const
|
||||
{
|
||||
if (this->is_relative())
|
||||
{
|
||||
return this->u16string();
|
||||
}
|
||||
|
||||
return std::u16string(windows_path_detail::unc_prefix) + this->u16string();
|
||||
}
|
||||
|
||||
std::filesystem::path to_portable_path() const
|
||||
{
|
||||
std::u16string path{};
|
||||
if (this->drive_)
|
||||
{
|
||||
path.push_back(static_cast<char16_t>(*this->drive_));
|
||||
}
|
||||
|
||||
for (const auto& folder : this->folders_)
|
||||
{
|
||||
if (!path.empty())
|
||||
{
|
||||
path.push_back(u'/');
|
||||
}
|
||||
|
||||
path.append(folder);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
windows_path operator/(const windows_path& path) const
|
||||
{
|
||||
if (path.is_absolute())
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
auto folders = this->folders_;
|
||||
|
||||
for (const auto& folder : path.folders_)
|
||||
{
|
||||
folders.push_back(folder);
|
||||
}
|
||||
|
||||
return {this->drive_, std::move(folders)};
|
||||
}
|
||||
|
||||
windows_path& operator/=(const windows_path& path)
|
||||
{
|
||||
*this = *this / path;
|
||||
return *this;
|
||||
}
|
||||
|
||||
windows_path parent() const
|
||||
{
|
||||
auto folders = this->folders_;
|
||||
if (!folders.empty())
|
||||
{
|
||||
folders.pop_back();
|
||||
}
|
||||
|
||||
return {this->drive_, std::move(folders)};
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write_optional(this->drive_);
|
||||
buffer.write_list(this->folders_);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read_optional(this->drive_);
|
||||
buffer.read_list(this->folders_);
|
||||
}
|
||||
|
||||
bool operator==(const windows_path& other) const
|
||||
{
|
||||
return this->drive_ == other.drive_ && this->folders_ == other.folders_;
|
||||
}
|
||||
|
||||
bool operator!=(const windows_path& other) const
|
||||
{
|
||||
return !this->operator==(other);
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return !this->is_relative() && this->folders_.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<char> drive_{};
|
||||
std::list<std::u16string> folders_{};
|
||||
|
||||
void canonicalize()
|
||||
{
|
||||
if (this->drive_.has_value())
|
||||
{
|
||||
this->drive_ = utils::string::char_to_lower(*this->drive_);
|
||||
}
|
||||
|
||||
for (auto& folder : this->folders_)
|
||||
{
|
||||
for (auto& chr : folder)
|
||||
{
|
||||
chr = utils::string::char_to_lower(chr);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user