15 Commits
repl ... master

Author SHA1 Message Date
ae679b62be Format code 2020-08-01 04:47:59 +07:00
e2eac767e0 One binary setup 2020-08-01 04:47:38 +07:00
967684f140 Fix SSDT entry
SSDT entries can be negative, so signed int is used
2020-07-24 21:44:29 +07:00
2d7576b1e2 update 2020-07-21 17:07:52 +07:00
60513ee142 Add driver_irp and test_load 2020-07-07 23:43:29 +07:00
09114848fc Update eprocess and ethread scan 2020-07-03 02:08:27 +07:00
a154c71f9b Run rustfmt 2020-07-02 02:47:15 +07:00
b1c3107c74 Create object in object.rs 2020-07-02 02:44:01 +07:00
4e67e10aee Update working status for Windows 7 2020-07-01 00:01:12 +07:00
8cb553eb11 Update base code for windows 7, 8, 8.1
Because the tag is different in lower version of Windows, need to
change the tag in scan algorithm

4b29cf1986/volatility/framework/plugins/windows/poolscanner.py (L229)
2020-06-30 04:09:13 +07:00
abb7a70b72 Update
- Driver scan device tree and output more data
- Print ssdt scanning base on kernel modules traversing
2020-06-23 18:27:24 +07:00
199c3ca10b Update cargo dependencies
And ready for a lisp repl
2020-06-22 22:31:55 +07:00
0350ec46d9 Scan unloaded module/driver
By reversing MmLocateUnloadedDriver, we can know the algorithm
to extract name/start/end of unloaded drivers
2020-06-22 22:30:35 +07:00
5619048a4a Update lpus feature
Traverse scan
- PsActiveProcessHead
- PsLoadedModuleList
- KiProcessListHead
- HandleTableList

pdb_store has dt(struct) to display struct
2020-06-22 17:45:06 +07:00
8cf91aef79 Update scan for kernel modules and driver
Scan kernel modules
Driver scan major functions' address
2020-06-22 14:52:15 +07:00
27 changed files with 3919 additions and 1535 deletions

View File

@ -1,2 +1,5 @@
[target.x86_64-pc-windows-msvc] [target.x86_64-pc-windows-msvc]
# CRT static to make run on machine without VC++
# https://github.com/rust-lang/rust/pull/66801#issuecomment-558947376
# >> "-Clink-args=/subsystem:console,5.02"
rustflags = ["-Ctarget-feature=+crt-static"] rustflags = ["-Ctarget-feature=+crt-static"]

505
Cargo.lock generated
View File

@ -1,5 +1,13 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.26" version = "1.0.26"
@ -16,6 +24,26 @@ dependencies = [
"xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "arrayref"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "arrayvec"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.0.0" version = "1.0.0"
@ -31,11 +59,61 @@ name = "bitflags"
version = "1.2.1" version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "blake2b_simd"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bstr"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-automata 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.2.0" version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "0.5.4" version = "0.5.4"
@ -69,6 +147,25 @@ dependencies = [
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "clap"
version = "2.33.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.6.4" version = "0.6.4"
@ -83,11 +180,83 @@ name = "core-foundation-sys"
version = "0.6.2" version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "csv"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bstr 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "csv-core"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dirs"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dirs-next"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"dirs-sys-next 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "dtoa" name = "dtoa"
version = "0.4.5" version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.22" version = "0.8.22"
@ -96,6 +265,11 @@ dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "fallible-iterator" name = "fallible-iterator"
version = "0.1.6" version = "0.1.6"
@ -174,6 +348,14 @@ dependencies = [
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "generic-array"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.1.14" version = "0.1.14"
@ -349,13 +531,26 @@ version = "0.1.0"
dependencies = [ dependencies = [
"app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parse_int 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pdb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "pdb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"reqwest 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-embed 5.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustyline 6.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)",
"vergen 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"widestring 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "widestring 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "matches" name = "matches"
version = "0.1.8" version = "0.1.8"
@ -436,6 +631,18 @@ dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "nix"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "nom" name = "nom"
version = "4.2.3" version = "4.2.3"
@ -480,6 +687,11 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "openssl" name = "openssl"
version = "0.10.28" version = "0.10.28"
@ -510,6 +722,14 @@ dependencies = [
"vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "parse_int"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "pdb" name = "pdb"
version = "0.5.0" version = "0.5.0"
@ -525,6 +745,45 @@ name = "percent-encoding"
version = "2.1.0" version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pest"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ucd-trie 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pest_derive"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_generator 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pest_generator"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_meta 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pest_meta"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "0.4.8" version = "0.4.8"
@ -563,6 +822,19 @@ name = "ppv-lite86"
version = "0.2.6" version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "prettytable-rs"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "0.4.30" version = "0.4.30"
@ -637,6 +909,24 @@ name = "redox_syscall"
version = "0.1.56" version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_users"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-automata"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "remove_dir_all" name = "remove_dir_all"
version = "0.5.2" version = "0.5.2"
@ -679,6 +969,46 @@ dependencies = [
"winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "rust-argon2"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
"constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rust-embed"
version = "5.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rust-embed-impl 5.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-embed-utils 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rust-embed-impl"
version = "5.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-embed-utils 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rust-embed-utils"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.2.3" version = "0.2.3"
@ -687,11 +1017,37 @@ dependencies = [
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "rustyline"
version = "6.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"dirs-next 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8parse 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.2" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "schannel" name = "schannel"
version = "0.1.17" version = "0.1.17"
@ -701,6 +1057,11 @@ dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "scroll" name = "scroll"
version = "0.9.2" version = "0.9.2"
@ -778,6 +1139,17 @@ dependencies = [
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "sha-1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "shell32-sys" name = "shell32-sys"
version = "0.1.2" version = "0.1.2"
@ -802,6 +1174,11 @@ name = "sourcefile"
version = "0.1.4" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "syn" name = "syn"
version = "0.15.44" version = "0.15.44"
@ -835,6 +1212,24 @@ dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "term"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.1.42" version = "0.1.42"
@ -893,6 +1288,16 @@ name = "try-lock"
version = "0.2.2" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "typenum"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ucd-trie"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "unicase" name = "unicase"
version = "2.6.0" version = "2.6.0"
@ -922,6 +1327,11 @@ name = "unicode-segmentation"
version = "1.6.0" version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.1.0" version = "0.1.0"
@ -942,6 +1352,11 @@ dependencies = [
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "utf8parse"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "0.7.4" version = "0.7.4"
@ -952,6 +1367,20 @@ name = "vcpkg"
version = "0.2.8" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vergen"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.1.5" version = "0.1.5"
@ -962,6 +1391,21 @@ name = "version_check"
version = "0.9.1" version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "walkdir"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "want" name = "want"
version = "0.3.0" version = "0.3.0"
@ -1102,6 +1546,14 @@ name = "winapi-i686-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
@ -1130,21 +1582,42 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata] [metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" "checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c"
"checksum app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d" "checksum app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d"
"checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
"checksum bstr 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931"
"checksum bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" "checksum bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742"
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
"checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" "checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1"
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" "checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" "checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" "checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01"
"checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" "checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" "checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
"checksum csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279"
"checksum csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
"checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
"checksum dirs-next 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cbcf9241d9e8d106295bd496bbe2e9cffd5fa098f2a8c9e2bbcbf09773c11a8"
"checksum dirs-sys-next 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c60f7b8a8953926148223260454befb50c751d3c50e1c178c4fd1ace4083c9a"
"checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" "checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
"checksum encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
"checksum encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28" "checksum encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
"checksum fallible-iterator 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eb7217124812dc5672b7476d0c2d20cfe9f7c0f1ba0904b674a9762a0212f72e" "checksum fallible-iterator 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eb7217124812dc5672b7476d0c2d20cfe9f7c0f1ba0904b674a9762a0212f72e"
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
@ -1157,6 +1630,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" "checksum futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6"
"checksum futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" "checksum futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27"
"checksum futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" "checksum futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
"checksum h2 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9433d71e471c1736fd5a61b671fc0b148d7a2992f666c958d03cd8feb3b88d1" "checksum h2 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9433d71e471c1736fd5a61b671fc0b148d7a2992f666c958d03cd8feb3b88d1"
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
@ -1175,6 +1649,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
"checksum memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978" "checksum memchr 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978"
"checksum mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" "checksum mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
@ -1183,22 +1658,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" "checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e"
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
"checksum nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" "checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" "checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
"checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" "checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c"
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
"checksum openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)" = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52" "checksum openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)" = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52"
"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
"checksum openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)" = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" "checksum openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)" = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986"
"checksum parse_int 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "82db48cac18f0963b10ddad303fa88447b95bbe0e6dbe3385f98402b63d0cc48"
"checksum pdb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6b57b7067dc9dbd04b1305bb51a8ae7d0fc645956a73b6cb2807dd956ab6929" "checksum pdb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6b57b7067dc9dbd04b1305bb51a8ae7d0fc645956a73b6cb2807dd956ab6929"
"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
"checksum pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
"checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
"checksum pest_generator 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
"checksum pest_meta 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
"checksum pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" "checksum pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c"
"checksum pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f" "checksum pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f"
"checksum pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" "checksum pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae"
"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
"checksum prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd04b170004fa2daccf418a7f8253aaf033c27760b5f225889024cf66d7ac2e"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" "checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548"
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
@ -1208,11 +1691,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
"checksum redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431"
"checksum regex-automata 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4"
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
"checksum reqwest 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0e798e19e258bf6c30a304622e3e9ac820e483b06a1857a026e1f109b113fe4" "checksum reqwest 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0e798e19e258bf6c30a304622e3e9ac820e483b06a1857a026e1f109b113fe4"
"checksum rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
"checksum rust-embed 5.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "213acf1bc5a6dfcd70b62db1e9a7d06325c0e73439c312fcb8599d456d9686ee"
"checksum rust-embed-impl 5.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7903c2cf599db8f310b392332f38367ca4acc84420fa1aee3536299f433c10d5"
"checksum rust-embed-utils 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97655158074ccb2d2cfb1ccb4c956ef0f4054e43a2c1e71146d4991e6961e105"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum rustyline 6.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3358c21cbbc1a751892528db4e1de4b7a2b6a73f001e215aaba97d712cfa9777"
"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" "checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
"checksum schannel 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "507a9e6e8ffe0a4e0ebb9a10293e62fdf7657c06f1b8bb07a8fcf697d2abf295" "checksum schannel 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "507a9e6e8ffe0a4e0ebb9a10293e62fdf7657c06f1b8bb07a8fcf697d2abf295"
"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
"checksum scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383" "checksum scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383"
"checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb" "checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb"
"checksum security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df" "checksum security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df"
@ -1222,30 +1714,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" "checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
"checksum serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226" "checksum serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226"
"checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" "checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
"checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
"checksum shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c" "checksum shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c"
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" "checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc"
"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" "checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5"
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
"checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
"checksum tokio 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8fdd17989496f49cdc57978c96f0c9fe5e4a58a8bddc6813c449a4624f6a030b" "checksum tokio 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8fdd17989496f49cdc57978c96f0c9fe5e4a58a8bddc6813c449a4624f6a030b"
"checksum tokio-tls 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7bde02a3a5291395f59b06ec6945a3077602fac2b07eeeaf0dee2122f3619828" "checksum tokio-tls 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7bde02a3a5291395f59b06ec6945a3077602fac2b07eeeaf0dee2122f3619828"
"checksum tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" "checksum tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930"
"checksum tower-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" "checksum tower-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860"
"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382"
"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
"checksum ucd-trie 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" "checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
"checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" "checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4"
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" "checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
"checksum utf8parse 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
"checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" "checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a"
"checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" "checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
"checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
"checksum vergen 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ce50d8996df1f85af15f2cd8d33daae6e479575123ef4314a51a70a230739cb"
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" "checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
"checksum want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" "checksum want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
"checksum wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c" "checksum wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c"
@ -1262,6 +1766,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" "checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"

View File

@ -18,3 +18,15 @@ widestring = "0.4.0"
winapi = { version = "0.3.8", features = ["libloaderapi", "processthreadsapi", "winbase", "securitybaseapi", "handleapi", "winnt", "winreg", "fileapi", "ioapiset", "winioctl", "errhandlingapi", "sysinfoapi"] } winapi = { version = "0.3.8", features = ["libloaderapi", "processthreadsapi", "winbase", "securitybaseapi", "handleapi", "winnt", "winreg", "fileapi", "ioapiset", "winioctl", "errhandlingapi", "sysinfoapi"] }
reqwest = { version = "0.10.1", features = ["blocking"] } reqwest = { version = "0.10.1", features = ["blocking"] }
serde_json = "1.0.55" serde_json = "1.0.55"
parse_int = "0.4.0"
# repl dependencies
rustyline = "6.2.0"
pest = "2.1.3"
pest_derive = "2.1.0"
# others
prettytable-rs = "^0.8"
rust-embed="5.6.0"
clap="2.33.1"
[build-dependencies]
vergen="3.1.0"

View File

@ -1,6 +1,16 @@
# LPUS (A live pool-tag scanning solution) # LPUS (A live pool-tag scanning solution)
This is the frontend to the live pool tag scanning solution, the backend is a driver (which is now closed source). This is the frontend to the live pool tag scanning solution, the backend is a
driver (which is now closed source).
Works on Windows 7 and above (Vista not tested, but 7 ok and 10 ok), and on x64
systems only. (I hardcoded the address as u64 so only 64 systems should run this).
> The binary is runable, without crashing. But I still need to add some
manual instructions on referencing the structs and offset on some places.
> Windows 10, versions 2018, 2019 and 2020 is tested and works.
Windows XP is not supported: Windows XP Win32Api is missing here and there.
## How this works ## How this works
@ -21,7 +31,7 @@ use lpus::{
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let mut driver = DriverState::new(); let mut driver = DriverState::new();
println!("NtLoadDriver() -> 0x{:x}", driver.startup()); println!("NtLoadDriver() -> 0x{:x}", driver.startup());
driver.scan_pool(b"Tag ", |pool_addr, header, data_addr| { driver.scan_pool(b"Tag ", "_STRUCT_NAME", |pool_addr, header, data_addr| {
})?; })?;
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown()); println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
} }
@ -33,3 +43,67 @@ Parsing the struct data is up to you.
You can use `driver.deref_addr(addr, &value)` to dereference an address in kernel space You can use `driver.deref_addr(addr, &value)` to dereference an address in kernel space
and `driver.pdb_store.get_offset_r("offset")?` to get an offset from PDB file. and `driver.pdb_store.get_offset_r("offset")?` to get an offset from PDB file.
We also have a set of functions for scanning a specific tag/object.
- `pub fn scan_eprocess(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn scan_file(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn scan_ethread(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn scan_mutant(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn scan_driver(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn scan_kernel_module(driver: &DriverState) -> BoxResult<Vec<Value>>`
And a list traversing the kernel object:
- `pub fn traverse_loadedmodulelist(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn traverse_activehead(driver: &DriverState) -> BoxResult<Vec<Value>>`
- missing symbols `pub fn traverse_afdendpoint(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn traverse_kiprocesslist(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn traverse_handletable(driver: &DriverState) -> BoxResult<Vec<Value>>`
- `pub fn traverse_unloadeddrivers(driver: &DriverState) -> BoxResult<Vec<Value>>`
## Things to note
Right now, we only have one symbol file of ntoskrnl.exe. While we may need more
symbols, kernel32.sys, win32k.sys, tcpis.sys... This will be a future update
where symbols are combined into one big `HashMap` but still retain the module. I
haven't tested the debug symbols of others binary, I wonder if the PDB file even
exists.
The pdb file is not restricted in ntoskrnl.exe, I might need to split to a
smaller module or such.
Also the symbols list is parsed directly from the PDB file, but some structs
(like the callback routine members or network structs) are missing. Right now a
simple hardcoded to add in a struct member is used, but it would break if the
OS running have a different layout.
The HashMap of symbols/struct is now using string and u32 to store member
offset and types, this should be changed into something that would be type-safe
and more functional.
I also follow a few Volatility implementation on Rootkit, The art of Memory
forensics Chapter 13. Scanning in Windows 10 yields promising result, though I
haven't tested on any malware to see if we can have the "same" result.
At the pace of development, I seperate the binary to functionalities for
testing, I would add a CLI and a REPL.
One last thing, the backend doesn't have any check on address referencing, so
one may get a blue screen, eventhough I tried to avoid it, I'm not 100% sure it
would not crash the system.
## Future works
- [ ] An interactive repl (1)
- [ ] More kernel modules symbols (2)
- [ ] Implementation of more technique (reference Volatility here)
- [ ] Quick and easy way to add manual struct, symbols (3)
(1) This is quite hard to work out, because we have to make the *types* works.
The currently chosen repl is based on Lisp, because lisp is cool. If the repl
is online, we can combine everything into one binary.
(2) We may need to download it all and combine to one `HashMap`, with their
types as a specific struct. (Try to avoid string).
(3) Have no idea on this.

8
build.rs Normal file
View File

@ -0,0 +1,8 @@
extern crate vergen;
use vergen::{generate_cargo_keys, ConstantsFlags};
fn main() {
let flags = ConstantsFlags::all();
generate_cargo_keys(flags).expect("Unable to generate the cargo keys!");
}

7
other/to_epoch.py Normal file
View File

@ -0,0 +1,7 @@
windows_epoch_diff = 11644473600000 * 10000
filetime = 132380977838542980
process_time_epoch = (filetime - windows_epoch_diff) // 10000
print(process_time_epoch)

View File

@ -1,7 +1,7 @@
use std::rc::Rc;
use std::ops::{Add, AddAssign, Sub, SubAssign};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt; use std::fmt;
use std::ops::{Add, AddAssign, Sub, SubAssign};
use std::rc::Rc;
// pub struct Object { // pub struct Object {
// name: String, // name: String,
@ -43,41 +43,40 @@ impl Address {
} }
} }
fn deref<F>(&self, resolver: &F) -> Address fn deref<F>(&self, resolver: &F) -> Address
where F: Fn(u64) -> u64 { where
F: Fn(u64) -> u64,
{
match &self.pointer { match &self.pointer {
Some(p) => { Some(p) => {
let addr = p.deref(resolver); let addr = p.deref(resolver);
// println!("deref: {} -> {}; resolve: 0x{:x}", self, addr, addr.base + addr.offset); // println!("deref: {} -> {}; resolve: 0x{:x}", self, addr, addr.base + addr.offset);
let base = let base = if addr.base != 0 {
if addr.base != 0 { resolver(addr.base + addr.offset)
resolver(addr.base + addr.offset) } else {
} else { 0
0 };
};
Address { Address {
base: base, base: base,
pointer: None, pointer: None,
offset: self.offset, offset: self.offset,
} }
},
None => {
Address {
base: self.base,
pointer: None,
offset: self.offset,
}
} }
None => Address {
base: self.base,
pointer: None,
offset: self.offset,
},
} }
} }
pub fn get<F>(&self, resolver: &F) -> u64 pub fn get<F>(&self, resolver: &F) -> u64
where F: Fn(u64) -> u64 { where
F: Fn(u64) -> u64,
{
if self.pointer.is_some() { if self.pointer.is_some() {
self.deref(resolver).get(resolver) self.deref(resolver).get(resolver)
} } else if self.base == 0 {
else if self.base == 0 {
0 0
} } else {
else {
self.base + self.offset self.base + self.offset
} }
} }
@ -136,9 +135,10 @@ impl SubAssign<u64> for Address {
impl PartialEq for Address { impl PartialEq for Address {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.pointer.is_none() && other.pointer.is_none() self.pointer.is_none()
&& self.base == other.base && other.pointer.is_none()
&& self.offset == other.offset && self.base == other.base
&& self.offset == other.offset
} }
} }
@ -146,8 +146,7 @@ impl PartialOrd for Address {
fn partial_cmp(&self, other: &Address) -> Option<Ordering> { fn partial_cmp(&self, other: &Address) -> Option<Ordering> {
if self.pointer.is_some() || other.pointer.is_some() { if self.pointer.is_some() || other.pointer.is_some() {
None None
} } else {
else {
let this = self.base + self.offset; let this = self.base + self.offset;
let that = other.base + other.offset; let that = other.base + other.offset;
Some(this.cmp(&that)) Some(this.cmp(&that))
@ -159,9 +158,10 @@ impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(p) = &self.pointer { if let Some(p) = &self.pointer {
write!(f, "*({}) + 0x{:x}", *p, self.offset) write!(f, "*({}) + 0x{:x}", *p, self.offset)
} } else if self.offset != 0 {
else {
write!(f, "0x{:x} + 0x{:x}", self.base, self.offset) write!(f, "0x{:x} + 0x{:x}", self.base, self.offset)
} else {
write!(f, "0x{:x}", self.base)
} }
} }
} }
@ -171,7 +171,7 @@ impl Clone for Address {
Address { Address {
base: self.base, base: self.base,
pointer: self.pointer.clone(), pointer: self.pointer.clone(),
offset: self.offset offset: self.offset,
} }
} }
} }

82
src/bin/driver_irp.rs Normal file
View File

@ -0,0 +1,82 @@
use std::error::Error;
use parse_int::parse;
use rustyline::error::ReadlineError;
use rustyline::Editor;
use lpus::{driver_state::DriverState, get_irp_name, scan_driver, scan_kernel_module};
fn main() -> Result<(), Box<dyn Error>> {
let mut driver = DriverState::new();
if !driver.is_supported() {
return Err(format!(
"Windows version {:?} is not supported",
driver.windows_ffi.short_version
)
.into());
}
println!("NtLoadDriver() -> 0x{:x}", driver.startup());
let drivers = scan_driver(&driver).unwrap_or(Vec::new());
let kmods = scan_kernel_module(&driver).unwrap_or(Vec::new());
for d in drivers.iter() {
println!("{} {}", d["address"], d["device"]);
}
let mut rl = Editor::<()>::new();
loop {
let readline = rl.readline("irp> ");
match readline {
Ok(line) => {
rl.add_history_entry(line.as_str());
for d in drivers.iter() {
if d["address"].as_str().unwrap_or("") == line {
println!("{:#}", d);
for (idx, addr_) in d["major_function"]
.as_array()
.unwrap_or(&Vec::new())
.iter()
.enumerate()
{
let addr: u64 = addr_.as_str().and_then(|x| parse(x).ok()).unwrap_or(0);
let mut owner = "(??)";
println!("{} {}", addr, get_irp_name(idx));
for kmod in kmods.iter() {
let base: u64 = kmod["dllbase"]
.as_str()
.and_then(|x| parse(x).ok())
.unwrap_or(0);
let size: u64 = kmod["size"]
.as_str()
.and_then(|x| parse(x).ok())
.unwrap_or(0);
if addr > base && addr < base + size {
owner = kmod["BaseName"].as_str().unwrap_or("(??)");
break;
}
}
println!("\towned by {}", owner);
}
break;
}
}
}
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
break;
}
Err(ReadlineError::Eof) => {
println!("CTRL-D");
break;
}
Err(err) => {
println!("Error: {:?}", err);
break;
}
}
}
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
Ok(())
}

View File

@ -1,12 +1,16 @@
use std::error::Error; use std::error::Error;
use lpus::{ use lpus::{driver_state::DriverState, scan_driver};
driver_state::{DriverState},
scan_driver
};
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let mut driver = DriverState::new(); let mut driver = DriverState::new();
if !driver.is_supported() {
return Err(format!(
"Windows version {:?} is not supported",
driver.windows_ffi.short_version
)
.into());
}
println!("NtLoadDriver() -> 0x{:x}", driver.startup()); println!("NtLoadDriver() -> 0x{:x}", driver.startup());
let result = scan_driver(&driver).unwrap_or(Vec::new()); let result = scan_driver(&driver).unwrap_or(Vec::new());
@ -18,4 +22,3 @@ fn main() -> Result<(), Box<dyn Error>> {
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown()); println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
Ok(()) Ok(())
} }

View File

@ -1,21 +1,104 @@
use std::error::Error; use serde_json::Value;
use std::collections::HashSet;
use lpus::{ use std::error::Error;
driver_state::{DriverState},
scan_eprocess #[macro_use]
}; extern crate prettytable;
use prettytable::Table;
fn main() -> Result<(), Box<dyn Error>> {
let mut driver = DriverState::new(); use lpus::{
println!("NtLoadDriver() -> 0x{:x}", driver.startup()); driver_state::DriverState, scan_eprocess, scan_ethread, traverse_activehead,
traverse_handletable, traverse_kiprocesslist,
let result = scan_eprocess(&driver).unwrap_or(Vec::new()); };
for r in result.iter() { fn process_in_list(addr: &str, list: &Vec<Value>) -> bool {
println!("{:#}", r.to_string()); for r in list.iter() {
} if r["address"].as_str().unwrap() == addr {
return true;
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown()); }
Ok(()) }
} false
}
fn get_from_list(addr: &str, list: &Vec<Value>) -> Option<Value> {
for r in list.iter() {
if r["address"].as_str().unwrap() == addr {
return Some(r.clone());
}
}
None
}
fn process_in_list_thread(addr: &str, list: &Vec<Value>) -> bool {
for r in list.iter() {
if r["eprocess"].as_str().unwrap() == addr {
return true;
}
}
false
}
// fn get_process_from_list(addr: String, list: &Vec<Value>) -> String { }
fn main() -> Result<(), Box<dyn Error>> {
let mut driver = DriverState::new();
if !driver.is_supported() {
return Err(format!(
"Windows version {:?} is not supported",
driver.windows_ffi.short_version
)
.into());
}
println!("NtLoadDriver() -> 0x{:x}", driver.startup());
let process_scan = scan_eprocess(&driver).unwrap_or(Vec::new());
let thread_scan = scan_ethread(&driver).unwrap_or(Vec::new());
let activehead = traverse_activehead(&driver).unwrap_or(Vec::new());
let kiprocesslist = traverse_kiprocesslist(&driver).unwrap_or(Vec::new());
let handletable = traverse_handletable(&driver).unwrap_or(Vec::new());
let mut unique_process = HashSet::new();
for list in [&process_scan, &activehead, &kiprocesslist, &handletable].iter() {
for r in list.iter() {
let addr = r["address"].as_str().unwrap();
unique_process.insert(addr);
}
}
let mut table = Table::new();
table.add_row(row![
"Address",
"Name",
"pid",
"ppid",
"PoolTagScan",
"ActiveProcessHead",
"KiProcessListHead",
"HandleTableList",
"ThreadScan"
]);
for p in &unique_process {
let addr = p.to_string();
let v = get_from_list(&addr, &activehead).unwrap_or_default();
table.add_row(row![
&addr,
v["name"].as_str().unwrap_or("(??)"),
v["pid"].as_i64().unwrap_or(-1),
v["ppid"].as_i64().unwrap_or(-1),
process_in_list(&addr, &process_scan),
process_in_list(&addr, &activehead),
process_in_list(&addr, &kiprocesslist),
process_in_list(&addr, &handletable),
process_in_list_thread(&addr, &thread_scan)
]);
}
table.printstd();
// for r in process_scan.iter() {
// println!("{:#}", r.to_string());
// }
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
Ok(())
}

View File

@ -0,0 +1,36 @@
use std::error::Error;
use lpus::{
driver_state::DriverState, traverse_activehead, traverse_handletable, traverse_kiprocesslist,
};
fn main() -> Result<(), Box<dyn Error>> {
let mut driver = DriverState::new();
if !driver.is_supported() {
return Err(format!(
"Windows version {:?} is not supported",
driver.windows_ffi.short_version
)
.into());
}
println!("NtLoadDriver() -> 0x{:x}", driver.startup());
let activehead = traverse_activehead(&driver).unwrap_or(Vec::new());
let kiprocesslist = traverse_kiprocesslist(&driver).unwrap_or(Vec::new());
let handletable = traverse_handletable(&driver).unwrap_or(Vec::new());
for r in activehead.iter() {
println!("{:#}", r.to_string());
}
println!("=========================================");
for r in kiprocesslist.iter() {
println!("{:#}", r.to_string());
}
println!("=========================================");
for r in handletable.iter() {
println!("{:#}", r.to_string());
}
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
Ok(())
}

View File

@ -1,12 +1,16 @@
use std::error::Error; use std::error::Error;
use lpus::{ use lpus::{driver_state::DriverState, scan_ethread /* scan_mutant */};
driver_state::{DriverState},
scan_ethread, /* scan_mutant */
};
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let mut driver = DriverState::new(); let mut driver = DriverState::new();
if !driver.is_supported() {
return Err(format!(
"Windows version {:?} is not supported",
driver.windows_ffi.short_version
)
.into());
}
println!("NtLoadDriver() -> 0x{:x}", driver.startup()); println!("NtLoadDriver() -> 0x{:x}", driver.startup());
let threads = scan_ethread(&driver).unwrap_or(Vec::new()); let threads = scan_ethread(&driver).unwrap_or(Vec::new());
@ -22,5 +26,3 @@ fn main() -> Result<(), Box<dyn Error>> {
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown()); println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
Ok(()) Ok(())
} }

View File

@ -1,22 +1,24 @@
use std::error::Error; use std::error::Error;
use lpus::{ use lpus::{driver_state::DriverState, scan_file};
driver_state::{DriverState},
scan_file fn main() -> Result<(), Box<dyn Error>> {
}; let mut driver = DriverState::new();
if !driver.is_supported() {
fn main() -> Result<(), Box<dyn Error>> { return Err(format!(
let mut driver = DriverState::new(); "Windows version {:?} is not supported",
println!("NtLoadDriver() -> 0x{:x}", driver.startup()); driver.windows_ffi.short_version
)
let result = scan_file(&driver).unwrap_or(Vec::new()); .into());
}
for r in result.iter() { println!("NtLoadDriver() -> 0x{:x}", driver.startup());
println!("{:#}", r.to_string());
} let result = scan_file(&driver).unwrap_or(Vec::new());
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown()); for r in result.iter() {
Ok(()) println!("{:#}", r.to_string());
} }
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
Ok(())
}

View File

@ -0,0 +1,24 @@
use std::error::Error;
use lpus::{driver_state::DriverState, scan_kernel_module};
fn main() -> Result<(), Box<dyn Error>> {
let mut driver = DriverState::new();
if !driver.is_supported() {
return Err(format!(
"Windows version {:?} is not supported",
driver.windows_ffi.short_version
)
.into());
}
println!("NtLoadDriver() -> 0x{:x}", driver.startup());
let result = scan_kernel_module(&driver).unwrap_or(Vec::new());
for r in result.iter() {
println!("{:#}", r.to_string());
}
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
Ok(())
}

View File

@ -0,0 +1,78 @@
use std::error::Error;
use parse_int::parse;
use lpus::{
driver_state::DriverState, ssdt_table, traverse_loadedmodulelist, traverse_unloadeddrivers,
};
fn main() -> Result<(), Box<dyn Error>> {
let mut driver = DriverState::new();
if !driver.is_supported() {
return Err(format!(
"Windows version {:?} is not supported",
driver.windows_ffi.short_version
)
.into());
}
println!("NtLoadDriver() -> 0x{:x}", driver.startup());
let loaded = traverse_loadedmodulelist(&driver).unwrap_or(Vec::new());
let unloaded = traverse_unloadeddrivers(&driver).unwrap_or(Vec::new());
let ssdt = ssdt_table(&driver)?;
let ntosbase = driver.get_kernel_base();
// for r in loaded.iter() {
// println!("{:#}", r.to_string());
// }
println!("=============================================");
for r in unloaded.iter() {
println!("{:#}", r);
}
println!("=============================================");
for (idx, func) in ssdt.iter().enumerate() {
println!("SSDT [{}]\t0x{:x}", idx, func);
let owner = loaded.iter().find_map(|r| {
let base = r["dllbase"]
.as_str()
.and_then(|b| parse::<u64>(b).ok())
.unwrap_or(0);
let size = r["size"]
.as_str()
.and_then(|s| parse::<u64>(s).ok())
.unwrap_or(0);
if *func > base && *func < base + size {
let module = r["BaseName"].as_str().unwrap();
Some(module)
} else {
None
}
});
if owner == Some("ntoskrnl.exe") {
let offset = func - ntosbase.address();
let funcname: String = {
driver
.pdb_store
.symbols
.iter()
.find_map(|(name, o)| {
if o.clone() == offset {
Some(name.clone())
} else {
None
}
})
.unwrap_or("(??)".to_string())
};
println!("\towned by nt!{}", funcname);
} else if let Some(owner_) = owner {
println!("\\thooked by {}", owner_);
} else {
println!("\tmissing owner");
}
}
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
Ok(())
}

143
src/bin/lpus.rs Normal file
View File

@ -0,0 +1,143 @@
use serde_json::json;
use std::error::Error;
use std::fs;
use std::io::Write;
extern crate clap;
extern crate prettytable;
use app_dirs::{app_dir, AppDataType};
use clap::{App, Arg, SubCommand};
use rust_embed::RustEmbed;
#[derive(RustEmbed)]
#[folder = "resource"]
struct Asset;
use lpus::{
commands::{driverscan, modscan, psxview, ssdt, unloadedmodules},
driver_state::DriverState,
APP_INFO,
};
fn extract_driver() {
let driver_bytes = Asset::get("lpus.sys").unwrap();
let mut driver_location =
app_dir(AppDataType::UserData, &APP_INFO, &format!("driver")).unwrap();
driver_location.push("lpus.sys");
println!("driver location: {:?}", driver_location);
if let Ok(mut f) = fs::File::create(driver_location) {
f.write_all(&driver_bytes).unwrap();
}
}
fn main() -> Result<(), Box<dyn Error>> {
let version = format!(
"{}-{} commit on {}",
env!("VERGEN_SEMVER"),
env!("VERGEN_SHA_SHORT"),
env!("VERGEN_COMMIT_DATE")
);
let matches = App::new("LPUS")
.version(&*version)
.author("Khoa Nguyen Anh <mail.nganhkhoa@gmail.com>")
.about("Live memory fornesics on Windows")
.arg(
Arg::with_name("load")
.short("l")
.help("Load the driver and exit"),
)
.arg(
Arg::with_name("unload")
.short("u")
.help("Unload the driver and exit"),
)
.subcommand(
SubCommand::with_name("repl").about("Run the Interactive REPL (in development)"),
)
.subcommand(SubCommand::with_name("pdb").about("Inspect the PDB file"))
.subcommand(
SubCommand::with_name("hide_notepad")
.about("Compare processes found from multiple commands"),
)
.subcommand(
SubCommand::with_name("psxview")
.about("Compare processes found from multiple commands"),
)
.subcommand(
SubCommand::with_name("unloadedmodules")
.about("Compare processes found from multiple commands"),
)
.subcommand(
SubCommand::with_name("modscan")
.about("Compare processes found from multiple commands"),
)
.subcommand(
SubCommand::with_name("driverscan")
.about("Compare processes found from multiple commands"),
)
.subcommand(
SubCommand::with_name("ssdt")
.about("Dump the SSDT table")
.arg(
Arg::with_name("hook")
.short("h")
.help("print only hooked function"),
),
)
.get_matches();
extract_driver();
let mut driver = DriverState::new();
if !driver.is_supported() {
return Err(format!(
"Windows version {:?} is not supported",
driver.windows_ffi.short_version
)
.into());
}
if matches.is_present("load") {
println!("NtLoadDriver() -> 0x{:x}", driver.startup());
return Ok(());
}
if matches.is_present("unload") {
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
return Ok(());
}
driver.connect();
if let Some(_c) = matches.subcommand_matches("hide_notepad") {
driver.hide_notepad();
return Ok(());
}
if let Some(c) = matches.subcommand_matches("ssdt") {
ssdt(&driver, c.is_present("hook"));
return Ok(());
}
if let Some(_c) = matches.subcommand_matches("psxview") {
psxview(&driver);
return Ok(());
}
if let Some(_c) = matches.subcommand_matches("unloadedmodules") {
unloadedmodules(&driver);
return Ok(());
}
if let Some(_c) = matches.subcommand_matches("driverscan") {
driverscan(&driver);
return Ok(());
}
if let Some(_c) = matches.subcommand_matches("modscan") {
modscan(&driver);
return Ok(());
}
Ok(())
}

53
src/bin/lpus_all.rs Normal file
View File

@ -0,0 +1,53 @@
use serde_json::json;
use std::error::Error;
use std::fs;
use lpus::{
driver_state::DriverState, scan_driver, scan_eprocess, scan_ethread, scan_kernel_module,
ssdt_table, traverse_activehead, traverse_handletable, traverse_kiprocesslist,
traverse_loadedmodulelist, traverse_unloadeddrivers,
};
fn main() -> Result<(), Box<dyn Error>> {
let mut driver = DriverState::new();
if !driver.is_supported() {
return Err(format!(
"Windows version {:?} is not supported",
driver.windows_ffi.short_version
)
.into());
}
println!("NtLoadDriver() -> 0x{:x}", driver.startup());
let eprocess_1 = scan_eprocess(&driver)?;
let eprocess_2 = traverse_activehead(&driver)?;
let eprocess_3 = traverse_kiprocesslist(&driver)?;
let eprocess_4 = traverse_handletable(&driver)?;
let ethread = scan_ethread(&driver)?;
let drivers = scan_driver(&driver)?;
let kernel_module_1 = scan_kernel_module(&driver)?;
let kernel_module_2 = traverse_loadedmodulelist(&driver)?;
let unloaded_driver = traverse_unloadeddrivers(&driver)?;
let ssdt: Vec<String> = ssdt_table(&driver)?
.into_iter()
.map(|x| format!("0x{:x}", x))
.collect();
let result = json!({
"scan_eprocess": eprocess_1,
"traverse_activehead": eprocess_2,
"traverse_kiprocesslist": eprocess_3,
"traverse_handletable": eprocess_4,
"scan_ethread": ethread,
"scan_driver": drivers,
"scan_kernel_module": kernel_module_1,
"traverse_loadedmodulelist": kernel_module_2,
"traverse_unloadeddrivers": unloaded_driver,
"ssdt_table": ssdt
});
fs::write("./lpus.json", format!("{:#}", result)).ok();
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
Ok(())
}

View File

@ -1,12 +1,61 @@
use std::error::Error; use std::error::Error;
// use std::time::{SystemTime, UNIX_EPOCH};
use lpus::{
driver_state::{DriverState}, use rustyline::error::ReadlineError;
}; use rustyline::Editor;
fn main() -> Result<(), Box<dyn Error>> { use lpus::driver_state::DriverState;
let driver = DriverState::new();
driver.windows_ffi.print_version(); pub fn to_epoch(filetime: u64) -> u64 {
driver.pdb_store.print_default_information(); // https://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/
Ok(()) let windows_epoch_diff = 11644473600000 * 10000;
} if filetime < windows_epoch_diff {
return 0;
}
let process_time_epoch = (filetime - windows_epoch_diff) / 10000;
// let now_ms = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_millis() as u64;
process_time_epoch
}
fn main() -> Result<(), Box<dyn Error>> {
let driver = DriverState::new();
driver.windows_ffi.print_version();
driver.pdb_store.print_default_information();
println!("{}", to_epoch(0xfffffa80018cb688));
println!("{}", to_epoch(0x01d64ecd8b295318));
let mut rl = Editor::<()>::new();
if rl.load_history("history.lpus").is_err() {
println!("No previous history.");
}
loop {
let readline = rl.readline(">> ");
match readline {
Ok(line) => {
rl.add_history_entry(line.as_str());
println!("Line: {}", line);
// TODO: add parser here
if let Err(e) = driver.pdb_store.dt(&line) {
println!("{}", e);
}
}
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
break;
}
Err(ReadlineError::Eof) => {
println!("CTRL-D");
break;
}
Err(err) => {
println!("Error: {:?}", err);
break;
}
}
}
rl.save_history("history.lpus").unwrap();
Ok(())
}

11
src/bin/test_load.rs Normal file
View File

@ -0,0 +1,11 @@
use std::error::Error;
use lpus::driver_state::DriverState;
fn main() -> Result<(), Box<dyn Error>> {
let mut driver = DriverState::new();
println!("NtLoadDriver() -> 0x{:x}", driver.startup());
println!("NtUnloadDriver() -> 0x{:x}", driver.shutdown());
Ok(())
}

190
src/commands.rs Normal file
View File

@ -0,0 +1,190 @@
use serde_json::Value;
use std::collections::HashSet;
use std::error::Error;
use prettytable::{cell, row, Table};
use parse_int::parse;
use crate::{
driver_state::DriverState, scan_driver, scan_eprocess, scan_ethread, scan_kernel_module,
ssdt_table, traverse_activehead, traverse_handletable, traverse_kiprocesslist,
traverse_loadedmodulelist, traverse_unloadeddrivers,
};
pub fn ssdt(driver: &DriverState, only_hooked: bool) {
let loaded = traverse_loadedmodulelist(&driver).unwrap_or(Vec::new());
let ssdt = ssdt_table(&driver).unwrap_or(Vec::new());
let ntosbase = driver.get_kernel_base();
for (idx, func) in ssdt.iter().enumerate() {
let owner = loaded.iter().find_map(|r| {
let base = r["dllbase"]
.as_str()
.and_then(|b| parse::<u64>(b).ok())
.unwrap_or(0);
let size = r["size"]
.as_str()
.and_then(|s| parse::<u64>(s).ok())
.unwrap_or(0);
if *func > base && *func < base + size {
let module = r["BaseName"].as_str().unwrap();
Some(module)
} else {
None
}
});
if owner == Some("ntoskrnl.exe") {
if !only_hooked {
let offset = func - ntosbase.address();
let funcname: String = {
driver
.pdb_store
.symbols
.iter()
.find_map(|(name, o)| {
if o.clone() == offset {
Some(name.clone())
} else {
None
}
})
.unwrap_or("(??)".to_string())
};
println!("SSDT [{}]\t0x{:x}", idx, func);
println!("\towned by nt!{}", funcname);
}
} else if let Some(owner_) = owner {
println!("SSDT [{}]\t0x{:x}", idx, func);
println!("\\thooked by {}", owner_);
} else {
println!("SSDT [{}]\t0x{:x}", idx, func);
println!("\tmissing owner");
}
}
}
pub fn psxview(driver: &DriverState) {
fn process_in_list(addr: &str, list: &Vec<Value>) -> bool {
for r in list.iter() {
if r["address"].as_str().unwrap() == addr {
return true;
}
}
false
}
fn get_from_list(addr: &str, list: &Vec<Value>) -> Option<Value> {
for r in list.iter() {
if r["address"].as_str().unwrap() == addr {
return Some(r.clone());
}
}
None
}
fn process_in_list_thread(addr: &str, list: &Vec<Value>) -> bool {
for r in list.iter() {
if r["eprocess"].as_str().unwrap() == addr {
return true;
}
}
false
}
let process_scan = scan_eprocess(&driver).unwrap_or(Vec::new());
let thread_scan = scan_ethread(&driver).unwrap_or(Vec::new());
let activehead = traverse_activehead(&driver).unwrap_or(Vec::new());
let kiprocesslist = traverse_kiprocesslist(&driver).unwrap_or(Vec::new());
let handletable = traverse_handletable(&driver).unwrap_or(Vec::new());
let mut unique_process = HashSet::new();
for list in [&process_scan, &activehead, &kiprocesslist, &handletable].iter() {
for r in list.iter() {
let addr = r["address"].as_str().unwrap();
unique_process.insert(addr);
}
}
let mut table = Table::new();
table.add_row(row![
"Address",
"Name",
"pid",
"ppid",
"PoolTagScan",
"ActiveProcessHead",
"KiProcessListHead",
"HandleTableList",
"ThreadScan"
]);
for p in &unique_process {
let addr = p.to_string();
let v = {
if let Some(vv) = get_from_list(&addr, &activehead) {
vv
} else {
get_from_list(&addr, &process_scan).unwrap_or_default()
}
};
table.add_row(row![
&addr,
v["name"].as_str().unwrap_or("(??)"),
v["pid"].as_i64().unwrap_or(-1),
v["ppid"].as_i64().unwrap_or(-1),
process_in_list(&addr, &process_scan),
process_in_list(&addr, &activehead),
process_in_list(&addr, &kiprocesslist),
process_in_list(&addr, &handletable),
process_in_list_thread(&addr, &thread_scan)
]);
}
table.printstd();
}
pub fn modscan(driver: &DriverState) {
let dd = scan_kernel_module(&driver).unwrap_or(Vec::new());
let mut table = Table::new();
table.add_row(row!["Address", "Base name", "Base", "Size", "File"]);
for d in &dd {
table.add_row(row![
d["address"].as_str().unwrap_or("(??)"),
d["BaseName"].as_str().unwrap_or("(??)"),
d["dllbase"].as_str().unwrap_or("(??)"),
d["size"].as_str().unwrap_or("(??)"),
d["FullName"].as_str().unwrap_or("(??)"),
]);
}
table.printstd();
}
pub fn driverscan(driver: &DriverState) {
let dd = scan_driver(&driver).unwrap_or(Vec::new());
let mut table = Table::new();
table.add_row(row!["Address", "Device", "Service key", "Start", "Size"]);
for d in &dd {
table.add_row(row![
d["address"].as_str().unwrap_or("(??)"),
d["device"].as_str().unwrap_or("(??)"),
d["servicekey"].as_str().unwrap_or("(??)"),
d["start"].as_str().unwrap_or("(??)"),
d["size"].as_str().unwrap_or("(??)"),
]);
}
table.printstd();
}
pub fn unloadedmodules(driver: &DriverState) {
let modules = traverse_unloadeddrivers(&driver).unwrap_or(Vec::new());
let mut table = Table::new();
table.add_row(row!["Address", "Driver", "Start", "End", "Time"]);
for m in &modules {
table.add_row(row![
m["address"].as_str().unwrap_or("(??)"),
m["name"].as_str().unwrap_or("(??)"),
m["start_addr"].as_str().unwrap_or("(??)"),
m["end_addr"].as_str().unwrap_or("(??)"),
m["time_rfc2822"].as_str().unwrap_or("(??)"),
]);
}
table.printstd();
}

53
src/downloader.rs Normal file
View File

@ -0,0 +1,53 @@
use std::error::Error;
use indicatif::{ProgressBar, ProgressStyle};
use reqwest::{header, Client};
use std::path::Path;
use structopt::StructOpt;
use tokio::{fs, io::AsyncWriteExt};
type BoxResult<T> = Result<T, Box<dyn Error>>;
async fn download(url: &str, path: &PathBuf) -> BoxResult<()> {
let client = Client::new();
let total_size = {
let resp = client.head(url).send().await?;
if resp.status().is_success() {
resp.headers()
.get(header::CONTENT_LENGTH)
.and_then(|ct_len| ct_len.to_str().ok())
.and_then(|ct_len| ct_len.parse().ok())
.unwrap_or(0)
} else {
return Err(format!("Couldn'n download URL: {}", url));
}
};
let mut request = client.get(url);
let pb = ProgressBar::new(total_size);
pb.set_style(
ProgressStyle::default_bar()
.template("{spinner:.green} [{elapsed_precise}] \
[{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})")
.progress_chars("#>-"));
let file = Path::new(path);
if file.exists() {
return Err(format!("File {:?} existed", file));
// let size = file.metadata()?.len().saturating_sub(1);
// request = request.header(header::RANGE, format!("bytes={}-", size));
// pb.inc(size);
}
let mut source = request.send().await?;
let mut dest = fs::OpenOptions::new().create(true).append(true).open(&file).await?;
while let Some(chunk) = source.chunk().await? {
dest.write_all(&chunk).await?;
pb.inc(chunk.len() as u64);
}
println!("Download of '{}' has been completed.", file.to_str().unwrap());
Ok(())
}

View File

@ -1,347 +1,397 @@
use std::default::Default; use std::clone::Clone;
use std::clone::Clone; use std::default::Default;
use std::error::Error; use std::error::Error;
// use std::io::{Error, ErrorKind}; // use std::io::{Error, ErrorKind};
use std::ffi::c_void; use std::ffi::c_void;
use std::mem::{size_of_val}; use std::mem::{size_of, size_of_val};
use winapi::shared::ntdef::{NTSTATUS}; use winapi::shared::minwindef::DWORD;
use winapi::shared::minwindef::{DWORD}; use winapi::shared::ntdef::NTSTATUS;
use winapi::um::winioctl::{ use winapi::um::winioctl::{
CTL_CODE, FILE_ANY_ACCESS, CTL_CODE, FILE_ANY_ACCESS, METHOD_IN_DIRECT, /* METHOD_BUFFERED, */ METHOD_NEITHER,
METHOD_IN_DIRECT, METHOD_OUT_DIRECT, /* METHOD_BUFFERED, */ METHOD_NEITHER METHOD_OUT_DIRECT,
}; };
use crate::address::Address; use crate::address::Address;
use crate::pdb_store::{PdbStore, parse_pdb}; use crate::ioctl_protocol::{
use crate::windows::{WindowsFFI, WindowsVersion}; DerefAddr, HideProcess, InputData, /* OutputData, */ Nothing, OffsetData, ScanPoolData,
use crate::ioctl_protocol::{ };
InputData, OffsetData, DerefAddr, ScanPoolData, /* HideProcess, */ use crate::pdb_store::{parse_pdb, PdbStore};
/* OutputData, */ Nothing use crate::windows::{WindowsFFI, WindowsVersion};
};
type BoxResult<T> = Result<T, Box<dyn Error>>;
type BoxResult<T> = Result<T, Box<dyn Error>>;
const SIOCTL_TYPE: DWORD = 40000;
const SIOCTL_TYPE: DWORD = 40000;
pub fn to_epoch(filetime: u64) -> u64 {
pub fn to_epoch(filetime: u64) -> u64 { let windows_epoch_diff: u64 = 11644473600000 * 10000;
let windows_epoch_diff: u64 = 11644473600000 * 10000; if filetime < windows_epoch_diff {
if filetime < windows_epoch_diff { return 0;
return 0; }
} let process_time_epoch: u64 = (filetime - windows_epoch_diff) / 10000;
let process_time_epoch: u64 = (filetime - windows_epoch_diff) / 10000; process_time_epoch
process_time_epoch }
}
#[allow(dead_code)]
#[allow(dead_code)] #[derive(Debug)]
#[derive(Debug)] pub enum DriverAction {
pub enum DriverAction { SetupOffset,
SetupOffset, GetKernelBase,
GetKernelBase, ScanPsActiveHead,
ScanPsActiveHead, ScanPool,
ScanPool, ScanPoolRemote,
ScanPoolRemote, DereferenceAddress,
DereferenceAddress, HideProcess,
HideProcess }
}
impl DriverAction {
impl DriverAction { pub fn get_code(&self) -> DWORD {
pub fn get_code(&self) -> DWORD { match self {
match self { DriverAction::SetupOffset => {
DriverAction::SetupOffset => CTL_CODE(SIOCTL_TYPE, 0x900, METHOD_IN_DIRECT, FILE_ANY_ACCESS), CTL_CODE(SIOCTL_TYPE, 0x900, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
DriverAction::GetKernelBase => CTL_CODE(SIOCTL_TYPE, 0x901, METHOD_OUT_DIRECT, FILE_ANY_ACCESS), }
DriverAction::ScanPsActiveHead => CTL_CODE(SIOCTL_TYPE, 0x902, METHOD_NEITHER, FILE_ANY_ACCESS), DriverAction::GetKernelBase => {
DriverAction::ScanPool => CTL_CODE(SIOCTL_TYPE, 0x903, METHOD_IN_DIRECT, FILE_ANY_ACCESS), CTL_CODE(SIOCTL_TYPE, 0x901, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
DriverAction::ScanPoolRemote => CTL_CODE(SIOCTL_TYPE, 0x904, METHOD_IN_DIRECT, FILE_ANY_ACCESS), }
DriverAction::DereferenceAddress => CTL_CODE(SIOCTL_TYPE, 0xA00, METHOD_OUT_DIRECT, FILE_ANY_ACCESS), DriverAction::ScanPsActiveHead => {
DriverAction::HideProcess => CTL_CODE(SIOCTL_TYPE, 0xA01, METHOD_IN_DIRECT, FILE_ANY_ACCESS) CTL_CODE(SIOCTL_TYPE, 0x902, METHOD_NEITHER, FILE_ANY_ACCESS)
} }
} DriverAction::ScanPool => {
} CTL_CODE(SIOCTL_TYPE, 0x903, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
}
#[derive(Debug)] DriverAction::ScanPoolRemote => {
pub struct EprocessPoolChunk { CTL_CODE(SIOCTL_TYPE, 0x904, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
pub pool_addr: u64, }
pub eprocess_addr: u64, DriverAction::DereferenceAddress => {
pub eprocess_name: String, CTL_CODE(SIOCTL_TYPE, 0xA00, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
pub create_time: u64, }
pub exit_time: u64 DriverAction::HideProcess => {
} CTL_CODE(SIOCTL_TYPE, 0xA01, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
}
impl PartialEq for EprocessPoolChunk { }
fn eq(&self, other: &Self) -> bool { }
self.eprocess_addr == other.eprocess_addr }
}
} #[derive(Debug)]
pub struct EprocessPoolChunk {
#[allow(dead_code)] pub pool_addr: u64,
pub struct DriverState { pub eprocess_addr: u64,
// TODO: Make private, only call methods of DriverState pub eprocess_name: String,
pub pdb_store: PdbStore, pub create_time: u64,
pub windows_ffi: WindowsFFI, pub exit_time: u64,
} }
impl DriverState { impl PartialEq for EprocessPoolChunk {
pub fn new() -> Self { fn eq(&self, other: &Self) -> bool {
Self { self.eprocess_addr == other.eprocess_addr
pdb_store: parse_pdb().expect("Cannot get PDB file"), }
windows_ffi: WindowsFFI::new() }
}
} #[allow(dead_code)]
pub struct DriverState {
pub fn startup(&mut self) -> NTSTATUS { // TODO: Make private, only call methods of DriverState
let s = self.windows_ffi.load_driver(); pub pdb_store: PdbStore,
let mut input = InputData { pub windows_ffi: WindowsFFI,
offset_value: OffsetData::new(&self.pdb_store, self.windows_ffi.short_version) }
};
self.windows_ffi.device_io(DriverAction::SetupOffset.get_code(), impl DriverState {
&mut input, &mut Nothing); pub fn new() -> Self {
s Self {
} pdb_store: parse_pdb().expect("Cannot get PDB file"),
windows_ffi: WindowsFFI::new(),
pub fn shutdown(&self) -> NTSTATUS { }
self.windows_ffi.unload_driver() }
}
pub fn startup(&mut self) -> NTSTATUS {
pub fn get_kernel_base(&self) -> Address { let s = self.windows_ffi.load_driver();
let mut ntosbase = 0u64; let mut input = InputData {
self.windows_ffi.device_io(DriverAction::GetKernelBase.get_code(), offset_value: OffsetData::new(&self.pdb_store, self.windows_ffi.short_version),
&mut Nothing, &mut ntosbase); };
Address::from_base(ntosbase) self.windows_ffi.device_io(
} DriverAction::SetupOffset.get_code(),
&mut input,
// pub fn scan_active_head(&self) -> BoxResult<Vec<EprocessPoolChunk>> { &mut Nothing,
// let ntosbase = self.get_kernel_base(); );
// let ps_active_head = ntosbase + self.pdb_store.get_offset_r("PsActiveProcessHead")?; s
// let flink_offset = self.pdb_store.get_offset_r("_LIST_ENTRY.Flink")?; }
// let eprocess_link_offset = self.pdb_store.get_offset_r("_EPROCESS.ActiveProcessLinks")?;
// let eprocess_name_offset = self.pdb_store.get_offset_r("_EPROCESS.ImageFileName")?; pub fn shutdown(&self) -> NTSTATUS {
// self.windows_ffi.unload_driver()
// let mut ptr = ps_active_head; }
// self.deref_addr((ptr + flink_offset).get(), &mut ptr);
// pub fn connect(&mut self) {
// let mut result: Vec<EprocessPoolChunk> = Vec::new(); self.windows_ffi.file_connect();
// while ptr != ps_active_head { }
// let mut image_name = [0u8; 15];
// let eprocess = ptr - eprocess_link_offset; pub fn is_supported(&self) -> bool {
// self.deref_addr(eprocess + eprocess_name_offset, &mut image_name); self.windows_ffi.short_version.is_supported()
// match std::str::from_utf8(&image_name) { }
// Ok(n) => {
// result.push(EprocessPoolChunk { pub fn hide_notepad(&self) {
// pool_addr: 0, let s = String::from("notepad.exe");
// eprocess_addr: eprocess, let s_bytes = s.as_bytes();
// eprocess_name: n.to_string() let mut name = [0u8; 15];
// .trim_end_matches(char::from(0)) for i in 0..s.len() {
// .to_string(), name[i] = s_bytes[i];
// create_time: 0, }
// exit_time: 0 let mut input = InputData {
// hide_process: HideProcess {
// }); name,
// }, size: s.len() as u64,
// _ => {} },
// }; };
// self.deref_addr(ptr + flink_offset, &mut ptr); self.windows_ffi.device_io(
// } DriverAction::HideProcess.get_code(),
// Ok(result) &mut input,
// } &mut Nothing,
);
pub fn scan_pool<F>(&self, tag: &[u8; 4], expected_struct: &str, mut handler: F) -> BoxResult<bool> }
where F: FnMut(Address, &[u8], Address) -> BoxResult<bool>
// F(Pool Address, Pool Header Data, Pool Data Address) pub fn use_old_tag(&self) -> bool {
// TODO: Pool Header as a real struct // use old tag to scan, for Window < 8
{ if self.windows_ffi.short_version < WindowsVersion::Windows8 {
// TODO: make generator, in hold: https://github.com/rust-lang/rust/issues/43122 true
// Making this function a generator will turn the call to a for loop } else {
// https://docs.rs/gen-iter/0.2.0/gen_iter/ false
// >> More flexibility in code }
let pool_header_size = self.pdb_store.get_offset_r("_POOL_HEADER.struct_size")?; }
let minimum_block_size = self.pdb_store.get_offset_r(&format!("{}.struct_size", expected_struct))?
+ pool_header_size; pub fn get_kernel_base(&self) -> Address {
let code = DriverAction::ScanPoolRemote.get_code(); let mut ntosbase = 0u64;
let ntosbase = self.get_kernel_base(); self.windows_ffi.device_io(
let [start_address, end_address] = self.get_nonpaged_range(&ntosbase)?; DriverAction::GetKernelBase.get_code(),
&mut Nothing,
println!("kernel base: {}; non-paged pool (start, end): ({}, {})", ntosbase, start_address, end_address); &mut ntosbase,
);
let mut ptr = start_address; Address::from_base(ntosbase)
while ptr < end_address { }
let mut next_found = 0u64;
let mut input = InputData { pub fn scan_pool<F>(
scan_range: ScanPoolData::new(&[ptr.address(), end_address.address()], tag) &self,
}; tag: &[u8; 4],
self.windows_ffi.device_io(code, &mut input, &mut next_found); expected_struct: &str,
ptr = Address::from_base(next_found); mut handler: F,
if ptr >= end_address { ) -> BoxResult<bool>
break; where
} F: FnMut(Address, &[u8], Address) -> BoxResult<bool>, // F(Pool Address, Pool Header Data, Pool Data Address)
// TODO: Pool Header as a real struct
let pool_addr = Address::from_base(ptr.address()); {
let header: Vec<u8> = self.deref_array(&pool_addr, pool_header_size); // TODO: scan large pool
let chunk_size = (header[2] as u64) * 16u64; // TODO: make generator, in hold: https://github.com/rust-lang/rust/issues/43122
// Making this function a generator will turn the call to a for loop
if pool_addr.address() + chunk_size > end_address.address() { // https://docs.rs/gen-iter/0.2.0/gen_iter/
// the chunk surpasses the non page pool range // >> More flexibility in code
break; let pool_header_size = self.pdb_store.get_offset_r("_POOL_HEADER.struct_size")?;
} let minimum_block_size = self
.pdb_store
// automatically reject bad chunk .get_offset_r(&format!("{}.struct_size", expected_struct))?
if chunk_size < minimum_block_size { + pool_header_size;
ptr += 0x4; let code = DriverAction::ScanPoolRemote.get_code();
continue; let ntosbase = self.get_kernel_base();
} let [start_address, end_address] = self.get_nonpaged_range(&ntosbase)?;
let data_addr = Address::from_base(pool_addr.address() + pool_header_size); println!(
"kernel base: {}; non-paged pool (start, end): ({}, {}); tag: {:?} {}",
let success = handler(pool_addr, &header, data_addr)?; ntosbase, start_address, end_address, tag, expected_struct
if success { );
ptr += chunk_size; /* skip this chunk */
} let mut ptr = start_address;
else { while ptr < end_address {
ptr += 0x4; /* search next */ let mut next_found = 0u64;
} let mut input = InputData {
} scan_range: ScanPoolData::new(&[ptr.address(), end_address.address()], tag),
Ok(true) };
} self.windows_ffi
.device_io(code, &mut input, &mut next_found);
pub fn address_of(&self, addr: &Address, name: &str) -> BoxResult<u64> { ptr = Address::from_base(next_found);
let resolver = |p| { self.deref_addr_new(p) }; if ptr >= end_address {
let r = self.pdb_store.decompose(&addr, &name)?; break;
Ok(r.get(&resolver)) }
}
let pool_addr = Address::from_base(ptr.address());
pub fn decompose<T: Default>(&self, addr: &Address, name: &str) -> BoxResult<T> { let header: Vec<u8> = self.deref_array(&pool_addr, pool_header_size);
// interface to pdb_store.decompose let chunk_size = (header[2] as u64) * 16u64;
let resolver = |p| { self.deref_addr_new(p) };
let r: T = self.deref_addr_new(self.pdb_store.decompose(&addr, &name)?.get(&resolver)); if pool_addr.address() + chunk_size > end_address.address() {
Ok(r) // the chunk surpasses the non page pool range
} break;
}
pub fn decompose_array<T: Default + Clone>(&self, addr: &Address, name: &str, len: u64) -> BoxResult<Vec<T>> {
// interface to pdb_store.decompose for array // automatically reject bad chunk
let r: Vec<T> = self.deref_array(&self.pdb_store.decompose(&addr, &name)?, len); if chunk_size < minimum_block_size {
Ok(r) ptr += 0x4;
} continue;
}
pub fn deref_addr_new<T: Default>(&self, addr: u64) -> T {
let mut r: T = Default::default(); let data_addr = Address::from_base(pool_addr.address() + pool_header_size);
if addr != 0 { let success = handler(pool_addr, &header, data_addr).unwrap_or(false);
self.deref_addr(addr, &mut r); if success {
} ptr += chunk_size; // skip this chunk
r } else {
} ptr += 0x4; // search next
}
pub fn deref_array<T: Default + Clone>(&self, addr: &Address, len: u64) -> Vec<T> { }
let resolver = |p| { self.deref_addr_new(p) }; Ok(true)
let mut r: Vec<T> = vec![Default::default(); len as usize]; }
self.deref_addr_ptr(addr.get(&resolver), r.as_mut_ptr(), len);
r pub fn address_of(&self, addr: &Address, name: &str) -> BoxResult<u64> {
} let resolver = |p| self.deref_addr_new(p);
let r = self.pdb_store.decompose(&addr, &name)?;
// #[deprecated(note="use deref_addr_new<T>")] Ok(r.get(&resolver))
pub fn deref_addr<T>(&self, addr: u64, outbuf: &mut T) { }
let code = DriverAction::DereferenceAddress.get_code();
let size: usize = size_of_val(outbuf); pub fn decompose<T: Default>(&self, addr: &Address, name: &str) -> BoxResult<T> {
let mut input = InputData { // interface to pdb_store.decompose
deref_addr: DerefAddr { let resolver = |p| self.deref_addr_new(p);
addr, let r: T = self.deref_addr_new(self.pdb_store.decompose(&addr, &name)?.get(&resolver));
size: size as u64 Ok(r)
} }
};
self.windows_ffi.device_io(code, &mut input, outbuf); pub fn decompose_array<T: Default + Clone>(
} &self,
addr: &Address,
// #[deprecated(note="use deref_array<T>")] name: &str,
pub fn deref_addr_ptr<T>(&self, addr: u64, outptr: *mut T, output_len: u64) { len: u64,
let code = DriverAction::DereferenceAddress.get_code(); ) -> BoxResult<Vec<T>> {
let mut input = InputData { // interface to pdb_store.decompose for array
deref_addr: DerefAddr { let r: Vec<T> = self.deref_array(&self.pdb_store.decompose(&addr, &name)?, len);
addr, Ok(r)
size: output_len }
}
}; pub fn deref_addr_new<T: Default>(&self, addr: u64) -> T {
self.windows_ffi.device_io_raw(code, let mut r: T = Default::default();
&mut input as *mut _ as *mut c_void, size_of_val(&input) as DWORD, if addr != 0 {
outptr as *mut c_void, output_len as DWORD); self.deref_addr(addr, &mut r);
} }
r
pub fn get_unicode_string(&self, unicode_str_addr: u64, deref: bool) -> BoxResult<String> { }
if unicode_str_addr == 0 {
return Err("Not a valid address".into()); pub fn deref_array<T: Default + Clone>(&self, addr: &Address, len: u64) -> Vec<T> {
} let resolver = |p| self.deref_addr_new(p);
let mut r: Vec<T> = vec![Default::default(); len as usize];
let mut strlen = 0u16; let size_in_byte = (len as usize) * size_of::<T>();
let mut capacity = 0u16; self.deref_addr_ptr(addr.get(&resolver), r.as_mut_ptr(), size_in_byte as u64);
let mut bufaddr = 0u64; r
let buffer_ptr = unicode_str_addr + self.pdb_store.get_offset_r("_UNICODE_STRING.Buffer")?; }
let capacity_addr = unicode_str_addr + self.pdb_store.get_offset_r("_UNICODE_STRING.MaximumLength")?;
// #[deprecated(note="use deref_addr_new<T>")]
self.deref_addr(unicode_str_addr, &mut strlen); pub fn deref_addr<T>(&self, addr: u64, outbuf: &mut T) {
self.deref_addr(capacity_addr, &mut capacity); let code = DriverAction::DereferenceAddress.get_code();
self.deref_addr(buffer_ptr, &mut bufaddr); let size: usize = size_of_val(outbuf);
let mut input = InputData {
if bufaddr == 0 || strlen > capacity || strlen == 0 || strlen % 2 != 0 { deref_addr: DerefAddr {
return Err("Unicode string is empty".into()); addr,
} size: size as u64,
},
if !deref { };
return Ok("".to_string()); self.windows_ffi.device_io(code, &mut input, outbuf);
} }
let mut buf = vec![0u16; (strlen / 2) as usize]; // #[deprecated(note="use deref_array<T>")]
self.deref_addr_ptr(bufaddr, buf.as_mut_ptr(), strlen as u64); pub fn deref_addr_ptr<T>(&self, addr: u64, outptr: *mut T, output_len_as_byte: u64) {
// TODO: BUG with deref_array, len is wrong, let code = DriverAction::DereferenceAddress.get_code();
// >> the size of vector is strlen / 2 let mut input = InputData {
// >> the size to dereference is strlen deref_addr: DerefAddr {
// XXX: use Vec<u8> and turn to Vec<u16> addr,
// let buf: Vec<u16> = self.deref_array(&Address::from_base(bufaddr), (strlen / 2) as u64); size: output_len_as_byte,
},
Ok(String::from_utf16(&buf)?) };
} self.windows_ffi.device_io_raw(
code,
pub fn get_nonpaged_range(&self, ntosbase: &Address) -> BoxResult<[Address; 2]> { &mut input as *mut _ as *mut c_void,
// TODO: Add support for other Windows version here size_of_val(&input) as DWORD,
match self.windows_ffi.short_version { outptr as *mut c_void,
WindowsVersion::Windows10FastRing => { output_len_as_byte as DWORD,
let mistate = ntosbase.clone() + self.pdb_store.get_offset_r("MiState")?; );
let path_first_va: String = vec![ }
"_MI_SYSTEM_INFORMATION",
"Hardware", pub fn get_unicode_string(&self, unicode_str_addr: u64) -> BoxResult<String> {
"SystemNodeNonPagedPool", if unicode_str_addr == 0 {
"NonPagedPoolFirstVa" return Err("Not a valid address".into());
].join("."); }
let path_last_va: String = vec![
"_MI_SYSTEM_INFORMATION", let mut strlen = 0u16;
"Hardware", let mut capacity = 0u16;
"SystemNodeNonPagedPool", let mut bufaddr = 0u64;
"NonPagedPoolLastVa" let buffer_ptr =
].join("."); unicode_str_addr + self.pdb_store.get_offset_r("_UNICODE_STRING.Buffer")?;
let first_va = Address::from_base(self.decompose(&mistate, &path_first_va)?); let capacity_addr = unicode_str_addr
let last_va = Address::from_base(self.decompose(&mistate, &path_last_va)?); + self
Ok([first_va, last_va]) .pdb_store
}, .get_offset_r("_UNICODE_STRING.MaximumLength")?;
WindowsVersion::Windows10_2019 |
WindowsVersion::Windows10_2018 => { self.deref_addr(unicode_str_addr, &mut strlen);
let mistate = ntosbase.clone() + self.pdb_store.get_offset_r("MiState")?; self.deref_addr(capacity_addr, &mut capacity);
let path_first_va: String = vec![ self.deref_addr(buffer_ptr, &mut bufaddr);
"_MI_SYSTEM_INFORMATION",
"Hardware", if bufaddr == 0 || strlen > capacity || strlen == 0 || strlen % 2 != 0 {
"SystemNodeInformation", return Err("Unicode string is empty".into());
"NonPagedPoolFirstVa" }
].join(".");
let path_last_va: String = vec![ let mut buf = vec![0u16; (strlen / 2) as usize];
"_MI_SYSTEM_INFORMATION", self.deref_addr_ptr(bufaddr, buf.as_mut_ptr(), strlen as u64);
"Hardware", // TODO: BUG with deref_array, len is wrong,
"SystemNodeInformation", // >> the size of vector is strlen / 2
"NonPagedPoolLastVa" // >> the size to dereference is strlen
].join("."); // XXX: use Vec<u8> and turn to Vec<u16>
let first_va = Address::from_base(self.decompose(&mistate, &path_first_va)?); // let buf: Vec<u16> = self.deref_array(&Address::from_base(bufaddr), (strlen / 2) as u64);
let last_va = Address::from_base(self.decompose(&mistate, &path_last_va)?);
Ok([first_va, last_va]) Ok(String::from_utf16(&buf)?)
}, }
_ => {
Err("Windows version for nonpaged pool algorithm is not implemented".into()) pub fn get_nonpaged_range(&self, ntosbase: &Address) -> BoxResult<[Address; 2]> {
} // TODO: Add support for other Windows version here
} match self.windows_ffi.short_version {
} WindowsVersion::WindowsFastRing => {
let mistate = ntosbase.clone() + self.pdb_store.get_offset_r("MiState")?;
} let path_first_va: String = vec![
"_MI_SYSTEM_INFORMATION",
"Hardware",
"SystemNodeNonPagedPool",
"NonPagedPoolFirstVa",
]
.join(".");
let path_last_va: String = vec![
"_MI_SYSTEM_INFORMATION",
"Hardware",
"SystemNodeNonPagedPool",
"NonPagedPoolLastVa",
]
.join(".");
let first_va = Address::from_base(self.decompose(&mistate, &path_first_va)?);
let last_va = Address::from_base(self.decompose(&mistate, &path_last_va)?);
Ok([first_va, last_va])
}
WindowsVersion::Windows10_2019 | WindowsVersion::Windows10_2018 => {
let mistate = ntosbase.clone() + self.pdb_store.get_offset_r("MiState")?;
let path_first_va: String = vec![
"_MI_SYSTEM_INFORMATION",
"Hardware",
"SystemNodeInformation",
"NonPagedPoolFirstVa",
]
.join(".");
let path_last_va: String = vec![
"_MI_SYSTEM_INFORMATION",
"Hardware",
"SystemNodeInformation",
"NonPagedPoolLastVa",
]
.join(".");
let first_va = Address::from_base(self.decompose(&mistate, &path_first_va)?);
let last_va = Address::from_base(self.decompose(&mistate, &path_last_va)?);
Ok([first_va, last_va])
}
WindowsVersion::Windows7 => {
let path_first_va =
ntosbase.clone() + self.pdb_store.get_offset_r("MmNonPagedPoolStart")?;
let path_last_va =
ntosbase.clone() + self.pdb_store.get_offset_r("MiNonPagedPoolEnd")?;
let first_va = Address::from_base(self.deref_addr_new(path_first_va.address()));
let last_va = Address::from_base(self.deref_addr_new(path_last_va.address()));
Ok([first_va, last_va])
}
_ => Err("Windows version for nonpaged pool algorithm is not implemented".into()),
}
}
}

View File

@ -1,121 +1,184 @@
use crate::pdb_store::PdbStore; use crate::pdb_store::PdbStore;
use crate::windows::WindowsVersion; use crate::windows::WindowsVersion;
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct OffsetData { pub struct OffsetData {
eprocess_name_offset: u64, eprocess_name_offset: u64,
eprocess_link_offset: u64, eprocess_link_offset: u64,
list_blink_offset: u64, list_blink_offset: u64,
process_head_offset: u64, process_head_offset: u64,
mistate_offset: u64, mistate_offset: u64,
hardware_offset: u64, hardware_offset: u64,
system_node_offset: u64, system_node_offset: u64,
first_va_offset: u64, first_va_offset: u64,
last_va_offset: u64, last_va_offset: u64,
large_page_table_offset: u64, large_page_table_offset: u64,
large_page_size_offset: u64, large_page_size_offset: u64,
pool_chunk_size: u64, pool_chunk_size: u64,
} }
// TODO: Move to WindowsScanStrategy and return the corresponding struct base on Windows version // TODO: Move to WindowsScanStrategy and return the corresponding struct base on Windows version
impl OffsetData { impl OffsetData {
pub fn new(pdb_store: &PdbStore, windows_version: WindowsVersion) -> Self { pub fn new(pdb_store: &PdbStore, windows_version: WindowsVersion) -> Self {
match windows_version { // TODO: Fix the backend so that only neccessary fields are used
WindowsVersion::Windows10FastRing => Self { // This is too much, most of the functionality has been move to the frontend
eprocess_name_offset: pdb_store.get_offset("_EPROCESS.ImageFileName").unwrap_or(0u64), match windows_version {
eprocess_link_offset: pdb_store.get_offset("_EPROCESS.ActiveProcessLinks").unwrap_or(0u64), WindowsVersion::WindowsFastRing => Self {
list_blink_offset: pdb_store.get_offset("_LIST_ENTRY.Blink").unwrap_or(0u64), eprocess_name_offset: pdb_store
process_head_offset: pdb_store.get_offset("PsActiveProcessHead").unwrap_or(0u64), .get_offset("_EPROCESS.ImageFileName")
mistate_offset: pdb_store.get_offset("MiState").unwrap_or(0u64), .unwrap_or(0u64),
hardware_offset: pdb_store.get_offset("_MI_SYSTEM_INFORMATION.Hardware").unwrap_or(0u64), eprocess_link_offset: pdb_store
system_node_offset: pdb_store.get_offset("_MI_HARDWARE_STATE.SystemNodeNonPagedPool").unwrap_or(0u64), .get_offset("_EPROCESS.ActiveProcessLinks")
first_va_offset: pdb_store.get_offset("_MI_SYSTEM_NODE_NONPAGED_POOL.NonPagedPoolFirstVa").unwrap_or(0u64), .unwrap_or(0u64),
last_va_offset: pdb_store.get_offset("_MI_SYSTEM_NODE_NONPAGED_POOL.NonPagedPoolLastVa").unwrap_or(0u64), list_blink_offset: pdb_store.get_offset("_LIST_ENTRY.Blink").unwrap_or(0u64),
large_page_table_offset: pdb_store.get_offset("PoolBigPageTable").unwrap_or(0u64), process_head_offset: pdb_store.get_offset("PsActiveProcessHead").unwrap_or(0u64),
large_page_size_offset: pdb_store.get_offset("PoolBigPageTableSize").unwrap_or(0u64), mistate_offset: pdb_store.get_offset("MiState").unwrap_or(0u64),
pool_chunk_size: pdb_store.get_offset("_POOL_HEADER.struct_size").unwrap_or(0u64), hardware_offset: pdb_store
}, .get_offset("_MI_SYSTEM_INFORMATION.Hardware")
WindowsVersion::Windows10_2019 | .unwrap_or(0u64),
WindowsVersion::Windows10_2018 => Self { system_node_offset: pdb_store
eprocess_name_offset: pdb_store.get_offset("_EPROCESS.ImageFileName").unwrap_or(0u64), .get_offset("_MI_HARDWARE_STATE.SystemNodeNonPagedPool")
eprocess_link_offset: pdb_store.get_offset("_EPROCESS.ActiveProcessLinks").unwrap_or(0u64), .unwrap_or(0u64),
list_blink_offset: pdb_store.get_offset("_LIST_ENTRY.Blink").unwrap_or(0u64), first_va_offset: pdb_store
process_head_offset: pdb_store.get_offset("PsActiveProcessHead").unwrap_or(0u64), .get_offset("_MI_SYSTEM_NODE_NONPAGED_POOL.NonPagedPoolFirstVa")
mistate_offset: pdb_store.get_offset("MiState").unwrap_or(0u64), .unwrap_or(0u64),
hardware_offset: pdb_store.get_offset("_MI_SYSTEM_INFORMATION.Hardware").unwrap_or(0u64), last_va_offset: pdb_store
system_node_offset: pdb_store.get_offset("_MI_HARDWARE_STATE.SystemNodeInformation").unwrap_or(0u64), .get_offset("_MI_SYSTEM_NODE_NONPAGED_POOL.NonPagedPoolLastVa")
first_va_offset: pdb_store.get_offset("_MI_SYSTEM_NODE_INFORMATION.NonPagedPoolFirstVa").unwrap_or(0u64), .unwrap_or(0u64),
last_va_offset: pdb_store.get_offset("_MI_SYSTEM_NODE_INFORMATION.NonPagedPoolLastVa").unwrap_or(0u64), large_page_table_offset: pdb_store.get_offset("PoolBigPageTable").unwrap_or(0u64),
large_page_table_offset: pdb_store.get_offset("PoolBigPageTable").unwrap_or(0u64), large_page_size_offset: pdb_store
large_page_size_offset: pdb_store.get_offset("PoolBigPageTableSize").unwrap_or(0u64), .get_offset("PoolBigPageTableSize")
pool_chunk_size: pdb_store.get_offset("_POOL_HEADER.struct_size").unwrap_or(0u64), .unwrap_or(0u64),
}, pool_chunk_size: pdb_store
// TODO: Add other version of Windows here .get_offset("_POOL_HEADER.struct_size")
// TODO: Warn user of unknown windows version, because BSOD will occur .unwrap_or(0u64),
_ => Self { },
eprocess_name_offset: 0u64, WindowsVersion::Windows10_2019 | WindowsVersion::Windows10_2018 => Self {
eprocess_link_offset: 0u64, eprocess_name_offset: pdb_store
list_blink_offset: 0u64, .get_offset("_EPROCESS.ImageFileName")
process_head_offset: 0u64, .unwrap_or(0u64),
mistate_offset: 0u64, eprocess_link_offset: pdb_store
hardware_offset: 0u64, .get_offset("_EPROCESS.ActiveProcessLinks")
system_node_offset: 0u64, .unwrap_or(0u64),
first_va_offset: 0u64, list_blink_offset: pdb_store.get_offset("_LIST_ENTRY.Blink").unwrap_or(0u64),
last_va_offset: 0u64, process_head_offset: pdb_store.get_offset("PsActiveProcessHead").unwrap_or(0u64),
large_page_table_offset: 0u64, mistate_offset: pdb_store.get_offset("MiState").unwrap_or(0u64),
large_page_size_offset: 0u64, hardware_offset: pdb_store
pool_chunk_size: 0u64, .get_offset("_MI_SYSTEM_INFORMATION.Hardware")
} .unwrap_or(0u64),
} system_node_offset: pdb_store
} .get_offset("_MI_HARDWARE_STATE.SystemNodeInformation")
} .unwrap_or(0u64),
first_va_offset: pdb_store
#[repr(C)] .get_offset("_MI_SYSTEM_NODE_INFORMATION.NonPagedPoolFirstVa")
#[derive(Debug, Copy, Clone)] .unwrap_or(0u64),
pub struct DerefAddr { last_va_offset: pdb_store
pub addr: u64, .get_offset("_MI_SYSTEM_NODE_INFORMATION.NonPagedPoolLastVa")
pub size: u64 .unwrap_or(0u64),
} large_page_table_offset: pdb_store.get_offset("PoolBigPageTable").unwrap_or(0u64),
large_page_size_offset: pdb_store
#[repr(C)] .get_offset("PoolBigPageTableSize")
#[derive(Debug, Copy, Clone)] .unwrap_or(0u64),
pub struct ScanPoolData { pool_chunk_size: pdb_store
pub start: u64, .get_offset("_POOL_HEADER.struct_size")
pub end: u64, .unwrap_or(0u64),
pub tag: u32 },
} WindowsVersion::Windows7 => Self {
eprocess_name_offset: pdb_store
impl ScanPoolData{ .get_offset("_EPROCESS.ImageFileName")
pub fn new(arr: &[u64; 2], tag: &[u8; 4]) -> Self { .unwrap_or(0u64),
Self { eprocess_link_offset: pdb_store
start: arr[0], .get_offset("_EPROCESS.ActiveProcessLinks")
end: arr[1], .unwrap_or(0u64),
tag: u32::from_le_bytes(*tag) list_blink_offset: pdb_store.get_offset("_LIST_ENTRY.Blink").unwrap_or(0u64),
} process_head_offset: pdb_store.get_offset("PsActiveProcessHead").unwrap_or(0u64),
} mistate_offset: pdb_store.get_offset("MiState").unwrap_or(0u64),
} hardware_offset: pdb_store
.get_offset("_MI_SYSTEM_INFORMATION.Hardware")
#[repr(C)] .unwrap_or(0u64),
#[derive(Debug, Copy, Clone)] system_node_offset: pdb_store
pub struct HideProcess { .get_offset("_MI_HARDWARE_STATE.SystemNodeInformation")
pub name: [u8; 15], .unwrap_or(0u64),
pub size: u64 first_va_offset: pdb_store
} .get_offset("_MI_SYSTEM_NODE_INFORMATION.NonPagedPoolFirstVa")
.unwrap_or(0u64),
#[repr(C)] last_va_offset: pdb_store
pub union InputData { .get_offset("_MI_SYSTEM_NODE_INFORMATION.NonPagedPoolLastVa")
pub offset_value: OffsetData, .unwrap_or(0u64),
pub deref_addr: DerefAddr, large_page_table_offset: pdb_store.get_offset("PoolBigPageTable").unwrap_or(0u64),
pub scan_range: ScanPoolData, large_page_size_offset: pdb_store
pub hide_process: HideProcess, .get_offset("PoolBigPageTableSize")
} .unwrap_or(0u64),
pool_chunk_size: pdb_store
#[repr(C)] .get_offset("_POOL_HEADER.struct_size")
#[derive(Debug, Copy, Clone)] .unwrap_or(0u64),
pub struct Nothing; // for empty data },
// TODO: Add other version of Windows here
#[repr(C)] // TODO: Warn user of unknown windows version, because BSOD will occur
pub union OutputData { _ => Self {
pub nothing: Nothing, eprocess_name_offset: 0u64,
} eprocess_link_offset: 0u64,
list_blink_offset: 0u64,
process_head_offset: 0u64,
mistate_offset: 0u64,
hardware_offset: 0u64,
system_node_offset: 0u64,
first_va_offset: 0u64,
last_va_offset: 0u64,
large_page_table_offset: 0u64,
large_page_size_offset: 0u64,
pool_chunk_size: 0u64,
},
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct DerefAddr {
pub addr: u64,
pub size: u64,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ScanPoolData {
pub start: u64,
pub end: u64,
pub tag: u32,
}
impl ScanPoolData {
pub fn new(arr: &[u64; 2], tag: &[u8; 4]) -> Self {
Self {
start: arr[0],
end: arr[1],
tag: u32::from_le_bytes(*tag),
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct HideProcess {
pub name: [u8; 15],
pub size: u64,
}
#[repr(C)]
pub union InputData {
pub offset_value: OffsetData,
pub deref_addr: DerefAddr,
pub scan_range: ScanPoolData,
pub hide_process: HideProcess,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Nothing; // for empty data
#[repr(C)]
pub union OutputData {
pub nothing: Nothing,
}

View File

@ -1,288 +1,594 @@
extern crate chrono; extern crate app_dirs;
extern crate app_dirs; extern crate chrono;
pub mod pdb_store; pub mod address;
pub mod windows; pub mod commands;
pub mod ioctl_protocol; pub mod driver_state;
pub mod driver_state; pub mod ioctl_protocol;
pub mod address; pub mod object;
pub mod pdb_store;
use std::error::Error; pub mod windows;
use std::str::{from_utf8};
use serde_json::{json, Value}; use app_dirs::AppInfo;
use driver_state::DriverState; use chrono::{DateTime, Local, TimeZone};
use address::Address; use serde_json::{json, Value};
use std::error::Error;
type BoxResult<T> = Result<T, Box<dyn Error>>;
use address::Address;
pub fn scan_eprocess(driver: &DriverState) -> BoxResult<Vec<Value>> { use driver_state::DriverState;
let mut result: Vec<Value> = Vec::new(); use object::*;
driver.scan_pool(b"Proc", "_EPROCESS", |pool_addr, header, data_addr| {
let chunk_size = (header[2] as u64) * 16u64; type BoxResult<T> = Result<T, Box<dyn Error>>;
let eprocess_size = driver.pdb_store.get_offset_r("_EPROCESS.struct_size")?; pub const APP_INFO: AppInfo = AppInfo {
name: "lpus",
let eprocess_valid_start = &data_addr; author: "nganhkhoa",
let eprocess_valid_end = (pool_addr.clone() + chunk_size) - eprocess_size; };
let mut try_eprocess_ptr = eprocess_valid_start.clone();
pub fn to_epoch(filetime: u64) -> DateTime<Local> {
while try_eprocess_ptr <= eprocess_valid_end { // return seconds from epoch
let create_time: u64 = driver.decompose(&try_eprocess_ptr, "_EPROCESS.CreateTime")?; let windows_epoch_diff = 11_644_473_600_000 * 10_000;
if driver.windows_ffi.valid_process_time(create_time) { if filetime < windows_epoch_diff {
break; return Local.timestamp(0, 0);
} }
try_eprocess_ptr += 0x4; // search exhaustively let filetime_epoch = (filetime - windows_epoch_diff) / 10_000_000;
} Local.timestamp(filetime_epoch as i64, 0)
if try_eprocess_ptr > eprocess_valid_end { }
return Ok(false);
} pub fn get_irp_name(idx: usize) -> String {
let irp_names = vec![
let eprocess_ptr = &try_eprocess_ptr; "IRP_MJ_CREATE",
"IRP_MJ_CREATE_NAMED_PIPE",
let pid: u64 = driver.decompose(eprocess_ptr, "_EPROCESS.UniqueProcessId")?; "IRP_MJ_CLOSE",
let ppid: u64 = driver.decompose(eprocess_ptr, "_EPROCESS.InheritedFromUniqueProcessId")?; "IRP_MJ_READ",
let image_name: Vec<u8> = driver.decompose_array(eprocess_ptr, "_EPROCESS.ImageFileName", 15)?; "IRP_MJ_WRITE",
let unicode_str_ptr = driver.address_of(eprocess_ptr, "_EPROCESS.ImageFilePointer.FileName")?; "IRP_MJ_QUERY_INFORMATION",
"IRP_MJ_SET_INFORMATION",
let eprocess_name = "IRP_MJ_QUERY_EA",
if let Ok(name) = from_utf8(&image_name) { "IRP_MJ_SET_EA",
name.to_string().trim_end_matches(char::from(0)).to_string() "IRP_MJ_FLUSH_BUFFERS",
} else { "IRP_MJ_QUERY_VOLUME_INFORMATION",
"".to_string() "IRP_MJ_SET_VOLUME_INFORMATION",
}; "IRP_MJ_DIRECTORY_CONTROL",
let binary_path = driver.get_unicode_string(unicode_str_ptr, true) "IRP_MJ_FILE_SYSTEM_CONTROL",
.unwrap_or("".to_string()); "IRP_MJ_DEVICE_CONTROL",
"IRP_MJ_INTERNAL_DEVICE_CONTROL",
result.push(json!({ "IRP_MJ_SHUTDOWN",
"pool": format!("0x{:x}", pool_addr.address()), "IRP_MJ_LOCK_CONTROL",
"address": format!("0x{:x}", eprocess_ptr.address()), "IRP_MJ_CLEANUP",
"type": "_EPROCESS", "IRP_MJ_CREATE_MAILSLOT",
"pid": pid, "IRP_MJ_QUERY_SECURITY",
"ppid": ppid, "IRP_MJ_SET_SECURITY",
"name": eprocess_name, "IRP_MJ_POWER",
"path": binary_path "IRP_MJ_SYSTEM_CONTROL",
})); "IRP_MJ_DEVICE_CHANGE",
Ok(true) "IRP_MJ_QUERY_QUOTA",
})?; "IRP_MJ_SET_QUOTA",
Ok(result) "IRP_MJ_PNP",
} ]
.iter()
pub fn scan_file(driver: &DriverState) -> BoxResult<Vec<Value>> { .map(|x| x.to_string())
let mut result: Vec<Value> = Vec::new(); .collect::<Vec<String>>();
driver.scan_pool(b"File", "_FILE_OBJECT", |pool_addr, header, data_addr| { if let Some(name) = irp_names.get(idx) {
let chunk_size = (header[2] as u64) * 16u64; name.clone()
} else {
let fob_size = driver.pdb_store.get_offset_r("_FILE_OBJECT.struct_size")?; "UNKNOWN".to_string()
let valid_end = (pool_addr.clone() + chunk_size) - fob_size; }
let mut try_ptr = data_addr; }
while try_ptr <= valid_end { fn get_device_type(typ: u32) -> String {
let ftype: u16 = driver.decompose(&try_ptr, "_FILE_OBJECT.Type")?; match typ {
let size: u16 = driver.decompose(&try_ptr, "_FILE_OBJECT.Size")?; 0x00000027 => "FILE_DEVICE_8042_PORT",
if (size as u64) == fob_size && ftype == 5u16 { 0x00000032 => "FILE_DEVICE_ACPI",
break; 0x00000029 => "FILE_DEVICE_BATTERY",
} 0x00000001 => "FILE_DEVICE_BEEP",
try_ptr += 0x4; // search exhaustively 0x0000002a => "FILE_DEVICE_BUS_EXTENDER",
} 0x00000002 => "FILE_DEVICE_CD_ROM",
if try_ptr > valid_end { 0x00000003 => "FILE_DEVICE_CD_ROM_FILE_SYSTEM",
return Ok(false); 0x00000030 => "FILE_DEVICE_CHANGER",
} 0x00000004 => "FILE_DEVICE_CONTROLLER",
0x00000005 => "FILE_DEVICE_DATALINK",
let fob_addr = &try_ptr; 0x00000006 => "FILE_DEVICE_DFS",
let read_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.ReadAccess")?; 0x00000035 => "FILE_DEVICE_DFS_FILE_SYSTEM",
let write_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.WriteAccess")?; 0x00000036 => "FILE_DEVICE_DFS_VOLUME",
let delete_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.DeleteAccess")?; 0x00000007 => "FILE_DEVICE_DISK",
let share_read_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.SharedRead")?; 0x00000008 => "FILE_DEVICE_DISK_FILE_SYSTEM",
let share_write_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.SharedWrite")?; 0x00000033 => "FILE_DEVICE_DVD",
let share_delete_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.SharedDelete")?; 0x00000009 => "FILE_DEVICE_FILE_SYSTEM",
let filename_ptr = driver.address_of(fob_addr, "_FILE_OBJECT.FileName")?; 0x0000003a => "FILE_DEVICE_FIPS",
let devicename_ptr: u64 = driver.address_of(fob_addr, "_FILE_OBJECT.DeviceObject.DriverObject.DriverName")?; 0x00000034 => "FILE_DEVICE_FULLSCREEN_VIDEO",
let hardware_ptr: u64 = driver.decompose(fob_addr, "_FILE_OBJECT.DeviceObject.DriverObject.HardwareDatabase")?; 0x0000000a => "FILE_DEVICE_INPORT_PORT",
0x0000000b => "FILE_DEVICE_KEYBOARD",
let filename = 0x0000002f => "FILE_DEVICE_KS",
if read_ok == 0 { 0x00000039 => "FILE_DEVICE_KSEC",
"[NOT READABLE]".to_string() 0x0000000c => "FILE_DEVICE_MAILSLOT",
} 0x0000002d => "FILE_DEVICE_MASS_STORAGE",
else if let Ok(n) = driver.get_unicode_string(filename_ptr, true) { 0x0000000d => "FILE_DEVICE_MIDI_IN",
n 0x0000000e => "FILE_DEVICE_MIDI_OUT",
} 0x0000002b => "FILE_DEVICE_MODEM",
else { 0x0000000f => "FILE_DEVICE_MOUSE",
"[NOT A VALID _UNICODE_STRING]".to_string() 0x00000010 => "FILE_DEVICE_MULTI_UNC_PROVIDER",
}; 0x00000011 => "FILE_DEVICE_NAMED_PIPE",
let devicename = driver.get_unicode_string(devicename_ptr, true) 0x00000012 => "FILE_DEVICE_NETWORK",
.unwrap_or("".to_string()); 0x00000013 => "FILE_DEVICE_NETWORK_BROWSER",
let hardware = driver.get_unicode_string(hardware_ptr, true) 0x00000014 => "FILE_DEVICE_NETWORK_FILE_SYSTEM",
.unwrap_or("".to_string()); 0x00000028 => "FILE_DEVICE_NETWORK_REDIRECTOR",
result.push(json!({ 0x00000015 => "FILE_DEVICE_NULL",
"pool": format!("0x{:x}", pool_addr.address()), 0x00000016 => "FILE_DEVICE_PARALLEL_PORT",
"address": format!("0x{:x}", fob_addr.address()), 0x00000017 => "FILE_DEVICE_PHYSICAL_NETCARD",
"type": "_FILE_OBJECT", 0x00000018 => "FILE_DEVICE_PRINTER",
"path": filename, 0x00000019 => "FILE_DEVICE_SCANNER",
"device": devicename, 0x0000001c => "FILE_DEVICE_SCREEN",
"hardware": hardware, 0x00000037 => "FILE_DEVICE_SERENUM",
"access": { 0x0000001a => "FILE_DEVICE_SERIAL_MOUSE_PORT",
"r": read_ok == 1, 0x0000001b => "FILE_DEVICE_SERIAL_PORT",
"w": write_ok == 1, 0x00000031 => "FILE_DEVICE_SMARTCARD",
"d": delete_ok == 1, 0x0000002e => "FILE_DEVICE_SMB",
"R": share_read_ok == 1, 0x0000001d => "FILE_DEVICE_SOUND",
"W": share_write_ok == 1, 0x0000001e => "FILE_DEVICE_STREAMS",
"D": share_delete_ok == 1 0x0000001f => "FILE_DEVICE_TAPE",
} 0x00000020 => "FILE_DEVICE_TAPE_FILE_SYSTEM",
})); 0x00000038 => "FILE_DEVICE_TERMSRV",
Ok(true) 0x00000021 => "FILE_DEVICE_TRANSPORT",
})?; 0x00000022 => "FILE_DEVICE_UNKNOWN",
0x0000002c => "FILE_DEVICE_VDM",
Ok(result) 0x00000023 => "FILE_DEVICE_VIDEO",
} 0x00000024 => "FILE_DEVICE_VIRTUAL_DISK",
0x00000025 => "FILE_DEVICE_WAVE_IN",
pub fn scan_ethread(driver: &DriverState) -> BoxResult<Vec<Value>> { 0x00000026 => "FILE_DEVICE_WAVE_OUT",
let mut result: Vec<Value> = Vec::new(); _ => "UNKNOWN",
}
driver.scan_pool(b"Thre", "_ETHREAD", |pool_addr, header, data_addr| { .to_string()
let chunk_size = (header[2] as u64) * 16u64; }
let ethread_size = driver.pdb_store.get_offset_r("_ETHREAD.struct_size")?; pub fn scan_eprocess(driver: &DriverState) -> BoxResult<Vec<Value>> {
let ethread_valid_start = &data_addr; let mut result: Vec<Value> = Vec::new();
let ethread_valid_end = (pool_addr.clone() + chunk_size) - ethread_size; let tag = if driver.use_old_tag() {
let mut try_ethread_ptr = ethread_valid_start.clone(); b"Pro\xe3"
} else {
while try_ethread_ptr <= ethread_valid_end { b"Proc"
let create_time: u64 = driver.decompose(&try_ethread_ptr, "_ETHREAD.CreateTime")?; };
if driver.windows_ffi.valid_process_time(create_time) { driver.scan_pool(tag, "_EPROCESS", |pool_addr, header, data_addr| {
break; let chunk_size = (header[2] as u64) * 16u64;
}
try_ethread_ptr += 0x4; // search exhaustively let eprocess_size = driver.pdb_store.get_offset_r("_EPROCESS.struct_size")?;
}
if try_ethread_ptr > ethread_valid_end { let eprocess_valid_start = &data_addr;
return Ok(false); let eprocess_valid_end = (pool_addr.clone() + chunk_size) - eprocess_size;
} let mut try_eprocess_ptr = eprocess_valid_start.clone();
let ethread_ptr = &try_ethread_ptr; while try_eprocess_ptr <= eprocess_valid_end {
let create_time: u64 = driver.decompose(&try_eprocess_ptr, "_EPROCESS.CreateTime")?;
let pid: u64 = driver.decompose(ethread_ptr, "_ETHREAD.Cid.UniqueProcess")?; if driver.windows_ffi.valid_process_time(create_time) {
let tid: u64 = driver.decompose(ethread_ptr, "_ETHREAD.Cid.UniqueThread")?; break;
let unicode_str_ptr: u64 = driver.address_of(ethread_ptr, "_ETHREAD.ThreadName")?; }
try_eprocess_ptr += 0x4; // search exhaustively
let thread_name = }
if let Ok(name) = driver.get_unicode_string(unicode_str_ptr, true) { if try_eprocess_ptr > eprocess_valid_end {
name return Ok(false);
} }
else {
"".to_string() result.push(make_eprocess(driver, &try_eprocess_ptr)?);
}; Ok(true)
})?;
result.push(json!({ Ok(result)
"pool": format!("0x{:x}", pool_addr.address()), }
"address": format!("0x{:x}", ethread_ptr.address()),
"type": "_ETHREAD", pub fn scan_file(driver: &DriverState) -> BoxResult<Vec<Value>> {
"pid": pid, let mut result: Vec<Value> = Vec::new();
"tid": tid,
"name": thread_name let tag = if driver.use_old_tag() {
})); b"Fil\xe5"
Ok(true) } else {
})?; b"File"
};
Ok(result) driver.scan_pool(tag, "_FILE_OBJECT", |pool_addr, header, data_addr| {
} let chunk_size = (header[2] as u64) * 16u64;
// Unstable, do not use let fob_size = driver.pdb_store.get_offset_r("_FILE_OBJECT.struct_size")?;
pub fn scan_mutant(driver: &DriverState) -> BoxResult<Vec<Value>> { let valid_end = (pool_addr.clone() + chunk_size) - fob_size;
let mut result: Vec<Value> = Vec::new(); let mut try_ptr = data_addr;
let ntosbase = driver.get_kernel_base(); while try_ptr <= valid_end {
let [start, end] = driver.get_nonpaged_range(&ntosbase)?; let ftype: u16 = driver.decompose(&try_ptr, "_FILE_OBJECT.Type")?;
let size: u16 = driver.decompose(&try_ptr, "_FILE_OBJECT.Size")?;
driver.scan_pool(b"Muta", "_KMUTANT", |pool_addr, header, data_addr| { if (size as u64) == fob_size && ftype == 5u16 {
let chunk_size = (header[2] as u64) * 16u64; break;
}
let kmutant_size = driver.pdb_store.get_offset_r("_KMUTANT.struct_size")?; try_ptr += 0x4; // search exhaustively
}
let kmutant_valid_start = data_addr; if try_ptr > valid_end {
let kmutant_valid_end = (pool_addr.clone() + chunk_size) - kmutant_size; return Ok(false);
let mut try_kmutant_ptr = kmutant_valid_start.clone(); }
while try_kmutant_ptr <= kmutant_valid_end { let fob_addr = &try_ptr;
// TODO: Stronger constrain let read_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.ReadAccess")?;
let kthread_ptr = driver.address_of(&try_kmutant_ptr, "_KMUTANT.OwnerThread")?; let write_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.WriteAccess")?;
if kthread_ptr > start.address() && kthread_ptr < end.address() { let delete_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.DeleteAccess")?;
break; let share_read_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.SharedRead")?;
} let share_write_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.SharedWrite")?;
try_kmutant_ptr += 0x4; // search exhaustively let share_delete_ok: u8 = driver.decompose(fob_addr, "_FILE_OBJECT.SharedDelete")?;
} let filename_ptr = driver.address_of(fob_addr, "_FILE_OBJECT.FileName")?;
if try_kmutant_ptr > kmutant_valid_end { let devicename_ptr: u64 = driver.address_of(
return Ok(false); fob_addr,
} "_FILE_OBJECT.DeviceObject.DriverObject.DriverName",
)?;
let kmutant_ptr = try_kmutant_ptr; let hardware_ptr: u64 = driver.decompose(
let ethread_ptr = Address::from_base(driver.address_of(&kmutant_ptr, "_KMUTANT.OwnerThread")?); fob_addr,
"_FILE_OBJECT.DeviceObject.DriverObject.HardwareDatabase",
let pid: u64 = driver.decompose(&ethread_ptr, "_ETHREAD.Cid.UniqueProcess")?; )?;
let tid: u64 = driver.decompose(&ethread_ptr, "_ETHREAD.Cid.UniqueThread")?;
let unicode_str_ptr: u64 = driver.address_of(&ethread_ptr, "_ETHREAD.ThreadName")?; let filename = if read_ok == 0 {
"[NOT READABLE]".to_string()
let thread_name = } else if let Ok(n) = driver.get_unicode_string(filename_ptr) {
if let Ok(name) = driver.get_unicode_string(unicode_str_ptr, true) { n
name } else {
} "[NOT A VALID _UNICODE_STRING]".to_string()
else { };
"".to_string() let devicename = driver
}; .get_unicode_string(devicename_ptr)
.unwrap_or("".to_string());
result.push(json!({ let hardware = driver
"pool": format!("0x{:x}", pool_addr.address()), .get_unicode_string(hardware_ptr)
"address": format!("0x{:x}", ethread_ptr.address()), .unwrap_or("".to_string());
"type": "_KMUTANT", result.push(json!({
"pid": pid, "pool": format!("0x{:x}", pool_addr.address()),
"tid": tid, "address": format!("0x{:x}", fob_addr.address()),
"name": thread_name "type": "_FILE_OBJECT",
})); "path": filename,
Ok(true) "device": devicename,
})?; "hardware": hardware,
"access": {
Ok(result) "r": read_ok == 1,
} "w": write_ok == 1,
"d": delete_ok == 1,
pub fn scan_driver(driver: &DriverState) -> BoxResult<Vec<Value>> { "R": share_read_ok == 1,
let mut result: Vec<Value> = Vec::new(); "W": share_write_ok == 1,
"D": share_delete_ok == 1
driver.scan_pool(b"Driv", "_DRIVER_OBJECT", |pool_addr, header, data_addr| { }
let chunk_size = (header[2] as u64) * 16u64; }));
Ok(true)
let dob_size = driver.pdb_store.get_offset_r("_DRIVER_OBJECT.struct_size")?; })?;
let valid_end = (pool_addr.clone() + chunk_size) - dob_size;
let mut try_ptr = data_addr; Ok(result)
}
while try_ptr <= valid_end {
// No documentation on type constrain pub fn scan_ethread(driver: &DriverState) -> BoxResult<Vec<Value>> {
// let ftype: u16 = driver.decompose(&try_ptr, "_DRIVER_OBJECT.Type")?; let mut result: Vec<Value> = Vec::new();
let size: u16 = driver.decompose(&try_ptr, "_DRIVER_OBJECT.Size")?;
if (size as u64) == dob_size /* && ftype == 5u16 */ { let tag = if driver.use_old_tag() {
break; b"Thr\xe5"
} } else {
try_ptr += 0x4; // search exhaustively b"Thre"
} };
if try_ptr > valid_end { driver.scan_pool(tag, "_ETHREAD", |pool_addr, header, data_addr| {
return Ok(false); let chunk_size = (header[2] as u64) * 16u64;
}
let dob_addr = &try_ptr; let object_header_size = driver
.pdb_store
let devicename_ptr = driver.address_of(dob_addr, "_DRIVER_OBJECT.DriverName")?; .get_offset_r("_OBJECT_HEADER.struct_size")?;
let hardware_ptr: u64 = driver.decompose(dob_addr, "_DRIVER_OBJECT.HardwareDatabase")?; let header_size = driver.pdb_store.get_offset_r("_POOL_HEADER.struct_size")?;
let ethread_size = driver.pdb_store.get_offset_r("_ETHREAD.struct_size")?;
let devicename = driver.get_unicode_string(devicename_ptr, true) let ethread_valid_start = &data_addr;
.unwrap_or("".to_string()); let ethread_valid_end = (pool_addr.clone() + chunk_size) - ethread_size;
let hardware = driver.get_unicode_string(hardware_ptr, true) let mut try_ethread_ptr = ethread_valid_start.clone();
.unwrap_or("".to_string());
result.push(json!({ if chunk_size == header_size + object_header_size + ethread_size {
"pool": format!("0x{:x}", pool_addr.address()), try_ethread_ptr = ethread_valid_end.clone();
"address": format!("0x{:x}", dob_addr.address()), } else {
"type": "_DRIVER_OBJECT", while try_ethread_ptr <= ethread_valid_end {
"device": devicename, let create_time: u64 = driver.decompose(&try_ethread_ptr, "_ETHREAD.CreateTime")?;
"hardware": hardware if driver.windows_ffi.valid_process_time(create_time) {
})); break;
Ok(true) }
})?; try_ethread_ptr += 0x4; // search exhaustively
}
Ok(result) if try_ethread_ptr > ethread_valid_end {
} return Ok(false);
}
}
result.push(make_ethread(driver, &try_ethread_ptr)?);
Ok(true)
})?;
Ok(result)
}
// Unstable, do not use
// pub fn scan_mutant(driver: &DriverState) -> BoxResult<Vec<Value>> {
// let mut result: Vec<Value> = Vec::new();
//
// let ntosbase = driver.get_kernel_base();
// let [start, end] = driver.get_nonpaged_range(&ntosbase)?;
//
// let tag =
// if driver.use_old_tag() { b"Mut\xe1" }
// else { b"Muta" };
// driver.scan_pool(tag, "_KMUTANT", |pool_addr, header, data_addr| {
// let chunk_size = (header[2] as u64) * 16u64;
//
// let kmutant_size = driver.pdb_store.get_offset_r("_KMUTANT.struct_size")?;
//
// let kmutant_valid_start = data_addr;
// let kmutant_valid_end = (pool_addr.clone() + chunk_size) - kmutant_size;
// let mut try_kmutant_ptr = kmutant_valid_start.clone();
//
// while try_kmutant_ptr <= kmutant_valid_end {
// // TODO: Stronger constrain
// let kthread_ptr = driver.address_of(&try_kmutant_ptr, "_KMUTANT.OwnerThread")?;
// if kthread_ptr > start.address() && kthread_ptr < end.address() {
// break;
// }
// try_kmutant_ptr += 0x4; // search exhaustively
// }
// if try_kmutant_ptr > kmutant_valid_end {
// return Ok(false);
// }
//
// let kmutant_ptr = try_kmutant_ptr;
// let ethread_ptr = Address::from_base(driver.address_of(&kmutant_ptr, "_KMUTANT.OwnerThread")?);
//
// let pid: u64 = driver.decompose(&ethread_ptr, "_ETHREAD.Cid.UniqueProcess")?;
// let tid: u64 = driver.decompose(&ethread_ptr, "_ETHREAD.Cid.UniqueThread")?;
// let unicode_str_ptr: u64 = driver.address_of(&ethread_ptr, "_ETHREAD.ThreadName")?;
//
// let thread_name =
// if let Ok(name) = driver.get_unicode_string(unicode_str_ptr) {
// name
// }
// else {
// "".to_string()
// };
//
// result.push(json!({
// "pool": format!("0x{:x}", pool_addr.address()),
// "address": format!("0x{:x}", ethread_ptr.address()),
// "type": "_KMUTANT",
// "pid": pid,
// "tid": tid,
// "name": thread_name
// }));
// Ok(true)
// })?;
//
// Ok(result)
// }
pub fn scan_driver(driver: &DriverState) -> BoxResult<Vec<Value>> {
let mut result: Vec<Value> = Vec::new();
let tag = if driver.use_old_tag() {
b"Dri\xf6"
} else {
b"Driv"
};
driver.scan_pool(tag, "_DRIVER_OBJECT", |pool_addr, header, data_addr| {
let chunk_size = (header[2] as u64) * 16u64;
let dob_size = driver
.pdb_store
.get_offset_r("_DRIVER_OBJECT.struct_size")?;
let valid_end = (pool_addr.clone() + chunk_size) - dob_size;
let mut try_ptr = data_addr;
while try_ptr <= valid_end {
// No documentation on type constrain
let size: u16 = driver.decompose(&try_ptr, "_DRIVER_OBJECT.Size")?;
if (size as u64) == dob_size {
break;
}
try_ptr += 0x4; // search exhaustively
}
if try_ptr > valid_end {
return Ok(false);
}
result.push(make_driver(driver, &try_ptr)?);
Ok(true)
})?;
Ok(result)
}
pub fn scan_kernel_module(driver: &DriverState) -> BoxResult<Vec<Value>> {
let mut result: Vec<Value> = Vec::new();
driver.scan_pool(
b"MmLd",
"_LDR_DATA_TABLE_ENTRY",
|_pool_addr, _, data_addr| {
// By reversing, this structure does not have any header
result.push(make_ldr(driver, &data_addr)?);
Ok(true)
},
)?;
Ok(result)
}
pub fn traverse_loadedmodulelist(driver: &DriverState) -> BoxResult<Vec<Value>> {
let ntosbase = driver.get_kernel_base();
let module_list_head = ntosbase + driver.pdb_store.get_offset_r("PsLoadedModuleList")?;
let result = make_list_entry(
driver,
module_list_head.clone(),
"_LDR_DATA_TABLE_ENTRY.InLoadOrderLinks",
)?
.iter()
.map(|x| make_ldr(driver, &x).unwrap_or(json!({})))
.collect();
Ok(result)
}
// dx Debugger.Utility.Collections.FromListEntry( *(nt!_LIST_ENTRY*)&(nt!PsActiveProcessHead), "nt!_EPROCESS", "ActiveProcessLinks")
pub fn traverse_activehead(driver: &DriverState) -> BoxResult<Vec<Value>> {
let mut result: Vec<Value> = Vec::new();
let ntosbase = driver.get_kernel_base();
let process_list_head = ntosbase + driver.pdb_store.get_offset_r("PsActiveProcessHead")?;
let eprocess_listentry_offset = driver
.pdb_store
.get_offset_r("_EPROCESS.ActiveProcessLinks")?;
// TODO: make_list_entry
let mut ptr: u64 = driver.decompose(&process_list_head, "_LIST_ENTRY.Flink")?;
while ptr != process_list_head.address() {
let eprocess_ptr = Address::from_base(ptr - eprocess_listentry_offset);
result.push(make_eprocess(driver, &eprocess_ptr)?);
ptr = driver.decompose(&eprocess_ptr, "_EPROCESS.ActiveProcessLinks.Flink")?;
}
Ok(result)
}
// TODO: where is afd!
// dx Debugger.Utility.Collections.FromListEntry( *(nt!_LIST_ENTRY*)&(afd!AfdEndpointListHead), "nt!_EPROCESS", "ActiveProcessLinks")
// pub fn traverse_afdendpoint(driver: &DriverState) -> BoxResult<Vec<Value>> {
// let mut result: Vec<Value> = Vec::new();
//
// let ntosbase = driver.get_kernel_base();
// let process_list_head = ntosbase + driver.pdb_store.get_offset_r("PsActiveProcessHead")?;
// let eprocess_listentry_offset = driver.pdb_store.get_offset_r("_EPROCESS.ActiveProcessLinks")?;
//
// let mut ptr: u64 = driver.decompose(&process_list_head, "_LIST_ENTRY.Flink")?;
// while ptr != process_list_head.address() {
// let eprocess_ptr = Address::from_base(ptr - eprocess_listentry_offset);
//
// let pid: u64 = driver.decompose(&eprocess_ptr, "_EPROCESS.UniqueProcessId")?;
// let ppid: u64 = driver.decompose(&eprocess_ptr, "_EPROCESS.InheritedFromUniqueProcessId")?;
// let image_name: Vec<u8> = driver.decompose_array(&eprocess_ptr, "_EPROCESS.ImageFileName", 15)?;
// let unicode_str_ptr = driver.address_of(&eprocess_ptr, "_EPROCESS.ImageFilePointer.FileName")?;
//
// let eprocess_name =
// if let Ok(name) = from_utf8(&image_name) {
// name.to_string().trim_end_matches(char::from(0)).to_string()
// } else {
// "".to_string()
// };
// let binary_path = driver.get_unicode_string(unicode_str_ptr)
// .unwrap_or("".to_string());
//
// result.push(json!({
// "address": format!("0x{:x}", &eprocess_ptr.address()),
// "type": "_EPROCESS",
// "pid": pid,
// "ppid": ppid,
// "name": eprocess_name,
// "path": binary_path
// }));
//
// ptr = driver.decompose(&eprocess_ptr, "_EPROCESS.ActiveProcessLinks.Flink")?;
// }
//
// Ok(result)
// }
// dx Debugger.Utility.Collections.FromListEntry( *(nt!_LIST_ENTRY*)&(nt!KiProcessListHead), "nt!_KPROCESS", "ProcessListEntry").Select( p => new {Process = (nt!_EPROCESS*)&p )
pub fn traverse_kiprocesslist(driver: &DriverState) -> BoxResult<Vec<Value>> {
let mut result: Vec<Value> = Vec::new();
let ntosbase = driver.get_kernel_base();
let process_list_head = ntosbase + driver.pdb_store.get_offset_r("KiProcessListHead")?;
let eprocess_listentry_offset = driver
.pdb_store
.get_offset_r("_KPROCESS.ProcessListEntry")?;
// TODO: make_list_entry
let mut ptr: u64 = driver.decompose(&process_list_head, "_LIST_ENTRY.Flink")?;
while ptr != process_list_head.address() {
let eprocess_ptr = Address::from_base(ptr - eprocess_listentry_offset);
result.push(make_eprocess(driver, &eprocess_ptr)?);
ptr = driver.decompose(&eprocess_ptr, "_KPROCESS.ProcessListEntry.Flink")?;
}
Ok(result)
}
// dx Debugger.Utility.Collections.FromListEntry(*(nt!_LIST_ENTRY*)&nt!HandleTableListHead, "nt!_HANDLE_TABLE", "HandleTableList").Where(h => h.QuotaProcess != 0).Select( qp => new {Process= qp.QuotaProcess} )
pub fn traverse_handletable(driver: &DriverState) -> BoxResult<Vec<Value>> {
let mut result: Vec<Value> = Vec::new();
let ntosbase = driver.get_kernel_base();
let process_list_head = ntosbase + driver.pdb_store.get_offset_r("HandleTableListHead")?;
let handle_list_offset = driver
.pdb_store
.get_offset_r("_HANDLE_TABLE.HandleTableList")?;
let mut ptr: u64 = driver.decompose(&process_list_head, "_LIST_ENTRY.Flink")?;
while ptr != process_list_head.address() {
let handle_ptr = Address::from_base(ptr - handle_list_offset);
let quota_process: u64 = driver.decompose(&handle_ptr, "_HANDLE_TABLE.QuotaProcess")?;
if quota_process != 0 {
let eprocess_ptr = Address::from_base(quota_process);
result.push(make_eprocess(driver, &eprocess_ptr)?);
}
ptr = driver.decompose(&handle_ptr, "_HANDLE_TABLE.HandleTableList.Flink")?;
}
Ok(result)
}
pub fn traverse_unloadeddrivers(driver: &DriverState) -> BoxResult<Vec<Value>> {
let mut result: Vec<Value> = Vec::new();
let ntosbase = driver.get_kernel_base();
let unload_array_ptr = ntosbase.clone() + driver.pdb_store.get_offset_r("MmUnloadedDrivers")?;
let num_unload_ptr =
ntosbase.clone() + driver.pdb_store.get_offset_r("MmLastUnloadedDriver")?;
let unload_array = driver.deref_addr_new::<u64>(unload_array_ptr.address());
if unload_array == 0 {
return Err("The unload driver list pointer is null".into());
}
// by reversing MmLocateUnloadedDriver
let num_unload = driver.deref_addr_new::<u32>(num_unload_ptr.address()) as u64;
let bound = if num_unload > 0x32 { 0x32 } else { num_unload };
let drivers = (0..bound).map(|i| Address::from_base(unload_array + (i * 0x28)));
for driver_addr in drivers {
let name = driver
.get_unicode_string(driver_addr.address())
.unwrap_or("".to_string());
let start_addr: u64 = driver.decompose(&driver_addr, "_UNLOADED_DRIVERS.StartAddress")?;
let end_addr: u64 = driver.decompose(&driver_addr, "_UNLOADED_DRIVERS.EndAddress")?;
let current_time: u64 = driver.decompose(&driver_addr, "_UNLOADED_DRIVERS.CurrentTime")?;
let time = to_epoch(current_time);
result.push(json!({
"address": format!("0x{:x}", driver_addr.address()),
"type": "_UNLOADED_DRIVERS",
"name": name,
"start_addr": format!("0x{:x}", start_addr),
"end_addr": format!("0x{:x}", end_addr),
"time_unix": time.timestamp(),
"time_rfc2822": time.to_rfc2822()
}));
}
Ok(result)
}
pub fn ssdt_table(driver: &DriverState) -> BoxResult<Vec<u64>> {
// https://github.com/volatilityfoundation/volatility3/blob/master/volatility/framework/plugins/windows/ssdt.py
let ntosbase = driver.get_kernel_base();
let servicetable = ntosbase.clone() + driver.pdb_store.get_offset_r("KiServiceTable")?;
let servicelimit_ptr = ntosbase.clone() + driver.pdb_store.get_offset_r("KiServiceLimit")?;
let servicelimit = driver.deref_addr_new::<u32>(servicelimit_ptr.address()) as u64;
let ssdt: Vec<u64> = driver
.deref_array::<i32>(&servicetable, servicelimit)
.iter()
.map(|entry| {
// the entry can be negative, we need to do calculation using signed int
// and convert back to unsigned int for address
((servicetable.address() as i64) + ((*entry >> 4) as i64)) as u64
})
.collect();
Ok(ssdt)
}

290
src/object.rs Normal file
View File

@ -0,0 +1,290 @@
use crate::address::Address;
use crate::driver_state::DriverState;
use crate::{get_device_type, to_epoch};
use serde_json::{json, Value};
use std::error::Error;
use std::str::from_utf8;
type BoxResult<T> = Result<T, Box<dyn Error>>;
pub fn make_list_entry(d: &DriverState, a: Address, next: &str) -> BoxResult<Vec<Address>> {
// `a` is the address to the _LIST_ENTRY
// `next` is the _LIST_ENTRY field in the object
// return a list of address for object
let mut result: Vec<Address> = Vec::new();
let list_offset = d.pdb_store.get_offset_r(next)?;
let mut ptr: u64 = d.deref_addr_new(a.address());
while ptr != a.address() {
let obj_ptr = Address::from_base(ptr - list_offset);
ptr = d.decompose(&obj_ptr, &format!("{}.Flink", next))?;
result.push(obj_ptr);
}
Ok(result)
}
pub fn make_eprocess(d: &DriverState, a: &Address) -> BoxResult<Value> {
let createtime: u64 = d.decompose(a, "_EPROCESS.CreateTime")?;
let exittime: u64 = d.decompose(a, "_EPROCESS.ExitTime")?;
let pid: u64 = d.decompose(a, "_EPROCESS.UniqueProcessId")?;
let ppid: u64 = d.decompose(a, "_EPROCESS.InheritedFromUniqueProcessId")?;
let image_name: Vec<u8> = d.decompose_array(a, "_EPROCESS.ImageFileName", 15)?;
let filename_ptr = d
.address_of(a, "_EPROCESS.ImageFilePointer.FileName")
.unwrap_or(0); // ImageFilePointer is after Windows 10 Anniversary
let eprocess_name = if let Ok(name) = from_utf8(&image_name) {
name.to_string().trim_end_matches(char::from(0)).to_string()
} else {
"".to_string()
};
let binary_path = d.get_unicode_string(filename_ptr).unwrap_or("".to_string());
let thread_head = d.address_of(a, "_EPROCESS.ThreadListHead")?;
let threads: Vec<Value> = make_list_entry(
d,
Address::from_base(thread_head),
"_ETHREAD.ThreadListEntry",
)
.unwrap_or(Vec::new())
.iter()
.map(|thread_addr| {
make_ethread(d, thread_addr).unwrap_or(json!({})) // unlikely
})
.collect();
let c_t = to_epoch(createtime);
let e_t = to_epoch(exittime);
Ok(json!({
"address": format!("0x{:x}", a.address()),
"type": "_EPROCESS",
"pid": pid,
"ppid": ppid,
"name": eprocess_name,
"path": binary_path,
"threads": threads,
"createtime": {
"unix": c_t.timestamp(),
"rfc2822": c_t.to_rfc2822()
},
"exittime": {
"unix": e_t.timestamp(),
"rfc2822": e_t.to_rfc2822(),
}
}))
}
pub fn make_ethread(d: &DriverState, a: &Address) -> BoxResult<Value> {
// let createtime: u64 = d.decompose(a, "_ETHREAD.CreateTime")?;
// let exittime: u64 = d.decompose(a, "_ETHREAD.ExitTime")?;
let pid: u64 = d.decompose(a, "_ETHREAD.Cid.UniqueProcess")?;
let tid: u64 = d.decompose(a, "_ETHREAD.Cid.UniqueThread")?;
let eprocess: u64 = d.decompose(a, "_ETHREAD.Tcb.Process")?;
let flags: u32 = d.decompose(a, "_ETHREAD.CrossThreadFlags")?;
let state = match d.decompose::<u8>(a, "_ETHREAD.Tcb.State")? {
0 => "Initialized",
1 => "Ready",
2 => "Running",
3 => "Standby",
4 => "Terminated",
5 => "Waiting",
6 => "Transition",
7 => "DeferredReady",
8 => "GateWait",
_ => "Unknown",
};
let wait = match d.decompose::<u8>(a, "_ETHREAD.Tcb.WaitReason")? {
0 => "Executive",
1 => "FreePage",
2 => "PageIn",
3 => "PoolAllocation",
4 => "DelayExecution",
5 => "Suspended",
6 => "UserRequest",
7 => "WrExecutive",
8 => "WrFreePage",
9 => "WrPageIn",
10 => "WrPoolAllocation",
11 => "WrDelayExecution",
12 => "WrSuspended",
13 => "WrUserRequest",
14 => "WrEventPair",
15 => "WrQueue",
16 => "WrLpcReceive",
17 => "WrLpcReply",
18 => "WrVirtualMemory",
19 => "WrPageOut",
20 => "WrRendezvous",
21 => "Spare2",
22 => "Spare3",
23 => "Spare4",
24 => "Spare5",
25 => "Spare6",
26 => "WrKernel",
27 => "WrResource",
28 => "WrPushLock",
29 => "WrMutex",
30 => "WrQuantumEnd",
31 => "WrDispatchInt",
32 => "WrPreempted",
33 => "WrYieldExecution",
34 => "WrFastMutex",
35 => "WrGuardedMutex",
36 => "WrRundown",
37 => "MaximumWaitReason",
_ => "Unknown",
};
let name_ptr: u64 = d.address_of(a, "_ETHREAD.ThreadName").unwrap_or(0); // ThreadName is after Windows 10 Anniversary
let thread_name = if let Ok(name) = d.get_unicode_string(name_ptr) {
name
} else {
"".to_string()
};
// let c_t = to_epoch(createtime);
// let e_t = to_epoch(exittime);
Ok(json!({
"address": format!("0x{:x}", a.address()),
"type": "_ETHREAD",
"tid": tid,
"pid": pid,
"name": thread_name,
"eprocess": format!("0x{:x}", eprocess),
"state": state,
"wait_reason": wait,
"flags": {
"raw": format!("0x{:x}", flags),
"PS_CROSS_THREAD_FLAGS_TERMINATED": flags & 1 != 0,
"PS_CROSS_THREAD_FLAGS_DEADTHREAD": flags & 2 != 0,
"PS_CROSS_THREAD_FLAGS_HIDEFROMDBG": flags & 3 != 0,
"PS_CROSS_THREAD_FLAGS_IMPERSONATING": flags & 4 != 0,
"PS_CROSS_THREAD_FLAGS_SYSTEM": flags & 5 != 0,
"PS_CROSS_THREAD_FLAGS_HARD_ERRORS_DISABLED": flags & 6 != 0,
"PS_CROSS_THREAD_FLAGS_BREAK_ON_TERMINATION": flags & 7 != 0,
"PS_CROSS_THREAD_FLAGS_SKIP_CREATION_MSG": flags & 8 != 0,
"PS_CROSS_THREAD_FLAGS_SKIP_TERMINATION_MSG": flags & 9 != 0,
},
// "createtime": {
// "unix": c_t.timestamp(),
// "rfc2822": c_t.to_rfc2822()
// },
// "exittime": {
// "unix": e_t.timestamp(),
// "rfc2822": e_t.to_rfc2822(),
// }
}))
}
pub fn make_driver(d: &DriverState, a: &Address) -> BoxResult<Value> {
let devicename_ptr = d.address_of(a, "_DRIVER_OBJECT.DriverName")?;
let servicekey_ptr = d.address_of(a, "_DRIVER_OBJECT.DriverExtension.ServiceKeyName")?;
let hardware_ptr: u64 = d.decompose(a, "_DRIVER_OBJECT.HardwareDatabase")?;
let major_function: Vec<u64> = d.decompose_array(a, "_DRIVER_OBJECT.MajorFunction", 28)?;
let start: u64 = d.decompose(a, "_DRIVER_OBJECT.DriverStart")?;
let init: u64 = d.decompose(a, "_DRIVER_OBJECT.DriverInit")?;
let unload: u64 = d.decompose(a, "_DRIVER_OBJECT.DriverUnload")?;
let size: u64 = d.decompose(a, "_DRIVER_OBJECT.DriverSize")?;
let devicename = d
.get_unicode_string(devicename_ptr)
.unwrap_or("".to_string());
let hardware = d.get_unicode_string(hardware_ptr).unwrap_or("".to_string());
let servicekey = d
.get_unicode_string(servicekey_ptr)
.unwrap_or("".to_string());
// device tree walk
let devices = {
let mut driver_devices: Vec<Value> = Vec::new();
let mut device_ptr: u64 = d.decompose(a, "_DRIVER_OBJECT.DeviceObject")?;
while device_ptr != 0 {
let addr = Address::from_base(device_ptr);
let device_type: u32 = d.decompose(&addr, "_DEVICE_OBJECT.DeviceType")?;
// get attached devices
let mut attached_ptr: u64 = d.decompose(&addr, "_DEVICE_OBJECT.AttachedDevice")?;
let mut attached_devices: Vec<Value> = Vec::new();
while attached_ptr != 0 {
let attached = Address::from_base(attached_ptr);
let attached_device_type: u32 =
d.decompose(&attached, "_DEVICE_OBJECT.DeviceType")?;
attached_devices.push(json!({
"address": format!("0x{:x}", attached_ptr),
"type": "_DEVICE_OBJECT",
"devicetype": get_device_type(attached_device_type)
}));
attached_ptr = d.decompose(&attached, "_DEVICE_OBJECT.AttachedDevice")?;
}
driver_devices.push(json!({
"address": format!("0x{:x}", device_ptr),
"type": "_DEVICE_OBJECT",
"devicetype": get_device_type(device_type),
"attached": attached_devices
}));
device_ptr = d.decompose(&addr, "_DEVICE_OBJECT.NextDevice")?;
}
driver_devices
};
Ok(json!({
"address": format!("0x{:x}", a.address()),
"type": "_DRIVER_OBJECT",
"device": devicename,
"hardware": hardware,
"major_function": major_function.into_iter()
.map(|func| format!("0x{:x}", func))
.collect::<Vec<String>>(),
"servicekey": servicekey,
"start": format!("0x{:x}", start),
"init": format!("0x{:x}", init),
"unload": format!("0x{:x}", unload),
"size": format!("0x{:x}", size),
"devicetree": devices
}))
}
pub fn make_ldr(d: &DriverState, a: &Address) -> BoxResult<Value> {
let dllbase: u64 = d.decompose(a, "_LDR_DATA_TABLE_ENTRY.DllBase")?;
let entry: u64 = d.decompose(a, "_LDR_DATA_TABLE_ENTRY.EntryPoint")?;
let size: u64 = d.decompose(a, "_LDR_DATA_TABLE_ENTRY.SizeOfImage")?;
let fullname_ptr = d.address_of(a, "_LDR_DATA_TABLE_ENTRY.FullDllName")?;
let basename_ptr = d.address_of(a, "_LDR_DATA_TABLE_ENTRY.BaseDllName")?;
let fullname = d.get_unicode_string(fullname_ptr).unwrap_or("".to_string());
let basename = d.get_unicode_string(basename_ptr).unwrap_or("".to_string());
let ldr_load: Vec<String> =
make_list_entry(d, a.clone(), "_LDR_DATA_TABLE_ENTRY.InLoadOrderLinks")?
.iter()
.map(|x| format!("0x{:x}", x.address()))
.collect();
let ldr_mem: Vec<String> =
make_list_entry(d, a.clone(), "_LDR_DATA_TABLE_ENTRY.InMemoryOrderLinks")?
.iter()
.map(|x| format!("0x{:x}", x.address()))
.collect();
let ldr_init: Vec<String> = make_list_entry(
d,
a.clone(),
"_LDR_DATA_TABLE_ENTRY.InInitializationOrderLinks",
)?
.iter()
.map(|x| format!("0x{:x}", x.address()))
.collect();
Ok(json!({
"address": format!("0x{:x}", a.address()),
"type": "_LDR_DATA_TABLE_ENTRY",
"dllbase": format!("0x{:x}", dllbase),
"entry": format!("0x{:x}", entry),
"size": format!("0x{:x}", size),
"FullName": fullname,
"BaseName": basename,
"ldr_load": ldr_load,
"ldr_mem": ldr_mem,
"ldr_init": ldr_init
}))
}

View File

@ -1,425 +1,573 @@
use std::error::Error; use std::collections::HashMap;
use std::io; use std::error::Error;
use std::io::{Read}; use std::fs::File;
use std::path::{PathBuf}; use std::io;
use std::fs::File; use std::io::Read;
use std::collections::HashMap; use std::path::PathBuf;
use pdb::{ use app_dirs::{app_dir, AppDataType};
PDB, SymbolData, TypeData, ClassType, ModifierType, Rva, use pdb::{
FallibleIterator, TypeFinder, TypeIndex ClassType, FallibleIterator, ModifierType, Rva, SymbolData, TypeData, TypeFinder, TypeIndex,
}; PDB,
use app_dirs::{AppInfo, AppDataType, app_dir}; };
use crate::address::Address; use crate::address::Address;
use crate::APP_INFO;
const APP_INFO: AppInfo = AppInfo { name: "lpus", author: "nganhkhoa" };
const KERNEL_PDB_NAME: &str = "ntkrnlmp.pdb";
const KERNEL_PDB_NAME: &str = "ntkrnlmp.pdb"; const NTOSKRNL_PATH: &str = "C:\\Windows\\System32\\ntoskrnl.exe";
const NTOSKRNL_PATH: &str = "C:\\Windows\\System32\\ntoskrnl.exe"; const PDB_SERVER_PATH: &str = "http://msdl.microsoft.com/download/symbols";
const PDB_SERVER_PATH: &str = "http://msdl.microsoft.com/download/symbols";
type BoxResult<T> = Result<T, Box<dyn Error>>;
type BoxResult<T> = Result<T, Box<dyn Error>>;
type SymbolStore = HashMap<String, u64>;
type SymbolStore = HashMap<String, u64>; type StructStore = HashMap<String, HashMap<String, (String, u64)>>;
type StructStore = HashMap<String, HashMap<String, (String, u64)>>;
pub struct PdbStore {
pub struct PdbStore { pub symbols: SymbolStore,
pub symbols: SymbolStore, pub structs: StructStore,
pub structs: StructStore }
}
impl PdbStore {
impl PdbStore { pub fn get_offset_r(&self, name: &str) -> BoxResult<u64> {
pub fn get_offset_r(&self, name: &str) -> BoxResult<u64> { self.get_offset(name)
self.get_offset(name) .ok_or(format!("{} is not found in PDB", name).into())
.ok_or(format!("{} is not found in PDB", name).into()) }
} #[allow(dead_code)]
#[allow(dead_code)] pub fn get_offset(&self, name: &str) -> Option<u64> {
pub fn get_offset(&self, name: &str) -> Option<u64> { if name.contains(".") {
if name.contains(".") { let v: Vec<&str> = name.split_terminator('.').collect();
let v: Vec<&str> = name.split_terminator('.').collect(); match self.structs.get(v[0]) {
match self.structs.get(v[0]) { Some(member_info) => match member_info.get(v[1]) {
Some(member_info) => { Some((_memtype, offset)) => Some(*offset),
match member_info.get(v[1]) { None => None,
Some((_memtype, offset)) => Some(*offset), },
None => None None => None,
} }
}, } else {
None => None match self.symbols.get(name) {
} Some(offset) => Some(*offset),
} None => None,
else { }
match self.symbols.get(name) { }
Some(offset) => Some(*offset), }
None => None
} #[allow(dead_code)]
} pub fn addr_decompose(&self, addr: u64, full_name: &str) -> BoxResult<u64> {
} if !full_name.contains(".") {
return Err("Not decomposable".into());
#[allow(dead_code)] }
pub fn addr_decompose(&self, addr: u64, full_name: &str) -> BoxResult<u64>{
if !full_name.contains(".") { let mut name_part: Vec<&str> = full_name.split_terminator('.').collect();
return Err("Not decomposable".into()); let mut next: Vec<_> = name_part.drain(2..).collect();
} match self.structs.get(name_part[0]) {
Some(member_info) => match member_info.get(name_part[1]) {
let mut name_part: Vec<&str> = full_name.split_terminator('.').collect(); Some((memtype, offset)) => {
let mut next: Vec<_> = name_part.drain(2..).collect(); if next.len() != 0 {
match self.structs.get(name_part[0]) { if memtype.contains("*") {
Some(member_info) => { return Err(format!(
match member_info.get(name_part[1]) { "Cannot dereference pointer at {} {}",
Some((memtype, offset)) => { memtype, name_part[1]
if next.len() != 0 { )
if memtype.contains("*") { .into());
return Err(format!("Cannot dereference pointer at {} {}", memtype, name_part[1]).into()); }
} next.insert(0, memtype);
next.insert(0, memtype); self.addr_decompose(addr + *offset, &next.join("."))
self.addr_decompose(addr + *offset, &next.join(".")) } else {
} Ok(addr + *offset)
else { }
Ok(addr + *offset) }
} None => Err(format!("Not found member {}", name_part[1]).into()),
}, },
None => Err(format!("Not found member {}", name_part[1]).into()) None => Err(format!("Struct {} not found", name_part[0]).into()),
} }
}, }
None => Err(format!("Struct {} not found", name_part[0]).into())
} pub fn decompose(&self, source: &Address, full_name: &str) -> BoxResult<Address> {
} // println!("decompose {}", full_name);
if !full_name.contains(".") {
pub fn decompose(&self, source: &Address, full_name: &str) -> BoxResult<Address> { return Err("Not decomposable".into());
// println!("decompose {}", full_name); }
if !full_name.contains(".") {
return Err("Not decomposable".into()); let mut name_part: Vec<&str> = full_name.split_terminator('.').collect();
} let mut next: Vec<_> = name_part.drain(2..).collect();
let member_info = self
let mut name_part: Vec<&str> = full_name.split_terminator('.').collect(); .structs
let mut next: Vec<_> = name_part.drain(2..).collect(); .get(name_part[0])
let member_info = self.structs.get(name_part[0]) .ok_or(format!("No struct {}", name_part[0]))?;
.ok_or(format!("No struct {}", name_part[0]))?; let (memtype, offset) = member_info
let (memtype, offset) = member_info.get(name_part[1]) .get(name_part[1])
.ok_or(format!("No member {} in {}", name_part[1], name_part[0]))?; .ok_or(format!("No member {} in {}", name_part[1], name_part[0]))?;
if next.len() == 0 { if next.len() == 0 {
return Ok(source.clone() + *offset); return Ok(source.clone() + *offset);
} }
if memtype.contains("*") { if memtype.contains("*") {
let mut t = memtype.clone(); // remove * let mut t = memtype.clone(); // remove *
t.pop(); t.pop();
next.insert(0, &t); next.insert(0, &t);
let p = Address::from_ptr(source.clone() + *offset); let p = Address::from_ptr(source.clone() + *offset);
self.decompose(&p, &next.join(".")) self.decompose(&p, &next.join("."))
} else {
} next.insert(0, memtype);
else { self.decompose(&(source.clone() + *offset), &next.join("."))
next.insert(0, memtype); }
self.decompose(&(source.clone() + *offset), &next.join(".")) }
}
} #[allow(dead_code)]
pub fn print_default_information(&self) {
#[allow(dead_code)] let need_symbols = [
pub fn print_default_information(&self) { "PsLoadedModuleList",
let need_symbols = [ "PsActiveProcessHead",
"PsLoadedModuleList", "PsActiveProcessHead", "KeNumberNodes", "KeNumberNodes",
"PoolBigPageTable", "PoolBigPageTableSize", "PoolBigPageTable",
// "PoolVector", "ExpNumberOfNonPagedPools", "PoolBigPageTableSize",
"KdDebuggerDataBlock", "MmNonPagedPoolStart", "MmNonPagedPoolEnd", // Windows XP // "PoolVector", "ExpNumberOfNonPagedPools",
"MiNonPagedPoolStartAligned", "MiNonPagedPoolEnd", "MiNonPagedPoolBitMap", // Windows 7, 8 "KdDebuggerDataBlock",
"MiNonPagedPoolBitMap", "MiNonPagedPoolVaBitMap", "MmNonPagedPoolStart",
"MiState" // Windows 10 "MmNonPagedPoolEnd", // Windows XP
]; "MiNonPagedPoolStartAligned",
"MiNonPagedPoolEnd",
let mut need_structs = HashMap::new(); "MiNonPagedPoolBitMap", // Windows 7, 8
need_structs.insert("_POOL_HEADER", vec![ "MiNonPagedPoolBitMap",
"struct_size", "MiNonPagedPoolVaBitMap",
"PoolType", "BlockSize", "PoolTag" "MiState", // Windows 10
]); ];
need_structs.insert("_PEB", vec![]);
need_structs.insert("_LIST_ENTRY", vec![ let mut need_structs = HashMap::new();
"Flink", "Blink" need_structs.insert(
]); "_POOL_HEADER",
need_structs.insert("_FILE_OBJECT", vec![ vec!["struct_size", "PoolType", "BlockSize", "PoolTag"],
"FileName" );
]); need_structs.insert("_PEB", vec![]);
need_structs.insert("_EPROCESS", vec![ need_structs.insert("_LIST_ENTRY", vec!["Flink", "Blink"]);
"struct_size", need_structs.insert("_FILE_OBJECT", vec!["FileName"]);
"UniqueProcessId", "ActiveProcessLinks", "CreateTime", need_structs.insert(
"Peb", "ImageFilePointer", "ImageFileName", "ThreadListHead" "_EPROCESS",
]); vec![
need_structs.insert("_KDDEBUGGER_DATA64", vec![ "struct_size",
"MmNonPagedPoolStart", "MmNonPagedPoolEnd", // Windows XP "UniqueProcessId",
]); "ActiveProcessLinks",
need_structs.insert("_POOL_TRACKER_BIG_PAGES", vec![]); "CreateTime",
"Peb",
// these struct supports finding NonPagedPool{First,Last}Va in windows 10 "ImageFilePointer",
need_structs.insert("_MI_SYSTEM_INFORMATION", vec![ "ImageFileName",
"Hardware", // windows 10 2016+ "ThreadListHead",
"SystemNodeInformation" // windows 10 2015 ],
]); );
need_structs.insert("_MI_HARDWARE_STATE", vec![ need_structs.insert(
"SystemNodeInformation", // till windows 10 1900 "_KDDEBUGGER_DATA64",
"SystemNodeNonPagedPool" // windows insider, 2020 vec![
]); "MmNonPagedPoolStart",
need_structs.insert("_MI_SYSTEM_NODE_INFORMATION", vec![ // till windows 10 1900 "MmNonPagedPoolEnd", // Windows XP
"NonPagedPoolFirstVa", "NonPagedPoolLastVa", ],
"NonPagedBitMap", // missing on windows 10 1900+ );
"DynamicBitMapNonPagedPool" // some weird field need_structs.insert("_POOL_TRACKER_BIG_PAGES", vec![]);
]);
need_structs.insert("_MI_SYSTEM_NODE_NONPAGED_POOL", vec![ // windows insider, 2020 // these struct supports finding NonPagedPool{First,Last}Va in windows 10
"NonPagedPoolFirstVa", "NonPagedPoolLastVa", need_structs.insert(
"DynamicBitMapNonPagedPool" // some weird field "_MI_SYSTEM_INFORMATION",
]); vec![
need_structs.insert("_MI_DYNAMIC_BITMAP", vec![]); "Hardware", // windows 10 2016+
need_structs.insert("_RTL_BITMAP", vec![]); // windows 10 until 2020 "SystemNodeInformation", // windows 10 2015
need_structs.insert("_RTL_BITMAP_EX", vec![]); // windows insider, 2020 ],
);
for &symbol in &need_symbols { need_structs.insert(
match self.symbols.get(symbol) { "_MI_HARDWARE_STATE",
Some(offset) => println!("0x{:x} {}", offset, symbol), vec![
None => {} "SystemNodeInformation", // till windows 10 1900
} "SystemNodeNonPagedPool", // windows insider, 2020
} ],
);
for (&struct_name, members) in &need_structs { need_structs.insert(
match self.structs.get(struct_name) { "_MI_SYSTEM_NODE_INFORMATION",
Some(member_info) => { vec![
for &member in members { // till windows 10 1900
match member_info.get(member) { "NonPagedPoolFirstVa",
Some((memtype, offset)) => "NonPagedPoolLastVa",
println!("0x{:x} {} {}.{}", offset, memtype, struct_name, member), "NonPagedBitMap", // missing on windows 10 1900+
None => {} "DynamicBitMapNonPagedPool", // some weird field
} ],
} );
}, need_structs.insert(
None => {} "_MI_SYSTEM_NODE_NONPAGED_POOL",
} vec![
} // windows insider, 2020
} "NonPagedPoolFirstVa",
} "NonPagedPoolLastVa",
"DynamicBitMapNonPagedPool", // some weird field
fn get_type_as_str(type_finder: &TypeFinder, typ: &TypeIndex) -> String { ],
match type_finder.find(*typ).unwrap().parse().unwrap() { );
TypeData::Class(ct) => { need_structs.insert("_MI_DYNAMIC_BITMAP", vec![]);
format!("{}", ct.name.to_string()) need_structs.insert("_RTL_BITMAP", vec![]); // windows 10 until 2020
}, need_structs.insert("_RTL_BITMAP_EX", vec![]); // windows insider, 2020
TypeData::Primitive(pt) => {
format!("{:?}", pt.kind) for &symbol in &need_symbols {
}, match self.symbols.get(symbol) {
TypeData::Pointer(pt) => { Some(offset) => println!("0x{:x} {}", offset, symbol),
format!("{}*", get_type_as_str(type_finder, &pt.underlying_type)) None => {}
}, }
TypeData::StaticMember(st) => { }
format!("static {}", get_type_as_str(type_finder, &st.field_type))
}, for (&struct_name, members) in &need_structs {
TypeData::Array(at) => { match self.structs.get(struct_name) {
format!("{}{:?}", Some(member_info) => {
get_type_as_str(type_finder, &at.element_type), /* get_type_as_str(type_finder, &at.indexing_type), */ at.dimensions) for &member in members {
}, match member_info.get(member) {
// TypeData::Enumeration(et) => { Some((memtype, offset)) => {
// format!("enumeration") println!("0x{:x} {} {}.{}", offset, memtype, struct_name, member)
// }, }
// TypeData::Enumerate(et) => { None => {}
// format!("enumerate") }
// }, }
// TypeData::MemberFunction(mft) => { }
// format!("member function") None => {}
// }, }
// TypeData::OverloadedMethod(ovmt) => { }
// format!("overloaded method") }
// },
// TypeData::Nested(nt) => { pub fn dt(&self, struct_name: &str) -> BoxResult<()> {
// format!("nested") let member_info = self
// }, .structs
// TypeData::BaseClass(bct) => { .get(struct_name)
// format!("base class") .ok_or(format!("no struct named {}", struct_name))?;
// }, let (_, struct_size) = member_info.get("struct_size").ok_or("")?;
// TypeData::VirtualBaseClass(vbct) => { println!("// 0x{:x} bytes", struct_size);
// format!("virtual base class") println!("struct {} {{", struct_name);
// },
// TypeData::VirtualFunctionTablePointer(vftpt) => { // Vec<(offset, type, name)>
// format!("virtual function table pointer") let mut members: Vec<(u64, String, String)> = Vec::new();
// }, for (name, (t, offset)) in member_info.iter() {
TypeData::Procedure(pt) => { if name != "struct_size" {
let rettype = match pt.return_type { members.push((*offset, t.to_string(), name.to_string()));
Some(rt) => get_type_as_str(type_finder, &rt), }
_ => "UNKNOWN".to_string() }
}; members.sort_by(|(o1, _, _), (o2, _, _)| o1.partial_cmp(o2).unwrap());
format!("{}({})", rettype, get_type_as_str(type_finder, &pt.argument_list))
}, for (offset, memtype, member) in members.iter() {
TypeData::Modifier(mt) => { println!(" +0x{:x} {} {};", offset, memtype, member);
match mt { }
ModifierType { constant: true, volatile: true, unaligned: true, .. } =>
format!("const volatile unaligned {}", get_type_as_str(type_finder, &mt.underlying_type)), println!("}} // {}", struct_name);
ModifierType { constant: true, volatile: true, unaligned: false, .. } => Ok(())
format!("const volatile {}", get_type_as_str(type_finder, &mt.underlying_type)), }
ModifierType { constant: true, volatile: false, unaligned: true, .. } => }
format!("const unaligned {}", get_type_as_str(type_finder, &mt.underlying_type)),
ModifierType { constant: false, volatile: true, unaligned: true, .. } => fn get_type_as_str(type_finder: &TypeFinder, typ: &TypeIndex) -> String {
format!("volatile unaligned {}", get_type_as_str(type_finder, &mt.underlying_type)), match type_finder.find(*typ).unwrap().parse().unwrap() {
ModifierType { constant: true, volatile: false, unaligned: false, .. } => TypeData::Class(ct) => format!("{}", ct.name.to_string()),
format!("const {}", get_type_as_str(type_finder, &mt.underlying_type)), TypeData::Primitive(pt) => format!("{:?}", pt.kind),
ModifierType { constant: false, volatile: true, unaligned: false, .. } => TypeData::Pointer(pt) => format!("{}*", get_type_as_str(type_finder, &pt.underlying_type)),
format!("volatile {}", get_type_as_str(type_finder, &mt.underlying_type)), TypeData::StaticMember(st) => {
ModifierType { constant: false, volatile: false, unaligned: true, .. } => format!("static {}", get_type_as_str(type_finder, &st.field_type))
format!("unaligned {}", get_type_as_str(type_finder, &mt.underlying_type)), }
_ => format!("modifier {}", get_type_as_str(type_finder, &mt.underlying_type)) TypeData::Array(at) => {
} format!(
}, "{}{:?}",
// TypeData::Union(ut) => { get_type_as_str(type_finder, &at.element_type),
// format!("union") /* get_type_as_str(type_finder, &at.indexing_type), */ at.dimensions
// }, )
// TypeData::Bitfield(bft) => { }
// format!("bitfield") // TypeData::Enumeration(et) => {
// }, // format!("enumeration")
TypeData::FieldList(_flt) => { // },
format!("fieldlist") // TypeData::Enumerate(et) => {
}, // format!("enumerate")
// TypeData::ArgumentList(alt) => { // },
// format!("arglist") // TypeData::MemberFunction(mft) => {
// }, // format!("member function")
// TypeData::MethodList(mlt) => { // },
// format!("methodlist") // TypeData::OverloadedMethod(ovmt) => {
// }, // format!("overloaded method")
unk => { // },
match unk.name() { // TypeData::Nested(nt) => {
Some(s) => format!("{}", s.to_string()), // format!("nested")
_ => "UNNOWN".to_string() // },
} // TypeData::BaseClass(bct) => {
} // format!("base class")
} // },
} // TypeData::VirtualBaseClass(vbct) => {
// format!("virtual base class")
fn get_guid_age(exe_file: &str) -> BoxResult<(String, u32)>{ // },
// TODO: Check file existance // TypeData::VirtualFunctionTablePointer(vftpt) => {
let mut file = File::open(exe_file)?; // format!("virtual function table pointer")
// },
let mut buffer = Vec::new(); TypeData::Procedure(pt) => {
file.read_to_end(&mut buffer)?; let rettype = match pt.return_type {
Some(rt) => get_type_as_str(type_finder, &rt),
let mut buffiter = buffer.chunks(4); _ => "UNKNOWN".to_string(),
while buffiter.next().unwrap() != [0x52, 0x53, 0x44, 0x53] { };
// signature == RSDS format!(
} "{}({})",
rettype,
// next 16 bytes is guid in raw bytes get_type_as_str(type_finder, &pt.argument_list)
let raw_guid: Vec<u8> = vec![ )
buffiter.next().unwrap(), }
buffiter.next().unwrap(), TypeData::Modifier(mt) => match mt {
buffiter.next().unwrap(), ModifierType {
buffiter.next().unwrap(), constant: true,
].concat(); volatile: true,
unaligned: true,
// guid to hex string ..
let guid = (vec![ } => format!(
raw_guid[3], raw_guid[2], raw_guid[1], raw_guid[0], "const volatile unaligned {}",
raw_guid[5], raw_guid[4], get_type_as_str(type_finder, &mt.underlying_type)
raw_guid[7], raw_guid[6], ),
raw_guid[8], raw_guid[9], raw_guid[10], raw_guid[11], ModifierType {
raw_guid[12], raw_guid[13], raw_guid[14], raw_guid[15], constant: true,
].iter().map(|b| format!("{:02X}", b)).collect::<Vec<String>>()).join(""); volatile: true,
unaligned: false,
// next 4 bytes is age, in little endian ..
let raw_age = buffiter.next().unwrap(); } => format!(
let age = u32::from_le_bytes([ "const volatile {}",
raw_age[0], raw_age[1], raw_age[2], raw_age[3] get_type_as_str(type_finder, &mt.underlying_type)
]); ),
ModifierType {
Ok((guid, age)) constant: true,
} volatile: false,
unaligned: true,
fn pdb_exists(pdbname: &str, guid: &str, age: u32) -> BoxResult<(bool, PathBuf)> { ..
// Use a folder at %APPDATA% to save pdb files } => format!(
// %APPDATA%\nganhkhoaa\lpus "const unaligned {}",
// |--ntkrnlmp.pdb get_type_as_str(type_finder, &mt.underlying_type)
// |--|--GUID ),
// |--|--|--ntkrnlmp.pdb ModifierType {
// |--file.pdb constant: false,
// |--|--GUID volatile: true,
// |--|--|--file.pdb unaligned: true,
let mut pdb_location = app_dir(AppDataType::UserData, &APP_INFO, ..
&format!("{}/{}/{}", pdbname, guid, age))?; } => format!(
pdb_location.push(pdbname); "volatile unaligned {}",
Ok((pdb_location.exists(), pdb_location)) get_type_as_str(type_finder, &mt.underlying_type)
} ),
ModifierType {
fn download_pdb(pdbname: &str, guid: &str, age: u32, outfile: &PathBuf) -> BoxResult<()> { constant: true,
let downloadurl = format!("{}/{}/{}{:X}/{}", PDB_SERVER_PATH, pdbname, guid, age, pdbname); volatile: false,
println!("{}", downloadurl); unaligned: false,
..
let mut resp = reqwest::blocking::get(&downloadurl)?; } => format!(
let mut out = File::create(outfile)?; "const {}",
io::copy(&mut resp, &mut out)?; get_type_as_str(type_finder, &mt.underlying_type)
Ok(()) ),
} ModifierType {
constant: false,
pub fn parse_pdb() -> BoxResult<PdbStore> { volatile: true,
// TODO: Resolve pdb name unaligned: false,
// ntoskrnl.exe -> ntkrnlmp.pdb ..
// tcpip.sys -> tcpip.pdb ????? } => format!(
// There may be more pdb files in the future "volatile {}",
let (guid, age) = get_guid_age(NTOSKRNL_PATH)?; get_type_as_str(type_finder, &mt.underlying_type)
let (exists, pdb_path) = pdb_exists(KERNEL_PDB_NAME, &guid, age)?; ),
if !exists { ModifierType {
println!("PDB not found, download into {:?}", pdb_path); constant: false,
download_pdb(KERNEL_PDB_NAME, &guid, age, &pdb_path)?; volatile: false,
} unaligned: true,
let f = File::open(pdb_path)?; ..
let mut pdb = PDB::open(f)?; } => format!(
"unaligned {}",
let info = pdb.pdb_information()?; get_type_as_str(type_finder, &mt.underlying_type)
let dbi = pdb.debug_information()?; ),
println!("PDB for {}, guid: {}, age: {}\n", _ => format!(
dbi.machine_type().unwrap(), info.guid, dbi.age().unwrap_or(0)); "modifier {}",
get_type_as_str(type_finder, &mt.underlying_type)
let type_information = pdb.type_information()?; ),
let mut type_finder = type_information.type_finder(); },
let mut iter = type_information.iter(); // TypeData::Union(ut) => {
while let Some(_typ) = iter.next().unwrap() { // format!("union")
type_finder.update(&iter); // },
} // TypeData::Bitfield(bft) => {
// format!("bitfield")
let mut symbol_extracted: SymbolStore = HashMap::new(); // },
let addr_map = pdb.address_map()?; TypeData::FieldList(_flt) => format!("fieldlist"),
let glosym = pdb.global_symbols()?; // TypeData::ArgumentList(alt) => {
let mut symbols = glosym.iter(); // format!("arglist")
while let Some(symbol) = symbols.next().unwrap() { // },
match symbol.parse() { // TypeData::MethodList(mlt) => {
Ok(SymbolData::PublicSymbol(data)) => { // format!("methodlist")
let name = symbol.name().unwrap().to_string(); // },
let Rva(rva) = data.offset.to_rva(&addr_map).unwrap_or_default(); unk => match unk.name() {
symbol_extracted.insert(format!("{}", name), rva as u64); Some(s) => format!("{}", s.to_string()),
}, _ => "UNNOWN".to_string(),
_ => { },
} }
} }
}
fn get_guid_age(exe_file: &str) -> BoxResult<(String, u32)> {
let mut struct_extracted: StructStore = HashMap::new(); // TODO: Check file existance
iter = type_information.iter(); let mut file = File::open(exe_file)?;
while let Some(typ) = iter.next().unwrap() {
match typ.parse() { let mut buffer = Vec::new();
Ok(TypeData::Class(ClassType {name, fields: Some(fields), size, ..})) => { file.read_to_end(&mut buffer)?;
let mut struct_fields = HashMap::new();
struct_fields.insert("struct_size".to_string(), ("u32".to_string(), size as u64)); let mut buffiter = buffer.chunks(4);
match type_finder.find(fields).unwrap().parse().unwrap() { while buffiter.next().unwrap() != [0x52, 0x53, 0x44, 0x53] {
TypeData::FieldList(list) => { // signature == RSDS
for field in list.fields { }
if let TypeData::Member(member) = field {
let mem_typ = get_type_as_str(&type_finder, &member.field_type); // next 16 bytes is guid in raw bytes
struct_fields.insert( let raw_guid: Vec<u8> = vec![
format!("{}", member.name), (mem_typ, member.offset as u64)); buffiter.next().unwrap(),
} buffiter.next().unwrap(),
} buffiter.next().unwrap(),
} buffiter.next().unwrap(),
_ => {} ]
} .concat();
struct_extracted.insert(format!("{}", name), struct_fields);
}, // guid to hex string
_ => {} let guid = (vec![
} raw_guid[3],
} raw_guid[2],
raw_guid[1],
Ok(PdbStore { raw_guid[0],
symbols: symbol_extracted, raw_guid[5],
structs: struct_extracted raw_guid[4],
}) raw_guid[7],
} raw_guid[6],
raw_guid[8],
raw_guid[9],
raw_guid[10],
raw_guid[11],
raw_guid[12],
raw_guid[13],
raw_guid[14],
raw_guid[15],
]
.iter()
.map(|b| format!("{:02X}", b))
.collect::<Vec<String>>())
.join("");
// next 4 bytes is age, in little endian
let raw_age = buffiter.next().unwrap();
let age = u32::from_le_bytes([raw_age[0], raw_age[1], raw_age[2], raw_age[3]]);
Ok((guid, age))
}
fn pdb_exists(pdbname: &str, guid: &str, age: u32) -> BoxResult<(bool, PathBuf)> {
// Use a folder at %APPDATA% to save pdb files
// %APPDATA%\nganhkhoaa\lpus
// |--ntkrnlmp.pdb
// |--|--GUID
// |--|--|--ntkrnlmp.pdb
// |--file.pdb
// |--|--GUID
// |--|--|--file.pdb
let mut pdb_location = app_dir(
AppDataType::UserData,
&APP_INFO,
&format!("{}/{}/{}", pdbname, guid, age),
)?;
pdb_location.push(pdbname);
Ok((pdb_location.exists(), pdb_location))
}
fn download_pdb(pdbname: &str, guid: &str, age: u32, outfile: &PathBuf) -> BoxResult<()> {
let downloadurl = format!(
"{}/{}/{}{:X}/{}",
PDB_SERVER_PATH, pdbname, guid, age, pdbname
);
println!("{}", downloadurl);
let mut resp = reqwest::blocking::get(&downloadurl)?;
let mut out = File::create(outfile)?;
io::copy(&mut resp, &mut out)?;
Ok(())
}
pub fn parse_pdb() -> BoxResult<PdbStore> {
// TODO: Resolve pdb name
// ntoskrnl.exe -> ntkrnlmp.pdb
// tcpip.sys -> tcpip.pdb ?????
// There may be more pdb files in the future
let (guid, age) = get_guid_age(NTOSKRNL_PATH)?;
let (exists, pdb_path) = pdb_exists(KERNEL_PDB_NAME, &guid, age)?;
if !exists {
println!("PDB not found, download into {:?}", pdb_path);
download_pdb(KERNEL_PDB_NAME, &guid, age, &pdb_path)?;
}
let f = File::open(pdb_path)?;
let mut pdb = PDB::open(f)?;
let info = pdb.pdb_information()?;
let dbi = pdb.debug_information()?;
println!(
"PDB for {}, guid: {}, age: {}\n",
dbi.machine_type().unwrap(),
info.guid,
dbi.age().unwrap_or(0)
);
let type_information = pdb.type_information()?;
let mut type_finder = type_information.type_finder();
let mut iter = type_information.iter();
while let Some(_typ) = iter.next().unwrap() {
type_finder.update(&iter);
}
let mut symbol_extracted: SymbolStore = HashMap::new();
let addr_map = pdb.address_map()?;
let glosym = pdb.global_symbols()?;
let mut symbols = glosym.iter();
while let Some(symbol) = symbols.next().unwrap() {
match symbol.parse() {
Ok(SymbolData::PublicSymbol(data)) => {
let name = symbol.name().unwrap().to_string();
let Rva(rva) = data.offset.to_rva(&addr_map).unwrap_or_default();
symbol_extracted.insert(format!("{}", name), rva as u64);
}
_ => {}
}
}
let mut struct_extracted: StructStore = HashMap::new();
iter = type_information.iter();
while let Some(typ) = iter.next().unwrap() {
match typ.parse() {
Ok(TypeData::Class(ClassType {
name,
fields: Some(fields),
size,
..
})) => {
let mut struct_fields = HashMap::new();
struct_fields.insert("struct_size".to_string(), ("U32".to_string(), size as u64));
match type_finder.find(fields).unwrap().parse().unwrap() {
TypeData::FieldList(list) => {
for field in list.fields {
if let TypeData::Member(member) = field {
let mem_typ = get_type_as_str(&type_finder, &member.field_type);
struct_fields.insert(
format!("{}", member.name),
(mem_typ, member.offset as u64),
);
}
}
}
_ => {}
}
struct_extracted.insert(format!("{}", name), struct_fields);
}
_ => {}
}
}
{
// https://github.com/Zer0Mem0ry/ntoskrnl/blob/master/Include/mm.h#L1107
let mut unload_driver_member = HashMap::new();
unload_driver_member.insert("struct_size".to_string(), ("U32".to_string(), 0x30));
unload_driver_member.insert("Name".to_string(), ("_UNICODE_STRING".to_string(), 0));
unload_driver_member.insert("StartAddress".to_string(), ("PVOID".to_string(), 0x10));
unload_driver_member.insert("EndAddress".to_string(), ("PVOID".to_string(), 0x18));
unload_driver_member.insert(
"CurrentTime".to_string(),
("_LARGE_INTEGER".to_string(), 0x20),
);
struct_extracted.insert("_UNLOADED_DRIVERS".to_string(), unload_driver_member);
}
Ok(PdbStore {
symbols: symbol_extracted,
structs: struct_extracted,
})
}

View File

@ -1,257 +1,366 @@
use std::ffi::{c_void, CString}; use std::ffi::{c_void, CString};
use std::mem::{transmute, size_of_val}; use std::mem::{size_of_val, transmute};
use std::ptr::null_mut; use std::ptr::null_mut;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use widestring::U16CString;
use app_dirs::{app_dir, AppDataType};
use winapi::shared::ntdef::*; use widestring::U16CString;
use winapi::shared::minwindef::{DWORD, HKEY, HMODULE};
use winapi::um::winnt::{ use crate::APP_INFO;
SE_PRIVILEGE_ENABLED, TOKEN_PRIVILEGES, TOKEN_ADJUST_PRIVILEGES, LUID_AND_ATTRIBUTES,
REG_DWORD, REG_SZ, REG_OPTION_NON_VOLATILE, KEY_WRITE, use winapi::shared::minwindef::{DWORD, HKEY, HMODULE};
PRTL_OSVERSIONINFOW, OSVERSIONINFOW, use winapi::shared::ntdef::*;
FILE_ATTRIBUTE_NORMAL, GENERIC_READ, GENERIC_WRITE use winapi::um::winnt::{
}; FILE_ATTRIBUTE_NORMAL, GENERIC_READ, GENERIC_WRITE, KEY_WRITE, LUID_AND_ATTRIBUTES,
OSVERSIONINFOW, PRTL_OSVERSIONINFOW, REG_DWORD, REG_OPTION_NON_VOLATILE, REG_SZ,
use winapi::um::ioapiset::{DeviceIoControl}; SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES,
use winapi::um::errhandlingapi::{GetLastError}; };
use winapi::um::fileapi::{CreateFileA, CREATE_ALWAYS};
use winapi::um::handleapi::{INVALID_HANDLE_VALUE, CloseHandle}; use winapi::um::errhandlingapi::GetLastError;
use winapi::um::libloaderapi::{LoadLibraryA, GetProcAddress}; use winapi::um::fileapi::{CreateFileA, CREATE_ALWAYS};
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken}; use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
use winapi::um::sysinfoapi::{GetTickCount64}; use winapi::um::ioapiset::DeviceIoControl;
use winapi::um::securitybaseapi::{AdjustTokenPrivileges}; use winapi::um::libloaderapi::{GetProcAddress, LoadLibraryA};
use winapi::um::winbase::{LookupPrivilegeValueA}; use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken};
use winapi::um::winreg::{RegCreateKeyExA, RegSetValueExA, RegCloseKey, HKEY_LOCAL_MACHINE}; use winapi::um::securitybaseapi::AdjustTokenPrivileges;
use winapi::um::sysinfoapi::GetTickCount64;
const STR_DRIVER_REGISTRY_PATH: &str = "\\Registry\\Machine\\System\\CurrentControlSet\\Services\\lpus"; use winapi::um::winbase::LookupPrivilegeValueA;
use winapi::um::winreg::{RegCloseKey, RegCreateKeyExA, RegSetValueExA, HKEY_LOCAL_MACHINE};
#[allow(dead_code)]
#[derive(Debug, Copy, Clone)] const STR_DRIVER_REGISTRY_PATH: &str =
pub enum WindowsVersion { "\\Registry\\Machine\\System\\CurrentControlSet\\Services\\lpus";
Windows10_2015,
Windows10_2016, #[allow(dead_code)]
Windows10_2017, #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
Windows10_2018, pub enum WindowsVersion {
Windows10_2019, Windows7,
Windows10_2020, Windows8,
Windows10FastRing, Windows10Legacy,
Windows10VersionUnknown Windows10_2015,
} Windows10_2016,
Windows10_2017,
#[allow(dead_code)] Windows10_2018,
#[derive(Copy, Clone)] Windows10_2019,
pub struct WindowsFFI { Windows10_2020,
pub version_info: OSVERSIONINFOW, WindowsFastRing,
pub short_version: WindowsVersion, WindowsUnknown,
driver_handle: HANDLE, }
ntdll: HMODULE,
nt_load_driver: extern "system" fn(PUNICODE_STRING) -> NTSTATUS, impl WindowsVersion {
nt_unload_driver: extern "system" fn(PUNICODE_STRING) -> NTSTATUS, pub fn not_supported(self) -> bool {
rtl_init_unicode_str: extern "system" fn(PUNICODE_STRING, PCWSTR), match self {
rtl_get_version: extern "system" fn(PRTL_OSVERSIONINFOW) -> NTSTATUS, WindowsVersion::Windows10Legacy
} | WindowsVersion::Windows10_2015
| WindowsVersion::Windows10_2016
impl WindowsFFI { | WindowsVersion::Windows10_2017
pub fn new() -> Self { | WindowsVersion::Windows8
let str_ntdll = CString::new("ntdll").unwrap(); | WindowsVersion::WindowsUnknown => true,
let str_nt_load_driver = CString::new("NtLoadDriver").unwrap(); _ => false,
let str_nt_unload_driver = CString::new("NtUnloadDriver").unwrap(); }
let str_rtl_init_unicode_str = CString::new("RtlInitUnicodeString").unwrap(); }
let str_rtl_get_version = CString::new("RtlGetVersion").unwrap(); pub fn is_supported(self) -> bool {
let str_se_load_driver_privilege = CString::new("SeLoadDriverPrivilege").unwrap(); !self.not_supported()
}
let str_driver_path = CString::new("\\SystemRoot\\System32\\DRIVERS\\lpus.sys").unwrap(); }
let str_registry_path = CString::new("System\\CurrentControlSet\\Services\\lpus").unwrap();
let str_type = CString::new("Type").unwrap(); #[allow(dead_code)]
let str_error_control = CString::new("ErrorControl").unwrap(); #[derive(Copy, Clone)]
let str_start = CString::new("Start").unwrap(); pub struct WindowsFFI {
let str_image_path = CString::new("ImagePath").unwrap(); pub version_info: OSVERSIONINFOW,
pub short_version: WindowsVersion,
let mut version_info = OSVERSIONINFOW { driver_handle: HANDLE,
dwOSVersionInfoSize: 0u32, ntdll: HMODULE,
dwMajorVersion: 0u32, nt_load_driver: extern "system" fn(PUNICODE_STRING) -> NTSTATUS,
dwMinorVersion: 0u32, nt_unload_driver: extern "system" fn(PUNICODE_STRING) -> NTSTATUS,
dwBuildNumber: 0u32, rtl_init_unicode_str: extern "system" fn(PUNICODE_STRING, PCWSTR),
dwPlatformId: 0u32, rtl_get_version: extern "system" fn(PRTL_OSVERSIONINFOW) -> NTSTATUS,
szCSDVersion: [0u16; 128], }
};
impl WindowsFFI {
let ntdll: HMODULE; pub fn new() -> Self {
let nt_load_driver: extern "system" fn(PUNICODE_STRING) -> NTSTATUS; let str_ntdll = CString::new("ntdll").unwrap();
let nt_unload_driver: extern "system" fn(PUNICODE_STRING) -> NTSTATUS; let str_nt_load_driver = CString::new("NtLoadDriver").unwrap();
let rtl_init_unicode_str: extern "system" fn(PUNICODE_STRING, PCWSTR); let str_nt_unload_driver = CString::new("NtUnloadDriver").unwrap();
let rtl_get_version: extern "system" fn(PRTL_OSVERSIONINFOW) -> NTSTATUS; let str_rtl_init_unicode_str = CString::new("RtlInitUnicodeString").unwrap();
let str_rtl_get_version = CString::new("RtlGetVersion").unwrap();
// some pointer unsafe C code let str_se_load_driver_privilege = CString::new("SeLoadDriverPrivilege").unwrap();
unsafe { let str_driver_path = {
ntdll = LoadLibraryA(str_ntdll.as_ptr()); let mut driver_location =
let nt_load_driver_ = GetProcAddress(ntdll, str_nt_load_driver.as_ptr()); app_dir(AppDataType::UserData, &APP_INFO, &format!("driver")).unwrap();
let nt_unload_driver_ = GetProcAddress(ntdll, str_nt_unload_driver.as_ptr()); driver_location.push("lpus.sys");
let rtl_init_unicode_str_ = GetProcAddress(ntdll, str_rtl_init_unicode_str.as_ptr()); if driver_location.is_file() {
let rtl_get_version_ = GetProcAddress(ntdll, str_rtl_get_version.as_ptr()); let p = driver_location.to_str().unwrap();
CString::new(format!("\\??\\{}", p)).unwrap()
nt_load_driver = transmute(nt_load_driver_); } else {
nt_unload_driver = transmute(nt_unload_driver_); CString::new("\\SystemRoot\\System32\\DRIVERS\\lpus.sys").unwrap()
rtl_init_unicode_str = transmute(rtl_init_unicode_str_); }
rtl_get_version = transmute(rtl_get_version_); };
let str_registry_path = CString::new("System\\CurrentControlSet\\Services\\lpus").unwrap();
// setup registry let str_type = CString::new("Type").unwrap();
let mut registry_key: HKEY = null_mut(); let str_error_control = CString::new("ErrorControl").unwrap();
RegCreateKeyExA( let str_start = CString::new("Start").unwrap();
HKEY_LOCAL_MACHINE, str_registry_path.as_ptr(), let str_image_path = CString::new("ImagePath").unwrap();
0, null_mut(),
REG_OPTION_NON_VOLATILE, KEY_WRITE, let mut version_info = OSVERSIONINFOW {
null_mut(), &mut registry_key, null_mut() dwOSVersionInfoSize: 0u32,
); dwMajorVersion: 0u32,
let type_value: [u8; 4] = 1u32.to_le_bytes(); dwMinorVersion: 0u32,
let error_control_value: [u8; 4] = 1u32.to_le_bytes(); dwBuildNumber: 0u32,
let start_value: [u8; 4] = 3u32.to_le_bytes(); dwPlatformId: 0u32,
let registry_values = [ szCSDVersion: [0u16; 128],
(str_type.as_ptr(), REG_DWORD, type_value.as_ptr(), 4), };
(str_error_control.as_ptr(), REG_DWORD, error_control_value.as_ptr(), 4),
(str_start.as_ptr(), REG_DWORD, start_value.as_ptr(), 4), let ntdll: HMODULE;
(str_image_path.as_ptr(), REG_SZ, let nt_load_driver: extern "system" fn(PUNICODE_STRING) -> NTSTATUS;
str_driver_path.as_ptr() as *const u8, str_driver_path.to_bytes().len() + 1) let nt_unload_driver: extern "system" fn(PUNICODE_STRING) -> NTSTATUS;
]; let rtl_init_unicode_str: extern "system" fn(PUNICODE_STRING, PCWSTR);
for &(key, keytype, value_ptr, size_in_bytes) in &registry_values { let rtl_get_version: extern "system" fn(PRTL_OSVERSIONINFOW) -> NTSTATUS;
RegSetValueExA(
registry_key, key, 0, // some pointer unsafe C code
keytype, value_ptr, size_in_bytes as u32 unsafe {
); ntdll = LoadLibraryA(str_ntdll.as_ptr());
} let nt_load_driver_ = GetProcAddress(ntdll, str_nt_load_driver.as_ptr());
RegCloseKey(registry_key); let nt_unload_driver_ = GetProcAddress(ntdll, str_nt_unload_driver.as_ptr());
let rtl_init_unicode_str_ = GetProcAddress(ntdll, str_rtl_init_unicode_str.as_ptr());
// Setup privilege SeLoadDriverPrivilege let rtl_get_version_ = GetProcAddress(ntdll, str_rtl_get_version.as_ptr());
let mut token_handle: HANDLE = null_mut();
let mut luid = LUID::default(); nt_load_driver = transmute(nt_load_driver_);
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &mut token_handle); nt_unload_driver = transmute(nt_unload_driver_);
LookupPrivilegeValueA(null_mut(), str_se_load_driver_privilege.as_ptr(), &mut luid); rtl_init_unicode_str = transmute(rtl_init_unicode_str_);
let mut new_token_state = TOKEN_PRIVILEGES { rtl_get_version = transmute(rtl_get_version_);
PrivilegeCount: 1,
Privileges: [LUID_AND_ATTRIBUTES { // setup registry
Luid: luid, let mut registry_key: HKEY = null_mut();
Attributes: SE_PRIVILEGE_ENABLED RegCreateKeyExA(
}] HKEY_LOCAL_MACHINE,
}; str_registry_path.as_ptr(),
AdjustTokenPrivileges( 0,
token_handle, 0, &mut new_token_state, 16, null_mut(), null_mut()); null_mut(),
CloseHandle(token_handle); REG_OPTION_NON_VOLATILE,
} KEY_WRITE,
null_mut(),
rtl_get_version(&mut version_info); &mut registry_key,
null_mut(),
let short_version = match version_info.dwBuildNumber { );
17134 | 17763 => WindowsVersion::Windows10_2018, let type_value: [u8; 4] = 1u32.to_le_bytes();
18362 | 18363 => WindowsVersion::Windows10_2019, let error_control_value: [u8; 4] = 1u32.to_le_bytes();
19041 => WindowsVersion::Windows10_2020, let start_value: [u8; 4] = 3u32.to_le_bytes();
_ if version_info.dwBuildNumber >= 19536 => WindowsVersion::Windows10FastRing, let registry_values = [
_ => WindowsVersion::Windows10VersionUnknown (str_type.as_ptr(), REG_DWORD, type_value.as_ptr(), 4),
}; (
str_error_control.as_ptr(),
Self { REG_DWORD,
version_info, error_control_value.as_ptr(),
short_version, 4,
driver_handle: INVALID_HANDLE_VALUE, ),
ntdll, (str_start.as_ptr(), REG_DWORD, start_value.as_ptr(), 4),
nt_load_driver, (
nt_unload_driver, str_image_path.as_ptr(),
rtl_init_unicode_str, REG_SZ,
rtl_get_version str_driver_path.as_ptr() as *const u8,
} str_driver_path.to_bytes().len() + 1,
} ),
];
pub fn driver_loaded(self) -> bool { for &(key, keytype, value_ptr, size_in_bytes) in &registry_values {
self.driver_handle != INVALID_HANDLE_VALUE RegSetValueExA(
} registry_key,
key,
pub fn load_driver(&mut self) -> NTSTATUS { 0,
// TODO: Move this to new() keytype,
// If we move this function to new(), self.driver_handle will be init, and thus no mut here value_ptr,
let str_driver_reg = U16CString::from_str(STR_DRIVER_REGISTRY_PATH).unwrap(); size_in_bytes as u32,
let mut str_driver_reg_unicode = UNICODE_STRING::default(); );
(self.rtl_init_unicode_str)(&mut str_driver_reg_unicode, str_driver_reg.as_ptr() as *const u16); }
let status = (self.nt_load_driver)(&mut str_driver_reg_unicode); RegCloseKey(registry_key);
let filename = CString::new("\\\\.\\poolscanner").unwrap(); // Setup privilege SeLoadDriverPrivilege
let driver_file_handle: HANDLE = unsafe { let mut token_handle: HANDLE = null_mut();
CreateFileA(filename.as_ptr(), let mut luid = LUID::default();
GENERIC_READ | GENERIC_WRITE, OpenProcessToken(
0, null_mut(), CREATE_ALWAYS, GetCurrentProcess(),
FILE_ATTRIBUTE_NORMAL, null_mut()) TOKEN_ADJUST_PRIVILEGES,
}; &mut token_handle,
);
if driver_file_handle == INVALID_HANDLE_VALUE { LookupPrivilegeValueA(null_mut(), str_se_load_driver_privilege.as_ptr(), &mut luid);
println!("Driver CreateFileA failed"); let mut new_token_state = TOKEN_PRIVILEGES {
} PrivilegeCount: 1,
else { Privileges: [LUID_AND_ATTRIBUTES {
self.driver_handle = driver_file_handle; Luid: luid,
} Attributes: SE_PRIVILEGE_ENABLED,
status }],
} };
AdjustTokenPrivileges(
pub fn unload_driver(&self) -> NTSTATUS { token_handle,
let str_driver_reg = U16CString::from_str(STR_DRIVER_REGISTRY_PATH).unwrap(); 0,
let mut str_driver_reg_unicode = UNICODE_STRING::default(); &mut new_token_state,
(self.rtl_init_unicode_str)(&mut str_driver_reg_unicode, str_driver_reg.as_ptr()); 16,
(self.nt_unload_driver)(&mut str_driver_reg_unicode) null_mut(),
} null_mut(),
);
#[allow(dead_code)] CloseHandle(token_handle);
pub fn get_build_number(&self) -> DWORD { }
self.version_info.dwBuildNumber
} rtl_get_version(&mut version_info);
#[allow(dead_code)] let short_version = match version_info.dwBuildNumber {
pub fn print_version(&self) { // 2600 => WindowsVersion::WindowsXP,
println!("Windows version: {}.{}.{} {:?}", // 6000 | 6001 | 6002 => WindowsVersion::WindowsVista,
self.version_info.dwMajorVersion, 7600 | 7601 => WindowsVersion::Windows7,
self.version_info.dwMinorVersion, 9200 | 9600 => WindowsVersion::Windows8,
self.version_info.dwBuildNumber, 10240 => WindowsVersion::Windows10Legacy,
self.short_version 10586 => WindowsVersion::Windows10_2015,
); 14393 => WindowsVersion::Windows10_2016,
} 15063 | 16299 => WindowsVersion::Windows10_2017,
17134 | 17763 => WindowsVersion::Windows10_2018,
pub fn valid_process_time(&self, filetime: u64) -> bool { 18363 | 18362 => WindowsVersion::Windows10_2019,
// https://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ 19041 => WindowsVersion::Windows10_2020,
let windows_epoch_diff = 11644473600000 * 10000; x if x >= 19536 => WindowsVersion::WindowsFastRing,
if filetime < windows_epoch_diff { _ => WindowsVersion::WindowsUnknown,
return false; };
}
let system_up_time_ms = unsafe { GetTickCount64() }; Self {
let process_time_epoch = (filetime - windows_epoch_diff) / 10000; version_info,
let now_ms = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_millis() as u64; short_version,
let system_start_up_time_ms = now_ms - system_up_time_ms; driver_handle: INVALID_HANDLE_VALUE,
ntdll,
if process_time_epoch < system_start_up_time_ms { nt_load_driver,
false nt_unload_driver,
} else if process_time_epoch > now_ms { rtl_init_unicode_str,
false rtl_get_version,
} else { }
true }
}
} pub fn driver_loaded(&self) -> bool {
self.driver_handle != INVALID_HANDLE_VALUE
pub fn device_io<T, E>(&self, code: DWORD, inbuf: &mut T, outbuf: &mut E) -> DWORD { }
self.device_io_raw(code,
inbuf as *mut _ as *mut c_void, size_of_val(inbuf) as DWORD, pub fn file_connect(&mut self) {
outbuf as *mut _ as *mut c_void, size_of_val(outbuf) as DWORD) let filename = CString::new("\\\\.\\poolscanner").unwrap();
} let driver_file_handle: HANDLE = unsafe {
CreateFileA(
pub fn device_io_raw(&self, code: DWORD, filename.as_ptr(),
input_ptr: *mut c_void, input_len: DWORD, GENERIC_READ | GENERIC_WRITE,
output_ptr: *mut c_void, output_len: DWORD) -> DWORD { 0,
// println!("driver loaded: {}; device_io_code: {}", self.driver_loaded(), code); null_mut(),
let mut bytes_returned: DWORD = 0; CREATE_ALWAYS,
unsafe { FILE_ATTRIBUTE_NORMAL,
let status = DeviceIoControl(self.driver_handle, code, null_mut(),
input_ptr, input_len, )
output_ptr, output_len, };
&mut bytes_returned, null_mut());
if status == 0 { if driver_file_handle == INVALID_HANDLE_VALUE {
println!("device io failed: last error {}", GetLastError()); println!("Driver CreateFileA failed");
} } else {
}; self.driver_handle = driver_file_handle;
bytes_returned }
} }
}
pub fn load_driver(&mut self) -> NTSTATUS {
// TODO: Move this to new()
// If we move this function to new(), self.driver_handle will be init, and thus no mut here
let str_driver_reg = U16CString::from_str(STR_DRIVER_REGISTRY_PATH).unwrap();
let mut str_driver_reg_unicode = UNICODE_STRING::default();
(self.rtl_init_unicode_str)(
&mut str_driver_reg_unicode,
str_driver_reg.as_ptr() as *const u16,
);
let status = (self.nt_load_driver)(&mut str_driver_reg_unicode);
self.file_connect();
status
}
pub fn unload_driver(&self) -> NTSTATUS {
let str_driver_reg = U16CString::from_str(STR_DRIVER_REGISTRY_PATH).unwrap();
let mut str_driver_reg_unicode = UNICODE_STRING::default();
(self.rtl_init_unicode_str)(&mut str_driver_reg_unicode, str_driver_reg.as_ptr());
(self.nt_unload_driver)(&mut str_driver_reg_unicode)
}
#[allow(dead_code)]
pub fn get_build_number(&self) -> DWORD {
self.version_info.dwBuildNumber
}
#[allow(dead_code)]
pub fn print_version(&self) {
println!(
"Windows version: {}.{}.{} {:?}",
self.version_info.dwMajorVersion,
self.version_info.dwMinorVersion,
self.version_info.dwBuildNumber,
self.short_version
);
}
pub fn to_epoch(&self, filetime: u64) -> u64 {
let windows_epoch_diff = 11644473600000 * 10000;
if filetime < windows_epoch_diff {
return 0;
}
let process_time_epoch = (filetime - windows_epoch_diff) / 10000;
process_time_epoch
}
pub fn valid_process_time(&self, filetime: u64) -> bool {
// https://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/
let windows_epoch_diff = 11644473600000 * 10000;
if filetime < windows_epoch_diff {
return false;
}
let system_up_time_ms = unsafe { GetTickCount64() };
let process_time_epoch = (filetime - windows_epoch_diff) / 10000; // in milisecond
let now_ms = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_millis() as u64;
let system_start_up_time_ms =
now_ms - system_up_time_ms - (10 * 3600 * 1000/* 10 minutes penalty */);
if process_time_epoch < system_start_up_time_ms {
false
} else if process_time_epoch > now_ms {
false
} else {
true
}
}
pub fn device_io<T, E>(&self, code: DWORD, inbuf: &mut T, outbuf: &mut E) -> DWORD {
self.device_io_raw(
code,
inbuf as *mut _ as *mut c_void,
size_of_val(inbuf) as DWORD,
outbuf as *mut _ as *mut c_void,
size_of_val(outbuf) as DWORD,
)
}
pub fn device_io_raw(
&self,
code: DWORD,
input_ptr: *mut c_void,
input_len: DWORD,
output_ptr: *mut c_void,
output_len: DWORD,
) -> DWORD {
// println!("driver loaded: {}; device_io_code: {}", self.driver_loaded(), code);
let mut bytes_returned: DWORD = 0;
unsafe {
let status = DeviceIoControl(
self.driver_handle,
code,
input_ptr,
input_len,
output_ptr,
output_len,
&mut bytes_returned,
null_mut(),
);
if status == 0 {
println!("device io failed: last error {}", GetLastError());
}
};
bytes_returned
}
}