mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-09 15:46:17 +00:00
Merge remote-tracking branch 'origin/main' into unicorn-upgrade-2
This commit is contained in:
@@ -14,6 +14,7 @@ SortIncludes: false
|
||||
AlignEscapedNewlines: Left
|
||||
PackConstructorInitializers: Never
|
||||
IndentPPDirectives: None
|
||||
ColumnLimit: 140
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: true
|
||||
AcrossEmptyLines: true
|
||||
|
||||
60
.github/workflows/build.yml
vendored
60
.github/workflows/build.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
LLVM_VERSION: 20
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Verify Formatting
|
||||
uses: jidicula/clang-format-action@v4.15.0
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -120,7 +120,7 @@ jobs:
|
||||
# runner: windows-2019
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Download DirectX Runtime
|
||||
run: curl --connect-timeout 20 --max-time 200 --retry 5 --retry-delay 2 --retry-max-time 200 -L -o directx_Jun2010_redist.exe https://download.microsoft.com/download/8/4/A/84A35BF1-DAFE-4AE8-82AF-AD2AE20B6B14/directx_Jun2010_redist.exe
|
||||
@@ -132,7 +132,7 @@ jobs:
|
||||
run: "cmd /c \"start /wait .\\dxrt\\dxsetup.exe /silent\""
|
||||
|
||||
- name: Download API Set Dumper
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: Temp API Set Dumper
|
||||
path: build/release/artifacts
|
||||
@@ -227,7 +227,7 @@ jobs:
|
||||
cmake-options: "-DMOMO_EMSCRIPTEN_SUPPORT_NODEJS=On -DMOMO_ENABLE_RUST_CODE=Off -DCMAKE_TOOLCHAIN_FILE=$(dirname $(which emcc))/cmake/Modules/Platform/Emscripten.cmake"
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -322,7 +322,7 @@ jobs:
|
||||
devcmd_arch: x64
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -399,7 +399,7 @@ jobs:
|
||||
runner: macos-13
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -411,26 +411,26 @@ jobs:
|
||||
echo "EMULATOR_ICICLE=${{ matrix.emulator == 'Icicle' }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Download Test Configuration
|
||||
uses: actions/download-artifact@v4.3.0
|
||||
uses: actions/download-artifact@v5.0.0
|
||||
with:
|
||||
name: Temp ${{ matrix.platform }} ${{matrix.configuration}} Test Config
|
||||
path: build/${{matrix.preset}}
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: ${{ matrix.platform }} ${{matrix.configuration}} Artifacts
|
||||
path: build/${{matrix.preset}}/artifacts
|
||||
|
||||
- name: Download Windows Artifacts
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
if: "${{ matrix.platform != 'Windows x86_64' }}"
|
||||
with:
|
||||
name: Windows x86_64 Release Artifacts
|
||||
path: build/${{matrix.preset}}/artifacts
|
||||
|
||||
- name: Download Emulation Root
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: ${{ matrix.emulation-root }} Emulation Root
|
||||
path: build/${{matrix.preset}}/artifacts/root
|
||||
@@ -470,7 +470,7 @@ jobs:
|
||||
preset: release
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -482,19 +482,19 @@ jobs:
|
||||
echo "EMULATOR_ICICLE=${{ matrix.emulator == 'Icicle' }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Download Test Configuration
|
||||
uses: actions/download-artifact@v4.3.0
|
||||
uses: actions/download-artifact@v5.0.0
|
||||
with:
|
||||
name: Temp Isolate Windows x86_64 ${{ matrix.configuration}} Test Config
|
||||
path: build/${{ matrix.preset }}
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: Temp Isolate Windows x86_64 ${{ matrix.configuration}} Artifacts
|
||||
path: build/${{ matrix.preset }}/artifacts
|
||||
|
||||
- name: Download Emulation Root
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: ${{ matrix.emulation-root }} Emulation Root
|
||||
path: build/${{ matrix.preset }}/artifacts/root
|
||||
@@ -515,19 +515,19 @@ jobs:
|
||||
needs: [create-emulation-root, build]
|
||||
steps:
|
||||
- name: Download Artifacts
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: Emscripten Node.js Release Artifacts
|
||||
path: build/release/artifacts
|
||||
|
||||
- name: Download Windows Artifacts
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: Windows x86_64 Release Artifacts
|
||||
path: build/release/artifacts
|
||||
|
||||
- name: Download Emulation Root
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: Windows 2022 Emulation Root
|
||||
path: build/release/artifacts/root
|
||||
@@ -547,13 +547,13 @@ jobs:
|
||||
needs: [create-emulation-root, build]
|
||||
steps:
|
||||
- name: Download Artifacts
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: MinGW x86_64 Release Artifacts
|
||||
path: build/release/artifacts
|
||||
|
||||
- name: Download Emulation Root
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: Windows 2022 Emulation Root
|
||||
path: build/release/artifacts/root
|
||||
@@ -605,19 +605,19 @@ jobs:
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: Android ${{matrix.architecture}} ${{matrix.configuration}} Artifacts
|
||||
path: build/${{matrix.preset}}/artifacts
|
||||
|
||||
- name: Download Windows Artifacts
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: Windows x86_64 Release Artifacts
|
||||
path: build/${{matrix.preset}}/artifacts
|
||||
|
||||
- name: Download Emulation Root
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: ${{ matrix.emulation-root }} Emulation Root
|
||||
path: build/${{matrix.preset}}/artifacts/root
|
||||
@@ -638,30 +638,30 @@ jobs:
|
||||
needs: [create-emulation-root, build]
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Download Emscripten Web Artifacts
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: Emscripten Web Release Artifacts
|
||||
path: build/release/artifacts/32
|
||||
|
||||
- name: Download Emscripten Web Memory 64 Artifacts
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: Emscripten Web Memory 64 Release Artifacts
|
||||
path: build/release/artifacts/64
|
||||
|
||||
- name: Download Windows Artifacts
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: Windows x86_64 Release Artifacts
|
||||
path: build/release/artifacts
|
||||
|
||||
- name: Download Emulation Root
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: Windows 2022 Emulation Root
|
||||
path: build/release/artifacts/root
|
||||
@@ -703,7 +703,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Download Page Artifacts
|
||||
uses: pyTooling/download-artifact@v4
|
||||
uses: pyTooling/download-artifact@v5
|
||||
with:
|
||||
name: Page Artifacts
|
||||
path: ./page/dist/
|
||||
|
||||
5
.gitmodules
vendored
5
.gitmodules
vendored
@@ -33,4 +33,7 @@
|
||||
path = deps/minidump_cpp
|
||||
url = https://github.com/redthing1/minidump_cpp
|
||||
shallow = true
|
||||
|
||||
[submodule "deps/capstone"]
|
||||
path = deps/capstone
|
||||
url = https://github.com/capstone-engine/capstone.git
|
||||
shallow = true
|
||||
@@ -8,7 +8,7 @@ option(MOMO_ENABLE_CLANG_TIDY "Enable clang-tidy checks" OFF)
|
||||
option(MOMO_ENABLE_RUST_CODE "Enable code parts written in rust" ON)
|
||||
option(MOMO_EMSCRIPTEN_MEMORY64 "Enable memory 64 support for emscripten builds" OFF)
|
||||
option(MOMO_EMSCRIPTEN_SUPPORT_NODEJS "Enable Node.js filesystem for emscripten compilation" OFF)
|
||||
option(MOMO_BUILD_AS_LIBRARY "Configure and Build the emulator as a shared library (without the samples and tests)" OFF)
|
||||
option(MOMO_BUILD_AS_LIBRARY "Configure and Build the sogen as a shared library (without the samples and tests)" OFF)
|
||||
|
||||
set(MOMO_REFLECTION_LEVEL "0" CACHE STRING "Reflection level for the build")
|
||||
message(STATUS "Reflection level is set to: ${MOMO_REFLECTION_LEVEL}")
|
||||
@@ -32,7 +32,7 @@ set(ENV{ARCHFLAGS} "nope")
|
||||
|
||||
##########################################
|
||||
|
||||
project(emulator LANGUAGES C CXX)
|
||||
project(sogen LANGUAGES C CXX)
|
||||
enable_testing()
|
||||
|
||||
##########################################
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<img src="./docs/images/cover.png" />
|
||||
<h1 align="center">
|
||||
Sogen
|
||||
<img src="https://momo5502.com/emulator/banner.png" height="200" />
|
||||
<br>
|
||||
<a href="https://github.com/momo5502/sogen?tab=GPL-2.0-1-ov-file"><img src="https://img.shields.io/github/license/momo5502/sogen?color=00B0F8"/></a>
|
||||
<a href="https://github.com/momo5502/sogen/actions"><img src="https://img.shields.io/github/actions/workflow/status/momo5502/sogen/build.yml?branch=main&label=build"/></a>
|
||||
@@ -34,10 +33,6 @@ Try it out: <a href="https://sogen.dev">sogen.dev</a>
|
||||
* 💻 __Debugging Interface__
|
||||
* Implements GDB serial protocol for integration with common debugging tools (IDA Pro, GDB, LLDB, VS Code, ...)
|
||||
|
||||
##
|
||||
> [!NOTE]
|
||||
> The project is still in a very early, prototypical state. The code still needs a lot of cleanup and many features and syscalls need to be implemented. However, constant progress is being made :)
|
||||
|
||||
## Preview
|
||||
|
||||

|
||||
|
||||
10
deps/CMakeLists.txt
vendored
10
deps/CMakeLists.txt
vendored
@@ -29,5 +29,15 @@ add_subdirectory(minidump_cpp)
|
||||
|
||||
##########################################
|
||||
|
||||
|
||||
option(CAPSTONE_BUILD_MACOS_THIN "" ON)
|
||||
option(CAPSTONE_X86_SUPPORT "" ON)
|
||||
option(CAPSTONE_X86_ATT_DISABLE "" ON)
|
||||
option(CAPSTONE_ARCHITECTURE_DEFAULT "" OFF)
|
||||
option(CAPSTONE_BUILD_STATIC_MSVC_RUNTIME "" OFF)
|
||||
add_subdirectory(capstone)
|
||||
|
||||
##########################################
|
||||
|
||||
include(googletest.cmake)
|
||||
include(zlib.cmake)
|
||||
|
||||
1
deps/capstone
vendored
Submodule
1
deps/capstone
vendored
Submodule
Submodule deps/capstone added at 2f85e932c1
2
deps/flatbuffers
vendored
2
deps/flatbuffers
vendored
Submodule deps/flatbuffers updated: 64e5252b4e...067bfdbde9
2
deps/googletest
vendored
2
deps/googletest
vendored
Submodule deps/googletest updated: 3983f67e32...a05c091507
2
deps/gtest-parallel
vendored
2
deps/gtest-parallel
vendored
Submodule deps/gtest-parallel updated: 96f4f90492...cd488bdedc
2
deps/reflect
vendored
2
deps/reflect
vendored
Submodule deps/reflect updated: 239f695545...c21a44839d
2
deps/unicorn
vendored
2
deps/unicorn
vendored
Submodule deps/unicorn updated: 4b7b89432f...f5108b33a5
9
mcp/package-lock.json
generated
9
mcp/package-lock.json
generated
@@ -9,13 +9,14 @@
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.13.3"
|
||||
"@modelcontextprotocol/sdk": "^1.17.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk": {
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.13.3.tgz",
|
||||
"integrity": "sha512-bGwA78F/U5G2jrnsdRkPY3IwIwZeWUEfb5o764b79lb0rJmMT76TLwKhdNZOWakOQtedYefwIR4emisEMvInKA==",
|
||||
"version": "1.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.3.tgz",
|
||||
"integrity": "sha512-JPwUKWSsbzx+DLFznf/QZ32Qa+ptfbUlHhRLrBQBAFu9iI1iYvizM4p+zhhRDceSsPutXp4z+R/HPVphlIiclg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.6",
|
||||
"content-type": "^1.0.5",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"dev": "node --watch server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.13.3"
|
||||
"@modelcontextprotocol/sdk": "^1.17.3"
|
||||
},
|
||||
"keywords": ["mcp", "server"],
|
||||
"author": "",
|
||||
|
||||
1820
page/package-lock.json
generated
1820
page/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,53 +12,55 @@
|
||||
"dependencies": {
|
||||
"@fontsource/inter": "^5.2.6",
|
||||
"@irori/idbfs": "^0.5.1",
|
||||
"@radix-ui/react-checkbox": "^1.3.2",
|
||||
"@radix-ui/react-context-menu": "^2.2.15",
|
||||
"@radix-ui/react-dialog": "^1.1.14",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-context-menu": "^2.2.16",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-popover": "^1.1.14",
|
||||
"@radix-ui/react-scroll-area": "^1.2.9",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||
"@radix-ui/react-separator": "^1.1.7",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-tabs": "^1.1.12",
|
||||
"@radix-ui/react-tooltip": "^1.2.7",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@tailwindcss/vite": "^4.1.12",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"flatbuffers": "^25.2.10",
|
||||
"jszip": "^3.10.1",
|
||||
"lucide-react": "^0.525.0",
|
||||
"lucide-react": "^0.540.0",
|
||||
"pe-library": "^1.0.1",
|
||||
"react": "^19.0.0",
|
||||
"react": "^19.1.1",
|
||||
"react-bootstrap-icons": "^1.11.6",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-dropzone": "^14.3.8",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-resizable-panels": "^3.0.3",
|
||||
"react-router-dom": "^7.6.3",
|
||||
"react-resizable-panels": "^3.0.4",
|
||||
"react-router-dom": "^7.8.1",
|
||||
"react-window": "^1.8.11",
|
||||
"shell-quote": "^1.8.3",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"tw-animate-css": "^1.3.4",
|
||||
"tailwindcss": "^4.1.12",
|
||||
"tw-animate-css": "^1.3.7",
|
||||
"vaul": "^1.1.2",
|
||||
"wasm-feature-detect": "^1.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.30.0",
|
||||
"@types/node": "^24.0.8",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@types/node": "^24.3.0",
|
||||
"@types/react": "^19.1.10",
|
||||
"@types/react-dom": "^19.1.7",
|
||||
"@types/react-helmet": "^6.1.11",
|
||||
"@vitejs/plugin-react": "^4.6.0",
|
||||
"eslint": "^9.30.0",
|
||||
"@types/shell-quote": "^1.7.5",
|
||||
"@vitejs/plugin-react": "^5.0.1",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint-plugin-react-hooks": "^6.0.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.20",
|
||||
"globals": "^16.3.0",
|
||||
"prettier": "3.6.2",
|
||||
"typescript": "~5.8.3",
|
||||
"typescript-eslint": "^8.35.1",
|
||||
"vite": "^7.0.0"
|
||||
"typescript-eslint": "^8.40.0",
|
||||
"vite": "^7.1.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ onmessage = async (event) => {
|
||||
runEmulation(
|
||||
payload.file,
|
||||
payload.options,
|
||||
payload.arguments,
|
||||
payload.persist,
|
||||
payload.wasm64,
|
||||
payload.cacheBuster,
|
||||
@@ -71,8 +72,15 @@ function getMessageFromQueue() {
|
||||
return msgQueue.shift();
|
||||
}
|
||||
|
||||
function runEmulation(file, options, persist, wasm64, cacheBuster) {
|
||||
const mainArguments = [...options, "-e", "./root", file];
|
||||
function runEmulation(
|
||||
file,
|
||||
options,
|
||||
appArguments,
|
||||
persist,
|
||||
wasm64,
|
||||
cacheBuster,
|
||||
) {
|
||||
const mainArguments = [...options, "-e", "./root", file, ...appArguments];
|
||||
|
||||
globalThis.Module = {
|
||||
arguments: mainArguments,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import { HashRouter, Route, Routes, Navigate } from "react-router-dom";
|
||||
import { Playground } from "./playground";
|
||||
import { Playground, PlaygroundFile, storeEmulateData } from "./playground";
|
||||
import { LandingPage } from "./landing-page";
|
||||
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import "@fontsource/inter/100.css";
|
||||
import "@fontsource/inter/200.css";
|
||||
import "@fontsource/inter/300.css";
|
||||
@@ -16,6 +18,12 @@ import "@fontsource/inter/900.css";
|
||||
|
||||
import "./App.css";
|
||||
|
||||
function EmulateFile() {
|
||||
const { encodedData } = useParams();
|
||||
storeEmulateData(encodedData);
|
||||
return <Navigate to="/playground" replace />;
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
|
||||
@@ -25,6 +33,7 @@ function App() {
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
<Route path="/" element={<LandingPage />} />
|
||||
<Route path="/playground" element={<Playground />} />
|
||||
<Route path="/emulate/:encodedData?" element={<EmulateFile />} />
|
||||
</Routes>
|
||||
</HashRouter>
|
||||
</TooltipProvider>
|
||||
|
||||
@@ -30,6 +30,7 @@ type ClickHandler = (element: FolderElement) => void;
|
||||
type CreateFolderHandler = () => void;
|
||||
type RemoveElementHandler = (element: FolderElement) => void;
|
||||
type RenameElementHandler = (element: FolderElement) => void;
|
||||
type DownloadElementHandler = (element: FolderElement) => void;
|
||||
type AddFilesHandler = () => void;
|
||||
type IconReader = (element: FolderElement) => string | null;
|
||||
|
||||
@@ -40,6 +41,7 @@ export interface FolderProps {
|
||||
createFolderHandler: CreateFolderHandler;
|
||||
removeElementHandler: RemoveElementHandler;
|
||||
renameElementHandler: RenameElementHandler;
|
||||
downloadElementHandler: DownloadElementHandler;
|
||||
addFilesHandler: AddFilesHandler;
|
||||
}
|
||||
|
||||
@@ -133,6 +135,15 @@ function renderElementWithContext(element: FolderElement, props: FolderProps) {
|
||||
<ContextMenuContent>
|
||||
<ContextMenuLabel>{trimFilename(element.name)}</ContextMenuLabel>
|
||||
<ContextMenuSeparator />
|
||||
{element.type != FolderElementType.File ? (
|
||||
<></>
|
||||
) : (
|
||||
<ContextMenuItem
|
||||
onClick={() => props.downloadElementHandler(element)}
|
||||
>
|
||||
Download
|
||||
</ContextMenuItem>
|
||||
)}
|
||||
<ContextMenuItem onClick={() => props.renameElementHandler(element)}>
|
||||
Rename
|
||||
</ContextMenuItem>
|
||||
|
||||
83
page/src/components/item-list.tsx
Normal file
83
page/src/components/item-list.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import { Input } from "./ui/input";
|
||||
import { Button } from "./ui/button";
|
||||
import { Plus, Trash } from "react-bootstrap-icons";
|
||||
import { Label } from "./ui/label";
|
||||
|
||||
interface ItemListProps {
|
||||
title: string;
|
||||
items: string[];
|
||||
onChange: (items: string[]) => void;
|
||||
}
|
||||
|
||||
export function ItemList(props: ItemListProps) {
|
||||
const removeItem = (index: number) => {
|
||||
const newItems = [...props.items];
|
||||
newItems.splice(index, 1);
|
||||
props.onChange(newItems);
|
||||
};
|
||||
|
||||
const addItem = (item: string) => {
|
||||
if (item.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newItems = props.items.concat(item);
|
||||
props.onChange(newItems);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid gap-3">
|
||||
<div className="space-y-2">
|
||||
<h4 className="font-medium leading-none">{props.title}</h4>
|
||||
{/*<p className="text-sm text-muted-foreground">
|
||||
Set the settings for the emulation.
|
||||
</p>*/}
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2 overflow-auto overflow-x-hidden max-h-45 mt-2 mb-2">
|
||||
{props.items.map((item, index) => {
|
||||
return (
|
||||
<div
|
||||
key={`item-list-item-${index}-${item}`}
|
||||
className="flex gap-3 items-center min-w-0"
|
||||
>
|
||||
<Label className="flex-1 text-left truncate min-w-0">
|
||||
{item}
|
||||
</Label>
|
||||
<Button
|
||||
onClick={() => removeItem(index)}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="fancy rounded-lg"
|
||||
>
|
||||
<Trash />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
const nameInput = (e.target as any).elements.name;
|
||||
const newItem = nameInput.value;
|
||||
nameInput.value = "";
|
||||
|
||||
addItem(newItem);
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
<div className="flex gap-3 items-center">
|
||||
<Input id="name" />
|
||||
<Button
|
||||
type="submit"
|
||||
variant="secondary"
|
||||
className="fancy rounded-lg"
|
||||
>
|
||||
<Plus />
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,15 @@ import { Label } from "./ui/label";
|
||||
|
||||
import { Settings } from "@/settings";
|
||||
import { TextTooltip } from "./text-tooltip";
|
||||
import { ItemList } from "./item-list";
|
||||
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { ChevronDown } from "react-bootstrap-icons";
|
||||
import { Input } from "./ui/input";
|
||||
|
||||
interface SettingsMenuProps {
|
||||
settings: Settings;
|
||||
@@ -46,6 +55,10 @@ export class SettingsMenu extends React.Component<SettingsMenuProps, Settings> {
|
||||
}
|
||||
}
|
||||
|
||||
updateArgv(commandLine: string) {
|
||||
this.setState({ commandLine });
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="grid gap-4">
|
||||
@@ -56,6 +69,15 @@ export class SettingsMenu extends React.Component<SettingsMenuProps, Settings> {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-6">
|
||||
<Input
|
||||
id="settings-argv"
|
||||
placeholder="Command-Line Arguments"
|
||||
value={this.state.commandLine}
|
||||
onChange={(e) => this.updateArgv(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-6">
|
||||
<Checkbox
|
||||
id="settings-verbose"
|
||||
@@ -133,6 +155,38 @@ export class SettingsMenu extends React.Component<SettingsMenuProps, Settings> {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-6">
|
||||
<Checkbox
|
||||
id="settings-foreign"
|
||||
checked={this.state.foreignAccess}
|
||||
onCheckedChange={(checked: boolean) => {
|
||||
this.setState({ foreignAccess: checked });
|
||||
}}
|
||||
/>
|
||||
<SettingsLabel
|
||||
htmlFor="settings-foreign"
|
||||
text={"Log Foreign Access"}
|
||||
tooltip={
|
||||
"Log when the application reads/writes memory of other modules"
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-6">
|
||||
<Checkbox
|
||||
id="settings-summary"
|
||||
checked={this.state.instructionSummary}
|
||||
onCheckedChange={(checked: boolean) => {
|
||||
this.setState({ instructionSummary: checked });
|
||||
}}
|
||||
/>
|
||||
<SettingsLabel
|
||||
htmlFor="settings-summary"
|
||||
text={"Print Instruction Summary"}
|
||||
tooltip={"Print summary of executed instructions"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-6">
|
||||
<Checkbox
|
||||
id="settings-persist"
|
||||
@@ -143,7 +197,7 @@ export class SettingsMenu extends React.Component<SettingsMenuProps, Settings> {
|
||||
/>
|
||||
<SettingsLabel
|
||||
htmlFor="settings-persist"
|
||||
text={"Persist filesystem"}
|
||||
text={"Persist Filesystem"}
|
||||
tooltip={
|
||||
"Persist files and folders that were created, modified or deleted during the emulation"
|
||||
}
|
||||
@@ -167,6 +221,46 @@ export class SettingsMenu extends React.Component<SettingsMenuProps, Settings> {
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<TextTooltip tooltip="Don't log executions of listed functions">
|
||||
<div className="flex items-center">
|
||||
<Label className="flex-1 text-left cursor-pointer">
|
||||
Ignored Functions
|
||||
</Label>
|
||||
<ChevronDown />
|
||||
</div>
|
||||
</TextTooltip>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="shadow-2xl">
|
||||
<ItemList
|
||||
title="Ignored Functions"
|
||||
items={this.state.ignoredFunctions}
|
||||
onChange={(items) => this.setState({ ignoredFunctions: items })}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<TextTooltip tooltip="Log interactions of additional modules">
|
||||
<div className="flex items-center">
|
||||
<Label className="flex-1 text-left cursor-pointer">
|
||||
Interesting Modules
|
||||
</Label>
|
||||
<ChevronDown />
|
||||
</div>
|
||||
</TextTooltip>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="shadow-2xl">
|
||||
<ItemList
|
||||
title="Interesting Modules"
|
||||
items={this.state.interestingModules}
|
||||
onChange={(items) => this.setState({ interestingModules: items })}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
86
page/src/download.ts
Normal file
86
page/src/download.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
export type DownloadProgressHandler = (
|
||||
receivedBytes: number,
|
||||
totalBytes: number,
|
||||
) => void;
|
||||
|
||||
export type DownloadPercentHandler = (percent: number) => void;
|
||||
|
||||
export function makePercentHandler(
|
||||
handler: DownloadPercentHandler,
|
||||
): DownloadProgressHandler {
|
||||
const progress = {
|
||||
tracked: 0,
|
||||
};
|
||||
|
||||
return (current, total) => {
|
||||
if (total == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const percent = Math.floor((current * 100) / total);
|
||||
const sanePercent = Math.max(Math.min(percent, 100), 0);
|
||||
|
||||
if (sanePercent + 1 > progress.tracked) {
|
||||
progress.tracked = sanePercent + 1;
|
||||
handler(sanePercent);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function downloadBinaryFile(
|
||||
file: string,
|
||||
progressCallback: DownloadProgressHandler,
|
||||
) {
|
||||
return fetch(file, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/octet-stream",
|
||||
},
|
||||
}).then((response) => {
|
||||
const maybeReader = response.body?.getReader();
|
||||
if (!maybeReader) {
|
||||
throw new Error("Bad reader");
|
||||
}
|
||||
|
||||
const reader = maybeReader;
|
||||
|
||||
const contentLength = parseInt(
|
||||
response.headers?.get("Content-Length") || "0",
|
||||
);
|
||||
|
||||
let receivedLength = 0;
|
||||
let chunks: Uint8Array<ArrayBufferLike>[] = [];
|
||||
|
||||
function processData(
|
||||
res: ReadableStreamReadResult<Uint8Array<ArrayBufferLike>>,
|
||||
): Promise<ArrayBuffer> {
|
||||
if (res.value) {
|
||||
chunks.push(res.value);
|
||||
receivedLength += res.value.length;
|
||||
}
|
||||
|
||||
progressCallback(receivedLength, contentLength);
|
||||
|
||||
if (!res.done) {
|
||||
return reader.read().then(processData);
|
||||
}
|
||||
const chunksAll = new Uint8Array(receivedLength);
|
||||
let position = 0;
|
||||
for (const chunk of chunks) {
|
||||
chunksAll.set(new Uint8Array(chunk), position);
|
||||
position += chunk.length;
|
||||
}
|
||||
|
||||
return Promise.resolve(chunksAll.buffer);
|
||||
}
|
||||
|
||||
return reader.read().then(processData);
|
||||
});
|
||||
}
|
||||
|
||||
export function downloadBinaryFilePercent(
|
||||
file: string,
|
||||
progressCallback: DownloadPercentHandler,
|
||||
) {
|
||||
return downloadBinaryFile(file, makePercentHandler(progressCallback));
|
||||
}
|
||||
@@ -114,11 +114,14 @@ export class Emulator {
|
||||
this._setState(EmulationState.Running);
|
||||
this.stautsUpdateHandler(createDefaultEmulationStatus());
|
||||
|
||||
const options = translateSettings(settings);
|
||||
|
||||
this.worker.postMessage({
|
||||
message: "run",
|
||||
data: {
|
||||
file,
|
||||
options: translateSettings(settings),
|
||||
options: options.emulatorOptions,
|
||||
arguments: options.applicationOptions,
|
||||
persist: settings.persist,
|
||||
wasm64: settings.wasm64,
|
||||
cacheBuster: import.meta.env.VITE_BUILD_TIME || Date.now(),
|
||||
|
||||
@@ -236,6 +236,26 @@ function generateBreadcrumbElements(path: string[]): BreadcrumbElement[] {
|
||||
return elements;
|
||||
}
|
||||
|
||||
function downloadData(
|
||||
data: Uint8Array,
|
||||
filename: string,
|
||||
mimeType: string = "application/octet-stream",
|
||||
) {
|
||||
const buffer = data.buffer.slice(
|
||||
data.byteOffset,
|
||||
data.byteOffset + data.byteLength,
|
||||
) as ArrayBuffer;
|
||||
const blob = new Blob([buffer], { type: mimeType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
link.click();
|
||||
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
export class FilesystemExplorer extends React.Component<
|
||||
FilesystemExplorerProps,
|
||||
FilesystemExplorerState
|
||||
@@ -597,6 +617,12 @@ export class FilesystemExplorer extends React.Component<
|
||||
this.props.iconCache.delete(file);
|
||||
}
|
||||
|
||||
_downloadFile(file: string) {
|
||||
const fullPath = makeFullPathWithState(this.state, file);
|
||||
const data = this.props.filesystem.readFile(fullPath);
|
||||
downloadData(data, file);
|
||||
}
|
||||
|
||||
render() {
|
||||
const elements = getFolderElements(this.props.filesystem, this.state.path);
|
||||
|
||||
@@ -638,6 +664,7 @@ export class FilesystemExplorer extends React.Component<
|
||||
renameElementHandler={(e) =>
|
||||
this.setState({ renameFile: e.name })
|
||||
}
|
||||
downloadElementHandler={(e) => this._downloadFile(e.name)}
|
||||
addFilesHandler={this._onAddFiles}
|
||||
iconReader={(e) =>
|
||||
getPeIcon(
|
||||
|
||||
@@ -1,62 +1,14 @@
|
||||
import { downloadBinaryFilePercent, DownloadPercentHandler } from "./download";
|
||||
import { parseZipFile, ProgressHandler } from "./zip-file";
|
||||
import idbfsModule, { MainModule } from "@irori/idbfs";
|
||||
|
||||
type DownloadProgressHandler = (
|
||||
receivedBytes: number,
|
||||
totalBytes: number,
|
||||
) => void;
|
||||
|
||||
function fetchFilesystemZip(progressCallback: DownloadProgressHandler) {
|
||||
return fetch("./root.zip", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/octet-stream",
|
||||
},
|
||||
}).then((response) => {
|
||||
const maybeReader = response.body?.getReader();
|
||||
if (!maybeReader) {
|
||||
throw new Error("Bad reader");
|
||||
}
|
||||
|
||||
const reader = maybeReader;
|
||||
|
||||
const contentLength = parseInt(
|
||||
response.headers?.get("Content-Length") || "0",
|
||||
);
|
||||
|
||||
let receivedLength = 0;
|
||||
let chunks: Uint8Array<ArrayBufferLike>[] = [];
|
||||
|
||||
function processData(
|
||||
res: ReadableStreamReadResult<Uint8Array<ArrayBufferLike>>,
|
||||
): Promise<ArrayBuffer> {
|
||||
if (res.value) {
|
||||
chunks.push(res.value);
|
||||
receivedLength += res.value.length;
|
||||
}
|
||||
|
||||
progressCallback(receivedLength, contentLength);
|
||||
|
||||
if (!res.done) {
|
||||
return reader.read().then(processData);
|
||||
}
|
||||
const chunksAll = new Uint8Array(receivedLength);
|
||||
let position = 0;
|
||||
for (const chunk of chunks) {
|
||||
chunksAll.set(new Uint8Array(chunk), position);
|
||||
position += chunk.length;
|
||||
}
|
||||
|
||||
return Promise.resolve(chunksAll.buffer);
|
||||
}
|
||||
|
||||
return reader.read().then(processData);
|
||||
});
|
||||
function fetchFilesystemZip(progressCallback: DownloadPercentHandler) {
|
||||
return downloadBinaryFilePercent("./root.zip", progressCallback);
|
||||
}
|
||||
|
||||
async function fetchFilesystem(
|
||||
progressHandler: ProgressHandler,
|
||||
downloadProgressHandler: DownloadProgressHandler,
|
||||
downloadProgressHandler: DownloadPercentHandler,
|
||||
) {
|
||||
const filesys = await fetchFilesystemZip(downloadProgressHandler);
|
||||
return await parseZipFile(filesys, progressHandler);
|
||||
@@ -74,6 +26,28 @@ function synchronizeIDBFS(idbfs: MainModule, populate: boolean) {
|
||||
});
|
||||
}
|
||||
|
||||
const filesystemPrefix = "/root/filesys/";
|
||||
|
||||
export function internalToWindowsPath(internalPath: string): string {
|
||||
if (
|
||||
!internalPath.startsWith(filesystemPrefix) ||
|
||||
internalPath.length <= filesystemPrefix.length
|
||||
) {
|
||||
throw new Error("Invalid path");
|
||||
}
|
||||
|
||||
const winPath = internalPath.substring(filesystemPrefix.length);
|
||||
return `${winPath[0]}:${winPath.substring(1)}`;
|
||||
}
|
||||
|
||||
export function windowsToInternalPath(windowsPath: string): string {
|
||||
if (windowsPath.length < 2 || windowsPath[1] != ":") {
|
||||
throw new Error("Invalid path");
|
||||
}
|
||||
|
||||
return `${filesystemPrefix}${windowsPath[0]}${windowsPath.substring(2)}`;
|
||||
}
|
||||
|
||||
async function initializeIDBFS() {
|
||||
const idbfs = await idbfsModule();
|
||||
|
||||
@@ -210,7 +184,7 @@ export class Filesystem {
|
||||
|
||||
export async function setupFilesystem(
|
||||
progressHandler: ProgressHandler,
|
||||
downloadProgressHandler: DownloadProgressHandler,
|
||||
downloadProgressHandler: DownloadPercentHandler,
|
||||
) {
|
||||
const idbfs = await initializeIDBFS();
|
||||
const fs = new Filesystem(idbfs);
|
||||
|
||||
@@ -2,21 +2,18 @@ import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
Shield,
|
||||
FileCode,
|
||||
Layers,
|
||||
Cpu,
|
||||
Database,
|
||||
Terminal,
|
||||
ExternalLink,
|
||||
Github,
|
||||
Play,
|
||||
ArrowRight,
|
||||
Code,
|
||||
Zap,
|
||||
Target,
|
||||
Users,
|
||||
BookOpen,
|
||||
Download,
|
||||
Lock,
|
||||
Bug,
|
||||
Split,
|
||||
Layers,
|
||||
} from "lucide-react";
|
||||
import { Header } from "./Header";
|
||||
|
||||
@@ -24,44 +21,31 @@ export function LandingPage() {
|
||||
const features = [
|
||||
{
|
||||
icon: <Cpu className="h-8 w-8" />,
|
||||
title: "Syscall-Level Emulation",
|
||||
title: "Syscall Emulation",
|
||||
description:
|
||||
"Operates at syscall level, leveraging existing system DLLs instead of reimplementing Windows APIs",
|
||||
accent: "from-cyan-500 to-blue-500",
|
||||
accent: "from-[#f76548] to-[#b00101]",
|
||||
},
|
||||
{
|
||||
icon: <Database className="h-8 w-8" />,
|
||||
title: "Advanced Memory Management",
|
||||
icon: <Split className="h-8 w-8" />,
|
||||
title: "Hooking Capabilities",
|
||||
description:
|
||||
"Supports Windows-specific memory types including reserved, committed, built on top of Unicorn's memory management",
|
||||
accent: "from-purple-500 to-pink-500",
|
||||
},
|
||||
{
|
||||
icon: <FileCode className="h-8 w-8" />,
|
||||
title: "Complete PE Loading",
|
||||
description:
|
||||
"Handles executable and DLL loading with proper memory mapping, relocations, and TLS",
|
||||
accent: "from-lime-400 to-green-500",
|
||||
},
|
||||
{
|
||||
icon: <Shield className="h-8 w-8" />,
|
||||
title: "Exception Handling",
|
||||
description:
|
||||
"Implements Windows structured exception handling (SEH) with proper exception dispatcher and unwinding support",
|
||||
accent: "from-orange-400 to-red-500",
|
||||
},
|
||||
{
|
||||
icon: <Layers className="h-8 w-8" />,
|
||||
title: "Threading Support",
|
||||
description: "Provides a scheduled (round-robin) threading model",
|
||||
accent: "from-teal-500 to-blue-500",
|
||||
"Provides flexible hooking interfaces to intercept memory access, code execution and much more",
|
||||
accent: "from-[#ffcb00] to-[#da6000]",
|
||||
},
|
||||
{
|
||||
icon: <Terminal className="h-8 w-8" />,
|
||||
title: "Debugging Interface",
|
||||
description:
|
||||
"Implements GDB serial protocol for integration with common debugging tools",
|
||||
accent: "from-purple-500 to-indigo-500",
|
||||
accent: "from-[#00c4e9] to-[#005ff6]",
|
||||
},
|
||||
{
|
||||
icon: <Layers className="h-8 w-8" />,
|
||||
title: "State Management",
|
||||
description:
|
||||
"Saves and restores the entire state of the emulator to quickly resume your work exactly where you left off.",
|
||||
accent: "from-[#aee703] to-[#5f7001]",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -73,13 +57,13 @@ export function LandingPage() {
|
||||
"Analyze malware and security vulnerabilities in a controlled environment",
|
||||
},
|
||||
{
|
||||
icon: <Code className="h-6 w-6" />,
|
||||
icon: <Lock className="h-6 w-6" />,
|
||||
title: "DRM Research",
|
||||
description:
|
||||
"Study digital rights management systems and protection mechanisms",
|
||||
},
|
||||
{
|
||||
icon: <Target className="h-6 w-6" />,
|
||||
icon: <Bug className="h-6 w-6" />,
|
||||
title: "Malware Analysis",
|
||||
description:
|
||||
"Reverse engineer malicious software with full process control",
|
||||
@@ -88,9 +72,9 @@ export function LandingPage() {
|
||||
|
||||
const stats = [
|
||||
{ value: "100%", label: "Open Source" },
|
||||
{ value: "C++", label: "High Performance" },
|
||||
{ value: "GDB", label: "Debug Protocol" },
|
||||
{ value: "64 bit", label: "Native PE Loading" },
|
||||
{ value: "14", label: "Platforms" },
|
||||
{ value: "2", label: "Backends" },
|
||||
{ value: "100%", label: "Deterministic" },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -104,25 +88,20 @@ export function LandingPage() {
|
||||
<section className="relative overflow-hidden">
|
||||
{/* Animated Background Elements */}
|
||||
<div className="absolute inset-0">
|
||||
<div className="absolute top-20 left-10 w-72 h-72 bg-blue-500/10 rounded-full blur-3xl animate-pulse"></div>
|
||||
<div className="absolute top-40 right-20 w-96 h-96 bg-purple-500/10 rounded-full blur-3xl animate-pulse delay-1000"></div>
|
||||
<div className="absolute bottom-20 left-1/3 w-80 h-80 bg-cyan-500/10 rounded-full blur-3xl animate-pulse delay-2000"></div>
|
||||
<div className="absolute top-20 left-10 w-72 h-72 bg-yellow-500/10 rounded-full blur-3xl"></div>
|
||||
<div className="absolute top-40 right-20 w-96 h-96 bg-lime-500/10 rounded-full blur-3xl"></div>
|
||||
<div className="absolute bottom-20 left-1/3 w-80 h-80 bg-cyan-500/10 rounded-full blur-3xl"></div>
|
||||
</div>
|
||||
|
||||
<div className="relative container mx-auto min-h-[100dvh] p-4 flex items-center xl:min-h-0 xl:px-6 xl:py-32">
|
||||
<div className="text-center space-y-8 max-w-4xl mx-auto">
|
||||
{/* Main Headline */}
|
||||
<h1 className="text-5xl md:text-7xl font-bold bg-gradient-to-r from-white via-blue-100 to-cyan-200 bg-clip-text text-transparent leading-tight">
|
||||
<h1 className="text-5xl md:text-7xl font-bold text-white leading-tight">
|
||||
Sogen
|
||||
</h1>
|
||||
|
||||
<p className="text-xl md:text-2xl text-neutral-300 font-light leading-relaxed">
|
||||
Revolutionary Windows user space emulator.
|
||||
<br />
|
||||
<span className="text-blue-400">
|
||||
Perfect for security research, malware analysis, and DRM
|
||||
research.
|
||||
</span>
|
||||
A high-performance Windows user space emulator.
|
||||
</p>
|
||||
|
||||
{/* CTA Buttons */}
|
||||
@@ -130,7 +109,7 @@ export function LandingPage() {
|
||||
<a href="#/playground">
|
||||
<Button
|
||||
size="lg"
|
||||
className="bg-gradient-to-r from-blue-600 to-cyan-600 hover:from-blue-700 hover:to-cyan-700 text-white border-0 px-8 py-6 text-lg font-semibold group transition-all duration-300 transform hover:scale-105"
|
||||
className="bg-gradient-to-br from-white to-neutral-300 text-neutral-900 border-0 px-8 py-6 text-lg font-semibold group transition-all duration-100 transform hover:scale-105"
|
||||
>
|
||||
<Play className="mr-2 h-5 w-5 group-hover:scale-110 transition-transform" />
|
||||
Try Online
|
||||
@@ -141,7 +120,7 @@ export function LandingPage() {
|
||||
<Button
|
||||
size="lg"
|
||||
variant="outline"
|
||||
className="border-gray-600 text-gray-300 hover:bg-gray-800/50 px-8 py-6 text-lg font-semibold group transition-all duration-300"
|
||||
className="border-neutral-600 text-neutral-300 hover:bg-neutral-800/50 px-8 py-6 text-lg font-semibold group transition-all duration-300"
|
||||
>
|
||||
<Github className="mr-2 h-5 w-5 group-hover:scale-110 transition-transform" />
|
||||
View Source
|
||||
@@ -157,7 +136,7 @@ export function LandingPage() {
|
||||
<div className="text-2xl font-bold text-white">
|
||||
{stat.value}
|
||||
</div>
|
||||
<div className="text-sm text-gray-400">{stat.label}</div>
|
||||
<div className="text-sm text-neutral-400">{stat.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -172,30 +151,29 @@ export function LandingPage() {
|
||||
<h2 className="text-4xl md:text-5xl font-bold text-white mb-6">
|
||||
Powerful Features
|
||||
</h2>
|
||||
<p className="text-xl text-gray-400 max-w-2xl mx-auto">
|
||||
Built from the ground up for performance and accuracy in Windows
|
||||
process emulation
|
||||
<p className="text-xl text-neutral-400 max-w-2xl mx-auto">
|
||||
Built from the ground up for performance and accuracy.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-8 lg:m-32">
|
||||
{features.map((feature, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
className="bg-gray-800/50 border-gray-700 hover:bg-gray-800/80 transition-all duration-200 group hover:shadow-2xl"
|
||||
className="bg-neutral-800/50 border-neutral-700 hover:bg-neutral-800/80 transition-all duration-200 group hover:shadow-2xl"
|
||||
>
|
||||
<CardHeader className="pb-4">
|
||||
<div
|
||||
className={`w-16 h-16 rounded-xl bg-gradient-to-br ${feature.accent} p-4 mb-4 transition-transform duration-200`}
|
||||
>
|
||||
<div className="text-white">{feature.icon}</div>
|
||||
<div className="text-neutral-900">{feature.icon}</div>
|
||||
</div>
|
||||
<CardTitle className="text-white text-xl font-semibold group-hover:text-blue-300 transition-colors">
|
||||
<CardTitle className="text-white text-xl font-semibold transition-colors">
|
||||
{feature.title}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-gray-300 leading-relaxed">
|
||||
<p className="text-neutral-300 leading-relaxed">
|
||||
{feature.description}
|
||||
</p>
|
||||
</CardContent>
|
||||
@@ -206,15 +184,15 @@ export function LandingPage() {
|
||||
</section>
|
||||
|
||||
{/* Use Cases */}
|
||||
<section className="py-24 bg-gray-900/50">
|
||||
<section className="py-24 bg-neutral-800/40">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-4xl font-bold text-white mb-6">
|
||||
Perfect For Your Research
|
||||
</h2>
|
||||
<p className="text-xl text-gray-400">
|
||||
<p className="text-xl text-neutral-400">
|
||||
Designed for researchers who need precise control over Windows
|
||||
process execution
|
||||
process execution.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -222,15 +200,15 @@ export function LandingPage() {
|
||||
{useCases.map((useCase, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="text-center p-8 rounded-2xl bg-gray-800/50 border border-gray-700 hover:border-blue-500/50 transition-all duration-300 group"
|
||||
className="text-center p-8 rounded-2xl bg-neutral-800/50 border border-neutral-700 hover:border-blue-400/50 transition-all duration-300 group"
|
||||
>
|
||||
<div className="w-12 h-12 mx-auto mb-4 rounded-xl bg-gradient-to-br from-blue-500 to-cyan-500 p-3 group-hover:scale-110 transition-transform">
|
||||
<div className="text-white">{useCase.icon}</div>
|
||||
<div className="w-12 h-12 mx-auto mb-4 rounded-xl bg-gradient-to-br from-cyan-500 to-blue-500 p-3 group-hover:scale-110 transition-transform">
|
||||
<div className="text-neutral-800">{useCase.icon}</div>
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-white mb-3">
|
||||
{useCase.title}
|
||||
</h3>
|
||||
<p className="text-gray-400">{useCase.description}</p>
|
||||
<p className="text-neutral-400">{useCase.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -244,16 +222,16 @@ export function LandingPage() {
|
||||
<h2 className="text-4xl font-bold text-white mb-6">
|
||||
See Sogen in Action
|
||||
</h2>
|
||||
<p className="text-xl text-gray-400 max-w-3xl mx-auto">
|
||||
<p className="text-xl text-neutral-400 max-w-3xl mx-auto">
|
||||
Watch a comprehensive overview of the emulator's capabilities
|
||||
and discover how it can accelerate your research workflow.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<div className="relative group">
|
||||
<div className="absolute -inset-4 bg-gradient-to-r from-neutral-500/10 to-neutral-500/10 rounded-3xl blur-xl group-hover:blur-2xl transition-all duration-300"></div>
|
||||
<div className="relative aspect-video rounded-2xl overflow-hidden border border-gray-700">
|
||||
<div className="relative aspect-video rounded-2xl overflow-hidden border border-neutral-700">
|
||||
<iframe
|
||||
className="w-full h-full"
|
||||
src="https://www.youtube.com/embed/wY9Q0DhodOQ?si=Lm_anpeBU6Txl5AW"
|
||||
@@ -270,20 +248,19 @@ export function LandingPage() {
|
||||
</section>
|
||||
|
||||
{/* CTA Section */}
|
||||
<section className="py-24 bg-gradient-to-r from-gray-900 to-zinc-900">
|
||||
<section className="py-24 bg-gradient-to-r from-neutral-900 to-zinc-900">
|
||||
<div className="container mx-auto px-6 text-center">
|
||||
<h2 className="text-4xl font-bold text-white mb-6">
|
||||
Ready to Start Emulating?
|
||||
</h2>
|
||||
<p className="text-xl text-gray-300 mb-8 max-w-2xl mx-auto">
|
||||
Join researchers worldwide who trust Sogen for their Windows
|
||||
emulation needs.
|
||||
<p className="text-xl text-neutral-300 mb-8 max-w-2xl mx-auto">
|
||||
Try Sogen directly in your browser or explore the source code.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<a href="#/playground">
|
||||
<Button
|
||||
size="lg"
|
||||
className="bg-white text-blue-900 hover:bg-blue-50 px-8 py-6 text-lg font-semibold"
|
||||
className="bg-gradient-to-br from-white to-neutral-300 text-neutral-900 px-8 py-6 text-lg font-semibold transition-all duration-100"
|
||||
>
|
||||
<Play className="mr-2 h-5 w-5" />
|
||||
Launch Playground
|
||||
@@ -296,7 +273,7 @@ export function LandingPage() {
|
||||
className="border-white text-white hover:bg-white/10 px-8 py-6 text-lg font-semibold"
|
||||
>
|
||||
<Download className="mr-2 h-5 w-5" />
|
||||
Download Source
|
||||
Get Source
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
@@ -304,17 +281,12 @@ export function LandingPage() {
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="py-16 border-t border-gray-800">
|
||||
<footer className="py-16 border-t border-neutral-800">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="flex flex-col md:flex-row justify-between items-center">
|
||||
<div className="mb-8 md:mb-0 text-center md:text-left">
|
||||
<h2 className="text-3xl font-bold bg-gradient-to-r from-blue-400 to-cyan-400 bg-clip-text text-transparent">
|
||||
Sogen
|
||||
</h2>
|
||||
<p className="mt-2 text-gray-400 text-lg">
|
||||
Windows User Space Emulator
|
||||
</p>
|
||||
<p className="mt-1 text-gray-500 text-sm">
|
||||
<h2 className="text-3xl font-bold">Sogen</h2>
|
||||
<p className="mt-1 text-neutral-500 text-sm">
|
||||
Built by{" "}
|
||||
<a
|
||||
href="https://momo5502.com"
|
||||
@@ -338,20 +310,20 @@ export function LandingPage() {
|
||||
<a
|
||||
href="https://github.com/momo5502/sogen"
|
||||
target="_blank"
|
||||
className="text-gray-400 hover:text-blue-400 transition-colors p-2 rounded-lg hover:bg-gray-800/50"
|
||||
className="text-neutral-400 hover:text-blue-400 transition-colors p-2 rounded-lg hover:bg-neutral-800/50"
|
||||
>
|
||||
<Github className="h-6 w-6" />
|
||||
</a>
|
||||
<a
|
||||
href="#/playground"
|
||||
className="text-gray-400 hover:text-blue-400 transition-colors p-2 rounded-lg hover:bg-gray-800/50"
|
||||
className="text-neutral-400 hover:text-blue-400 transition-colors p-2 rounded-lg hover:bg-neutral-800/50"
|
||||
>
|
||||
<Play className="h-6 w-6" />
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/momo5502/sogen/wiki"
|
||||
target="_blank"
|
||||
className="text-gray-400 hover:text-blue-400 transition-colors p-2 rounded-lg hover:bg-gray-800/50"
|
||||
className="text-neutral-400 hover:text-blue-400 transition-colors p-2 rounded-lg hover:bg-neutral-800/50"
|
||||
>
|
||||
<BookOpen className="h-6 w-6" />
|
||||
</a>
|
||||
|
||||
@@ -3,7 +3,11 @@ import React from "react";
|
||||
import { Output } from "@/components/output";
|
||||
|
||||
import { Emulator, EmulationState, isFinalState } from "./emulator";
|
||||
import { Filesystem, setupFilesystem } from "./filesystem";
|
||||
import {
|
||||
Filesystem,
|
||||
setupFilesystem,
|
||||
windowsToInternalPath,
|
||||
} from "./filesystem";
|
||||
|
||||
import { memory64 } from "wasm-feature-detect";
|
||||
|
||||
@@ -39,11 +43,17 @@ import {
|
||||
} from "@/components/ui/drawer";
|
||||
import { FilesystemExplorer } from "./filesystem-explorer";
|
||||
import { EmulationStatus } from "./emulator";
|
||||
import { TextTooltip } from "./components/text-tooltip";
|
||||
import { EmulationSummary } from "./components/emulation-summary";
|
||||
import { downloadBinaryFilePercent } from "./download";
|
||||
|
||||
interface PlaygroundProps {}
|
||||
interface PlaygroundState {
|
||||
export interface PlaygroundFile {
|
||||
file: string;
|
||||
storage: string;
|
||||
}
|
||||
|
||||
export interface PlaygroundProps {}
|
||||
|
||||
export interface PlaygroundState {
|
||||
settings: Settings;
|
||||
filesystemPromise?: Promise<Filesystem>;
|
||||
filesystem?: Filesystem;
|
||||
@@ -52,28 +62,57 @@ interface PlaygroundState {
|
||||
application?: string;
|
||||
drawerOpen: boolean;
|
||||
allowWasm64: boolean;
|
||||
file?: PlaygroundFile;
|
||||
}
|
||||
|
||||
function makePercentHandler(
|
||||
handler: (percent: number) => void,
|
||||
): (current: number, total: number) => void {
|
||||
const progress = {
|
||||
tracked: 0,
|
||||
};
|
||||
function decodeFileData(data: string | null): PlaygroundFile | undefined {
|
||||
if (!data) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return (current, total) => {
|
||||
if (total == 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const jsonData = JSON.parse(atob(data));
|
||||
|
||||
const percent = Math.floor((current * 100) / total);
|
||||
const sanePercent = Math.max(Math.min(percent, 100), 0);
|
||||
return {
|
||||
file: jsonData.file,
|
||||
storage: jsonData.storage,
|
||||
};
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
if (sanePercent + 1 > progress.tracked) {
|
||||
progress.tracked = sanePercent + 1;
|
||||
handler(sanePercent);
|
||||
}
|
||||
};
|
||||
return undefined;
|
||||
}
|
||||
|
||||
interface GlobalThisExt {
|
||||
emulateCache?: string | null;
|
||||
}
|
||||
|
||||
function getGlobalThis() {
|
||||
return globalThis as GlobalThisExt;
|
||||
}
|
||||
|
||||
export function storeEmulateData(data?: string) {
|
||||
getGlobalThis().emulateCache = undefined;
|
||||
|
||||
if (data) {
|
||||
localStorage.setItem("emulate", data);
|
||||
} else {
|
||||
localStorage.removeItem("emulate");
|
||||
}
|
||||
}
|
||||
|
||||
function getEmulateData() {
|
||||
const gt = getGlobalThis();
|
||||
if (gt.emulateCache) {
|
||||
return gt.emulateCache;
|
||||
}
|
||||
|
||||
const emulateData = localStorage.getItem("emulate");
|
||||
localStorage.removeItem("emulate");
|
||||
|
||||
gt.emulateCache = emulateData;
|
||||
return emulateData;
|
||||
}
|
||||
|
||||
export class Playground extends React.Component<
|
||||
@@ -90,13 +129,14 @@ export class Playground extends React.Component<
|
||||
|
||||
this.start = this.start.bind(this);
|
||||
this.resetFilesys = this.resetFilesys.bind(this);
|
||||
this.createEmulator = this.createEmulator.bind(this);
|
||||
this.startEmulator = this.startEmulator.bind(this);
|
||||
this.toggleEmulatorState = this.toggleEmulatorState.bind(this);
|
||||
|
||||
this.state = {
|
||||
settings: loadSettings(),
|
||||
drawerOpen: false,
|
||||
allowWasm64: false,
|
||||
file: decodeFileData(getEmulateData()),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -104,6 +144,10 @@ export class Playground extends React.Component<
|
||||
memory64().then((allowWasm64) => {
|
||||
this.setState({ allowWasm64 });
|
||||
});
|
||||
|
||||
if (this.state.file) {
|
||||
this.emulateRemoteFile(this.state.file);
|
||||
}
|
||||
}
|
||||
|
||||
async resetFilesys() {
|
||||
@@ -151,9 +195,9 @@ export class Playground extends React.Component<
|
||||
(current, total, file) => {
|
||||
this.logLine(`Processing filesystem (${current}/${total}): ${file}`);
|
||||
},
|
||||
makePercentHandler((percent) => {
|
||||
(percent) => {
|
||||
this.logLine(`Downloading filesystem: ${percent}%`);
|
||||
}),
|
||||
},
|
||||
).then(resolve);
|
||||
});
|
||||
|
||||
@@ -167,6 +211,29 @@ export class Playground extends React.Component<
|
||||
this.setState({ drawerOpen });
|
||||
}
|
||||
|
||||
async downloadFileToFilesystem(file: PlaygroundFile) {
|
||||
const fs = await this.initFilesys();
|
||||
|
||||
const fileData = await downloadBinaryFilePercent(
|
||||
file.storage,
|
||||
(percent) => {
|
||||
this.logLine(`Downloading binary: ${percent}%`);
|
||||
},
|
||||
);
|
||||
|
||||
await fs.storeFiles([
|
||||
{
|
||||
name: windowsToInternalPath(file.file),
|
||||
data: fileData,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
async emulateRemoteFile(file: PlaygroundFile) {
|
||||
await this.downloadFileToFilesystem(file);
|
||||
await this.startEmulator(file.file);
|
||||
}
|
||||
|
||||
async start() {
|
||||
await this.initFilesys();
|
||||
this.setDrawerOpen(true);
|
||||
@@ -195,7 +262,7 @@ export class Playground extends React.Component<
|
||||
}
|
||||
}
|
||||
|
||||
async createEmulator(userFile: string) {
|
||||
async startEmulator(userFile: string) {
|
||||
this.state.emulator?.stop();
|
||||
this.output.current?.clear();
|
||||
|
||||
@@ -316,7 +383,7 @@ export class Playground extends React.Component<
|
||||
<FilesystemExplorer
|
||||
filesystem={this.state.filesystem}
|
||||
iconCache={this.iconCache}
|
||||
runFile={this.createEmulator}
|
||||
runFile={this.startEmulator}
|
||||
resetFilesys={this.resetFilesys}
|
||||
path={["c"]}
|
||||
/>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { parse } from "shell-quote";
|
||||
|
||||
export interface Settings {
|
||||
verbose: boolean;
|
||||
concise: boolean;
|
||||
@@ -5,7 +7,17 @@ export interface Settings {
|
||||
bufferStdout: boolean;
|
||||
persist: boolean;
|
||||
execAccess: boolean;
|
||||
foreignAccess: boolean;
|
||||
wasm64: boolean;
|
||||
instructionSummary: boolean;
|
||||
ignoredFunctions: string[];
|
||||
interestingModules: string[];
|
||||
commandLine: string;
|
||||
}
|
||||
|
||||
export interface TranslatedSettings {
|
||||
emulatorOptions: string[];
|
||||
applicationOptions: string[];
|
||||
}
|
||||
|
||||
export function createDefaultSettings(): Settings {
|
||||
@@ -16,7 +28,12 @@ export function createDefaultSettings(): Settings {
|
||||
bufferStdout: true,
|
||||
persist: false,
|
||||
execAccess: true,
|
||||
foreignAccess: false,
|
||||
wasm64: false,
|
||||
instructionSummary: false,
|
||||
ignoredFunctions: [],
|
||||
interestingModules: [],
|
||||
commandLine: "",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,8 +63,9 @@ export function saveSettings(settings: Settings) {
|
||||
localStorage.setItem("settings", JSON.stringify(settings));
|
||||
}
|
||||
|
||||
export function translateSettings(settings: Settings): string[] {
|
||||
export function translateSettings(settings: Settings): TranslatedSettings {
|
||||
const switches: string[] = [];
|
||||
const options: string[] = [];
|
||||
|
||||
if (settings.verbose) {
|
||||
switches.push("-v");
|
||||
@@ -69,5 +87,33 @@ export function translateSettings(settings: Settings): string[] {
|
||||
switches.push("-x");
|
||||
}
|
||||
|
||||
return switches;
|
||||
if (settings.foreignAccess) {
|
||||
switches.push("-f");
|
||||
}
|
||||
|
||||
if (settings.instructionSummary) {
|
||||
switches.push("-is");
|
||||
}
|
||||
|
||||
settings.ignoredFunctions.forEach((f) => {
|
||||
switches.push("-i");
|
||||
switches.push(f);
|
||||
});
|
||||
|
||||
settings.interestingModules.forEach((m) => {
|
||||
switches.push("-m");
|
||||
switches.push(m);
|
||||
});
|
||||
|
||||
try {
|
||||
const argv = parse(settings.commandLine) as string[];
|
||||
options.push(...argv);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
return {
|
||||
applicationOptions: options,
|
||||
emulatorOptions: switches,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ endif()
|
||||
target_link_libraries(analyzer PRIVATE
|
||||
reflect
|
||||
debugger
|
||||
capstone
|
||||
windows-emulator
|
||||
windows-gdb-stub
|
||||
backend-selection
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "std_include.hpp"
|
||||
|
||||
#include "analysis.hpp"
|
||||
#include "disassembler.hpp"
|
||||
#include "windows_emulator.hpp"
|
||||
#include <utils/lazy_object.hpp>
|
||||
|
||||
@@ -12,6 +13,8 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr size_t MAX_INSTRUCTION_BYTES = 15;
|
||||
|
||||
template <typename Return, typename... Args>
|
||||
std::function<Return(Args...)> make_callback(analysis_context& c, Return (*callback)(analysis_context&, Args...))
|
||||
{
|
||||
@@ -21,19 +24,54 @@ namespace
|
||||
}
|
||||
|
||||
template <typename Return, typename... Args>
|
||||
std::function<Return(Args...)> make_callback(analysis_context& c,
|
||||
Return (*callback)(const analysis_context&, Args...))
|
||||
std::function<Return(Args...)> make_callback(analysis_context& c, Return (*callback)(const analysis_context&, Args...))
|
||||
{
|
||||
return [&c, callback](Args... args) {
|
||||
return callback(c, std::forward<Args>(args)...); //
|
||||
};
|
||||
}
|
||||
|
||||
std::string get_instruction_string(const disassembler& d, const emulator& emu, const uint64_t address)
|
||||
{
|
||||
std::array<uint8_t, MAX_INSTRUCTION_BYTES> instruction_bytes{};
|
||||
const auto result = emu.try_read_memory(address, instruction_bytes.data(), instruction_bytes.size());
|
||||
if (!result)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto instructions = d.disassemble(instruction_bytes, 1);
|
||||
if (instructions.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto& inst = instructions[0];
|
||||
return std::string(inst.mnemonic) + (strlen(inst.op_str) ? " "s + inst.op_str : "");
|
||||
}
|
||||
|
||||
void handle_suspicious_activity(const analysis_context& c, const std::string_view details)
|
||||
{
|
||||
std::string addition{};
|
||||
const auto rip = c.win_emu->emu().read_instruction_pointer();
|
||||
c.win_emu->log.print(color::pink, "Suspicious: %.*s at 0x%" PRIx64 " (via 0x%" PRIx64 ")\n",
|
||||
STR_VIEW_VA(details), rip, c.win_emu->process.previous_ip);
|
||||
|
||||
// TODO: Pass enum?
|
||||
if (details == "Illegal instruction")
|
||||
{
|
||||
const auto inst = get_instruction_string(c.d, c.win_emu->emu(), rip);
|
||||
if (!inst.empty())
|
||||
{
|
||||
addition = " (" + inst + ")";
|
||||
}
|
||||
}
|
||||
|
||||
c.win_emu->log.print(color::pink, "Suspicious: %.*s%.*s at 0x%" PRIx64 " (via 0x%" PRIx64 ")\n", STR_VIEW_VA(details),
|
||||
STR_VIEW_VA(addition), rip, c.win_emu->current_thread().previous_ip);
|
||||
}
|
||||
|
||||
void handle_debug_string(const analysis_context& c, const std::string_view details)
|
||||
{
|
||||
c.win_emu->log.info("--> Debug string: %.*s\n", STR_VIEW_VA(details));
|
||||
}
|
||||
|
||||
void handle_generic_activity(const analysis_context& c, const std::string_view details)
|
||||
@@ -49,22 +87,20 @@ namespace
|
||||
void handle_memory_allocate(const analysis_context& c, const uint64_t address, const uint64_t length,
|
||||
const memory_permission permission, const bool commit)
|
||||
{
|
||||
const auto* action = commit ? "Committed" : "Allocated";
|
||||
const auto* action = commit ? "Committed" : "Allocating";
|
||||
|
||||
c.win_emu->log.print(is_executable(permission) ? color::gray : color::dark_gray,
|
||||
"--> %s 0x%" PRIx64 " - 0x%" PRIx64 " (%s)\n", action, address, address + length,
|
||||
c.win_emu->log.print(is_executable(permission) ? color::gray : color::dark_gray, "--> %s 0x%" PRIx64 " - 0x%" PRIx64 " (%s)\n",
|
||||
action, address, address + length, get_permission_string(permission).c_str());
|
||||
}
|
||||
|
||||
void handle_memory_protect(const analysis_context& c, const uint64_t address, const uint64_t length, const memory_permission permission)
|
||||
{
|
||||
c.win_emu->log.print(color::dark_gray, "--> Changing protection at 0x%" PRIx64 "-0x%" PRIx64 " to %s\n", address, address + length,
|
||||
get_permission_string(permission).c_str());
|
||||
}
|
||||
|
||||
void handle_memory_protect(const analysis_context& c, const uint64_t address, const uint64_t length,
|
||||
const memory_permission permission)
|
||||
{
|
||||
c.win_emu->log.print(color::dark_gray, "--> Changing protection at 0x%" PRIx64 "-0x%" PRIx64 " to %s\n",
|
||||
address, address + length, get_permission_string(permission).c_str());
|
||||
}
|
||||
|
||||
void handle_memory_violate(const analysis_context& c, const uint64_t address, const uint64_t size,
|
||||
const memory_operation operation, const memory_violation_type type)
|
||||
void handle_memory_violate(const analysis_context& c, const uint64_t address, const uint64_t size, const memory_operation operation,
|
||||
const memory_violation_type type)
|
||||
{
|
||||
const auto permission = get_permission_string(operation);
|
||||
const auto ip = c.win_emu->emu().read_instruction_pointer();
|
||||
@@ -72,35 +108,29 @@ namespace
|
||||
|
||||
if (type == memory_violation_type::protection)
|
||||
{
|
||||
c.win_emu->log.print(color::gray,
|
||||
"Protection violation: 0x%" PRIx64 " (%" PRIx64 ") - %s at 0x%" PRIx64 " (%s)\n",
|
||||
address, size, permission.c_str(), ip, name);
|
||||
c.win_emu->log.print(color::gray, "Protection violation: 0x%" PRIx64 " (%" PRIx64 ") - %s at 0x%" PRIx64 " (%s)\n", address,
|
||||
size, permission.c_str(), ip, name);
|
||||
}
|
||||
else if (type == memory_violation_type::unmapped)
|
||||
{
|
||||
c.win_emu->log.print(color::gray,
|
||||
"Mapping violation: 0x%" PRIx64 " (%" PRIx64 ") - %s at 0x%" PRIx64 " (%s)\n", address,
|
||||
size, permission.c_str(), ip, name);
|
||||
c.win_emu->log.print(color::gray, "Mapping violation: 0x%" PRIx64 " (%" PRIx64 ") - %s at 0x%" PRIx64 " (%s)\n", address, size,
|
||||
permission.c_str(), ip, name);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_ioctrl(const analysis_context& c, const io_device&, const std::u16string_view device_name,
|
||||
const ULONG code)
|
||||
void handle_ioctrl(const analysis_context& c, const io_device&, const std::u16string_view device_name, const ULONG code)
|
||||
{
|
||||
c.win_emu->log.print(color::dark_gray, "--> %s: 0x%X\n", u16_to_u8(device_name).c_str(),
|
||||
static_cast<uint32_t>(code));
|
||||
c.win_emu->log.print(color::dark_gray, "--> %s: 0x%X\n", u16_to_u8(device_name).c_str(), static_cast<uint32_t>(code));
|
||||
}
|
||||
|
||||
void handle_thread_set_name(const analysis_context& c, const emulator_thread& t)
|
||||
{
|
||||
c.win_emu->log.print(color::blue, "Setting thread (%d) name: %s\n", t.id, u16_to_u8(t.name).c_str());
|
||||
c.win_emu->log.print(color::blue, "Setting thread (%u) name: %s\n", t.id, u16_to_u8(t.name).c_str());
|
||||
}
|
||||
|
||||
void handle_thread_switch(const analysis_context& c, const emulator_thread& current_thread,
|
||||
const emulator_thread& new_thread)
|
||||
void handle_thread_switch(const analysis_context& c, const emulator_thread& current_thread, const emulator_thread& new_thread)
|
||||
{
|
||||
c.win_emu->log.print(color::dark_gray, "Performing thread switch: %X -> %X\n", current_thread.id,
|
||||
new_thread.id);
|
||||
c.win_emu->log.print(color::dark_gray, "Performing thread switch: %X -> %X\n", current_thread.id, new_thread.id);
|
||||
}
|
||||
|
||||
void handle_module_load(const analysis_context& c, const mapped_module& mod)
|
||||
@@ -124,7 +154,7 @@ namespace
|
||||
}
|
||||
|
||||
template <typename CharType = char>
|
||||
void print_arg_as_string(windows_emulator& win_emu, size_t index)
|
||||
void print_arg_as_string(windows_emulator& win_emu, const size_t index)
|
||||
{
|
||||
const auto var_ptr = get_function_argument(win_emu.emu(), index);
|
||||
if (var_ptr)
|
||||
@@ -134,7 +164,17 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
void handle_function_details(analysis_context& c, const std::string_view function)
|
||||
void print_module_name(windows_emulator& win_emu, const size_t index)
|
||||
{
|
||||
const auto var_ptr = get_function_argument(win_emu.emu(), index);
|
||||
if (var_ptr)
|
||||
{
|
||||
const auto* module_name = win_emu.mod_manager.find_name(var_ptr);
|
||||
print_string(win_emu.log, module_name);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_function_details(const analysis_context& c, const std::string_view function)
|
||||
{
|
||||
if (function == "GetEnvironmentVariableA" || function == "ExpandEnvironmentStringsA")
|
||||
{
|
||||
@@ -150,11 +190,114 @@ namespace
|
||||
print_arg_as_string<char16_t>(*c.win_emu, 2);
|
||||
print_arg_as_string<char16_t>(*c.win_emu, 1);
|
||||
}
|
||||
else if (function == "GetProcAddress")
|
||||
{
|
||||
print_module_name(*c.win_emu, 0);
|
||||
print_arg_as_string(*c.win_emu, 1);
|
||||
}
|
||||
else if (function == "WinVerifyTrust")
|
||||
{
|
||||
auto& emu = c.win_emu->emu();
|
||||
emu.reg(x86_register::rip, emu.read_stack(0));
|
||||
emu.reg(x86_register::rsp, emu.reg(x86_register::rsp) + 8);
|
||||
emu.reg(x86_register::rax, 1);
|
||||
}
|
||||
else if (function == "lstrcmp" || function == "lstrcmpi")
|
||||
{
|
||||
print_arg_as_string(*c.win_emu, 0);
|
||||
print_arg_as_string(*c.win_emu, 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_thread_alive(const analysis_context& c, const uint32_t thread_id)
|
||||
{
|
||||
for (const auto& t : c.win_emu->process.threads | std::views::values)
|
||||
{
|
||||
if (t.id == thread_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void update_import_access(analysis_context& c, const uint64_t address)
|
||||
{
|
||||
if (c.accessed_imports.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& t = c.win_emu->current_thread();
|
||||
for (auto entry = c.accessed_imports.begin(); entry != c.accessed_imports.end();)
|
||||
{
|
||||
auto& a = *entry;
|
||||
const auto is_same_thread = t.id == a.thread_id;
|
||||
|
||||
if (is_same_thread && address == a.address)
|
||||
{
|
||||
entry = c.accessed_imports.erase(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
constexpr auto inst_delay = 100u;
|
||||
const auto execution_delay_reached = is_same_thread && a.access_inst_count + inst_delay <= t.executed_instructions;
|
||||
|
||||
if (!execution_delay_reached && is_thread_alive(c, a.thread_id))
|
||||
{
|
||||
++entry;
|
||||
continue;
|
||||
}
|
||||
|
||||
c.win_emu->log.print(color::green, "Import read access without execution: %s (%s) at 0x%" PRIx64 " (%s)\n",
|
||||
a.import_name.c_str(), a.import_module.c_str(), a.access_rip, a.accessor_module.c_str());
|
||||
|
||||
entry = c.accessed_imports.erase(entry);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_return(const disassembler& d, const emulator& emu, const uint64_t address)
|
||||
{
|
||||
std::array<uint8_t, MAX_INSTRUCTION_BYTES> instruction_bytes{};
|
||||
const auto result = emu.try_read_memory(address, instruction_bytes.data(), instruction_bytes.size());
|
||||
if (!result)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto instructions = d.disassemble(instruction_bytes, 1);
|
||||
if (instructions.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return cs_insn_group(d.get_handle(), instructions.data(), CS_GRP_RET);
|
||||
}
|
||||
|
||||
void record_instruction(analysis_context& c, const uint64_t address)
|
||||
{
|
||||
std::array<uint8_t, MAX_INSTRUCTION_BYTES> instruction_bytes{};
|
||||
const auto result = c.win_emu->emu().try_read_memory(address, instruction_bytes.data(), instruction_bytes.size());
|
||||
if (!result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
disassembler disasm{};
|
||||
const auto instructions = disasm.disassemble(instruction_bytes, 1);
|
||||
if (instructions.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
++c.instructions[instructions[0].id];
|
||||
}
|
||||
|
||||
void handle_instruction(analysis_context& c, const uint64_t address)
|
||||
{
|
||||
auto& win_emu = *c.win_emu;
|
||||
update_import_access(c, address);
|
||||
|
||||
#ifdef OS_EMSCRIPTEN
|
||||
if ((win_emu.get_executed_instructions() % 0x20000) == 0)
|
||||
@@ -164,8 +307,9 @@ namespace
|
||||
}
|
||||
#endif
|
||||
|
||||
const auto previous_ip = c.win_emu->current_thread().previous_ip;
|
||||
const auto is_main_exe = win_emu.mod_manager.executable->is_within(address);
|
||||
const auto is_previous_main_exe = win_emu.mod_manager.executable->is_within(c.win_emu->process.previous_ip);
|
||||
const auto is_previous_main_exe = win_emu.mod_manager.executable->is_within(previous_ip);
|
||||
|
||||
const auto binary = utils::make_lazy([&] {
|
||||
if (is_main_exe)
|
||||
@@ -182,7 +326,11 @@ namespace
|
||||
return win_emu.mod_manager.executable;
|
||||
}
|
||||
|
||||
return win_emu.mod_manager.find_by_address(win_emu.process.previous_ip); //
|
||||
return win_emu.mod_manager.find_by_address(previous_ip); //
|
||||
});
|
||||
|
||||
const auto is_current_binary_interesting = utils::make_lazy([&] {
|
||||
return is_main_exe || (binary && c.settings->modules.contains(binary->name)); //
|
||||
});
|
||||
|
||||
const auto is_in_interesting_module = [&] {
|
||||
@@ -191,12 +339,16 @@ namespace
|
||||
return false;
|
||||
}
|
||||
|
||||
return (binary && c.settings->modules.contains(binary->name)) ||
|
||||
(previous_binary && c.settings->modules.contains(previous_binary->name));
|
||||
return is_current_binary_interesting || (previous_binary && c.settings->modules.contains(previous_binary->name));
|
||||
};
|
||||
|
||||
if (c.settings->instruction_summary && (is_current_binary_interesting || !binary))
|
||||
{
|
||||
record_instruction(c, address);
|
||||
}
|
||||
|
||||
const auto is_interesting_call = is_previous_main_exe //
|
||||
|| is_main_exe //
|
||||
|| !previous_binary //
|
||||
|| is_in_interesting_module();
|
||||
|
||||
if (!c.has_reached_main && c.settings->concise_logging && !c.settings->silent && is_main_exe)
|
||||
@@ -211,30 +363,73 @@ namespace
|
||||
}
|
||||
|
||||
const auto export_entry = binary->address_names.find(address);
|
||||
if (export_entry != binary->address_names.end() &&
|
||||
!c.settings->ignored_functions.contains(export_entry->second))
|
||||
if (export_entry != binary->address_names.end())
|
||||
{
|
||||
const auto rsp = win_emu.emu().read_stack_pointer();
|
||||
|
||||
uint64_t return_address{};
|
||||
win_emu.emu().try_read_memory(rsp, &return_address, sizeof(return_address));
|
||||
|
||||
const auto* mod_name = win_emu.mod_manager.find_name(return_address);
|
||||
|
||||
win_emu.log.print(is_interesting_call ? color::yellow : color::dark_gray,
|
||||
"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);
|
||||
|
||||
if (is_interesting_call)
|
||||
if (!c.settings->ignored_functions.contains(export_entry->second))
|
||||
{
|
||||
handle_function_details(c, export_entry->second);
|
||||
win_emu.log.print(is_interesting_call ? color::yellow : color::dark_gray,
|
||||
"Executing function: %s (%s) (0x%" PRIx64 ") via (0x%" PRIx64 ") %s\n", export_entry->second.c_str(),
|
||||
binary->name.c_str(), address, previous_ip, previous_binary ? previous_binary->name.c_str() : "<N/A>");
|
||||
|
||||
if (is_interesting_call)
|
||||
{
|
||||
handle_function_details(c, export_entry->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (address == binary->entry_point)
|
||||
{
|
||||
win_emu.log.print(is_interesting_call ? color::yellow : color::gray,
|
||||
"Executing entry point: %s (0x%" PRIx64 ")\n", binary->name.c_str(), address);
|
||||
win_emu.log.print(is_interesting_call ? color::yellow : color::gray, "Executing entry point: %s (0x%" PRIx64 ")\n",
|
||||
binary->name.c_str(), address);
|
||||
}
|
||||
else if (is_previous_main_exe && binary != previous_binary && !is_return(c.d, c.win_emu->emu(), previous_ip))
|
||||
{
|
||||
auto nearest_entry = binary->address_names.upper_bound(address);
|
||||
if (nearest_entry == binary->address_names.begin())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
--nearest_entry;
|
||||
|
||||
win_emu.log.print(is_interesting_call ? color::yellow : color::dark_gray,
|
||||
"Transition to foreign code: %s+0x%" PRIx64 " (%s) (0x%" PRIx64 ") via (0x%" PRIx64 ") %s\n",
|
||||
nearest_entry->second.c_str(), address - nearest_entry->first, binary->name.c_str(), address, previous_ip,
|
||||
previous_binary ? previous_binary->name.c_str() : "<N/A>");
|
||||
}
|
||||
}
|
||||
|
||||
void handle_rdtsc(const analysis_context& c)
|
||||
{
|
||||
auto& win_emu = *c.win_emu;
|
||||
auto& emu = win_emu.emu();
|
||||
|
||||
const auto rip = emu.read_instruction_pointer();
|
||||
const auto mod = get_module_if_interesting(win_emu.mod_manager, c.settings->modules, rip);
|
||||
|
||||
if (!mod.has_value())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
win_emu.log.print(color::blue, "Executing RDTSC instruction at 0x%" PRIx64 " (%s)\n", rip, (*mod) ? (*mod)->name.c_str() : "<N/A>");
|
||||
}
|
||||
|
||||
void handle_rdtscp(const analysis_context& c)
|
||||
{
|
||||
auto& win_emu = *c.win_emu;
|
||||
auto& emu = win_emu.emu();
|
||||
|
||||
const auto rip = emu.read_instruction_pointer();
|
||||
const auto mod = get_module_if_interesting(win_emu.mod_manager, c.settings->modules, rip);
|
||||
|
||||
if (!mod.has_value())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
win_emu.log.print(color::blue, "Executing RDTSCP instruction at 0x%" PRIx64 " (%s)\n", rip,
|
||||
(*mod) ? (*mod)->name.c_str() : "<N/A>");
|
||||
}
|
||||
|
||||
emulator_callbacks::continuation handle_syscall(const analysis_context& c, const uint32_t syscall_id,
|
||||
@@ -246,13 +441,14 @@ namespace
|
||||
const auto address = emu.read_instruction_pointer();
|
||||
const auto* mod = win_emu.mod_manager.find_by_address(address);
|
||||
const auto is_sus_module = mod != win_emu.mod_manager.ntdll && mod != win_emu.mod_manager.win32u;
|
||||
const auto previous_ip = win_emu.current_thread().previous_ip;
|
||||
|
||||
if (is_sus_module)
|
||||
{
|
||||
win_emu.log.print(color::blue, "Executing inline syscall: %.*s (0x%X) at 0x%" PRIx64 " (%s)\n",
|
||||
STR_VIEW_VA(syscall_name), syscall_id, address, mod ? mod->name.c_str() : "<N/A>");
|
||||
win_emu.log.print(color::blue, "Executing inline syscall: %.*s (0x%X) at 0x%" PRIx64 " (%s)\n", STR_VIEW_VA(syscall_name),
|
||||
syscall_id, address, mod ? mod->name.c_str() : "<N/A>");
|
||||
}
|
||||
else if (mod->is_within(win_emu.process.previous_ip))
|
||||
else if (mod->is_within(previous_ip))
|
||||
{
|
||||
const auto rsp = emu.read_stack_pointer();
|
||||
|
||||
@@ -261,18 +457,16 @@ namespace
|
||||
|
||||
const auto* caller_mod_name = win_emu.mod_manager.find_name(return_address);
|
||||
|
||||
win_emu.log.print(color::dark_gray,
|
||||
"Executing syscall: %.*s (0x%X) at 0x%" PRIx64 " via 0x%" PRIx64 " (%s)\n",
|
||||
win_emu.log.print(color::dark_gray, "Executing syscall: %.*s (0x%X) at 0x%" PRIx64 " via 0x%" PRIx64 " (%s)\n",
|
||||
STR_VIEW_VA(syscall_name), syscall_id, address, return_address, caller_mod_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto* previous_mod = win_emu.mod_manager.find_by_address(win_emu.process.previous_ip);
|
||||
const auto* previous_mod = win_emu.mod_manager.find_by_address(previous_ip);
|
||||
|
||||
win_emu.log.print(color::blue,
|
||||
"Crafted out-of-line syscall: %.*s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64 " (%s)\n",
|
||||
STR_VIEW_VA(syscall_name), syscall_id, address, mod ? mod->name.c_str() : "<N/A>",
|
||||
win_emu.process.previous_ip, previous_mod ? previous_mod->name.c_str() : "<N/A>");
|
||||
win_emu.log.print(color::blue, "Crafted out-of-line syscall: %.*s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64 " (%s)\n",
|
||||
STR_VIEW_VA(syscall_name), syscall_id, address, mod ? mod->name.c_str() : "<N/A>", previous_ip,
|
||||
previous_mod ? previous_mod->name.c_str() : "<N/A>");
|
||||
}
|
||||
|
||||
return instruction_hook_continuation::run_instruction;
|
||||
@@ -293,6 +487,68 @@ namespace
|
||||
c.win_emu->log.info("%.*s%s", static_cast<int>(data.size()), data.data(), data.ends_with("\n") ? "" : "\n");
|
||||
}
|
||||
}
|
||||
|
||||
void watch_import_table(analysis_context& c)
|
||||
{
|
||||
c.win_emu->setup_process_if_necessary();
|
||||
|
||||
const auto& import_list = c.win_emu->mod_manager.executable->imports;
|
||||
if (import_list.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto min = std::numeric_limits<uint64_t>::max();
|
||||
auto max = std::numeric_limits<uint64_t>::min();
|
||||
|
||||
for (const auto& imports : import_list | std::views::values)
|
||||
{
|
||||
for (const auto& import : imports)
|
||||
{
|
||||
min = std::min(import.address, min);
|
||||
max = std::max(import.address, max);
|
||||
}
|
||||
}
|
||||
|
||||
c.win_emu->emu().hook_memory_read(min, max - min, [&c](const uint64_t address, const void*, size_t) {
|
||||
const auto& import_list = c.win_emu->mod_manager.executable->imports;
|
||||
|
||||
const auto rip = c.win_emu->emu().read_instruction_pointer();
|
||||
if (!c.win_emu->mod_manager.executable->is_within(rip))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& [module_name, imports] : import_list)
|
||||
{
|
||||
for (const auto& import : imports)
|
||||
{
|
||||
if (address != import.address)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
accessed_import access{};
|
||||
|
||||
access.address = c.win_emu->emu().read_memory<uint64_t>(address);
|
||||
|
||||
access.access_rip = c.win_emu->emu().read_instruction_pointer();
|
||||
access.accessor_module = c.win_emu->mod_manager.find_name(access.access_rip);
|
||||
|
||||
access.import_name = import.name;
|
||||
access.import_module = module_name;
|
||||
|
||||
const auto& t = c.win_emu->current_thread();
|
||||
access.thread_id = t.id;
|
||||
access.access_inst_count = t.executed_instructions;
|
||||
|
||||
c.accessed_imports.push_back(std::move(access));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void register_analysis_callbacks(analysis_context& c)
|
||||
@@ -301,6 +557,8 @@ void register_analysis_callbacks(analysis_context& c)
|
||||
|
||||
cb.on_stdout = make_callback(c, handle_stdout);
|
||||
cb.on_syscall = make_callback(c, handle_syscall);
|
||||
cb.on_rdtsc = make_callback(c, handle_rdtsc);
|
||||
cb.on_rdtscp = make_callback(c, handle_rdtscp);
|
||||
cb.on_ioctrl = make_callback(c, handle_ioctrl);
|
||||
|
||||
cb.on_memory_protect = make_callback(c, handle_memory_protect);
|
||||
@@ -314,28 +572,32 @@ void register_analysis_callbacks(analysis_context& c)
|
||||
cb.on_thread_set_name = make_callback(c, handle_thread_set_name);
|
||||
|
||||
cb.on_instruction = make_callback(c, handle_instruction);
|
||||
cb.on_debug_string = make_callback(c, handle_debug_string);
|
||||
cb.on_generic_access = make_callback(c, handle_generic_access);
|
||||
cb.on_generic_activity = make_callback(c, handle_generic_activity);
|
||||
cb.on_suspicious_activity = make_callback(c, handle_suspicious_activity);
|
||||
|
||||
watch_import_table(c);
|
||||
}
|
||||
|
||||
mapped_module* get_module_if_interesting(module_manager& manager, const string_set& modules, uint64_t address)
|
||||
std::optional<mapped_module*> get_module_if_interesting(module_manager& manager, const string_set& modules, const uint64_t address)
|
||||
{
|
||||
if (manager.executable->is_within(address))
|
||||
{
|
||||
return manager.executable;
|
||||
}
|
||||
|
||||
if (modules.empty())
|
||||
auto* mod = manager.find_by_address(address);
|
||||
if (!mod)
|
||||
{
|
||||
// Not being part of any module is interesting
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* mod = manager.find_by_address(address);
|
||||
if (mod && modules.contains(mod->name))
|
||||
if (modules.contains(mod->name))
|
||||
{
|
||||
return mod;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include "disassembler.hpp"
|
||||
|
||||
struct mapped_module;
|
||||
class module_manager;
|
||||
@@ -15,11 +16,23 @@ struct analysis_settings
|
||||
bool verbose_logging{false};
|
||||
bool silent{false};
|
||||
bool buffer_stdout{false};
|
||||
bool instruction_summary{false};
|
||||
|
||||
string_set modules{};
|
||||
string_set ignored_functions{};
|
||||
};
|
||||
|
||||
struct accessed_import
|
||||
{
|
||||
uint64_t address{};
|
||||
uint32_t thread_id{};
|
||||
uint64_t access_rip{};
|
||||
uint64_t access_inst_count{};
|
||||
std::string accessor_module{};
|
||||
std::string import_name{};
|
||||
std::string import_module{};
|
||||
};
|
||||
|
||||
struct analysis_context
|
||||
{
|
||||
const analysis_settings* settings{};
|
||||
@@ -27,7 +40,11 @@ struct analysis_context
|
||||
|
||||
std::string output{};
|
||||
bool has_reached_main{false};
|
||||
|
||||
disassembler d{};
|
||||
std::unordered_map<uint32_t, uint64_t> instructions{};
|
||||
std::vector<accessed_import> accessed_imports{};
|
||||
};
|
||||
|
||||
void register_analysis_callbacks(analysis_context& c);
|
||||
mapped_module* get_module_if_interesting(module_manager& manager, const string_set& modules, uint64_t address);
|
||||
std::optional<mapped_module*> get_module_if_interesting(module_manager& manager, const string_set& modules, uint64_t address);
|
||||
|
||||
72
src/analyzer/disassembler.cpp
Normal file
72
src/analyzer/disassembler.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "std_include.hpp"
|
||||
#include "disassembler.hpp"
|
||||
#include <utils/finally.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
void cse(const cs_err error)
|
||||
{
|
||||
if (error != CS_ERR_OK)
|
||||
{
|
||||
throw std::runtime_error(cs_strerror(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disassembler::disassembler()
|
||||
{
|
||||
auto deleter = utils::finally([&] { this->release(); });
|
||||
|
||||
cse(cs_open(CS_ARCH_X86, CS_MODE_64, &this->handle_));
|
||||
cse(cs_option(this->handle_, CS_OPT_DETAIL, CS_OPT_ON));
|
||||
|
||||
deleter.cancel();
|
||||
}
|
||||
|
||||
disassembler::~disassembler()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
disassembler::disassembler(disassembler&& obj) noexcept
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
|
||||
disassembler& disassembler::operator=(disassembler&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->release();
|
||||
this->handle_ = obj.handle_;
|
||||
obj.handle_ = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void disassembler::release()
|
||||
{
|
||||
if (this->handle_)
|
||||
{
|
||||
cs_close(&this->handle_);
|
||||
this->handle_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
instructions disassembler::disassemble(const std::span<const uint8_t> data, const size_t count) const
|
||||
{
|
||||
cs_insn* insts{};
|
||||
const auto inst_count = cs_disasm(this->handle_, data.data(), data.size(), count, 0, &insts);
|
||||
return instructions{std::span(insts, inst_count)};
|
||||
}
|
||||
|
||||
void instructions::release()
|
||||
{
|
||||
if (!this->instructions_.empty())
|
||||
{
|
||||
cs_free(this->instructions_.data(), this->instructions_.size());
|
||||
}
|
||||
|
||||
this->instructions_ = {};
|
||||
}
|
||||
104
src/analyzer/disassembler.hpp
Normal file
104
src/analyzer/disassembler.hpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#include <capstone/capstone.h>
|
||||
|
||||
class instructions
|
||||
{
|
||||
public:
|
||||
instructions() = default;
|
||||
~instructions()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
instructions(instructions&& obj) noexcept
|
||||
: instructions()
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
|
||||
instructions& operator=(instructions&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->release();
|
||||
this->instructions_ = obj.instructions_;
|
||||
obj.instructions_ = {};
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
instructions(const instructions&) = delete;
|
||||
instructions& operator=(const instructions&) = delete;
|
||||
|
||||
operator std::span<cs_insn>() const
|
||||
{
|
||||
return this->instructions_;
|
||||
}
|
||||
|
||||
bool empty() const noexcept
|
||||
{
|
||||
return this->instructions_.empty();
|
||||
}
|
||||
|
||||
size_t size() const noexcept
|
||||
{
|
||||
return this->instructions_.size();
|
||||
}
|
||||
|
||||
const cs_insn* data() const noexcept
|
||||
{
|
||||
return this->instructions_.data();
|
||||
}
|
||||
|
||||
const cs_insn& operator[](const size_t index) const
|
||||
{
|
||||
return this->instructions_[index];
|
||||
}
|
||||
|
||||
auto begin() const
|
||||
{
|
||||
return this->instructions_.begin();
|
||||
}
|
||||
auto end() const
|
||||
{
|
||||
return this->instructions_.end();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class disassembler;
|
||||
std::span<cs_insn> instructions_{};
|
||||
|
||||
explicit instructions(const std::span<cs_insn> insts)
|
||||
: instructions_(insts)
|
||||
{
|
||||
}
|
||||
|
||||
void release();
|
||||
};
|
||||
|
||||
class disassembler
|
||||
{
|
||||
public:
|
||||
disassembler();
|
||||
~disassembler();
|
||||
|
||||
disassembler(disassembler&& obj) noexcept;
|
||||
disassembler& operator=(disassembler&& obj) noexcept;
|
||||
|
||||
disassembler(const disassembler& obj) = delete;
|
||||
disassembler& operator=(const disassembler& obj) = delete;
|
||||
|
||||
instructions disassemble(std::span<const uint8_t> data, size_t count) const;
|
||||
|
||||
csh get_handle() const
|
||||
{
|
||||
return this->handle_;
|
||||
}
|
||||
|
||||
private:
|
||||
csh handle_{};
|
||||
|
||||
void release();
|
||||
};
|
||||
@@ -4,16 +4,16 @@
|
||||
#include <backend_selection.hpp>
|
||||
#include <win_x64_gdb_stub_handler.hpp>
|
||||
#include <minidump_loader.hpp>
|
||||
#include <scoped_hook.hpp>
|
||||
|
||||
#include "object_watching.hpp"
|
||||
#include "snapshot.hpp"
|
||||
#include "analysis.hpp"
|
||||
#include "tenet_tracer.hpp"
|
||||
|
||||
#include <utils/finally.hpp>
|
||||
#include <utils/interupt_handler.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef OS_EMSCRIPTEN
|
||||
#include <event_handler.hpp>
|
||||
#endif
|
||||
@@ -24,6 +24,8 @@ namespace
|
||||
{
|
||||
mutable bool use_gdb{false};
|
||||
bool log_executable_access{false};
|
||||
bool log_foreign_module_access{false};
|
||||
bool tenet_trace{false};
|
||||
std::filesystem::path dump{};
|
||||
std::filesystem::path minidump_path{};
|
||||
std::string registry_path{"./registry"};
|
||||
@@ -31,8 +33,7 @@ namespace
|
||||
std::unordered_map<windows_path, std::filesystem::path> path_mappings{};
|
||||
};
|
||||
|
||||
void split_and_insert(std::set<std::string, std::less<>>& container, const std::string_view str,
|
||||
const char splitter = ',')
|
||||
void split_and_insert(std::set<std::string, std::less<>>& container, const std::string_view str, const char splitter = ',')
|
||||
{
|
||||
size_t current_start = 0;
|
||||
for (size_t i = 0; i < str.size(); ++i)
|
||||
@@ -57,8 +58,113 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
void watch_system_objects(windows_emulator& win_emu, const std::set<std::string, std::less<>>& modules,
|
||||
const bool verbose)
|
||||
#if !defined(__GNUC__) || defined(__clang__)
|
||||
struct analysis_state
|
||||
{
|
||||
windows_emulator& win_emu_;
|
||||
scoped_hook env_data_hook_;
|
||||
scoped_hook env_ptr_hook_;
|
||||
scoped_hook params_hook_;
|
||||
scoped_hook ldr_hook_;
|
||||
std::set<std::string, std::less<>> modules_;
|
||||
bool verbose_;
|
||||
|
||||
analysis_state(windows_emulator& win_emu, std::set<std::string, std::less<>> modules, const bool verbose)
|
||||
: win_emu_(win_emu),
|
||||
env_data_hook_(win_emu.emu()),
|
||||
env_ptr_hook_(win_emu.emu()),
|
||||
params_hook_(win_emu.emu()),
|
||||
ldr_hook_(win_emu.emu()),
|
||||
modules_(std::move(modules)),
|
||||
verbose_(verbose)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
emulator_object<RTL_USER_PROCESS_PARAMETERS64> get_process_params(windows_emulator& win_emu)
|
||||
{
|
||||
const auto peb = win_emu.process.peb.read();
|
||||
return {win_emu.emu(), peb.ProcessParameters};
|
||||
}
|
||||
|
||||
uint64_t get_environment_ptr(windows_emulator& win_emu)
|
||||
{
|
||||
const auto process_params = get_process_params(win_emu);
|
||||
return process_params.read().Environment;
|
||||
}
|
||||
|
||||
size_t get_environment_size(const x86_64_emulator& emu, const uint64_t env)
|
||||
{
|
||||
std::array<uint8_t, 4> data{};
|
||||
std::array<uint8_t, 4> empty{};
|
||||
|
||||
for (size_t i = 0; i < 0x100000; ++i)
|
||||
{
|
||||
if (!emu.try_read_memory(env + i, data.data(), data.size()))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
if (data == empty)
|
||||
{
|
||||
return i + data.size();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
emulator_hook* install_env_hook(const std::shared_ptr<analysis_state>& state)
|
||||
{
|
||||
const auto process_params = get_process_params(state->win_emu_);
|
||||
|
||||
auto install_env_access_hook = [state] {
|
||||
const auto env_ptr = get_environment_ptr(state->win_emu_);
|
||||
const auto env_size = get_environment_size(state->win_emu_.emu(), env_ptr);
|
||||
if (!env_size)
|
||||
{
|
||||
state->env_data_hook_.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
auto hook_handler = [state, env_ptr](const uint64_t address, const void*, const size_t size) {
|
||||
const auto rip = state->win_emu_.emu().read_instruction_pointer();
|
||||
const auto* mod = state->win_emu_.mod_manager.find_by_address(rip);
|
||||
const auto is_main_access = !mod || (mod == state->win_emu_.mod_manager.executable || state->modules_.contains(mod->name));
|
||||
|
||||
if (!is_main_access && !state->verbose_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto offset = address - env_ptr;
|
||||
const auto* mod_name = mod ? mod->name.c_str() : "<N/A>";
|
||||
state->win_emu_.log.print(is_main_access ? color::green : color::dark_gray,
|
||||
"Environment access: 0x%" PRIx64 " (0x%zX) at 0x%" PRIx64 " (%s)\n", offset, size, rip, mod_name);
|
||||
};
|
||||
|
||||
state->env_data_hook_ = state->win_emu_.emu().hook_memory_read(env_ptr, env_size, std::move(hook_handler));
|
||||
};
|
||||
|
||||
install_env_access_hook();
|
||||
|
||||
auto& win_emu = state->win_emu_;
|
||||
return state->win_emu_.emu().hook_memory_write(
|
||||
process_params.value() + offsetof(RTL_USER_PROCESS_PARAMETERS64, Environment), 0x8,
|
||||
[&win_emu, install = std::move(install_env_access_hook)](const uint64_t address, const void*, size_t) {
|
||||
const auto new_process_params = get_process_params(win_emu);
|
||||
|
||||
const auto target_address = new_process_params.value() + offsetof(RTL_USER_PROCESS_PARAMETERS64, Environment);
|
||||
|
||||
if (address == target_address)
|
||||
{
|
||||
install();
|
||||
}
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
void watch_system_objects(windows_emulator& win_emu, const std::set<std::string, std::less<>>& modules, const bool verbose)
|
||||
{
|
||||
win_emu.setup_process_if_necessary();
|
||||
|
||||
@@ -69,26 +175,31 @@ namespace
|
||||
#if !defined(__GNUC__) || defined(__clang__)
|
||||
watch_object(win_emu, modules, *win_emu.current_thread().teb, verbose);
|
||||
watch_object(win_emu, modules, win_emu.process.peb, verbose);
|
||||
watch_object(win_emu, modules, emulator_object<KUSER_SHARED_DATA64>{win_emu.emu(), kusd_mmio::address()},
|
||||
verbose);
|
||||
watch_object<KUSER_SHARED_DATA64>(win_emu, modules, kusd_mmio::address(), verbose);
|
||||
|
||||
auto* params_hook = watch_object(win_emu, modules, win_emu.process.process_params, verbose);
|
||||
auto state = std::make_shared<analysis_state>(win_emu, modules, verbose);
|
||||
|
||||
state->params_hook_ = watch_object(win_emu, modules, win_emu.process.process_params, verbose);
|
||||
state->ldr_hook_ = watch_object<PEB_LDR_DATA64>(win_emu, modules, win_emu.process.peb.read().Ldr, verbose);
|
||||
|
||||
const auto update_env_hook = [state] {
|
||||
state->env_ptr_hook_ = install_env_hook(state); //
|
||||
};
|
||||
|
||||
update_env_hook();
|
||||
|
||||
win_emu.emu().hook_memory_write(win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters), 0x8,
|
||||
[state, update_env = std::move(update_env_hook)](const uint64_t, const void*, size_t) {
|
||||
const auto new_ptr = state->win_emu_.process.peb.read().ProcessParameters;
|
||||
state->params_hook_ = watch_object<RTL_USER_PROCESS_PARAMETERS64>(
|
||||
state->win_emu_, state->modules_, new_ptr, state->verbose_);
|
||||
update_env();
|
||||
});
|
||||
|
||||
win_emu.emu().hook_memory_write(
|
||||
win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters), 0x8,
|
||||
[&win_emu, verbose, params_hook, modules](const uint64_t address, const void*, size_t) mutable {
|
||||
const auto target_address = win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters);
|
||||
|
||||
if (address == target_address)
|
||||
{
|
||||
const emulator_object<RTL_USER_PROCESS_PARAMETERS64> obj{
|
||||
win_emu.emu(),
|
||||
win_emu.emu().read_memory<uint64_t>(address),
|
||||
};
|
||||
|
||||
win_emu.emu().delete_hook(params_hook);
|
||||
params_hook = watch_object(win_emu, modules, obj, verbose);
|
||||
}
|
||||
win_emu.process.peb.value() + offsetof(PEB64, Ldr), 0x8, [state](const uint64_t, const void*, size_t) {
|
||||
const auto new_ptr = state->win_emu_.process.peb.read().Ldr;
|
||||
state->ldr_hook_ = watch_object<PEB_LDR_DATA64>(state->win_emu_, state->modules_, new_ptr, state->verbose_);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
@@ -110,12 +221,37 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
void print_instruction_summary(const analysis_context& c)
|
||||
{
|
||||
std::map<uint64_t, std::vector<uint32_t>> instruction_counts{};
|
||||
|
||||
for (const auto& [instruction, count] : c.instructions)
|
||||
{
|
||||
instruction_counts[count].push_back(instruction);
|
||||
}
|
||||
|
||||
c.win_emu->log.print(color::white, "Instruction summary:\n");
|
||||
|
||||
for (const auto& [count, instructions] : instruction_counts)
|
||||
{
|
||||
for (const auto& instruction : instructions)
|
||||
{
|
||||
const auto* mnemonic = cs_insn_name(c.d.get_handle(), instruction);
|
||||
c.win_emu->log.print(color::white, "%s: %" PRIx64 "\n", mnemonic, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_post_emulation_work(const analysis_context& c)
|
||||
{
|
||||
if (c.settings->instruction_summary)
|
||||
{
|
||||
print_instruction_summary(c);
|
||||
}
|
||||
|
||||
if (c.settings->buffer_stdout)
|
||||
{
|
||||
c.win_emu->log.info("%.*s%s", static_cast<int>(c.output.size()), c.output.data(),
|
||||
c.output.ends_with("\n") ? "" : "\n");
|
||||
c.win_emu->log.info("%.*s%s", static_cast<int>(c.output.size()), c.output.data(), c.output.ends_with("\n") ? "" : "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +264,7 @@ namespace
|
||||
const auto value = signals_received++;
|
||||
if (value == 1)
|
||||
{
|
||||
win_emu.log.log("Exit already requested. Press CTRL+C again to force kill!");
|
||||
win_emu.log.log("Exit already requested. Press CTRL+C again to force kill!\n");
|
||||
}
|
||||
else if (value >= 2)
|
||||
{
|
||||
@@ -184,8 +320,7 @@ namespace
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
do_post_emulation_work(c);
|
||||
win_emu.log.error("Emulation failed at: 0x%" PRIx64 " - %s\n", win_emu.emu().read_instruction_pointer(),
|
||||
e.what());
|
||||
win_emu.log.error("Emulation failed at: 0x%" PRIx64 " - %s\n", win_emu.emu().read_instruction_pointer(), e.what());
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
@@ -209,8 +344,7 @@ namespace
|
||||
{
|
||||
do_post_emulation_work(c);
|
||||
win_emu.log.disable_output(false);
|
||||
win_emu.log.print(success ? color::green : color::red, "Emulation terminated with status: %X\n",
|
||||
*exit_status);
|
||||
win_emu.log.print(success ? color::green : color::red, "Emulation terminated with status: %X\n", *exit_status);
|
||||
}
|
||||
|
||||
return success;
|
||||
@@ -262,8 +396,7 @@ namespace
|
||||
return std::make_unique<windows_emulator>(create_x86_64_emulator(), std::move(app_settings), settings);
|
||||
}
|
||||
|
||||
std::unique_ptr<windows_emulator> setup_emulator(const analysis_options& options,
|
||||
const std::span<const std::string_view> args)
|
||||
std::unique_ptr<windows_emulator> setup_emulator(const analysis_options& options, const std::span<const std::string_view> args)
|
||||
{
|
||||
if (!options.dump.empty())
|
||||
{
|
||||
@@ -284,6 +417,33 @@ namespace
|
||||
return create_application_emulator(options, args);
|
||||
}
|
||||
|
||||
const char* get_module_memory_region_name(const mapped_module& mod, const uint64_t address)
|
||||
{
|
||||
if (!mod.is_within(address))
|
||||
{
|
||||
return "outside???";
|
||||
}
|
||||
|
||||
uint64_t first_section = mod.image_base + mod.size_of_image;
|
||||
|
||||
for (const auto& section : mod.sections)
|
||||
{
|
||||
first_section = std::min(first_section, section.region.start);
|
||||
|
||||
if (is_within_start_and_length(address, section.region.start, section.region.length))
|
||||
{
|
||||
return section.name.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
if (address < first_section)
|
||||
{
|
||||
return "header";
|
||||
}
|
||||
|
||||
return "?";
|
||||
}
|
||||
|
||||
bool run(const analysis_options& options, const std::span<const std::string_view> args)
|
||||
{
|
||||
analysis_context context{
|
||||
@@ -296,6 +456,13 @@ namespace
|
||||
|
||||
win_emu->log.log("Using emulator: %s\n", win_emu->emu().get_name().c_str());
|
||||
|
||||
std::optional<tenet_tracer> tenet_tracer{};
|
||||
if (options.tenet_trace)
|
||||
{
|
||||
win_emu->log.log("Tenet Tracer enabled. Output: tenet_trace.log\n");
|
||||
tenet_tracer.emplace(*win_emu, "tenet_trace.log");
|
||||
}
|
||||
|
||||
register_analysis_callbacks(context);
|
||||
watch_system_objects(*win_emu, options.modules, options.verbose_logging);
|
||||
|
||||
@@ -303,20 +470,68 @@ namespace
|
||||
|
||||
const auto concise_logging = !options.verbose_logging;
|
||||
|
||||
win_emu->emu().hook_instruction(x86_hookable_instructions::cpuid, [&] {
|
||||
win_emu->emu().hook_instruction(x86_hookable_instructions::sgdt, [&](const uint64_t) {
|
||||
const auto rip = win_emu->emu().read_instruction_pointer();
|
||||
auto* mod = get_module_if_interesting(win_emu->mod_manager, options.modules, rip);
|
||||
const auto mod = get_module_if_interesting(win_emu->mod_manager, options.modules, rip);
|
||||
|
||||
if (mod)
|
||||
if (mod.has_value())
|
||||
{
|
||||
const auto leaf = win_emu->emu().reg<uint32_t>(x86_register::eax);
|
||||
win_emu->log.print(color::blue, "Executing CPUID instruction with leaf 0x%X at 0x%" PRIx64 " (%s)\n",
|
||||
leaf, rip, mod->name.c_str());
|
||||
win_emu->log.print(color::blue, "Executing SGDT instruction at 0x%" PRIx64 " (%s)\n", rip,
|
||||
(*mod) ? (*mod)->name.c_str() : "<N/A>");
|
||||
}
|
||||
|
||||
return instruction_hook_continuation::run_instruction;
|
||||
});
|
||||
|
||||
win_emu->emu().hook_instruction(x86_hookable_instructions::cpuid, [&] {
|
||||
const auto rip = win_emu->emu().read_instruction_pointer();
|
||||
const auto mod = get_module_if_interesting(win_emu->mod_manager, options.modules, rip);
|
||||
|
||||
if (mod.has_value())
|
||||
{
|
||||
const auto leaf = win_emu->emu().reg<uint32_t>(x86_register::eax);
|
||||
win_emu->log.print(color::blue, "Executing CPUID instruction with leaf 0x%X at 0x%" PRIx64 " (%s)\n", leaf, rip,
|
||||
(*mod) ? (*mod)->name.c_str() : "<N/A>");
|
||||
}
|
||||
|
||||
return instruction_hook_continuation::run_instruction;
|
||||
});
|
||||
|
||||
if (options.log_foreign_module_access)
|
||||
{
|
||||
auto module_cache = std::make_shared<std::map<std::string, uint64_t>>();
|
||||
win_emu->emu().hook_memory_read(
|
||||
0, std::numeric_limits<uint64_t>::max(), [&, module_cache](const uint64_t address, const void*, size_t) {
|
||||
const auto rip = win_emu->emu().read_instruction_pointer();
|
||||
const auto accessor = get_module_if_interesting(win_emu->mod_manager, options.modules, rip);
|
||||
|
||||
if (!accessor.has_value())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* mod = win_emu->mod_manager.find_by_address(address);
|
||||
if (!mod || mod == *accessor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (concise_logging)
|
||||
{
|
||||
const auto count = ++(*module_cache)[mod->name];
|
||||
if (count > 100 && count % 100000 != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto* region_name = get_module_memory_region_name(*mod, address);
|
||||
|
||||
win_emu->log.print(color::pink, "Reading from module %s at 0x%" PRIx64 " (%s) via 0x%" PRIx64 " (%s)\n",
|
||||
mod->name.c_str(), address, region_name, rip, (*accessor) ? (*accessor)->name.c_str() : "<N/A>");
|
||||
});
|
||||
}
|
||||
|
||||
if (options.log_executable_access)
|
||||
{
|
||||
for (const auto& section : exe.sections)
|
||||
@@ -337,14 +552,13 @@ namespace
|
||||
{
|
||||
static uint64_t count{0};
|
||||
++count;
|
||||
if (count > 100 && count % 100000 != 0)
|
||||
if (count > 20 && count % 100000 != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
win_emu->log.print(color::green,
|
||||
"Reading from executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
|
||||
win_emu->log.print(color::green, "Reading from executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
|
||||
section.name.c_str(), address, rip);
|
||||
};
|
||||
|
||||
@@ -365,8 +579,7 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
win_emu->log.print(color::blue,
|
||||
"Writing to executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
|
||||
win_emu->log.print(color::blue, "Writing to executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
|
||||
section.name.c_str(), address, rip);
|
||||
};
|
||||
|
||||
@@ -399,15 +612,18 @@ namespace
|
||||
printf(" -s, --silent Silent mode\n");
|
||||
printf(" -v, --verbose Verbose logging\n");
|
||||
printf(" -b, --buffer Buffer stdout\n");
|
||||
printf(" -f, --foreign Log read access to foreign modules\n");
|
||||
printf(" -c, --concise Concise logging\n");
|
||||
printf(" -x, --exec Log r/w access to executable memory\n");
|
||||
printf(" -m, --module <module> Specify module to track\n");
|
||||
printf(" -e, --emulation <path> Set emulation root path\n");
|
||||
printf(" -a, --snapshot <path> Load snapshot dump from path\n");
|
||||
printf(" --minidump <path> Load minidump from path\n");
|
||||
printf(" -t, --tenet-trace Enable Tenet tracer\n");
|
||||
printf(" -i, --ignore <funcs> Comma-separated list of functions to ignore\n");
|
||||
printf(" -p, --path <src> <dst> Map Windows path to host path\n");
|
||||
printf(" -r, --registry <path> Set registry path (default: ./registry)\n\n");
|
||||
printf(" -is, --inst-summary Print a summary of executed instructions of the analyzed modules\n");
|
||||
printf("Examples:\n");
|
||||
printf(" analyzer -v -e path/to/root myapp.exe\n");
|
||||
printf(" analyzer -e path/to/root -p c:/analysis-sample.exe /path/to/sample.exe c:/analysis-sample.exe\n");
|
||||
@@ -427,7 +643,8 @@ namespace
|
||||
print_help();
|
||||
std::exit(0);
|
||||
}
|
||||
else if (arg == "-d" || arg == "--debug")
|
||||
|
||||
if (arg == "-d" || arg == "--debug")
|
||||
{
|
||||
options.use_gdb = true;
|
||||
}
|
||||
@@ -447,10 +664,22 @@ namespace
|
||||
{
|
||||
options.log_executable_access = true;
|
||||
}
|
||||
else if (arg == "-f" || arg == "--foreign")
|
||||
{
|
||||
options.log_foreign_module_access = true;
|
||||
}
|
||||
else if (arg == "-c" || arg == "--concise")
|
||||
{
|
||||
options.concise_logging = true;
|
||||
}
|
||||
else if (arg == "-t" || arg == "--tenet-trace")
|
||||
{
|
||||
options.tenet_trace = true;
|
||||
}
|
||||
else if (arg == "-is" || arg == "--inst-summary")
|
||||
{
|
||||
options.instruction_summary = true;
|
||||
}
|
||||
else if (arg == "-m" || arg == "--module")
|
||||
{
|
||||
if (args.size() < 2)
|
||||
@@ -555,10 +784,10 @@ namespace
|
||||
catch (std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
|
||||
#if defined(_WIN32) && 0
|
||||
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
||||
#endif
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
puts("An unknown exception occured");
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -5,17 +5,17 @@
|
||||
#include <cinttypes>
|
||||
|
||||
template <typename T>
|
||||
emulator_hook* watch_object(windows_emulator& emu, const std::set<std::string, std::less<>>& modules,
|
||||
emulator_object<T> object, const auto verbose)
|
||||
emulator_hook* watch_object(windows_emulator& emu, const std::set<std::string, std::less<>>& modules, emulator_object<T> object,
|
||||
const auto verbose)
|
||||
{
|
||||
const reflect_type_info<T> info{};
|
||||
|
||||
return emu.emu().hook_memory_read(
|
||||
object.value(), static_cast<size_t>(object.size()),
|
||||
[i = std::move(info), object, &emu, verbose, modules](const uint64_t address, const void*, size_t) {
|
||||
[i = std::move(info), object, &emu, verbose, modules](const uint64_t address, const void*, const size_t size) {
|
||||
const auto rip = emu.emu().read_instruction_pointer();
|
||||
const auto* mod = emu.mod_manager.find_by_address(rip);
|
||||
const auto is_main_access = mod == emu.mod_manager.executable || modules.contains(mod->name);
|
||||
const auto is_main_access = !mod || (mod == emu.mod_manager.executable || modules.contains(mod->name));
|
||||
|
||||
if (!verbose && !is_main_access)
|
||||
{
|
||||
@@ -25,19 +25,53 @@ emulator_hook* watch_object(windows_emulator& emu, const std::set<std::string, s
|
||||
if (!verbose)
|
||||
{
|
||||
static std::unordered_set<uint64_t> logged_addresses{};
|
||||
if (is_main_access && !logged_addresses.insert(address).second)
|
||||
|
||||
bool is_new = false;
|
||||
for (size_t j = 0; j < size; ++j)
|
||||
{
|
||||
is_new |= logged_addresses.insert(address + j).second;
|
||||
}
|
||||
|
||||
if (!is_new)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto offset = address - object.value();
|
||||
const auto start_offset = address - object.value();
|
||||
const auto end_offset = start_offset + size;
|
||||
const auto* mod_name = mod ? mod->name.c_str() : "<N/A>";
|
||||
const auto& type_name = i.get_type_name();
|
||||
const auto member_name = i.get_member_name(static_cast<size_t>(offset));
|
||||
|
||||
emu.log.print(is_main_access ? color::green : color::dark_gray,
|
||||
"Object access: %s - 0x%" PRIx64 " (%s) at 0x%" PRIx64 " (%s)\n", type_name.c_str(), offset,
|
||||
member_name.c_str(), rip, mod_name);
|
||||
for (auto offset = start_offset; offset < end_offset;)
|
||||
{
|
||||
const auto member_info = i.get_member_info(static_cast<size_t>(offset));
|
||||
if (!member_info.has_value())
|
||||
{
|
||||
const auto remaining_size = end_offset - offset;
|
||||
emu.log.print(is_main_access ? color::green : color::dark_gray,
|
||||
"Object access: %s - 0x%" PRIx64 " 0x%" PRIx64 " (<N/A>) at 0x%" PRIx64 " (%s)\n", type_name.c_str(),
|
||||
offset, remaining_size, rip, mod_name);
|
||||
break;
|
||||
}
|
||||
|
||||
const auto remaining_size = end_offset - offset;
|
||||
const auto member_end = member_info->offset + member_info->size;
|
||||
const auto member_access_size = member_end - offset;
|
||||
const auto access_size = std::min(remaining_size, member_access_size);
|
||||
|
||||
emu.log.print(is_main_access ? color::green : color::dark_gray,
|
||||
"Object access: %s - 0x%" PRIx64 " 0x%" PRIx64 " (%s) at 0x%" PRIx64 " (%s)\n", type_name.c_str(), offset,
|
||||
access_size, member_info->get_diff_name(static_cast<size_t>(offset)).c_str(), rip, mod_name);
|
||||
|
||||
offset = member_end;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
emulator_hook* watch_object(windows_emulator& emu, const std::set<std::string, std::less<>>& modules, const uint64_t address,
|
||||
const auto verbose)
|
||||
{
|
||||
return watch_object<T>(emu, modules, emulator_object<T>{emu.emu(), address}, verbose);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4189)
|
||||
#pragma warning(disable : 4308)
|
||||
#endif
|
||||
|
||||
@@ -42,34 +43,61 @@ class reflect_type_info
|
||||
reflect::for_each<T>([this](auto I) {
|
||||
const auto member_name = reflect::member_name<I, T>();
|
||||
const auto member_offset = reflect::offset_of<I, T>();
|
||||
const auto member_size = reflect::size_of<I, T>();
|
||||
|
||||
this->members_[member_offset] = member_name;
|
||||
this->members_[member_offset] = std::make_pair(std::string(member_name), member_size);
|
||||
});
|
||||
}
|
||||
|
||||
std::string get_member_name(const size_t offset) const
|
||||
{
|
||||
size_t last_offset{};
|
||||
std::string_view last_member{};
|
||||
|
||||
for (const auto& member : this->members_)
|
||||
const auto info = this->get_member_info(offset);
|
||||
if (!info.has_value())
|
||||
{
|
||||
if (offset == member.first)
|
||||
{
|
||||
return member.second;
|
||||
}
|
||||
|
||||
if (offset < member.first)
|
||||
{
|
||||
const auto diff = offset - last_offset;
|
||||
return std::string(last_member) + "+" + std::to_string(diff);
|
||||
}
|
||||
|
||||
last_offset = member.first;
|
||||
last_member = member.second;
|
||||
return "<N/A>";
|
||||
}
|
||||
|
||||
return "<N/A>";
|
||||
return info->get_diff_name(offset);
|
||||
}
|
||||
|
||||
struct member_info
|
||||
{
|
||||
std::string name{};
|
||||
size_t offset{};
|
||||
size_t size{};
|
||||
|
||||
std::string get_diff_name(const size_t access) const
|
||||
{
|
||||
const auto diff = access - this->offset;
|
||||
if (diff == 0)
|
||||
{
|
||||
return this->name;
|
||||
}
|
||||
|
||||
return this->name + "+" + std::to_string(diff);
|
||||
}
|
||||
};
|
||||
|
||||
std::optional<member_info> get_member_info(const size_t offset) const
|
||||
{
|
||||
auto entry = this->members_.upper_bound(offset);
|
||||
if (entry == this->members_.begin())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
--entry;
|
||||
|
||||
if (entry->first + entry->second.second <= offset)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return member_info{
|
||||
.name = entry->second.first,
|
||||
.offset = entry->first,
|
||||
.size = entry->second.second,
|
||||
};
|
||||
}
|
||||
|
||||
const std::string& get_type_name() const
|
||||
@@ -79,5 +107,5 @@ class reflect_type_info
|
||||
|
||||
private:
|
||||
std::string type_name_{};
|
||||
std::map<size_t, std::string> members_{};
|
||||
std::map<size_t, std::pair<std::string, size_t>> members_{};
|
||||
};
|
||||
|
||||
@@ -85,8 +85,7 @@ namespace snapshot
|
||||
|
||||
std::filesystem::path write_emulator_snapshot(const windows_emulator& win_emu, const bool log)
|
||||
{
|
||||
std::filesystem::path snapshot_file =
|
||||
get_main_executable_name(win_emu) + "-" + std::to_string(time(nullptr)) + ".snap";
|
||||
std::filesystem::path snapshot_file = get_main_executable_name(win_emu) + "-" + std::to_string(time(nullptr)) + ".snap";
|
||||
|
||||
if (log)
|
||||
{
|
||||
|
||||
@@ -6,15 +6,16 @@
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <ranges>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
@@ -23,6 +24,7 @@
|
||||
#include <unordered_set>
|
||||
#include <condition_variable>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
|
||||
#include <platform/platform.hpp>
|
||||
|
||||
269
src/analyzer/tenet_tracer.cpp
Normal file
269
src/analyzer/tenet_tracer.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
#include "std_include.hpp"
|
||||
#include "tenet_tracer.hpp"
|
||||
#include <utils/finally.hpp>
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string format_hex(uint64_t value)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "0x" << std::hex << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string format_byte_array(const uint8_t* data, size_t size)
|
||||
{
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(data[i]);
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void parse_and_accumulate_changes(const std::string& line, std::map<std::string, std::string>& changes)
|
||||
{
|
||||
size_t start = 0;
|
||||
while (start < line.length())
|
||||
{
|
||||
size_t end = line.find(',', start);
|
||||
if (end == std::string::npos)
|
||||
{
|
||||
end = line.length();
|
||||
}
|
||||
|
||||
std::string pair_str = line.substr(start, end - start);
|
||||
size_t equals_pos = pair_str.find('=');
|
||||
if (equals_pos != std::string::npos)
|
||||
{
|
||||
std::string key = pair_str.substr(0, equals_pos);
|
||||
std::string value = pair_str.substr(equals_pos + 1);
|
||||
changes[key] = value;
|
||||
}
|
||||
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tenet_tracer::tenet_tracer(windows_emulator& win_emu, const std::filesystem::path& log_filename)
|
||||
: win_emu_(win_emu),
|
||||
log_file_(log_filename)
|
||||
{
|
||||
if (!log_file_)
|
||||
{
|
||||
throw std::runtime_error("TenetTracer: Failed to open log file -> " + log_filename.string());
|
||||
}
|
||||
|
||||
auto& emu = win_emu_.emu();
|
||||
|
||||
auto* read_hook = emu.hook_memory_read(0, 0xFFFFFFFFFFFFFFFF, [this](uint64_t a, const void* d, size_t s) {
|
||||
this->log_memory_read(a, d, s); //
|
||||
});
|
||||
read_hook_ = scoped_hook(emu, read_hook);
|
||||
|
||||
auto* write_hook = emu.hook_memory_write(0, 0xFFFFFFFFFFFFFFFF, [this](uint64_t a, const void* d, size_t s) {
|
||||
this->log_memory_write(a, d, s); //
|
||||
});
|
||||
write_hook_ = scoped_hook(emu, write_hook);
|
||||
|
||||
auto* execute_hook = emu.hook_memory_execution([&](uint64_t address) {
|
||||
this->process_instruction(address); //
|
||||
});
|
||||
execute_hook_ = scoped_hook(emu, execute_hook);
|
||||
}
|
||||
|
||||
tenet_tracer::~tenet_tracer()
|
||||
{
|
||||
filter_and_write_buffer();
|
||||
|
||||
if (log_file_.is_open())
|
||||
{
|
||||
log_file_.close();
|
||||
}
|
||||
}
|
||||
|
||||
void tenet_tracer::filter_and_write_buffer()
|
||||
{
|
||||
if (raw_log_buffer_.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* exe_module = win_emu_.mod_manager.executable;
|
||||
if (!exe_module)
|
||||
{
|
||||
for (const auto& line : raw_log_buffer_)
|
||||
{
|
||||
log_file_ << line << '\n';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!raw_log_buffer_.empty())
|
||||
{
|
||||
log_file_ << raw_log_buffer_.front() << '\n';
|
||||
}
|
||||
|
||||
bool currently_outside = false;
|
||||
std::map<std::string, std::string> accumulated_changes;
|
||||
|
||||
for (size_t i = 1; i < raw_log_buffer_.size(); ++i)
|
||||
{
|
||||
const auto& line = raw_log_buffer_[i];
|
||||
|
||||
size_t rip_pos = line.find("rip=0x");
|
||||
if (rip_pos == std::string::npos)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
char* end_ptr = nullptr;
|
||||
uint64_t address = std::strtoull(line.c_str() + rip_pos + 6, &end_ptr, 16);
|
||||
|
||||
bool is_line_inside = exe_module->is_within(address);
|
||||
const auto _1 = utils::finally([&] {
|
||||
currently_outside = !is_line_inside; //
|
||||
});
|
||||
|
||||
if (!is_line_inside)
|
||||
{
|
||||
parse_and_accumulate_changes(line, accumulated_changes);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto _2 = utils::finally([&] {
|
||||
log_file_ << line << '\n'; //
|
||||
});
|
||||
|
||||
if (!currently_outside || accumulated_changes.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::stringstream summary_line;
|
||||
bool first = true;
|
||||
|
||||
auto rip_it = accumulated_changes.find("rip");
|
||||
std::string last_rip;
|
||||
if (rip_it != accumulated_changes.end())
|
||||
{
|
||||
last_rip = rip_it->second;
|
||||
accumulated_changes.erase(rip_it);
|
||||
}
|
||||
|
||||
for (const auto& pair : accumulated_changes)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
summary_line << ",";
|
||||
}
|
||||
summary_line << pair.first << "=" << pair.second;
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (!last_rip.empty())
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
summary_line << ",";
|
||||
}
|
||||
summary_line << "rip=" << last_rip;
|
||||
}
|
||||
|
||||
log_file_ << summary_line.str() << '\n';
|
||||
accumulated_changes.clear();
|
||||
}
|
||||
|
||||
raw_log_buffer_.clear();
|
||||
}
|
||||
|
||||
void tenet_tracer::log_memory_read(uint64_t address, const void* data, size_t size)
|
||||
{
|
||||
if (!mem_read_log_.str().empty())
|
||||
{
|
||||
mem_read_log_ << ";";
|
||||
}
|
||||
|
||||
mem_read_log_ << format_hex(address) << ":" << format_byte_array(static_cast<const uint8_t*>(data), size);
|
||||
}
|
||||
|
||||
void tenet_tracer::log_memory_write(uint64_t address, const void* data, size_t size)
|
||||
{
|
||||
if (!mem_write_log_.str().empty())
|
||||
{
|
||||
mem_write_log_ << ";";
|
||||
}
|
||||
|
||||
mem_write_log_ << format_hex(address) << ":" << format_byte_array(static_cast<const uint8_t*>(data), size);
|
||||
}
|
||||
|
||||
void tenet_tracer::process_instruction(const uint64_t address)
|
||||
{
|
||||
auto& emu = win_emu_.emu();
|
||||
std::stringstream trace_line;
|
||||
|
||||
std::array<uint64_t, GPRs_TO_TRACE.size()> current_regs{};
|
||||
for (size_t i = 0; i < GPRs_TO_TRACE.size(); ++i)
|
||||
{
|
||||
current_regs[i] = emu.reg<uint64_t>(GPRs_TO_TRACE[i].first);
|
||||
}
|
||||
|
||||
bool first_entry = true;
|
||||
auto append_separator = [&] {
|
||||
if (!first_entry)
|
||||
{
|
||||
trace_line << ",";
|
||||
}
|
||||
first_entry = false;
|
||||
};
|
||||
|
||||
if (is_first_instruction_)
|
||||
{
|
||||
for (size_t i = 0; i < GPRs_TO_TRACE.size(); ++i)
|
||||
{
|
||||
append_separator();
|
||||
trace_line << GPRs_TO_TRACE[i].second << "=" << format_hex(current_regs[i]);
|
||||
}
|
||||
is_first_instruction_ = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < GPRs_TO_TRACE.size(); ++i)
|
||||
{
|
||||
if (previous_registers_[i] != current_regs[i])
|
||||
{
|
||||
append_separator();
|
||||
trace_line << GPRs_TO_TRACE[i].second << "=" << format_hex(current_regs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
append_separator();
|
||||
trace_line << "rip=" << format_hex(address);
|
||||
|
||||
const auto mem_reads = mem_read_log_.str();
|
||||
if (!mem_reads.empty())
|
||||
{
|
||||
append_separator();
|
||||
trace_line << "mr=" << mem_reads;
|
||||
}
|
||||
|
||||
const auto mem_writes = mem_write_log_.str();
|
||||
if (!mem_writes.empty())
|
||||
{
|
||||
append_separator();
|
||||
trace_line << "mw=" << mem_writes;
|
||||
}
|
||||
|
||||
raw_log_buffer_.push_back(trace_line.str());
|
||||
previous_registers_ = current_regs;
|
||||
|
||||
mem_read_log_.str("");
|
||||
mem_read_log_.clear();
|
||||
mem_write_log_.str("");
|
||||
mem_write_log_.clear();
|
||||
}
|
||||
58
src/analyzer/tenet_tracer.hpp
Normal file
58
src/analyzer/tenet_tracer.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows_emulator.hpp>
|
||||
#include <emulator/x86_register.hpp>
|
||||
#include <emulator/scoped_hook.hpp>
|
||||
|
||||
constexpr std::array<std::pair<x86_register, std::string_view>, 16> GPRs_TO_TRACE = {
|
||||
{
|
||||
{x86_register::rax, "rax"},
|
||||
{x86_register::rbx, "rbx"},
|
||||
{x86_register::rcx, "rcx"},
|
||||
{x86_register::rdx, "rdx"},
|
||||
{x86_register::rsi, "rsi"},
|
||||
{x86_register::rdi, "rdi"},
|
||||
{x86_register::rbp, "rbp"},
|
||||
{x86_register::rsp, "rsp"},
|
||||
{x86_register::r8, "r8"},
|
||||
{x86_register::r9, "r9"},
|
||||
{x86_register::r10, "r10"},
|
||||
{x86_register::r11, "r11"},
|
||||
{x86_register::r12, "r12"},
|
||||
{x86_register::r13, "r13"},
|
||||
{x86_register::r14, "r14"},
|
||||
{x86_register::r15, "r15"},
|
||||
},
|
||||
};
|
||||
|
||||
class tenet_tracer
|
||||
{
|
||||
public:
|
||||
tenet_tracer(windows_emulator& win_emu, const std::filesystem::path& log_filename);
|
||||
~tenet_tracer();
|
||||
|
||||
tenet_tracer(tenet_tracer&) = delete;
|
||||
tenet_tracer(const tenet_tracer&) = delete;
|
||||
tenet_tracer& operator=(tenet_tracer&) = delete;
|
||||
tenet_tracer& operator=(const tenet_tracer&) = delete;
|
||||
|
||||
private:
|
||||
void filter_and_write_buffer();
|
||||
void log_memory_read(uint64_t address, const void* data, size_t size);
|
||||
void log_memory_write(uint64_t address, const void* data, size_t size);
|
||||
void process_instruction(uint64_t address);
|
||||
|
||||
windows_emulator& win_emu_;
|
||||
std::ofstream log_file_;
|
||||
|
||||
std::vector<std::string> raw_log_buffer_;
|
||||
std::array<uint64_t, GPRs_TO_TRACE.size()> previous_registers_{};
|
||||
bool is_first_instruction_ = true;
|
||||
|
||||
std::stringstream mem_read_log_;
|
||||
std::stringstream mem_write_log_;
|
||||
|
||||
scoped_hook read_hook_;
|
||||
scoped_hook write_hook_;
|
||||
scoped_hook execute_hook_;
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
use icicle_cpu::ExceptionCode;
|
||||
use icicle_cpu::ValueSource;
|
||||
use std::collections::HashSet;
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
use crate::registers;
|
||||
@@ -73,31 +74,96 @@ fn qualify_hook_id(hook_id: u32, hook_type: HookType) -> u32 {
|
||||
|
||||
pub struct HookContainer<Func: ?Sized> {
|
||||
hook_id: u32,
|
||||
is_iterating: bool,
|
||||
hooks: HashMap<u32, Box<Func>>,
|
||||
hooks_to_add: HashMap<u32, Box<Func>>,
|
||||
hooks_to_remove: HashSet<u32>,
|
||||
}
|
||||
|
||||
impl<Func: ?Sized> HookContainer<Func> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
hook_id: 0,
|
||||
is_iterating: false,
|
||||
hooks: HashMap::new(),
|
||||
hooks_to_add: HashMap::new(),
|
||||
hooks_to_remove: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_hook(&mut self, callback: Box<Func>) -> u32 {
|
||||
self.hook_id += 1;
|
||||
let id = self.hook_id;
|
||||
self.hooks.insert(id, callback);
|
||||
|
||||
if self.is_iterating {
|
||||
self.hooks_to_add.insert(id, callback);
|
||||
} else {
|
||||
self.hooks.insert(id, callback);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
pub fn get_hooks(&self) -> &HashMap<u32, Box<Func>> {
|
||||
return &self.hooks;
|
||||
pub fn for_each_hook<F>(&mut self, mut callback: F)
|
||||
where
|
||||
F: FnMut(&Func),
|
||||
{
|
||||
let was_iterating = self.do_pre_access_work();
|
||||
|
||||
for (_, func) in &self.hooks {
|
||||
callback(func.as_ref());
|
||||
}
|
||||
|
||||
self.do_post_access_work(was_iterating);
|
||||
}
|
||||
|
||||
pub fn access_hook<F>(&mut self, id: u32, mut callback: F)
|
||||
where
|
||||
F: FnMut(&Func),
|
||||
{
|
||||
let was_iterating = self.do_pre_access_work();
|
||||
|
||||
let hook = self.hooks.get(&id);
|
||||
if hook.is_some() {
|
||||
callback(hook.unwrap().as_ref());
|
||||
}
|
||||
|
||||
self.do_post_access_work(was_iterating);
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
return self.hooks.is_empty();
|
||||
}
|
||||
|
||||
pub fn remove_hook(&mut self, id: u32) {
|
||||
self.hooks.remove(&id);
|
||||
if self.is_iterating {
|
||||
self.hooks_to_remove.insert(id);
|
||||
} else {
|
||||
self.hooks.remove(&id);
|
||||
}
|
||||
}
|
||||
|
||||
fn do_pre_access_work(&mut self) -> bool {
|
||||
let was_iterating = self.is_iterating;
|
||||
self.is_iterating = true;
|
||||
return was_iterating;
|
||||
}
|
||||
|
||||
fn do_post_access_work(&mut self, was_iterating: bool) {
|
||||
self.is_iterating = was_iterating;
|
||||
if self.is_iterating {
|
||||
return;
|
||||
}
|
||||
|
||||
let to_remove = std::mem::take(&mut self.hooks_to_remove);
|
||||
for id in &to_remove {
|
||||
self.hooks.remove(&id);
|
||||
}
|
||||
|
||||
let to_add = std::mem::take(&mut self.hooks_to_add);
|
||||
for (id, func) in to_add {
|
||||
self.hooks.insert(id, func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,6 +223,7 @@ struct ExecutionHooks {
|
||||
specific_hooks: HookContainer<dyn Fn(u64)>,
|
||||
block_hooks: HookContainer<dyn Fn(u64, u64)>,
|
||||
address_mapping: HashMap<u64, Vec<u32>>,
|
||||
one_time_callbacks: Vec<Box<dyn Fn()>>,
|
||||
}
|
||||
|
||||
impl ExecutionHooks {
|
||||
@@ -167,31 +234,38 @@ impl ExecutionHooks {
|
||||
specific_hooks: HookContainer::new(),
|
||||
block_hooks: HookContainer::new(),
|
||||
address_mapping: HashMap::new(),
|
||||
one_time_callbacks: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_hooks(&self, address: u64) {
|
||||
for (_key, func) in self.generic_hooks.get_hooks() {
|
||||
func(address);
|
||||
fn run_hooks(&mut self, address: u64) {
|
||||
if !self.one_time_callbacks.is_empty() {
|
||||
let callbacks = std::mem::take(&mut self.one_time_callbacks);
|
||||
for cb in callbacks {
|
||||
cb.as_ref()();
|
||||
}
|
||||
}
|
||||
|
||||
self.generic_hooks.for_each_hook(|func| {
|
||||
func(address);
|
||||
});
|
||||
|
||||
let mapping = self.address_mapping.get(&address);
|
||||
if mapping.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
for id in mapping.unwrap() {
|
||||
let func = self.specific_hooks.get_hooks().get(&id);
|
||||
if func.is_some() {
|
||||
func.unwrap()(address);
|
||||
}
|
||||
self.specific_hooks.access_hook(*id, |func| {
|
||||
func(address);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_block(&mut self, address: u64, instructions: u64) {
|
||||
for (_key, func) in self.block_hooks.get_hooks() {
|
||||
self.block_hooks.for_each_hook(|func| {
|
||||
func(address, instructions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn execute(&mut self, cpu: &mut icicle_cpu::Cpu, address: u64) {
|
||||
@@ -224,6 +298,10 @@ impl ExecutionHooks {
|
||||
return id;
|
||||
}
|
||||
|
||||
pub fn schedule(&mut self, callback: Box<dyn Fn()>) {
|
||||
self.one_time_callbacks.push(callback);
|
||||
}
|
||||
|
||||
pub fn remove_generic_hook(&mut self, id: u32) {
|
||||
self.generic_hooks.remove_hook(id);
|
||||
}
|
||||
@@ -298,6 +376,15 @@ impl icicle_cpu::mem::IoMemory for MmioHandler {
|
||||
impl IcicleEmulator {
|
||||
pub fn new() -> Self {
|
||||
let mut virtual_machine = create_x64_vm();
|
||||
let capacity_400mb = 50_000;
|
||||
|
||||
let mut capacity = 8 * 2 * capacity_400mb; // ~8gb
|
||||
if cfg!(target_pointer_width = "32") {
|
||||
capacity = 2 * capacity_400mb; // ~1gb
|
||||
}
|
||||
|
||||
virtual_machine.cpu.mem.set_capacity(capacity);
|
||||
|
||||
let stop_value = Rc::new(RefCell::new(false));
|
||||
let exec_hooks = Rc::new(RefCell::new(ExecutionHooks::new(stop_value.clone())));
|
||||
|
||||
@@ -368,10 +455,10 @@ impl IcicleEmulator {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_interrupt(&self, code: i32) -> bool {
|
||||
for (_key, func) in self.interrupt_hooks.get_hooks() {
|
||||
fn handle_interrupt(&mut self, code: i32) -> bool {
|
||||
self.interrupt_hooks.for_each_hook(|func| {
|
||||
func(code);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -393,16 +480,15 @@ impl IcicleEmulator {
|
||||
}
|
||||
|
||||
fn handle_violation(&mut self, address: u64, permission: u8, unmapped: bool) -> bool {
|
||||
let hooks = &self.violation_hooks.get_hooks();
|
||||
if hooks.is_empty() {
|
||||
if self.violation_hooks.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut continue_execution = true;
|
||||
|
||||
for (_key, func) in self.violation_hooks.get_hooks() {
|
||||
self.violation_hooks.for_each_hook(|func| {
|
||||
continue_execution &= func(address, permission, unmapped);
|
||||
}
|
||||
});
|
||||
|
||||
return continue_execution;
|
||||
}
|
||||
@@ -412,9 +498,9 @@ impl IcicleEmulator {
|
||||
return self.handle_interrupt(value as i32);
|
||||
}
|
||||
|
||||
for (_key, func) in self.syscall_hooks.get_hooks() {
|
||||
self.syscall_hooks.for_each_hook(|func| {
|
||||
func();
|
||||
}
|
||||
});
|
||||
|
||||
self.vm.cpu.write_pc(self.vm.cpu.read_pc() + 2);
|
||||
return true;
|
||||
@@ -521,6 +607,10 @@ impl IcicleEmulator {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_on_next_instruction(&mut self, callback: Box<dyn Fn()>) {
|
||||
self.execution_hooks.borrow_mut().schedule(callback);
|
||||
}
|
||||
|
||||
pub fn map_memory(&mut self, address: u64, length: u64, permissions: u8) -> bool {
|
||||
const MAPPING_PERMISSIONS: u8 = icicle_vm::cpu::mem::perm::MAP
|
||||
| icicle_vm::cpu::mem::perm::INIT
|
||||
@@ -533,14 +623,7 @@ impl IcicleEmulator {
|
||||
value: 0x0,
|
||||
};
|
||||
|
||||
let layout = icicle_vm::cpu::mem::AllocLayout {
|
||||
addr: Some(address),
|
||||
size: length,
|
||||
align: 0x1000,
|
||||
};
|
||||
|
||||
let res = self.get_mem().alloc_memory(layout, mapping);
|
||||
return res.is_ok();
|
||||
return self.get_mem().map_memory_len(address, length, mapping);
|
||||
}
|
||||
|
||||
pub fn map_mmio(
|
||||
@@ -555,14 +638,7 @@ impl IcicleEmulator {
|
||||
let handler = MmioHandler::new(read_function, write_function);
|
||||
let handler_id = mem.register_io_handler(handler);
|
||||
|
||||
let layout = icicle_vm::cpu::mem::AllocLayout {
|
||||
addr: Some(address),
|
||||
size: length,
|
||||
align: 0x1000,
|
||||
};
|
||||
|
||||
let res = mem.alloc_memory(layout, handler_id);
|
||||
return res.is_ok();
|
||||
return self.get_mem().map_memory_len(address, length, handler_id);
|
||||
}
|
||||
|
||||
pub fn unmap_memory(&mut self, address: u64, length: u64) -> bool {
|
||||
|
||||
@@ -307,6 +307,14 @@ pub fn icicle_remove_hook(ptr: *mut c_void, id: u32) {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn icicle_run_on_next_instruction(ptr: *mut c_void, callback: RawFunction, data: *mut c_void) {
|
||||
unsafe {
|
||||
let emulator = &mut *(ptr as *mut IcicleEmulator);
|
||||
emulator.run_on_next_instruction(Box::new(move || callback(data)));
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn icicle_read_register(
|
||||
ptr: *mut c_void,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#define ICICLE_EMULATOR_IMPL
|
||||
#include "icicle_x86_64_emulator.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <utils/object.hpp>
|
||||
#include <utils/finally.hpp>
|
||||
|
||||
using icicle_emulator = struct icicle_emulator_;
|
||||
|
||||
@@ -21,8 +23,8 @@ extern "C"
|
||||
icicle_emulator* icicle_create_emulator();
|
||||
int32_t icicle_protect_memory(icicle_emulator*, uint64_t address, uint64_t length, uint8_t permissions);
|
||||
int32_t icicle_map_memory(icicle_emulator*, uint64_t address, uint64_t length, uint8_t permissions);
|
||||
int32_t icicle_map_mmio(icicle_emulator*, uint64_t address, uint64_t length, icicle_mmio_read_func* read_callback,
|
||||
void* read_data, icicle_mmio_write_func* write_callback, void* write_data);
|
||||
int32_t icicle_map_mmio(icicle_emulator*, uint64_t address, uint64_t length, icicle_mmio_read_func* read_callback, void* read_data,
|
||||
icicle_mmio_write_func* write_callback, void* write_data);
|
||||
int32_t icicle_unmap_memory(icicle_emulator*, uint64_t address, uint64_t length);
|
||||
int32_t icicle_read_memory(icicle_emulator*, uint64_t address, void* data, size_t length);
|
||||
int32_t icicle_write_memory(icicle_emulator*, uint64_t address, const void* data, size_t length);
|
||||
@@ -44,6 +46,7 @@ extern "C"
|
||||
void icicle_start(icicle_emulator*, size_t count);
|
||||
void icicle_stop(icicle_emulator*);
|
||||
void icicle_destroy_emulator(icicle_emulator*);
|
||||
void icicle_run_on_next_instruction(icicle_emulator*, raw_func* callback, void* data);
|
||||
}
|
||||
|
||||
namespace icicle
|
||||
@@ -58,24 +61,35 @@ namespace icicle
|
||||
}
|
||||
}
|
||||
|
||||
emulator_hook* wrap_hook(const uint32_t id)
|
||||
{
|
||||
return reinterpret_cast<emulator_hook*>(static_cast<size_t>(id));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct function_object : utils::object
|
||||
{
|
||||
bool* hook_state{};
|
||||
std::function<T> func{};
|
||||
|
||||
function_object(std::function<T> f = {})
|
||||
: func(std::move(f))
|
||||
function_object(std::function<T> f = {}, bool* state = nullptr)
|
||||
: hook_state(state),
|
||||
func(std::move(f))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto operator()(Args&&... args) const
|
||||
{
|
||||
bool old_state{};
|
||||
if (this->hook_state)
|
||||
{
|
||||
old_state = *this->hook_state;
|
||||
*this->hook_state = true;
|
||||
}
|
||||
|
||||
const auto _ = utils::finally([&] {
|
||||
if (this->hook_state)
|
||||
{
|
||||
*this->hook_state = old_state;
|
||||
}
|
||||
});
|
||||
|
||||
return this->func.operator()(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -83,10 +97,18 @@ namespace icicle
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::unique_ptr<function_object<T>> make_function_object(std::function<T> func)
|
||||
std::unique_ptr<function_object<T>> make_function_object(std::function<T> func, bool& hook_state)
|
||||
{
|
||||
return std::make_unique<function_object<T>>(std::move(func));
|
||||
return std::make_unique<function_object<T>>(std::move(func), &hook_state);
|
||||
}
|
||||
|
||||
struct memory_access_hook
|
||||
{
|
||||
uint64_t address{};
|
||||
uint64_t size{};
|
||||
memory_access_hook_callback callback{};
|
||||
bool is_read{};
|
||||
};
|
||||
}
|
||||
|
||||
class icicle_x86_64_emulator : public x86_64_emulator
|
||||
@@ -103,6 +125,10 @@ namespace icicle
|
||||
|
||||
~icicle_x86_64_emulator() override
|
||||
{
|
||||
reset_object_with_delayed_destruction(this->hooks_);
|
||||
reset_object_with_delayed_destruction(this->storage_);
|
||||
utils::reset_object_with_delayed_destruction(this->hooks_to_install_);
|
||||
|
||||
if (this->emu_)
|
||||
{
|
||||
icicle_destroy_emulator(this->emu_);
|
||||
@@ -162,8 +188,7 @@ namespace icicle
|
||||
return icicle_read_register(this->emu_, reg, value, size);
|
||||
}
|
||||
|
||||
void map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
|
||||
mmio_write_callback write_cb) override
|
||||
void map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) override
|
||||
{
|
||||
struct mmio_wrapper : utils::object
|
||||
{
|
||||
@@ -236,12 +261,12 @@ namespace icicle
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto obj = make_function_object(std::move(callback));
|
||||
auto obj = make_function_object(std::move(callback), this->is_in_hook_);
|
||||
auto* ptr = obj.get();
|
||||
|
||||
const auto invoker = +[](void* cb) {
|
||||
const auto& func = *static_cast<decltype(ptr)>(cb);
|
||||
(void)func(); //
|
||||
(void)func(0); //
|
||||
};
|
||||
|
||||
const auto id = icicle_add_syscall_hook(this->emu_, invoker, ptr);
|
||||
@@ -252,7 +277,7 @@ namespace icicle
|
||||
|
||||
emulator_hook* hook_basic_block(basic_block_hook_callback callback) override
|
||||
{
|
||||
auto object = make_function_object(std::move(callback));
|
||||
auto object = make_function_object(std::move(callback), this->is_in_hook_);
|
||||
auto* ptr = object.get();
|
||||
auto* wrapper = +[](void* user, const uint64_t addr, const uint64_t instructions) {
|
||||
basic_block block{};
|
||||
@@ -271,7 +296,7 @@ namespace icicle
|
||||
|
||||
emulator_hook* hook_interrupt(interrupt_hook_callback callback) override
|
||||
{
|
||||
auto obj = make_function_object(std::move(callback));
|
||||
auto obj = make_function_object(std::move(callback), this->is_in_hook_);
|
||||
auto* ptr = obj.get();
|
||||
auto* wrapper = +[](void* user, const int32_t code) {
|
||||
const auto& func = *static_cast<decltype(ptr)>(user);
|
||||
@@ -286,10 +311,9 @@ namespace icicle
|
||||
|
||||
emulator_hook* hook_memory_violation(memory_violation_hook_callback callback) override
|
||||
{
|
||||
auto obj = make_function_object(std::move(callback));
|
||||
auto obj = make_function_object(std::move(callback), this->is_in_hook_);
|
||||
auto* ptr = obj.get();
|
||||
auto* wrapper =
|
||||
+[](void* user, const uint64_t address, const uint8_t operation, const int32_t unmapped) -> int32_t {
|
||||
auto* wrapper = +[](void* user, const uint64_t address, const uint8_t operation, const int32_t unmapped) -> int32_t {
|
||||
const auto violation_type = unmapped //
|
||||
? memory_violation_type::unmapped
|
||||
: memory_violation_type::protection;
|
||||
@@ -307,7 +331,7 @@ namespace icicle
|
||||
|
||||
emulator_hook* hook_memory_execution(const uint64_t address, memory_execution_hook_callback callback) override
|
||||
{
|
||||
auto object = make_function_object(std::move(callback));
|
||||
auto object = make_function_object(std::move(callback), this->is_in_hook_);
|
||||
auto* ptr = object.get();
|
||||
auto* wrapper = +[](void* user, const uint64_t addr) {
|
||||
const auto& func = *static_cast<decltype(ptr)>(user);
|
||||
@@ -322,7 +346,7 @@ namespace icicle
|
||||
|
||||
emulator_hook* hook_memory_execution(memory_execution_hook_callback callback) override
|
||||
{
|
||||
auto object = make_function_object(std::move(callback));
|
||||
auto object = make_function_object(std::move(callback), this->is_in_hook_);
|
||||
auto* ptr = object.get();
|
||||
auto* wrapper = +[](void* user, const uint64_t addr) {
|
||||
const auto& func = *static_cast<decltype(ptr)>(user);
|
||||
@@ -335,49 +359,37 @@ namespace icicle
|
||||
return wrap_hook(id);
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_read(const uint64_t address, const uint64_t size,
|
||||
memory_access_hook_callback callback) override
|
||||
emulator_hook* hook_memory_read(const uint64_t address, const uint64_t size, memory_access_hook_callback callback) override
|
||||
{
|
||||
auto obj = make_function_object(std::move(callback));
|
||||
auto* ptr = obj.get();
|
||||
auto* wrapper = +[](void* user, const uint64_t address, const void* data, size_t length) {
|
||||
const auto& func = *static_cast<decltype(ptr)>(user);
|
||||
func(address, data, length);
|
||||
};
|
||||
|
||||
const auto id = icicle_add_read_hook(this->emu_, address, address + size, wrapper, ptr);
|
||||
this->hooks_[id] = std::move(obj);
|
||||
|
||||
return wrap_hook(id);
|
||||
return this->try_install_memory_access_hook(memory_access_hook{
|
||||
.address = address,
|
||||
.size = size,
|
||||
.callback = std::move(callback),
|
||||
.is_read = true,
|
||||
});
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_write(const uint64_t address, const uint64_t size,
|
||||
memory_access_hook_callback callback) override
|
||||
emulator_hook* hook_memory_write(const uint64_t address, const uint64_t size, memory_access_hook_callback callback) override
|
||||
{
|
||||
auto obj = make_function_object(std::move(callback));
|
||||
auto* ptr = obj.get();
|
||||
auto* wrapper = +[](void* user, const uint64_t address, const void* data, size_t length) {
|
||||
const auto& func = *static_cast<decltype(ptr)>(user);
|
||||
func(address, data, length);
|
||||
};
|
||||
|
||||
const auto id = icicle_add_write_hook(this->emu_, address, address + size, wrapper, ptr);
|
||||
this->hooks_[id] = std::move(obj);
|
||||
|
||||
return wrap_hook(id);
|
||||
return this->try_install_memory_access_hook(memory_access_hook{
|
||||
.address = address,
|
||||
.size = size,
|
||||
.callback = std::move(callback),
|
||||
.is_read = false,
|
||||
});
|
||||
}
|
||||
|
||||
void delete_hook(emulator_hook* hook) override
|
||||
{
|
||||
const auto id = static_cast<uint32_t>(reinterpret_cast<size_t>(hook));
|
||||
const auto entry = this->hooks_.find(id);
|
||||
if (entry == this->hooks_.end())
|
||||
if (this->is_in_hook_)
|
||||
{
|
||||
return;
|
||||
this->hooks_to_delete_.insert(hook);
|
||||
this->schedule_action_execution();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->delete_hook_internal(hook);
|
||||
}
|
||||
|
||||
icicle_remove_hook(this->emu_, id);
|
||||
this->hooks_.erase(entry);
|
||||
}
|
||||
|
||||
void serialize_state(utils::buffer_serializer& buffer, const bool is_snapshot) const override
|
||||
@@ -437,9 +449,138 @@ namespace icicle
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_in_hook_{false};
|
||||
std::list<std::unique_ptr<utils::object>> storage_{};
|
||||
std::unordered_map<uint32_t, std::unique_ptr<utils::object>> hooks_{};
|
||||
std::unordered_map<emulator_hook*, std::optional<uint32_t>> id_mapping_{};
|
||||
icicle_emulator* emu_{};
|
||||
uint32_t index_{0};
|
||||
|
||||
std::unordered_set<emulator_hook*> hooks_to_delete_{};
|
||||
std::unordered_map<emulator_hook*, memory_access_hook> hooks_to_install_{};
|
||||
|
||||
emulator_hook* wrap_hook(const std::optional<uint32_t> icicle_id)
|
||||
{
|
||||
const auto id = ++this->index_;
|
||||
auto* hook = reinterpret_cast<emulator_hook*>(static_cast<size_t>(id));
|
||||
|
||||
this->id_mapping_[hook] = icicle_id;
|
||||
|
||||
return hook;
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_access(memory_access_hook hook, emulator_hook* hook_id)
|
||||
{
|
||||
auto obj = make_function_object(std::move(hook.callback), this->is_in_hook_);
|
||||
auto* ptr = obj.get();
|
||||
auto* wrapper = +[](void* user, const uint64_t address, const void* data, size_t length) {
|
||||
const auto& func = *static_cast<decltype(ptr)>(user);
|
||||
func(address, data, length);
|
||||
};
|
||||
|
||||
auto* installer = hook.is_read ? &icicle_add_read_hook : &icicle_add_write_hook;
|
||||
const auto id = installer(this->emu_, hook.address, hook.address + hook.size, wrapper, ptr);
|
||||
this->hooks_[id] = std::move(obj);
|
||||
|
||||
if (hook_id)
|
||||
{
|
||||
this->id_mapping_[hook_id] = id;
|
||||
return hook_id;
|
||||
}
|
||||
|
||||
return wrap_hook(id);
|
||||
}
|
||||
|
||||
void delete_hook_internal(emulator_hook* hook)
|
||||
{
|
||||
auto hook_id = this->id_mapping_.find(hook);
|
||||
if (hook_id == this->id_mapping_.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hook_id->second.has_value())
|
||||
{
|
||||
this->hooks_to_delete_.insert(hook);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto id = *hook_id->second;
|
||||
this->id_mapping_.erase(hook_id);
|
||||
|
||||
const auto entry = this->hooks_.find(id);
|
||||
if (entry == this->hooks_.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
icicle_remove_hook(this->emu_, id);
|
||||
const auto obj = std::move(entry->second);
|
||||
this->hooks_.erase(entry);
|
||||
(void)obj;
|
||||
}
|
||||
|
||||
void perform_pending_actions()
|
||||
{
|
||||
auto hooks_to_install = std::move(this->hooks_to_install_);
|
||||
const auto hooks_to_delete = std::move(this->hooks_to_delete_);
|
||||
|
||||
this->hooks_to_delete_ = {};
|
||||
this->hooks_to_install_ = {};
|
||||
|
||||
for (auto& hook : hooks_to_install)
|
||||
{
|
||||
this->hook_memory_access(std::move(hook.second), hook.first);
|
||||
}
|
||||
|
||||
for (auto* hook : hooks_to_delete)
|
||||
{
|
||||
this->delete_hook_internal(hook);
|
||||
}
|
||||
}
|
||||
|
||||
emulator_hook* try_install_memory_access_hook(memory_access_hook hook)
|
||||
{
|
||||
if (!this->is_in_hook_)
|
||||
{
|
||||
return this->hook_memory_access(std::move(hook), nullptr);
|
||||
}
|
||||
|
||||
auto* hook_id = wrap_hook(std::nullopt);
|
||||
this->hooks_to_install_[hook_id] = std::move(hook);
|
||||
|
||||
this->schedule_action_execution();
|
||||
|
||||
return hook_id;
|
||||
}
|
||||
|
||||
void schedule_action_execution()
|
||||
{
|
||||
this->run_on_next_instruction([this] {
|
||||
this->perform_pending_actions(); //
|
||||
});
|
||||
}
|
||||
|
||||
void run_on_next_instruction(std::function<void()> func) const
|
||||
{
|
||||
auto* heap_func = new std::function(std::move(func));
|
||||
auto* callback = +[](void* data) {
|
||||
auto* cb = static_cast<std::function<void()>*>(data);
|
||||
|
||||
try
|
||||
{
|
||||
(*cb)();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
|
||||
delete cb;
|
||||
};
|
||||
|
||||
icicle_run_on_next_instruction(this->emu_, callback, heap_func);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<x86_64_emulator> create_x86_64_emulator()
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace unicorn
|
||||
return UC_X86_INS_RDTSC;
|
||||
case x86_hookable_instructions::rdtscp:
|
||||
return UC_X86_INS_RDTSCP;
|
||||
case x86_hookable_instructions::sgdt:
|
||||
return UC_X86_INS_SGDT;
|
||||
default:
|
||||
throw std::runtime_error("Bad instruction for mapping");
|
||||
}
|
||||
@@ -207,7 +209,7 @@ namespace unicorn
|
||||
|
||||
~unicorn_x86_64_emulator() override
|
||||
{
|
||||
this->hooks_.clear();
|
||||
reset_object_with_delayed_destruction(this->hooks_);
|
||||
uc_close(this->uc_);
|
||||
}
|
||||
|
||||
@@ -244,8 +246,11 @@ namespace unicorn
|
||||
|
||||
void load_gdt(const pointer_type address, const uint32_t limit) override
|
||||
{
|
||||
const std::array<uint64_t, 4> gdtr = {0, address, limit, 0};
|
||||
this->write_register(x86_register::gdtr, gdtr.data(), gdtr.size() * sizeof(uint64_t));
|
||||
uc_x86_mmr gdt{};
|
||||
gdt.base = address;
|
||||
gdt.limit = limit;
|
||||
|
||||
this->write_register(x86_register::gdtr, &gdt, sizeof(gdt));
|
||||
}
|
||||
|
||||
void set_segment_base(const x86_register base, const pointer_type value) override
|
||||
@@ -288,8 +293,7 @@ namespace unicorn
|
||||
|
||||
if (size < result_size)
|
||||
{
|
||||
throw std::runtime_error("Register size mismatch: " + std::to_string(size) +
|
||||
" != " + std::to_string(result_size));
|
||||
throw std::runtime_error("Register size mismatch: " + std::to_string(size) + " != " + std::to_string(result_size));
|
||||
}
|
||||
|
||||
return result_size;
|
||||
@@ -303,15 +307,13 @@ namespace unicorn
|
||||
|
||||
if (size < result_size)
|
||||
{
|
||||
throw std::runtime_error("Register size mismatch: " + std::to_string(size) +
|
||||
" != " + std::to_string(result_size));
|
||||
throw std::runtime_error("Register size mismatch: " + std::to_string(size) + " != " + std::to_string(result_size));
|
||||
}
|
||||
|
||||
return result_size;
|
||||
}
|
||||
|
||||
void map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
|
||||
mmio_write_callback write_cb) override
|
||||
void map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) override
|
||||
{
|
||||
auto read_wrapper = [c = std::move(read_cb)](uc_engine*, const uint64_t addr, const uint32_t s) {
|
||||
assert_64bit_limit(s);
|
||||
@@ -320,8 +322,7 @@ namespace unicorn
|
||||
return value;
|
||||
};
|
||||
|
||||
auto write_wrapper = [c = std::move(write_cb)](uc_engine*, const uint64_t addr, const uint32_t s,
|
||||
const uint64_t value) {
|
||||
auto write_wrapper = [c = std::move(write_cb)](uc_engine*, const uint64_t addr, const uint32_t s, const uint64_t value) {
|
||||
assert_64bit_limit(s);
|
||||
c(addr, &value, s);
|
||||
};
|
||||
@@ -331,8 +332,8 @@ namespace unicorn
|
||||
.write = mmio_callbacks::write_wrapper(std::move(write_wrapper)),
|
||||
};
|
||||
|
||||
uce(uc_mmio_map(*this, address, size, cb.read.get_c_function(), cb.read.get_user_data(),
|
||||
cb.write.get_c_function(), cb.write.get_user_data()));
|
||||
uce(uc_mmio_map(*this, address, size, cb.read.get_c_function(), cb.read.get_user_data(), cb.write.get_c_function(),
|
||||
cb.write.get_user_data()));
|
||||
|
||||
this->mmio_[address] = std::move(cb);
|
||||
}
|
||||
@@ -368,8 +369,7 @@ namespace unicorn
|
||||
uce(uc_mem_write(*this, address, data, size));
|
||||
}
|
||||
|
||||
void apply_memory_protection(const uint64_t address, const size_t size,
|
||||
memory_permission permissions) override
|
||||
void apply_memory_protection(const uint64_t address, const size_t size, memory_permission permissions) override
|
||||
{
|
||||
uce(uc_mem_protect(*this, address, size, static_cast<uint32_t>(permissions)));
|
||||
}
|
||||
@@ -384,34 +384,44 @@ namespace unicorn
|
||||
if (inst_type == x86_hookable_instructions::invalid)
|
||||
{
|
||||
function_wrapper<int, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) {
|
||||
return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0;
|
||||
return (c(0) == instruction_hook_continuation::skip_instruction) ? 1 : 0;
|
||||
});
|
||||
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN_INVALID, wrapper.get_function(),
|
||||
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max()));
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN_INVALID, wrapper.get_function(), wrapper.get_user_data(), 0,
|
||||
std::numeric_limits<pointer_type>::max()));
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
}
|
||||
else if (inst_type == x86_hookable_instructions::syscall)
|
||||
{
|
||||
function_wrapper<void, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) { c(); });
|
||||
function_wrapper<void, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) { (void)c(0); });
|
||||
|
||||
const auto uc_instruction = map_hookable_instruction(inst_type);
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(),
|
||||
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max(),
|
||||
uc_instruction));
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(), wrapper.get_user_data(), 0,
|
||||
std::numeric_limits<pointer_type>::max(), uc_instruction));
|
||||
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
}
|
||||
else if (inst_type == x86_hookable_instructions::sgdt)
|
||||
{
|
||||
function_wrapper<int, uc_engine*, uint64_t> wrapper([c = std::move(callback)](uc_engine*, const uint64_t data) {
|
||||
return (c(data) == instruction_hook_continuation::skip_instruction) ? 1 : 0;
|
||||
});
|
||||
|
||||
const auto uc_instruction = map_hookable_instruction(inst_type);
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(), wrapper.get_user_data(), 0,
|
||||
std::numeric_limits<pointer_type>::max(), uc_instruction));
|
||||
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
}
|
||||
else
|
||||
{
|
||||
function_wrapper<int, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) {
|
||||
return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0;
|
||||
return (c(0) == instruction_hook_continuation::skip_instruction) ? 1 : 0;
|
||||
});
|
||||
|
||||
const auto uc_instruction = map_hookable_instruction(inst_type);
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(),
|
||||
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max(),
|
||||
uc_instruction));
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(), wrapper.get_user_data(), 0,
|
||||
std::numeric_limits<pointer_type>::max(), uc_instruction));
|
||||
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
}
|
||||
@@ -437,8 +447,8 @@ namespace unicorn
|
||||
unicorn_hook hook{*this};
|
||||
auto container = std::make_unique<hook_container>();
|
||||
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_BLOCK, wrapper.get_function(),
|
||||
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max()));
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_BLOCK, wrapper.get_function(), wrapper.get_user_data(), 0,
|
||||
std::numeric_limits<pointer_type>::max()));
|
||||
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
|
||||
@@ -455,8 +465,8 @@ namespace unicorn
|
||||
unicorn_hook hook{*this};
|
||||
auto container = std::make_unique<hook_container>();
|
||||
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INTR, wrapper.get_function(),
|
||||
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max()));
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INTR, wrapper.get_function(), wrapper.get_user_data(), 0,
|
||||
std::numeric_limits<pointer_type>::max()));
|
||||
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
|
||||
@@ -468,16 +478,16 @@ namespace unicorn
|
||||
emulator_hook* hook_memory_violation(memory_violation_hook_callback callback) override
|
||||
{
|
||||
function_wrapper<bool, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(
|
||||
[c = std::move(callback), this](uc_engine*, const uc_mem_type type, const uint64_t address,
|
||||
const int size, const int64_t) {
|
||||
[c = std::move(callback), this](uc_engine*, const uc_mem_type type, const uint64_t address, const int size,
|
||||
const int64_t) {
|
||||
const auto ip = this->read_instruction_pointer();
|
||||
|
||||
assert(size >= 0);
|
||||
const auto operation = map_memory_operation(type);
|
||||
const auto violation = map_memory_violation_type(type);
|
||||
|
||||
const auto resume = c(address, static_cast<uint64_t>(size), operation, violation) ==
|
||||
memory_violation_continuation::resume;
|
||||
const auto resume =
|
||||
c(address, static_cast<uint64_t>(size), operation, violation) == memory_violation_continuation::resume;
|
||||
|
||||
const auto new_ip = this->read_instruction_pointer();
|
||||
const auto has_ip_changed = ip != new_ip;
|
||||
@@ -507,8 +517,8 @@ namespace unicorn
|
||||
unicorn_hook hook{*this};
|
||||
auto container = std::make_unique<hook_container>();
|
||||
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_INVALID, wrapper.get_function(),
|
||||
wrapper.get_user_data(), 0, std::numeric_limits<uint64_t>::max()));
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_INVALID, wrapper.get_function(), wrapper.get_user_data(), 0,
|
||||
std::numeric_limits<uint64_t>::max()));
|
||||
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
|
||||
@@ -517,11 +527,9 @@ namespace unicorn
|
||||
return result;
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_execution(const uint64_t address, const uint64_t size,
|
||||
memory_execution_hook_callback callback)
|
||||
emulator_hook* hook_memory_execution(const uint64_t address, const uint64_t size, memory_execution_hook_callback callback)
|
||||
{
|
||||
auto exec_wrapper = [c = std::move(callback)](uc_engine*, const uint64_t address,
|
||||
const uint32_t /*size*/) {
|
||||
auto exec_wrapper = [c = std::move(callback)](uc_engine*, const uint64_t address, const uint32_t /*size*/) {
|
||||
c(address); //
|
||||
};
|
||||
|
||||
@@ -529,8 +537,8 @@ namespace unicorn
|
||||
|
||||
unicorn_hook hook{*this};
|
||||
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_CODE, wrapper.get_function(),
|
||||
wrapper.get_user_data(), address, address + size));
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_CODE, wrapper.get_function(), wrapper.get_user_data(), address,
|
||||
address + size));
|
||||
|
||||
auto* container = this->create_hook_container();
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
@@ -542,17 +550,14 @@ namespace unicorn
|
||||
return this->hook_memory_execution(0, std::numeric_limits<uint64_t>::max(), std::move(callback));
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_execution(const uint64_t address,
|
||||
memory_execution_hook_callback callback) override
|
||||
emulator_hook* hook_memory_execution(const uint64_t address, memory_execution_hook_callback callback) override
|
||||
{
|
||||
return this->hook_memory_execution(address, 1, std::move(callback));
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_read(const uint64_t address, const uint64_t size,
|
||||
memory_access_hook_callback callback) override
|
||||
emulator_hook* hook_memory_read(const uint64_t address, const uint64_t size, memory_access_hook_callback callback) override
|
||||
{
|
||||
auto read_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type,
|
||||
const uint64_t address, const int length,
|
||||
auto read_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type, const uint64_t address, const int length,
|
||||
const uint64_t value) {
|
||||
const auto operation = map_memory_operation(type);
|
||||
if (operation == memory_operation::read && length > 0)
|
||||
@@ -561,23 +566,21 @@ namespace unicorn
|
||||
}
|
||||
};
|
||||
|
||||
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(
|
||||
std::move(read_wrapper));
|
||||
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(std::move(read_wrapper));
|
||||
|
||||
unicorn_hook hook{*this};
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_READ_AFTER, wrapper.get_function(),
|
||||
wrapper.get_user_data(), address, address + size));
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_READ_AFTER, wrapper.get_function(), wrapper.get_user_data(),
|
||||
address, address + size));
|
||||
|
||||
auto* container = this->create_hook_container();
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
return container->as_opaque_hook();
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_write(const uint64_t address, const uint64_t size,
|
||||
memory_access_hook_callback callback) override
|
||||
emulator_hook* hook_memory_write(const uint64_t address, const uint64_t size, memory_access_hook_callback callback) override
|
||||
{
|
||||
auto write_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type, const uint64_t addr,
|
||||
const int length, const uint64_t value) {
|
||||
auto write_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type, const uint64_t addr, const int length,
|
||||
const uint64_t value) {
|
||||
const auto operation = map_memory_operation(type);
|
||||
if (operation == memory_operation::write && length > 0)
|
||||
{
|
||||
@@ -585,13 +588,12 @@ namespace unicorn
|
||||
}
|
||||
};
|
||||
|
||||
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(
|
||||
std::move(write_wrapper));
|
||||
function_wrapper<void, uc_engine*, uc_mem_type, uint64_t, int, int64_t> wrapper(std::move(write_wrapper));
|
||||
|
||||
unicorn_hook hook{*this};
|
||||
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_WRITE, wrapper.get_function(),
|
||||
wrapper.get_user_data(), address, address + size));
|
||||
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_MEM_WRITE, wrapper.get_function(), wrapper.get_user_data(), address,
|
||||
address + size));
|
||||
|
||||
auto* container = this->create_hook_container();
|
||||
container->add(std::move(wrapper), std::move(hook));
|
||||
@@ -608,14 +610,14 @@ namespace unicorn
|
||||
|
||||
void delete_hook(emulator_hook* hook) override
|
||||
{
|
||||
const auto entry =
|
||||
std::ranges::find_if(this->hooks_, [&](const std::unique_ptr<hook_object>& hook_ptr) {
|
||||
return hook_ptr->as_opaque_hook() == hook;
|
||||
});
|
||||
const auto entry = std::ranges::find_if(
|
||||
this->hooks_, [&](const std::unique_ptr<hook_object>& hook_ptr) { return hook_ptr->as_opaque_hook() == hook; });
|
||||
|
||||
if (entry != this->hooks_.end())
|
||||
{
|
||||
const auto obj = std::move(*entry);
|
||||
this->hooks_.erase(entry);
|
||||
(void)obj;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,8 +89,7 @@ namespace network
|
||||
|
||||
if (this->is_ipv6())
|
||||
{
|
||||
return !memcmp(this->address6_.sin6_addr.s6_addr, obj.address6_.sin6_addr.s6_addr,
|
||||
sizeof(obj.address6_.sin6_addr.s6_addr));
|
||||
return !memcmp(this->address6_.sin6_addr.s6_addr, obj.address6_.sin6_addr.s6_addr, sizeof(obj.address6_.sin6_addr.s6_addr));
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -369,9 +368,8 @@ std::size_t std::hash<network::address>::operator()(const network::address& a) c
|
||||
hash ^= std::hash<decltype(a.get_in_addr().sin_addr.s_addr)>{}(a.get_in_addr().sin_addr.s_addr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
hash ^= std::hash<std::string_view>{}(
|
||||
std::string_view{reinterpret_cast<const char*>(a.get_in6_addr().sin6_addr.s6_addr),
|
||||
sizeof(a.get_in6_addr().sin6_addr.s6_addr)});
|
||||
hash ^= std::hash<std::string_view>{}(std::string_view{reinterpret_cast<const char*>(a.get_in6_addr().sin6_addr.s6_addr),
|
||||
sizeof(a.get_in6_addr().sin6_addr.s6_addr)});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -20,13 +20,11 @@ namespace network
|
||||
if (af == AF_INET6)
|
||||
{
|
||||
int i = 1;
|
||||
setsockopt(this->socket_, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&i),
|
||||
static_cast<int>(sizeof(i)));
|
||||
setsockopt(this->socket_, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&i), static_cast<int>(sizeof(i)));
|
||||
}
|
||||
|
||||
int optval = 1;
|
||||
setsockopt(this->socket_, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&optval),
|
||||
static_cast<int>(sizeof(optval)));
|
||||
setsockopt(this->socket_, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&optval), static_cast<int>(sizeof(optval)));
|
||||
}
|
||||
|
||||
socket::~socket()
|
||||
@@ -158,8 +156,7 @@ namespace network
|
||||
return this->is_valid() && is_socket_ready(this->socket_, in_poll);
|
||||
}
|
||||
|
||||
bool socket::sleep_sockets(const std::span<const socket*>& sockets, const std::chrono::milliseconds timeout,
|
||||
const bool in_poll)
|
||||
bool socket::sleep_sockets(const std::span<const socket*>& sockets, const std::chrono::milliseconds timeout, const bool in_poll)
|
||||
{
|
||||
std::vector<pollfd> pfds{};
|
||||
pfds.resize(sockets.size());
|
||||
@@ -215,8 +212,7 @@ namespace network
|
||||
}
|
||||
|
||||
bool socket::sleep_sockets_until(const std::span<const socket*>& sockets,
|
||||
const std::chrono::high_resolution_clock::time_point time_point,
|
||||
const bool in_poll)
|
||||
const std::chrono::high_resolution_clock::time_point time_point, const bool in_poll)
|
||||
{
|
||||
const auto duration = time_point - std::chrono::high_resolution_clock::now();
|
||||
return sleep_sockets(sockets, std::chrono::duration_cast<std::chrono::milliseconds>(duration), in_poll);
|
||||
|
||||
@@ -63,10 +63,9 @@ namespace network
|
||||
|
||||
bool is_ready(bool in_poll) const;
|
||||
|
||||
static bool sleep_sockets(const std::span<const socket*>& sockets, std::chrono::milliseconds timeout,
|
||||
bool in_poll);
|
||||
static bool sleep_sockets_until(const std::span<const socket*>& sockets,
|
||||
std::chrono::high_resolution_clock::time_point time_point, bool in_poll);
|
||||
static bool sleep_sockets(const std::span<const socket*>& sockets, std::chrono::milliseconds timeout, bool in_poll);
|
||||
static bool sleep_sockets_until(const std::span<const socket*>& sockets, std::chrono::high_resolution_clock::time_point time_point,
|
||||
bool in_poll);
|
||||
|
||||
static bool is_socket_ready(SOCKET s, bool in_poll);
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ namespace network
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
const auto res = sendto(this->get_socket(), data.data(), static_cast<send_size>(data.size()), 0,
|
||||
&target.get_addr(), target.get_size());
|
||||
const auto res =
|
||||
sendto(this->get_socket(), data.data(), static_cast<send_size>(data.size()), 0, &target.get_addr(), target.get_size());
|
||||
|
||||
if (res < 0 && GET_SOCKET_ERROR() == SERR(EWOULDBLOCK))
|
||||
{
|
||||
@@ -36,8 +36,7 @@ namespace network
|
||||
address source{};
|
||||
auto len = source.get_max_size();
|
||||
|
||||
const auto result =
|
||||
recvfrom(this->get_socket(), buffer.data(), static_cast<int>(buffer.size()), 0, &source.get_addr(), &len);
|
||||
const auto result = recvfrom(this->get_socket(), buffer.data(), static_cast<int>(buffer.size()), 0, &source.get_addr(), &len);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
return std::nullopt;
|
||||
|
||||
@@ -77,14 +77,13 @@
|
||||
#define SEC_RESERVE 0x04000000
|
||||
#endif
|
||||
|
||||
#define CTL_CODE(DeviceType, Function, Method, Access) \
|
||||
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
|
||||
#define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
|
||||
|
||||
#define METHOD_BUFFERED 0
|
||||
#define METHOD_BUFFERED 0
|
||||
|
||||
#define FILE_ANY_ACCESS 0
|
||||
#define FILE_READ_ACCESS (0x0001) // file & pipe
|
||||
#define FILE_WRITE_ACCESS (0x0002) // file & pipe
|
||||
#define FILE_ANY_ACCESS 0
|
||||
#define FILE_READ_ACCESS (0x0001) // file & pipe
|
||||
#define FILE_WRITE_ACCESS (0x0002) // file & pipe
|
||||
|
||||
typedef enum _FSINFOCLASS
|
||||
{
|
||||
@@ -147,10 +146,10 @@ typedef enum _FILE_INFORMATION_CLASS
|
||||
FileMailslotQueryInformation, // q: FILE_MAILSLOT_QUERY_INFORMATION
|
||||
FileMailslotSetInformation, // s: FILE_MAILSLOT_SET_INFORMATION
|
||||
FileCompressionInformation, // q: FILE_COMPRESSION_INFORMATION
|
||||
FileObjectIdInformation, // q: FILE_OBJECTID_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileCompletionInformation, // s: FILE_COMPLETION_INFORMATION // 30
|
||||
FileMoveClusterInformation, // s: FILE_MOVE_CLUSTER_INFORMATION (requires FILE_WRITE_DATA)
|
||||
FileQuotaInformation, // q: FILE_QUOTA_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileObjectIdInformation, // q: FILE_OBJECTID_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileCompletionInformation, // s: FILE_COMPLETION_INFORMATION // 30
|
||||
FileMoveClusterInformation, // s: FILE_MOVE_CLUSTER_INFORMATION (requires FILE_WRITE_DATA)
|
||||
FileQuotaInformation, // q: FILE_QUOTA_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileReparsePointInformation,
|
||||
// q: FILE_REPARSE_POINT_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileNetworkOpenInformation, // q: FILE_NETWORK_OPEN_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
|
||||
@@ -21,9 +21,8 @@
|
||||
#define CONTEXT_XSTATE_32 (CONTEXT_X86_MAIN | 0x20L)
|
||||
#define CONTEXT_XSTATE_64 (CONTEXT_AMD64_MAIN | 0x20L)
|
||||
|
||||
#define CONTEXT64_ALL \
|
||||
(CONTEXT_CONTROL_64 | CONTEXT_INTEGER_64 | CONTEXT_SEGMENTS_64 | CONTEXT_FLOATING_POINT_64 | \
|
||||
CONTEXT_DEBUG_REGISTERS_64)
|
||||
#define CONTEXT64_ALL \
|
||||
(CONTEXT_CONTROL_64 | CONTEXT_INTEGER_64 | CONTEXT_SEGMENTS_64 | CONTEXT_FLOATING_POINT_64 | CONTEXT_DEBUG_REGISTERS_64)
|
||||
|
||||
using SYSTEM_INFORMATION_CLASS = enum _SYSTEM_INFORMATION_CLASS
|
||||
{
|
||||
@@ -62,16 +61,16 @@ using SYSTEM_INFORMATION_CLASS = enum _SYSTEM_INFORMATION_CLASS
|
||||
SystemSummaryMemoryInformation, // not implemented // SYSTEM_MEMORY_USAGE_INFORMATION
|
||||
SystemMirrorMemoryInformation,
|
||||
// s (requires license value "Kernel-MemoryMirroringSupported") (requires SeShutdownPrivilege) // 30
|
||||
SystemPerformanceTraceInformation, // q; s: (type depends on EVENT_TRACE_INFORMATION_CLASS)
|
||||
SystemObsolete0, // not implemented
|
||||
SystemExceptionInformation, // q: SYSTEM_EXCEPTION_INFORMATION
|
||||
SystemCrashDumpStateInformation, // s: SYSTEM_CRASH_DUMP_STATE_INFORMATION (requires SeDebugPrivilege)
|
||||
SystemKernelDebuggerInformation, // q: SYSTEM_KERNEL_DEBUGGER_INFORMATION
|
||||
SystemContextSwitchInformation, // q: SYSTEM_CONTEXT_SWITCH_INFORMATION
|
||||
SystemRegistryQuotaInformation, // q: SYSTEM_REGISTRY_QUOTA_INFORMATION; s (requires SeIncreaseQuotaPrivilege)
|
||||
SystemExtendServiceTableInformation, // s (requires SeLoadDriverPrivilege) // loads win32k only
|
||||
SystemPrioritySeperation, // s (requires SeTcbPrivilege)
|
||||
SystemVerifierAddDriverInformation, // s (requires SeDebugPrivilege) // 40
|
||||
SystemPerformanceTraceInformation, // q; s: (type depends on EVENT_TRACE_INFORMATION_CLASS)
|
||||
SystemObsolete0, // not implemented
|
||||
SystemExceptionInformation, // q: SYSTEM_EXCEPTION_INFORMATION
|
||||
SystemCrashDumpStateInformation, // s: SYSTEM_CRASH_DUMP_STATE_INFORMATION (requires SeDebugPrivilege)
|
||||
SystemKernelDebuggerInformation, // q: SYSTEM_KERNEL_DEBUGGER_INFORMATION
|
||||
SystemContextSwitchInformation, // q: SYSTEM_CONTEXT_SWITCH_INFORMATION
|
||||
SystemRegistryQuotaInformation, // q: SYSTEM_REGISTRY_QUOTA_INFORMATION; s (requires SeIncreaseQuotaPrivilege)
|
||||
SystemExtendServiceTableInformation, // s (requires SeLoadDriverPrivilege) // loads win32k only
|
||||
SystemPrioritySeperation, // s (requires SeTcbPrivilege)
|
||||
SystemVerifierAddDriverInformation, // s (requires SeDebugPrivilege) // 40
|
||||
SystemVerifierRemoveDriverInformation, // s (requires SeDebugPrivilege)
|
||||
SystemProcessorIdleInformation, // q: SYSTEM_PROCESSOR_IDLE_INFORMATION (EX in: USHORT ProcessorGroup)
|
||||
SystemLegacyDriverInformation, // q: SYSTEM_LEGACY_DRIVER_INFORMATION
|
||||
@@ -87,26 +86,26 @@ using SYSTEM_INFORMATION_CLASS = enum _SYSTEM_INFORMATION_CLASS
|
||||
SystemSessionProcessInformation, // q: SYSTEM_SESSION_PROCESS_INFORMATION
|
||||
SystemLoadGdiDriverInSystemSpace,
|
||||
// s: SYSTEM_GDI_DRIVER_INFORMATION (kernel-mode only) (same as SystemLoadGdiDriverInformation)
|
||||
SystemNumaProcessorMap, // q: SYSTEM_NUMA_INFORMATION
|
||||
SystemPrefetcherInformation, // q; s: PREFETCHER_INFORMATION // PfSnQueryPrefetcherInformation
|
||||
SystemExtendedProcessInformation, // q: SYSTEM_PROCESS_INFORMATION
|
||||
SystemRecommendedSharedDataAlignment, // q: ULONG // KeGetRecommendedSharedDataAlignment
|
||||
SystemComPlusPackage, // q; s: ULONG
|
||||
SystemNumaAvailableMemory, // q: SYSTEM_NUMA_INFORMATION // 60
|
||||
SystemProcessorPowerInformation, // q: SYSTEM_PROCESSOR_POWER_INFORMATION (EX in: USHORT ProcessorGroup)
|
||||
SystemEmulationBasicInformation, // q: SYSTEM_BASIC_INFORMATION
|
||||
SystemEmulationProcessorInformation, // q: SYSTEM_PROCESSOR_INFORMATION
|
||||
SystemExtendedHandleInformation, // q: SYSTEM_HANDLE_INFORMATION_EX
|
||||
SystemLostDelayedWriteInformation, // q: ULONG
|
||||
SystemBigPoolInformation, // q: SYSTEM_BIGPOOL_INFORMATION
|
||||
SystemSessionPoolTagInformation, // q: SYSTEM_SESSION_POOLTAG_INFORMATION
|
||||
SystemSessionMappedViewInformation, // q: SYSTEM_SESSION_MAPPED_VIEW_INFORMATION
|
||||
SystemHotpatchInformation, // q; s: SYSTEM_HOTPATCH_CODE_INFORMATION
|
||||
SystemObjectSecurityMode, // q: ULONG // 70
|
||||
SystemWatchdogTimerHandler, // s: SYSTEM_WATCHDOG_HANDLER_INFORMATION // (kernel-mode only)
|
||||
SystemWatchdogTimerInformation, // q: SYSTEM_WATCHDOG_TIMER_INFORMATION // (kernel-mode only)
|
||||
SystemLogicalProcessorInformation, // q: SYSTEM_LOGICAL_PROCESSOR_INFORMATION (EX in: USHORT ProcessorGroup)
|
||||
SystemWow64SharedInformationObsolete, // not implemented
|
||||
SystemNumaProcessorMap, // q: SYSTEM_NUMA_INFORMATION
|
||||
SystemPrefetcherInformation, // q; s: PREFETCHER_INFORMATION // PfSnQueryPrefetcherInformation
|
||||
SystemExtendedProcessInformation, // q: SYSTEM_PROCESS_INFORMATION
|
||||
SystemRecommendedSharedDataAlignment, // q: ULONG // KeGetRecommendedSharedDataAlignment
|
||||
SystemComPlusPackage, // q; s: ULONG
|
||||
SystemNumaAvailableMemory, // q: SYSTEM_NUMA_INFORMATION // 60
|
||||
SystemProcessorPowerInformation, // q: SYSTEM_PROCESSOR_POWER_INFORMATION (EX in: USHORT ProcessorGroup)
|
||||
SystemEmulationBasicInformation, // q: SYSTEM_BASIC_INFORMATION
|
||||
SystemEmulationProcessorInformation, // q: SYSTEM_PROCESSOR_INFORMATION
|
||||
SystemExtendedHandleInformation, // q: SYSTEM_HANDLE_INFORMATION_EX
|
||||
SystemLostDelayedWriteInformation, // q: ULONG
|
||||
SystemBigPoolInformation, // q: SYSTEM_BIGPOOL_INFORMATION
|
||||
SystemSessionPoolTagInformation, // q: SYSTEM_SESSION_POOLTAG_INFORMATION
|
||||
SystemSessionMappedViewInformation, // q: SYSTEM_SESSION_MAPPED_VIEW_INFORMATION
|
||||
SystemHotpatchInformation, // q; s: SYSTEM_HOTPATCH_CODE_INFORMATION
|
||||
SystemObjectSecurityMode, // q: ULONG // 70
|
||||
SystemWatchdogTimerHandler, // s: SYSTEM_WATCHDOG_HANDLER_INFORMATION // (kernel-mode only)
|
||||
SystemWatchdogTimerInformation, // q: SYSTEM_WATCHDOG_TIMER_INFORMATION // (kernel-mode only)
|
||||
SystemLogicalProcessorInformation, // q: SYSTEM_LOGICAL_PROCESSOR_INFORMATION (EX in: USHORT ProcessorGroup)
|
||||
SystemWow64SharedInformationObsolete, // not implemented
|
||||
SystemRegisterFirmwareTableInformationHandler, // s: SYSTEM_FIRMWARE_TABLE_HANDLER // (kernel-mode only)
|
||||
SystemFirmwareTableInformation, // SYSTEM_FIRMWARE_TABLE_INFORMATION
|
||||
SystemModuleInformationEx, // q: RTL_PROCESS_MODULE_INFORMATION_EX
|
||||
@@ -147,7 +146,7 @@ using SYSTEM_INFORMATION_CLASS = enum _SYSTEM_INFORMATION_CLASS
|
||||
SystemDynamicTimeZoneInformation, // q; s: RTL_DYNAMIC_TIME_ZONE_INFORMATION (requires SeTimeZonePrivilege)
|
||||
SystemCodeIntegrityInformation, // q: SYSTEM_CODEINTEGRITY_INFORMATION // SeCodeIntegrityQueryInformation
|
||||
SystemProcessorMicrocodeUpdateInformation, // s: SYSTEM_PROCESSOR_MICROCODE_UPDATE_INFORMATION
|
||||
SystemProcessorBrandString, // q: CHAR[] // HaliQuerySystemInformation -> HalpGetProcessorBrandString, info class 23
|
||||
SystemProcessorBrandString, // q: CHAR[] // HaliQuerySystemInformation -> HalpGetProcessorBrandString, info class 23
|
||||
SystemVirtualAddressInformation,
|
||||
// q: SYSTEM_VA_LIST_INFORMATION[]; s: SYSTEM_VA_LIST_INFORMATION[] (requires SeIncreaseQuotaPrivilege) //
|
||||
// MmQuerySystemVaInformation
|
||||
@@ -223,9 +222,9 @@ using SYSTEM_INFORMATION_CLASS = enum _SYSTEM_INFORMATION_CLASS
|
||||
SystemCodeIntegrityPolicyInformation, // q; s: SYSTEM_CODEINTEGRITYPOLICY_INFORMATION
|
||||
SystemIsolatedUserModeInformation, // q: SYSTEM_ISOLATED_USER_MODE_INFORMATION
|
||||
SystemHardwareSecurityTestInterfaceResultsInformation,
|
||||
SystemSingleModuleInformation, // q: SYSTEM_SINGLE_MODULE_INFORMATION
|
||||
SystemAllowedCpuSetsInformation, // s: SYSTEM_WORKLOAD_ALLOWED_CPU_SET_INFORMATION
|
||||
SystemVsmProtectionInformation, // q: SYSTEM_VSM_PROTECTION_INFORMATION (previously SystemDmaProtectionInformation)
|
||||
SystemSingleModuleInformation, // q: SYSTEM_SINGLE_MODULE_INFORMATION
|
||||
SystemAllowedCpuSetsInformation, // s: SYSTEM_WORKLOAD_ALLOWED_CPU_SET_INFORMATION
|
||||
SystemVsmProtectionInformation, // q: SYSTEM_VSM_PROTECTION_INFORMATION (previously SystemDmaProtectionInformation)
|
||||
SystemInterruptCpuSetsInformation, // q: SYSTEM_INTERRUPT_CPU_SET_INFORMATION // 170
|
||||
SystemSecureBootPolicyFullInformation, // q: SYSTEM_SECUREBOOT_POLICY_FULL_INFORMATION
|
||||
SystemCodeIntegrityPolicyFullInformation,
|
||||
@@ -366,18 +365,18 @@ typedef enum _TOKEN_INFORMATION_CLASS
|
||||
TokenRestrictedDeviceClaimAttributes, // q: CLAIM_SECURITY_ATTRIBUTES_INFORMATION
|
||||
TokenDeviceGroups, // q: TOKEN_GROUPS
|
||||
TokenRestrictedDeviceGroups, // q: TOKEN_GROUPS
|
||||
TokenSecurityAttributes, // q; s: TOKEN_SECURITY_ATTRIBUTES_[AND_OPERATION_]INFORMATION (requires SeTcbPrivilege)
|
||||
TokenIsRestricted, // q: ULONG // 40
|
||||
TokenProcessTrustLevel, // q: TOKEN_PROCESS_TRUST_LEVEL // since WINBLUE
|
||||
TokenPrivateNameSpace, // q; s: ULONG (requires SeTcbPrivilege) // since THRESHOLD
|
||||
TokenSingletonAttributes, // q: TOKEN_SECURITY_ATTRIBUTES_INFORMATION // since REDSTONE
|
||||
TokenBnoIsolation, // q: TOKEN_BNO_ISOLATION_INFORMATION // since REDSTONE2
|
||||
TokenChildProcessFlags, // s: ULONG (requires SeTcbPrivilege) // since REDSTONE3
|
||||
TokenIsLessPrivilegedAppContainer, // q: ULONG // since REDSTONE5
|
||||
TokenIsSandboxed, // q: ULONG // since 19H1
|
||||
TokenIsAppSilo, // q: ULONG // since WIN11 22H2 // previously TokenOriginatingProcessTrustLevel // q:
|
||||
// TOKEN_PROCESS_TRUST_LEVEL
|
||||
TokenLoggingInformation, // TOKEN_LOGGING_INFORMATION // since 24H2
|
||||
TokenSecurityAttributes, // q; s: TOKEN_SECURITY_ATTRIBUTES_[AND_OPERATION_]INFORMATION (requires SeTcbPrivilege)
|
||||
TokenIsRestricted, // q: ULONG // 40
|
||||
TokenProcessTrustLevel, // q: TOKEN_PROCESS_TRUST_LEVEL // since WINBLUE
|
||||
TokenPrivateNameSpace, // q; s: ULONG (requires SeTcbPrivilege) // since THRESHOLD
|
||||
TokenSingletonAttributes, // q: TOKEN_SECURITY_ATTRIBUTES_INFORMATION // since REDSTONE
|
||||
TokenBnoIsolation, // q: TOKEN_BNO_ISOLATION_INFORMATION // since REDSTONE2
|
||||
TokenChildProcessFlags, // s: ULONG (requires SeTcbPrivilege) // since REDSTONE3
|
||||
TokenIsLessPrivilegedAppContainer, // q: ULONG // since REDSTONE5
|
||||
TokenIsSandboxed, // q: ULONG // since 19H1
|
||||
TokenIsAppSilo, // q: ULONG // since WIN11 22H2 // previously TokenOriginatingProcessTrustLevel // q:
|
||||
// TOKEN_PROCESS_TRUST_LEVEL
|
||||
TokenLoggingInformation, // TOKEN_LOGGING_INFORMATION // since 24H2
|
||||
MaxTokenInfoClass
|
||||
} TOKEN_INFORMATION_CLASS, *PTOKEN_INFORMATION_CLASS;
|
||||
|
||||
@@ -385,54 +384,54 @@ typedef enum _TOKEN_INFORMATION_CLASS
|
||||
|
||||
using PROCESSINFOCLASS = enum _PROCESSINFOCLASS
|
||||
{
|
||||
ProcessBasicInformation, // q: PROCESS_BASIC_INFORMATION, PROCESS_EXTENDED_BASIC_INFORMATION
|
||||
ProcessQuotaLimits, // qs: QUOTA_LIMITS, QUOTA_LIMITS_EX
|
||||
ProcessIoCounters, // q: IO_COUNTERS
|
||||
ProcessVmCounters, // q: VM_COUNTERS, VM_COUNTERS_EX, VM_COUNTERS_EX2
|
||||
ProcessTimes, // q: KERNEL_USER_TIMES
|
||||
ProcessBasePriority, // s: KPRIORITY
|
||||
ProcessRaisePriority, // s: ULONG
|
||||
ProcessDebugPort, // q: HANDLE
|
||||
ProcessExceptionPort, // s: PROCESS_EXCEPTION_PORT (requires SeTcbPrivilege)
|
||||
ProcessAccessToken, // s: PROCESS_ACCESS_TOKEN
|
||||
ProcessLdtInformation, // qs: PROCESS_LDT_INFORMATION // 10
|
||||
ProcessLdtSize, // s: PROCESS_LDT_SIZE
|
||||
ProcessDefaultHardErrorMode, // qs: ULONG
|
||||
ProcessIoPortHandlers, // (kernel-mode only) // s: PROCESS_IO_PORT_HANDLER_INFORMATION
|
||||
ProcessPooledUsageAndLimits, // q: POOLED_USAGE_AND_LIMITS
|
||||
ProcessWorkingSetWatch, // q: PROCESS_WS_WATCH_INFORMATION[]; s: void
|
||||
ProcessUserModeIOPL, // qs: ULONG (requires SeTcbPrivilege)
|
||||
ProcessEnableAlignmentFaultFixup, // s: BOOLEAN
|
||||
ProcessPriorityClass, // qs: PROCESS_PRIORITY_CLASS
|
||||
ProcessWx86Information, // qs: ULONG (requires SeTcbPrivilege) (VdmAllowed)
|
||||
ProcessHandleCount, // q: ULONG, PROCESS_HANDLE_INFORMATION // 20
|
||||
ProcessAffinityMask, // (q >WIN7)s: KAFFINITY, qs: GROUP_AFFINITY
|
||||
ProcessPriorityBoost, // qs: ULONG
|
||||
ProcessDeviceMap, // qs: PROCESS_DEVICEMAP_INFORMATION, PROCESS_DEVICEMAP_INFORMATION_EX
|
||||
ProcessSessionInformation, // q: PROCESS_SESSION_INFORMATION
|
||||
ProcessForegroundInformation, // s: PROCESS_FOREGROUND_BACKGROUND
|
||||
ProcessWow64Information, // q: ULONG_PTR
|
||||
ProcessImageFileName, // q: UNICODE_STRING
|
||||
ProcessLUIDDeviceMapsEnabled, // q: ULONG
|
||||
ProcessBreakOnTermination, // qs: ULONG
|
||||
ProcessDebugObjectHandle, // q: HANDLE // 30
|
||||
ProcessDebugFlags, // qs: ULONG
|
||||
ProcessHandleTracing, // q: PROCESS_HANDLE_TRACING_QUERY; s: PROCESS_HANDLE_TRACING_ENABLE[_EX] or void to disable
|
||||
ProcessIoPriority, // qs: IO_PRIORITY_HINT
|
||||
ProcessExecuteFlags, // qs: ULONG (MEM_EXECUTE_OPTION_*)
|
||||
ProcessTlsInformation, // PROCESS_TLS_INFORMATION // ProcessResourceManagement
|
||||
ProcessCookie, // q: ULONG
|
||||
ProcessImageInformation, // q: SECTION_IMAGE_INFORMATION
|
||||
ProcessCycleTime, // q: PROCESS_CYCLE_TIME_INFORMATION // since VISTA
|
||||
ProcessPagePriority, // qs: PAGE_PRIORITY_INFORMATION
|
||||
ProcessInstrumentationCallback, // s: PVOID or PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION // 40
|
||||
ProcessThreadStackAllocation, // s: PROCESS_STACK_ALLOCATION_INFORMATION, PROCESS_STACK_ALLOCATION_INFORMATION_EX
|
||||
ProcessWorkingSetWatchEx, // q: PROCESS_WS_WATCH_INFORMATION_EX[]; s: void
|
||||
ProcessImageFileNameWin32, // q: UNICODE_STRING
|
||||
ProcessImageFileMapping, // q: HANDLE (input)
|
||||
ProcessAffinityUpdateMode, // qs: PROCESS_AFFINITY_UPDATE_MODE
|
||||
ProcessMemoryAllocationMode, // qs: PROCESS_MEMORY_ALLOCATION_MODE
|
||||
ProcessGroupInformation, // q: USHORT[]
|
||||
ProcessBasicInformation, // q: PROCESS_BASIC_INFORMATION, PROCESS_EXTENDED_BASIC_INFORMATION
|
||||
ProcessQuotaLimits, // qs: QUOTA_LIMITS, QUOTA_LIMITS_EX
|
||||
ProcessIoCounters, // q: IO_COUNTERS
|
||||
ProcessVmCounters, // q: VM_COUNTERS, VM_COUNTERS_EX, VM_COUNTERS_EX2
|
||||
ProcessTimes, // q: KERNEL_USER_TIMES
|
||||
ProcessBasePriority, // s: KPRIORITY
|
||||
ProcessRaisePriority, // s: ULONG
|
||||
ProcessDebugPort, // q: HANDLE
|
||||
ProcessExceptionPort, // s: PROCESS_EXCEPTION_PORT (requires SeTcbPrivilege)
|
||||
ProcessAccessToken, // s: PROCESS_ACCESS_TOKEN
|
||||
ProcessLdtInformation, // qs: PROCESS_LDT_INFORMATION // 10
|
||||
ProcessLdtSize, // s: PROCESS_LDT_SIZE
|
||||
ProcessDefaultHardErrorMode, // qs: ULONG
|
||||
ProcessIoPortHandlers, // (kernel-mode only) // s: PROCESS_IO_PORT_HANDLER_INFORMATION
|
||||
ProcessPooledUsageAndLimits, // q: POOLED_USAGE_AND_LIMITS
|
||||
ProcessWorkingSetWatch, // q: PROCESS_WS_WATCH_INFORMATION[]; s: void
|
||||
ProcessUserModeIOPL, // qs: ULONG (requires SeTcbPrivilege)
|
||||
ProcessEnableAlignmentFaultFixup, // s: BOOLEAN
|
||||
ProcessPriorityClass, // qs: PROCESS_PRIORITY_CLASS
|
||||
ProcessWx86Information, // qs: ULONG (requires SeTcbPrivilege) (VdmAllowed)
|
||||
ProcessHandleCount, // q: ULONG, PROCESS_HANDLE_INFORMATION // 20
|
||||
ProcessAffinityMask, // (q >WIN7)s: KAFFINITY, qs: GROUP_AFFINITY
|
||||
ProcessPriorityBoost, // qs: ULONG
|
||||
ProcessDeviceMap, // qs: PROCESS_DEVICEMAP_INFORMATION, PROCESS_DEVICEMAP_INFORMATION_EX
|
||||
ProcessSessionInformation, // q: PROCESS_SESSION_INFORMATION
|
||||
ProcessForegroundInformation, // s: PROCESS_FOREGROUND_BACKGROUND
|
||||
ProcessWow64Information, // q: ULONG_PTR
|
||||
ProcessImageFileName, // q: UNICODE_STRING
|
||||
ProcessLUIDDeviceMapsEnabled, // q: ULONG
|
||||
ProcessBreakOnTermination, // qs: ULONG
|
||||
ProcessDebugObjectHandle, // q: HANDLE // 30
|
||||
ProcessDebugFlags, // qs: ULONG
|
||||
ProcessHandleTracing, // q: PROCESS_HANDLE_TRACING_QUERY; s: PROCESS_HANDLE_TRACING_ENABLE[_EX] or void to disable
|
||||
ProcessIoPriority, // qs: IO_PRIORITY_HINT
|
||||
ProcessExecuteFlags, // qs: ULONG (MEM_EXECUTE_OPTION_*)
|
||||
ProcessTlsInformation, // PROCESS_TLS_INFORMATION // ProcessResourceManagement
|
||||
ProcessCookie, // q: ULONG
|
||||
ProcessImageInformation, // q: SECTION_IMAGE_INFORMATION
|
||||
ProcessCycleTime, // q: PROCESS_CYCLE_TIME_INFORMATION // since VISTA
|
||||
ProcessPagePriority, // qs: PAGE_PRIORITY_INFORMATION
|
||||
ProcessInstrumentationCallback, // s: PVOID or PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION // 40
|
||||
ProcessThreadStackAllocation, // s: PROCESS_STACK_ALLOCATION_INFORMATION, PROCESS_STACK_ALLOCATION_INFORMATION_EX
|
||||
ProcessWorkingSetWatchEx, // q: PROCESS_WS_WATCH_INFORMATION_EX[]; s: void
|
||||
ProcessImageFileNameWin32, // q: UNICODE_STRING
|
||||
ProcessImageFileMapping, // q: HANDLE (input)
|
||||
ProcessAffinityUpdateMode, // qs: PROCESS_AFFINITY_UPDATE_MODE
|
||||
ProcessMemoryAllocationMode, // qs: PROCESS_MEMORY_ALLOCATION_MODE
|
||||
ProcessGroupInformation, // q: USHORT[]
|
||||
ProcessTokenVirtualizationEnabled, // s: ULONG
|
||||
ProcessConsoleHostProcess, // qs: ULONG_PTR // ProcessOwnerInformation
|
||||
ProcessWindowInformation, // q: PROCESS_WINDOW_INFORMATION // 50
|
||||
|
||||
@@ -10,6 +10,7 @@ using NTSTATUS = std::uint32_t;
|
||||
#define STATUS_TIMEOUT ((NTSTATUS)0x00000102L)
|
||||
#define STATUS_PENDING ((NTSTATUS)0x00000103L)
|
||||
|
||||
#define STATUS_GUARD_PAGE_VIOLATION ((NTSTATUS)0x80000001L)
|
||||
#define STATUS_BREAKPOINT ((NTSTATUS)0x80000003L)
|
||||
#define STATUS_SINGLE_STEP ((NTSTATUS)0x80000004L)
|
||||
|
||||
@@ -35,6 +36,7 @@ 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_INVALID_PAGE_PROTECTION ((NTSTATUS)0xC0000045L)
|
||||
#define STATUS_MUTANT_NOT_OWNED ((NTSTATUS)0xC0000046L)
|
||||
#define STATUS_SEMAPHORE_LIMIT_EXCEEDED ((NTSTATUS)0xC0000047L)
|
||||
#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
|
||||
@@ -50,6 +52,16 @@ using NTSTATUS = std::uint32_t;
|
||||
#define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS)0xC0000328L)
|
||||
#define STATUS_PORT_NOT_SET ((NTSTATUS)0xC0000353L)
|
||||
#define STATUS_DEBUGGER_INACTIVE ((NTSTATUS)0xC0000354L)
|
||||
#define STATUS_PIPE_BROKEN ((NTSTATUS)0xC000014BL)
|
||||
#define STATUS_PIPE_EMPTY ((NTSTATUS)0xC00000D9L)
|
||||
#define STATUS_PIPE_BUSY ((NTSTATUS)0xC00000AAL)
|
||||
#define STATUS_PIPE_DISCONNECTED ((NTSTATUS)0xC00000B0L)
|
||||
#define STATUS_PIPE_LISTENING ((NTSTATUS)0x00000105L)
|
||||
#define STATUS_PIPE_CONNECTED ((NTSTATUS)0x00000106L)
|
||||
#define STATUS_PIPE_CLOSING ((NTSTATUS)0xC00000B1L)
|
||||
#define STATUS_PIPE_NOT_AVAILABLE ((NTSTATUS)0xC00000ACL)
|
||||
#define STATUS_INVALID_PIPE_STATE ((NTSTATUS)0xC00000ADL)
|
||||
#define STATUS_PIPE_NOT_CONNECTED ((NTSTATUS)0xC00000BEL)
|
||||
|
||||
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
|
||||
|
||||
|
||||
@@ -283,6 +283,47 @@ typedef struct _IMAGE_BASE_RELOCATION
|
||||
// WORD TypeOffset[1];
|
||||
} IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;
|
||||
|
||||
#define IMAGE_ORDINAL_FLAG64 0x8000000000000000
|
||||
#define IMAGE_ORDINAL_FLAG32 0x80000000
|
||||
#define IMAGE_ORDINAL64(Ordinal) (Ordinal & 0xffff)
|
||||
#define IMAGE_ORDINAL32(Ordinal) (Ordinal & 0xffff)
|
||||
#define IMAGE_SNAP_BY_ORDINAL64(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG64) != 0)
|
||||
#define IMAGE_SNAP_BY_ORDINAL32(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG32) != 0)
|
||||
|
||||
typedef struct _IMAGE_IMPORT_BY_NAME
|
||||
{
|
||||
WORD Hint;
|
||||
CHAR Name[1];
|
||||
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
|
||||
|
||||
typedef struct _IMAGE_IMPORT_DESCRIPTOR
|
||||
{
|
||||
// union
|
||||
//{
|
||||
// DWORD Characteristics; // 0 for terminating null import descriptor
|
||||
DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
|
||||
//} DUMMYUNIONNAME;
|
||||
DWORD TimeDateStamp; // 0 if not bound,
|
||||
// -1 if bound, and real date\time stamp
|
||||
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
|
||||
// O.W. date/time stamp of DLL bound to (Old BIND)
|
||||
|
||||
DWORD ForwarderChain; // -1 if no forwarders
|
||||
DWORD Name;
|
||||
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
|
||||
} IMAGE_IMPORT_DESCRIPTOR;
|
||||
|
||||
typedef struct _IMAGE_THUNK_DATA64
|
||||
{
|
||||
union
|
||||
{
|
||||
ULONGLONG ForwarderString; // PBYTE
|
||||
ULONGLONG Function; // PDWORD
|
||||
ULONGLONG Ordinal;
|
||||
ULONGLONG AddressOfData; // PIMAGE_IMPORT_BY_NAME
|
||||
} u1;
|
||||
} IMAGE_THUNK_DATA64;
|
||||
|
||||
#endif
|
||||
|
||||
template <typename Traits>
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
namespace utils
|
||||
{
|
||||
template <typename Type, typename SpanElement = const std::byte>
|
||||
requires(std::is_trivially_copyable_v<Type> && (std::is_same_v<uint8_t, std::remove_cv_t<SpanElement>> ||
|
||||
std::is_same_v<std::byte, std::remove_cv_t<SpanElement>>))
|
||||
requires(std::is_trivially_copyable_v<Type> &&
|
||||
(std::is_same_v<uint8_t, std::remove_cv_t<SpanElement>> || std::is_same_v<std::byte, std::remove_cv_t<SpanElement>>))
|
||||
class safe_object_accessor
|
||||
{
|
||||
public:
|
||||
@@ -54,8 +54,7 @@ namespace utils
|
||||
};
|
||||
|
||||
template <typename SpanElement>
|
||||
requires(std::is_same_v<uint8_t, std::remove_cv_t<SpanElement>> ||
|
||||
std::is_same_v<std::byte, std::remove_cv_t<SpanElement>>)
|
||||
requires(std::is_same_v<uint8_t, std::remove_cv_t<SpanElement>> || std::is_same_v<std::byte, std::remove_cv_t<SpanElement>>)
|
||||
class safe_buffer_accessor
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -42,8 +42,7 @@ namespace utils
|
||||
{
|
||||
using is_transparent = void;
|
||||
|
||||
bool operator()(const std::basic_string_view<Elem, Traits> lhs,
|
||||
const std::basic_string_view<Elem, Traits> rhs) const
|
||||
bool operator()(const std::basic_string_view<Elem, Traits> lhs, const std::basic_string_view<Elem, Traits> rhs) const
|
||||
{
|
||||
return string::equals_ignore_case(lhs, rhs);
|
||||
}
|
||||
@@ -64,8 +63,7 @@ namespace utils
|
||||
using unordered_u16string_map = std::unordered_map<std::u16string, T, u16string_hash, std::equal_to<>>;
|
||||
|
||||
template <typename T>
|
||||
using unordered_insensitive_string_map =
|
||||
std::unordered_map<std::string, T, insensitive_string_hash, insensitive_string_equal>;
|
||||
using unordered_insensitive_string_map = std::unordered_map<std::string, T, insensitive_string_hash, insensitive_string_equal>;
|
||||
template <typename T>
|
||||
using unordered_insensitive_u16string_map =
|
||||
std::unordered_map<std::u16string, T, insensitive_u16string_hash, insensitive_u16string_equal>;
|
||||
|
||||
@@ -28,8 +28,7 @@ namespace utils::io
|
||||
io::create_directory(file.parent_path());
|
||||
}
|
||||
|
||||
std::ofstream stream(file, std::ios::binary | std::ofstream::out |
|
||||
(append ? std::ofstream::app : std::ofstream::out));
|
||||
std::ofstream stream(file, std::ios::binary | std::ofstream::out | (append ? std::ofstream::app : std::ofstream::out));
|
||||
|
||||
if (stream.is_open())
|
||||
{
|
||||
@@ -114,9 +113,8 @@ namespace utils::io
|
||||
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target)
|
||||
{
|
||||
std::error_code ec{};
|
||||
std::filesystem::copy(
|
||||
src, target, std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::recursive,
|
||||
ec);
|
||||
std::filesystem::copy(src, target, std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::recursive,
|
||||
ec);
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> list_files(const std::filesystem::path& directory, const bool recursive)
|
||||
|
||||
@@ -12,4 +12,12 @@ namespace utils
|
||||
object& operator=(object&&) = default;
|
||||
object& operator=(const object&) = default;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void reset_object_with_delayed_destruction(T& obj)
|
||||
{
|
||||
T new_obj{};
|
||||
const auto old = std::move(obj);
|
||||
obj = std::move(new_obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,18 +180,14 @@ namespace utils::string
|
||||
}
|
||||
|
||||
template <class Elem, class Traits, class Alloc>
|
||||
bool equals_ignore_case(const std::basic_string<Elem, Traits, Alloc>& lhs,
|
||||
const std::basic_string<Elem, Traits, Alloc>& rhs)
|
||||
bool equals_ignore_case(const std::basic_string<Elem, Traits, Alloc>& lhs, const std::basic_string<Elem, Traits, Alloc>& rhs)
|
||||
{
|
||||
return std::ranges::equal(lhs, rhs,
|
||||
[](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); });
|
||||
return std::ranges::equal(lhs, rhs, [](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); });
|
||||
}
|
||||
|
||||
template <class Elem, class Traits>
|
||||
bool equals_ignore_case(const std::basic_string_view<Elem, Traits>& lhs,
|
||||
const std::basic_string_view<Elem, Traits>& rhs)
|
||||
bool equals_ignore_case(const std::basic_string_view<Elem, Traits>& lhs, const std::basic_string_view<Elem, Traits>& rhs)
|
||||
{
|
||||
return std::ranges::equal(lhs, rhs,
|
||||
[](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); });
|
||||
return std::ranges::equal(lhs, rhs, [](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,14 @@
|
||||
|
||||
namespace utils
|
||||
{
|
||||
std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(clock& c,
|
||||
const LARGE_INTEGER delay_interval)
|
||||
std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(clock& c, const LARGE_INTEGER delay_interval)
|
||||
{
|
||||
if (delay_interval.QuadPart <= 0)
|
||||
{
|
||||
const auto relative_time = -delay_interval.QuadPart;
|
||||
const auto relative_ticks_in_ms = relative_time / 10;
|
||||
const auto relative_fraction_ns = (relative_time % 10) * 100;
|
||||
const auto relative_duration =
|
||||
std::chrono::microseconds(relative_ticks_in_ms) + std::chrono::nanoseconds(relative_fraction_ns);
|
||||
const auto relative_duration = std::chrono::microseconds(relative_ticks_in_ms) + std::chrono::nanoseconds(relative_fraction_ns);
|
||||
|
||||
return c.steady_now() + relative_duration;
|
||||
}
|
||||
@@ -22,13 +20,12 @@ namespace utils
|
||||
|
||||
const auto delay_seconds_since_1970 = delay_seconds_since_1601 - EPOCH_DIFFERENCE_1601_TO_1970_SECONDS;
|
||||
|
||||
const auto target_time = std::chrono::system_clock::from_time_t(delay_seconds_since_1970) +
|
||||
std::chrono::nanoseconds(delay_fraction_ns);
|
||||
const auto target_time =
|
||||
std::chrono::system_clock::from_time_t(delay_seconds_since_1970) + std::chrono::nanoseconds(delay_fraction_ns);
|
||||
|
||||
const auto now_system = c.system_now();
|
||||
|
||||
const auto duration_until_target =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(target_time - now_system);
|
||||
const auto duration_until_target = std::chrono::duration_cast<std::chrono::microseconds>(target_time - now_system);
|
||||
|
||||
return c.steady_now() + duration_until_target;
|
||||
}
|
||||
|
||||
@@ -40,8 +40,7 @@ namespace utils
|
||||
/// TODO: find better solution for ARM and Figure out better CPU base frequency heuristics
|
||||
virtual uint64_t timestamp_counter()
|
||||
{
|
||||
#if defined(_M_X64) || defined(_M_AMD64) || defined(_M_IX86) || defined(__x86_64__) || defined(__i386__) || \
|
||||
defined(__amd64__)
|
||||
#if defined(_M_X64) || defined(_M_AMD64) || defined(_M_IX86) || defined(__x86_64__) || defined(__i386__) || defined(__amd64__)
|
||||
return __rdtsc(); // any x86 system will have this instrinsic
|
||||
#else
|
||||
const auto count = std::chrono::high_resolution_clock::now().time_since_epoch().count();
|
||||
@@ -53,8 +52,7 @@ namespace utils
|
||||
class tick_clock : public clock
|
||||
{
|
||||
public:
|
||||
tick_clock(const uint64_t frequency = 1, const system_time_point system_start = {},
|
||||
const steady_time_point steady_start = {})
|
||||
tick_clock(const uint64_t frequency = 1, const system_time_point system_start = {}, const steady_time_point steady_start = {})
|
||||
: frequency_(frequency),
|
||||
system_start_(system_start),
|
||||
steady_start_(steady_start)
|
||||
|
||||
@@ -127,8 +127,7 @@ namespace debugger
|
||||
void handle_read_register(const event_context& c, const Debugger::ReadRegisterRequestT& request)
|
||||
{
|
||||
std::array<uint8_t, 512> buffer{};
|
||||
const auto res = c.win_emu.emu().read_register(static_cast<x86_register>(request.register_), buffer.data(),
|
||||
buffer.size());
|
||||
const auto res = c.win_emu.emu().read_register(static_cast<x86_register>(request.register_), buffer.data(), buffer.size());
|
||||
|
||||
const auto size = std::min(buffer.size(), res);
|
||||
|
||||
@@ -146,8 +145,8 @@ namespace debugger
|
||||
|
||||
try
|
||||
{
|
||||
size = c.win_emu.emu().write_register(static_cast<x86_register>(request.register_), request.data.data(),
|
||||
request.data.size());
|
||||
size =
|
||||
c.win_emu.emu().write_register(static_cast<x86_register>(request.register_), request.data.data(), request.data.size());
|
||||
success = true;
|
||||
}
|
||||
catch (...)
|
||||
|
||||
@@ -28,8 +28,7 @@ constexpr bool regions_intersect(const uint64_t start1, const uint64_t end1, con
|
||||
return start1 < end2 && start2 < end1;
|
||||
}
|
||||
|
||||
constexpr bool regions_with_length_intersect(const uint64_t start1, const uint64_t length1, const uint64_t start2,
|
||||
const uint64_t length2)
|
||||
constexpr bool regions_with_length_intersect(const uint64_t start1, const uint64_t length1, const uint64_t start2, const uint64_t length2)
|
||||
{
|
||||
return regions_intersect(start1, start1 + length1, start2, start2 + length2);
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ enum class x86_hookable_instructions
|
||||
cpuid,
|
||||
rdtsc,
|
||||
rdtscp,
|
||||
sgdt,
|
||||
};
|
||||
|
||||
// --[x86_64]-------------------------------------------------------------------------
|
||||
|
||||
@@ -35,18 +35,18 @@ struct basic_block
|
||||
size_t size;
|
||||
};
|
||||
|
||||
using edge_generation_hook_callback =
|
||||
std::function<void(const basic_block& current_block, const basic_block& previous_block)>;
|
||||
using edge_generation_hook_callback = std::function<void(const basic_block& current_block, const basic_block& previous_block)>;
|
||||
using basic_block_hook_callback = std::function<void(const basic_block& block)>;
|
||||
|
||||
using instruction_hook_callback = std::function<instruction_hook_continuation()>;
|
||||
using simple_instruction_hook_callback = std::function<instruction_hook_continuation()>;
|
||||
using instruction_hook_callback = std::function<instruction_hook_continuation(uint64_t data)>;
|
||||
using interrupt_hook_callback = std::function<void(int interrupt)>;
|
||||
|
||||
using memory_access_hook_callback = std::function<void(uint64_t address, const void* data, size_t size)>;
|
||||
using memory_execution_hook_callback = std::function<void(uint64_t address)>;
|
||||
|
||||
using memory_violation_hook_callback = std::function<memory_violation_continuation(
|
||||
uint64_t address, size_t size, memory_operation operation, memory_violation_type type)>;
|
||||
using memory_violation_hook_callback =
|
||||
std::function<memory_violation_continuation(uint64_t address, size_t size, memory_operation operation, memory_violation_type type)>;
|
||||
|
||||
class hook_interface
|
||||
{
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
#include "memory_permission.hpp"
|
||||
#include <cstddef>
|
||||
|
||||
template <typename PermissionType = memory_permission>
|
||||
struct basic_memory_region
|
||||
{
|
||||
uint64_t start{};
|
||||
size_t length{}; // uint64_t?
|
||||
memory_permission permissions{};
|
||||
PermissionType permissions{};
|
||||
};
|
||||
|
||||
struct memory_region : basic_memory_region
|
||||
struct memory_region : basic_memory_region<>
|
||||
{
|
||||
bool committed{};
|
||||
};
|
||||
|
||||
@@ -6,6 +6,11 @@ class scoped_hook
|
||||
public:
|
||||
scoped_hook() = default;
|
||||
|
||||
scoped_hook(emulator& emu)
|
||||
: emu_(&emu)
|
||||
{
|
||||
}
|
||||
|
||||
scoped_hook(emulator& emu, emulator_hook* hook)
|
||||
: scoped_hook(emu, std::vector{hook})
|
||||
{
|
||||
@@ -44,6 +49,23 @@ class scoped_hook
|
||||
return *this;
|
||||
}
|
||||
|
||||
scoped_hook& operator=(emulator_hook* hook)
|
||||
{
|
||||
this->replace({hook});
|
||||
return *this;
|
||||
}
|
||||
|
||||
void replace(std::vector<emulator_hook*> hooks)
|
||||
{
|
||||
if (!this->emu_)
|
||||
{
|
||||
throw std::runtime_error("Invalid scoped hook");
|
||||
}
|
||||
|
||||
this->remove();
|
||||
this->hooks_ = std::move(hooks);
|
||||
}
|
||||
|
||||
void remove()
|
||||
{
|
||||
auto hooks = std::move(this->hooks_);
|
||||
|
||||
@@ -40,9 +40,8 @@ namespace utils
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct has_serialize_function<T,
|
||||
std::void_t<decltype(serialize(std::declval<buffer_serializer&>(),
|
||||
std::declval<const std::remove_cvref_t<T>&>()))>>
|
||||
struct has_serialize_function<
|
||||
T, std::void_t<decltype(serialize(std::declval<buffer_serializer&>(), std::declval<const std::remove_cvref_t<T>&>()))>>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
@@ -53,8 +52,8 @@ namespace utils
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct has_deserialize_function<T, std::void_t<decltype(deserialize(std::declval<buffer_deserializer&>(),
|
||||
std::declval<std::remove_cvref_t<T>&>()))>>
|
||||
struct has_deserialize_function<
|
||||
T, std::void_t<decltype(deserialize(std::declval<buffer_deserializer&>(), std::declval<std::remove_cvref_t<T>&>()))>>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
@@ -506,8 +505,7 @@ namespace utils
|
||||
const auto factory = this->factories_.find(std::type_index(typeid(T)));
|
||||
if (factory == this->factories_.end())
|
||||
{
|
||||
throw std::runtime_error("Object construction failed. Missing factory for type: " +
|
||||
std::string(typeid(T).name()));
|
||||
throw std::runtime_error("Object construction failed. Missing factory for type: " + std::string(typeid(T).name()));
|
||||
}
|
||||
|
||||
auto* object = static_cast<T*>(factory->second());
|
||||
|
||||
@@ -81,6 +81,13 @@ class typed_emulator : public emulator
|
||||
return this->hook_instruction(static_cast<int>(instruction_type), std::move(callback));
|
||||
}
|
||||
|
||||
emulator_hook* hook_instruction(hookable_instructions instruction_type, simple_instruction_hook_callback callback)
|
||||
{
|
||||
return this->hook_instruction(instruction_type, [c = std::move(callback)](const uint64_t) {
|
||||
return c(); //
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) override = 0;
|
||||
|
||||
|
||||
@@ -112,8 +112,7 @@ namespace
|
||||
restore_emulator();
|
||||
|
||||
const auto memory = emu.memory.allocate_memory(
|
||||
static_cast<size_t>(page_align_up(std::max(data.size(), static_cast<size_t>(1)))),
|
||||
memory_permission::read_write);
|
||||
static_cast<size_t>(page_align_up(std::max(data.size(), static_cast<size_t>(1)))), memory_permission::read_write);
|
||||
emu.emu().write_memory(memory, data.data(), data.size());
|
||||
|
||||
emu.emu().reg(x86_register::rcx, memory);
|
||||
|
||||
@@ -177,8 +177,7 @@ namespace fuzzer
|
||||
const auto executions = context.executions.exchange(0);
|
||||
const auto highest_scorer = context.generator.get_highest_scorer();
|
||||
const auto avg_score = context.generator.get_average_score();
|
||||
printf("Executions/s: %" PRIu64 " - Score: %" PRIx64 " - Avg: %.3f\n", executions, highest_scorer.score,
|
||||
avg_score);
|
||||
printf("Executions/s: %" PRIu64 " - Score: %" PRIx64 " - Avg: %.3f\n", executions, highest_scorer.score, avg_score);
|
||||
}
|
||||
|
||||
const auto duration = t.elapsed();
|
||||
|
||||
@@ -19,8 +19,7 @@ namespace fuzzer
|
||||
{
|
||||
virtual ~executer() = default;
|
||||
|
||||
virtual execution_result execute(std::span<const uint8_t> data,
|
||||
const std::function<coverage_functor>& coverage_handler) = 0;
|
||||
virtual execution_result execute(std::span<const uint8_t> data, const std::function<coverage_functor>& coverage_handler) = 0;
|
||||
};
|
||||
|
||||
struct fuzzing_handler
|
||||
|
||||
@@ -106,8 +106,7 @@ namespace fuzzer
|
||||
}
|
||||
|
||||
const auto insert_at_random = this->rng.get(10) == 0;
|
||||
const auto index =
|
||||
insert_at_random ? (this->rng.get<size_t>() % this->top_scorer_.size()) : this->lowest_scorer;
|
||||
const auto index = insert_at_random ? (this->rng.get<size_t>() % this->top_scorer_.size()) : this->lowest_scorer;
|
||||
|
||||
this->top_scorer_[index] = std::move(entry);
|
||||
|
||||
|
||||
@@ -23,8 +23,7 @@ namespace gdb_stub
|
||||
}
|
||||
}
|
||||
|
||||
connection_handler::connection_handler(network::tcp_client_socket& client,
|
||||
utils::optional_function<bool()> should_stop)
|
||||
connection_handler::connection_handler(network::tcp_client_socket& client, utils::optional_function<bool()> should_stop)
|
||||
: should_stop_(std::move(should_stop)),
|
||||
client_(client)
|
||||
{
|
||||
|
||||
@@ -35,8 +35,7 @@ namespace gdb_stub
|
||||
async_handler& async;
|
||||
};
|
||||
|
||||
network::tcp_client_socket accept_client(const network::address& bind_address,
|
||||
const utils::optional_function<bool()>& should_stop)
|
||||
network::tcp_client_socket accept_client(const network::address& bind_address, const utils::optional_function<bool()>& should_stop)
|
||||
{
|
||||
network::tcp_server_socket server{bind_address.get_family()};
|
||||
if (!server.bind(bind_address))
|
||||
@@ -194,8 +193,8 @@ namespace gdb_stub
|
||||
return static_cast<breakpoint_type>(type);
|
||||
}
|
||||
|
||||
bool change_breakpoint(debugging_handler& handler, const bool set, const breakpoint_type type,
|
||||
const uint64_t address, const size_t size)
|
||||
bool change_breakpoint(debugging_handler& handler, const bool set, const breakpoint_type type, const uint64_t address,
|
||||
const size_t size)
|
||||
{
|
||||
if (set)
|
||||
{
|
||||
|
||||
@@ -295,8 +295,7 @@ namespace
|
||||
for (DWORD i = 0;; ++i)
|
||||
{
|
||||
auto name_buffer_len = static_cast<DWORD>(name_buffer.size());
|
||||
const LSTATUS status =
|
||||
RegEnumKeyExA(key, i, name_buffer.data(), &name_buffer_len, nullptr, nullptr, nullptr, nullptr);
|
||||
const LSTATUS status = RegEnumKeyExA(key, i, name_buffer.data(), &name_buffer_len, nullptr, nullptr, nullptr, nullptr);
|
||||
if (status == ERROR_SUCCESS)
|
||||
{
|
||||
keys.emplace_back(name_buffer.data(), name_buffer_len);
|
||||
@@ -340,8 +339,7 @@ namespace
|
||||
for (DWORD i = 0;; ++i)
|
||||
{
|
||||
auto name_buffer_len = static_cast<DWORD>(name_buffer.size());
|
||||
const auto status =
|
||||
RegEnumValueA(key, i, name_buffer.data(), &name_buffer_len, nullptr, nullptr, nullptr, nullptr);
|
||||
const auto status = RegEnumValueA(key, i, name_buffer.data(), &name_buffer_len, nullptr, nullptr, nullptr, nullptr);
|
||||
if (status == ERROR_SUCCESS)
|
||||
{
|
||||
values.emplace_back(name_buffer.data(), name_buffer_len);
|
||||
@@ -383,16 +381,14 @@ namespace
|
||||
|
||||
// WOW64 Redirection Test
|
||||
const auto pst_display = read_registry_string(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
R"(SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Time Zones\Pacific Standard Time)", "Display");
|
||||
HKEY_LOCAL_MACHINE, R"(SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Time Zones\Pacific Standard Time)", "Display");
|
||||
if (!pst_display || pst_display->empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Key Sub-keys Enumeration Test
|
||||
const auto subkeys_opt =
|
||||
get_all_registry_keys(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)");
|
||||
const auto subkeys_opt = get_all_registry_keys(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)");
|
||||
if (!subkeys_opt)
|
||||
{
|
||||
return false;
|
||||
@@ -413,8 +409,7 @@ namespace
|
||||
}
|
||||
|
||||
// Key Values Enumeration Test
|
||||
const auto values_opt =
|
||||
get_all_registry_values(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)");
|
||||
const auto values_opt = get_all_registry_values(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)");
|
||||
if (!values_opt)
|
||||
{
|
||||
return false;
|
||||
@@ -489,17 +484,15 @@ namespace
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current_dtzi.StandardDate.wYear != 0 || current_dtzi.StandardDate.wMonth != 10 ||
|
||||
current_dtzi.StandardDate.wDayOfWeek != 0 || current_dtzi.StandardDate.wDay != 5 ||
|
||||
current_dtzi.StandardDate.wHour != 3 || current_dtzi.StandardDate.wMinute != 0 ||
|
||||
if (current_dtzi.StandardDate.wYear != 0 || current_dtzi.StandardDate.wMonth != 10 || current_dtzi.StandardDate.wDayOfWeek != 0 ||
|
||||
current_dtzi.StandardDate.wDay != 5 || current_dtzi.StandardDate.wHour != 3 || current_dtzi.StandardDate.wMinute != 0 ||
|
||||
current_dtzi.StandardDate.wSecond != 0 || current_dtzi.StandardDate.wMilliseconds != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current_dtzi.DaylightDate.wYear != 0 || current_dtzi.DaylightDate.wMonth != 3 ||
|
||||
current_dtzi.DaylightDate.wDayOfWeek != 0 || current_dtzi.DaylightDate.wDay != 5 ||
|
||||
current_dtzi.DaylightDate.wHour != 2 || current_dtzi.DaylightDate.wMinute != 0 ||
|
||||
if (current_dtzi.DaylightDate.wYear != 0 || current_dtzi.DaylightDate.wMonth != 3 || current_dtzi.DaylightDate.wDayOfWeek != 0 ||
|
||||
current_dtzi.DaylightDate.wDay != 5 || current_dtzi.DaylightDate.wHour != 2 || current_dtzi.DaylightDate.wMinute != 0 ||
|
||||
current_dtzi.DaylightDate.wSecond != 0 || current_dtzi.DaylightDate.wMilliseconds != 0)
|
||||
{
|
||||
return false;
|
||||
@@ -583,8 +576,7 @@ namespace
|
||||
sockaddr_in sender_addr{};
|
||||
int sender_length = sizeof(sender_addr);
|
||||
|
||||
const auto len =
|
||||
recvfrom(receiver, buffer, sizeof(buffer), 0, reinterpret_cast<sockaddr*>(&sender_addr), &sender_length);
|
||||
const auto len = recvfrom(receiver, buffer, sizeof(buffer), 0, reinterpret_cast<sockaddr*>(&sender_addr), &sender_length);
|
||||
|
||||
if (len != send_data.size())
|
||||
{
|
||||
@@ -647,9 +639,105 @@ namespace
|
||||
return res;
|
||||
}
|
||||
|
||||
INT32 test_guard_page_seh_filter(LPVOID address, DWORD code, struct _EXCEPTION_POINTERS* ep)
|
||||
{
|
||||
// We are only looking for guard page exceptions.
|
||||
if (code != STATUS_GUARD_PAGE_VIOLATION)
|
||||
{
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
// The number of defined elements in the ExceptionInformation array for
|
||||
// a guard page violation should be 2.
|
||||
if (ep->ExceptionRecord->NumberParameters != 2)
|
||||
{
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
// The ExceptionInformation array specifies additional arguments that
|
||||
// describe the exception.
|
||||
auto* exception_information = ep->ExceptionRecord->ExceptionInformation;
|
||||
|
||||
// If this value is zero, the thread attempted to read the inaccessible
|
||||
// data. If this value is 1, the thread attempted to write to an
|
||||
// inaccessible address.
|
||||
if (exception_information[0] != 1)
|
||||
{
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
// The second array element specifies the virtual address of the
|
||||
// inaccessible data.
|
||||
if (exception_information[1] != (ULONG_PTR)address)
|
||||
{
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
bool test_guard_page_exception()
|
||||
{
|
||||
SYSTEM_INFO sys_info;
|
||||
GetSystemInfo(&sys_info);
|
||||
|
||||
// Allocate a guarded memory region with the length of the system page
|
||||
// size.
|
||||
auto* addr = static_cast<LPBYTE>(VirtualAlloc(nullptr, sys_info.dwPageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD));
|
||||
if (addr == nullptr)
|
||||
{
|
||||
puts("Failed to allocate guard page");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
|
||||
// We want to access some arbitrary offset into the guarded page, to
|
||||
// ensure that ExceptionInformation correctly contains the virtual
|
||||
// address of the inaccessible data, not the base address of the region.
|
||||
constexpr size_t offset = 10;
|
||||
|
||||
// Trigger a guard page violation
|
||||
__try
|
||||
{
|
||||
addr[offset] = 255;
|
||||
}
|
||||
// If the filter function returns EXCEPTION_CONTINUE_SEARCH, the
|
||||
// exception contains all of the correct information.
|
||||
__except (test_guard_page_seh_filter(addr + offset, GetExceptionCode(), GetExceptionInformation()))
|
||||
{
|
||||
success = true;
|
||||
}
|
||||
|
||||
// The page guard should be lifted, so no exception should be raised.
|
||||
__try
|
||||
{
|
||||
// The previous write should not have went through, this is probably
|
||||
// superflous.
|
||||
if (addr[offset] == 255)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
puts("Failed to read from page after guard exception!");
|
||||
success = false;
|
||||
}
|
||||
|
||||
// Free the allocated memory
|
||||
if (!VirtualFree(addr, 0, MEM_RELEASE))
|
||||
{
|
||||
puts("Failed to free allocated region");
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool test_native_exceptions()
|
||||
{
|
||||
return test_access_violation_exception() && test_illegal_instruction_exception();
|
||||
return test_access_violation_exception() && test_illegal_instruction_exception() && test_guard_page_exception();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -695,6 +783,11 @@ namespace
|
||||
|
||||
bool test_apis()
|
||||
{
|
||||
if (VirtualProtect(nullptr, 0, 0, nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
wchar_t buffer[0x100];
|
||||
DWORD size = sizeof(buffer) / 2;
|
||||
return GetComputerNameExW(ComputerNameNetBIOS, buffer, &size);
|
||||
|
||||
@@ -56,8 +56,8 @@ void print_apiset(PAPI_SET_NAMESPACE api_set_map)
|
||||
{
|
||||
for (ULONG i = 0; i < api_set_map->Count; i++)
|
||||
{
|
||||
const auto entry = reinterpret_cast<PAPI_SET_NAMESPACE_ENTRY>(
|
||||
reinterpret_cast<ULONG_PTR>(api_set_map) + api_set_map->EntryOffset + i * sizeof(API_SET_NAMESPACE_ENTRY));
|
||||
const auto entry = reinterpret_cast<PAPI_SET_NAMESPACE_ENTRY>(reinterpret_cast<ULONG_PTR>(api_set_map) + api_set_map->EntryOffset +
|
||||
i * sizeof(API_SET_NAMESPACE_ENTRY));
|
||||
|
||||
// printf(" Flags: %08X\n", entry->Flags);
|
||||
// printf(" NameOffset: %08X\n", entry->NameOffset);
|
||||
@@ -72,8 +72,8 @@ void print_apiset(PAPI_SET_NAMESPACE api_set_map)
|
||||
|
||||
for (ULONG x = 0; x < entry->ValueCount; x++)
|
||||
{
|
||||
const auto value = reinterpret_cast<PAPI_SET_VALUE_ENTRY>(
|
||||
reinterpret_cast<ULONG_PTR>(api_set_map) + entry->ValueOffset + x * sizeof(API_SET_VALUE_ENTRY));
|
||||
const auto value = reinterpret_cast<PAPI_SET_VALUE_ENTRY>(reinterpret_cast<ULONG_PTR>(api_set_map) + entry->ValueOffset +
|
||||
x * sizeof(API_SET_VALUE_ENTRY));
|
||||
// printf(" Value %d\n", x);
|
||||
// printf(" Flags: %08X\n", value->Flags);
|
||||
// printf(" NameOffset: %08X\n", value->NameOffset);
|
||||
@@ -81,12 +81,10 @@ void print_apiset(PAPI_SET_NAMESPACE api_set_map)
|
||||
// printf(" ValueOffset: %08X\n", value->ValueOffset);
|
||||
// printf(" ValueLength: %08X\n", value->ValueLength);
|
||||
|
||||
std::wstring hostName(
|
||||
reinterpret_cast<wchar_t*>(reinterpret_cast<ULONG_PTR>(api_set_map) + value->NameOffset),
|
||||
value->NameLength / sizeof(wchar_t));
|
||||
std::wstring altName(
|
||||
reinterpret_cast<wchar_t*>(reinterpret_cast<ULONG_PTR>(api_set_map) + value->ValueOffset),
|
||||
value->ValueLength / sizeof(wchar_t));
|
||||
std::wstring hostName(reinterpret_cast<wchar_t*>(reinterpret_cast<ULONG_PTR>(api_set_map) + value->NameOffset),
|
||||
value->NameLength / sizeof(wchar_t));
|
||||
std::wstring altName(reinterpret_cast<wchar_t*>(reinterpret_cast<ULONG_PTR>(api_set_map) + value->ValueOffset),
|
||||
value->ValueLength / sizeof(wchar_t));
|
||||
printf(" HostName: %ls - AltName: %ls\n", hostName.empty() ? L"<none>" : hostName.data(),
|
||||
altName.empty() ? L"<none>" : altName.data());
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
endif()
|
||||
|
||||
add_test(NAME windows-emulator-test
|
||||
COMMAND "${PYTHON3_EXE}" "${PROJECT_SOURCE_DIR}/deps/gtest-parallel/gtest_parallel.py" ./windows-emulator-test
|
||||
COMMAND ${CMAKE_COMMAND} -E env "LD_LIBRARY_PATH=." -- "${PYTHON3_EXE}" "${PROJECT_SOURCE_DIR}/deps/gtest-parallel/gtest_parallel.py" ./windows-emulator-test
|
||||
WORKING_DIRECTORY "$<TARGET_FILE_DIR:windows-emulator-test>")
|
||||
|
||||
momo_targets_set_folder("tests" windows-emulator-test)
|
||||
|
||||
@@ -190,7 +190,6 @@ namespace test
|
||||
|
||||
const auto rip = emu.emu().read_instruction_pointer();
|
||||
|
||||
printf("Diff detected after 0x%" PRIx64 " instructions at 0x%" PRIx64 " (%s)\n", lower_bound, rip,
|
||||
emu.mod_manager.find_name(rip));
|
||||
printf("Diff detected after 0x%" PRIx64 " instructions at 0x%" PRIx64 " (%s)\n", lower_bound, rip, emu.mod_manager.find_name(rip));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace apiset
|
||||
{
|
||||
namespace
|
||||
{
|
||||
uint64_t copy_string(x86_64_emulator& emu, emulator_allocator& allocator, const void* base_ptr,
|
||||
const uint64_t offset, const size_t length)
|
||||
uint64_t copy_string(x86_64_emulator& emu, emulator_allocator& allocator, const void* base_ptr, const uint64_t offset,
|
||||
const size_t length)
|
||||
{
|
||||
if (!length)
|
||||
{
|
||||
@@ -29,8 +29,8 @@ namespace apiset
|
||||
return str_obj;
|
||||
}
|
||||
|
||||
ULONG copy_string_as_relative(x86_64_emulator& emu, emulator_allocator& allocator, const uint64_t result_base,
|
||||
const void* base_ptr, const uint64_t offset, const size_t length)
|
||||
ULONG copy_string_as_relative(x86_64_emulator& emu, emulator_allocator& allocator, const uint64_t result_base, const void* base_ptr,
|
||||
const uint64_t offset, const size_t length)
|
||||
{
|
||||
const auto address = copy_string(emu, allocator, base_ptr, offset, length);
|
||||
if (!address)
|
||||
@@ -115,14 +115,12 @@ namespace apiset
|
||||
return obtain(apiset_loc, root);
|
||||
}
|
||||
|
||||
emulator_object<API_SET_NAMESPACE> clone(x86_64_emulator& emu, emulator_allocator& allocator,
|
||||
const container& container)
|
||||
emulator_object<API_SET_NAMESPACE> clone(x86_64_emulator& emu, emulator_allocator& allocator, const container& container)
|
||||
{
|
||||
return clone(emu, allocator, container.get());
|
||||
}
|
||||
|
||||
emulator_object<API_SET_NAMESPACE> clone(x86_64_emulator& emu, emulator_allocator& allocator,
|
||||
const API_SET_NAMESPACE& orig_api_set_map)
|
||||
emulator_object<API_SET_NAMESPACE> clone(x86_64_emulator& emu, emulator_allocator& allocator, const API_SET_NAMESPACE& orig_api_set_map)
|
||||
{
|
||||
const auto api_set_map_obj = allocator.reserve<API_SET_NAMESPACE>();
|
||||
const auto ns_entries_obj = allocator.reserve<API_SET_NAMESPACE_ENTRY>(orig_api_set_map.Count);
|
||||
@@ -134,18 +132,16 @@ namespace apiset
|
||||
api_set.HashOffset = static_cast<ULONG>(hash_entries_obj.value() - api_set_map_obj.value());
|
||||
});
|
||||
|
||||
const auto* orig_ns_entries =
|
||||
offset_pointer<API_SET_NAMESPACE_ENTRY>(&orig_api_set_map, orig_api_set_map.EntryOffset);
|
||||
const auto* orig_hash_entries =
|
||||
offset_pointer<API_SET_HASH_ENTRY>(&orig_api_set_map, orig_api_set_map.HashOffset);
|
||||
const auto* orig_ns_entries = offset_pointer<API_SET_NAMESPACE_ENTRY>(&orig_api_set_map, orig_api_set_map.EntryOffset);
|
||||
const auto* orig_hash_entries = offset_pointer<API_SET_HASH_ENTRY>(&orig_api_set_map, orig_api_set_map.HashOffset);
|
||||
|
||||
for (ULONG i = 0; i < orig_api_set_map.Count; ++i)
|
||||
{
|
||||
auto ns_entry = orig_ns_entries[i];
|
||||
const auto hash_entry = orig_hash_entries[i];
|
||||
|
||||
ns_entry.NameOffset = copy_string_as_relative(emu, allocator, api_set_map_obj.value(), &orig_api_set_map,
|
||||
ns_entry.NameOffset, ns_entry.NameLength);
|
||||
ns_entry.NameOffset = copy_string_as_relative(emu, allocator, api_set_map_obj.value(), &orig_api_set_map, ns_entry.NameOffset,
|
||||
ns_entry.NameLength);
|
||||
|
||||
if (!ns_entry.ValueCount)
|
||||
{
|
||||
@@ -161,13 +157,13 @@ namespace apiset
|
||||
{
|
||||
auto value = orig_values[j];
|
||||
|
||||
value.ValueOffset = copy_string_as_relative(emu, allocator, api_set_map_obj.value(), &orig_api_set_map,
|
||||
value.ValueOffset, value.ValueLength);
|
||||
value.ValueOffset = copy_string_as_relative(emu, allocator, api_set_map_obj.value(), &orig_api_set_map, value.ValueOffset,
|
||||
value.ValueLength);
|
||||
|
||||
if (value.NameLength)
|
||||
{
|
||||
value.NameOffset = copy_string_as_relative(emu, allocator, api_set_map_obj.value(),
|
||||
&orig_api_set_map, value.NameOffset, value.NameLength);
|
||||
value.NameOffset = copy_string_as_relative(emu, allocator, api_set_map_obj.value(), &orig_api_set_map, value.NameOffset,
|
||||
value.NameLength);
|
||||
}
|
||||
|
||||
values_obj.write(value, j);
|
||||
|
||||
@@ -32,6 +32,5 @@ namespace apiset
|
||||
emulator_object<API_SET_NAMESPACE> clone(x86_64_emulator& emu, emulator_allocator& allocator,
|
||||
const API_SET_NAMESPACE& orig_api_set_map);
|
||||
|
||||
emulator_object<API_SET_NAMESPACE> clone(x86_64_emulator& emu, emulator_allocator& allocator,
|
||||
const container& container);
|
||||
emulator_object<API_SET_NAMESPACE> clone(x86_64_emulator& emu, emulator_allocator& allocator, const container& container);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -221,8 +221,7 @@ namespace
|
||||
return win_emu.emu().read_memory<afd_creation_data>(data.buffer);
|
||||
}
|
||||
|
||||
std::pair<AFD_POLL_INFO64, std::vector<AFD_POLL_HANDLE_INFO64>> get_poll_info(windows_emulator& win_emu,
|
||||
const io_device_context& c)
|
||||
std::pair<AFD_POLL_INFO64, std::vector<AFD_POLL_HANDLE_INFO64>> get_poll_info(windows_emulator& win_emu, const io_device_context& c)
|
||||
{
|
||||
constexpr auto info_size = offsetof(AFD_POLL_INFO64, Handles);
|
||||
if (!c.input_buffer || c.input_buffer_length < info_size || c.input_buffer != c.output_buffer)
|
||||
@@ -272,8 +271,8 @@ namespace
|
||||
return socket_events;
|
||||
}
|
||||
|
||||
ULONG map_socket_response_events_to_afd(const int16_t socket_events, const ULONG afd_poll_events,
|
||||
const bool is_listening, const bool is_connecting)
|
||||
ULONG map_socket_response_events_to_afd(const int16_t socket_events, const ULONG afd_poll_events, const bool is_listening,
|
||||
const bool is_connecting)
|
||||
{
|
||||
ULONG afd_events = 0;
|
||||
|
||||
@@ -306,8 +305,7 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
if ((socket_events & (POLLHUP | POLLERR)) == (POLLHUP | POLLERR) &&
|
||||
afd_poll_events & (AFD_POLL_CONNECT_FAIL | AFD_POLL_ABORT))
|
||||
if ((socket_events & (POLLHUP | POLLERR)) == (POLLHUP | POLLERR) && afd_poll_events & (AFD_POLL_CONNECT_FAIL | AFD_POLL_ABORT))
|
||||
{
|
||||
afd_events |= (AFD_POLL_CONNECT_FAIL | AFD_POLL_ABORT);
|
||||
}
|
||||
@@ -388,8 +386,7 @@ namespace
|
||||
|
||||
void delay_ioctrl(const io_device_context& c, const std::optional<bool> require_poll = {},
|
||||
const std::optional<std::chrono::steady_clock::time_point> timeout = {},
|
||||
const std::optional<std::function<void(windows_emulator&, const io_device_context&)>>&
|
||||
timeout_callback = {})
|
||||
const std::optional<std::function<void(windows_emulator&, const io_device_context&)>>& timeout_callback = {})
|
||||
{
|
||||
if (this->executing_delayed_ioctl_)
|
||||
{
|
||||
@@ -426,8 +423,7 @@ namespace
|
||||
}
|
||||
if (this->event_select_mask_)
|
||||
{
|
||||
pfd.events =
|
||||
static_cast<int16_t>(pfd.events | map_afd_request_events_to_socket(this->event_select_mask_));
|
||||
pfd.events = static_cast<int16_t>(pfd.events | map_afd_request_events_to_socket(this->event_select_mask_));
|
||||
}
|
||||
pfd.revents = pfd.events;
|
||||
|
||||
@@ -440,10 +436,9 @@ namespace
|
||||
|
||||
if (socket_events && this->event_select_mask_)
|
||||
{
|
||||
const bool is_connecting =
|
||||
this->delayed_ioctl_ && _AFD_REQUEST(this->delayed_ioctl_->io_control_code) == AFD_CONNECT;
|
||||
ULONG current_events = map_socket_response_events_to_afd(socket_events, this->event_select_mask_,
|
||||
pfd.s->is_listening(), is_connecting);
|
||||
const bool is_connecting = this->delayed_ioctl_ && _AFD_REQUEST(this->delayed_ioctl_->io_control_code) == AFD_CONNECT;
|
||||
ULONG current_events =
|
||||
map_socket_response_events_to_afd(socket_events, this->event_select_mask_, pfd.s->is_listening(), is_connecting);
|
||||
|
||||
if ((current_events & ~this->triggered_events_) != 0)
|
||||
{
|
||||
@@ -463,8 +458,7 @@ namespace
|
||||
|
||||
if (this->require_poll_.has_value())
|
||||
{
|
||||
const auto is_ready =
|
||||
socket_events & ((*this->require_poll_ ? POLLIN : POLLOUT) | POLLHUP | POLLERR);
|
||||
const auto is_ready = socket_events & ((*this->require_poll_ ? POLLIN : POLLOUT) | POLLHUP | POLLERR);
|
||||
if (!is_ready)
|
||||
{
|
||||
return;
|
||||
@@ -955,8 +949,7 @@ namespace
|
||||
endpoint->delayed_ioctl_ && _AFD_REQUEST(endpoint->delayed_ioctl_->io_control_code) == AFD_CONNECT;
|
||||
|
||||
auto entry = handle_info_obj.read(i);
|
||||
entry.PollEvents = map_socket_response_events_to_afd(pfd.revents, handle.PollEvents,
|
||||
pfd.s->is_listening(), is_connecting);
|
||||
entry.PollEvents = map_socket_response_events_to_afd(pfd.revents, handle.PollEvents, pfd.s->is_listening(), is_connecting);
|
||||
entry.Status = STATUS_SUCCESS;
|
||||
|
||||
handle_info_obj.write(entry, current_index++);
|
||||
@@ -1102,8 +1095,8 @@ namespace
|
||||
const auto send_info = emu.read_memory<AFD_SEND_DATAGRAM_INFO<EmulatorTraits<Emu64>>>(c.input_buffer);
|
||||
const auto buffer = emu.read_memory<EMU_WSABUF<EmulatorTraits<Emu64>>>(send_info.BufferArray);
|
||||
|
||||
auto address_buffer = emu.read_memory(send_info.TdiConnInfo.RemoteAddress,
|
||||
static_cast<size_t>(send_info.TdiConnInfo.RemoteAddressLength));
|
||||
auto address_buffer =
|
||||
emu.read_memory(send_info.TdiConnInfo.RemoteAddress, static_cast<size_t>(send_info.TdiConnInfo.RemoteAddressLength));
|
||||
|
||||
const auto target = convert_to_host_address(win_emu, address_buffer);
|
||||
const auto data = emu.read_memory(buffer.buf, buffer.len);
|
||||
|
||||
@@ -76,8 +76,7 @@ namespace
|
||||
MOUNTMGR_MOUNT_POINT point{};
|
||||
const auto symlink = write_string<char16_t>(buffer, make_volume(drive, 0));
|
||||
const auto id = write_string<char>(buffer, make_drive_id(drive, 0));
|
||||
const auto name = write_string<char16_t>(buffer, u"\\Device\\HarddiskVolume" +
|
||||
u8_to_u16(std::to_string(drive - 'a' + 1)));
|
||||
const auto name = write_string<char16_t>(buffer, u"\\Device\\HarddiskVolume" + u8_to_u16(std::to_string(drive - 'a' + 1)));
|
||||
|
||||
point.SymbolicLinkNameOffset = symlink.first;
|
||||
point.SymbolicLinkNameLength = symlink.second;
|
||||
|
||||
@@ -2,45 +2,33 @@
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
#define MOUNTMGRCONTROLTYPE 0x0000006D // 'm'
|
||||
#define MOUNTDEVCONTROLTYPE 0x0000004D // 'M'
|
||||
#define MOUNTMGRCONTROLTYPE 0x0000006D // 'm'
|
||||
#define MOUNTDEVCONTROLTYPE 0x0000004D // 'M'
|
||||
|
||||
#define IOCTL_MOUNTMGR_CREATE_POINT \
|
||||
CTL_CODE(MOUNTMGRCONTROLTYPE, 0, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_DELETE_POINTS \
|
||||
CTL_CODE(MOUNTMGRCONTROLTYPE, 1, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_QUERY_POINTS CTL_CODE(MOUNTMGRCONTROLTYPE, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY \
|
||||
CTL_CODE(MOUNTMGRCONTROLTYPE, 3, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER \
|
||||
CTL_CODE(MOUNTMGRCONTROLTYPE, 4, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS \
|
||||
CTL_CODE(MOUNTMGRCONTROLTYPE, 5, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED \
|
||||
CTL_CODE(MOUNTMGRCONTROLTYPE, 6, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED \
|
||||
CTL_CODE(MOUNTMGRCONTROLTYPE, 7, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_CHANGE_NOTIFY CTL_CODE(MOUNTMGRCONTROLTYPE, 8, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_KEEP_LINKS_WHEN_OFFLINE \
|
||||
CTL_CODE(MOUNTMGRCONTROLTYPE, 9, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_CREATE_POINT CTL_CODE(MOUNTMGRCONTROLTYPE, 0, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_DELETE_POINTS CTL_CODE(MOUNTMGRCONTROLTYPE, 1, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_QUERY_POINTS CTL_CODE(MOUNTMGRCONTROLTYPE, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY CTL_CODE(MOUNTMGRCONTROLTYPE, 3, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER CTL_CODE(MOUNTMGRCONTROLTYPE, 4, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS CTL_CODE(MOUNTMGRCONTROLTYPE, 5, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED CTL_CODE(MOUNTMGRCONTROLTYPE, 6, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED CTL_CODE(MOUNTMGRCONTROLTYPE, 7, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_CHANGE_NOTIFY CTL_CODE(MOUNTMGRCONTROLTYPE, 8, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_KEEP_LINKS_WHEN_OFFLINE CTL_CODE(MOUNTMGRCONTROLTYPE, 9, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_CHECK_UNPROCESSED_VOLUMES CTL_CODE(MOUNTMGRCONTROLTYPE, 10, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION CTL_CODE(MOUNTMGRCONTROLTYPE, 11, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH CTL_CODE(MOUNTMGRCONTROLTYPE, 12, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS CTL_CODE(MOUNTMGRCONTROLTYPE, 13, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_SCRUB_REGISTRY \
|
||||
CTL_CODE(MOUNTMGRCONTROLTYPE, 14, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT CTL_CODE(MOUNTMGRCONTROLTYPE, 15, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_SET_AUTO_MOUNT \
|
||||
CTL_CODE(MOUNTMGRCONTROLTYPE, 16, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_SCRUB_REGISTRY CTL_CODE(MOUNTMGRCONTROLTYPE, 14, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT CTL_CODE(MOUNTMGRCONTROLTYPE, 15, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_SET_AUTO_MOUNT CTL_CODE(MOUNTMGRCONTROLTYPE, 16, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_BOOT_DL_ASSIGNMENT \
|
||||
CTL_CODE(MOUNTMGRCONTROLTYPE, 17, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) // since WIN7
|
||||
#define IOCTL_MOUNTMGR_TRACELOG_CACHE CTL_CODE(MOUNTMGRCONTROLTYPE, 18, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_PREPARE_VOLUME_DELETE \
|
||||
CTL_CODE(MOUNTMGRCONTROLTYPE, 19, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_TRACELOG_CACHE CTL_CODE(MOUNTMGRCONTROLTYPE, 18, METHOD_BUFFERED, FILE_READ_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_PREPARE_VOLUME_DELETE CTL_CODE(MOUNTMGRCONTROLTYPE, 19, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
#define IOCTL_MOUNTMGR_CANCEL_VOLUME_DELETE \
|
||||
CTL_CODE(MOUNTMGRCONTROLTYPE, 20, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) // since WIN8
|
||||
#define IOCTL_MOUNTMGR_SILO_ARRIVAL \
|
||||
CTL_CODE(MOUNTMGRCONTROLTYPE, 21, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) // since RS1
|
||||
#define IOCTL_MOUNTMGR_SILO_ARRIVAL CTL_CODE(MOUNTMGRCONTROLTYPE, 21, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) // since RS1
|
||||
|
||||
#define IOCTL_MOUNTDEV_QUERY_DEVICE_NAME CTL_CODE(MOUNTDEVCONTROLTYPE, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
|
||||
35
src/windows-emulator/devices/named_pipe.hpp
Normal file
35
src/windows-emulator/devices/named_pipe.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include "../io_device.hpp"
|
||||
|
||||
class named_pipe : public io_device_container
|
||||
{
|
||||
public:
|
||||
std::u16string name;
|
||||
std::deque<std::string> write_queue;
|
||||
ACCESS_MASK access = 0;
|
||||
ULONG pipe_type;
|
||||
ULONG read_mode;
|
||||
ULONG completion_mode;
|
||||
ULONG max_instances;
|
||||
ULONG inbound_quota;
|
||||
ULONG outbound_quota;
|
||||
LARGE_INTEGER default_timeout;
|
||||
|
||||
void create(windows_emulator&, const io_device_creation_data&) override
|
||||
{
|
||||
}
|
||||
void work(windows_emulator&) override
|
||||
{
|
||||
}
|
||||
NTSTATUS io_control(windows_emulator&, const io_device_context&) override
|
||||
{
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
void serialize_object(utils::buffer_serializer&) const override
|
||||
{
|
||||
}
|
||||
void deserialize_object(utils::buffer_deserializer&) override
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -10,19 +10,17 @@ namespace
|
||||
// RNG Microsoft Primitive Provider
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
std::uint8_t output_data[216] = //
|
||||
{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x50, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x98, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x52, 0x00, 0x4E, 0x00, 0x47,
|
||||
0x00, 0x00, 0x00, 0x4D, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x6F, 0x00,
|
||||
0x66, 0x00, 0x74, 0x00, 0x20, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6D, 0x00, 0x69, 0x00, 0x74,
|
||||
0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x76, 0x00,
|
||||
0x69, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00,
|
||||
0x63, 0x00, 0x72, 0x00, 0x79, 0x00, 0x70, 0x00, 0x74, 0x00, 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6D,
|
||||
0x00, 0x69, 0x00, 0x74, 0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2E, 0x00, 0x64, 0x00,
|
||||
0x6C, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0x52, 0x00, 0x4E, 0x00, 0x47, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x73, 0x00,
|
||||
0x6F, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6D, 0x00, 0x69, 0x00, 0x74, 0x00,
|
||||
0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x76, 0x00, 0x69, 0x00, 0x64, 0x00,
|
||||
0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x63, 0x00, 0x72, 0x00, 0x79, 0x00, 0x70, 0x00, 0x74, 0x00,
|
||||
0x70, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6D, 0x00, 0x69, 0x00, 0x74, 0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x73, 0x00,
|
||||
0x2E, 0x00, 0x64, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& c) override
|
||||
{
|
||||
|
||||
@@ -10,8 +10,7 @@ namespace
|
||||
emulator_object<T> allocate_object_on_stack(x86_64_emulator& emu)
|
||||
{
|
||||
const auto old_sp = emu.reg(x86_register::rsp);
|
||||
const auto new_sp =
|
||||
align_down(old_sp - sizeof(T), std::max(alignof(T), alignof(x86_64_emulator::pointer_type)));
|
||||
const auto new_sp = align_down(old_sp - sizeof(T), std::max(alignof(T), alignof(x86_64_emulator::pointer_type)));
|
||||
emu.reg(x86_register::rsp, new_sp);
|
||||
return {emu, new_sp};
|
||||
}
|
||||
@@ -88,8 +87,7 @@ namespace
|
||||
}
|
||||
|
||||
emulator_thread::emulator_thread(memory_manager& memory, const process_context& context, const uint64_t start_address,
|
||||
const uint64_t argument, const uint64_t stack_size, const bool suspended,
|
||||
const uint32_t id)
|
||||
const uint64_t argument, const uint64_t stack_size, const bool suspended, const uint32_t id)
|
||||
: memory_ptr(&memory),
|
||||
stack_size(page_align_up(std::max(stack_size, static_cast<uint64_t>(STACK_SIZE)))),
|
||||
start_address(start_address),
|
||||
@@ -119,6 +117,7 @@ emulator_thread::emulator_thread(memory_manager& memory, const process_context&
|
||||
teb_obj.NtTib.StackLimit = this->stack_base;
|
||||
teb_obj.NtTib.StackBase = this->stack_base + this->stack_size;
|
||||
teb_obj.NtTib.Self = this->teb->value();
|
||||
teb_obj.CurrentLocale = 0x409;
|
||||
teb_obj.ProcessEnvironmentBlock = context.peb.value();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -48,8 +48,8 @@ class emulator_thread : public ref_counted_object
|
||||
{
|
||||
}
|
||||
|
||||
emulator_thread(memory_manager& memory, const process_context& context, uint64_t start_address, uint64_t argument,
|
||||
uint64_t stack_size, bool suspended, uint32_t id);
|
||||
emulator_thread(memory_manager& memory, const process_context& context, uint64_t start_address, uint64_t argument, uint64_t stack_size,
|
||||
bool suspended, uint32_t id);
|
||||
|
||||
emulator_thread(const emulator_thread&) = delete;
|
||||
emulator_thread& operator=(const emulator_thread&) = delete;
|
||||
@@ -74,6 +74,9 @@ class emulator_thread : public ref_counted_object
|
||||
|
||||
uint32_t id{};
|
||||
|
||||
uint64_t current_ip{0};
|
||||
uint64_t previous_ip{0};
|
||||
|
||||
std::u16string name{};
|
||||
|
||||
std::optional<NTSTATUS> exit_status{};
|
||||
@@ -144,6 +147,8 @@ class emulator_thread : public ref_counted_object
|
||||
buffer.write(this->argument);
|
||||
buffer.write(this->executed_instructions);
|
||||
buffer.write(this->id);
|
||||
buffer.write(this->current_ip);
|
||||
buffer.write(this->previous_ip);
|
||||
|
||||
buffer.write_string(this->name);
|
||||
|
||||
@@ -182,6 +187,8 @@ class emulator_thread : public ref_counted_object
|
||||
buffer.read(this->argument);
|
||||
buffer.read(this->executed_instructions);
|
||||
buffer.read(this->id);
|
||||
buffer.read(this->current_ip);
|
||||
buffer.read(this->previous_ip);
|
||||
|
||||
buffer.read_string(this->name);
|
||||
|
||||
|
||||
@@ -251,8 +251,8 @@ class emulator_allocator
|
||||
result.MaximumLength = static_cast<USHORT>(max_length);
|
||||
}
|
||||
|
||||
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> make_unicode_string(
|
||||
const std::u16string_view str, const std::optional<size_t> maximum_length = std::nullopt)
|
||||
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> make_unicode_string(const std::u16string_view str,
|
||||
const std::optional<size_t> maximum_length = std::nullopt)
|
||||
{
|
||||
const auto unicode_string = this->reserve<UNICODE_STRING<EmulatorTraits<Emu64>>>();
|
||||
|
||||
@@ -316,8 +316,7 @@ class emulator_allocator
|
||||
};
|
||||
|
||||
template <typename Element>
|
||||
std::basic_string<Element> read_string(memory_interface& mem, const uint64_t address,
|
||||
const std::optional<size_t> size = {})
|
||||
std::basic_string<Element> read_string(memory_interface& mem, const uint64_t address, const std::optional<size_t> size = {})
|
||||
{
|
||||
std::basic_string<Element> result{};
|
||||
|
||||
@@ -357,8 +356,7 @@ inline std::u16string read_unicode_string(const emulator& emu, const UNICODE_STR
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::u16string read_unicode_string(const emulator& emu,
|
||||
const emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> uc_string)
|
||||
inline std::u16string read_unicode_string(const emulator& emu, const emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> uc_string)
|
||||
{
|
||||
const auto ucs = uc_string.read();
|
||||
return read_unicode_string(emu, ucs);
|
||||
@@ -369,7 +367,7 @@ inline std::u16string read_unicode_string(emulator& emu, const uint64_t uc_strin
|
||||
return read_unicode_string(emu, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>>{emu, uc_string});
|
||||
}
|
||||
|
||||
inline uint64_t get_function_argument(x86_64_emulator& emu, const size_t index, bool is_syscall = false)
|
||||
inline uint64_t get_function_argument(x86_64_emulator& emu, const size_t index, const bool is_syscall = false)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
|
||||
@@ -8,8 +8,7 @@ namespace
|
||||
using exception_record = EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>;
|
||||
using exception_record_map = std::unordered_map<const exception_record*, emulator_object<exception_record>>;
|
||||
|
||||
emulator_object<exception_record> save_exception_record(emulator_allocator& allocator,
|
||||
const exception_record& record,
|
||||
emulator_object<exception_record> save_exception_record(emulator_allocator& allocator, const exception_record& record,
|
||||
exception_record_map& record_mapping)
|
||||
{
|
||||
const auto record_obj = allocator.reserve<exception_record>();
|
||||
@@ -28,8 +27,8 @@ namespace
|
||||
}
|
||||
else
|
||||
{
|
||||
nested_record_obj = save_exception_record(
|
||||
allocator, *reinterpret_cast<exception_record*>(record.ExceptionRecord), record_mapping);
|
||||
nested_record_obj =
|
||||
save_exception_record(allocator, *reinterpret_cast<exception_record*>(record.ExceptionRecord), record_mapping);
|
||||
}
|
||||
|
||||
record_obj.access([&](exception_record& r) {
|
||||
@@ -40,8 +39,7 @@ namespace
|
||||
return record_obj;
|
||||
}
|
||||
|
||||
emulator_object<exception_record> save_exception_record(emulator_allocator& allocator,
|
||||
const exception_record& record)
|
||||
emulator_object<exception_record> save_exception_record(emulator_allocator& allocator, const exception_record& record)
|
||||
{
|
||||
exception_record_map record_mapping{};
|
||||
return save_exception_record(allocator, record, record_mapping);
|
||||
@@ -94,8 +92,7 @@ namespace
|
||||
{
|
||||
constexpr auto mach_frame_size = 0x40;
|
||||
constexpr auto context_record_size = 0x4F0;
|
||||
const auto exception_record_size =
|
||||
calculate_exception_record_size(*reinterpret_cast<exception_record*>(pointers.ExceptionRecord));
|
||||
const auto exception_record_size = calculate_exception_record_size(*reinterpret_cast<exception_record*>(pointers.ExceptionRecord));
|
||||
const auto combined_size = align_up(exception_record_size + context_record_size, 0x10);
|
||||
|
||||
assert(combined_size == 0x590);
|
||||
@@ -120,8 +117,7 @@ namespace
|
||||
context_record_obj.write(*reinterpret_cast<CONTEXT64*>(pointers.ContextRecord));
|
||||
|
||||
emulator_allocator allocator{emu, new_sp + context_record_size, exception_record_size};
|
||||
const auto exception_record_obj =
|
||||
save_exception_record(allocator, *reinterpret_cast<exception_record*>(pointers.ExceptionRecord));
|
||||
const auto exception_record_obj = save_exception_record(allocator, *reinterpret_cast<exception_record*>(pointers.ExceptionRecord));
|
||||
|
||||
if (exception_record_obj.value() != allocator.get_base())
|
||||
{
|
||||
@@ -172,8 +168,7 @@ void dispatch_exception(x86_64_emulator& emu, const process_context& proc, const
|
||||
dispatch_exception_pointers(emu, proc.ki_user_exception_dispatcher, pointers);
|
||||
}
|
||||
|
||||
void dispatch_access_violation(x86_64_emulator& emu, const process_context& proc, const uint64_t address,
|
||||
const memory_operation operation)
|
||||
void dispatch_access_violation(x86_64_emulator& emu, const process_context& proc, const uint64_t address, const memory_operation operation)
|
||||
{
|
||||
dispatch_exception(emu, proc, STATUS_ACCESS_VIOLATION,
|
||||
{
|
||||
@@ -182,6 +177,16 @@ void dispatch_access_violation(x86_64_emulator& emu, const process_context& proc
|
||||
});
|
||||
}
|
||||
|
||||
void dispatch_guard_page_violation(x86_64_emulator& emu, const process_context& proc, const uint64_t address,
|
||||
const memory_operation operation)
|
||||
{
|
||||
dispatch_exception(emu, proc, STATUS_GUARD_PAGE_VIOLATION,
|
||||
{
|
||||
map_violation_operation_to_parameter(operation),
|
||||
address,
|
||||
});
|
||||
}
|
||||
|
||||
void dispatch_illegal_instruction_violation(x86_64_emulator& emu, const process_context& proc)
|
||||
{
|
||||
dispatch_exception(emu, proc, STATUS_ILLEGAL_INSTRUCTION, {});
|
||||
|
||||
@@ -17,8 +17,8 @@ void dispatch_exception(x86_64_emulator& emu, const process_context& proc, const
|
||||
dispatch_exception(emu, proc, static_cast<DWORD>(status), parameters);
|
||||
}
|
||||
|
||||
void dispatch_access_violation(x86_64_emulator& emu, const process_context& proc, uint64_t address,
|
||||
memory_operation operation);
|
||||
void dispatch_access_violation(x86_64_emulator& emu, const process_context& proc, uint64_t address, memory_operation operation);
|
||||
void dispatch_guard_page_violation(x86_64_emulator& emu, const process_context& proc, uint64_t address, memory_operation operation);
|
||||
void dispatch_illegal_instruction_violation(x86_64_emulator& emu, const process_context& proc);
|
||||
void dispatch_integer_division_by_zero(x86_64_emulator& emu, const process_context& proc);
|
||||
void dispatch_single_step(x86_64_emulator& emu, const process_context& proc);
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "devices/afd_endpoint.hpp"
|
||||
#include "devices/mount_point_manager.hpp"
|
||||
#include "devices/security_support_provider.hpp"
|
||||
#include "devices/named_pipe.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -49,6 +51,11 @@ std::unique_ptr<io_device> create_device(const std::u16string_view device)
|
||||
return create_security_support_provider();
|
||||
}
|
||||
|
||||
if (device == u"NamedPipe")
|
||||
{
|
||||
return std::make_unique<named_pipe>();
|
||||
}
|
||||
|
||||
throw std::runtime_error("Unsupported device: " + u16_to_u8(device));
|
||||
}
|
||||
|
||||
|
||||
@@ -65,8 +65,8 @@ struct io_device_creation_data
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
inline NTSTATUS write_io_status(const emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block,
|
||||
const NTSTATUS status, const bool clear_struct = false)
|
||||
inline NTSTATUS write_io_status(const emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block, const NTSTATUS status,
|
||||
const bool clear_struct = false)
|
||||
{
|
||||
io_status_block.access([=](IO_STATUS_BLOCK<EmulatorTraits<Emu64>>& status_block) {
|
||||
if (clear_struct)
|
||||
|
||||
@@ -27,16 +27,16 @@ namespace
|
||||
kusd.TimeZoneBias.High2Time = -17;
|
||||
kusd.TimeZoneId = 0x00000002;
|
||||
kusd.LargePageMinimum = 0x00200000;
|
||||
kusd.RNGSeedVersion = 0x0000000000000013;
|
||||
kusd.RNGSeedVersion = 0;
|
||||
kusd.TimeZoneBiasStamp = 0x00000004;
|
||||
kusd.NtBuildNumber = 0x00006c51;
|
||||
kusd.NtBuildNumber = 19045;
|
||||
kusd.NtProductType = NtProductWinNt;
|
||||
kusd.ProductTypeIsValid = 0x01;
|
||||
kusd.NativeProcessorArchitecture = 0x0009;
|
||||
kusd.NtMajorVersion = 0x0000000a;
|
||||
kusd.BootId = 0x0000000b;
|
||||
kusd.SystemExpirationDate.QuadPart = 0x01dc26860a9ff300;
|
||||
kusd.SuiteMask = 0x00000110;
|
||||
kusd.BootId = 0;
|
||||
kusd.SystemExpirationDate.QuadPart = 0;
|
||||
kusd.SuiteMask = 0;
|
||||
kusd.MitigationPolicies.MitigationPolicies = 0x0a;
|
||||
kusd.MitigationPolicies.NXSupportPolicy = 0x02;
|
||||
kusd.MitigationPolicies.SEHValidationPolicy = 0x02;
|
||||
@@ -48,7 +48,7 @@ namespace
|
||||
kusd.FullNumberOfPhysicalPages = 0x0000000000bf0958;
|
||||
kusd.TickCount.TickCount.LowPart = 0x001f7f05;
|
||||
kusd.TickCount.TickCountQuad = 0x00000000001f7f05;
|
||||
kusd.Cookie = 0x1c3471da;
|
||||
kusd.Cookie = 0;
|
||||
kusd.ConsoleSessionForegroundProcessId = 0x00000000000028f4;
|
||||
kusd.TimeUpdateLock = 0x0000000002b28586;
|
||||
kusd.BaselineSystemTimeQpc = 0x0000004b17cd596c;
|
||||
@@ -61,7 +61,7 @@ namespace
|
||||
kusd.TelemetryCoverageRound = 0x00000001;
|
||||
kusd.LangGenerationCount = 0x00000003;
|
||||
kusd.InterruptTimeBias = 0x00000015a5d56406;
|
||||
kusd.ActiveProcessorCount = 0x0000000c;
|
||||
kusd.ActiveProcessorCount = 0x00000004;
|
||||
kusd.ActiveGroupCount = 0x01;
|
||||
kusd.TimeZoneBiasEffectiveStart.QuadPart = 0x01db276e654cb2ff;
|
||||
kusd.TimeZoneBiasEffectiveEnd.QuadPart = 0x01db280b8c3b2800;
|
||||
|
||||
@@ -74,29 +74,47 @@ namespace
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
|
||||
std::string_view format(va_list* ap, const char* message)
|
||||
int format_internal(const char* message, std::span<char> buffer, va_list* ap)
|
||||
{
|
||||
return vsnprintf(buffer.data(), buffer.size(), message, *ap);
|
||||
}
|
||||
|
||||
std::string_view format(const char* message, std::string& reserve_buffer, va_list* ap1, va_list* ap2)
|
||||
{
|
||||
thread_local std::array<char, 0x1000> buffer{};
|
||||
|
||||
#ifdef _WIN32
|
||||
const int count = _vsnprintf_s(buffer.data(), buffer.size(), buffer.size(), message, *ap);
|
||||
#else
|
||||
const int count = vsnprintf(buffer.data(), buffer.size(), message, *ap);
|
||||
#endif
|
||||
auto count = format_internal(message, buffer, ap1);
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return {buffer.data(), static_cast<size_t>(count)};
|
||||
if (static_cast<size_t>(count) < buffer.size())
|
||||
{
|
||||
return {buffer.data(), static_cast<size_t>(count)};
|
||||
}
|
||||
|
||||
reserve_buffer.resize(count + 1);
|
||||
count = format_internal(message, reserve_buffer, ap2);
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return {reserve_buffer.data(), std::min(static_cast<size_t>(count), reserve_buffer.size() - 1)};
|
||||
}
|
||||
|
||||
#define format_to_string(msg, str) \
|
||||
va_list ap; \
|
||||
va_start(ap, msg); \
|
||||
const auto str = format(&ap, msg); \
|
||||
va_end(ap)
|
||||
#define format_to_string(msg, str) \
|
||||
std::string buf{}; \
|
||||
va_list ap1; \
|
||||
va_list ap2; \
|
||||
va_start(ap1, msg); \
|
||||
va_start(ap2, msg); \
|
||||
const auto str = format(msg, buf, &ap1, &ap2); \
|
||||
va_end(ap2); \
|
||||
va_end(ap1)
|
||||
|
||||
void print_colored(const std::string_view& line, const color_type base_color)
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "memory_region.hpp"
|
||||
#include "address_utils.hpp"
|
||||
#include "memory_permission_ext.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
@@ -24,8 +25,7 @@ namespace
|
||||
|
||||
i->second.length = static_cast<size_t>(first_length);
|
||||
|
||||
regions[split_point] =
|
||||
memory_manager::committed_region{static_cast<size_t>(second_length), i->second.permissions};
|
||||
regions[split_point] = memory_manager::committed_region{static_cast<size_t>(second_length), i->second.permissions};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ namespace utils
|
||||
static void deserialize(buffer_deserializer& buffer, memory_manager::committed_region& region)
|
||||
{
|
||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||
region.permissions = buffer.read<memory_permission>();
|
||||
region.permissions = buffer.read<nt_memory_permission>();
|
||||
}
|
||||
|
||||
static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region)
|
||||
@@ -189,8 +189,8 @@ void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer
|
||||
}
|
||||
}
|
||||
|
||||
bool memory_manager::protect_memory(const uint64_t address, const size_t size, const memory_permission permissions,
|
||||
memory_permission* old_permissions)
|
||||
bool memory_manager::protect_memory(const uint64_t address, const size_t size, const nt_memory_permission permissions,
|
||||
nt_memory_permission* old_permissions)
|
||||
{
|
||||
const auto entry = this->find_reserved_region(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
@@ -243,8 +243,7 @@ bool memory_manager::protect_memory(const uint64_t address, const size_t size, c
|
||||
return true;
|
||||
}
|
||||
|
||||
bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
|
||||
mmio_write_callback write_cb)
|
||||
bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb)
|
||||
{
|
||||
if (this->overlaps_reserved_region(address, size))
|
||||
{
|
||||
@@ -268,7 +267,7 @@ bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mm
|
||||
return true;
|
||||
}
|
||||
|
||||
bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const memory_permission permissions,
|
||||
bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const nt_memory_permission permissions,
|
||||
const bool reserve_only)
|
||||
{
|
||||
if (this->overlaps_reserved_region(address, size))
|
||||
@@ -286,8 +285,8 @@ bool memory_manager::allocate_memory(const uint64_t address, const size_t size,
|
||||
|
||||
if (!reserve_only)
|
||||
{
|
||||
this->map_memory(address, size, permissions);
|
||||
entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
|
||||
this->map_memory(address, size, permissions.is_guarded() ? memory_permission::none : permissions.common);
|
||||
entry->second.committed_regions[address] = committed_region{size, permissions};
|
||||
}
|
||||
|
||||
this->update_layout_version();
|
||||
@@ -295,7 +294,7 @@ bool memory_manager::allocate_memory(const uint64_t address, const size_t size,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool memory_manager::commit_memory(const uint64_t address, const size_t size, const memory_permission permissions)
|
||||
bool memory_manager::commit_memory(const uint64_t address, const size_t size, const nt_memory_permission permissions)
|
||||
{
|
||||
const auto entry = this->find_reserved_region(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
@@ -473,8 +472,7 @@ void memory_manager::unmap_all_memory()
|
||||
this->reserved_regions_.clear();
|
||||
}
|
||||
|
||||
uint64_t memory_manager::allocate_memory(const size_t size, const memory_permission permissions,
|
||||
const bool reserve_only)
|
||||
uint64_t memory_manager::allocate_memory(const size_t size, const nt_memory_permission permissions, const bool reserve_only)
|
||||
{
|
||||
const auto allocation_base = this->find_free_allocation_base(size);
|
||||
if (!allocate_memory(allocation_base, size, permissions, reserve_only))
|
||||
@@ -519,8 +517,8 @@ region_info memory_manager::get_region_info(const uint64_t address)
|
||||
region_info result{};
|
||||
result.start = MIN_ALLOCATION_ADDRESS;
|
||||
result.length = static_cast<size_t>(MAX_ALLOCATION_ADDRESS - result.start);
|
||||
result.permissions = memory_permission::none;
|
||||
result.initial_permissions = memory_permission::none;
|
||||
result.permissions = nt_memory_permission();
|
||||
result.initial_permissions = nt_memory_permission();
|
||||
result.allocation_base = {};
|
||||
result.allocation_length = result.length;
|
||||
result.is_committed = false;
|
||||
@@ -637,8 +635,7 @@ void memory_manager::write_memory(const uint64_t address, const void* data, cons
|
||||
this->memory_->write_memory(address, data, size);
|
||||
}
|
||||
|
||||
void memory_manager::map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
|
||||
mmio_write_callback write_cb)
|
||||
void memory_manager::map_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb)
|
||||
{
|
||||
this->memory_->map_mmio(address, size, std::move(read_cb), std::move(write_cb));
|
||||
}
|
||||
@@ -653,8 +650,7 @@ void memory_manager::unmap_memory(const uint64_t address, const size_t size)
|
||||
this->memory_->unmap_memory(address, size);
|
||||
}
|
||||
|
||||
void memory_manager::apply_memory_protection(const uint64_t address, const size_t size,
|
||||
const memory_permission permissions)
|
||||
void memory_manager::apply_memory_protection(const uint64_t address, const size_t size, const memory_permission permissions)
|
||||
{
|
||||
this->memory_->apply_memory_protection(address, size, permissions);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
#include "memory_permission_ext.hpp"
|
||||
#include "memory_region.hpp"
|
||||
#include "serialization.hpp"
|
||||
|
||||
@@ -12,13 +13,15 @@ constexpr auto ALLOCATION_GRANULARITY = 0x0000000000010000ULL;
|
||||
constexpr auto MIN_ALLOCATION_ADDRESS = 0x0000000000010000ULL;
|
||||
constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL;
|
||||
|
||||
struct region_info : basic_memory_region
|
||||
// This maps to the `basic_memory_region` struct defined in
|
||||
// emulator\memory_region.hpp
|
||||
struct region_info : basic_memory_region<nt_memory_permission>
|
||||
{
|
||||
uint64_t allocation_base{};
|
||||
size_t allocation_length{};
|
||||
bool is_reserved{};
|
||||
bool is_committed{};
|
||||
memory_permission initial_permissions{};
|
||||
nt_memory_permission initial_permissions{};
|
||||
};
|
||||
|
||||
using mmio_read_callback = std::function<void(uint64_t addr, void* data, size_t size)>;
|
||||
@@ -41,7 +44,7 @@ class memory_manager : public memory_interface
|
||||
struct committed_region
|
||||
{
|
||||
size_t length{};
|
||||
memory_permission permissions{};
|
||||
nt_memory_permission permissions{};
|
||||
};
|
||||
|
||||
using committed_region_map = std::map<uint64_t, committed_region>;
|
||||
@@ -60,20 +63,19 @@ class memory_manager : public memory_interface
|
||||
bool try_read_memory(uint64_t address, void* data, size_t size) const final;
|
||||
void write_memory(uint64_t address, const void* data, size_t size) final;
|
||||
|
||||
bool protect_memory(uint64_t address, size_t size, memory_permission permissions,
|
||||
memory_permission* old_permissions = nullptr);
|
||||
bool protect_memory(uint64_t address, size_t size, nt_memory_permission permissions, nt_memory_permission* old_permissions = nullptr);
|
||||
|
||||
bool allocate_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb);
|
||||
bool allocate_memory(uint64_t address, size_t size, memory_permission permissions, bool reserve_only = false);
|
||||
bool allocate_memory(uint64_t address, size_t size, nt_memory_permission permissions, bool reserve_only = false);
|
||||
|
||||
bool commit_memory(uint64_t address, size_t size, memory_permission permissions);
|
||||
bool commit_memory(uint64_t address, size_t size, nt_memory_permission permissions);
|
||||
bool decommit_memory(uint64_t address, size_t size);
|
||||
|
||||
bool release_memory(uint64_t address, size_t size);
|
||||
|
||||
void unmap_all_memory();
|
||||
|
||||
uint64_t allocate_memory(size_t size, memory_permission permissions, bool reserve_only = false);
|
||||
uint64_t allocate_memory(size_t size, nt_memory_permission permissions, bool reserve_only = false);
|
||||
|
||||
uint64_t find_free_allocation_base(size_t size, uint64_t start = 0) const;
|
||||
|
||||
|
||||
180
src/windows-emulator/memory_permission_ext.hpp
Normal file
180
src/windows-emulator/memory_permission_ext.hpp
Normal file
@@ -0,0 +1,180 @@
|
||||
#pragma once
|
||||
#include "memory_permission.hpp"
|
||||
|
||||
enum class memory_permission_ext : uint8_t
|
||||
{
|
||||
none = 0,
|
||||
guard = 1 << 0,
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
constexpr memory_permission_ext operator&(const memory_permission_ext x, const memory_permission_ext y)
|
||||
{
|
||||
return static_cast<memory_permission_ext>(static_cast<uint8_t>(x) & static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
constexpr memory_permission_ext operator|(const memory_permission_ext x, const memory_permission_ext y)
|
||||
{
|
||||
return static_cast<memory_permission_ext>(static_cast<uint8_t>(x) | static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
constexpr memory_permission_ext operator^(const memory_permission_ext x, const memory_permission_ext y)
|
||||
{
|
||||
return static_cast<memory_permission_ext>(static_cast<uint8_t>(x) ^ static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
constexpr memory_permission_ext operator~(memory_permission_ext x)
|
||||
{
|
||||
return static_cast<memory_permission_ext>(~static_cast<uint8_t>(x));
|
||||
}
|
||||
|
||||
inline memory_permission_ext& operator&=(memory_permission_ext& x, const memory_permission_ext y)
|
||||
{
|
||||
x = x & y;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline memory_permission_ext& operator|=(memory_permission_ext& x, const memory_permission_ext y)
|
||||
{
|
||||
x = x | y;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline memory_permission_ext& operator^=(memory_permission_ext& x, const memory_permission_ext y)
|
||||
{
|
||||
x = x ^ y;
|
||||
return x;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
struct nt_memory_permission
|
||||
{
|
||||
memory_permission common;
|
||||
memory_permission_ext extended;
|
||||
|
||||
constexpr nt_memory_permission()
|
||||
: common(memory_permission::none),
|
||||
extended(memory_permission_ext::none)
|
||||
{
|
||||
}
|
||||
constexpr nt_memory_permission(memory_permission common)
|
||||
: common(common),
|
||||
extended(memory_permission_ext::none)
|
||||
{
|
||||
}
|
||||
constexpr nt_memory_permission(memory_permission common, memory_permission_ext ext)
|
||||
: common(common),
|
||||
extended(ext)
|
||||
{
|
||||
}
|
||||
|
||||
// Implicit coercions
|
||||
operator memory_permission() const
|
||||
{
|
||||
return common;
|
||||
}
|
||||
operator memory_permission_ext() const
|
||||
{
|
||||
return extended;
|
||||
}
|
||||
|
||||
// This just does memberwise equality on each of the members in declaration order
|
||||
bool operator==(nt_memory_permission const&) const = default;
|
||||
|
||||
nt_memory_permission& operator=(memory_permission const& y)
|
||||
{
|
||||
this->common = y;
|
||||
this->extended = memory_permission_ext::none;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr bool is_guarded() const
|
||||
{
|
||||
return (this->extended & memory_permission_ext::guard) == memory_permission_ext::guard;
|
||||
}
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
constexpr nt_memory_permission operator&(const nt_memory_permission x, const memory_permission y)
|
||||
{
|
||||
return nt_memory_permission{x.common & y, x.extended};
|
||||
}
|
||||
|
||||
constexpr nt_memory_permission operator&(const nt_memory_permission x, const memory_permission_ext y)
|
||||
{
|
||||
return nt_memory_permission{x.common, x.extended & y};
|
||||
}
|
||||
|
||||
constexpr nt_memory_permission operator|(const nt_memory_permission x, const memory_permission y)
|
||||
{
|
||||
return nt_memory_permission{x.common | y, x.extended};
|
||||
}
|
||||
|
||||
constexpr nt_memory_permission operator|(const nt_memory_permission x, const memory_permission_ext y)
|
||||
{
|
||||
return nt_memory_permission{x.common, x.extended | y};
|
||||
}
|
||||
|
||||
constexpr nt_memory_permission operator^(const nt_memory_permission x, const memory_permission y)
|
||||
{
|
||||
return nt_memory_permission{x.common ^ y, x.extended};
|
||||
}
|
||||
|
||||
constexpr nt_memory_permission operator^(const nt_memory_permission x, const memory_permission_ext y)
|
||||
{
|
||||
return nt_memory_permission{x.common, x.extended ^ y};
|
||||
}
|
||||
|
||||
inline nt_memory_permission& operator&=(nt_memory_permission& x, const memory_permission y)
|
||||
{
|
||||
x = x & y;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline nt_memory_permission& operator&=(nt_memory_permission& x, const memory_permission_ext y)
|
||||
{
|
||||
x = x & y;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline nt_memory_permission& operator|=(nt_memory_permission& x, const memory_permission y)
|
||||
{
|
||||
x.common | y;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline nt_memory_permission& operator|=(nt_memory_permission& x, const nt_memory_permission y)
|
||||
{
|
||||
x.extended | y;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline nt_memory_permission& operator^=(nt_memory_permission& x, const memory_permission y)
|
||||
{
|
||||
x.common ^ y;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline nt_memory_permission& operator^=(nt_memory_permission& x, const nt_memory_permission y)
|
||||
{
|
||||
x.extended ^ y;
|
||||
return x;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
inline bool is_guarded(const memory_permission_ext permission)
|
||||
{
|
||||
return (permission & memory_permission_ext::guard) != memory_permission_ext::none;
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <emulator.hpp>
|
||||
#include "memory_permission_ext.hpp"
|
||||
|
||||
inline std::string get_permission_string(const memory_permission permission)
|
||||
{
|
||||
@@ -19,28 +20,55 @@ inline std::string get_permission_string(const memory_permission permission)
|
||||
return res;
|
||||
}
|
||||
|
||||
inline memory_permission map_nt_to_emulator_protection(uint32_t nt_protection)
|
||||
inline std::optional<nt_memory_permission> try_map_nt_to_emulator_protection(uint32_t nt_protection)
|
||||
{
|
||||
nt_protection &= ~static_cast<uint32_t>(PAGE_GUARD); // TODO: Implement that
|
||||
memory_permission_ext ext = memory_permission_ext::none;
|
||||
// TODO: Check for invalid combinations
|
||||
if (nt_protection & PAGE_GUARD)
|
||||
{
|
||||
// Unset the guard flag so the following switch statement will still work
|
||||
nt_protection &= ~static_cast<uint32_t>(PAGE_GUARD);
|
||||
ext = memory_permission_ext::guard;
|
||||
}
|
||||
|
||||
memory_permission common = memory_permission::none;
|
||||
switch (nt_protection)
|
||||
{
|
||||
case PAGE_NOACCESS:
|
||||
return memory_permission::none;
|
||||
common = memory_permission::none;
|
||||
break;
|
||||
case PAGE_READONLY:
|
||||
return memory_permission::read;
|
||||
common = memory_permission::read;
|
||||
break;
|
||||
case PAGE_READWRITE:
|
||||
case PAGE_WRITECOPY:
|
||||
return memory_permission::read | memory_permission::write;
|
||||
common = memory_permission::read | memory_permission::write;
|
||||
break;
|
||||
case PAGE_EXECUTE:
|
||||
case PAGE_EXECUTE_READ:
|
||||
return memory_permission::read | memory_permission::exec;
|
||||
common = memory_permission::read | memory_permission::exec;
|
||||
break;
|
||||
case PAGE_EXECUTE_READWRITE:
|
||||
return memory_permission::all;
|
||||
common = memory_permission::all;
|
||||
break;
|
||||
case 0:
|
||||
case PAGE_EXECUTE_WRITECOPY:
|
||||
default:
|
||||
throw std::runtime_error("Failed to map protection");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return nt_memory_permission{common, ext};
|
||||
}
|
||||
|
||||
inline nt_memory_permission map_nt_to_emulator_protection(uint32_t nt_protection)
|
||||
{
|
||||
const auto protection = try_map_nt_to_emulator_protection(nt_protection);
|
||||
if (protection.has_value())
|
||||
{
|
||||
return *protection;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Failed to map protection: " + std::to_string(nt_protection));
|
||||
}
|
||||
|
||||
inline uint32_t map_emulator_to_nt_protection(const memory_permission permission)
|
||||
|
||||
@@ -51,8 +51,7 @@ namespace minidump_loader
|
||||
}
|
||||
|
||||
bool parse_minidump_file(windows_emulator& win_emu, const std::filesystem::path& minidump_path,
|
||||
std::unique_ptr<minidump::minidump_file>& dump_file,
|
||||
std::unique_ptr<minidump::minidump_reader>& dump_reader)
|
||||
std::unique_ptr<minidump::minidump_file>& dump_file, std::unique_ptr<minidump::minidump_reader>& dump_reader)
|
||||
{
|
||||
win_emu.log.info("Parsing minidump file\n");
|
||||
|
||||
@@ -160,10 +159,9 @@ namespace minidump_loader
|
||||
stats.total_memory_size += segment.size;
|
||||
}
|
||||
|
||||
win_emu.log.info(
|
||||
"Summary: %s, %zu threads, %zu modules, %zu regions, %zu segments, %zu handles, %" PRIu64 " bytes memory\n",
|
||||
get_architecture_string(dump_file).c_str(), stats.thread_count, stats.module_count,
|
||||
stats.memory_region_count, stats.memory_segment_count, stats.handle_count, stats.total_memory_size);
|
||||
win_emu.log.info("Summary: %s, %zu threads, %zu modules, %zu regions, %zu segments, %zu handles, %" PRIu64 " bytes memory\n",
|
||||
get_architecture_string(dump_file).c_str(), stats.thread_count, stats.module_count, stats.memory_region_count,
|
||||
stats.memory_segment_count, stats.handle_count, stats.total_memory_size);
|
||||
}
|
||||
|
||||
void process_streams(windows_emulator& win_emu, const minidump::minidump_file* dump_file)
|
||||
@@ -177,9 +175,8 @@ namespace minidump_loader
|
||||
const auto* sys_info = dump_file->get_system_info();
|
||||
if (sys_info)
|
||||
{
|
||||
win_emu.log.info("System: OS %u.%u.%u, %u processors, type %u, platform %u\n", sys_info->major_version,
|
||||
sys_info->minor_version, sys_info->build_number, sys_info->number_of_processors,
|
||||
sys_info->product_type, sys_info->platform_id);
|
||||
win_emu.log.info("System: OS %u.%u.%u, %u processors, type %u, platform %u\n", sys_info->major_version, sys_info->minor_version,
|
||||
sys_info->build_number, sys_info->number_of_processors, sys_info->product_type, sys_info->platform_id);
|
||||
}
|
||||
|
||||
// Process memory info
|
||||
@@ -199,8 +196,7 @@ namespace minidump_loader
|
||||
guard_pages++;
|
||||
}
|
||||
}
|
||||
win_emu.log.info("Memory: %zu regions, %" PRIu64 " bytes reserved, %" PRIu64
|
||||
" bytes committed, %zu guard pages\n",
|
||||
win_emu.log.info("Memory: %zu regions, %" PRIu64 " bytes reserved, %" PRIu64 " bytes committed, %zu guard pages\n",
|
||||
memory_regions.size(), total_reserved, total_committed, guard_pages);
|
||||
|
||||
// Process memory content
|
||||
@@ -214,25 +210,23 @@ namespace minidump_loader
|
||||
}
|
||||
if (!memory_segments.empty())
|
||||
{
|
||||
win_emu.log.info("Content: %zu segments, range 0x%" PRIx64 "-0x%" PRIx64 " (%" PRIu64 " bytes span)\n",
|
||||
memory_segments.size(), min_addr, max_addr, max_addr - min_addr);
|
||||
win_emu.log.info("Content: %zu segments, range 0x%" PRIx64 "-0x%" PRIx64 " (%" PRIu64 " bytes span)\n", memory_segments.size(),
|
||||
min_addr, max_addr, max_addr - min_addr);
|
||||
}
|
||||
|
||||
// Process modules
|
||||
const auto& modules = dump_file->modules();
|
||||
for (const auto& mod : modules)
|
||||
{
|
||||
win_emu.log.info("Module: %s at 0x%" PRIx64 " (%u bytes)\n", mod.module_name.c_str(), mod.base_of_image,
|
||||
mod.size_of_image);
|
||||
win_emu.log.info("Module: %s at 0x%" PRIx64 " (%u bytes)\n", mod.module_name.c_str(), mod.base_of_image, mod.size_of_image);
|
||||
}
|
||||
|
||||
// Process threads
|
||||
const auto& threads = dump_file->threads();
|
||||
for (const auto& thread : threads)
|
||||
{
|
||||
win_emu.log.info("Thread %u: TEB 0x%" PRIx64 ", stack 0x%" PRIx64 " (%u bytes), context %u bytes\n",
|
||||
thread.thread_id, thread.teb, thread.stack_start_of_memory_range, thread.stack_data_size,
|
||||
thread.context_data_size);
|
||||
win_emu.log.info("Thread %u: TEB 0x%" PRIx64 ", stack 0x%" PRIx64 " (%u bytes), context %u bytes\n", thread.thread_id,
|
||||
thread.teb, thread.stack_start_of_memory_range, thread.stack_data_size, thread.context_data_size);
|
||||
}
|
||||
|
||||
// Process handles
|
||||
@@ -272,8 +266,7 @@ namespace minidump_loader
|
||||
const auto& memory_regions = dump_file->memory_regions();
|
||||
const auto& memory_segments = dump_file->memory_segments();
|
||||
|
||||
win_emu.log.info("Reconstructing memory: %zu regions, %zu data segments\n", memory_regions.size(),
|
||||
memory_segments.size());
|
||||
win_emu.log.info("Reconstructing memory: %zu regions, %zu data segments\n", memory_regions.size(), memory_segments.size());
|
||||
size_t reserved_count = 0;
|
||||
size_t committed_count = 0;
|
||||
size_t failed_count = 0;
|
||||
@@ -281,8 +274,8 @@ namespace minidump_loader
|
||||
for (const auto& region : memory_regions)
|
||||
{
|
||||
// Log the memory region details
|
||||
win_emu.log.info("Region: 0x%" PRIx64 ", size=%" PRIu64 ", state=0x%08X, protect=0x%08X\n",
|
||||
region.base_address, region.region_size, region.state, region.protect);
|
||||
win_emu.log.info("Region: 0x%" PRIx64 ", size=%" PRIu64 ", state=0x%08X, protect=0x%08X\n", region.base_address,
|
||||
region.region_size, region.state, region.protect);
|
||||
|
||||
const bool is_reserved = (region.state & MEM_RESERVE) != 0;
|
||||
const bool is_committed = (region.state & MEM_COMMIT) != 0;
|
||||
@@ -297,8 +290,7 @@ namespace minidump_loader
|
||||
if (protect_value == 0)
|
||||
{
|
||||
protect_value = PAGE_READONLY;
|
||||
win_emu.log.warn(" Region 0x%" PRIx64 " has zero protection, using PAGE_READONLY\n",
|
||||
region.base_address);
|
||||
win_emu.log.warn(" Region 0x%" PRIx64 " has zero protection, using PAGE_READONLY\n", region.base_address);
|
||||
}
|
||||
|
||||
memory_permission perms = map_nt_to_emulator_protection(protect_value);
|
||||
@@ -307,35 +299,31 @@ namespace minidump_loader
|
||||
{
|
||||
if (is_committed)
|
||||
{
|
||||
if (win_emu.memory.allocate_memory(region.base_address, static_cast<size_t>(region.region_size),
|
||||
perms, false))
|
||||
if (win_emu.memory.allocate_memory(region.base_address, static_cast<size_t>(region.region_size), perms, false))
|
||||
{
|
||||
committed_count++;
|
||||
win_emu.log.info(" Allocated committed 0x%" PRIx64 ": size=%" PRIu64
|
||||
", state=0x%08X, protect=0x%08X\n",
|
||||
win_emu.log.info(" Allocated committed 0x%" PRIx64 ": size=%" PRIu64 ", state=0x%08X, protect=0x%08X\n",
|
||||
region.base_address, region.region_size, region.state, region.protect);
|
||||
}
|
||||
else
|
||||
{
|
||||
failed_count++;
|
||||
win_emu.log.warn(" Failed to allocate committed 0x%" PRIx64 ": size=%" PRIu64 "\n",
|
||||
region.base_address, region.region_size);
|
||||
win_emu.log.warn(" Failed to allocate committed 0x%" PRIx64 ": size=%" PRIu64 "\n", region.base_address,
|
||||
region.region_size);
|
||||
}
|
||||
}
|
||||
else if (is_reserved)
|
||||
{
|
||||
if (win_emu.memory.allocate_memory(region.base_address, static_cast<size_t>(region.region_size),
|
||||
perms, true))
|
||||
if (win_emu.memory.allocate_memory(region.base_address, static_cast<size_t>(region.region_size), perms, true))
|
||||
{
|
||||
reserved_count++;
|
||||
win_emu.log.info(" Reserved 0x%" PRIx64 ": size=%" PRIu64 ", state=0x%08X, protect=0x%08X\n",
|
||||
region.base_address, region.region_size, region.state, region.protect);
|
||||
win_emu.log.info(" Reserved 0x%" PRIx64 ": size=%" PRIu64 ", state=0x%08X, protect=0x%08X\n", region.base_address,
|
||||
region.region_size, region.state, region.protect);
|
||||
}
|
||||
else
|
||||
{
|
||||
failed_count++;
|
||||
win_emu.log.warn(" Failed to reserve 0x%" PRIx64 ": size=%" PRIu64 "\n", region.base_address,
|
||||
region.region_size);
|
||||
win_emu.log.warn(" Failed to reserve 0x%" PRIx64 ": size=%" PRIu64 "\n", region.base_address, region.region_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -346,8 +334,7 @@ namespace minidump_loader
|
||||
}
|
||||
}
|
||||
|
||||
win_emu.log.info("Regions: %zu reserved, %zu committed, %zu failed\n", reserved_count, committed_count,
|
||||
failed_count);
|
||||
win_emu.log.info("Regions: %zu reserved, %zu committed, %zu failed\n", reserved_count, committed_count, failed_count);
|
||||
size_t written_count = 0;
|
||||
size_t write_failed_count = 0;
|
||||
uint64_t total_bytes_written = 0;
|
||||
@@ -356,25 +343,21 @@ namespace minidump_loader
|
||||
{
|
||||
try
|
||||
{
|
||||
auto memory_data =
|
||||
dump_reader->read_memory(segment.start_virtual_address, static_cast<size_t>(segment.size));
|
||||
win_emu.memory.write_memory(segment.start_virtual_address, memory_data.data(),
|
||||
static_cast<size_t>(memory_data.size()));
|
||||
auto memory_data = dump_reader->read_memory(segment.start_virtual_address, static_cast<size_t>(segment.size));
|
||||
win_emu.memory.write_memory(segment.start_virtual_address, memory_data.data(), static_cast<size_t>(memory_data.size()));
|
||||
written_count++;
|
||||
total_bytes_written += memory_data.size();
|
||||
win_emu.log.info(" Written segment 0x%" PRIx64 ": %zu bytes\n", segment.start_virtual_address,
|
||||
memory_data.size());
|
||||
win_emu.log.info(" Written segment 0x%" PRIx64 ": %zu bytes\n", segment.start_virtual_address, memory_data.size());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
write_failed_count++;
|
||||
win_emu.log.error(" Failed to write segment 0x%" PRIx64 ": %s\n", segment.start_virtual_address,
|
||||
e.what());
|
||||
win_emu.log.error(" Failed to write segment 0x%" PRIx64 ": %s\n", segment.start_virtual_address, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
win_emu.log.info("Content: %zu segments written (%" PRIu64 " bytes), %zu failed\n", written_count,
|
||||
total_bytes_written, write_failed_count);
|
||||
win_emu.log.info("Content: %zu segments written (%" PRIu64 " bytes), %zu failed\n", written_count, total_bytes_written,
|
||||
write_failed_count);
|
||||
}
|
||||
|
||||
bool is_main_executable(const minidump::module_info& mod)
|
||||
@@ -414,15 +397,14 @@ namespace minidump_loader
|
||||
{
|
||||
try
|
||||
{
|
||||
auto* mapped_module = win_emu.mod_manager.map_memory_module(mod.base_of_image, mod.size_of_image,
|
||||
mod.module_name, win_emu.log);
|
||||
auto* mapped_module =
|
||||
win_emu.mod_manager.map_memory_module(mod.base_of_image, mod.size_of_image, mod.module_name, win_emu.log);
|
||||
|
||||
if (mapped_module)
|
||||
{
|
||||
mapped_count++;
|
||||
win_emu.log.info(" Mapped %s at 0x%" PRIx64 " (%u bytes, %zu sections, %zu exports)\n",
|
||||
mod.module_name.c_str(), mod.base_of_image, mod.size_of_image,
|
||||
mapped_module->sections.size(), mapped_module->exports.size());
|
||||
win_emu.log.info(" Mapped %s at 0x%" PRIx64 " (%u bytes, %zu sections, %zu exports)\n", mod.module_name.c_str(),
|
||||
mod.base_of_image, mod.size_of_image, mapped_module->sections.size(), mapped_module->exports.size());
|
||||
|
||||
if (is_main_executable(mod))
|
||||
{
|
||||
@@ -446,8 +428,7 @@ namespace minidump_loader
|
||||
else
|
||||
{
|
||||
failed_count++;
|
||||
win_emu.log.warn(" Failed to map %s at 0x%" PRIx64 "\n", mod.module_name.c_str(),
|
||||
mod.base_of_image);
|
||||
win_emu.log.warn(" Failed to map %s at 0x%" PRIx64 "\n", mod.module_name.c_str(), mod.base_of_image);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
@@ -457,8 +438,8 @@ namespace minidump_loader
|
||||
}
|
||||
}
|
||||
|
||||
win_emu.log.info("Module reconstruction: %zu mapped, %zu failed, %zu system modules identified\n", mapped_count,
|
||||
failed_count, identified_count);
|
||||
win_emu.log.info("Module reconstruction: %zu mapped, %zu failed, %zu system modules identified\n", mapped_count, failed_count,
|
||||
identified_count);
|
||||
}
|
||||
|
||||
void setup_kusd_from_dump(windows_emulator& win_emu, const minidump::minidump_file* dump_file)
|
||||
@@ -483,8 +464,7 @@ namespace minidump_loader
|
||||
kusd.ProductTypeIsValid = 1;
|
||||
|
||||
win_emu.log.info("KUSD updated: Windows %u.%u.%u, %u processors, product type %u\n", sys_info->major_version,
|
||||
sys_info->minor_version, sys_info->build_number, sys_info->number_of_processors,
|
||||
sys_info->product_type);
|
||||
sys_info->minor_version, sys_info->build_number, sys_info->number_of_processors, sys_info->product_type);
|
||||
}
|
||||
|
||||
bool load_thread_context(const std::filesystem::path& minidump_path, const minidump::thread_info& thread_info,
|
||||
@@ -546,8 +526,8 @@ namespace minidump_loader
|
||||
thread.teb->set_address(thread_info.teb);
|
||||
}
|
||||
|
||||
win_emu.log.info(" Thread %u: TEB=0x%" PRIx64 ", stack=0x%" PRIx64 " (%u bytes), context=%s\n",
|
||||
thread_info.thread_id, thread_info.teb, thread.stack_base, thread_info.stack_data_size,
|
||||
win_emu.log.info(" Thread %u: TEB=0x%" PRIx64 ", stack=0x%" PRIx64 " (%u bytes), context=%s\n", thread_info.thread_id,
|
||||
thread_info.teb, thread.stack_base, thread_info.stack_data_size,
|
||||
context_loaded ? "loaded" : "unavailable");
|
||||
|
||||
win_emu.process.threads.store(std::move(thread));
|
||||
@@ -566,8 +546,8 @@ namespace minidump_loader
|
||||
win_emu.process.active_thread = &first_thread;
|
||||
}
|
||||
|
||||
win_emu.log.info("Thread reconstruction: %zu/%zu threads created, %zu with context\n", success_count,
|
||||
threads.size(), context_loaded_count);
|
||||
win_emu.log.info("Thread reconstruction: %zu/%zu threads created, %zu with context\n", success_count, threads.size(),
|
||||
context_loaded_count);
|
||||
}
|
||||
|
||||
void setup_peb_from_teb(windows_emulator& win_emu, const minidump::minidump_file* dump_file)
|
||||
@@ -652,8 +632,8 @@ namespace minidump_loader
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
win_emu.log.error(" Failed to create %s handle '%s': %s\n", handle_info.type_name.c_str(),
|
||||
handle_info.object_name.c_str(), e.what());
|
||||
win_emu.log.error(" Failed to create %s handle '%s': %s\n", handle_info.type_name.c_str(), handle_info.object_name.c_str(),
|
||||
e.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -674,10 +654,9 @@ namespace minidump_loader
|
||||
return;
|
||||
}
|
||||
|
||||
win_emu.process.current_ip = exception_info->exception_record.exception_address;
|
||||
win_emu.log.info("Exception context: address=0x%" PRIx64 ", code=0x%08X, thread=%u\n",
|
||||
exception_info->exception_record.exception_address,
|
||||
exception_info->exception_record.exception_code, exception_info->thread_id);
|
||||
exception_info->exception_record.exception_address, exception_info->exception_record.exception_code,
|
||||
exception_info->thread_id);
|
||||
}
|
||||
|
||||
void load_minidump_into_emulator(windows_emulator& win_emu, const std::filesystem::path& minidump_path)
|
||||
@@ -724,4 +703,4 @@ namespace minidump_loader
|
||||
throw;
|
||||
}
|
||||
}
|
||||
} // namespace minidump_loader
|
||||
} // namespace minidump_loader
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user