Compare commits
10 Commits
6213e8c853
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 254e179742 | |||
| e4ad24253d | |||
| 5ef279b0bc | |||
| 7a528fb2f5 | |||
| 253cfcd399 | |||
| 8835ca5f26 | |||
| 9806c9bacd | |||
| ce85787be9 | |||
| a89a5ae376 | |||
| 1ce2913be5 |
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "src-tauri/pe_parse"]
|
||||
path = src-tauri/pe_parse
|
||||
url = ssh://git@81.70.52.148:17022/asahi/pe_parse.git
|
||||
@@ -16,11 +16,13 @@
|
||||
"@tauri-apps/plugin-shell": "^2",
|
||||
"antd": "^5.22.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^2",
|
||||
"@types/antd": "^1.0.4",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
|
||||
34
pnpm-lock.yaml
generated
34
pnpm-lock.yaml
generated
@@ -22,10 +22,13 @@ importers:
|
||||
version: 2.0.1
|
||||
antd:
|
||||
specifier: ^5.22.3
|
||||
version: 5.22.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
version: 5.22.3(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
lodash-es:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
moment:
|
||||
specifier: ^2.30.1
|
||||
version: 2.30.1
|
||||
react:
|
||||
specifier: ^18.0.0
|
||||
version: 18.3.1
|
||||
@@ -36,6 +39,9 @@ importers:
|
||||
'@tauri-apps/cli':
|
||||
specifier: ^2
|
||||
version: 2.1.0
|
||||
'@types/antd':
|
||||
specifier: ^1.0.4
|
||||
version: 1.0.4(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@types/lodash-es':
|
||||
specifier: ^4.17.12
|
||||
version: 4.17.12
|
||||
@@ -659,6 +665,10 @@ packages:
|
||||
'@tauri-apps/plugin-shell@2.0.1':
|
||||
resolution: {integrity: sha512-akU1b77sw3qHiynrK0s930y8zKmcdrSD60htjH+mFZqv5WaakZA/XxHR3/sF1nNv9Mgmt/Shls37HwnOr00aSw==}
|
||||
|
||||
'@types/antd@1.0.4':
|
||||
resolution: {integrity: sha512-gp4PGQckP1kNjj2H6juhjKIVwkpXwCIyIvOlwp2DC6geuhVpDHEEB5gwH4hJabVgBAFtrjBPJ58VIRV9VV9W2g==}
|
||||
deprecated: This is a stub types definition. antd provides its own type definitions, so you do not need this installed.
|
||||
|
||||
'@types/babel__core@7.20.5':
|
||||
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
|
||||
|
||||
@@ -825,6 +835,9 @@ packages:
|
||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
moment@2.30.1:
|
||||
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
@@ -1704,6 +1717,16 @@ snapshots:
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.1.1
|
||||
|
||||
'@types/antd@1.0.4(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
antd: 5.22.3(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
transitivePeerDependencies:
|
||||
- date-fns
|
||||
- luxon
|
||||
- moment
|
||||
- react
|
||||
- react-dom
|
||||
|
||||
'@types/babel__core@7.20.5':
|
||||
dependencies:
|
||||
'@babel/parser': 7.26.3
|
||||
@@ -1755,7 +1778,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
antd@5.22.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
antd@5.22.3(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
'@ant-design/colors': 7.1.0
|
||||
'@ant-design/cssinjs': 1.22.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@@ -1787,7 +1810,7 @@ snapshots:
|
||||
rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
rc-notification: 5.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
rc-pagination: 4.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
rc-picker: 4.8.3(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
rc-picker: 4.8.3(dayjs@1.11.13)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
rc-progress: 4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
rc-rate: 2.13.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
rc-resize-observer: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@@ -1934,6 +1957,8 @@ snapshots:
|
||||
picomatch: 2.3.1
|
||||
optional: true
|
||||
|
||||
moment@2.30.1: {}
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
nanoid@3.3.8: {}
|
||||
@@ -2104,7 +2129,7 @@ snapshots:
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
|
||||
rc-picker@4.8.3(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
rc-picker@4.8.3(dayjs@1.11.13)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.0
|
||||
'@rc-component/trigger': 2.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@@ -2116,6 +2141,7 @@ snapshots:
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
optionalDependencies:
|
||||
dayjs: 1.11.13
|
||||
moment: 2.30.1
|
||||
|
||||
rc-progress@4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
|
||||
162
src-tauri/Cargo.lock
generated
162
src-tauri/Cargo.lock
generated
@@ -520,6 +520,25 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.20"
|
||||
@@ -753,6 +772,12 @@ version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "embed-resource"
|
||||
version = "2.5.1"
|
||||
@@ -862,13 +887,16 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"memmap2",
|
||||
"pe_parse",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sysinfo",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-dialog",
|
||||
"tauri-plugin-shell",
|
||||
"thiserror 2.0.4",
|
||||
"windows 0.58.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2047,6 +2075,15 @@ version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
@@ -2423,6 +2460,16 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
||||
|
||||
[[package]]
|
||||
name = "pe_parse"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"memmap2",
|
||||
"serde",
|
||||
"thiserror 2.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
@@ -2809,6 +2856,26 @@ version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.7"
|
||||
@@ -3395,6 +3462,20 @@ dependencies = [
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "948512566b1895f93b1592c7574baeb2de842f224f2aab158799ecadb8ebbb46"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"memchr",
|
||||
"ntapi",
|
||||
"rayon",
|
||||
"windows 0.57.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "6.2.2"
|
||||
@@ -3441,7 +3522,7 @@ dependencies = [
|
||||
"tao-macros",
|
||||
"unicode-segmentation",
|
||||
"url",
|
||||
"windows",
|
||||
"windows 0.58.0",
|
||||
"windows-core 0.58.0",
|
||||
"windows-version",
|
||||
"x11-dl",
|
||||
@@ -3511,7 +3592,7 @@ dependencies = [
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"window-vibrancy",
|
||||
"windows",
|
||||
"windows 0.58.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3672,7 +3753,7 @@ dependencies = [
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.4",
|
||||
"url",
|
||||
"windows",
|
||||
"windows 0.58.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3697,7 +3778,7 @@ dependencies = [
|
||||
"url",
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"windows",
|
||||
"windows 0.58.0",
|
||||
"wry",
|
||||
]
|
||||
|
||||
@@ -4416,10 +4497,10 @@ checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c"
|
||||
dependencies = [
|
||||
"webview2-com-macros",
|
||||
"webview2-com-sys",
|
||||
"windows",
|
||||
"windows 0.58.0",
|
||||
"windows-core 0.58.0",
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-implement 0.58.0",
|
||||
"windows-interface 0.58.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4440,7 +4521,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886"
|
||||
dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
"windows",
|
||||
"windows 0.58.0",
|
||||
"windows-core 0.58.0",
|
||||
]
|
||||
|
||||
@@ -4489,6 +4570,16 @@ dependencies = [
|
||||
"windows-version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
|
||||
dependencies = [
|
||||
"windows-core 0.57.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.58.0"
|
||||
@@ -4508,19 +4599,42 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
||||
dependencies = [
|
||||
"windows-implement 0.57.0",
|
||||
"windows-interface 0.57.0",
|
||||
"windows-result 0.1.2",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-result",
|
||||
"windows-implement 0.58.0",
|
||||
"windows-interface 0.58.0",
|
||||
"windows-result 0.2.0",
|
||||
"windows-strings",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.58.0"
|
||||
@@ -4532,6 +4646,17 @@ dependencies = [
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.58.0"
|
||||
@@ -4549,11 +4674,20 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-result 0.2.0",
|
||||
"windows-strings",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.2.0"
|
||||
@@ -4569,7 +4703,7 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-result 0.2.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
@@ -4873,7 +5007,7 @@ dependencies = [
|
||||
"webkit2gtk",
|
||||
"webkit2gtk-sys",
|
||||
"webview2-com",
|
||||
"windows",
|
||||
"windows 0.58.0",
|
||||
"windows-core 0.58.0",
|
||||
"windows-version",
|
||||
"x11-dl",
|
||||
|
||||
@@ -18,11 +18,24 @@ name = "test_tauri_lib"
|
||||
tauri-build = {version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
bitflags = {version= "2.6.0", features = ["serde"] }
|
||||
bitflags = {version = "2.6.0", features = ["serde"] }
|
||||
memmap2 = "0.9.5"
|
||||
serde = {version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
sysinfo = "0.33.0"
|
||||
tauri = {version = "2", features = [] }
|
||||
tauri-plugin-dialog = "2"
|
||||
tauri-plugin-shell = "2"
|
||||
thiserror = "2.0.4"
|
||||
pe_parse = { path = "./pe_parse", features = ["memmap2_impl"] }
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.58.0"
|
||||
features = [
|
||||
"Data_Xml_Dom",
|
||||
"Win32_Foundation",
|
||||
"Win32_Security",
|
||||
"Win32_System_Threading",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
"Win32_System_ProcessStatus"
|
||||
]
|
||||
1
src-tauri/pe_parse
Submodule
1
src-tauri/pe_parse
Submodule
Submodule src-tauri/pe_parse added at f48803b3c5
@@ -1,12 +1,15 @@
|
||||
use thiserror;
|
||||
|
||||
use crate::pe_parse::error::{
|
||||
use pe_parse::error::{
|
||||
MutablePEError,
|
||||
PEParseError,
|
||||
};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum AppError {
|
||||
#[error(transparent)]
|
||||
Utf8Error(#[from] std::str::Utf8Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
@@ -35,6 +38,11 @@ pub enum AppError {
|
||||
/// 节名过长
|
||||
#[error("节名过长!")]
|
||||
SectionNameTooLong,
|
||||
|
||||
#[error("打开进程失败!: {0}")]
|
||||
OpenProcessError(String),
|
||||
#[error("查询进程路径失败!: {0}")]
|
||||
QueryFullProcessImageNameWError(String),
|
||||
}
|
||||
|
||||
impl serde::Serialize for AppError {
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use std::io::Write;
|
||||
use std::mem::{self};
|
||||
|
||||
use crate::app_error::AppError;
|
||||
use crate::pe_parse::header::{ImageSectionHeader, SectionCharacteristics};
|
||||
use crate::pe_parse::pe::MutablePE;
|
||||
use crate::services::file::expand_file_size;
|
||||
use crate::{pe_parse::pe::ReadOnlyPE, services::file};
|
||||
use memmap2::Mmap;
|
||||
use pe_parse::header::{
|
||||
ImageDirectoryEntry, ImageImportDescriptor, ImageSectionHeader, ImportLookupTable32,
|
||||
ImportLookupTable64, SectionCharacteristics,
|
||||
};
|
||||
use pe_parse::pe::{MutablePE, ReadOnlyPE};
|
||||
use serde::Serialize;
|
||||
use crate::services::file;
|
||||
use std::ffi::CStr;
|
||||
use std::mem::{self};
|
||||
|
||||
/// 应用的状态结构体
|
||||
#[derive(Default)]
|
||||
@@ -101,7 +104,10 @@ impl AppState {
|
||||
// 2. 以可读可写的方式重新映射文件
|
||||
rw_mmap_option = Some(file::mmap_file_rw(file_path)?);
|
||||
// 拓展头部大小 拓展一个节的大小
|
||||
rw_mmap_option.as_mut().unwrap().expand_headers(file_alignment)?;
|
||||
rw_mmap_option
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.expand_headers(file_alignment)?;
|
||||
}
|
||||
// 2. 添加节
|
||||
// 先拓展文件大小
|
||||
@@ -112,7 +118,7 @@ impl AppState {
|
||||
}
|
||||
let rw_mmap = rw_mmap_option.as_mut().unwrap();
|
||||
|
||||
let characteristics = SectionCharacteristics::from_bits(section_characteristics).unwrap();
|
||||
let characteristics = SectionCharacteristics::from_bits(section_characteristics).unwrap();
|
||||
|
||||
rw_mmap.add_section(§ion_name_bytes, section_size, characteristics)?;
|
||||
|
||||
@@ -121,4 +127,142 @@ impl AppState {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_import_tables(&self) -> Result<Vec<ImportModuleTable>, AppError> {
|
||||
let mmap = self.get_mmap_ref()?;
|
||||
let is_64_bit = self.is_64_bit.unwrap();
|
||||
// 1. 获取导入表
|
||||
let import_table = mmap.get_data_directory(ImageDirectoryEntry::Import)?;
|
||||
// 2. 获取导入表的RVA
|
||||
let import_table_rva = import_table.virtual_address;
|
||||
// 3. RVA转FOA
|
||||
let import_table_foa = mmap.rva_to_foa(import_table_rva)?;
|
||||
// 从文件import_table_foa循环解析ImageImportDescriptor,当解析出的导入表==ImageImportDescriptor::default()时,结束循环
|
||||
let mut origin_import_module_tables: Vec<&ImageImportDescriptor> = Vec::new();
|
||||
let mut base_offset = import_table_foa as usize;
|
||||
loop {
|
||||
let import_module_item = unsafe {
|
||||
let ptr = mmap.as_ptr().add(base_offset) as *const ImageImportDescriptor;
|
||||
ptr.as_ref().unwrap()
|
||||
};
|
||||
if import_module_item == &ImageImportDescriptor::default() {
|
||||
break;
|
||||
}
|
||||
origin_import_module_tables.push(import_module_item);
|
||||
base_offset += mem::size_of::<ImageImportDescriptor>();
|
||||
}
|
||||
// 4. 解析导入表 主要是来格式化一些数据 先创建一个ImportModuleTable 的变长数组
|
||||
let mut import_module_tables: Vec<ImportModuleTable> = Vec::new();
|
||||
// 循环处理origin_import_module_tables
|
||||
for origin_item in origin_import_module_tables.iter() {
|
||||
let mut import_module = ImportModuleTable::default();
|
||||
// 1. 获取模块名称
|
||||
// 先获取模块名称的FOA
|
||||
let module_name_foa = mmap.rva_to_foa(origin_item.name)?;
|
||||
// 获取模块名称 需要unsafe读取
|
||||
let module_name = unsafe {
|
||||
let ptr = mmap[module_name_foa as usize..].as_ptr() as *const i8;
|
||||
CStr::from_ptr(ptr).to_str()?.to_string()
|
||||
};
|
||||
import_module.module_name = module_name;
|
||||
// 其他成员
|
||||
import_module.forwarder_chain = origin_item.forwarder_chain;
|
||||
import_module.iat_rva = origin_item.first_thunk;
|
||||
import_module.int_rva = origin_item.original_first_thunk;
|
||||
import_module.module_name_rva = origin_item.name;
|
||||
import_module.timestamp = origin_item.time_date_stamp;
|
||||
// 2. 获取函数表
|
||||
let mut functions: Vec<ImportFunctionTable> = Vec::new();
|
||||
// 获取函数表的FOA
|
||||
let mut base_offset = mmap.rva_to_foa(origin_item.original_first_thunk)? as usize;
|
||||
// 循环解析函数表
|
||||
loop {
|
||||
// TODO: 这里需要判断是64位还是32位
|
||||
// 如果是64位,每次读取8字节 否则读取4字节
|
||||
let import_lookup_table = if is_64_bit {
|
||||
let ptr = mmap[base_offset as usize..].as_ptr() as *const u64;
|
||||
unsafe { ptr.read() as u64 }
|
||||
} else {
|
||||
let ptr = mmap[base_offset as usize..].as_ptr() as *const u32;
|
||||
unsafe { ptr.read() as u64 }
|
||||
};
|
||||
|
||||
// 如果import_lookup_table == 0 则表示结束
|
||||
if import_lookup_table == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
// 1. 判断是序号还是命名函数
|
||||
let is_named = match is_64_bit {
|
||||
true => {
|
||||
import_lookup_table & ImportLookupTable64::IMPORT_BY_ORDINAL.bits() == 0
|
||||
}
|
||||
false => {
|
||||
import_lookup_table as u32 & ImportLookupTable32::IMPORT_BY_ORDINAL.bits()
|
||||
== 0
|
||||
}
|
||||
};
|
||||
let mut import_function_table = ImportFunctionTable::default();
|
||||
|
||||
if is_named == true {
|
||||
// TODO: 仅当序号/名称标志位字段为 0(按名称导入)时,才使用此字段。
|
||||
let hint_name_table_rva = import_lookup_table as u32 & 0x7FFFFFFF;
|
||||
let hint_name_table_foa = mmap.rva_to_foa(hint_name_table_rva)?;
|
||||
// 1. 读hint 两个字节
|
||||
let funtion_hint: u16 = unsafe {
|
||||
let ptr = mmap[hint_name_table_foa as usize..].as_ptr() as *const u16;
|
||||
ptr.read()
|
||||
};
|
||||
import_function_table.function_hint = funtion_hint;
|
||||
import_function_table.function_address = hint_name_table_rva;
|
||||
let function_name = unsafe {
|
||||
let ptr = mmap[hint_name_table_foa as usize + 2..].as_ptr() as *const i8;
|
||||
CStr::from_ptr(ptr).to_str()?.to_string()
|
||||
};
|
||||
import_function_table.function_name = function_name;
|
||||
} else {
|
||||
let function_ordianl = import_lookup_table as u32
|
||||
& ImportLookupTable32::IMPORT_BY_ORDINAL_MASK.bits();
|
||||
import_function_table.function_hint = function_ordianl as u16;
|
||||
import_function_table.function_type = ImportFunctionTableItem::Ordinal;
|
||||
}
|
||||
functions.push(import_function_table);
|
||||
// 下一个
|
||||
base_offset += if is_64_bit { 8 } else { 4 };
|
||||
}
|
||||
import_module.functions = functions;
|
||||
import_module_tables.push(import_module);
|
||||
}
|
||||
Ok(import_module_tables)
|
||||
}
|
||||
}
|
||||
|
||||
// 定义一个获取导入表后需要返回的结构
|
||||
#[derive(Debug, Default, Clone, Serialize)]
|
||||
pub struct ImportModuleTable {
|
||||
pub module_name: String, // 模块名称
|
||||
pub functions: Vec<ImportFunctionTable>, // 这里要另一个结构体来描述
|
||||
pub timestamp: u32, // 时间戳
|
||||
pub forwarder_chain: u32, // 转发链
|
||||
pub module_name_rva: u32, // dll名称的RVA
|
||||
pub iat_rva: u32, // IAT的RVA
|
||||
pub int_rva: u32, // INT的RVA
|
||||
}
|
||||
|
||||
// 因为导入查找表可能是函数名、也可能是序号 所以用枚举包一下最好
|
||||
#[derive(Debug, Clone, Serialize, Default)]
|
||||
pub enum ImportFunctionTableItem {
|
||||
// 命名的导入函数
|
||||
#[default]
|
||||
Named,
|
||||
// 序号表示的
|
||||
Ordinal,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize)]
|
||||
pub struct ImportFunctionTable {
|
||||
pub function_name: String, // 函数名称
|
||||
pub function_address: u32, // 函数地址 IAT
|
||||
pub function_hint: u16, // 函数提示
|
||||
pub function_type: ImportFunctionTableItem,
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::{
|
||||
app_error::AppError,
|
||||
app_state::AppState,
|
||||
pe_parse::pe::{MutablePE, ReadOnlyPE},
|
||||
};
|
||||
use serde::Serialize;
|
||||
use crate::{app_error::AppError, app_state::AppState};
|
||||
use pe_parse::pe::ReadOnlyPE;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use tauri::{AppHandle, Manager, State};
|
||||
|
||||
@@ -101,6 +98,11 @@ pub fn command_get_file_pe_node_tree_data(
|
||||
key: "section_header".to_string(),
|
||||
children: vec![],
|
||||
},
|
||||
PeNodeTreeData {
|
||||
title: "导入目录".to_string(),
|
||||
key: "import_directory".to_string(),
|
||||
children: vec![],
|
||||
},
|
||||
],
|
||||
};
|
||||
Ok(vec![result])
|
||||
@@ -127,7 +129,7 @@ pub fn command_get_pe_data_nt_header(
|
||||
) -> Result<Value, AppError> {
|
||||
let app_state = app_state.lock().unwrap();
|
||||
let mmap = app_state.get_mmap_ref()?;
|
||||
let nt_header: crate::pe_parse::header::ImageNTHeader = mmap.get_nt_header()?;
|
||||
let nt_header: pe_parse::header::ImageNTHeader = mmap.get_nt_header()?;
|
||||
let nt_offset = mmap.get_nt_headers_offset()?;
|
||||
Ok(json! ({
|
||||
"fields": nt_header,
|
||||
@@ -183,6 +185,19 @@ pub fn command_get_pe_data_section_headers(
|
||||
}))
|
||||
}
|
||||
|
||||
// 命令,获取导入表数据
|
||||
#[tauri::command(async)]
|
||||
pub fn command_get_pe_data_import_directory(
|
||||
app_state: State<'_, Mutex<AppState>>,
|
||||
) -> Result<Value, AppError> {
|
||||
let app_state = app_state.lock().unwrap();
|
||||
let import_directory = app_state.get_import_tables()?;
|
||||
Ok(json!({
|
||||
"fields": import_directory,
|
||||
"base_offset": 0,
|
||||
}))
|
||||
}
|
||||
|
||||
// 命令,修改偏移处数据
|
||||
#[tauri::command(async)]
|
||||
pub fn command_write_data(
|
||||
@@ -195,6 +210,24 @@ pub fn command_write_data(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
pub fn command_write_data_list(
|
||||
app_state: State<'_, Mutex<AppState>>,
|
||||
data_list: Vec<EditData>,
|
||||
) -> Result<(), AppError> {
|
||||
let mut app_state = app_state.lock().unwrap();
|
||||
for edit_data in data_list {
|
||||
app_state.write_data(edit_data.offset, &edit_data.data)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct EditData {
|
||||
offset: usize,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
// 命令,添加节
|
||||
#[tauri::command(async)]
|
||||
pub fn command_add_section(
|
||||
|
||||
@@ -5,7 +5,6 @@ use tauri::Manager;
|
||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
pub mod app_error;
|
||||
pub mod commands;
|
||||
pub mod pe_parse;
|
||||
pub mod services;
|
||||
pub mod app_state;
|
||||
|
||||
@@ -24,7 +23,9 @@ pub fn run() {
|
||||
commands::command_get_pe_data_file_header,
|
||||
commands::command_get_pe_data_optional_header,
|
||||
commands::command_get_pe_data_section_headers,
|
||||
commands::command_get_pe_data_import_directory,
|
||||
commands::command_write_data,
|
||||
commands::command_write_data_list,
|
||||
commands::command_add_section,
|
||||
commands::command_test,
|
||||
])
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum PEParseError {
|
||||
#[error("无效的DOSMagic值")]
|
||||
InvalidDOSMagic,
|
||||
#[error("Invalid NT header signature")]
|
||||
InvalidNTSignature,
|
||||
#[error("Invalid optional header magic number")]
|
||||
InvalidOptionalMagic,
|
||||
#[error("Invalid optional header size")]
|
||||
InvalidOptionalSize,
|
||||
#[error("解析超出了文件范围")]
|
||||
OutOfBounds,
|
||||
}
|
||||
/// PE操作的错误
|
||||
#[derive(Error, Debug)]
|
||||
pub enum MutablePEError {
|
||||
// 兼容PE解析错误
|
||||
#[error(transparent)]
|
||||
PEParseError(#[from] PEParseError),
|
||||
// 头部映射无法扩容,超出最大可扩容范围
|
||||
#[error("头部映射无法扩容, 需要扩容大小:{0:#X},超过了最大可扩容范围:{1:#X}")]
|
||||
CannotExpandHeader(u32, u32),
|
||||
|
||||
// 文件大小不足,无法扩容
|
||||
#[error("文件大小不足,无法扩容! 需要的大小:{0:#X}, 当前的文件大小:{1:#X}")]
|
||||
CannotExpandFileSize(u32, u32),
|
||||
|
||||
}
|
||||
@@ -1,393 +0,0 @@
|
||||
use super::types::*;
|
||||
use bitflags::bitflags;
|
||||
use serde::Serialize;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Serialize)]
|
||||
pub struct ImageDosHeader {
|
||||
pub e_magic: u16, // Magic number 固定值 0x5A4D
|
||||
pub e_cblp: u16,
|
||||
pub e_cp: u16,
|
||||
pub e_crlc: u16,
|
||||
pub e_cparhdr: u16,
|
||||
pub e_minalloc: u16,
|
||||
pub e_maxalloc: u16,
|
||||
pub e_ss: u16,
|
||||
pub e_sp: u16,
|
||||
pub e_csum: u16,
|
||||
pub e_ip: u16,
|
||||
pub e_cs: u16,
|
||||
pub e_lfarlc: u16,
|
||||
pub e_ovno: u16,
|
||||
pub e_res: [u16; 4],
|
||||
pub e_oemid: u16,
|
||||
pub e_oeminfo: u16,
|
||||
pub e_res2: [u16; 10],
|
||||
pub e_lfanew: Offset, // File address of new exe header nt头的偏移
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Serialize)]
|
||||
pub struct ImageNTHeader32 {
|
||||
pub signature: u32,
|
||||
pub file_header: ImageFileHeader,
|
||||
pub optional_header: ImageOptionalHeader32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Serialize)]
|
||||
pub struct ImageNTHeader64 {
|
||||
pub signature: u32,
|
||||
pub file_header: ImageFileHeader,
|
||||
pub optional_header: ImageOptionalHeader64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Serialize, Clone, Copy)]
|
||||
#[serde(untagged)]
|
||||
pub enum ImageNTHeader{
|
||||
NTHeader32(ImageNTHeader32),
|
||||
NTHeader64(ImageNTHeader64),
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum ImageNTHeaderMut<'a>{
|
||||
NTHeader32(&'a mut ImageNTHeader32),
|
||||
NTHeader64(&'a mut ImageNTHeader64),
|
||||
}
|
||||
|
||||
impl <'a> ImageNTHeaderMut<'a>{
|
||||
pub fn get_optional_header_mut(&mut self) -> ImageOptionalHeaderMut{
|
||||
match self{
|
||||
ImageNTHeaderMut::NTHeader32(header) => ImageOptionalHeaderMut::OptionalHeader32(&mut header.optional_header),
|
||||
ImageNTHeaderMut::NTHeader64(header) => ImageOptionalHeaderMut::OptionalHeader64(&mut header.optional_header),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Serialize)]
|
||||
pub struct ImageFileHeader {
|
||||
pub machine: u16,
|
||||
pub number_of_sections: u16,
|
||||
pub time_date_stamp: u32,
|
||||
pub pointer_to_symbol_table: Offset,
|
||||
pub number_of_symbols: u32,
|
||||
pub size_of_optional_header: u16,
|
||||
pub characteristics: FileCharacteristics,
|
||||
}
|
||||
impl Serialize for FileCharacteristics {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
// 直接返回bitflags的整数值
|
||||
serializer.serialize_u16(self.bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for DLLCharacteristics {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_u16(self.bits())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for SectionCharacteristics {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_u32(self.bits())
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FileCharacteristics: u16 {
|
||||
const RELOCS_STRIPPED = 0x0001;
|
||||
const EXECUTABLE_IMAGE = 0x0002;
|
||||
const LINE_NUMS_STRIPPED = 0x0004;
|
||||
const LOCAL_SYMS_STRIPPED = 0x0008;
|
||||
const AGGRESSIVE_WS_TRIM = 0x0010;
|
||||
const LARGE_ADDRESS_AWARE = 0x0020;
|
||||
const BYTES_REVERSED_LO = 0x0080;
|
||||
const MACHINE_32BIT = 0x0100;
|
||||
const DEBUG_STRIPPED = 0x0200;
|
||||
const REMOVABLE_RUN_FROM_SWAP = 0x0400;
|
||||
const NET_RUN_FROM_SWAP = 0x0800;
|
||||
const SYSTEM = 0x1000;
|
||||
const DLL = 0x2000;
|
||||
const UP_SYSTEM_ONLY = 0x4000;
|
||||
const BYTES_REVERSED_HI = 0x8000;
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct DLLCharacteristics: u16 {
|
||||
const RESERVED1 = 0x0001;
|
||||
const RESERVED2 = 0x0002;
|
||||
const RESERVED4 = 0x0004;
|
||||
const RESERVED8 = 0x0008;
|
||||
const HIGH_ENTROPY_VA = 0x0020;
|
||||
const DYNAMIC_BASE = 0x0040;
|
||||
const FORCE_INTEGRITY = 0x0080;
|
||||
const NX_COMPAT = 0x0100;
|
||||
const NO_ISOLATION = 0x0200;
|
||||
const NO_SEH = 0x0400;
|
||||
const NO_BIND = 0x0800;
|
||||
const APPCONTAINER = 0x1000;
|
||||
const WDM_DRIVER = 0x2000;
|
||||
const GUARD_CF = 0x4000;
|
||||
const TERMINAL_SERVER_AWARE = 0x8000;
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SectionCharacteristics: u32 {
|
||||
/// Reserved for future use.
|
||||
const TYPE_REG = 0x00000000;
|
||||
/// Reserved for future use.
|
||||
const TYPE_DSECT = 0x00000001;
|
||||
/// Reserved for future use.
|
||||
const TYPE_NOLOAD = 0x00000002;
|
||||
/// Reserved for future use.
|
||||
const TYPE_GROUP = 0x00000004;
|
||||
/// The section should not be padded to the next boundary.
|
||||
/// This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES.
|
||||
/// This is valid only for object files.
|
||||
const TYPE_NO_PAD = 0x00000008;
|
||||
/// Reserved for future use.
|
||||
const TYPE_COPY = 0x00000010;
|
||||
/// The section contains executable code.
|
||||
const CNT_CODE = 0x00000020;
|
||||
/// The section contains initialized data.
|
||||
const CNT_INITIALIZED_DATA = 0x00000040;
|
||||
/// The section contains uninitialized data.
|
||||
const CNT_UNINITIALIZED_DATA = 0x00000080;
|
||||
/// Reserved for future use.
|
||||
const LNK_OTHER = 0x00000100;
|
||||
/// The section contains comments or other information.
|
||||
/// The .drectve section has this type. This is valid for object files only.
|
||||
const LNK_INFO = 0x00000200;
|
||||
/// Reserved for future use.
|
||||
const TYPE_OVER = 0x00000400;
|
||||
/// The section will not become part of the image.
|
||||
/// This is valid only for object files.
|
||||
const LNK_REMOVE = 0x00000800;
|
||||
/// The section contains COMDAT data. This is valid only for object files.
|
||||
const LNK_COMDAT = 0x00001000;
|
||||
/// Unknown/Reserved.
|
||||
const RESERVED = 0x00002000;
|
||||
/// Unknown flag.
|
||||
const MEM_PROTECTED = 0x00004000;
|
||||
/// Unknown flag.
|
||||
const NO_DEFER_SPEC_EXC = 0x00004000;
|
||||
/// The section contains data referenced through the global pointer (GP).
|
||||
const GPREL = 0x00008000;
|
||||
/// Reserved for future use.
|
||||
const MEM_FARDATA = 0x00008000;
|
||||
/// Reserved for future use.
|
||||
const MEM_SYSHEAP = 0x00010000;
|
||||
/// Reserved for future use.
|
||||
const MEM_PURGEABLE = 0x00020000;
|
||||
/// Reserved for future use.
|
||||
const MEM_16BIT = 0x00020000;
|
||||
/// Reserved for future use.
|
||||
const MEM_LOCKED = 0x00040000;
|
||||
/// Reserved for future use.
|
||||
const MEM_PRELOAD = 0x00080000;
|
||||
/// Align data on a 1-byte boundary. Valid only for object files.
|
||||
const ALIGN_1BYTES = 0x00100000;
|
||||
/// Align data on a 2-byte boundary. Valid only for object files.
|
||||
const ALIGN_2BYTES = 0x00200000;
|
||||
/// Align data on a 4-byte boundary. Valid only for object files.
|
||||
const ALIGN_4BYTES = 0x00300000;
|
||||
/// Align data on an 8-byte boundary. Valid only for object files.
|
||||
const ALIGN_8BYTES = 0x00400000;
|
||||
/// Align data on a 16-byte boundary. Valid only for object files.
|
||||
const ALIGN_16BYTES = 0x00500000;
|
||||
/// Align data on a 32-byte boundary. Valid only for object files.
|
||||
const ALIGN_32BYTES = 0x00600000;
|
||||
/// Align data on a 64-byte boundary. Valid only for object files.
|
||||
const ALIGN_64BYTES = 0x00700000;
|
||||
/// Align data on a 128-byte boundary. Valid only for object files.
|
||||
const ALIGN_128BYTES = 0x00800000;
|
||||
/// Align data on a 256-byte boundary. Valid only for object files.
|
||||
const ALIGN_256BYTES = 0x00900000;
|
||||
/// Align data on a 512-byte boundary. Valid only for object files.
|
||||
const ALIGN_512BYTES = 0x00A00000;
|
||||
/// Align data on a 1024-byte boundary. Valid only for object files.
|
||||
const ALIGN_1024BYTES = 0x00B00000;
|
||||
/// Align data on a 2048-byte boundary. Valid only for object files.
|
||||
const ALIGN_2048BYTES = 0x00C00000;
|
||||
/// Align data on a 4096-byte boundary. Valid only for object files.
|
||||
const ALIGN_4096BYTES = 0x00D00000;
|
||||
/// Align data on an 8192-byte boundary. Valid only for object files.
|
||||
const ALIGN_8192BYTES = 0x00E00000;
|
||||
/// Mask for alignment.
|
||||
const ALIGN_MASK = 0x00F00000;
|
||||
/// The section contains extended relocations.
|
||||
const LNK_NRELOC_OVFL = 0x01000000;
|
||||
/// The section can be discarded as needed.
|
||||
const MEM_DISCARDABLE = 0x02000000;
|
||||
/// The section cannot be cached.
|
||||
const MEM_NOT_CACHED = 0x04000000;
|
||||
/// The section is not pageable.
|
||||
const MEM_NOT_PAGED = 0x08000000;
|
||||
/// The section can be shared in memory.
|
||||
const MEM_SHARED = 0x10000000;
|
||||
/// The section can be executed as code.
|
||||
const MEM_EXECUTE = 0x20000000;
|
||||
/// The section can be read.
|
||||
const MEM_READ = 0x40000000;
|
||||
/// The section can be written to.
|
||||
const MEM_WRITE = 0x80000000;
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Serialize)]
|
||||
pub struct ImageOptionalHeader32 {
|
||||
pub magic: u16,
|
||||
pub major_linker_version: u8,
|
||||
pub minor_linker_version: u8,
|
||||
pub size_of_code: u32,
|
||||
pub size_of_initialized_data: u32,
|
||||
pub size_of_uninitialized_data: u32,
|
||||
pub address_of_entry_point: u32,
|
||||
pub base_of_code: u32,
|
||||
pub base_of_data: u32,
|
||||
pub image_base: u32,
|
||||
pub section_alignment: u32,
|
||||
pub file_alignment: u32,
|
||||
pub major_operating_system_version: u16,
|
||||
pub minor_operating_system_version: u16,
|
||||
pub major_image_version: u16,
|
||||
pub minor_image_version: u16,
|
||||
pub major_subsystem_version: u16,
|
||||
pub minor_subsystem_version: u16,
|
||||
pub win32_version_value: u32,
|
||||
pub size_of_image: u32,
|
||||
pub size_of_headers: u32,
|
||||
pub checksum: u32,
|
||||
pub subsystem: u16,
|
||||
pub dll_characteristics: DLLCharacteristics,
|
||||
pub size_of_stack_reserve: u32,
|
||||
pub size_of_stack_commit: u32,
|
||||
pub size_of_heap_reserve: u32,
|
||||
pub size_of_heap_commit: u32,
|
||||
pub loader_flags: u32,
|
||||
pub number_of_rva_and_sizes: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Serialize)]
|
||||
pub struct ImageOptionalHeader64 {
|
||||
pub magic: u16,
|
||||
pub major_linker_version: u8,
|
||||
pub minor_linker_version: u8,
|
||||
pub size_of_code: u32,
|
||||
pub size_of_initialized_data: u32,
|
||||
pub size_of_uninitialized_data: u32,
|
||||
pub address_of_entry_point: u32,
|
||||
pub base_of_code: u32,
|
||||
pub image_base: u64,
|
||||
pub section_alignment: u32,
|
||||
pub file_alignment: u32,
|
||||
pub major_operating_system_version: u16,
|
||||
pub minor_operating_system_version: u16,
|
||||
pub major_image_version: u16,
|
||||
pub minor_image_version: u16,
|
||||
pub major_subsystem_version: u16,
|
||||
pub minor_subsystem_version: u16,
|
||||
pub win32_version_value: u32,
|
||||
pub size_of_image: u32,
|
||||
pub size_of_headers: u32,
|
||||
pub checksum: u32,
|
||||
pub subsystem: u16,
|
||||
pub dll_characteristics: DLLCharacteristics,
|
||||
pub size_of_stack_reserve: u64,
|
||||
pub size_of_stack_commit: u64,
|
||||
pub size_of_heap_reserve: u64,
|
||||
pub size_of_heap_commit: u64,
|
||||
pub loader_flags: u32,
|
||||
pub number_of_rva_and_sizes: u32,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Serialize, Clone, Copy)]
|
||||
#[serde(untagged)]
|
||||
pub enum ImageOptionalHeader {
|
||||
OptionalHeader32(ImageOptionalHeader32),
|
||||
OptionalHeader64(ImageOptionalHeader64),
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Default, Debug)]
|
||||
pub struct ImageDataDirectory {
|
||||
pub virtual_address: u32,
|
||||
pub size: u32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum ImageDirectoryEntry {
|
||||
Export = 0,
|
||||
Import = 1,
|
||||
Resource = 2,
|
||||
Exception = 3,
|
||||
Security = 4,
|
||||
BaseReloc = 5,
|
||||
Debug = 6,
|
||||
Architecture = 7,
|
||||
GlobalPTR = 8,
|
||||
TLS = 9,
|
||||
LoadConfig = 10,
|
||||
BoundImport = 11,
|
||||
IAT = 12,
|
||||
DelayImport = 13,
|
||||
COMDescriptor = 14,
|
||||
Reserved = 15,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum ImageOptionalHeaderMut<'a> {
|
||||
OptionalHeader32(&'a mut ImageOptionalHeader32),
|
||||
OptionalHeader64(&'a mut ImageOptionalHeader64),
|
||||
}
|
||||
|
||||
impl ImageOptionalHeader {
|
||||
pub fn get_size_of_headers(&self) -> u32 {
|
||||
match self {
|
||||
ImageOptionalHeader::OptionalHeader32(header) => header.size_of_headers,
|
||||
ImageOptionalHeader::OptionalHeader64(header) => header.size_of_headers,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_size_of_image(&self) -> u32 {
|
||||
match self {
|
||||
ImageOptionalHeader::OptionalHeader32(header) => header.size_of_image,
|
||||
ImageOptionalHeader::OptionalHeader64(header) => header.size_of_image,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Serialize)]
|
||||
pub struct ImageSectionHeader {
|
||||
pub name: [u8; 8],
|
||||
pub virtual_size: u32,
|
||||
pub virtual_address: u32,
|
||||
pub size_of_raw_data: u32,
|
||||
pub pointer_to_raw_data: u32,
|
||||
pub pointer_to_relocations: u32,
|
||||
pub pointer_to_linenumbers: u32,
|
||||
pub number_of_relocations: u16,
|
||||
pub number_of_linenumbers: u16,
|
||||
pub characteristics: SectionCharacteristics,
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
pub mod error;
|
||||
pub mod header;
|
||||
pub mod pe;
|
||||
pub mod types;
|
||||
@@ -1,435 +0,0 @@
|
||||
use super::{
|
||||
error::{MutablePEError, PEParseError},
|
||||
header::*,
|
||||
};
|
||||
use std::{
|
||||
mem::{self},
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
pub trait ReadOnlyPE: Deref<Target = [u8]> + Sized + AsRef<[u8]> {
|
||||
/// Get a reference to a type at a given offset.
|
||||
fn get_ref<T>(&self, offset: usize) -> Result<&T, PEParseError> {
|
||||
let len = std::mem::size_of::<T>();
|
||||
if offset + len > self.len() {
|
||||
return Err(PEParseError::OutOfBounds);
|
||||
}
|
||||
let ptr = self.as_ptr().wrapping_offset(offset as isize) as *const T;
|
||||
Ok(unsafe { &*ptr })
|
||||
}
|
||||
|
||||
// 判断是64位还是32位
|
||||
fn is_64_bit(&self) -> Result<bool, PEParseError> {
|
||||
// 先以32位加载可选头,通过可选头的Magic字段判断
|
||||
let optional_header: &ImageOptionalHeader32 = self.get_optional_header_32()?;
|
||||
let is_64_bit = optional_header.magic == 0x20b;
|
||||
Ok(is_64_bit)
|
||||
}
|
||||
|
||||
// TODO: 需要一个RVA->FOA的转换函数
|
||||
// TODO: 需要一个FOA->RVA的转换函数
|
||||
// TODO: 获取数据目录、可变数据目录
|
||||
// TODO: 通过枚举获取某个数据目录的数据
|
||||
|
||||
|
||||
/// 将size与节对齐值进行对齐,返回对齐后的值
|
||||
/// size: 需要对齐的值
|
||||
fn align_size_with_section_alignment(&self, size: u32) -> Result<u32, PEParseError> {
|
||||
let nt_header = self.get_nt_header()?;
|
||||
let section_alignment = match nt_header {
|
||||
ImageNTHeader::NTHeader32(nt_header) => nt_header.optional_header.section_alignment,
|
||||
ImageNTHeader::NTHeader64(nt_header) => nt_header.optional_header.section_alignment,
|
||||
};
|
||||
let aligned_size = (size + section_alignment - 1) & !(section_alignment - 1);
|
||||
Ok(aligned_size)
|
||||
}
|
||||
|
||||
/// 将size与文件对齐值进行对齐,返回对齐后的值
|
||||
/// size: 需要对齐的值
|
||||
fn align_size_with_file_alignment(&self, size: u32) -> Result<u32, PEParseError> {
|
||||
let nt_header = self.get_nt_header()?;
|
||||
let file_alignment = match nt_header {
|
||||
ImageNTHeader::NTHeader32(nt_header) => nt_header.optional_header.file_alignment,
|
||||
ImageNTHeader::NTHeader64(nt_header) => nt_header.optional_header.file_alignment,
|
||||
};
|
||||
let aligned_size = (size + file_alignment - 1) & !(file_alignment - 1);
|
||||
Ok(aligned_size)
|
||||
}
|
||||
|
||||
/// Get the DOS header without verifying its contents.
|
||||
fn get_dos_header(&self) -> Result<&ImageDosHeader, PEParseError> {
|
||||
let result = self.get_ref::<ImageDosHeader>(0)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// 作为32位的NT头解析
|
||||
fn get_nt_header32(&self) -> Result<&ImageNTHeader32, PEParseError> {
|
||||
let nt_offset = self.get_nt_headers_offset()?;
|
||||
let result = self.get_ref::<ImageNTHeader32>(nt_offset)?;
|
||||
Ok(result)
|
||||
}
|
||||
/// 作为64位的NT头解析
|
||||
fn get_nt_header64(&self) -> Result<&ImageNTHeader64, PEParseError> {
|
||||
let nt_offset = self.get_nt_headers_offset()?;
|
||||
let result = self.get_ref::<ImageNTHeader64>(nt_offset)?;
|
||||
Ok(result)
|
||||
}
|
||||
/// 动态的解析NT头 无论是32位还是64位
|
||||
fn get_nt_header(&self) -> Result<ImageNTHeader, PEParseError> {
|
||||
let is_64_bit = self.is_64_bit()?;
|
||||
let result = match is_64_bit {
|
||||
true => ImageNTHeader::NTHeader64(*self.get_nt_header64()?),
|
||||
false => ImageNTHeader::NTHeader32(*self.get_nt_header32()?),
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// 获取nt头的偏移
|
||||
fn get_nt_headers_offset(&self) -> Result<usize, PEParseError> {
|
||||
let dos_header = self.get_dos_header()?;
|
||||
let nt_offset = dos_header.e_lfanew.0 as usize;
|
||||
Ok(nt_offset)
|
||||
}
|
||||
|
||||
// 获取文件头的偏移
|
||||
fn get_file_header_offset(&self) -> Result<usize, PEParseError> {
|
||||
// 1. 获取nt头偏移
|
||||
let nt_offset = self.get_nt_headers_offset()?;
|
||||
// 2. 计算文件头偏移
|
||||
let file_header_offset = nt_offset + std::mem::size_of::<u32>();
|
||||
Ok(file_header_offset)
|
||||
}
|
||||
|
||||
// 获取文件头数据
|
||||
fn get_file_header(&self) -> Result<&ImageFileHeader, PEParseError> {
|
||||
let file_header_offset = self.get_file_header_offset()?;
|
||||
let result = self.get_ref::<ImageFileHeader>(file_header_offset)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// 获取节区数量
|
||||
fn get_number_of_sections(&self) -> Result<usize, PEParseError> {
|
||||
let file_header = self.get_file_header()?;
|
||||
Ok(file_header.number_of_sections as usize)
|
||||
}
|
||||
|
||||
// 获取可选头大小
|
||||
fn get_size_of_optional_header(&self) -> Result<usize, PEParseError> {
|
||||
let file_header = self.get_file_header()?;
|
||||
Ok(file_header.size_of_optional_header as usize)
|
||||
}
|
||||
/// 作为32位的可选头解析
|
||||
fn get_optional_header_32(&self) -> Result<&ImageOptionalHeader32, PEParseError> {
|
||||
let file_header_offset = self.get_file_header_offset()?;
|
||||
let optional_header_offset = file_header_offset + std::mem::size_of::<ImageFileHeader>();
|
||||
let result = self.get_ref::<ImageOptionalHeader32>(optional_header_offset)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// 作为64位的可选头解析
|
||||
fn get_optional_header_64(&self) -> Result<&ImageOptionalHeader64, PEParseError> {
|
||||
let file_header_offset = self.get_file_header_offset()?;
|
||||
let optional_header_offset = file_header_offset + std::mem::size_of::<ImageFileHeader>();
|
||||
let result = self.get_ref::<ImageOptionalHeader64>(optional_header_offset)?;
|
||||
Ok(result)
|
||||
}
|
||||
/// 动态的解析可选头 无论是32位还是64位
|
||||
fn get_optional_header(&self) -> Result<ImageOptionalHeader, PEParseError> {
|
||||
let is_64_bit = self.is_64_bit()?;
|
||||
let result = match is_64_bit {
|
||||
true => ImageOptionalHeader::OptionalHeader64(*self.get_optional_header_64()?),
|
||||
false => ImageOptionalHeader::OptionalHeader32(*self.get_optional_header_32()?),
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
/// 获取可选头的偏移
|
||||
fn get_optional_header_offset(&self) -> Result<usize, PEParseError> {
|
||||
let file_header_offset = self.get_file_header_offset()?;
|
||||
let optional_header_offset = file_header_offset + std::mem::size_of::<ImageFileHeader>();
|
||||
Ok(optional_header_offset)
|
||||
}
|
||||
|
||||
/// 获取节区头的偏移
|
||||
fn get_section_headers_offset(&self) -> Result<usize, PEParseError> {
|
||||
// 节区头偏移在可选头之后,可选头大小是可变的,所以需要计算
|
||||
let optional_header_size = self.get_size_of_optional_header()?;
|
||||
let file_header_offset = self.get_file_header_offset()?;
|
||||
let section_header_offset =
|
||||
file_header_offset + std::mem::size_of::<ImageFileHeader>() + optional_header_size;
|
||||
Ok(section_header_offset)
|
||||
}
|
||||
|
||||
// 获取所有节区头数据
|
||||
fn get_section_headers(&self) -> Result<&[ImageSectionHeader], PEParseError> {
|
||||
// 1. 获取节区数量
|
||||
let number_of_sections = self.get_number_of_sections()?;
|
||||
// 2. 获取节区头偏移
|
||||
let section_header_offset = self.get_section_headers_offset()?;
|
||||
// 3. 获取节区头数据
|
||||
unsafe {
|
||||
let ptr = self
|
||||
.as_ptr()
|
||||
.wrapping_offset(section_header_offset as isize)
|
||||
as *const ImageSectionHeader;
|
||||
let result = std::slice::from_raw_parts(ptr, number_of_sections);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取头映射最大可扩容大小
|
||||
fn get_header_max_expand_size(&self) -> Result<u32, PEParseError> {
|
||||
// 最大可扩容大小 = 节对齐大小 - 最后一个节区头最后一个字节的偏移
|
||||
let nt_header: ImageNTHeader = self.get_nt_header()?;
|
||||
let (section_alignment, file_alignment) = match nt_header {
|
||||
ImageNTHeader::NTHeader32(nt_header) => (
|
||||
nt_header.optional_header.section_alignment,
|
||||
nt_header.optional_header.file_alignment,
|
||||
),
|
||||
ImageNTHeader::NTHeader64(nt_header) => (
|
||||
nt_header.optional_header.section_alignment,
|
||||
nt_header.optional_header.file_alignment,
|
||||
),
|
||||
};
|
||||
let section_header_offset = self.get_section_headers_offset()?;
|
||||
let section_headers = self.get_number_of_sections()? as u32;
|
||||
let last_section_header_offset = section_header_offset
|
||||
+ (section_headers as usize * std::mem::size_of::<ImageSectionHeader>());
|
||||
let mut max_size_of_headers = section_alignment - last_section_header_offset as u32;
|
||||
// max_size_of_headers 向下对齐
|
||||
max_size_of_headers &= !(file_alignment - 1);
|
||||
|
||||
Ok(max_size_of_headers)
|
||||
}
|
||||
|
||||
/// 获取头部空余空间大小
|
||||
/// 头部空余空间大小 = 可选头的SizeOfHeaders - 最后一个节区头最后一个字节的偏移
|
||||
fn get_header_empty_space_size(&self) -> Result<u32, PEParseError> {
|
||||
let nt_header: ImageNTHeader = self.get_nt_header()?;
|
||||
let size_of_headers = match nt_header {
|
||||
ImageNTHeader::NTHeader32(nt_header) => nt_header.optional_header.size_of_headers,
|
||||
ImageNTHeader::NTHeader64(nt_header) => nt_header.optional_header.size_of_headers,
|
||||
};
|
||||
let section_header_offset = self.get_section_headers_offset()?;
|
||||
let section_headers = self.get_number_of_sections()? as u32;
|
||||
let last_section_header_offset = section_header_offset
|
||||
+ (section_headers as usize * std::mem::size_of::<ImageSectionHeader>());
|
||||
let empty_space_size = size_of_headers - last_section_header_offset as u32;
|
||||
Ok(empty_space_size)
|
||||
}
|
||||
}
|
||||
/// 可修改的PE trait
|
||||
pub trait MutablePE: ReadOnlyPE + DerefMut<Target = [u8]> + AsMut<[u8]> {
|
||||
/// 实现一个get_mut
|
||||
fn get_mut<T>(&mut self, offset: usize) -> Result<&mut T, PEParseError> {
|
||||
let len = std::mem::size_of::<T>();
|
||||
if offset + len > self.len() {
|
||||
return Err(PEParseError::OutOfBounds);
|
||||
}
|
||||
let ptr = self.as_mut_ptr().wrapping_offset(offset as isize) as *mut T;
|
||||
Ok(unsafe { &mut *ptr })
|
||||
}
|
||||
|
||||
/// 获取可变的文件头数据
|
||||
fn get_file_header_mut(&mut self) -> Result<&mut ImageFileHeader, PEParseError> {
|
||||
let file_header_offset = self.get_file_header_offset()?;
|
||||
let result = self.get_mut::<ImageFileHeader>(file_header_offset)?;
|
||||
Ok(result)
|
||||
}
|
||||
/// 获取可变的节区头数据
|
||||
fn get_section_headers_mut(&mut self) -> Result<&mut [ImageSectionHeader], PEParseError> {
|
||||
// 1. 获取节区数量
|
||||
let number_of_sections = self.get_number_of_sections()?;
|
||||
// 2. 获取节区头偏移
|
||||
let section_header_offset = self.get_section_headers_offset()?;
|
||||
// 3. 获取节区头数据
|
||||
unsafe {
|
||||
let ptr = self
|
||||
.as_mut_ptr()
|
||||
.wrapping_offset(section_header_offset as isize)
|
||||
as *mut ImageSectionHeader;
|
||||
let result = std::slice::from_raw_parts_mut(ptr, number_of_sections);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
/// 获取可变的NT头数据64位
|
||||
fn get_nt_header64_mut(&mut self) -> Result<&mut ImageNTHeader64, PEParseError> {
|
||||
let nt_offset = self.get_nt_headers_offset()?;
|
||||
let result = self.get_mut::<ImageNTHeader64>(nt_offset)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// 获取可变的NT头数据32位
|
||||
fn get_nt_header32_mut(&mut self) -> Result<&mut ImageNTHeader32, PEParseError> {
|
||||
let nt_offset = self.get_nt_headers_offset()?;
|
||||
let result = self.get_mut::<ImageNTHeader32>(nt_offset)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// 获取可变的NT头数据
|
||||
fn get_nt_header_mut(&mut self) -> Result<ImageNTHeaderMut, PEParseError> {
|
||||
let is_64_bit = self.is_64_bit()?;
|
||||
let result = match is_64_bit {
|
||||
true => ImageNTHeaderMut::NTHeader64(self.get_nt_header64_mut()?),
|
||||
false => ImageNTHeaderMut::NTHeader32(self.get_nt_header32_mut()?),
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// 扩大头映射大小,调用者必须要保证当挪动所有节区数据时,不会超出文件范围
|
||||
/// 也就是,调用者必须要在扩大头映射大小之前,先扩大文件大小
|
||||
/// add_size: 需要扩大的大小,不需要进行对齐,会自动对齐
|
||||
fn expand_headers(&mut self, add_size: u32) -> Result<(), MutablePEError> {
|
||||
// aligned_size: 需要增加的大小,对齐后的值
|
||||
let aligned_file_size = self.align_size_with_file_alignment(add_size)?;
|
||||
let max_expand_size = self.get_header_max_expand_size()?;
|
||||
let optional_header = self.get_optional_header()?;
|
||||
let origin_size_for_headers = optional_header.get_size_of_headers();
|
||||
// 如果扩大后超过了最大可扩容大小,就不允许扩容
|
||||
if aligned_file_size > max_expand_size {
|
||||
return Err(MutablePEError::CannotExpandHeader(
|
||||
aligned_file_size,
|
||||
origin_size_for_headers,
|
||||
));
|
||||
}
|
||||
// 检查一下把,万一调用者没有扩大文件大小
|
||||
let sections = self.get_section_headers()?;
|
||||
let self_len = self.len();
|
||||
// 需要反向遍历,找到最后一个size_of_raw_data不为0的节区
|
||||
// 然后计算这个节区的PointerToRawData+(SizeOfRawData对齐到FileAlignment的值)
|
||||
let mut last_section_end = 0;
|
||||
for section in sections.iter().rev() {
|
||||
if section.size_of_raw_data != 0 {
|
||||
last_section_end = section.pointer_to_raw_data
|
||||
+ self.align_size_with_file_alignment(section.size_of_raw_data)?;
|
||||
if last_section_end as usize + aligned_file_size as usize > self_len {
|
||||
return Err(MutablePEError::CannotExpandFileSize(
|
||||
last_section_end as u32 + aligned_file_size,
|
||||
self_len as u32,
|
||||
));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
let mut nt_header = self.get_nt_header_mut()?;
|
||||
let op = nt_header.get_optional_header_mut();
|
||||
// 更新可选头的SizeOfHeaders和SizeOfImage
|
||||
match op {
|
||||
ImageOptionalHeaderMut::OptionalHeader32(op) => {
|
||||
op.size_of_headers += aligned_file_size;
|
||||
}
|
||||
ImageOptionalHeaderMut::OptionalHeader64(op) => {
|
||||
op.size_of_headers += aligned_file_size;
|
||||
}
|
||||
}
|
||||
let mut_section = self.get_section_headers_mut()?;
|
||||
let start_offset = mut_section.first().unwrap().pointer_to_raw_data;
|
||||
let end_offset = last_section_end;
|
||||
// 更新节区头的PointerToRawData
|
||||
for section in mut_section.iter_mut() {
|
||||
section.pointer_to_raw_data += aligned_file_size;
|
||||
}
|
||||
{
|
||||
let self_data = self.as_mut();
|
||||
// 把[start_offset, end_offset)的数据往后挪动aligned_file_size
|
||||
self_data.copy_within(
|
||||
start_offset as usize..end_offset as usize,
|
||||
(start_offset + aligned_file_size) as usize,
|
||||
);
|
||||
// 把[start_offset, start_offset+aligned_file_size)的数据填充为0
|
||||
self_data[start_offset as usize..(start_offset + aligned_file_size) as usize].fill(0);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 添加一个节区
|
||||
/// section_name: 节区名
|
||||
/// section_size: 节区大小
|
||||
/// section_characteristics: 节区特征
|
||||
fn add_section(
|
||||
&mut self,
|
||||
section_name: &[u8; 8],
|
||||
section_size: u32,
|
||||
section_characteristics: SectionCharacteristics,
|
||||
) -> Result<(), MutablePEError> {
|
||||
// 1. 判断是否有空余空间
|
||||
let empty_space_size = self.get_header_empty_space_size()?;
|
||||
const SIZE_OF_SECTION_HEADER: u32 = mem::size_of::<ImageSectionHeader>() as u32;
|
||||
// section_size与文件对齐后的大小
|
||||
let file_alignment = self.align_size_with_file_alignment(section_size)?;
|
||||
|
||||
if empty_space_size < SIZE_OF_SECTION_HEADER {
|
||||
// 判断是否能够扩容
|
||||
let max_expand_size = self.get_header_max_expand_size()?;
|
||||
if max_expand_size < file_alignment {
|
||||
return Err(MutablePEError::CannotExpandHeader(
|
||||
file_alignment,
|
||||
max_expand_size,
|
||||
));
|
||||
}
|
||||
self.expand_headers(file_alignment)?;
|
||||
}
|
||||
// 增加节区头
|
||||
let sections_offset = self.get_section_headers_offset()?;
|
||||
let number_of_sections = self.get_number_of_sections()?;
|
||||
let new_section_offset =
|
||||
sections_offset + (number_of_sections * SIZE_OF_SECTION_HEADER as usize) as usize;
|
||||
let new_section = unsafe {
|
||||
let ptr = self
|
||||
.as_mut_ptr()
|
||||
.wrapping_offset(new_section_offset as isize)
|
||||
as *mut ImageSectionHeader;
|
||||
&mut *ptr
|
||||
};
|
||||
new_section.name = *section_name;
|
||||
// 新节区的virtual_size是section_size对齐到section_alignment的值
|
||||
let mut section_alignment = self.align_size_with_section_alignment(section_size)?;
|
||||
if section_size == 0 {
|
||||
section_alignment = self.align_size_with_section_alignment(1)?;
|
||||
}
|
||||
new_section.virtual_size = section_alignment;
|
||||
// 如果section_size是0,那么virtual_size必须得是最小的section_alignment
|
||||
// 新节区的virtual_address是上一个节区的virtual_address+(pointer_to_raw_data 对齐到 section_alignment的值)
|
||||
let last_section = self.get_section_headers()?.last().unwrap();
|
||||
let mut last_section_size_of_raw_data_aligned =
|
||||
self.align_size_with_section_alignment(last_section.size_of_raw_data)?;
|
||||
if last_section_size_of_raw_data_aligned == 0 {
|
||||
last_section_size_of_raw_data_aligned = self.align_size_with_section_alignment(1)?;
|
||||
}
|
||||
new_section.virtual_address =
|
||||
last_section.virtual_address + last_section_size_of_raw_data_aligned;
|
||||
new_section.size_of_raw_data = file_alignment;
|
||||
// 如果上一个节区的size_of_raw_data是0,那么上一个节区的pointer_to_raw_data是可以乱写的
|
||||
// 正确的做法应该是,找到上一个size_of_raw_data不为0的节区,然后计算
|
||||
let sections = self.get_section_headers()?;
|
||||
// 反向遍历
|
||||
for section in sections.iter().rev() {
|
||||
if section.size_of_raw_data != 0 {
|
||||
new_section.pointer_to_raw_data = section.pointer_to_raw_data
|
||||
+ self.align_size_with_file_alignment(section.size_of_raw_data)?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
new_section.characteristics = section_characteristics;
|
||||
// 其他字段最好填充为0
|
||||
new_section.number_of_linenumbers = 0;
|
||||
new_section.number_of_relocations = 0;
|
||||
new_section.pointer_to_linenumbers = 0;
|
||||
new_section.pointer_to_relocations = 0;
|
||||
// 更新文件头的NumberOfSections
|
||||
let file_header = self.get_file_header_mut()?;
|
||||
file_header.number_of_sections += 1;
|
||||
|
||||
// 更新可选头的SizeOfImage
|
||||
let mut nt_header = self.get_nt_header_mut()?;
|
||||
let op = nt_header.get_optional_header_mut();
|
||||
match op {
|
||||
ImageOptionalHeaderMut::OptionalHeader32(op) => {
|
||||
op.size_of_image += section_alignment;
|
||||
}
|
||||
ImageOptionalHeaderMut::OptionalHeader64(op) => {
|
||||
op.size_of_image += section_alignment;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
use std::fmt::{Formatter, UpperHex};
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Serialize)]
|
||||
pub struct Offset(pub u32);
|
||||
|
||||
impl UpperHex for Offset {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Serialize)]
|
||||
pub struct RVA(pub u32);
|
||||
@@ -1,13 +1,3 @@
|
||||
use memmap2::{MmapMut, Mmap};
|
||||
use crate::pe_parse::pe::{
|
||||
ReadOnlyPE,
|
||||
MutablePE
|
||||
};
|
||||
pub mod file;
|
||||
pub mod win_proc;
|
||||
|
||||
// 为文件映射实现PE结构
|
||||
impl ReadOnlyPE for Mmap {}
|
||||
|
||||
impl ReadOnlyPE for MmapMut {}
|
||||
// 为可变文件映射实现可操作PE结构
|
||||
impl MutablePE for MmapMut {}
|
||||
|
||||
215
src-tauri/src/services/win_proc.rs
Normal file
215
src-tauri/src/services/win_proc.rs
Normal file
@@ -0,0 +1,215 @@
|
||||
use std::{
|
||||
alloc::{alloc, Layout},
|
||||
io::{Seek, Write},
|
||||
};
|
||||
|
||||
use crate::app_error::AppError;
|
||||
|
||||
use pe_parse::{header::ImageDosHeader, pe::ReadOnlyPE};
|
||||
|
||||
use memmap2::Mmap;
|
||||
use sysinfo::{self, System};
|
||||
use windows::{
|
||||
core::PWSTR,
|
||||
Win32::{
|
||||
Foundation::{CloseHandle, HMODULE},
|
||||
System::{
|
||||
Diagnostics::Debug::ReadProcessMemory,
|
||||
ProcessStatus::EnumProcessModules,
|
||||
Threading::{
|
||||
OpenProcess, QueryFullProcessImageNameW, PROCESS_ACCESS_RIGHTS, PROCESS_ALL_ACCESS,
|
||||
PROCESS_NAME_WIN32,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/// 遍历进程并返回进程名\PID
|
||||
pub fn get_process_list() -> Vec<(String, u32)> {
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_all();
|
||||
let mut process_list = Vec::new();
|
||||
for (pid, process) in sys.processes() {
|
||||
let pid = pid.as_u32();
|
||||
process_list.push((process.name().to_str().unwrap().to_string(), pid));
|
||||
}
|
||||
process_list
|
||||
}
|
||||
pub fn dump_process(pid: u32, save_file_path: &str) -> Result<(), AppError> {
|
||||
#[warn(unused_assignments)]
|
||||
let mut file_path = String::new();
|
||||
// 1. 打开进程
|
||||
|
||||
let mut image_base = 0; // 模块基址
|
||||
let h_process = unsafe {
|
||||
OpenProcess(PROCESS_ACCESS_RIGHTS(PROCESS_ALL_ACCESS.0), false, pid)
|
||||
.map_err(|e| AppError::OpenProcessError(e.to_string()))?
|
||||
};
|
||||
// 2. 获取进程对应的文件路径
|
||||
let mut buffer = [0u16; 1024];
|
||||
let pwstr = PWSTR(buffer.as_mut_ptr());
|
||||
let mut size = buffer.len() as u32;
|
||||
unsafe {
|
||||
let result = QueryFullProcessImageNameW(h_process, PROCESS_NAME_WIN32, pwstr, &mut size);
|
||||
if result.is_err() {
|
||||
return Err(AppError::QueryFullProcessImageNameWError(
|
||||
result.unwrap_err().to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
file_path = pwstr.to_string().unwrap();
|
||||
println!("进程路径: {:?}", file_path);
|
||||
}
|
||||
// 创建新文件
|
||||
let mut save_file = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(save_file_path)
|
||||
.map_err(|e| AppError::Io(e))?;
|
||||
|
||||
// 3. 读取进程的文件,读取它的PE头
|
||||
// 3.1 映射文件
|
||||
let file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.open(file_path)
|
||||
.map_err(|e| AppError::Io(e))?;
|
||||
let mmap = unsafe { Mmap::map(&file).map_err(|e| AppError::Io(e)) }?;
|
||||
// 3.2 读取PE头
|
||||
{
|
||||
// 3.2.1 读取DOS头
|
||||
let dos_header = mmap.get_dos_header()?;
|
||||
let write_size = std::mem::size_of::<ImageDosHeader>() as u64;
|
||||
save_file.write_all(&mmap[..write_size as usize])?;
|
||||
// 3.2.2 读取PE头
|
||||
let nt_header = mmap.get_nt_header()?;
|
||||
let nt_header_size = match nt_header {
|
||||
pe_parse::header::ImageNTHeader::NTHeader32(_) => {
|
||||
std::mem::size_of::<pe_parse::header::ImageNTHeader32>() as u64
|
||||
}
|
||||
pe_parse::header::ImageNTHeader::NTHeader64(_) => {
|
||||
std::mem::size_of::<pe_parse::header::ImageNTHeader64>() as u64
|
||||
}
|
||||
};
|
||||
let nt_header_offset = dos_header.e_lfanew;
|
||||
// 移动文件指针
|
||||
save_file.seek(std::io::SeekFrom::Start(nt_header_offset.0 as u64))?;
|
||||
// 写入PE头
|
||||
save_file.write_all(
|
||||
&mmap[nt_header_offset.0 as usize
|
||||
..(nt_header_offset.0 as usize + nt_header_size as usize) as usize],
|
||||
)?;
|
||||
// 写入nt_header_offset.0 - sizeof(dos_header)的数据
|
||||
// 移动文件指针到dos_header之后
|
||||
save_file.seek(std::io::SeekFrom::Start(write_size))?;
|
||||
save_file.write_all(&mmap[write_size as usize..nt_header_offset.0 as usize])?;
|
||||
// 移动文件指针到最后
|
||||
save_file.seek(std::io::SeekFrom::Start(
|
||||
nt_header_offset.0 as u64 + nt_header_size,
|
||||
))?;
|
||||
// 数据目录
|
||||
let data_directory_offset = nt_header_offset.0 as usize + nt_header_size as usize;
|
||||
save_file.write_all(&mmap[data_directory_offset..data_directory_offset + 16 * 17])?;
|
||||
// 节区头
|
||||
let sections_num = mmap.get_section_headers()?.len();
|
||||
let section_header_offset = mmap.get_section_headers_offset()?;
|
||||
let section_header_size = std::mem::size_of::<pe_parse::header::ImageSectionHeader>()
|
||||
as u64
|
||||
* sections_num as u64;
|
||||
save_file.seek(std::io::SeekFrom::Start(section_header_offset as u64))?;
|
||||
save_file.write_all(
|
||||
&mmap[section_header_offset as usize
|
||||
..(section_header_offset as usize + section_header_size as usize) as usize],
|
||||
)?;
|
||||
save_file.flush()?;
|
||||
}
|
||||
// 获取模块基址
|
||||
{
|
||||
let mut h_module = HMODULE::default();
|
||||
let mut cb_needed = 0;
|
||||
unsafe {
|
||||
EnumProcessModules(
|
||||
h_process,
|
||||
&mut h_module,
|
||||
std::mem::size_of::<HMODULE>() as u32,
|
||||
&mut cb_needed,
|
||||
)
|
||||
.unwrap();
|
||||
// 获取模块基址
|
||||
image_base = h_module.0 as u64;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 遍历节表头,从进程中读取节的内容,写入文件
|
||||
{
|
||||
let sections = mmap.get_section_headers()?;
|
||||
for section in sections.iter() {
|
||||
let section_va = section.virtual_address as u64;
|
||||
let section_size = section.size_of_raw_data as u64;
|
||||
let section_size_align = mmap.align_size_with_section_alignment(section_size as u32)?;
|
||||
let section_offset = image_base + section_va;
|
||||
let section_foa = section.pointer_to_raw_data as u64;
|
||||
|
||||
let data_buf = unsafe {
|
||||
let layout = Layout::array::<u8>(section_size_align as usize).unwrap();
|
||||
alloc(layout)
|
||||
};
|
||||
|
||||
let mut read_size = 0;
|
||||
|
||||
unsafe {
|
||||
ReadProcessMemory(
|
||||
h_process,
|
||||
section_offset as *const _,
|
||||
data_buf as *mut _,
|
||||
section_size_align as usize,
|
||||
Some(&mut read_size),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
println!(
|
||||
"读取节: VA: {:#x}, Size: {:#x}, FOA: {:#x}",
|
||||
section_va, section_size, section_foa
|
||||
);
|
||||
println!(
|
||||
"读取节: Size: {:#x}, ReadSize: {:#x}",
|
||||
section_size_align, read_size
|
||||
);
|
||||
// 如果文件长度不够,则先扩容
|
||||
let file_size = save_file.metadata().unwrap().len();
|
||||
if file_size < section_foa + section_size_align as u64 {
|
||||
// 文件指针要移动到末尾
|
||||
save_file.seek(std::io::SeekFrom::End(0))?;
|
||||
save_file.set_len(section_foa + section_size_align as u64)?;
|
||||
}
|
||||
|
||||
save_file.seek(std::io::SeekFrom::Start(section_foa))?;
|
||||
save_file.write_all(unsafe {
|
||||
std::slice::from_raw_parts(data_buf as *const u8, section_size_align as usize)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
// 6. 关闭进程
|
||||
unsafe {
|
||||
CloseHandle(h_process).unwrap();
|
||||
};
|
||||
// 7. 返回
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 来个测试
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_get_process_list() {
|
||||
let process_list = get_process_list();
|
||||
for (name, pid) in process_list {
|
||||
println!("进程名: {}, PID: {}", name, pid);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_dump_process() {
|
||||
dump_process(11696, "D:\\download\\a.exe").unwrap();
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,19 @@
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"windows": {
|
||||
"allowDowngrades": false,
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": null,
|
||||
"nsis": null,
|
||||
"signCommand": null,
|
||||
"timestampUrl": null,
|
||||
"tsp": false,
|
||||
"webviewInstallMode": {
|
||||
"type": "skip"
|
||||
},
|
||||
"wix": null
|
||||
},
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// import MainLayout from "./layouts/MainLayout";
|
||||
import MainPage from "./pages/MainPage";
|
||||
|
||||
import moment from "moment";
|
||||
import "./App.css";
|
||||
// 初始化moment地区
|
||||
moment.locale("zh-cn");
|
||||
|
||||
|
||||
function App() {
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
.root {
|
||||
margin: 8px;
|
||||
margin-top: 0;
|
||||
height: 100%;
|
||||
background-color: white;
|
||||
border-radius: 5px;
|
||||
padding: 16px;
|
||||
|
||||
|
||||
.optionalContent{
|
||||
margin-bottom: 12px;
|
||||
justify-content: end;
|
||||
}
|
||||
.optionalItem{
|
||||
margin-left: 16px;
|
||||
}
|
||||
@@ -4,7 +4,166 @@ import NodeTableComponent, {
|
||||
DataType,
|
||||
} from "../NodeTableComponent/NodeTableComponent";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { message } from "antd";
|
||||
import { Button, Flex, message } from "antd";
|
||||
import styles from './DosHeader.module.scss'
|
||||
|
||||
|
||||
const dataTemplate: DataTemplateInterface = {
|
||||
e_magic: {
|
||||
name: "e_magic",
|
||||
description: "标识文件的魔数,DOS 可执行文件的值通常为 'MZ'。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 0,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_cblp: {
|
||||
name: "e_cblp",
|
||||
description: "文件最后一页的字节数,指示文件不完整页的大小。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 2,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_cp: {
|
||||
name: "e_cp",
|
||||
description: "文件的总页数(每页 512 字节)。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 4,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_crlc: {
|
||||
name: "e_crlc",
|
||||
description: "重定位表中的条目数。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 6,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_cparhdr: {
|
||||
name: "e_cparhdr",
|
||||
description: "文件头部的段数,单位为 16 字节段。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 8,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_minalloc: {
|
||||
name: "e_minalloc",
|
||||
description: "程序需要的最小额外段数。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 10,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_maxalloc: {
|
||||
name: "e_maxalloc",
|
||||
description: "程序可以分配的最大额外段数。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 12,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_ss: {
|
||||
name: "e_ss",
|
||||
description: "初始的 SS 寄存器值,段偏移地址。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 14,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_sp: {
|
||||
name: "e_sp",
|
||||
description: "初始的 SP 寄存器值,栈顶指针。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 16,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_csum: {
|
||||
name: "e_csum",
|
||||
description: "校验和(一般为 0)。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 18,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_ip: {
|
||||
name: "e_ip",
|
||||
description: "初始的 IP 寄存器值,代码段的入口偏移地址。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 20,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_cs: {
|
||||
name: "e_cs",
|
||||
description: "初始的 CS 寄存器值,代码段的段基址。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 22,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_lfarlc: {
|
||||
name: "e_lfarlc",
|
||||
description: "重定位表的文件偏移。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 24,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_ovno: {
|
||||
name: "e_ovno",
|
||||
description: "覆盖号(通常为 0)。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 26,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_res: {
|
||||
name: "e_res",
|
||||
description: "保留字段,通常为 0。",
|
||||
value: null,
|
||||
size: 8,
|
||||
offset: 28,
|
||||
array_element_size: 2,
|
||||
data_type: "array",
|
||||
},
|
||||
e_oemid: {
|
||||
name: "e_oemid",
|
||||
description: "OEM 标识符。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 36,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_oeminfo: {
|
||||
name: "e_oeminfo",
|
||||
description: "OEM 信息。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 38,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_res2: {
|
||||
name: "e_res2",
|
||||
description: "保留字段,通常为 0。",
|
||||
value: 0,
|
||||
size: 20,
|
||||
offset: 40,
|
||||
data_type: "array",
|
||||
array_element_size: 2,
|
||||
},
|
||||
e_lfanew: {
|
||||
name: "e_lfanew",
|
||||
description: "PE 文件头的偏移地址。",
|
||||
value: 0,
|
||||
size: 4,
|
||||
offset: 60,
|
||||
data_type: "hex",
|
||||
},
|
||||
};
|
||||
interface DosHeaderResponseData {
|
||||
base_offset: number;
|
||||
fields: {
|
||||
@@ -50,176 +209,7 @@ const formatEditValue = (item: DataType) => {
|
||||
|
||||
export default function DosHeader() {
|
||||
const [data, setData] = useState<DosHeaderResponseData>();
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const success = () => {
|
||||
messageApi.open({
|
||||
type: "success",
|
||||
content: "修改成功!",
|
||||
});
|
||||
};
|
||||
const error = () => {
|
||||
messageApi.open({
|
||||
type: "error",
|
||||
content: "修改失败!",
|
||||
});
|
||||
};
|
||||
|
||||
const dataTemplate: DataTemplateInterface = {
|
||||
e_magic: {
|
||||
name: "e_magic",
|
||||
description: "标识文件的魔数,DOS 可执行文件的值通常为 'MZ'。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 0,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_cblp: {
|
||||
name: "e_cblp",
|
||||
description: "文件最后一页的字节数,指示文件不完整页的大小。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 2,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_cp: {
|
||||
name: "e_cp",
|
||||
description: "文件的总页数(每页 512 字节)。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 4,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_crlc: {
|
||||
name: "e_crlc",
|
||||
description: "重定位表中的条目数。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 6,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_cparhdr: {
|
||||
name: "e_cparhdr",
|
||||
description: "文件头部的段数,单位为 16 字节段。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 8,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_minalloc: {
|
||||
name: "e_minalloc",
|
||||
description: "程序需要的最小额外段数。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 10,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_maxalloc: {
|
||||
name: "e_maxalloc",
|
||||
description: "程序可以分配的最大额外段数。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 12,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_ss: {
|
||||
name: "e_ss",
|
||||
description: "初始的 SS 寄存器值,段偏移地址。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 14,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_sp: {
|
||||
name: "e_sp",
|
||||
description: "初始的 SP 寄存器值,栈顶指针。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 16,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_csum: {
|
||||
name: "e_csum",
|
||||
description: "校验和(一般为 0)。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 18,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_ip: {
|
||||
name: "e_ip",
|
||||
description: "初始的 IP 寄存器值,代码段的入口偏移地址。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 20,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_cs: {
|
||||
name: "e_cs",
|
||||
description: "初始的 CS 寄存器值,代码段的段基址。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 22,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_lfarlc: {
|
||||
name: "e_lfarlc",
|
||||
description: "重定位表的文件偏移。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 24,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_ovno: {
|
||||
name: "e_ovno",
|
||||
description: "覆盖号(通常为 0)。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 26,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_res: {
|
||||
name: "e_res",
|
||||
description: "保留字段,通常为 0。",
|
||||
value: null,
|
||||
size: 8,
|
||||
offset: 28,
|
||||
array_element_size: 2,
|
||||
data_type: "array",
|
||||
},
|
||||
e_oemid: {
|
||||
name: "e_oemid",
|
||||
description: "OEM 标识符。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 36,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_oeminfo: {
|
||||
name: "e_oeminfo",
|
||||
description: "OEM 信息。",
|
||||
value: 0,
|
||||
size: 2,
|
||||
offset: 38,
|
||||
data_type: "hex",
|
||||
},
|
||||
e_res2: {
|
||||
name: "e_res2",
|
||||
description: "保留字段,通常为 0。",
|
||||
value: 0,
|
||||
size: 20,
|
||||
offset: 40,
|
||||
data_type: "array",
|
||||
array_element_size: 2,
|
||||
},
|
||||
e_lfanew: {
|
||||
name: "e_lfanew",
|
||||
description: "PE 文件头的偏移地址。",
|
||||
value: 0,
|
||||
size: 4,
|
||||
offset: 60,
|
||||
data_type: "hex",
|
||||
},
|
||||
};
|
||||
const [editValues, setEditValues] = useState([]);
|
||||
|
||||
const command = "command_get_pe_data_dos_header";
|
||||
|
||||
@@ -259,24 +249,34 @@ export default function DosHeader() {
|
||||
},
|
||||
];
|
||||
|
||||
const handleSave = (item: DataType) => {
|
||||
const handleEdit = (item: DataType) => {
|
||||
if (!checkEditValue(item)) {
|
||||
return;
|
||||
}
|
||||
const bufs = formatEditValue(item);
|
||||
console.log("bufs", bufs, item);
|
||||
invoke("command_write_data", {
|
||||
offset: item.offset,
|
||||
data: bufs,
|
||||
const formatEditData: any = {};
|
||||
formatEditData.offset = item.offset;
|
||||
formatEditData.data = formatEditValue(item);
|
||||
console.log("formatEditData", formatEditData);
|
||||
const newEditValues = [...editValues, formatEditData];
|
||||
setEditValues(newEditValues);
|
||||
// 需要同步修改data
|
||||
const newData = { ...data };
|
||||
newData.fields[item.name] = item.value;
|
||||
setData(newData);
|
||||
}
|
||||
|
||||
|
||||
const handleSave = () => {
|
||||
const command = "command_write_data_list";
|
||||
invoke(command, { dataList: editValues }).then((res) => {
|
||||
message.success("保存成功");
|
||||
// 刷新数据
|
||||
getData();
|
||||
// 清空编辑数据
|
||||
setEditValues([]);
|
||||
}).catch((err) => {
|
||||
message.error(`保存失败: ${err}`);
|
||||
})
|
||||
.then((_res) => {
|
||||
// 提示
|
||||
success();
|
||||
getData();
|
||||
})
|
||||
.catch(() => {
|
||||
error();
|
||||
});
|
||||
};
|
||||
|
||||
const getData = () => {
|
||||
@@ -291,12 +291,25 @@ export default function DosHeader() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextHolder}
|
||||
<Flex className={styles.optionalContent}>
|
||||
<Button className={styles.optionalItem}
|
||||
disabled={editValues.length === 0}
|
||||
type="primary" onClick={handleSave}>保存</Button>
|
||||
<Button className={styles.optionalItem}
|
||||
disabled={editValues.length === 0}
|
||||
|
||||
danger onClick={()=>{
|
||||
// 清空编辑数据
|
||||
setEditValues([]);
|
||||
// 重新获取数据
|
||||
getData();
|
||||
}}>还原</Button>
|
||||
</Flex>
|
||||
<NodeTableComponent
|
||||
dataTemplate={dataTemplate}
|
||||
defaultData={data}
|
||||
columns={columns as any}
|
||||
onEdit={handleSave}
|
||||
onEdit={handleEdit}
|
||||
></NodeTableComponent>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.moduleContainer{
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.functionContainer{
|
||||
|
||||
}
|
||||
190
src/components/ImportDirectory/ImportDirectory.tsx
Normal file
190
src/components/ImportDirectory/ImportDirectory.tsx
Normal file
@@ -0,0 +1,190 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import styles from "./ImportDirectory.module.scss";
|
||||
import moment from "moment";
|
||||
import { Flex, Table } from "antd";
|
||||
import Title from "antd/es/typography/Title";
|
||||
|
||||
interface ModuleProps {
|
||||
module_name: String;
|
||||
timestamp: number;
|
||||
forwarder_chain: number;
|
||||
module_name_rva: number;
|
||||
functions: FunctionProps[];
|
||||
}
|
||||
interface FunctionProps {
|
||||
function_name: string;
|
||||
function_address: number;
|
||||
function_hint: number;
|
||||
function_type: "Named" | "Ordinal";
|
||||
}
|
||||
|
||||
interface ImportModuleComponentProps {
|
||||
moduleList: ModuleProps[];
|
||||
}
|
||||
|
||||
interface ModuleFuntionsComponentProps {
|
||||
functionList: FunctionProps[];
|
||||
}
|
||||
|
||||
const ImportModuleComponent: React.FC<ImportModuleComponentProps> = (props) => {
|
||||
const { moduleList } = props;
|
||||
const [selectedRowKey, setSelectedRowKey] = useState([]);
|
||||
const handleRowSelection = (selectedRowKeys: any) => {
|
||||
setSelectedRowKey(selectedRowKeys);
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: "模块名称",
|
||||
dataIndex: "module_name",
|
||||
key: "module_name",
|
||||
},
|
||||
{
|
||||
title: "导入函数数量",
|
||||
render: (_text: any, record: any) => {
|
||||
console.log("record", record);
|
||||
return record.functions.length;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "时间戳",
|
||||
dataIndex: "timestamp",
|
||||
key: "timestamp",
|
||||
render: (text: number) => {
|
||||
return moment(text).format("YYYY-MM-DD HH:mm:ss");
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "转发链",
|
||||
dataIndex: "forwarder_chain",
|
||||
key: "forwarder_chain",
|
||||
},
|
||||
{
|
||||
title: "模块名称的RVA",
|
||||
dataIndex: "module_name_rva",
|
||||
key: "module_name_rva",
|
||||
render: (text: number) => {
|
||||
return "0x" + text.toString(16).toUpperCase();
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const select_functions = useMemo(() => {
|
||||
return moduleList.find((module) => module.module_name === selectedRowKey[0])
|
||||
?.functions;
|
||||
}, [selectedRowKey]);
|
||||
|
||||
return (
|
||||
<Flex className={styles.moduleContainer}>
|
||||
<div>
|
||||
<Title level={3}>导入模块</Title>
|
||||
<Table
|
||||
columns={columns}
|
||||
rowSelection={{
|
||||
type: "radio",
|
||||
selectedRowKeys: selectedRowKey,
|
||||
selections: [
|
||||
{
|
||||
key: "module_name",
|
||||
text: "模块名称",
|
||||
},
|
||||
],
|
||||
}}
|
||||
onRow={(record, rowIndex) => {
|
||||
return {
|
||||
onClick: (event) => {
|
||||
handleRowSelection([record.module_name]);
|
||||
},
|
||||
};
|
||||
}}
|
||||
bordered
|
||||
dataSource={moduleList}
|
||||
rowKey="module_name"
|
||||
pagination={false}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Title level={3}>导入模块</Title>
|
||||
<ModuleFuntionsComponent functionList={select_functions} />
|
||||
</div>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const ModuleFuntionsComponent: React.FC<ModuleFuntionsComponentProps> = (
|
||||
props
|
||||
) => {
|
||||
const { functionList } = props;
|
||||
const columns = [
|
||||
{
|
||||
title: "序号",
|
||||
render:(_text,_record,index)=>`${index+1}`,
|
||||
rowScope: 'row',
|
||||
},
|
||||
{
|
||||
title: "函数名称",
|
||||
dataIndex: "function_name",
|
||||
key: "function_name",
|
||||
},
|
||||
{
|
||||
title: "函数地址",
|
||||
dataIndex: "function_address",
|
||||
key: "function_address",
|
||||
render: (text: number) => {
|
||||
return "0x" + text.toString(16).toUpperCase();
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "函数提示",
|
||||
dataIndex: "function_hint",
|
||||
key: "function_hint",
|
||||
},
|
||||
{
|
||||
title: "函数类型",
|
||||
dataIndex: "function_type",
|
||||
key: "function_type",
|
||||
render: (text: string) => {
|
||||
console.log("text", text);
|
||||
return text == "Named" ? "命名" : "序数";
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Flex className={styles.functionContainer}>
|
||||
<Table
|
||||
columns={columns as any}
|
||||
dataSource={functionList}
|
||||
rowKey="function_name"
|
||||
pagination={false}
|
||||
bordered
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default function ImportDirectory() {
|
||||
const [data, setData] = useState([]);
|
||||
|
||||
const getData = () => {
|
||||
invoke("command_get_pe_data_import_directory")
|
||||
.then((res) => {
|
||||
const { fields } = res as any;
|
||||
setData(fields);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("err", err);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getData();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ImportModuleComponent moduleList={data} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,5 +3,5 @@
|
||||
}
|
||||
|
||||
.NodeTableComponentTable{
|
||||
height: calc(100% - 16px);
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
@@ -288,7 +288,6 @@ export default function NodeTableComponent(props: NodeTableComponentProps) {
|
||||
rowKey={"name"}
|
||||
bordered
|
||||
size="small"
|
||||
sticky={{ offsetHeader: -16 }}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -26,10 +26,11 @@
|
||||
.content{
|
||||
background-color: white;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
justify-content: start;
|
||||
border-radius: 5px;
|
||||
overflow: scroll;
|
||||
padding: 16px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.mainContent {
|
||||
|
||||
@@ -12,6 +12,7 @@ import FileHeader from "../components/FileHeader/FileHeader";
|
||||
import SectionHeaders from "../components/SectionHeaders/SectionHeaders";
|
||||
import OptionalHeader from "../components/OptionalHeader/OptionalHeader";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import ImportDirectory from "../components/ImportDirectory/ImportDirectory";
|
||||
|
||||
const SelectNodeMap = {
|
||||
dos_header: <DosHeader />,
|
||||
@@ -19,6 +20,7 @@ const SelectNodeMap = {
|
||||
file_header: <FileHeader />,
|
||||
optional_header: <OptionalHeader />,
|
||||
section_header: <SectionHeaders />,
|
||||
import_directory: <ImportDirectory />,
|
||||
};
|
||||
|
||||
export default function MainPage() {
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
"noImplicitAny": false,
|
||||
/* Linting */
|
||||
"strict": false,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"],
|
||||
|
||||
Reference in New Issue
Block a user