[
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: build\n\non: [push, pull_request]\n\njobs:\n  clippy:\n    # Runs Clippy to check for lints in the Rust code\n    name: Clippy Lint Check\n    runs-on: windows-latest\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Set up Rust\n        run: |\n          rustup default stable\n          rustup component add clippy\n\n      - name: Run Clippy\n        run: cargo clippy -- -D warnings\n\n  doc:\n    # Builds project documentation, including private items\n    name: Docs Check\n    runs-on: windows-latest\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Check docs\n        run: cargo doc --no-deps --document-private-items"
  },
  {
    "path": ".gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\ndebug/\ntarget/\n\n# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries\n# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html\nexamples/Cargo.lock\ntests/Cargo.lock\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# MSVC Windows builds of rustc generate these, which store debugging information\n*.pdb\n\n\n# RustRover\n#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can\n#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore\n#  and can be added to the global gitignore or merged into this file.  For a more nuclear\n#  option (not recommended) you can uncomment the following to ignore the entire idea folder.\n#.idea/"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"rust-analyzer.linkedProjects\": [\n        \"Cargo.toml\"\n    ],\n}"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"uwd\"\nversion = \"0.3.5\"\nedition = \"2024\"\ndescription = \"Call Stack Spoofing for Rust\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/joaoviictorti/uwd\"\nhomepage = \"https://github.com/joaoviictorti/uwd\"\nreadme = \"README.md\"\nbuild = \"build.rs\"\nkeywords = [\"spoofing\", \"stack\", \"windows\", \"rust\", \"redteam\"]\ncategories = [\"os\", \"security\"]\ninclude = [\n    \"src/**\",\n    \"build.rs\",\n    \"Cargo.toml\",\n    \"README.md\",\n    \"LICENSE\",\n]\n\n[lib]\ndoctest = false\n\n[dependencies]\nbitfield = \"0.19.0\"\nobfstr = \"0.4.4\"\ndinvk = \"0.4.2\"\nanyhow = { version = \"1.0.98\", default-features = false }\nmemchr = { version = \"2.7.4\", default-features = false }\n\n[build-dependencies]\ncc = \"1.2.19\"\nnasm-rs = \"0.3.0\"\n\n[features]\ndefault = []\ndesync = []\n\n[package.metadata.docs.rs]\ndefault-target = \"x86_64-pc-windows-msvc\"\ntargets = [\"x86_64-pc-windows-gnu\", \"x86_64-pc-windows-msvc\"]\n"
  },
  {
    "path": "Justfile",
    "content": "# Aliases\nalias c := clean\nalias up := update\n\n# Use PowerShell shell on Windows\nset windows-shell := [\"powershell.exe\", \"-NoLogo\", \"-Command\"]\n\n# Clean target\nclean:\n    cargo clean\n\n# Updates dependencies as per Cargo.toml\nupdate:\n    cargo update\n\n# Publishes the crate to crates.io\npublish:\n    cargo publish --allow-dirty\n\n# Format all .toml files using Taplo\ntaplo:\n    taplo format"
  },
  {
    "path": "LICENSE-APACHE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "LICENSE-MIT",
    "content": "MIT License\n\nCopyright (c) 2025 Victor\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "# uwd\n\n![Rust](https://img.shields.io/badge/made%20with-Rust-red)\n![crate](https://img.shields.io/crates/v/uwd.svg)\n![docs](https://docs.rs/uwd/badge.svg)\n![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-brightgreen)\n[![Actions status](https://github.com/joaoviictorti/uwd/actions/workflows/ci.yml/badge.svg)](https://github.com/joaoviictorti/uwd/actions)\n\nRust library for call stack spoofing on Windows, allowing you to execute arbitrary functions with a forged call stack that evades analysis, logging, or detection during stack unwinding.\n\nInspired by [SilentMoonwalk](https://github.com/klezVirus/SilentMoonwalk), this crate brings low-level spoofing capabilities into a clean, idiomatic Rust interface with full support for `#[no_std]`, `MSVC` and `GNU` toolchains, and automated gadget resolution.\n\n## Features\n\n- ✅ Call stack spoofing via `Synthetic` and `Desync`.\n- ✅ Compatible with both `MSVC` and `GNU` toolchains (**x64**).\n- ✅ Inline macros: `spoof!` / `syscall!`.\n- ✅ Supports `#[no_std]` environments (with `alloc`).\n\nTo enable Desync mode, activate the `desync` feature in your project, the macros will automatically use Desync behavior when the feature is enabled.\n\n## Getting started\n\nAdd `uwd` to your project by updating your `Cargo.toml`:\n```bash\ncargo add uwd\n```\n\n## Usage\n\n`uwd` allows you to spoof the call stack in Rust when calling either standard Windows APIs or performing indirect syscalls. The library handles the full setup of fake frames, gadget chains, and register preparation to make execution appear as if it came from a legitimate source.\n\nYou can spoof:\n\n* Normal functions (like `VirtualAlloc`, `WinExec`, etc.)\n* Native syscalls with automatic SSN and stub resolution (like `NtAllocateVirtualMemory`)\n\n### Spoofing WinExec\n\nThis example shows how to spawn `calc.exe` using a spoofed call stack. We call `WinExec` twice once using the Desync technique, and again using the Synthetic one.\n\n```rust\nuse dinvk::module::{get_module_address, get_proc_address};\nuse uwd::spoof;\n\n// Resolves addresses of the WinAPI functions to be used\nlet kernel32 = get_module_address(\"kernel32.dll\", None);\nlet win_exec = get_proc_address(kernel32, \"WinExec\", None);\n\n// Execute command with `WinExec`\nlet cmd = c\"calc.exe\";\nlet mut result = spoof!(win_exec, cmd.as_ptr(), 1)?;\nif result.is_null() {\n    eprintln!(\"WinExec Failed\");\n    return Ok(());\n}\n```\n\n### Spoofing an Indirect Syscall\n\nThis example performs a indirect system call to `NtAllocateVirtualMemory` with a spoofed call stack.\n\n```rust\nuse std::{ffi::c_void, ptr::null_mut};\nuse dinvk::winapis::NT_SUCCESS;\nuse uwd::{syscall, AsPointer};\n\n// Running indirect syscall with Call Stack Spoofing\nlet mut addr = null_mut::<c_void>();\nlet mut size = (1 << 12) as usize;\nlet mut status = syscall!(\"NtAllocateVirtualMemory\", -1isize, addr.as_ptr_mut(), 0, size.as_ptr_mut(), 0x3000, 0x04)? as i32;\nif !NT_SUCCESS(status) {\n    eprintln!(\"[-] NtAllocateVirtualMemory Failed With Status: {status:#X}\");\n    return Ok(())\n}\n\nprintln!(\"[+] Address allocated: {:?}\", addr);\n```\n\n## References\n\nI want to express my gratitude to these projects that inspired me to create `uwd` and contribute with some features:\n\n- [SilentMoonwalk](https://github.com/klezVirus/SilentMoonwalk)\n\nSpecial thanks to:\n\n- [Kudaes](https://x.com/_Kudaes_)\n- [Klez](https://x.com/KlezVirus)\n- [Waldo-IRC](https://x.com/waldoirc)\n- [Trickster0](https://x.com/trickster012)\n- [namazso](https://x.com/namazso)\n\n## License\n\nuwd is licensed under either of\n\n- Apache License, Version 2.0, ([LICENSE-APACHE](https://github.com/joaoviictorti/uwd/tree/main/LICENSE-APACHE) or\n  <https://www.apache.org/licenses/LICENSE-2.0>)\n- MIT license ([LICENSE-MIT](https://github.com/joaoviictorti/uwd/tree/main/LICENSE-MIT) or <https://opensource.org/licenses/MIT>)\n\nat your option.\n\nUnless you explicitly state otherwise, any contribution intentionally submitted for inclusion in uwd\nby you, as defined in the Apache-2.0 license, shall be dually licensed as above, without any\nadditional terms or conditions."
  },
  {
    "path": "build.rs",
    "content": "use std::env;\n\nfn main() {\n    if env::var(\"DOCS_RS\").is_ok() {\n        println!(\"cargo:warning=Skipping ASM build for docs.rs\");\n        return;\n    }\n\n    let target = env::var(\"TARGET\").expect(\"Missing TARGET environment variable\");\n    let out_dir = env::var(\"OUT_DIR\").expect(\"Missing OUT_DIR environment variable\");\n\n    // Supports x86_64 environments only\n    if !target.contains(\"x86_64\") {\n        panic!(\"This build script only supports x86_64 targets.\");\n    }\n\n    if target.contains(\"msvc\") {\n        // Use MASM with cc\n        cc::Build::new()\n            .file(\"src/asm/msvc/desync.asm\")\n            .file(\"src/asm/msvc/synthetic.asm\")\n            .compile(\"spoof\");\n    } else if target.contains(\"gnu\") {\n        // Use NASM with nasm_rs\n        let sources = [\"src/asm/gnu/desync.asm\", \"src/asm/gnu/synthetic.asm\"];\n        if let Err(e) = nasm_rs::compile_library(\"spoof\", &sources) {\n            panic!(\"Failed to compile with NASM [spoof]: {}\", e);\n        }\n\n        for source in &sources {\n            println!(\"cargo:rerun-if-changed={}\", source);\n        }\n\n        println!(\"cargo:rustc-link-search=native={}\", out_dir);\n        println!(\"cargo:rustc-link-lib=static=spoof\");\n    } else {\n        panic!(\"Unsupported target: {}\", target);\n    }\n}\n"
  },
  {
    "path": "clippy.toml",
    "content": "msrv = \"1.89\""
  },
  {
    "path": "rust-toolchain.toml",
    "content": "[toolchain]\nchannel = \"stable\"\ntargets = [\"x86_64-pc-windows-msvc\"]\n"
  },
  {
    "path": "rustfmt.toml",
    "content": "# Uses Rustfmt 2024 edition rules, but not all of them are enforced 100%.\nedition = \"2024\"\nstyle_edition = \"2024\"\n"
  },
  {
    "path": "src/asm/gnu/desync.asm",
    "content": ";;\n;; Code responsible for Call Stack Spoofing Via Desync (NASM)\n;;\n[BITS 64]\n\n;;\n;; Export\n;;\nGLOBAL Spoof\n\n[SECTION .data]\n\n;;\n;; Configuration structure passed to the spoof ASM routine\n;;\nSTRUC Config\n    .RtlUserThreadStartAddr       RESQ 1\n    .RtlUserThreadStartFrameSize  RESQ 1\n    \n    .BaseThreadInitThunkAddr      RESQ 1\n    .BaseThreadInitThunkFrameSize RESQ 1\n\n    .FirstFrame                   RESQ 1\n    .SecondFrame                  RESQ 1\n    .JmpRbxGadget                 RESQ 1\n    .AddRspXGadget                RESQ 1\n\n    .FirstFrameSize               RESQ 1\n    .SecondFrameSize              RESQ 1\n    .JmpRbxGadgetFrameSize        RESQ 1\n    .AddRspXGadgetFrameSize       RESQ 1\n\n    .RbpOffset                    RESQ 1\n\n    .SpooFunction                 RESQ 1\n    .ReturnAddress                RESQ 1\n\n    .IsSyscall                    RESD 1\n    .Ssn                          RESD 1\n\n    .NArgs                        RESQ 1\n    .Arg01                        RESQ 1\n    .Arg02                        RESQ 1\n    .Arg03                        RESQ 1\n    .Arg04                        RESQ 1\n    .Arg05                        RESQ 1\n    .Arg06                        RESQ 1\n    .Arg07                        RESQ 1\n    .Arg08                        RESQ 1\n    .Arg09                        RESQ 1\n    .Arg10                        RESQ 1\n    .Arg11                        RESQ 1\nENDSTRUC\n\n[SECTION .text]\n\n;;\n;; Function responsible for Call Stack Spoofing\n;;\nSpoof:\n    ;;\n    ;; Saving non-vol registers\n    ;;\n    push rbp\n    push rbx\n\n    ;;\n    ;; Return main \n    ;;\n    mov rbp, rsp \n\n    ;;\n    ;; Creating stack pointer to Restore PROC\n    ;;\n    lea rax, [rel Restore]\n    push rax\n    lea rbx, [rsp]\n\n    ;;\n    ;; First Frame (Fake origin)\n    ;;\n    push QWORD [rcx + Config.FirstFrame]\n\n    mov rax, [rcx + Config.ReturnAddress]\n    sub rax, [rcx + Config.FirstFrameSize]\n\n    sub rsp, [rcx + Config.SecondFrameSize]\n    mov r10, [rcx + Config.RbpOffset]\n    mov [rsp + r10], rax\n    \n    ;;\n    ;; ROP Frames\n    ;;\n    push QWORD [rcx + Config.SecondFrame]\n\n    ;;\n    ;; JMP [RBX] Gadget / Stack Pivot (To restore original Control Flow Stack)\n    ;;\n    sub rsp, [rcx + Config.JmpRbxGadgetFrameSize]\n    push QWORD [rcx + Config.JmpRbxGadget]\n\n    sub rsp, [rcx + Config.AddRspXGadgetFrameSize]\n    push QWORD [rcx + Config.AddRspXGadget]\n\n    ;;\n    ;; Set the pointer to the function to call in R11\n    ;;\n    mov r11, [rcx + Config.SpooFunction]\n    jmp Parameters\n\n;;\n;; Set the parameters to pass to the target function\n;;\nParameters:\n    mov r12, rcx \n    mov rax, [r12 + Config.NArgs]\n    \n    ; Arg01 (rcx)\n    cmp rax, 1\n    jb skip_1\n    mov rcx, [r12 + Config.Arg01]\n\nskip_1:\n    ; Arg02 (rdx)\n    cmp rax, 2\n    jb skip_2\n    mov rdx, [r12 + Config.Arg02]\n\nskip_2:\n    ; Arg03 (r8)\n    cmp rax, 3\n    jb skip_3\n    mov r8, [r12 + Config.Arg03]\n    \nskip_3:\n    ; Arg04 (r9)\n    cmp rax, 4\n    jb skip_4\n    mov r9, [r12 + Config.Arg04]\n\nskip_4:\n    ; Stack-based args\n    lea r13, [rsp] \n\n    cmp rax, 5\n    jb skip_5\n    mov r10, [r12 + Config.Arg05]\n    mov [r13 + 28h], r10\n\nskip_5:\n    ; Arg06\n    cmp rax, 6\n    jb skip_6\n    mov r10, [r12 + Config.Arg06]\n    mov [r13 + 30h], r10\n\nskip_6:\n    ; Arg07\n    cmp rax, 7\n    jb skip_7\n    mov r10, [r12 + Config.Arg07]\n    mov [r13 + 38h], r10\n\nskip_7:\n    ; Arg08\n    cmp rax, 8\n    jb skip_8\n    mov r10, [r12 + Config.Arg08]\n    mov [r13 + 40h], r10\n    \nskip_8:\n    ; Arg09\n    cmp rax, 9\n    jb skip_9\n    mov r10, [r12 + Config.Arg09]\n    mov [r13 + 48h], r10\n\nskip_9:\n    ; Arg10\n    cmp rax, 10\n    jb skip_10\n    mov r10, [r12 + Config.Arg10]\n    mov [r13 + 50h], r10\n\nskip_10:\n    ; Arg11\n    cmp rax, 11\n    jb skip_11\n    mov r10, [r12 + Config.Arg11]\n    mov [r13 + 58h], r10\n\nskip_11:\n    cmp BYTE [r12 + Config.IsSyscall], 1\n    je ExecuteSyscall\n\n    jmp Execute\n\n;;\n;; Restores the original stack frame\n;;\nRestore:\n    mov rsp, rbp\n    pop rbx\n    pop rbp\n    ret\n\n;;\n;; Executes the target function\n;;\nExecute:\n    jmp r11\n\n;;\n;; Executes a native Windows system call using the spoofed context\n;;\nExecuteSyscall:\n    mov r10, rcx\n    mov eax, DWORD [r12 + Config.Ssn]\n    jmp r11"
  },
  {
    "path": "src/asm/gnu/synthetic.asm",
    "content": ";;\n;; Code responsible for Call Stack Spoofing Via Synthetic (NASM)\n;;\n[BITS 64]\n\n;;\n;; Export\n;;\nGLOBAL SpoofSynthetic\n\n[SECTION .data]\n\n;;\n;; Configuration structure passed to the spoof ASM routine\n;;\nSTRUC Config\n    .RtlUserThreadStartAddr       RESQ 1\n    .RtlUserThreadStartFrameSize  RESQ 1\n    \n    .BaseThreadInitThunkAddr      RESQ 1\n    .BaseThreadInitThunkFrameSize RESQ 1\n\n    .FirstFrame                   RESQ 1\n    .SecondFrame                  RESQ 1\n    .JmpRbxGadget                 RESQ 1\n    .AddRspXGadget                RESQ 1\n\n    .FirstFrameSize               RESQ 1\n    .SecondFrameSize              RESQ 1\n    .JmpRbxGadgetFrameSize        RESQ 1\n    .AddRspXGadgetFrameSize       RESQ 1\n\n    .RbpOffset                    RESQ 1\n\n    .SpooFunction                 RESQ 1\n    .ReturnAddress                RESQ 1\n\n    .IsSyscall                    RESD 1\n    .Ssn                          RESD 1\n\n    .NArgs                        RESQ 1\n    .Arg01                        RESQ 1\n    .Arg02                        RESQ 1\n    .Arg03                        RESQ 1\n    .Arg04                        RESQ 1\n    .Arg05                        RESQ 1\n    .Arg06                        RESQ 1\n    .Arg07                        RESQ 1\n    .Arg08                        RESQ 1\n    .Arg09                        RESQ 1\n    .Arg10                        RESQ 1\n    .Arg11                        RESQ 1\nENDSTRUC\n\n[SECTION .text]\n\n;;\n;; Function responsible for Call Stack Spoofing\n;;\nSpoofSynthetic:\n    ;;\n    ;; Saving non-vol registers\n    ;;\n    push QWORD rbp\n    push QWORD rbx\n    push QWORD r15\n\n    ;;\n    ;; Everything between RSP and RBP is our new stack frame for unwinding \n    ;;\n    sub rsp, 210h\n    mov rbp, rsp\n\n    ;;\n    ;; Creating stack pointer to Restore PROC\n    ;;\n    lea rax, [rel RestoreSynthetic]\n    push rax\n    lea rbx, [rsp]\n\n    ;;\n    ;; Cutting the call stack. The 0 pushed in this position will be the return address\n    ;; of the next frame \"RtlUserThreadStart\", making it effectively the originating function\n    ;;\n    xor rax, rax\n    push rax\n    \n    ;;\n    ;; RtlUserThreadStart\n    ;;\n    sub rsp, [rcx + Config.RtlUserThreadStartFrameSize]\n    push QWORD [rcx + Config.RtlUserThreadStartAddr]\n    add QWORD [rsp], 21h\n\n    ;;\n    ;; BaseThreadInitThunk\n    ;;\n    sub rsp, [rcx + Config.BaseThreadInitThunkFrameSize]\n    push QWORD [rcx + Config.BaseThreadInitThunkAddr]                          \n    add QWORD [rsp], 14h\n\n    ;;\n    ;; Return Adress\n    ;;\n    mov rax, rsp\n\n    ;;\n    ;; First Frame (Fake origin)\n    ;;\n    push QWORD [rcx + Config.FirstFrame]\n    sub rax, [rcx + Config.FirstFrameSize]\n\n    sub rsp, [rcx + Config.SecondFrameSize]\n    mov r10, [rcx + Config.RbpOffset]\n    mov [rsp + r10], rax\n    \n    ;;\n    ;; ROP Frames\n    ;;\n    push QWORD [rcx + Config.SecondFrame]\n\n    ;;\n    ;; JMP [RBX] Gadget / Stack Pivot (To restore original Control Flow Stack)\n    ;;\n    sub rsp, [rcx + Config.JmpRbxGadgetFrameSize]\n    push QWORD [rcx + Config.JmpRbxGadget]\n\n    sub rsp, [rcx + Config.AddRspXGadgetFrameSize]\n    push QWORD [rcx + Config.AddRspXGadget]\n\n    ;;\n    ;; Set the pointer to the function to call in R11\n    ;;\n    mov r11, [rcx + Config.SpooFunction]\n    jmp ParametersSynthetic\n\n;;\n;; Set the parameters to pass to the target function\n;;\nParametersSynthetic:\n    mov r12, rcx \n    mov rax, [r12 + Config.NArgs]\n    \n    ; Arg01 (rcx)\n    cmp rax, 1\n    jb skip_1\n    mov rcx, [r12 + Config.Arg01]\n\nskip_1:\n    ; Arg02 (rdx)\n    cmp rax, 2\n    jb skip_2\n    mov rdx, [r12 + Config.Arg02]\n\nskip_2:\n    ; Arg03 (r8)\n    cmp rax, 3\n    jb skip_3\n    mov r8, [r12 + Config.Arg03]\n    \nskip_3:\n    ; Arg04 (r9)\n    cmp rax, 4\n    jb skip_4\n    mov r9, [r12 + Config.Arg04]\n\nskip_4:\n    ; Stack-based args\n    lea r13, [rsp] \n\n    cmp rax, 5\n    jb skip_5\n    mov r10, [r12 + Config.Arg05]\n    mov [r13 + 28h], r10\n\nskip_5:\n    ; Arg06\n    cmp rax, 6\n    jb skip_6\n    mov r10, [r12 + Config.Arg06]\n    mov [r13 + 30h], r10\n\nskip_6:\n    ; Arg07\n    cmp rax, 7\n    jb skip_7\n    mov r10, [r12 + Config.Arg07]\n    mov [r13 + 38h], r10\n\nskip_7:\n    ; Arg08\n    cmp rax, 8\n    jb skip_8\n    mov r10, [r12 + Config.Arg08]\n    mov [r13 + 40h], r10\n    \nskip_8:\n    ; Arg09\n    cmp rax, 9\n    jb skip_9\n    mov r10, [r12 + Config.Arg09]\n    mov [r13 + 48h], r10\n\nskip_9:\n    ; Arg10\n    cmp rax, 10\n    jb skip_10\n    mov r10, [r12 + Config.Arg10]\n    mov [r13 + 50h], r10\n\nskip_10:\n    ; Arg11\n    cmp rax, 11\n    jb skip_11\n    mov r10, [r12 + Config.Arg11]\n    mov [r13 + 58h], r10\n\nskip_11:\n    cmp BYTE [r12 + Config.IsSyscall], 1\n    je ExecuteSyscallSynthetic\n\n    jmp ExecuteSynthetic\n\n;;\n;; Restores the original stack frame\n;;\nRestoreSynthetic:\n    mov rsp, rbp\n    add QWORD rsp, 210h\n    pop QWORD r15\n    pop QWORD rbx\n    pop QWORD rbp\n    ret\n\n;;\n;; Executes the target function\n;;\nExecuteSynthetic:\n    jmp r11\n\n;;\n;; Executes a native Windows system call using the spoofed context\n;;\nExecuteSyscallSynthetic:\n    mov r10, rcx\n    mov eax, DWORD [r12 + Config.Ssn]\n    jmp r11"
  },
  {
    "path": "src/asm/msvc/desync.asm",
    "content": ";;\n;; Code responsible for Call Stack Spoofing Via Desync (MASM)\n;;\n\n;;\n;; Export\n;;\nSpoof proto\n\n.data\n\n;;\n;; Configuration structure passed to the spoof ASM routine\n;;\nConfig STRUCT\n    RtlUserThreadStartAddr       DQ 1\n    RtlUserThreadStartFrameSize  DQ 1\n    \n    BaseThreadInitThunkAddr      DQ 1\n    BaseThreadInitThunkFrameSize DQ 1\n\n    FirstFrame                   DQ 1\n    SecondFrame                  DQ 1\n    JmpRbxGadget                 DQ 1\n    AddRspXGadget                DQ 1\n\n    FirstFrameSize               DQ 1\n    SecondFrameSize              DQ 1\n    JmpRbxGadgetFrameSize        DQ 1\n    AddRspXGadgetFrameSize       DQ 1\n\n    RbpOffset                    DQ 1\n\n    SpooFunction                 DQ 1\n    ReturnAddress                DQ 1\n\n    IsSyscall                    DD 0\n    Ssn                          DD 0\n\n    NArgs                        DQ 1\n    Arg01                        DQ 1\n    Arg02                        DQ 1\n    Arg03                        DQ 1\n    Arg04                        DQ 1\n    Arg05                        DQ 1\n    Arg06                        DQ 1\n    Arg07                        DQ 1\n    Arg08                        DQ 1\n    Arg09                        DQ 1\n    Arg10                        DQ 1\n    Arg11                        DQ 1\nConfig ENDS\n\n.code\n\n;;\n;; Function responsible for Call Stack Spoofing\n;;\nSpoof PROC\n    ;;\n    ;; Saving non-vol registers\n    ;;\n    push rbp\n    push rbx\n\n    ;;\n    ;; Return main \n    ;;\n    mov rbp, rsp \n\n    ;;\n    ;; Creating stack pointer to Restore PROC\n    ;;\n    lea rax, Restore\n    push rax\n    lea rbx, [rsp]\n\n    ;;\n    ;; First Frame (Fake origin)\n    ;;\n    push [rcx].Config.FirstFrame\n\n    mov rax, [rcx].Config.ReturnAddresS\n    sub rax, [rcx].Config.FirstFrameSize\n\n    sub rsp, [rcx].Config.SecondFrameSize\n    mov r10, [rcx].Config.RbpOffset\n    mov [rsp + r10], rax\n    \n    ;;\n    ;; ROP Frames\n    ;;\n    push [rcx].Config.SecondFrame\n\n    ;;\n    ;; JMP [RBX] Gadget / Stack Pivot (To restore original Control Flow Stack)\n    ;;\n    sub rsp, [rcx].Config.JmpRbxGadgetFrameSize\n    push [rcx].Config.JmpRbxGadget\n\n    sub rsp, [rcx].Config.AddRspXGadgetFrameSize\n    push [rcx].Config.AddRspXGadget\n\n    ;;\n    ;; Set the pointer to the function to call in R11\n    ;;\n    mov r11, [rcx].Config.SpooFunction\n    jmp Parameters\nSpoof ENDP\n\n;;\n;; Set the parameters to pass to the target function\n;;\nParameters PROC\n    mov r12, rcx \n    mov rax, [r12].Config.NArgs\n    \n    ; Arg01 (rcx)\n    cmp rax, 1\n    jb skip_1\n    mov rcx, [r12].Config.Arg01\n\nskip_1:\n    ; Arg02 (rdx)\n    cmp rax, 2\n    jb skip_2\n    mov rdx, [r12].Config.Arg02\n    \nskip_2:\n    ; Arg03 (r8)\n    cmp rax, 3\n    jb skip_3\n    mov r8, [r12].Config.Arg03\n\nskip_3:\n    ; Arg04 (r9)\n    cmp rax, 4\n    jb skip_4\n    mov r9, [r12].Config.Arg04\n\nskip_4:\n    ; Stack-based args\n    lea r13, [rsp] \n\n    cmp rax, 5\n    jb skip_5\n    mov r10, [r12].Config.Arg05\n    mov [r13 + 28h], r10\n\nskip_5:\n    ; Arg06\n    cmp rax, 6\n    jb skip_6\n    mov r10, [r12].Config.Arg06\n    mov [r13 + 30h], r10\n\nskip_6:\n    ; Arg07\n    cmp rax, 7\n    jb skip_7\n    mov r10, [r12].Config.Arg07\n    mov [r13 + 38h], r10\n\nskip_7:\n    ; Arg08\n    cmp rax, 8\n    jb skip_8\n    mov r10, [r12].Config.Arg08\n    mov [r13 + 40h], r10\n    \nskip_8:\n    ; Arg09\n    cmp rax, 9\n    jb skip_9\n    mov r10, [r12].Config.Arg09\n    mov [r13 + 48h], r10\n\nskip_9:\n    ; Arg10\n    cmp rax, 10\n    jb skip_10\n    mov r10, [r12].Config.Arg10\n    mov [r13 + 50h], r10\n\nskip_10:\n    ; Arg11\n    cmp rax, 11\n    jb skip_11\n    mov r10, [r12].Config.Arg11\n    mov [r13 + 58h], r10\n\nskip_11:\n    cmp [r12].Config.IsSyscall, 1\n    je ExecuteSyscall\n\n    jmp Execute\nParameters ENDP\n\n;;\n;; Restores the original stack frame\n;;\nRestore PROC\n    mov rsp, rbp\n    pop rbx\n    pop rbp\n    ret\nRestore ENDP\n\n;;\n;; Executes the target function\n;;\nExecute PROC\n    jmp QWORD PTR r11\nExecute ENDP\n\n;;\n;; Executes a native Windows system call using the spoofed context\n;;\nExecuteSyscall PROC\n    mov r10, rcx\n    mov eax, [r12].Config.Ssn\n    jmp QWORD PTR r11\nExecuteSyscall ENDP\n\nEND"
  },
  {
    "path": "src/asm/msvc/synthetic.asm",
    "content": ";;\n;; Code responsible for Call Stack Spoofing Via Synthetic (MASM)\n;;\n\n;;\n;; Export\n;;\nSpoofSynthetic proto\n\n.data\n\n;;\n;; Configuration structure passed to the spoof ASM routine\n;;\nConfig STRUCT\n    RtlUserThreadStartAddr       DQ 1\n    RtlUserThreadStartFrameSize  DQ 1\n    \n    BaseThreadInitThunkAddr      DQ 1\n    BaseThreadInitThunkFrameSize DQ 1\n\n    FirstFrame                   DQ 1\n    SecondFrame                  DQ 1\n    JmpRbxGadget                 DQ 1\n    AddRspXGadget                DQ 1\n\n    FirstFrameSize               DQ 1\n    SecondFrameSize              DQ 1\n    JmpRbxGadgetFrameSize        DQ 1\n    AddRspXGadgetFrameSize       DQ 1\n\n    RbpOffset                    DQ 1\n\n    SpooFunction                 DQ 1\n    ReturnAddress                DQ 1\n\n    IsSyscall                    DD 0\n    Ssn                          DD 0\n\n    NArgs                        DQ 1\n    Arg01                        DQ 1\n    Arg02                        DQ 1\n    Arg03                        DQ 1\n    Arg04                        DQ 1\n    Arg05                        DQ 1\n    Arg06                        DQ 1\n    Arg07                        DQ 1\n    Arg08                        DQ 1\n    Arg09                        DQ 1\n    Arg10                        DQ 1\n    Arg11                        DQ 1\nConfig ENDS\n\n.code\n\n;;\n;; Function responsible for Call Stack Spoofing\n;;\nSpoofSynthetic PROC\n    ;;\n    ;; Saving non-vol registers\n    ;;\n    push rbp\n    push rbx\n    push r15\n\n    ;;\n    ;; Everything between RSP and RBP is our new stack frame for unwinding \n    ;;\n    sub rsp, 210h\n    mov rbp, rsp\n\n    ;;\n    ;; Creating stack pointer to Restore PROC\n    ;;\n    lea rax, RestoreSynthetic\n    push rax\n    lea rbx, [rsp]\n\n    ;;\n    ;; Cutting the call stack. The 0 pushed in this position will be the return address\n    ;; of the next frame \"RtlUserThreadStart\", making it effectively the originating function\n    ;;\n    xor rax, rax\n    push rax\n\n    ;;\n    ;; RtlUserThreadStart\n    ;;\n    sub rsp, [rcx].Config.RtlUserThreadStartFrameSize\n    push [rcx].Config.RtlUserThreadStartAddr\n    add QWORD PTR [rsp], 21h\n\n    ;;\n    ;; BaseThreadInitThunk\n    ;;\n    sub rsp, [rcx].Config.BaseThreadInitThunkFrameSize\n    push [rcx].Config.BaseThreadInitThunkAddr                          \n    add QWORD PTR [rsp], 14h\n\n    ;;\n    ;; Return address\n    ;;\n    mov rax, rsp\n\n    ;;\n    ;; First Frame (Fake origin)\n    ;;\n    push [rcx].Config.FirstFrame\n    sub rax, [rcx].Config.FirstFrameSize\n\n    sub rsp, [rcx].Config.SecondFrameSize\n    mov r10, [rcx].Config.RbpOffset\n    mov [rsp + r10], rax\n    \n    ;;\n    ;; ROP Frames\n    ;;\n    push [rcx].Config.SecondFrame\n\n    ;;\n    ;; JMP [RBX] Gadget / Stack Pivot (To restore original Control Flow Stack)\n    ;;\n    sub rsp, [rcx].Config.JmpRbxGadgetFrameSize\n    push [rcx].Config.JmpRbxGadget\n\n    sub rsp, [rcx].Config.AddRspXGadgetFrameSize\n    push [rcx].Config.AddRspXGadget\n\n    ;;\n    ;; Set the pointer to the function to call in R11\n    ;;\n    mov r11, [rcx].Config.SpooFunction\n    jmp ParametersSynthetic\nSpoofSynthetic ENDP\n\n;;\n;; Set the parameters to pass to the target function\n;;\nParametersSynthetic PROC\n    mov r12, rcx \n    mov rax, [r12].Config.NArgs\n    \n    ; Arg01 (rcx)\n    cmp rax, 1\n    jb skip_1\n    mov rcx, [r12].Config.Arg01\n\nskip_1:\n    ; Arg02 (rdx)\n    cmp rax, 2\n    jb skip_2\n    mov rdx, [r12].Config.Arg02\n\nskip_2:\n    ; Arg03 (r8)\n    cmp rax, 3\n    jb skip_3\n    mov r8, [r12].Config.Arg03\n    \nskip_3:\n    ; Arg04 (r9)\n    cmp rax, 4\n    jb skip_4\n    mov r9, [r12].Config.Arg04\n\nskip_4:\n    ; Stack-based args\n    lea r13, [rsp] \n\n    cmp rax, 5\n    jb skip_5\n    mov r10, [r12].Config.Arg05\n    mov [r13 + 28h], r10\n\nskip_5:\n    ; Arg06\n    cmp rax, 6\n    jb skip_6\n    mov r10, [r12].Config.Arg06\n    mov [r13 + 30h], r10\n\nskip_6:\n    ; Arg07\n    cmp rax, 7\n    jb skip_7\n    mov r10, [r12].Config.Arg07\n    mov [r13 + 38h], r10\n\nskip_7:\n    ; Arg08\n    cmp rax, 8\n    jb skip_8\n    mov r10, [r12].Config.Arg08\n    mov [r13 + 40h], r10\n    \nskip_8:\n    ; Arg09\n    cmp rax, 9\n    jb skip_9\n    mov r10, [r12].Config.Arg09\n    mov [r13 + 48h], r10\n\nskip_9:\n    ; Arg10\n    cmp rax, 10\n    jb skip_10\n    mov r10, [r12].Config.Arg10\n    mov [r13 + 50h], r10\n\nskip_10:\n    ; Arg11\n    cmp rax, 11\n    jb skip_11\n    mov r10, [r12].Config.Arg11\n    mov [r13 + 58h], r10\n\nskip_11:\n    cmp [r12].Config.IsSyscall, 1\n    je ExecuteSyscallSynthetic\n\n    jmp ExecuteSynthetic\nParametersSynthetic ENDP\n\n;;\n;; Restores the original stack frame\n;;\nRestoreSynthetic PROC\n    mov rsp, rbp\n    add rsp, 210h\n    pop r15\n    pop rbx\n    pop rbp\n    ret\nRestoreSynthetic ENDP\n\n;;\n;; Executes the target function\n;;\nExecuteSynthetic  PROC\n    jmp QWORD PTR r11\nExecuteSynthetic  ENDP\n\n;;\n;; Executes a native Windows system call using the spoofed context\n;;\nExecuteSyscallSynthetic PROC\n    mov r10, rcx\n    mov eax, [r12].Config.Ssn\n    jmp QWORD PTR r11\nExecuteSyscallSynthetic ENDP\n\nEND"
  },
  {
    "path": "src/lib.rs",
    "content": "#![no_std]\n#![doc = include_str!(\"../README.md\")]\n\nextern crate alloc;\n\nmod types;\nmod uwd;\nmod util;\n\npub use uwd::*;\n"
  },
  {
    "path": "src/types.rs",
    "content": "#![allow(non_snake_case, non_camel_case_types)]\n\nuse core::{ffi::c_void, slice::from_raw_parts};\nuse dinvk::{types::*, helper::PE};\n\n/// Indicates the presence of an exception handler in the function.\npub const UNW_FLAG_EHANDLER: u8 = 0x1;\n\n/// Indicates chained unwind information is present.\npub const UNW_FLAG_CHAININFO: u8 = 0x4;\n\n/// Provides access to the unwind (exception handling) information of a PE image.\n#[derive(Debug)]\npub struct Unwind {\n    /// Reference to the parsed PE image.\n    pub pe: PE,\n}\n\nimpl Unwind {\n    /// Creates a new [`Unwind`].\n    pub fn new(pe: PE) -> Self {\n        Unwind { pe }\n    }\n\n    /// Returns all runtime function entries.\n    pub fn entries(&self) -> Option<&[IMAGE_RUNTIME_FUNCTION]> {\n        let nt = self.pe.nt_header()?;\n        let dir = unsafe {\n            (*nt).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]\n        };\n\n        if dir.VirtualAddress == 0 || dir.Size == 0 {\n            return None;\n        }\n\n        let addr = (self.pe.base as usize + dir.VirtualAddress as usize) as *const IMAGE_RUNTIME_FUNCTION;\n        let len = dir.Size as usize / size_of::<IMAGE_RUNTIME_FUNCTION>();\n\n        Some(unsafe { from_raw_parts(addr, len) })\n    }\n\n    /// Finds a runtime function by its RVA.\n    pub fn function_by_offset(&self, offset: u32) -> Option<&IMAGE_RUNTIME_FUNCTION> {\n        self.entries()?.iter().find(|f| f.BeginAddress == offset)\n    }\n\n    /// Gets the size in bytes of a function using the unwind table.\n    #[cfg(feature = \"desync\")]\n    pub fn function_size(&self, func: *mut c_void) -> Option<u64> {\n        let offset = (func as usize - self.pe.base as usize) as u32;\n        let entry = self.function_by_offset(offset)?;\n\n        let start = self.pe.base as u64 + entry.BeginAddress as u64;\n        let end = self.pe.base as u64 + entry.EndAddress as u64;\n        Some(end - start)\n    }\n}\n\n/// Configuration structure passed to the spoof ASM routine.\n#[repr(C)]\n#[derive(Debug)]\npub struct Config {\n    /// Address RtlUserThreadStart\n    pub rtl_user_addr: *const c_void,\n\n    /// Stack Size RtlUserThreadStart\n    pub rtl_user_thread_size: u64,\n\n    /// Address BaseThreadInitThunk\n    pub base_thread_addr: *const c_void,\n\n    /// Stack Size BaseThreadInitThunk\n    pub base_thread_size: u64,\n\n    /// First (fake) return address frame\n    pub first_frame_fp: *const c_void,\n\n    /// Second (ROP) return address frame\n    pub second_frame_fp: *const c_void,\n\n    /// Gadget: `jmp [rbx]`\n    pub jmp_rbx_gadget: *const c_void,\n\n    /// Gadget: `add rsp, X; ret`\n    pub add_rsp_gadget: *const c_void,\n\n    /// Stack size of first spoofed frame\n    pub first_frame_size: u64,\n\n    /// Stack size of second spoofed frame\n    pub second_frame_size: u64,\n\n    /// Stack frame size where the `jmp [rbx]` gadget resides\n    pub jmp_rbx_frame_size: u64,\n\n    /// Stack frame size where the `add rsp, X` gadget resides\n    pub add_rsp_frame_size: u64,\n\n    /// Offset on the stack where `rbp` is pushed\n    pub rbp_stack_offset: u64,\n\n    /// The function to be spoofed / called\n    pub spoof_function: *const c_void,\n\n    /// Return address (used as stack-resume point after call)\n    pub return_address: *const c_void,\n\n    /// Checks if the target is a syscall\n    pub is_syscall: u32,\n\n    /// System Service Number (SSN)\n    pub ssn: u32,\n\n    /// Arguments that will be passed to the function that will be spoofed\n    pub number_args: u64,\n    pub arg01: *const c_void,\n    pub arg02: *const c_void,\n    pub arg03: *const c_void,\n    pub arg04: *const c_void,\n    pub arg05: *const c_void,\n    pub arg06: *const c_void,\n    pub arg07: *const c_void,\n    pub arg08: *const c_void,\n    pub arg09: *const c_void,\n    pub arg10: *const c_void,\n    pub arg11: *const c_void,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        unsafe { core::mem::zeroed() }\n    }\n}\n\n/// Enumeration of x86_64 general-purpose registers.\n///\n/// Used in unwind parsing or register mapping logic.\n#[derive(Debug, Clone, Copy)]\n#[repr(u8)]\n#[allow(dead_code)]\npub enum Registers {\n    Rax = 0,\n    Rcx,\n    Rdx,\n    Rbx,\n    Rsp,\n    Rbp,\n    Rsi,\n    Rdi,\n    R8,\n    R9,\n    R10,\n    R11,\n    R12,\n    R13,\n    R14,\n    R15,\n}\n\nimpl PartialEq<usize> for Registers {\n    fn eq(&self, other: &usize) -> bool {\n        *self as usize == *other\n    }\n}\n\n/// Union representing a single unwind operation code.\n#[repr(C)]\npub union UNWIND_CODE {\n    /// Offset into the stack frame for the operation.\n    pub FrameOffset: u16,\n\n    /// Structured fields of the unwind code.\n    pub Anonymous: UNWIND_CODE_0,\n}\n\nbitfield::bitfield! {\n    /// Bitfield representation of an unwind code entry.\n    #[repr(C)]\n    #[derive(Debug, Clone, Copy)]\n    pub struct UNWIND_CODE_0(u16);\n\n    /// Byte offset from the start of the prologue where this operation is applied.\n    pub u8, CodeOffset, SetCodeOffset: 7, 0;\n\n    /// The unwind operation code.\n    pub u8, UnwindOp, SetUnwindOp: 11, 8;\n\n    /// Additional operation-specific information.\n    pub u8, OpInfo, SetOpInfo: 15, 12;\n}\n\n/// Union representing optional exception handler or chained function entry.\n#[repr(C)]\npub union UNWIND_INFO_0 {\n    /// Address of the exception handler (RVA).\n    pub ExceptionHandler: u32,\n\n    /// Address of a chained function entry.\n    pub FunctionEntry: u32,\n}\n\nbitfield::bitfield! {\n    /// Combines the `Version` and `Flags` fields in a compact format.\n    #[repr(C)]\n    #[derive(Debug, Clone, Copy)]\n    pub struct UNWIND_VERSION_FLAGS(u8);\n\n    /// Unwind info format version.\n    pub u8, Version, SetVersion: 2, 0;\n\n    /// Unwind flags.\n    pub u8, Flags, SetFlags: 7, 3;\n}\n\nbitfield::bitfield! {\n    /// Compact representation of frame register and offset fields.\n    #[repr(C)]\n    #[derive(Debug, Clone, Copy)]\n    pub struct UNWIND_FRAME_INFO(u8);\n\n    /// The register used as the frame pointer.\n    pub u8, FrameRegister, SetFrameRegister: 3, 0;\n\n    /// Offset from the stack pointer to the frame pointer.\n    pub u8, FrameOffset, SetFrameOffset: 7, 4;\n}\n\n/// Structure containing the unwind information of a function.\n#[repr(C)]\npub struct UNWIND_INFO {\n    /// Separate structure containing `Version` and `Flags`.\n    pub VersionFlags: UNWIND_VERSION_FLAGS,\n\n    /// Size of the function prologue in bytes.\n    pub SizeOfProlog: u8,\n\n    /// Number of non-array `UnwindCode` entries.\n    pub CountOfCodes: u8,\n\n    /// Separate structure containing `FrameRegister` and `FrameOffset`.\n    pub FrameInfo: UNWIND_FRAME_INFO,\n\n    /// Array of unwind codes describing specific operations.\n    pub UnwindCode: UNWIND_CODE,\n\n    /// Union containing `ExceptionHandler` or `FunctionEntry`.\n    pub Anonymous: UNWIND_INFO_0,\n\n    /// Optional exception data.\n    pub ExceptionData: u32,\n}\n\n/// Unwind operation codes used by the Windows x64 exception handling model.\n///\n/// For full details, refer to Microsoft documentation:\n/// https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64\n#[repr(u8)]\n#[allow(dead_code)]\npub enum UNWIND_OP_CODES {\n    UWOP_PUSH_NONVOL = 0,\n    UWOP_ALLOC_LARGE = 1,\n    UWOP_ALLOC_SMALL = 2,\n    UWOP_SET_FPREG = 3,\n    UWOP_SAVE_NONVOL = 4,\n    UWOP_SAVE_NONVOL_BIG = 5,\n    UWOP_EPILOG = 6,\n    UWOP_SPARE_CODE = 7,\n    UWOP_SAVE_XMM128 = 8,\n    UWOP_SAVE_XMM128BIG = 9,\n    UWOP_PUSH_MACH_FRAME = 10,\n}\n\nimpl TryFrom<u8> for UNWIND_OP_CODES {\n    type Error = ();\n\n    fn try_from(value: u8) -> Result<Self, Self::Error> {\n        match value {\n            0..=10 => Ok(unsafe { core::mem::transmute::<u8, UNWIND_OP_CODES>(value) }),\n            _ => Err(()),\n        }\n    }\n}"
  },
  {
    "path": "src/util.rs",
    "content": "use core::{ffi::c_void, slice::from_raw_parts};\nuse alloc::vec::Vec;\n\nuse obfstr::obfbytes as b;\nuse dinvk::types::IMAGE_RUNTIME_FUNCTION;\n\nuse crate::ignoring_set_fpreg;\n\n/// Searches for a valid instruction offset in a function.\n///\n/// This scans the function's code region for a `call qword ptr [rip+0]`\n/// instruction sequence and returns the offset *after* the instruction.\n///\n/// # Notes\n///\n/// The searched gadget pattern is `48 FF 15 00 00 00 00`, and the\n/// returned value is `match_offset + 7`.\npub fn find_valid_instruction_offset(\n    module: *mut c_void,\n    runtime: &IMAGE_RUNTIME_FUNCTION,\n) -> Option<u32> {\n    let start = module as u64 + runtime.BeginAddress as u64;\n    let end = module as u64 + runtime.EndAddress as u64;\n    let size = end - start;\n\n    // Find a gadget `call qword ptr [rip+0]`\n    let pattern = b!(&[0x48, 0xFF, 0x15]);\n    unsafe {\n        let bytes = from_raw_parts(start as *const u8, size as usize);\n        if let Some(pos) = memchr::memmem::find(bytes, pattern) {\n            // Returns valid RVA: offset of the gadget inside the function\n            return Some((pos + 7) as u32);\n        }\n    }\n\n    None\n}\n\n/// Scans the code of a module for a given byte pattern, restricted to valid\n/// RUNTIME_FUNCTION regions.\npub fn find_gadget(\n    module: *mut c_void, \n    pattern: &[u8], \n    runtime_table: &[IMAGE_RUNTIME_FUNCTION]\n) -> Option<(*mut u8, u32)> {\n    unsafe {\n        let mut gadgets = runtime_table\n            .iter()\n            .filter_map(|runtime| {\n                let start = module as u64 + runtime.BeginAddress as u64;\n                let end = module as u64 + runtime.EndAddress as u64;\n                let size = end.saturating_sub(start);\n\n                // Read bytes from the function's code region\n                let bytes = from_raw_parts(start as *const u8, size as usize);\n                let pos = memchr::memmem::find(bytes, pattern)?;\n\n                let addr = (start as *mut u8).wrapping_add(pos);\n                let frame_size = ignoring_set_fpreg(module, runtime)?;\n                if frame_size == 0 {\n                    return None;\n                }\n\n                Some((addr, frame_size))\n            })\n            .collect::<Vec<(*mut u8, u32)>>();\n\n        if gadgets.is_empty() {\n            return None;\n        }\n\n        // Shuffle to reduce pattern predictability.\n        shuffle(&mut gadgets);\n\n        gadgets.first().copied()\n    }\n}\n\n/// Scans the current thread's stack to locate the return address that falls within\n/// the range of the `BaseThreadInitThunk` function from `kernel32.dll`.\n#[cfg(feature = \"desync\")]\npub fn find_base_thread_return_address() -> Option<usize> {\n    use dinvk::module::{get_module_address, get_proc_address};\n    use dinvk::{hash::{jenkins3, murmur3}, helper::PE};\n    use crate::types::Unwind;\n\n    unsafe {\n        // Get handle for kernel32.dll\n        let kernel32 = get_module_address(2808682670u32, Some(murmur3));\n        if kernel32.is_null() {\n            return None;\n        }\n\n        // Resolves the address of the BaseThreadInitThunk function\n        let base_thread = get_proc_address(kernel32, 4073232152u32, Some(jenkins3));\n        if base_thread.is_null() {\n            return None;\n        }\n\n        // Calculate the size of the BaseThreadInitThunk function\n        let pe_kernel32 = Unwind::new(PE::parse(kernel32));\n        let size = pe_kernel32.function_size(base_thread)? as usize;\n\n        // Access the TEB and stack limits\n        let teb = dinvk::winapis::NtCurrentTeb();\n        let stack_base = (*teb).Reserved1[1] as usize;\n        let stack_limit = (*teb).Reserved1[2] as usize;\n\n        // Stack scanning begins\n        let base_addr = base_thread as usize;\n        let mut rsp = stack_base - 8;\n        while rsp >= stack_limit {\n            let val = (rsp as *const usize).read();\n\n            // Checks if the return is in the BaseThreadInitThunk range\n            if val >= base_addr && val < base_addr + size {\n                return Some(rsp);\n            }\n\n            rsp -= 8;\n        }\n\n        None\n    }\n}\n\n/// Randomly shuffles the elements of a list in place.\npub fn shuffle<T>(list: &mut [T]) {\n    let mut seed = unsafe { core::arch::x86_64::_rdtsc() };\n    for i in (1..list.len()).rev() {\n        seed = seed.wrapping_mul(1103515245).wrapping_add(12345);\n        let j = seed as usize % (i + 1);\n        list.swap(i, j);\n    }\n}\n"
  },
  {
    "path": "src/uwd.rs",
    "content": "use alloc::{string::String, vec::Vec};\nuse core::ffi::c_void;\n\nuse anyhow::{Context, Result, bail};\nuse obfstr::obfstring as s;\nuse dinvk::module::{get_module_address, get_proc_address};\nuse dinvk::types::IMAGE_RUNTIME_FUNCTION;\nuse dinvk::hash::murmur3;\nuse dinvk::helper::PE;\n\n#[cfg(feature = \"desync\")]\nuse crate::util::find_base_thread_return_address;\nuse crate::util::{find_gadget, shuffle, find_valid_instruction_offset};\nuse crate::types::{Config, Registers, UNWIND_OP_CODES::{self, *}};\nuse crate::types::{UNW_FLAG_CHAININFO, UNW_FLAG_EHANDLER};\nuse crate::types::{UNWIND_CODE, UNWIND_INFO};\nuse crate::types::Unwind;\n\n#[cfg(feature = \"desync\")]\nunsafe extern \"C\" {\n    /// Function responsible for Call Stack Spoofing (Desync)\n    fn Spoof(config: &mut Config) -> *mut c_void;\n}\n\n#[cfg(not(feature = \"desync\"))]\nunsafe extern \"C\" {\n    /// Function responsible for Call Stack Spoofing (Synthetic)\n    fn SpoofSynthetic(config: &mut Config) -> *mut c_void;\n}\n\n/// Invokes a function using a synthetic spoofed call stack.\n///\n/// # Examples\n///\n/// ```\n/// use core::ptr;\n/// use dinvk::module::{get_module_address, get_proc_address};\n/// use uwd::spoof;\n///\n/// let kernel32 = get_module_address(\"kernel32.dll\", None);\n/// let virtual_alloc = get_proc_address(kernel32, \"VirtualAlloc\", None);\n///\n/// let addr = spoof!(\n///     virtual_alloc,\n///     ptr::null_mut::<core::ffi::c_void>(),\n///     1 << 12,\n///     0x3000,\n///     0x04\n/// ).unwrap();\n///\n/// assert!(!addr.is_null());\n/// ```\n#[macro_export]\nmacro_rules! spoof {\n    ($addr:expr, $($arg:expr),+ $(,)?) => {\n        unsafe {\n            $crate::__private::spoof(\n                $addr,\n                &[$(::core::mem::transmute($arg as usize)),*],\n                $crate::SpoofKind::Function,\n            )\n        }\n    };\n}\n\n/// Invokes a Windows native syscall using a spoofed stack.\n///\n/// # Examples\n/// \n/// ```\n/// use core::ptr;\n/// use uwd::{AsPointer, syscall};\n///\n/// let mut addr = ptr::null_mut::<core::ffi::c_void>();\n/// let mut size = (1 << 12) as usize;\n///\n/// let status = syscall!(\n///     \"NtAllocateVirtualMemory\",\n///     -1isize,\n///     addr.as_ptr_mut(),\n///     0,\n///     size.as_ptr_mut(),\n///     0x3000,\n///     0x04\n/// ).unwrap() as i32;\n///\n/// assert_eq!(status, 0);\n/// ```\n#[macro_export]\nmacro_rules! syscall {\n    ($name:expr, $($arg:expr),* $(,)?) => {\n        unsafe {\n            $crate::__private::spoof(\n                core::ptr::null_mut(),\n                &[$(::core::mem::transmute($arg as usize)),*],\n                $crate::SpoofKind::Syscall($name),\n            )\n        }\n    };\n}\n\n#[doc(hidden)]\npub mod __private {\n    use core::ffi::c_void;\n    use super::*;\n\n    /// Performs call stack spoofing in `synthetic` mode.\n    #[cfg(not(feature = \"desync\"))]\n    pub fn spoof(addr: *mut c_void, args: &[*const c_void], kind: SpoofKind) -> Result<*mut c_void> {\n        // Max 11 args\n        if args.len() > 11 {\n            bail!(s!(\"too many arguments\"));\n        }\n\n        // Function pointer must be valid unless syscall spoof\n        if let SpoofKind::Function = kind && addr.is_null() {\n            bail!(s!(\"null function address\"));\n        }\n\n        let mut config = Config::default();\n\n        // Resolve kernelbase\n        let kernelbase = get_module_address(2737729883u32, Some(murmur3));\n\n        // Parse unwind table\n        let pe_kernelbase = Unwind::new(PE::parse(kernelbase));\n        let tables = pe_kernelbase\n            .entries()\n            .context(s!(\n                \"failed to read IMAGE_RUNTIME_FUNCTION entries from .pdata section\"\n            ))?;\n\n        // Resolve APIs\n        let ntdll = get_module_address(2788516083u32, Some(murmur3));\n        if ntdll.is_null() {\n            bail!(s!(\"ntdll.dll not found\"));\n        }\n\n        let kernel32 = get_module_address(2808682670u32, Some(murmur3));\n        let rlt_user_addr = get_proc_address(ntdll, 1578834099u32, Some(murmur3));\n        let base_thread_addr = get_proc_address(kernel32, 4083630997u32, Some(murmur3));\n\n        config.rtl_user_addr = rlt_user_addr;\n        config.base_thread_addr = base_thread_addr;\n\n        // Unwind lookup\n        let pe_ntdll = Unwind::new(PE::parse(ntdll));\n        let rtl_user_runtime = pe_ntdll\n            .function_by_offset(rlt_user_addr as u32 - ntdll as u32)\n            .context(s!(\"RtlUserThreadStart unwind info not found\"))?;\n\n        let pe_kernel32 = Unwind::new(PE::parse(kernel32));\n        let base_thread_runtime = pe_kernel32\n            .function_by_offset(base_thread_addr as u32 - kernel32 as u32)\n            .context(s!(\"BaseThreadInitThunk unwind info not found\"))?;\n\n        // Stack sizes\n        let rtl_user_size = ignoring_set_fpreg(ntdll, rtl_user_runtime)\n            .context(s!(\"RtlUserThreadStart stack size not found\"))?;\n        \n        let base_thread_size = ignoring_set_fpreg(kernel32, base_thread_runtime)\n            .context(s!(\"BaseThreadInitThunk stack size not found\"))?;\n\n        config.rtl_user_thread_size = rtl_user_size as u64;\n        config.base_thread_size = base_thread_size as u64;\n\n        // First prologue\n        let first_prolog = Prolog::find_prolog(kernelbase, tables)\n            .context(s!(\"first prolog not found\"))?;\n        \n        config.first_frame_fp = (first_prolog.frame + first_prolog.offset as u64) as *const c_void;\n        config.first_frame_size = first_prolog.stack_size as u64;\n\n        // Second prologue\n        let second_prolog = Prolog::find_push_rbp(kernelbase, tables)\n            .context(s!(\"second prolog not found\"))?;\n        \n        config.second_frame_fp = (second_prolog.frame + second_prolog.offset as u64) as *const c_void;\n        config.second_frame_size = second_prolog.stack_size as u64;\n        config.rbp_stack_offset = second_prolog.rbp_offset as u64;\n\n        // Gadget: `add rsp, 0x58; ret`\n        let (add_rsp_addr, size) = find_gadget(kernelbase, &[0x48, 0x83, 0xC4, 0x58, 0xC3], tables)\n            .context(s!(\"add rsp gadget not found\"))?;\n        \n        config.add_rsp_gadget = add_rsp_addr as *const c_void;\n        config.add_rsp_frame_size = size as u64;\n\n        // Gadget: `jmp rbx`\n        let (jmp_rbx_addr, size) = find_gadget(kernelbase, &[0xFF, 0x23], tables)\n            .context(s!(\"jmp rbx gadget not found\"))?;\n        \n        config.jmp_rbx_gadget = jmp_rbx_addr as *const c_void;\n        config.jmp_rbx_frame_size = size as u64;\n\n        // Prepare arguments\n        let len = args.len();\n        config.number_args = len as u64;\n        \n        for (i, &arg) in args.iter().take(len).enumerate() {\n            match i {\n                0 => config.arg01 = arg,\n                1 => config.arg02 = arg,\n                2 => config.arg03 = arg,\n                3 => config.arg04 = arg,\n                4 => config.arg05 = arg,\n                5 => config.arg06 = arg,\n                6 => config.arg07 = arg,\n                7 => config.arg08 = arg,\n                8 => config.arg09 = arg,\n                9 => config.arg10 = arg,\n                10 => config.arg11 = arg,\n                _ => break,\n            }\n        }\n\n        // Handle syscall spoofing\n        match kind {\n            SpoofKind::Function => config.spoof_function = addr,\n            SpoofKind::Syscall(name) => {\n                let addr = get_proc_address(ntdll, name, None);\n                if addr.is_null() {\n                    bail!(s!(\"get_proc_address returned null\"));\n                }\n\n                config.is_syscall = true as u32;\n                config.ssn = dinvk::ssn(name, ntdll).context(s!(\"ssn not found\"))?.into();\n                config.spoof_function = dinvk::get_syscall_address(addr)\n                    .context(s!(\"syscall address not found\"))? as *const c_void;\n            }\n        }\n\n        Ok(unsafe { SpoofSynthetic(&mut config) })\n    }\n\n    /// Performs call stack spoofing in `desync` mode.\n    #[cfg(feature = \"desync\")]\n    pub fn spoof(addr: *mut c_void, args: &[*const c_void], kind: SpoofKind) -> Result<*mut c_void> {\n        // Max 11 args\n        if args.len() > 11 {\n            bail!(s!(\"too many arguments\"));\n        }\n\n        // Function pointer must be valid unless syscall spoof\n        if let SpoofKind::Function = kind && addr.is_null() {\n            bail!(s!(\"null function address\"));\n        }\n\n        let mut config = Config::default();\n\n        // Resolve kernelbase\n        let kernelbase = get_module_address(2737729883u32, Some(murmur3));\n\n        // Parse unwind table\n        let pe = Unwind::new(PE::parse(kernelbase));\n        let tables = pe\n            .entries()\n            .context(s!(\n                \"failed to read IMAGE_RUNTIME_FUNCTION entries from .pdata section\"\n            ))?;\n\n        // Locate a return address from BaseThreadInitThunk on the current stack\n        config.return_address = find_base_thread_return_address()\n            .context(s!(\"return address not found\"))? as *const c_void;\n\n        // First prologue\n        let first_prolog = Prolog::find_prolog(kernelbase, tables)\n            .context(s!(\"first prolog not found\"))?;\n        \n        config.first_frame_fp = (first_prolog.frame + first_prolog.offset as u64) as *const c_void;\n        config.first_frame_size = first_prolog.stack_size as u64;\n\n        // Second prologue\n        let second_prolog = Prolog::find_push_rbp(kernelbase, tables)\n            .context(s!(\"second prolog not found\"))?;\n        \n        config.second_frame_fp = (second_prolog.frame + second_prolog.offset as u64) as *const c_void;\n        config.second_frame_size = second_prolog.stack_size as u64;\n        config.rbp_stack_offset = second_prolog.rbp_offset as u64;\n\n        // Gadget: `add rsp, 0x58; ret`\n        let (add_rsp_addr, size) = find_gadget(kernelbase, &[0x48, 0x83, 0xC4, 0x58, 0xC3], tables)\n            .context(s!(\"add rsp gadget not found\"))?;\n\n        config.add_rsp_gadget = add_rsp_addr as *const c_void;\n        config.add_rsp_frame_size = size as u64;\n\n        // Gadget: `jmp rbx`\n        let (jmp_rbx_addr, size) = find_gadget(kernelbase, &[0xFF, 0x23], tables)\n            .context(s!(\"jmp rbx gadget not found\"))?;\n\n        config.jmp_rbx_gadget = jmp_rbx_addr as *const c_void;\n        config.jmp_rbx_frame_size = size as u64;\n\n        // Prepare arguments\n        let len = args.len();\n        config.number_args = len as u64;\n        \n        for (i, &arg) in args.iter().take(len).enumerate() {\n            match i {\n                0 => config.arg01 = arg,\n                1 => config.arg02 = arg,\n                2 => config.arg03 = arg,\n                3 => config.arg04 = arg,\n                4 => config.arg05 = arg,\n                5 => config.arg06 = arg,\n                6 => config.arg07 = arg,\n                7 => config.arg08 = arg,\n                8 => config.arg09 = arg,\n                9 => config.arg10 = arg,\n                10 => config.arg11 = arg,\n                _ => break,\n            }\n        }\n\n        // Handle syscall spoofing\n        match kind {\n            SpoofKind::Function => config.spoof_function = addr,\n            SpoofKind::Syscall(name) => {\n                let ntdll = get_module_address(2788516083u32, Some(murmur3));\n                if ntdll.is_null() {\n                    bail!(s!(\"ntdll.dll not found\"));\n                }\n\n                let addr = get_proc_address(ntdll, name, None);\n                if addr.is_null() {\n                    bail!(s!(\"get_proc_address returned null\"));\n                }\n\n                config.is_syscall = true as u32;\n                config.ssn = dinvk::ssn(name, ntdll).context(s!(\"ssn not found\"))?.into();\n                config.spoof_function = dinvk::get_syscall_address(addr)\n                    .context(s!(\"syscall address not found\"))? as *const c_void;\n            }\n        }\n\n        Ok(unsafe { Spoof(&mut config) })\n    }\n}\n\n/// Metadata extracted from a function prologue that is suitable for spoofing.\n#[derive(Copy, Clone, Default)]\nstruct Prolog {\n    /// Address of the selected function frame.\n    frame: u64,\n\n    /// Total stack space reserved by the function.\n    stack_size: u32,\n\n    /// Offset inside the function where a valid instruction pattern was found.\n    offset: u32,\n\n    /// Offset in the stack where `rbp` is pushed or saved.\n    rbp_offset: u32,\n}\n\nimpl Prolog {\n    /// Finds the first prologue in the unwind table that looks safe for spoofing.\n    ///\n    /// This scans the RUNTIME_FUNCTION entries for a function that:\n    /// - Allocates a stack frame.\n    /// - Has a predictable prologue layout.\n    fn find_prolog(module_base: *mut c_void, runtime_table: &[IMAGE_RUNTIME_FUNCTION]) -> Option<Self> {\n        let mut prologs = runtime_table\n            .iter()\n            .filter_map(|runtime| {\n                let (is_valid, stack_size) = stack_frame(module_base, runtime)?;\n                if !is_valid {\n                    return None;\n                }\n\n                let offset = find_valid_instruction_offset(module_base, runtime)?;\n                let frame = module_base as u64 + runtime.BeginAddress as u64;\n                Some(Self {\n                    frame,\n                    stack_size,\n                    offset,\n                    ..Default::default()\n                })\n            })\n            .collect::<Vec<Self>>();\n\n        if prologs.is_empty() {\n            return None;\n        }\n\n        // Shuffle to reduce pattern predictability.\n        shuffle(&mut prologs);\n\n        prologs.first().copied()\n    }\n\n    /// Finds a prologue that uses `push rbp` and an RBP-based frame.\n    ///\n    /// This is useful when spoofing techniques rely on classic frame-pointer\n    /// based layouts rather than purely RSP-based stack frames.\n    fn find_push_rbp(module_base: *mut c_void, runtime_table: &[IMAGE_RUNTIME_FUNCTION]) -> Option<Self> {\n        let mut prologs = runtime_table\n            .iter()\n            .filter_map(|runtime| {\n                let (rbp_offset, stack_size) = rbp_offset(module_base, runtime)?;\n                if rbp_offset == 0 || stack_size == 0 || stack_size <= rbp_offset {\n                    return None;\n                }\n\n                let offset = find_valid_instruction_offset(module_base, runtime)?;\n                let frame = module_base as u64 + runtime.BeginAddress as u64;\n                Some(\n                    Self {\n                        frame,\n                        stack_size,\n                        offset,\n                        rbp_offset,\n                    }\n                )\n            })\n            .collect::<Vec<Self>>();\n\n        if prologs.is_empty() {\n            return None;\n        }\n\n        // The first frame is often not suitable on many Windows versions.\n        prologs.remove(0);\n\n        // Shuffle to reduce pattern predictability.\n        shuffle(&mut prologs);\n\n        prologs.first().copied()\n    }\n}\n\n/// Determines whether RBP is pushed or saved in a spoof-compatible manner and\n/// computes the total stack size for a function.\n///\n/// This inspects the unwind codes associated with the `IMAGE_RUNTIME_FUNCTION`\n/// entry to determine if the function frame uses a layout suitable for\n/// call stack spoofing.\npub fn rbp_offset(module: *mut c_void, runtime: &IMAGE_RUNTIME_FUNCTION) -> Option<(u32, u32)> {\n    unsafe {\n        let unwind_info = (module as usize + runtime.UnwindData as usize) as *mut UNWIND_INFO;\n        let unwind_code = (unwind_info as *mut u8).add(4) as *mut UNWIND_CODE;\n        let flag = (*unwind_info).VersionFlags.Flags();\n\n        let mut i = 0usize;\n        let mut total_stack = 0u32;\n        let mut rbp_pushed = false;\n        let mut stack_offset = 0;\n\n        while i < (*unwind_info).CountOfCodes as usize {\n            // Accessing `UNWIND_CODE` based on the index\n            let unwind_code = unwind_code.add(i);\n\n            // Information used in operation codes\n            let op_info = (*unwind_code).Anonymous.OpInfo() as usize;\n            let unwind_op = (*unwind_code).Anonymous.UnwindOp();\n\n            match UNWIND_OP_CODES::try_from(unwind_op) {\n                // Saves a non-volatile register on the stack.\n                //\n                // Example: push <reg>\n                Ok(UWOP_PUSH_NONVOL) => {\n                    if Registers::Rsp == op_info {\n                        return None;\n                    }\n\n                    if Registers::Rbp == op_info {\n                        if rbp_pushed {\n                            return None;\n                        }\n\n                        rbp_pushed = true;\n                        stack_offset = total_stack;\n                    }\n\n                    total_stack += 8;\n                    i += 1;\n                }\n\n                // Allocates large space on the stack.\n                // - OpInfo == 0: The next slot contains the /8 size of the allocation (maximum 512 KB - 8).\n                // - OpInfo == 1: The next two slots contain the full size of the allocation (up to 4 GB - 8).\n                //\n                // Example (OpInfo == 0): sub rsp, 0x100 ; Allocates 256 bytes\n                // Example (OpInfo == 1): sub rsp, 0x10000 ; Allocates 65536 bytes (two slots used)\n                Ok(UWOP_ALLOC_LARGE) => {\n                    if (*unwind_code).Anonymous.OpInfo() == 0 {\n                        // Case 1: OpInfo == 0 (Size in 1 slot, divided by 8)\n                        // Multiplies by 8 to the actual value\n\n                        let frame_offset = ((*unwind_code.add(1)).FrameOffset as i32) * 8;\n                        total_stack += frame_offset as u32;\n\n                        // Consumes 2 slots (1 for the instruction, 1 for the size divided by 8)\n                        i += 2\n                    } else {\n                        // Case 2: OpInfo == 1 (Size in 2 slots, 32 bits)\n                        let frame_offset = *(unwind_code.add(1) as *mut i32);\n                        total_stack += frame_offset as u32;\n\n                        // Consumes 3 slots (1 for the instruction, 2 for the full size)\n                        i += 3\n                    }\n                }\n\n                // Allocates small space in the stack.\n                //\n                // Example (OpInfo = 3): sub rsp, 0x20  ; Aloca 32 bytes (OpInfo + 1) * 8\n                Ok(UWOP_ALLOC_SMALL) => {\n                    total_stack += ((op_info + 1) * 8) as u32;\n                    i += 1;\n                }\n\n                // UWOP_SAVE_NONVOL: Saves the contents of a non-volatile register in a specific position on the stack.\n                // - Reg: Name of the saved register.\n                // - FrameOffset: Offset indicating where the value of the register is saved.\n                //\n                // Example: mov [rsp + 0x40], rsi ; Saves the contents of RSI in RSP + 0x40\n                Ok(UWOP_SAVE_NONVOL) => {\n                    if Registers::Rsp == op_info {\n                        return None;\n                    }\n\n                    if Registers::Rbp == op_info {\n                        if rbp_pushed {\n                            return None;\n                        }\n\n                        let offset = (*unwind_code.add(1)).FrameOffset * 8;\n                        stack_offset = total_stack + offset as u32;\n                        rbp_pushed = true;\n                    }\n\n                    i += 2;\n                }\n\n                // Saves a non-volatile register to a stack address with a long offset.\n                // - Reg: Name of the saved register.\n                // - FrameOffset: Long offset indicating where the value of the register is saved.\n                //\n                // Example: mov [rsp + 0x1040], rsi ; Saves the contents of RSI in RSP + 0x1040.\n                Ok(UWOP_SAVE_NONVOL_BIG) => {\n                    if Registers::Rsp == op_info {\n                        return None;\n                    }\n\n                    if Registers::Rbp == op_info {\n                        if rbp_pushed {\n                            return None;\n                        }\n\n                        let offset = *(unwind_code.add(1) as *mut u32);\n                        stack_offset = total_stack + offset;\n                        rbp_pushed = true;\n                    }\n\n                    i += 3;\n                }\n\n                // Return\n                Ok(UWOP_SET_FPREG) => return None,\n\n                // - Reg: Name of the saved XMM register.\n                // - FrameOffset: Offset indicating where the value of the register is saved.\n                Ok(UWOP_SAVE_XMM128) => i += 2,\n\n                // UWOP_SAVE_XMM128BIG: Saves the contents of a non-volatile XMM register to a stack address with a long offset.\n                // - Reg: Name of the saved XMM register.\n                // - FrameOffset: Long offset indicating where the value of the register is saved.\n                //\n                // Example: movaps [rsp + 0x1040], xmm6 ; Saves the contents of XMM6 in RSP + 0x1040.\n                Ok(UWOP_SAVE_XMM128BIG) => i += 3,\n\n                // Reserved code, not currently used.\n                Ok(UWOP_EPILOG) | Ok(UWOP_SPARE_CODE) => i += 1,\n\n                // Push a machine frame. This unwind code is used to record the effect of a hardware interrupt or exception.\n                Ok(UWOP_PUSH_MACH_FRAME) => {\n                    total_stack += if op_info == 0 { 0x40 } else { 0x48 };\n                    i += 1\n                }\n\n                _ => {}\n            }\n        }\n\n        // If there is a chain unwind structure, it too must be processed\n        // recursively and included in the stack size calculation.\n        if (flag & UNW_FLAG_CHAININFO) != 0 {\n            let count = (*unwind_info).CountOfCodes as usize;\n            let index = if count & 1 == 1 { count + 1 } else { count };\n            let runtime = unwind_code.add(index) as *const IMAGE_RUNTIME_FUNCTION;\n            if let Some((_, child_total)) = rbp_offset(module, &*runtime) {\n                total_stack += child_total;\n            } else {\n                return None;\n            }\n        }\n\n        Some((stack_offset, total_stack))\n    }\n}\n\n/// Computes stack frame metadata while rejecting `setfp` frames.\n///\n/// Used when locating suitable prologues for spoofed call frames.\npub fn stack_frame(module: *mut c_void, runtime: &IMAGE_RUNTIME_FUNCTION) -> Option<(bool, u32)> {\n    unsafe {\n        let unwind_info = (module as usize + runtime.UnwindData as usize) as *mut UNWIND_INFO;\n        let unwind_code = (unwind_info as *mut u8).add(4) as *mut UNWIND_CODE;\n        let flag = (*unwind_info).VersionFlags.Flags();\n\n        let mut i = 0usize;\n        let mut set_fpreg_hit = false;\n        let mut total_stack = 0i32;\n        while i < (*unwind_info).CountOfCodes as usize {\n            // Accessing `UNWIND_CODE` based on the index\n            let unwind_code = unwind_code.add(i);\n\n            // Information used in operation codes\n            let op_info = (*unwind_code).Anonymous.OpInfo() as usize;\n            let unwind_op = (*unwind_code).Anonymous.UnwindOp();\n\n            match UNWIND_OP_CODES::try_from(unwind_op) {\n                // Saves a non-volatile register on the stack.\n                //\n                // Example: push <reg>\n                Ok(UWOP_PUSH_NONVOL) => {\n                    if Registers::Rsp == op_info && !set_fpreg_hit {\n                        return None;\n                    }\n\n                    total_stack += 8;\n                    i += 1;\n                }\n\n                // Allocates small space in the stack.\n                //\n                // Example (OpInfo = 3): sub rsp, 0x20  ; Aloca 32 bytes (OpInfo + 1) * 8\n                Ok(UWOP_ALLOC_SMALL) => {\n                    total_stack += ((op_info + 1) * 8) as i32;\n                    i += 1;\n                }\n\n                // Allocates large space on the stack.\n                // - OpInfo == 0: The next slot contains the /8 size of the allocation (maximum 512 KB - 8).\n                // - OpInfo == 1: The next two slots contain the full size of the allocation (up to 4 GB - 8).\n                //\n                // Example (OpInfo == 0): sub rsp, 0x100 ; Allocates 256 bytes\n                // Example (OpInfo == 1): sub rsp, 0x10000 ; Allocates 65536 bytes (two slots used)\n                Ok(UWOP_ALLOC_LARGE) => {\n                    if (*unwind_code).Anonymous.OpInfo() == 0 {\n                        // Case 1: OpInfo == 0 (Size in 1 slot, divided by 8)\n                        // Multiplies by 8 to the actual value\n\n                        let frame_offset = ((*unwind_code.add(1)).FrameOffset as i32) * 8;\n                        total_stack += frame_offset;\n\n                        // Consumes 2 slots (1 for the instruction, 1 for the size divided by 8)\n                        i += 2\n                    } else {\n                        // Case 2: OpInfo == 1 (Size in 2 slots, 32 bits)\n                        let frame_offset = *(unwind_code.add(1) as *mut i32);\n                        total_stack += frame_offset;\n\n                        // Consumes 3 slots (1 for the instruction, 2 for the full size)\n                        i += 3\n                    }\n                }\n\n                // UWOP_SAVE_NONVOL: Saves the contents of a non-volatile register in a specific position on the stack.\n                // - Reg: Name of the saved register.\n                // - FrameOffset: Offset indicating where the value of the register is saved.\n                //\n                // Example: mov [rsp + 0x40], rsi ; Saves the contents of RSI in RSP + 0x40\n                Ok(UWOP_SAVE_NONVOL) => {\n                    if Registers::Rsp == op_info || Registers::Rbp == op_info {\n                        return None;\n                    }\n\n                    i += 2;\n                }\n\n                // Saves a non-volatile register to a stack address with a long offset.\n                // - Reg: Name of the saved register.\n                // - FrameOffset: Long offset indicating where the value of the register is saved.\n                //\n                // Example: mov [rsp + 0x1040], rsi ; Saves the contents of RSI in RSP + 0x1040.\n                Ok(UWOP_SAVE_NONVOL_BIG) => {\n                    if Registers::Rsp == op_info || Registers::Rbp == op_info {\n                        return None;\n                    }\n\n                    i += 3;\n                }\n\n                // Saves the contents of a non-volatile XMM register on the stack.\n                // - Reg: Name of the saved XMM register.\n                // - FrameOffset: Offset indicating where the value of the register is saved.\n                //\n                // Example: movaps [rsp + 0x20], xmm6 ; Saves the contents of XMM6 in RSP + 0x20.\n                Ok(UWOP_SAVE_XMM128) => i += 2,\n\n                // UWOP_SAVE_XMM128BIG: Saves the contents of a non-volatile XMM register to a stack address with a long offset.\n                // - Reg: Name of the saved XMM register.\n                // - FrameOffset: Long offset indicating where the value of the register is saved.\n                //\n                // Example: movaps [rsp + 0x1040], xmm6 ; Saves the contents of XMM6 in RSP + 0x1040.\n                Ok(UWOP_SAVE_XMM128BIG) => i += 3,\n\n                // UWOP_SET_FPREG: Marks use of register as stack base (e.g. RBP).\n                // Ignore if not RBP, has EH handler or chained unwind.\n                // Subtract `FrameOffset << 4` from the stack total.\n                Ok(UWOP_SET_FPREG) => {\n                    if (flag & UNW_FLAG_EHANDLER) != 0 && (flag & UNW_FLAG_CHAININFO) != 0 {\n                        return None;\n                    }\n\n                    if (*unwind_info).FrameInfo.FrameRegister() != Registers::Rbp as u8 {\n                        return None;\n                    }\n\n                    set_fpreg_hit = true;\n                    let offset = ((*unwind_info).FrameInfo.FrameOffset() as i32) << 4;\n                    total_stack -= offset;\n                    i += 1\n                }\n\n                // Reserved code, not currently used.\n                Ok(UWOP_EPILOG) | Ok(UWOP_SPARE_CODE) => i += 1,\n\n                // Push a machine frame. This unwind code is used to record the effect of a hardware interrupt or exception.\n                Ok(UWOP_PUSH_MACH_FRAME) => {\n                    total_stack += if op_info == 0 { 0x40 } else { 0x48 };\n                    i += 1\n                }\n                _ => {}\n            }\n        }\n\n        // If there is a chain unwind structure, it too must be processed\n        // recursively and included in the stack size calculation.\n        if (flag & UNW_FLAG_CHAININFO) != 0 {\n            let count = (*unwind_info).CountOfCodes as usize;\n            let index = if count & 1 == 1 { count + 1 } else { count };\n            let runtime = unwind_code.add(index) as *const IMAGE_RUNTIME_FUNCTION;\n            if let Some((chained_fpreg_hit, chained_stack)) = stack_frame(module, &*runtime) {\n                total_stack += chained_stack as i32;\n                set_fpreg_hit |= chained_fpreg_hit;\n            } else {\n                return None;\n            }\n        }\n\n        Some((set_fpreg_hit, total_stack as u32))\n    }\n}\n\n/// Computes the total stack frame size of a function while ignoring any `setfp` frames. \n/// Useful for identifying spoof-compatible RUNTIME_FUNCTION entries.\npub fn ignoring_set_fpreg(module: *mut c_void, runtime: &IMAGE_RUNTIME_FUNCTION) -> Option<u32> {\n    unsafe {\n        let unwind_info = (module as usize + runtime.UnwindData as usize) as *mut UNWIND_INFO;\n        let unwind_code = (unwind_info as *mut u8).add(4) as *mut UNWIND_CODE;\n        let flag = (*unwind_info).VersionFlags.Flags();\n\n        let mut i = 0usize;\n        let mut total_stack = 0u32;\n        while i < (*unwind_info).CountOfCodes as usize {\n            // Accessing `UNWIND_CODE` based on the index\n            let unwind_code = unwind_code.add(i);\n\n            // Information used in operation codes\n            let op_info = (*unwind_code).Anonymous.OpInfo() as usize;\n            let unwind_op = (*unwind_code).Anonymous.UnwindOp();\n\n            match UNWIND_OP_CODES::try_from(unwind_op) {\n                // Saves a non-volatile register on the stack.\n                //\n                // Example: push <reg>\n                Ok(UWOP_PUSH_NONVOL) => {\n                    if Registers::Rsp == op_info {\n                        return None;\n                    }\n\n                    total_stack += 8;\n                    i += 1;\n                }\n\n                // Allocates small space in the stack.\n                //\n                // Example (OpInfo = 3): sub rsp, 0x20  ; Aloca 32 bytes (OpInfo + 1) * 8\n                Ok(UWOP_ALLOC_SMALL) => {\n                    total_stack += ((op_info + 1) * 8) as u32;\n                    i += 1;\n                }\n\n                // Allocates large space on the stack.\n                // - OpInfo == 0: The next slot contains the /8 size of the allocation (maximum 512 KB - 8).\n                // - OpInfo == 1: The next two slots contain the full size of the allocation (up to 4 GB - 8).\n                //\n                // Example (OpInfo == 0): sub rsp, 0x100 ; Allocates 256 bytes\n                // Example (OpInfo == 1): sub rsp, 0x10000 ; Allocates 65536 bytes (two slots used)\n                Ok(UWOP_ALLOC_LARGE) => {\n                    if (*unwind_code).Anonymous.OpInfo() == 0 {\n                        // Case 1: OpInfo == 0 (Size in 1 slot, divided by 8)\n                        // Multiplies by 8 to the actual value\n\n                        let frame_offset = ((*unwind_code.add(1)).FrameOffset as i32) * 8;\n                        total_stack += frame_offset as u32;\n\n                        // Consumes 2 slots (1 for the instruction, 1 for the size divided by 8)\n                        i += 2\n                    } else {\n                        // Case 2: OpInfo == 1 (Size in 2 slots, 32 bits)\n                        let frame_offset = *(unwind_code.add(1) as *mut i32);\n                        total_stack += frame_offset as u32;\n\n                        // Consumes 3 slots (1 for the instruction, 2 for the full size)\n                        i += 3\n                    }\n                }\n\n                // UWOP_SAVE_NONVOL: Saves the contents of a non-volatile register in a specific position on the stack.\n                // - Reg: Name of the saved register.\n                // - FrameOffset: Offset indicating where the value of the register is saved.\n                //\n                // Example: mov [rsp + 0x40], rsi ; Saves the contents of RSI in RSP + 0x40\n                Ok(UWOP_SAVE_NONVOL) => {\n                    if Registers::Rsp == op_info {\n                        return None;\n                    }\n\n                    i += 2;\n                }\n\n                // Saves a non-volatile register to a stack address with a long offset.\n                // - Reg: Name of the saved register.\n                // - FrameOffset: Long offset indicating where the value of the register is saved.\n                //\n                // Example: mov [rsp + 0x1040], rsi ; Saves the contents of RSI in RSP + 0x1040.\n                Ok(UWOP_SAVE_NONVOL_BIG) => {\n                    if Registers::Rsp == op_info {\n                        return None;\n                    }\n\n                    i += 3;\n                }\n\n                // Saves the contents of a non-volatile XMM register on the stack.\n                // - Reg: Name of the saved XMM register.\n                // - FrameOffset: Offset indicating where the value of the register is saved.\n                //\n                // Example: movaps [rsp + 0x20], xmm6 ; Saves the contents of XMM6 in RSP + 0x20.\n                Ok(UWOP_SAVE_XMM128) => i += 2,\n\n                // UWOP_SAVE_XMM128BIG: Saves the contents of a non-volatile XMM register to a stack address with a long offset.\n                // - Reg: Name of the saved XMM register.\n                // - FrameOffset: Long offset indicating where the value of the register is saved.\n                //\n                // Example: movaps [rsp + 0x1040], xmm6 ; Saves the contents of XMM6 in RSP + 0x1040.\n                Ok(UWOP_SAVE_XMM128BIG) => i += 3,\n\n                // Ignoring.\n                Ok(UWOP_SET_FPREG) => i += 1,\n\n                // Reserved code, not currently used.\n                Ok(UWOP_EPILOG) | Ok(UWOP_SPARE_CODE) => i += 1,\n\n                // Push a machine frame. This unwind code is used to record the effect of a hardware interrupt or exception.\n                Ok(UWOP_PUSH_MACH_FRAME) => {\n                    total_stack += if op_info == 0 { 0x40 } else { 0x48 };\n                    i += 1\n                }\n                _ => {}\n            }\n        }\n\n        // If there is a chain unwind structure, it too must be processed\n        // recursively and included in the stack size calculation.\n        if (flag & UNW_FLAG_CHAININFO) != 0 {\n            let count = (*unwind_info).CountOfCodes as usize;\n            let index = if count & 1 == 1 { count + 1 } else { count };\n            let runtime = unwind_code.add(index) as *const IMAGE_RUNTIME_FUNCTION;\n            if let Some(chained_stack) = ignoring_set_fpreg(module, &*runtime) {\n                total_stack += chained_stack;\n            } else {\n                return None;\n            }\n        }\n\n        Some(total_stack)\n    }\n}\n\n/// Trait for safely converting any reference or mutable reference into a raw\n/// pointer usable in spoofing routines.\npub trait AsPointer {\n    /// Returns a raw immutable pointer to `self`.\n    fn as_ptr_const(&self) -> *const c_void;\n\n    /// Returns a raw mutable pointer to `self`.\n    fn as_ptr_mut(&mut self) -> *mut c_void;\n}\n\nimpl<T> AsPointer for T {\n    #[inline(always)]\n    fn as_ptr_const(&self) -> *const c_void {\n        self as *const _ as *const c_void\n    }\n\n    #[inline(always)]\n    fn as_ptr_mut(&mut self) -> *mut c_void {\n        self as *mut _ as *mut c_void\n    }\n}\n\n/// Specifies the spoofing mode used by the engine.\npub enum SpoofKind<'a> {\n    /// Spoofs a direct function call.\n    Function,\n\n    /// Spoofs a syscall using its name.\n    Syscall(&'a str),\n}\n\n#[cfg(test)]\nmod tests {\n    use core::ptr;\n    use alloc::boxed::Box;\n    use super::*;\n\n    #[test]\n    fn test_spoof() -> Result<(), Box<dyn core::error::Error>> {\n        let kernel32 = get_module_address(\"kernel32.dll\", None);\n        let virtual_alloc = get_proc_address(kernel32, \"VirtualAlloc\", None);   \n        let addr = spoof!(virtual_alloc, ptr::null_mut::<c_void>(), 1 << 12, 0x3000, 0x04)?;\n        assert_ne!(addr, ptr::null_mut());\n\n        Ok(())\n    }\n\n    #[test]\n    fn test_syscall() -> Result<(), Box<dyn core::error::Error>> {\n        let mut addr = ptr::null_mut::<c_void>();\n        let mut size = (1 << 12) as usize;\n        let status = syscall!(\"NtAllocateVirtualMemory\", -1isize, addr.as_ptr_mut(), 0, size.as_ptr_mut(), 0x3000, 0x04)? as i32;\n        assert_eq!(status, 0);\n\n        Ok(())\n    }\n}"
  },
  {
    "path": "taplo.toml",
    "content": "[formatting]\ncrlf = true\narray_auto_expand = true\n"
  }
]