mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-02-05 05:37:34 +00:00
Compare commits
428 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
844a9686af | ||
|
|
8f929366b3 | ||
|
|
dfa0fab979 | ||
|
|
415d6c587f | ||
|
|
b1034b964e | ||
|
|
85d2382680 | ||
|
|
a6e694fe5d | ||
|
|
a579bfea1f | ||
|
|
ba97abed44 | ||
|
|
9fe6b101bd | ||
|
|
1345182eea | ||
|
|
a84ac8d3cc | ||
|
|
1eb07c52e5 | ||
|
|
0d75ee135c | ||
|
|
c915f29c05 | ||
|
|
38d35d1991 | ||
|
|
dbc841cb7f | ||
|
|
46f53221c9 | ||
|
|
708fd01d1e | ||
|
|
4aa3ba0545 | ||
|
|
57499002d2 | ||
|
|
630f628598 | ||
|
|
d66c890b71 | ||
|
|
2b5649588a | ||
|
|
5425578f78 | ||
|
|
8c39adcc04 | ||
|
|
7773b32847 | ||
|
|
143b261a67 | ||
|
|
25fc2b9b04 | ||
|
|
d6fd0c4d2c | ||
|
|
44c44be412 | ||
|
|
7b71d7b4bf | ||
|
|
2c2aee6797 | ||
|
|
afdd032f73 | ||
|
|
9d52ca4b4c | ||
|
|
1bc8fe7ff6 | ||
|
|
6ab7a06dd5 | ||
|
|
7195ed3587 | ||
|
|
214e8d41c7 | ||
|
|
bd9f583659 | ||
|
|
abbf0b7ff5 | ||
|
|
f2b9e3a31b | ||
|
|
73dd669c20 | ||
|
|
32390149f3 | ||
|
|
9e73d8762e | ||
|
|
09854b469e | ||
|
|
e817063e53 | ||
|
|
7cdf6a8c79 | ||
|
|
d87087dcfb | ||
|
|
7c27fcd8a4 | ||
|
|
56408ed9f4 | ||
|
|
bf385f0bbf | ||
|
|
2a6a2930c1 | ||
|
|
9f676732a4 | ||
|
|
44fac8cc92 | ||
|
|
e510915098 | ||
|
|
b779f2f546 | ||
|
|
5344de96b2 | ||
|
|
1e70d960ba | ||
|
|
e03808fbc5 | ||
|
|
373268a6a8 | ||
|
|
905d440367 | ||
|
|
5628cf8d73 | ||
|
|
4aaea417f0 | ||
|
|
892886b730 | ||
|
|
1028050464 | ||
|
|
af79b00bd6 | ||
|
|
dc9a581e1c | ||
|
|
4d800fd644 | ||
|
|
126e8827de | ||
|
|
23c79d4452 | ||
|
|
173fc69a08 | ||
|
|
0411278f1d | ||
|
|
bb1f9bdcdc | ||
|
|
9d5ab935de | ||
|
|
1df9d145e4 | ||
|
|
fba30949bd | ||
|
|
198c320ad8 | ||
|
|
e798ba1104 | ||
|
|
f8f02a54f6 | ||
|
|
da01668cbe | ||
|
|
95770c63af | ||
|
|
af6e5d7441 | ||
|
|
ae5bdcc97a | ||
|
|
0fc415fb34 | ||
|
|
0fe30392d8 | ||
|
|
77fc11289c | ||
|
|
9d3969d4ce | ||
|
|
2ba2756a8f | ||
|
|
53088b4e60 | ||
|
|
0dc83739e7 | ||
|
|
e8a205b221 | ||
|
|
02c3d3fb4a | ||
|
|
2d3d66f077 | ||
|
|
a5f21adeee | ||
|
|
cbb4cdddfa | ||
|
|
e6b898882d | ||
|
|
3bd7f5c890 | ||
|
|
39c20fd0cd | ||
|
|
21117e81a3 | ||
|
|
df172b49db | ||
|
|
1ae0f694de | ||
|
|
040aa8daf6 | ||
|
|
3b9aa2d45c | ||
|
|
8705cac648 | ||
|
|
5a4e3caea8 | ||
|
|
593d4a35b7 | ||
|
|
801eef5f37 | ||
|
|
a0ac0ea189 | ||
|
|
f249455b00 | ||
|
|
8dbb8d9fe1 | ||
|
|
ed698e05d8 | ||
|
|
47b189bf87 | ||
|
|
460eb78ecd | ||
|
|
ffcaf4d16b | ||
|
|
64de357257 | ||
|
|
cc3f6622b4 | ||
|
|
f0b66d4bfb | ||
|
|
9c32f663b0 | ||
|
|
e0e22d91e1 | ||
|
|
dbc72cb4c2 | ||
|
|
17d6c6aa6b | ||
|
|
7be5916041 | ||
|
|
2d8a25178e | ||
|
|
5195025849 | ||
|
|
c3c2fc6171 | ||
|
|
6fa5e9a67f | ||
|
|
834018b325 | ||
|
|
027388f587 | ||
|
|
6452d39de1 | ||
|
|
6d78e2fff7 | ||
|
|
56ae245305 | ||
|
|
76b16ca6d4 | ||
|
|
d0a174d71c | ||
|
|
8e62f12f61 | ||
|
|
621bcdf380 | ||
|
|
1b54dd92ab | ||
|
|
2b0a43ca3e | ||
|
|
81ce49c219 | ||
|
|
b287c7236b | ||
|
|
b63d4a3da0 | ||
|
|
e652e43cba | ||
|
|
e6b2be1738 | ||
|
|
d2606e21fe | ||
|
|
22235cbe84 | ||
|
|
6bd5fae1cd | ||
|
|
ebb20bbe5e | ||
|
|
82d7395b79 | ||
|
|
451cb04714 | ||
|
|
15e5feafef | ||
|
|
4d19bd27f0 | ||
|
|
2400f2d0ad | ||
|
|
ee0193eb71 | ||
|
|
eb76acb767 | ||
|
|
6c77cccf53 | ||
|
|
b4ab969f88 | ||
|
|
2de4f3f808 | ||
|
|
4b5d0980f7 | ||
|
|
2bdbad1ba6 | ||
|
|
3b634877d0 | ||
|
|
2996bbb18f | ||
|
|
c4ca27608b | ||
|
|
5a85ff2ad3 | ||
|
|
a27b3cc43f | ||
|
|
ea8f557097 | ||
|
|
7bbed5985b | ||
|
|
0ec6dfb287 | ||
|
|
3b753c137b | ||
|
|
6cde7b8bef | ||
|
|
d26a89b8ab | ||
|
|
3ab0bcc0ae | ||
|
|
7548646ba2 | ||
|
|
0b75c6f046 | ||
|
|
958d306f42 | ||
|
|
742b25e4dd | ||
|
|
43845cf722 | ||
|
|
a2a0e5c2ee | ||
|
|
93e8322ba5 | ||
|
|
8a07c9cf4e | ||
|
|
6049eda580 | ||
|
|
177641894e | ||
|
|
dc49335ace | ||
|
|
3dcce8a8ac | ||
|
|
04651d46d8 | ||
|
|
56aeded8eb | ||
|
|
97c9c7e5ed | ||
|
|
a891391879 | ||
|
|
5aae9b01d4 | ||
|
|
b74a370b11 | ||
|
|
5e560661d4 | ||
|
|
93e450c2bf | ||
|
|
cc762754c5 | ||
|
|
7065436033 | ||
|
|
debe091502 | ||
|
|
80905b56cd | ||
|
|
0a7cd8a69e | ||
|
|
c3957977a2 | ||
|
|
ff602c77ed | ||
|
|
3667a5b57a | ||
|
|
3ac57b1c0c | ||
|
|
957d82b2f7 | ||
|
|
6d0817ad15 | ||
|
|
5b10e6d614 | ||
|
|
2d39b8c532 | ||
|
|
2ae860e8ca | ||
|
|
9e21c28e52 | ||
|
|
2f5053b49f | ||
|
|
7024136919 | ||
|
|
c74b5b3d29 | ||
|
|
9c3201aa4b | ||
|
|
dfd1141635 | ||
|
|
1188cad5e6 | ||
|
|
65fa2f8481 | ||
|
|
475e0b9d91 | ||
|
|
b76d09aa20 | ||
|
|
9a931eae67 | ||
|
|
47caa714c4 | ||
|
|
6740011c11 | ||
|
|
732078f24d | ||
|
|
5218aaaeb1 | ||
|
|
6b9df94613 | ||
|
|
b1ac88fc20 | ||
|
|
ca83019a58 | ||
|
|
d68272a5bb | ||
|
|
6ab0fd5e3b | ||
|
|
a0034b8fa8 | ||
|
|
a884737242 | ||
|
|
c7b3776386 | ||
|
|
b387c77179 | ||
|
|
f6b58223de | ||
|
|
8960ad3b16 | ||
|
|
28e95f9eb7 | ||
|
|
0bf5065cbc | ||
|
|
579c9c0f84 | ||
|
|
b2e8b66eae | ||
|
|
80e71f43de | ||
|
|
e0497e6fee | ||
|
|
18a78f44c0 | ||
|
|
f9e7fd5725 | ||
|
|
e9c1a170ad | ||
|
|
9ce84c75dd | ||
|
|
0bd5339b78 | ||
|
|
e06b1987b9 | ||
|
|
c8b271ac76 | ||
|
|
1672c73a57 | ||
|
|
32f6e0e8fc | ||
|
|
5c21de5a0f | ||
|
|
9f40a8c4c0 | ||
|
|
c179f29e2e | ||
|
|
f9d6fce3bd | ||
|
|
aa83896963 | ||
|
|
7d13b8c9db | ||
|
|
921292e077 | ||
|
|
c3e7f0b99f | ||
|
|
b9cc5e9ada | ||
|
|
76d76b2bf2 | ||
|
|
532e912a2d | ||
|
|
8ada667dfe | ||
|
|
28a4f7ce82 | ||
|
|
3a66183d0e | ||
|
|
7f91346878 | ||
|
|
2af0dc4a8c | ||
|
|
5240f2eb70 | ||
|
|
f25800510b | ||
|
|
6400c954ef | ||
|
|
e43423d2c9 | ||
|
|
bc613a0413 | ||
|
|
e47a52dbe0 | ||
|
|
da165345b6 | ||
|
|
8ea54328ef | ||
|
|
a4aeebee68 | ||
|
|
95a61c3b28 | ||
|
|
9aadf5e948 | ||
|
|
129ac1bb78 | ||
|
|
e4278c55b7 | ||
|
|
7aca58a6c9 | ||
|
|
ea022de022 | ||
|
|
bb4f16d91f | ||
|
|
f1c165845f | ||
|
|
15ae2441c3 | ||
|
|
6b8f8957de | ||
|
|
cf9bd99f3d | ||
|
|
557114d92d | ||
|
|
d01826ffa4 | ||
|
|
7e3ef544f0 | ||
|
|
ab07eb96ce | ||
|
|
7c53eaab13 | ||
|
|
fad7d87282 | ||
|
|
578519169a | ||
|
|
fe106d23ec | ||
|
|
e1669f031f | ||
|
|
9bff6d5fe1 | ||
|
|
544aaed9da | ||
|
|
b6b7a5e7aa | ||
|
|
cdc4d509ee | ||
|
|
07882f7632 | ||
|
|
deb3c01362 | ||
|
|
a51b16aed2 | ||
|
|
0e7d98158e | ||
|
|
56d8518ee4 | ||
|
|
8897fd8650 | ||
|
|
6f811e43d0 | ||
|
|
a2888e3371 | ||
|
|
30d3312d87 | ||
|
|
854a257fd6 | ||
|
|
28344c6e3f | ||
|
|
11a82addd8 | ||
|
|
152f6c7051 | ||
|
|
bedbceafa7 | ||
|
|
2f19bf7ceb | ||
|
|
a39ae9facf | ||
|
|
56b234bc96 | ||
|
|
633fe23b80 | ||
|
|
2867ce2e9a | ||
|
|
ac0e5e95a9 | ||
|
|
e3bed19e79 | ||
|
|
73aae8118f | ||
|
|
b3671a430e | ||
|
|
54465ff4e7 | ||
|
|
52eef84374 | ||
|
|
f4310206e9 | ||
|
|
7cfa9649e4 | ||
|
|
c6eaafebbe | ||
|
|
df1e14b6c9 | ||
|
|
ad2d854969 | ||
|
|
61202a87fb | ||
|
|
9ebbeaed0f | ||
|
|
aebc139d52 | ||
|
|
0e82eea891 | ||
|
|
7ec76acf2f | ||
|
|
21f17791ff | ||
|
|
fff5f2610a | ||
|
|
d574fb5e44 | ||
|
|
d8aacbcc5d | ||
|
|
6467ef97d5 | ||
|
|
5ec4872b36 | ||
|
|
30bfff833f | ||
|
|
e37d5a80ab | ||
|
|
df1081507d | ||
|
|
a140e1c444 | ||
|
|
f9a990b27b | ||
|
|
b841c7aa94 | ||
|
|
554520ae1f | ||
|
|
3155b3fe41 | ||
|
|
e52cfd244a | ||
|
|
c52b22fb4e | ||
|
|
b3f72bbbe1 | ||
|
|
9ebcfdba53 | ||
|
|
15f020cb06 | ||
|
|
4b387f86c1 | ||
|
|
1a3a73a86d | ||
|
|
31eff196e6 | ||
|
|
828d3403f1 | ||
|
|
28d6d06033 | ||
|
|
0a2477e1b3 | ||
|
|
01451d7009 | ||
|
|
cff9582bf5 | ||
|
|
428d839700 | ||
|
|
50db0044b0 | ||
|
|
68b1ec7b3f | ||
|
|
fd524e1d5c | ||
|
|
ef581c3f36 | ||
|
|
64e67b8daa | ||
|
|
8624350b82 | ||
|
|
e3b32fd974 | ||
|
|
6f789d2454 | ||
|
|
d365dd1164 | ||
|
|
aa3afd676b | ||
|
|
69a04ff825 | ||
|
|
938e1f94bb | ||
|
|
3eda785a5a | ||
|
|
6b895fa7c8 | ||
|
|
45e10a84ae | ||
|
|
81f0400790 | ||
|
|
df90583f73 | ||
|
|
5b980e138a | ||
|
|
0dd71d72ca | ||
|
|
a7364eab67 | ||
|
|
99013e3448 | ||
|
|
7f5e93db95 | ||
|
|
4aa9662ebe | ||
|
|
68dc6c3139 | ||
|
|
8b99577c66 | ||
|
|
20ea1b4427 | ||
|
|
cf0c6126b9 | ||
|
|
49eef3f45a | ||
|
|
23e852bbdb | ||
|
|
f182dccbf2 | ||
|
|
2d2cff4d0e | ||
|
|
c0621a83cb | ||
|
|
3c5da9f7ec | ||
|
|
a7b5288277 | ||
|
|
fee980e048 | ||
|
|
21eac43e9f | ||
|
|
b55698004c | ||
|
|
40bdb9b2d9 | ||
|
|
dfabb1a244 | ||
|
|
cd5f9561bf | ||
|
|
d8d6fac67e | ||
|
|
965c32482d | ||
|
|
c8c9d4ac64 | ||
|
|
5f5abf8a14 | ||
|
|
406791b938 | ||
|
|
eb2e6e7029 | ||
|
|
9f0b41c6aa | ||
|
|
e2b72d8a5b | ||
|
|
9802840309 | ||
|
|
4a6e2fd62d | ||
|
|
79c706e35a | ||
|
|
9be19d6925 | ||
|
|
0cfb9907d0 | ||
|
|
c18e9b3538 | ||
|
|
3754c3a65b | ||
|
|
513a64df4c | ||
|
|
d18a51c7fa | ||
|
|
b43433b9ed | ||
|
|
d553395f3f | ||
|
|
28dbe8542b | ||
|
|
912e605dc8 | ||
|
|
039982d02d | ||
|
|
1a40c6cbb2 | ||
|
|
35921e3cac | ||
|
|
a42040d644 | ||
|
|
e1fb1c7bcf | ||
|
|
095de1441d | ||
|
|
43cbafc0f5 | ||
|
|
9143e4c02c | ||
|
|
2af0692ab4 |
15
.gitmodules
vendored
Normal file
15
.gitmodules
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
[submodule "BurnOutSharp/External/LessIO"]
|
||||
path = BurnOutSharp/External/LessIO
|
||||
url = https://github.com/activescott/LessIO.git
|
||||
[submodule "BurnOutSharp/External/libmspack4n"]
|
||||
path = BurnOutSharp/External/libmspack4n
|
||||
url = https://github.com/activescott/libmspack4n.git
|
||||
[submodule "BurnOutSharp/External/hllib"]
|
||||
path = BurnOutSharp/External/hllib
|
||||
url = https://github.com/RavuAlHemio/hllib.git
|
||||
[submodule "BurnOutSharp/External/stormlibsharp"]
|
||||
path = BurnOutSharp/External/stormlibsharp
|
||||
url = https://github.com/robpaveza/stormlibsharp.git
|
||||
[submodule "BurnOutSharp/External/WixToolset"]
|
||||
path = WixToolset
|
||||
url = https://github.com/wixtoolset/Dtf.git
|
||||
27
.vscode/launch.json
vendored
Normal file
27
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": ".NET Core Launch (console)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/Test/bin/Debug/netcoreapp3.1/Test.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/Test",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach",
|
||||
"processId": "${command:pickProcess}"
|
||||
}
|
||||
]
|
||||
}
|
||||
42
.vscode/tasks.json
vendored
Normal file
42
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/Test/Test.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/Test/Test.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"${workspaceFolder}/Test/Test.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -13,6 +13,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WixToolset", "WixToolset", "{09D405CA-CF15-4929-8408-C970F0656C62}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{182E02A8-5E8E-4140-9C9B-61049C33E921}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Dtf.WindowsInstaller", "WixToolset\src\WixToolset.Dtf.WindowsInstaller\WixToolset.Dtf.WindowsInstaller.csproj", "{B3537EB7-CEF6-4D90-A041-47626442A656}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -27,6 +33,10 @@ Global
|
||||
{88735BA2-778D-4192-8EB2-FFF6843719E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{88735BA2-778D-4192-8EB2-FFF6843719E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{88735BA2-778D-4192-8EB2-FFF6843719E2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B3537EB7-CEF6-4D90-A041-47626442A656}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B3537EB7-CEF6-4D90-A041-47626442A656}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B3537EB7-CEF6-4D90-A041-47626442A656}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B3537EB7-CEF6-4D90-A041-47626442A656}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -34,4 +44,8 @@ Global
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {0B343DD2-8852-47B0-9647-DFCFBEDF933C}
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{182E02A8-5E8E-4140-9C9B-61049C33E921} = {09D405CA-CF15-4929-8408-C970F0656C62}
|
||||
{B3537EB7-CEF6-4D90-A041-47626442A656} = {182E02A8-5E8E-4140-9C9B-61049C33E921}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net472;net48;netcoreapp3.1</TargetFrameworks>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<Title>BurnOutSharp</Title>
|
||||
<AssemblyName>BurnOutSharp</AssemblyName>
|
||||
<Description>Port of BurnOut to C#, with additions</Description>
|
||||
<Authors>Matt Nadareski;Gernot Knippen</Authors>
|
||||
<Product>BurnOutSharp</Product>
|
||||
<Copyright>Copyright (c)2005-2010 Gernot Knippen, Copyright (c)2018-2020 Matt Nadareski</Copyright>
|
||||
<Copyright>Copyright (c)2005-2010 Gernot Knippen, Copyright (c)2018-2021 Matt Nadareski</Copyright>
|
||||
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
|
||||
<RepositoryUrl>https://github.com/mnadareski/BurnOutSharp</RepositoryUrl>
|
||||
<Version>1.4.0</Version>
|
||||
<AssemblyVersion>1.4.0</AssemblyVersion>
|
||||
<FileVersion>1.04.0</FileVersion>
|
||||
<Version>1.8.0</Version>
|
||||
<AssemblyVersion>1.8.0</AssemblyVersion>
|
||||
<FileVersion>1.8.0</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
</PropertyGroup>
|
||||
@@ -22,31 +23,48 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LessIO" Version="0.6.16" />
|
||||
<PackageReference Include="libmspack4n" Version="0.9.10" />
|
||||
<PackageReference Include="SharpCompress" Version="0.25.1" />
|
||||
<PackageReference Include="UnshieldSharp" Version="1.4.2.3" />
|
||||
<PackageReference Include="WiseUnpacker" Version="1.0.0" />
|
||||
<PackageReference Include="zlib.net" Version="1.0.4" />
|
||||
<PackageReference Include="SharpCompress" Version="0.29.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
|
||||
<PackageReference Include="Teronis.MSBuild.Packaging.ProjectBuildInPackage" Version="0.1.7">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="UnshieldSharp" Version="1.6.6" />
|
||||
<PackageReference Include="WiseUnpacker" Version="1.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- These are needed for dealing with submodules -->
|
||||
<PropertyGroup>
|
||||
<DefaultItemExcludes>
|
||||
$(DefaultItemExcludes);
|
||||
**\AssemblyInfo.cs;
|
||||
External\hllib\HLExtract\**\*;
|
||||
External\hllib\HLExtract.Net\Program.cs;
|
||||
External\hllib\HLLib\**\*;
|
||||
External\LessIO\src\LessIO.Tests\**\*;
|
||||
External\libmspack4n\lib\**\*;
|
||||
External\libmspack4n\libmspack4ntest\**\*;
|
||||
External\stormlibsharp\lib\**;
|
||||
External\stormlibsharp\TestConsole\**
|
||||
</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="LICENSE.txt" Pack="true" PackagePath="$(PackageLicenseFile)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="LICENSE.txt" Pack="true" PackagePath="$(PackageLicenseFile)"/>
|
||||
<None Include="*.dll" Pack="true">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="HLLib.x64.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="HLLib.x86.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="StormLib.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="StormLib.pdb">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<ProjectReference Include="..\WixToolset\src\WixToolset.Dtf.WindowsInstaller\WixToolset.Dtf.WindowsInstaller.csproj">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
BIN
BurnOutSharp/CascLib.dll
Normal file
BIN
BurnOutSharp/CascLib.dll
Normal file
Binary file not shown.
@@ -1,446 +0,0 @@
|
||||
//this file is part of BurnOut
|
||||
//Copyright (C)2005-2010 Gernot Knippen
|
||||
//Ported code with augments Copyright (C)2018 Matt Nadareski
|
||||
//
|
||||
//This program is free software; you can redistribute it and/or
|
||||
//modify it under the terms of the GNU General Public License
|
||||
//as published by the Free Software Foundation; either
|
||||
//version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
//This program is distributed in the hope that it will be useful,
|
||||
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
//GNU General Public License for more details.
|
||||
//
|
||||
//You can get a copy of the GNU General Public License
|
||||
//by writing to the Free Software
|
||||
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace BurnOutSharp
|
||||
{
|
||||
internal static class EVORE
|
||||
{
|
||||
private struct Section
|
||||
{
|
||||
public uint iVirtualSize;
|
||||
public uint iVirtualOffset;
|
||||
public uint iRawOffset;
|
||||
}
|
||||
|
||||
private const int WaitSeconds = 20;
|
||||
|
||||
private static Process StartSafe(string file)
|
||||
{
|
||||
if (file == null || !File.Exists(file))
|
||||
return null;
|
||||
|
||||
Process startingprocess = new Process();
|
||||
startingprocess.StartInfo.FileName = file;
|
||||
startingprocess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
startingprocess.StartInfo.CreateNoWindow = true;
|
||||
startingprocess.StartInfo.ErrorDialog = false;
|
||||
try
|
||||
{
|
||||
startingprocess.Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
return null;
|
||||
}
|
||||
return startingprocess;
|
||||
}
|
||||
|
||||
private static string MakeTempFile(byte[] fileContent, string sExtension = ".exe")
|
||||
{
|
||||
string filei = Guid.NewGuid().ToString();
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), "tmp", $"{filei}{sExtension}");
|
||||
try
|
||||
{
|
||||
File.Delete(tempPath);
|
||||
}
|
||||
catch { }
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
|
||||
using (BinaryWriter bw = new BinaryWriter(File.OpenWrite(tempPath)))
|
||||
{
|
||||
bw.Write(fileContent);
|
||||
}
|
||||
|
||||
return Path.GetFullPath(tempPath);
|
||||
}
|
||||
catch { }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool IsEXE(byte[] fileContent)
|
||||
{
|
||||
int PEHeaderOffset = BitConverter.ToInt32(fileContent, 60);
|
||||
short Characteristics = BitConverter.ToInt16(fileContent, PEHeaderOffset + 22);
|
||||
|
||||
// Check if file is dll
|
||||
if ((Characteristics & 0x2000) == 0x2000)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string[] CopyDependentDlls(string file, byte[] fileContent)
|
||||
{
|
||||
Section[] sections = ReadSections(fileContent);
|
||||
|
||||
long lastPosition;
|
||||
string[] saDependentDLLs = null;
|
||||
int index = 60;
|
||||
int PEHeaderOffset = BitConverter.ToInt32(fileContent, index);
|
||||
index = PEHeaderOffset + 120 + 8; //120 Bytes till IMAGE_DATA_DIRECTORY array,8 Bytes=size of IMAGE_DATA_DIRECTORY
|
||||
uint ImportTableRVA = BitConverter.ToUInt32(fileContent, index);
|
||||
index += 4;
|
||||
uint ImportTableSize = BitConverter.ToUInt32(fileContent, index);
|
||||
index = (int)RVA2Offset(ImportTableRVA, sections);
|
||||
index += 12;
|
||||
uint DllNameRVA = BitConverter.ToUInt32(fileContent, index);
|
||||
index += 4;
|
||||
while (DllNameRVA != 0)
|
||||
{
|
||||
string sDllName = "";
|
||||
byte bChar;
|
||||
lastPosition = index;
|
||||
uint DLLNameOffset = RVA2Offset(DllNameRVA, sections);
|
||||
if (DLLNameOffset > 0)
|
||||
{
|
||||
index = (int)DLLNameOffset;
|
||||
if ((char)fileContent[index] > -1)
|
||||
{
|
||||
do
|
||||
{
|
||||
bChar = fileContent[index];
|
||||
index++;
|
||||
sDllName += (char)bChar;
|
||||
} while (bChar != 0 && (char)fileContent[index] > -1);
|
||||
|
||||
sDllName = sDllName.Remove(sDllName.Length - 1, 1);
|
||||
if (File.Exists(Path.Combine(Path.GetDirectoryName(file), sDllName)))
|
||||
{
|
||||
if (saDependentDLLs == null)
|
||||
saDependentDLLs = new string[0];
|
||||
else
|
||||
saDependentDLLs = new string[saDependentDLLs.Length];
|
||||
|
||||
FileInfo fiDLL = new FileInfo(Path.Combine(Path.GetDirectoryName(file), sDllName));
|
||||
saDependentDLLs[saDependentDLLs.Length - 1] = fiDLL.CopyTo(Path.GetTempPath() + sDllName, true).FullName;
|
||||
}
|
||||
}
|
||||
|
||||
index = (int)lastPosition;
|
||||
}
|
||||
|
||||
index += 4 + 12;
|
||||
DllNameRVA = BitConverter.ToUInt32(fileContent, index);
|
||||
index += 4;
|
||||
}
|
||||
|
||||
return saDependentDLLs;
|
||||
}
|
||||
|
||||
private static Section[] ReadSections(byte[] fileContent)
|
||||
{
|
||||
if (fileContent == null)
|
||||
return null;
|
||||
|
||||
uint PEHeaderOffset = BitConverter.ToUInt32(fileContent, 60);
|
||||
ushort NumberOfSections = BitConverter.ToUInt16(fileContent, (int)PEHeaderOffset + 6);
|
||||
Section[] sections = new Section[NumberOfSections];
|
||||
int index = (int)PEHeaderOffset + 120 + 16 * 8;
|
||||
for (int i = 0; i < NumberOfSections; i++)
|
||||
{
|
||||
index += 8;
|
||||
uint ivs = BitConverter.ToUInt32(fileContent, index);
|
||||
index += 4;
|
||||
uint ivo = BitConverter.ToUInt32(fileContent, index);
|
||||
index += 4;
|
||||
index += 4;
|
||||
uint iro = BitConverter.ToUInt32(fileContent, index);
|
||||
index += 4;
|
||||
index += 16;
|
||||
|
||||
sections[i] = new Section()
|
||||
{
|
||||
iVirtualSize = ivs,
|
||||
iVirtualOffset = ivo,
|
||||
iRawOffset = iro,
|
||||
};
|
||||
}
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
private static uint RVA2Offset(uint RVA, Section[] sections)
|
||||
{
|
||||
int i = 0;
|
||||
while (i != sections.Length)
|
||||
{
|
||||
if (sections[i].iVirtualOffset <= RVA && sections[i].iVirtualOffset + sections[i].iVirtualSize > RVA)
|
||||
return RVA - sections[i].iVirtualOffset + sections[i].iRawOffset;
|
||||
i++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#region "EVORE version-search-functions"
|
||||
|
||||
public static string SearchProtectDiscVersion(string file, byte[] fileContent)
|
||||
{
|
||||
string version = "";
|
||||
DateTime timestart;
|
||||
if (!IsEXE(fileContent))
|
||||
return "";
|
||||
|
||||
string tempexe = MakeTempFile(fileContent);
|
||||
string[] DependentDlls = CopyDependentDlls(file, fileContent);
|
||||
try
|
||||
{
|
||||
File.Delete(Path.Combine(Path.GetTempPath(), "a*.tmp"));
|
||||
}
|
||||
catch { }
|
||||
try
|
||||
{
|
||||
File.Delete(Path.Combine(Path.GetTempPath(), "PCD*.sys"));
|
||||
}
|
||||
catch { }
|
||||
if (Directory.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ProtectDisc")))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ProtectDisc", "p*.dll"));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
Process exe = StartSafe(tempexe);
|
||||
if (exe == null)
|
||||
return "";
|
||||
|
||||
Process[] processes = new Process[0];
|
||||
timestart = DateTime.Now;
|
||||
do
|
||||
{
|
||||
exe.Refresh();
|
||||
string[] files = null;
|
||||
|
||||
//check for ProtectDisc 8.2-x
|
||||
if (Directory.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ProtectDisc")))
|
||||
{
|
||||
files = Directory.GetFiles(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ProtectDisc"), "p*.dll");
|
||||
}
|
||||
|
||||
if (files != null)
|
||||
{
|
||||
if (files.Length > 0)
|
||||
{
|
||||
FileVersionInfo fvinfo = FileVersionInfo.GetVersionInfo(files[0]);
|
||||
if (fvinfo.FileVersion != "")
|
||||
{
|
||||
version = fvinfo.FileVersion.Replace(" ", "").Replace(",", ".");
|
||||
//ProtectDisc 9 uses a ProtectDisc-Core dll version 8.0.x
|
||||
if (version.StartsWith("8.0"))
|
||||
version = "";
|
||||
fvinfo = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//check for ProtectDisc 7.1-8.1
|
||||
files = Directory.GetFiles(Path.GetTempPath(), "a*.tmp");
|
||||
if (files.Length > 0)
|
||||
{
|
||||
FileVersionInfo fvinfo = FileVersionInfo.GetVersionInfo(files[0]);
|
||||
if (fvinfo.FileVersion != "")
|
||||
{
|
||||
version = fvinfo.FileVersion.Replace(" ", "").Replace(",", ".");
|
||||
fvinfo = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (exe.HasExited)
|
||||
break;
|
||||
|
||||
processes = Process.GetProcessesByName(exe.ProcessName);
|
||||
if (processes.Length == 2)
|
||||
{
|
||||
processes[0].Refresh();
|
||||
processes[1].Refresh();
|
||||
if (processes[1].WorkingSet64 > exe.WorkingSet64)
|
||||
exe = processes[1];
|
||||
else if (processes[0].WorkingSet64 > exe.WorkingSet64) //else if (processes[0].Modules.Count > exe.Modules.Count)
|
||||
exe = processes[0];
|
||||
}
|
||||
} while (processes.Length > 0 && DateTime.Now.Subtract(timestart).TotalSeconds < WaitSeconds);
|
||||
|
||||
Thread.Sleep(500);
|
||||
if (!exe.HasExited)
|
||||
{
|
||||
processes = Process.GetProcessesByName(exe.ProcessName);
|
||||
if (processes.Length == 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
processes[0].Kill();
|
||||
}
|
||||
catch { }
|
||||
processes[0].Close();
|
||||
try
|
||||
{
|
||||
processes[1].Kill();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
else
|
||||
{
|
||||
exe.Refresh();
|
||||
try
|
||||
{
|
||||
exe.Kill();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
exe.Close();
|
||||
Thread.Sleep(500);
|
||||
if (Directory.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ProtectDisc")))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ProtectDisc", "p*.dll"));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.Delete(Path.Combine(Path.GetTempPath(), "a*.tmp"));
|
||||
}
|
||||
catch { }
|
||||
|
||||
try
|
||||
{
|
||||
File.Delete(Path.Combine(Path.GetTempPath(), "PCD*.sys"));
|
||||
}
|
||||
catch { }
|
||||
File.Delete(tempexe);
|
||||
if (DependentDlls != null)
|
||||
{
|
||||
for (int i = 0; i < DependentDlls.Length; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(DependentDlls[i]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("!error while deleting file " + DependentDlls[i] + "; " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
public static string SearchSafeDiscVersion(string file, byte[] fileContent)
|
||||
{
|
||||
Process exe = new Process();
|
||||
string version = "";
|
||||
DateTime timestart;
|
||||
if (!IsEXE(fileContent))
|
||||
return "";
|
||||
|
||||
string tempexe = MakeTempFile(fileContent);
|
||||
string[] DependentDlls = CopyDependentDlls(file, fileContent);
|
||||
try
|
||||
{
|
||||
Directory.Delete(Path.Combine(Path.GetTempPath(), "~e*"), true);
|
||||
}
|
||||
catch { }
|
||||
try
|
||||
{
|
||||
File.Delete(Path.Combine(Path.GetTempPath(), "~e*"));
|
||||
}
|
||||
catch { }
|
||||
|
||||
exe = StartSafe(tempexe);
|
||||
if (exe == null)
|
||||
return "";
|
||||
|
||||
timestart = DateTime.Now;
|
||||
do
|
||||
{
|
||||
if (Directory.GetDirectories(Path.GetTempPath(), "~e*").Length > 0)
|
||||
{
|
||||
string[] files = Directory.GetFiles(Directory.GetDirectories(Path.GetTempPath(), "~e*")[0], "~de*.tmp");
|
||||
if (files.Length > 0)
|
||||
{
|
||||
StreamReader sr;
|
||||
try
|
||||
{
|
||||
sr = new StreamReader(files[0], Encoding.Default);
|
||||
string FileContent = sr.ReadToEnd();
|
||||
sr.Close();
|
||||
int position = FileContent.IndexOf("%ld.%ld.%ld, %ld, %s,") - 1;
|
||||
if (position > -1)
|
||||
version = FileContent.Substring(position + 28, 12);
|
||||
break;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
} while (!exe.HasExited && DateTime.Now.Subtract(timestart).TotalSeconds < WaitSeconds);
|
||||
|
||||
if (!exe.HasExited)
|
||||
exe.Kill();
|
||||
exe.Close();
|
||||
|
||||
try
|
||||
{
|
||||
Directory.Delete(Path.Combine(Path.GetTempPath(), "~e*"), true);
|
||||
}
|
||||
catch { }
|
||||
try
|
||||
{
|
||||
File.Delete(Path.Combine(Path.GetTempPath(), "~e*"));
|
||||
File.Delete(tempexe);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (DependentDlls != null)
|
||||
{
|
||||
for (int i = 0; i < DependentDlls.Length; i--)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(DependentDlls[i]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("!error while deleting file " + DependentDlls[i] + "; " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
74
BurnOutSharp/ExecutableType/Microsoft/Constants.cs
Normal file
74
BurnOutSharp/ExecutableType/Microsoft/Constants.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft
|
||||
{
|
||||
/// <summary>
|
||||
/// All constant values needed for file header reading
|
||||
/// </summary>
|
||||
internal static class Constants
|
||||
{
|
||||
public const ushort IMAGE_DOS_SIGNATURE = 0x5A4D; // MZ
|
||||
public const ushort IMAGE_OS2_SIGNATURE = 0x454E; // NE
|
||||
public const ushort IMAGE_OS2_SIGNATURE_LE = 0x454C; // LE
|
||||
public const uint IMAGE_NT_SIGNATURE = 0x00004550; // PE00
|
||||
|
||||
#region IMAGE_DOS_HEADER
|
||||
|
||||
public const ushort ENEWEXE = 0x40; // Value of E_LFARLC for new .EXEs
|
||||
public const ushort ENEWHDR = 0x003C; // Offset in old hdr. of ptr. to new
|
||||
public const ushort ERESWDS = 0x0010; // No. of reserved words (OLD)
|
||||
public const ushort ERES1WDS = 0x0004; // No. of reserved words in e_res
|
||||
public const ushort ERES2WDS = 0x000A; // No. of reserved words in e_res2
|
||||
public const ushort ECP = 0x0004; // Offset in struct of E_CP
|
||||
public const ushort ECBLP = 0x0002; // Offset in struct of E_CBLP
|
||||
public const ushort EMINALLOC = 0x000A; // Offset in struct of E_MINALLOC
|
||||
|
||||
#endregion
|
||||
|
||||
#region IMAGE_OS2_HEADER
|
||||
|
||||
public const ushort NERESWORDS = 3; // 6 bytes reserved
|
||||
public const ushort NECRC = 8; //Offset into new header of NE_CRC
|
||||
|
||||
#endregion
|
||||
|
||||
#region NewSeg
|
||||
|
||||
public const ushort NSALIGN = 9; // Segment data aligned on 512 byte boundaries
|
||||
public const ushort NSLOADED = 0x0004; // ns_sector field contains memory addr
|
||||
|
||||
#endregion
|
||||
|
||||
#region RsrcNameInfo
|
||||
|
||||
public const ushort RSORDID = 0x8000; /* if high bit of ID set then integer id */
|
||||
|
||||
/* otherwise ID is offset of string from
|
||||
the beginning of the resource table */
|
||||
/* Ideally these are the same as the */
|
||||
/* corresponding segment flags */
|
||||
public const ushort RNMOVE = 0x0010; /* Moveable resource */
|
||||
public const ushort RNPURE = 0x0020; /* Pure (read-only) resource */
|
||||
public const ushort RNPRELOAD = 0x0040; /* Preloaded resource */
|
||||
public const ushort RNDISCARD = 0xF000; /* Discard priority level for resource */
|
||||
|
||||
#endregion
|
||||
|
||||
#region IMAGE_OPTIONAL_HEADER
|
||||
|
||||
public const ushort IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IMAGE_SECTION_HEADER
|
||||
|
||||
public const int IMAGE_SIZEOF_SHORT_NAME = 8;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IMAGE_RESOURCE_DATA_ENTRY
|
||||
|
||||
public const uint IMAGE_RESOURCE_DATA_IS_DIRECTORY = 0x80000000;
|
||||
public const uint IMAGE_RESOURCE_NAME_IS_STRING = 0x80000000;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Tools;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Headers;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Entries
|
||||
{
|
||||
/// <summary>
|
||||
/// Each entry in the export address table is a field that uses one of two formats in the following table.
|
||||
/// If the address specified is not within the export section (as defined by the address and length that are indicated in the optional header), the field is an export RVA, which is an actual address in code or data.
|
||||
/// Otherwise, the field is a forwarder RVA, which names a symbol in another DLL.
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-address-table</remarks>
|
||||
public class ExportAddressTableEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The address of the exported symbol when loaded into memory, relative to the image base.
|
||||
/// For example, the address of an exported function.
|
||||
/// </summary>
|
||||
public uint ExportRVA;
|
||||
|
||||
/// <summary>
|
||||
/// The pointer to a null-terminated ASCII string in the export section.
|
||||
/// This string must be within the range that is given by the export table data directory entry.
|
||||
/// This string gives the DLL name and the name of the export (for example, "MYDLL.expfunc") or the DLL name and the ordinal number of the export (for example, "MYDLL.#27").
|
||||
/// </summary>
|
||||
public uint ForwarderRVA; // TODO: Read this into a separate field
|
||||
|
||||
/// <summary>
|
||||
/// A null-terminated ASCII string in the export section.
|
||||
/// This string must be within the range that is given by the export table data directory entry.
|
||||
/// This string gives the DLL name and the name of the export (for example, "MYDLL.expfunc") or the DLL name and the ordinal number of the export (for example, "MYDLL.#27").
|
||||
/// </summary>
|
||||
public string Forwarder;
|
||||
|
||||
public static ExportAddressTableEntry Deserialize(Stream stream, SectionHeader[] sections)
|
||||
{
|
||||
var eate = new ExportAddressTableEntry();
|
||||
|
||||
eate.ExportRVA = stream.ReadUInt32();
|
||||
eate.ForwarderRVA = eate.ExportRVA;
|
||||
|
||||
int forwarderAddress = (int)PortableExecutable.ConvertVirtualAddress(eate.ForwarderRVA, sections);
|
||||
if (forwarderAddress > -1 && forwarderAddress < stream.Length)
|
||||
{
|
||||
long originalPosition = stream.Position;
|
||||
stream.Seek(forwarderAddress, SeekOrigin.Begin);
|
||||
eate.Forwarder = stream.ReadString(Encoding.ASCII);
|
||||
stream.Seek(originalPosition, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
return eate;
|
||||
}
|
||||
|
||||
public static ExportAddressTableEntry Deserialize(byte[] content, ref int offset, SectionHeader[] sections)
|
||||
{
|
||||
var eate = new ExportAddressTableEntry();
|
||||
|
||||
eate.ExportRVA = content.ReadUInt32(ref offset);
|
||||
eate.ForwarderRVA = eate.ExportRVA;
|
||||
|
||||
int forwarderAddress = (int)PortableExecutable.ConvertVirtualAddress(eate.ForwarderRVA, sections);
|
||||
if (forwarderAddress > -1 && forwarderAddress < content.Length)
|
||||
eate.Forwarder = content.ReadString(ref forwarderAddress, Encoding.ASCII);
|
||||
|
||||
return eate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Entries
|
||||
{
|
||||
/// <summary>
|
||||
/// Each entry in the export address table is a field that uses one of two formats in the following table.
|
||||
/// If the address specified is not within the export section (as defined by the address and length that are indicated in the optional header), the field is an export RVA, which is an actual address in code or data.
|
||||
/// Otherwise, the field is a forwarder RVA, which names a symbol in another DLL.
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#the-pdata-section</remarks>
|
||||
public class FunctionTableEntry
|
||||
{
|
||||
#region 32-bit MIPS
|
||||
|
||||
/// <summary>
|
||||
/// The VA of the corresponding function.
|
||||
/// </summary>
|
||||
public uint MIPSBeginAddress;
|
||||
|
||||
/// <summary>
|
||||
/// The VA of the end of the function.
|
||||
/// </summary>
|
||||
public uint MIPSEndAddress;
|
||||
|
||||
/// <summary>
|
||||
/// The pointer to the exception handler to be executed.
|
||||
/// </summary>
|
||||
public uint MIPSExceptionHandler;
|
||||
|
||||
/// <summary>
|
||||
/// The pointer to additional information to be passed to the handler.
|
||||
/// </summary>
|
||||
public uint MIPSHandlerData;
|
||||
|
||||
/// <summary>
|
||||
/// The VA of the end of the function's prolog.
|
||||
/// </summary>
|
||||
public uint MIPSPrologEndAddress;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ARM, PowerPC, SH3 and SH4 Windows CE
|
||||
|
||||
/// <summary>
|
||||
/// The VA of the corresponding function.
|
||||
/// </summary>
|
||||
public uint ARMBeginAddress;
|
||||
|
||||
/// <summary>
|
||||
/// The VA of the end of the function.
|
||||
///
|
||||
/// 8 bits Prolog Length The number of instructions in the function's prolog.
|
||||
/// 22 bits Function Length The number of instructions in the function.
|
||||
/// 1 bit 32-bit Flag If set, the function consists of 32-bit instructions. If clear, the function consists of 16-bit instructions.
|
||||
/// 1 bit Exception Flag If set, an exception handler exists for the function. Otherwise, no exception handler exists.
|
||||
/// </summary>
|
||||
public uint ARMLengthsAndFlags;
|
||||
|
||||
#endregion
|
||||
|
||||
#region x64 and Itanium
|
||||
|
||||
/// <summary>
|
||||
/// The RVA of the corresponding function.
|
||||
/// </summary>
|
||||
public uint X64BeginAddress;
|
||||
|
||||
/// <summary>
|
||||
/// The RVA of the end of the function.
|
||||
/// </summary>
|
||||
public uint X64EndAddress;
|
||||
|
||||
/// <summary>
|
||||
/// The RVA of the unwind information.
|
||||
/// </summary>
|
||||
public uint X64UnwindInformation;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Entries
|
||||
{
|
||||
/// <summary>
|
||||
/// Each entry in the hint/name table has the following format
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#hintname-table</remarks>
|
||||
public class HintNameTableEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// An index into the export name pointer table.
|
||||
/// A match is attempted first with this value.
|
||||
/// If it fails, a binary search is performed on the DLL's export name pointer table.
|
||||
/// </summary>
|
||||
public ushort Hint;
|
||||
|
||||
/// <summary>
|
||||
/// An ASCII string that contains the name to import.
|
||||
/// This is the string that must be matched to the public name in the DLL.
|
||||
/// This string is case sensitive and terminated by a null byte.
|
||||
/// </summary>
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// A trailing zero-pad byte that appears after the trailing null byte, if necessary, to align the next entry on an even boundary.
|
||||
/// </summary>
|
||||
public byte Pad;
|
||||
|
||||
public static HintNameTableEntry Deserialize(Stream stream)
|
||||
{
|
||||
var hnte = new HintNameTableEntry();
|
||||
|
||||
hnte.Hint = stream.ReadUInt16();
|
||||
hnte.Name = string.Empty;
|
||||
while (true)
|
||||
{
|
||||
char c = stream.ReadChar();
|
||||
if (c == (char)0x00)
|
||||
break;
|
||||
|
||||
hnte.Name += c;
|
||||
}
|
||||
|
||||
// If the name length is not even, read and pad
|
||||
if (hnte.Name.Length % 2 != 0)
|
||||
{
|
||||
stream.ReadByte();
|
||||
hnte.Pad = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
hnte.Pad = 0;
|
||||
}
|
||||
|
||||
return hnte;
|
||||
}
|
||||
|
||||
public static HintNameTableEntry Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var hnte = new HintNameTableEntry();
|
||||
|
||||
hnte.Hint = content.ReadUInt16(ref offset);
|
||||
hnte.Name = string.Empty;
|
||||
while (true)
|
||||
{
|
||||
char c = (char)content[offset]; offset += 1;
|
||||
if (c == (char)0x00)
|
||||
break;
|
||||
|
||||
hnte.Name += c;
|
||||
}
|
||||
|
||||
// If the name length is not even, read and pad
|
||||
if (hnte.Name.Length % 2 != 0)
|
||||
{
|
||||
offset += 1;
|
||||
hnte.Pad = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
hnte.Pad = 0;
|
||||
}
|
||||
|
||||
return hnte;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Entries
|
||||
{
|
||||
/// <summary>
|
||||
/// Each import address entry has the following format
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-address-table</remarks>
|
||||
public class ImportAddressTableEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The RVA of the import lookup table.
|
||||
/// This table contains a name or ordinal for each import.
|
||||
/// (The name "Characteristics" is used in Winnt.h, but no longer describes this field.)
|
||||
/// </summary>
|
||||
public uint ImportLookupTableRVA;
|
||||
|
||||
/// <summary>
|
||||
/// The stamp that is set to zero until the image is bound.
|
||||
/// After the image is bound, this field is set to the time/data stamp of the DLL.
|
||||
/// </summary>
|
||||
public uint TimeDateStamp;
|
||||
|
||||
/// <summary>
|
||||
/// The index of the first forwarder reference.
|
||||
/// </summary>
|
||||
public uint ForwarderChain;
|
||||
|
||||
/// <summary>
|
||||
/// The address of an ASCII string that contains the name of the DLL.
|
||||
/// This address is relative to the image base.
|
||||
/// </summary>
|
||||
public uint NameRVA;
|
||||
|
||||
/// <summary>
|
||||
/// The RVA of the import address table.
|
||||
/// The contents of this table are identical to the contents of the import lookup table until the image is bound.
|
||||
/// </summary>
|
||||
public uint ImportAddressTableRVA;
|
||||
|
||||
/// <summary>
|
||||
/// Determine if the entry is null or not
|
||||
/// This indicates the last entry in a table
|
||||
/// </summary>
|
||||
public bool IsNull()
|
||||
{
|
||||
return ImportLookupTableRVA == 0
|
||||
&& TimeDateStamp == 0
|
||||
&& ForwarderChain == 0
|
||||
&& NameRVA == 0
|
||||
&& ImportAddressTableRVA == 0;
|
||||
}
|
||||
|
||||
public static ImportAddressTableEntry Deserialize(Stream stream)
|
||||
{
|
||||
var iate = new ImportAddressTableEntry();
|
||||
|
||||
iate.ImportLookupTableRVA = stream.ReadUInt32();
|
||||
iate.TimeDateStamp = stream.ReadUInt32();
|
||||
iate.ForwarderChain = stream.ReadUInt32();
|
||||
iate.NameRVA = stream.ReadUInt32();
|
||||
iate.ImportAddressTableRVA = stream.ReadUInt32();
|
||||
|
||||
return iate;
|
||||
}
|
||||
|
||||
public static ImportAddressTableEntry Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var iate = new ImportAddressTableEntry();
|
||||
|
||||
iate.ImportLookupTableRVA = content.ReadUInt32(ref offset);
|
||||
iate.TimeDateStamp = content.ReadUInt32(ref offset);
|
||||
iate.ForwarderChain = content.ReadUInt32(ref offset);
|
||||
iate.NameRVA = content.ReadUInt32(ref offset);
|
||||
iate.ImportAddressTableRVA = content.ReadUInt32(ref offset);
|
||||
|
||||
return iate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Entries
|
||||
{
|
||||
/// <summary>
|
||||
/// Each import directory entry has the following format
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-directory-table</remarks>
|
||||
public class ImportDirectoryTableEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The RVA of the import lookup table.
|
||||
/// This table contains a name or ordinal for each import.
|
||||
/// (The name "Characteristics" is used in Winnt.h, but no longer describes this field.)
|
||||
/// </summary>
|
||||
public uint ImportLookupTableRVA;
|
||||
|
||||
/// <summary>
|
||||
/// The stamp that is set to zero until the image is bound.
|
||||
/// After the image is bound, this field is set to the time/data stamp of the DLL.
|
||||
/// </summary>
|
||||
public uint TimeDateStamp;
|
||||
|
||||
/// <summary>
|
||||
/// The index of the first forwarder reference.
|
||||
/// </summary>
|
||||
public uint ForwarderChain;
|
||||
|
||||
/// <summary>
|
||||
/// The address of an ASCII string that contains the name of the DLL.
|
||||
/// This address is relative to the image base.
|
||||
/// </summary>
|
||||
public uint NameRVA;
|
||||
|
||||
/// <summary>
|
||||
/// The RVA of the import address table.
|
||||
/// The contents of this table are identical to the contents of the import lookup table until the image is bound.
|
||||
/// </summary>
|
||||
public uint ImportAddressTableRVA;
|
||||
|
||||
/// <summary>
|
||||
/// Determine if the entry is null or not
|
||||
/// This indicates the last entry in a table
|
||||
/// </summary>
|
||||
public bool IsNull()
|
||||
{
|
||||
return ImportLookupTableRVA == 0
|
||||
&& TimeDateStamp == 0
|
||||
&& ForwarderChain == 0
|
||||
&& NameRVA == 0
|
||||
&& ImportAddressTableRVA == 0;
|
||||
}
|
||||
|
||||
public static ImportDirectoryTableEntry Deserialize(Stream stream)
|
||||
{
|
||||
var idte = new ImportDirectoryTableEntry();
|
||||
|
||||
idte.ImportLookupTableRVA = stream.ReadUInt32();
|
||||
idte.TimeDateStamp = stream.ReadUInt32();
|
||||
idte.ForwarderChain = stream.ReadUInt32();
|
||||
idte.NameRVA = stream.ReadUInt32();
|
||||
idte.ImportAddressTableRVA = stream.ReadUInt32();
|
||||
|
||||
return idte;
|
||||
}
|
||||
|
||||
public static ImportDirectoryTableEntry Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var idte = new ImportDirectoryTableEntry();
|
||||
|
||||
idte.ImportLookupTableRVA = content.ReadUInt32(ref offset);
|
||||
idte.TimeDateStamp = content.ReadUInt32(ref offset);
|
||||
idte.ForwarderChain = content.ReadUInt32(ref offset);
|
||||
idte.NameRVA = content.ReadUInt32(ref offset);
|
||||
idte.ImportAddressTableRVA = content.ReadUInt32(ref offset);
|
||||
|
||||
return idte;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Entries
|
||||
{
|
||||
/// <summary>
|
||||
/// Resource type and name strings
|
||||
/// </summary>
|
||||
public class NEResourceNameString
|
||||
{
|
||||
/// <summary>
|
||||
/// Length of the type or name string that follows. A zero value
|
||||
/// indicates the end of the resource type and name string, also
|
||||
/// the end of the resource table.
|
||||
/// </summary>
|
||||
public byte Length;
|
||||
|
||||
/// <summary>
|
||||
/// ASCII text of the type or name string.
|
||||
/// </summary>
|
||||
public char[] Value;
|
||||
|
||||
public static NEResourceNameString Deserialize(Stream stream)
|
||||
{
|
||||
var rds = new NEResourceNameString();
|
||||
|
||||
rds.Length = stream.ReadByteValue();
|
||||
rds.Value = stream.ReadChars(rds.Length, Encoding.ASCII);
|
||||
|
||||
return rds;
|
||||
}
|
||||
|
||||
public static NEResourceNameString Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var rds = new NEResourceNameString();
|
||||
|
||||
rds.Length = content.ReadByte(ref offset);
|
||||
rds.Value = Encoding.ASCII.GetChars(content, offset, rds.Length); offset += rds.Length;
|
||||
|
||||
return rds;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Entries
|
||||
{
|
||||
/// <summary>
|
||||
/// A table of resources for this type
|
||||
/// </summary>
|
||||
public class NEResourceTableEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// File offset to the contents of the resource data,
|
||||
/// relative to beginning of file. The offset is in terms
|
||||
/// of the alignment shift count value specified at
|
||||
/// beginning of the resource table.
|
||||
/// </summary>
|
||||
public ushort Offset;
|
||||
|
||||
/// <summary>
|
||||
/// Length of the resource in the file (in bytes).
|
||||
/// </summary>
|
||||
public ushort Length;
|
||||
|
||||
/// <summary>
|
||||
/// Resource flags
|
||||
/// </summary>
|
||||
public ResourceTableEntryFlags Flags;
|
||||
|
||||
/// <summary>
|
||||
/// This is an integer type if the high-order
|
||||
/// bit is set (8000h), otherwise it is the offset to the
|
||||
/// resource string, the offset is relative to the
|
||||
/// beginning of the resource table.
|
||||
/// </summary>
|
||||
public ushort ResourceID;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved.
|
||||
/// </summary>
|
||||
public ushort Handle;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved.
|
||||
/// </summary>
|
||||
public ushort Usage;
|
||||
|
||||
public static NEResourceTableEntry Deserialize(Stream stream)
|
||||
{
|
||||
var ni = new NEResourceTableEntry();
|
||||
|
||||
ni.Offset = stream.ReadUInt16();
|
||||
ni.Length = stream.ReadUInt16();
|
||||
ni.Flags = (ResourceTableEntryFlags)stream.ReadUInt16();
|
||||
ni.ResourceID = stream.ReadUInt16();
|
||||
ni.Handle = stream.ReadUInt16();
|
||||
ni.Usage = stream.ReadUInt16();
|
||||
|
||||
return ni;
|
||||
}
|
||||
|
||||
public static NEResourceTableEntry Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var ni = new NEResourceTableEntry();
|
||||
|
||||
ni.Offset = content.ReadUInt16(ref offset);
|
||||
ni.Length = content.ReadUInt16(ref offset);
|
||||
ni.Flags = (ResourceTableEntryFlags)content.ReadUInt16(ref offset);
|
||||
ni.ResourceID = content.ReadUInt16(ref offset);
|
||||
ni.Handle = content.ReadUInt16(ref offset);
|
||||
ni.Usage = content.ReadUInt16(ref offset);
|
||||
|
||||
return ni;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Entries
|
||||
{
|
||||
/// <summary>
|
||||
/// The segment table contains an entry for each segment in the executable
|
||||
/// file. The number of segment table entries are defined in the segmented
|
||||
/// EXE header. The first entry in the segment table is segment number 1.
|
||||
/// The following is the structure of a segment table entry.
|
||||
/// </summary>
|
||||
public class NESegmentTableEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Logical-sector offset (n byte) to the contents of the segment
|
||||
/// data, relative to the beginning of the file. Zero means no
|
||||
/// file data.
|
||||
/// </summary>
|
||||
public ushort StartFileSector;
|
||||
|
||||
/// <summary>
|
||||
/// Length of the segment in the file, in bytes. Zero means 64K.
|
||||
/// </summary>
|
||||
public ushort BytesInFile;
|
||||
|
||||
/// <summary>
|
||||
/// Attribute flags
|
||||
/// </summary>
|
||||
public SegmentTableEntryFlags Flags;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum allocation size of the segment, in bytes.
|
||||
/// Total size of the segment. Zero means 64K
|
||||
/// </summary>
|
||||
public ushort MinimumAllocation;
|
||||
|
||||
public static NESegmentTableEntry Deserialize(Stream stream)
|
||||
{
|
||||
var nste = new NESegmentTableEntry();
|
||||
|
||||
nste.StartFileSector = stream.ReadUInt16();
|
||||
nste.BytesInFile = stream.ReadUInt16();
|
||||
nste.Flags = (SegmentTableEntryFlags)stream.ReadUInt16();
|
||||
nste.MinimumAllocation = stream.ReadUInt16();
|
||||
|
||||
return nste;
|
||||
}
|
||||
|
||||
public static NESegmentTableEntry Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var nste = new NESegmentTableEntry();
|
||||
|
||||
nste.StartFileSector = content.ReadUInt16(ref offset);
|
||||
nste.BytesInFile = content.ReadUInt16(ref offset);
|
||||
nste.Flags = (SegmentTableEntryFlags)content.ReadUInt16(ref offset);
|
||||
nste.MinimumAllocation = content.ReadUInt16(ref offset);
|
||||
|
||||
return nste;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Headers;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Entries
|
||||
{
|
||||
/// <summary>
|
||||
/// Each Resource Data entry describes an actual unit of raw data in the Resource Data area.
|
||||
/// </summary>
|
||||
public class ResourceDataEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The address of a unit of resource data in the Resource Data area.
|
||||
/// </summary>
|
||||
public uint OffsetToData;
|
||||
|
||||
/// <summary>
|
||||
/// A unit of resource data in the Resource Data area.
|
||||
/// </summary>
|
||||
public byte[] Data;
|
||||
|
||||
/// <summary>
|
||||
/// A unit of resource data in the Resource Data area.
|
||||
/// </summary>
|
||||
public string DataAsUTF8String
|
||||
{
|
||||
get
|
||||
{
|
||||
int codePage = (int)CodePage;
|
||||
if (Data == null || codePage < 0)
|
||||
return string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
var originalEncoding = Encoding.GetEncoding(codePage);
|
||||
byte[] convertedData = Encoding.Convert(originalEncoding, Encoding.UTF8, Data);
|
||||
return Encoding.UTF8.GetString(convertedData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Encoding.ASCII.GetString(Data);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The size, in bytes, of the resource data that is pointed to by the Data RVA field.
|
||||
/// </summary>
|
||||
public uint Size;
|
||||
|
||||
/// <summary>
|
||||
/// The code page that is used to decode code point values within the resource data.
|
||||
/// Typically, the code page would be the Unicode code page.
|
||||
/// </summary>
|
||||
public uint CodePage;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved, must be 0.
|
||||
/// </summary>
|
||||
public uint Reserved;
|
||||
|
||||
public static ResourceDataEntry Deserialize(Stream stream, SectionHeader[] sections)
|
||||
{
|
||||
var rde = new ResourceDataEntry();
|
||||
|
||||
rde.OffsetToData = stream.ReadUInt32();
|
||||
rde.Size = stream.ReadUInt32();
|
||||
rde.CodePage = stream.ReadUInt32();
|
||||
rde.Reserved = stream.ReadUInt32();
|
||||
|
||||
int realOffsetToData = (int)PortableExecutable.ConvertVirtualAddress(rde.OffsetToData, sections);
|
||||
if (realOffsetToData > -1 && realOffsetToData < stream.Length && (int)rde.Size > 0 && realOffsetToData + (int)rde.Size < stream.Length)
|
||||
{
|
||||
long lastPosition = stream.Position;
|
||||
stream.Seek(realOffsetToData, SeekOrigin.Begin);
|
||||
rde.Data = stream.ReadBytes((int)rde.Size);
|
||||
stream.Seek(lastPosition, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
return rde;
|
||||
}
|
||||
|
||||
public static ResourceDataEntry Deserialize(byte[] content, ref int offset, SectionHeader[] sections)
|
||||
{
|
||||
var rde = new ResourceDataEntry();
|
||||
|
||||
rde.OffsetToData = content.ReadUInt32(ref offset);
|
||||
rde.Size = content.ReadUInt32(ref offset);
|
||||
rde.CodePage = content.ReadUInt32(ref offset);
|
||||
rde.Reserved = content.ReadUInt32(ref offset);
|
||||
|
||||
int realOffsetToData = (int)PortableExecutable.ConvertVirtualAddress(rde.OffsetToData, sections);
|
||||
if (realOffsetToData > -1 && realOffsetToData < content.Length && (int)rde.Size > 0 && realOffsetToData + (int)rde.Size < content.Length)
|
||||
rde.Data = new ArraySegment<byte>(content, realOffsetToData, (int)rde.Size).ToArray();
|
||||
|
||||
return rde;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Entries
|
||||
{
|
||||
/// <summary>
|
||||
/// The resource directory string area consists of Unicode strings, which are word-aligned.
|
||||
/// These strings are stored together after the last Resource Directory entry and before the first Resource Data entry.
|
||||
/// This minimizes the impact of these variable-length strings on the alignment of the fixed-size directory entries.
|
||||
/// </summary>
|
||||
public class ResourceDirectoryString
|
||||
{
|
||||
/// <summary>
|
||||
/// The size of the string, not including length field itself.
|
||||
/// </summary>
|
||||
public ushort Length;
|
||||
|
||||
/// <summary>
|
||||
/// The variable-length Unicode string data, word-aligned.
|
||||
/// </summary>
|
||||
public string UnicodeString;
|
||||
|
||||
public static ResourceDirectoryString Deserialize(Stream stream)
|
||||
{
|
||||
var rds = new ResourceDirectoryString();
|
||||
|
||||
rds.Length = stream.ReadUInt16();
|
||||
if (rds.Length + stream.Position > stream.Length)
|
||||
return null;
|
||||
|
||||
rds.UnicodeString = new string(stream.ReadChars(rds.Length, Encoding.Unicode));
|
||||
|
||||
return rds;
|
||||
}
|
||||
|
||||
public static ResourceDirectoryString Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var rds = new ResourceDirectoryString();
|
||||
|
||||
rds.Length = content.ReadUInt16(ref offset);
|
||||
if (rds.Length + offset > content.Length)
|
||||
return null;
|
||||
|
||||
rds.UnicodeString = Encoding.Unicode.GetString(content, offset, rds.Length); offset += rds.Length;
|
||||
|
||||
return rds;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Headers;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Tables;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Entries
|
||||
{
|
||||
/// <summary>
|
||||
/// The directory entries make up the rows of a table.
|
||||
/// Each resource directory entry has the following format.
|
||||
/// Whether the entry is a Name or ID entry is indicated by the
|
||||
/// resource directory table, which indicates how many Name and
|
||||
/// ID entries follow it (remember that all the Name entries
|
||||
/// precede all the ID entries for the table). All entries for
|
||||
/// the table are sorted in ascending order: the Name entries
|
||||
/// by case-sensitive string and the ID entries by numeric value.
|
||||
/// Offsets are relative to the address in the IMAGE_DIRECTORY_ENTRY_RESOURCE DataDirectory.
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#resource-directory-entries</remarks>
|
||||
public class ResourceDirectoryTableEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The offset of a string that gives the Type, Name, or Language ID entry, depending on level of table.
|
||||
/// </summary>
|
||||
public uint NameOffset => (uint)(IntegerId ^ (1 << 31));
|
||||
|
||||
/// <summary>
|
||||
/// The string that gives the Type, Name, or Language ID entry, depending on level of table pointed to by NameOffset
|
||||
/// </summary>
|
||||
public ResourceDirectoryString Name;
|
||||
|
||||
/// <summary>
|
||||
/// A 32-bit integer that identifies the Type, Name, or Language ID entry.
|
||||
/// </summary>
|
||||
public uint IntegerId;
|
||||
|
||||
/// <summary>
|
||||
/// High bit 0. Address of a Resource Data entry (a leaf).
|
||||
/// </summary>
|
||||
public uint DataEntryOffset;
|
||||
|
||||
/// <summary>
|
||||
/// High bit 1. The lower 31 bits are the address of another resource directory table (the next level down).
|
||||
/// </summary>
|
||||
public uint SubdirectoryOffset => (uint)(DataEntryOffset ^ (1 << 31));
|
||||
|
||||
/// <summary>
|
||||
/// Resource Data entry (a leaf).
|
||||
/// </summary>
|
||||
public ResourceDataEntry DataEntry;
|
||||
|
||||
/// <summary>
|
||||
/// Another resource directory table (the next level down).
|
||||
/// </summary>
|
||||
public ResourceDirectoryTable Subdirectory;
|
||||
|
||||
/// <summary>
|
||||
/// Determine if an entry has a name or integer identifier
|
||||
/// </summary>
|
||||
public bool IsIntegerIDEntry() => (IntegerId & (1 << 31)) == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Determine if an entry represents a leaf or another directory table
|
||||
/// </summary>
|
||||
public bool IsResourceDataEntry() => (DataEntryOffset & (1 << 31)) == 0;
|
||||
|
||||
public static ResourceDirectoryTableEntry Deserialize(Stream stream, long sectionStart, SectionHeader[] sections)
|
||||
{
|
||||
var rdte = new ResourceDirectoryTableEntry();
|
||||
|
||||
rdte.IntegerId = stream.ReadUInt32();
|
||||
if (!rdte.IsIntegerIDEntry())
|
||||
{
|
||||
int nameAddress = (int)(rdte.NameOffset + sectionStart);
|
||||
if (nameAddress >= 0 && nameAddress < stream.Length)
|
||||
{
|
||||
long lastPosition = stream.Position;
|
||||
stream.Seek(nameAddress, SeekOrigin.Begin);
|
||||
rdte.Name = ResourceDirectoryString.Deserialize(stream);
|
||||
stream.Seek(lastPosition, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
rdte.DataEntryOffset = stream.ReadUInt32();
|
||||
if (rdte.IsResourceDataEntry())
|
||||
{
|
||||
int dataEntryAddress = (int)(rdte.DataEntryOffset + sectionStart);
|
||||
if (dataEntryAddress > 0 && dataEntryAddress < stream.Length)
|
||||
{
|
||||
long lastPosition = stream.Position;
|
||||
stream.Seek(dataEntryAddress, SeekOrigin.Begin);
|
||||
rdte.DataEntry = ResourceDataEntry.Deserialize(stream, sections);
|
||||
stream.Seek(lastPosition, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int subdirectoryAddress = (int)(rdte.SubdirectoryOffset + sectionStart);
|
||||
if (subdirectoryAddress > 0 && subdirectoryAddress < stream.Length)
|
||||
{
|
||||
long lastPosition = stream.Position;
|
||||
stream.Seek(subdirectoryAddress, SeekOrigin.Begin);
|
||||
rdte.Subdirectory = ResourceDirectoryTable.Deserialize(stream, sectionStart, sections);
|
||||
stream.Seek(lastPosition, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
return rdte;
|
||||
}
|
||||
|
||||
public static ResourceDirectoryTableEntry Deserialize(byte[] content, ref int offset, long sectionStart, SectionHeader[] sections)
|
||||
{
|
||||
var rdte = new ResourceDirectoryTableEntry();
|
||||
|
||||
rdte.IntegerId = content.ReadUInt32(ref offset);
|
||||
if (!rdte.IsIntegerIDEntry())
|
||||
{
|
||||
int nameAddress = (int)(rdte.NameOffset + sectionStart);
|
||||
if (nameAddress >= 0 && nameAddress < content.Length)
|
||||
rdte.Name = ResourceDirectoryString.Deserialize(content, ref nameAddress);
|
||||
}
|
||||
|
||||
rdte.DataEntryOffset = content.ReadUInt32(ref offset);
|
||||
if (rdte.IsResourceDataEntry())
|
||||
{
|
||||
int dataEntryAddress = (int)(rdte.DataEntryOffset + sectionStart);
|
||||
if (dataEntryAddress > 0 && dataEntryAddress < content.Length)
|
||||
rdte.DataEntry = ResourceDataEntry.Deserialize(content, ref dataEntryAddress, sections);
|
||||
}
|
||||
else
|
||||
{
|
||||
int subdirectoryAddress = (int)(rdte.SubdirectoryOffset + sectionStart);
|
||||
if (subdirectoryAddress > 0 && subdirectoryAddress < content.Length)
|
||||
rdte.Subdirectory = ResourceDirectoryTable.Deserialize(content, ref subdirectoryAddress, sectionStart, sections);
|
||||
}
|
||||
|
||||
return rdte;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Entries
|
||||
{
|
||||
/// <summary>
|
||||
/// Resource type information block
|
||||
/// </summary>
|
||||
public class ResourceTypeInformationBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// Type ID. This is an integer type if the high-order bit is
|
||||
/// set (8000h); otherwise, it is an offset to the type string,
|
||||
/// the offset is relative to the beginning of the resource
|
||||
/// table. A zero type ID marks the end of the resource type
|
||||
/// information blocks.
|
||||
/// </summary>
|
||||
public ushort TypeID;
|
||||
|
||||
/// <summary>
|
||||
/// Number of resources for this type.
|
||||
/// </summary>
|
||||
public ushort ResourceCount;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved.
|
||||
/// </summary>
|
||||
public uint Reserved;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved.
|
||||
/// </summary>
|
||||
public NEResourceTableEntry[] ResourceTable;
|
||||
|
||||
public static ResourceTypeInformationBlock Deserialize(Stream stream)
|
||||
{
|
||||
var rtib = new ResourceTypeInformationBlock();
|
||||
|
||||
rtib.TypeID = stream.ReadUInt16();
|
||||
rtib.ResourceCount = stream.ReadUInt16();
|
||||
rtib.Reserved = stream.ReadUInt32();
|
||||
|
||||
rtib.ResourceTable = new NEResourceTableEntry[rtib.ResourceCount];
|
||||
for (int i = 0; i < rtib.ResourceCount; i++)
|
||||
{
|
||||
rtib.ResourceTable[i] = NEResourceTableEntry.Deserialize(stream);
|
||||
}
|
||||
|
||||
return rtib;
|
||||
}
|
||||
|
||||
public static ResourceTypeInformationBlock Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var rtib = new ResourceTypeInformationBlock();
|
||||
|
||||
rtib.TypeID = content.ReadUInt16(ref offset);
|
||||
rtib.ResourceCount = content.ReadUInt16(ref offset);
|
||||
rtib.Reserved = content.ReadUInt32(ref offset);
|
||||
|
||||
rtib.ResourceTable = new NEResourceTableEntry[rtib.ResourceCount];
|
||||
for (int i = 0; i < rtib.ResourceCount; i++)
|
||||
{
|
||||
rtib.ResourceTable[i] = NEResourceTableEntry.Deserialize(content, ref offset);
|
||||
}
|
||||
|
||||
return rtib;
|
||||
}
|
||||
}
|
||||
}
|
||||
1112
BurnOutSharp/ExecutableType/Microsoft/Enums.cs
Normal file
1112
BurnOutSharp/ExecutableType/Microsoft/Enums.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Headers
|
||||
{
|
||||
public class CommonObjectFileFormatHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// After the MS-DOS stub, at the file offset specified at offset 0x3c, is a 4-byte signature that identifies the file as a PE format image file.
|
||||
// This signature is "PE\0\0" (the letters "P" and "E" followed by two null bytes).
|
||||
/// </summary>
|
||||
public uint Signature;
|
||||
|
||||
/// <summary>
|
||||
/// The number that identifies the type of target machine.
|
||||
/// </summary>
|
||||
public MachineType Machine;
|
||||
|
||||
/// <summary>
|
||||
/// The number of sections.
|
||||
/// This indicates the size of the section table, which immediately follows the headers.
|
||||
/// </summary>
|
||||
public ushort NumberOfSections;
|
||||
|
||||
/// <summary>
|
||||
/// The low 32 bits of the number of seconds since 00:00 January 1, 1970 (a C run-time time_t value), which indicates when the file was created.
|
||||
/// </summary>
|
||||
public uint TimeDateStamp;
|
||||
|
||||
/// <summary>
|
||||
/// The file offset of the COFF symbol table, or zero if no COFF symbol table is present.
|
||||
/// This value should be zero for an image because COFF debugging information is deprecated.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public uint PointerToSymbolTable;
|
||||
|
||||
/// <summary>
|
||||
/// The number of entries in the symbol table. This data can be used to locate the string table, which immediately follows the symbol table.
|
||||
/// This value should be zero for an image because COFF debugging information is deprecated.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public uint NumberOfSymbols;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the optional header, which is required for executable files but not for object files.
|
||||
// This value should be zero for an object file.
|
||||
/// </summary>
|
||||
public ushort SizeOfOptionalHeader;
|
||||
|
||||
/// <summary>
|
||||
/// The flags that indicate the attributes of the file.
|
||||
/// </summary>
|
||||
public ImageObjectCharacteristics Characteristics;
|
||||
|
||||
public static CommonObjectFileFormatHeader Deserialize(Stream stream)
|
||||
{
|
||||
var ifh = new CommonObjectFileFormatHeader();
|
||||
|
||||
ifh.Signature = stream.ReadUInt32();
|
||||
ifh.Machine = (MachineType)stream.ReadUInt16();
|
||||
ifh.NumberOfSections = stream.ReadUInt16();
|
||||
ifh.TimeDateStamp = stream.ReadUInt32();
|
||||
ifh.PointerToSymbolTable = stream.ReadUInt32();
|
||||
ifh.NumberOfSymbols = stream.ReadUInt32();
|
||||
ifh.SizeOfOptionalHeader = stream.ReadUInt16();
|
||||
ifh.Characteristics = (ImageObjectCharacteristics)stream.ReadUInt16();
|
||||
|
||||
return ifh;
|
||||
}
|
||||
|
||||
public static CommonObjectFileFormatHeader Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var ifh = new CommonObjectFileFormatHeader();
|
||||
|
||||
ifh.Signature = content.ReadUInt32(ref offset);
|
||||
ifh.Machine = (MachineType)content.ReadUInt16(ref offset);
|
||||
ifh.NumberOfSections = content.ReadUInt16(ref offset);
|
||||
ifh.TimeDateStamp = content.ReadUInt32(ref offset);
|
||||
ifh.PointerToSymbolTable = content.ReadUInt32(ref offset);
|
||||
ifh.NumberOfSymbols = content.ReadUInt32(ref offset);
|
||||
ifh.SizeOfOptionalHeader = content.ReadUInt16(ref offset);
|
||||
ifh.Characteristics = (ImageObjectCharacteristics)content.ReadUInt16(ref offset);
|
||||
|
||||
return ifh;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Headers
|
||||
{
|
||||
public class DataDirectoryHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// The first field, VirtualAddress, is actually the RVA of the table.
|
||||
/// The RVA is the address of the table relative to the base address of the image when the table is loaded.
|
||||
/// </summary>
|
||||
public uint VirtualAddress;
|
||||
|
||||
/// <summary>
|
||||
/// The second field gives the size in bytes.
|
||||
/// </summary>
|
||||
public uint Size;
|
||||
|
||||
public static DataDirectoryHeader Deserialize(Stream stream)
|
||||
{
|
||||
var ddh = new DataDirectoryHeader();
|
||||
|
||||
ddh.VirtualAddress = stream.ReadUInt32();
|
||||
ddh.Size = stream.ReadUInt32();
|
||||
|
||||
return ddh;
|
||||
}
|
||||
|
||||
public static DataDirectoryHeader Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var ddh = new DataDirectoryHeader();
|
||||
|
||||
ddh.VirtualAddress = content.ReadUInt32(ref offset);
|
||||
ddh.Size = content.ReadUInt32(ref offset);
|
||||
|
||||
return ddh;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Headers
|
||||
{
|
||||
/// <summary>
|
||||
/// The MS-DOS EXE format, also known as MZ after its signature (the initials of Microsoft engineer Mark Zbykowski),
|
||||
/// was introduced with MS-DOS 2.0 (version 1.0 only sported the simple COM format). It is designed as a relocatable
|
||||
/// executable running under real mode. As such, only DOS and Windows 9x can use this format natively, but there are
|
||||
/// several free DOS emulators (e.g., DOSBox) that support it and that run under various operating systems (e.g.,
|
||||
/// Linux, Amiga, Windows NT, etc.). Although they can exist on their own, MZ executables are embedded in all NE, LE,
|
||||
/// and PE executables, usually as stubs so that when they are ran under DOS, they display a warning.
|
||||
/// </summary>
|
||||
/// <remarks>https://wiki.osdev.org/MZ</remarks>
|
||||
public class MSDOSExecutableHeader
|
||||
{
|
||||
#region Standard Fields
|
||||
|
||||
/// <summary>
|
||||
/// 0x5A4D (ASCII for 'M' and 'Z') [00]
|
||||
/// </summary>
|
||||
public ushort Magic;
|
||||
|
||||
/// <summary>
|
||||
/// Number of bytes in the last page. [02]
|
||||
/// </summary>
|
||||
public ushort LastPageBytes;
|
||||
|
||||
/// <summary>
|
||||
/// Number of whole/partial pages. [04]
|
||||
/// </summary>
|
||||
public ushort Pages;
|
||||
|
||||
/// <summary>
|
||||
/// Number of entries in the relocation table. [06]
|
||||
/// </summary>
|
||||
public ushort Relocations;
|
||||
|
||||
/// <summary>
|
||||
/// The number of paragraphs taken up by the header.It can be any value, as the loader
|
||||
/// just uses it to find where the actual executable data starts. It may be larger than
|
||||
/// what the "standard" fields take up, and you may use it if you want to include your
|
||||
/// own header metadata, or put the relocation table there, or use it for any other purpose. [08]
|
||||
/// </summary>
|
||||
public ushort HeaderParagraphSize;
|
||||
|
||||
/// <summary>
|
||||
/// The number of paragraphs required by the program, excluding the PSP and program image.
|
||||
/// If no free block is big enough, the loading stops. [0A]
|
||||
/// </summary>
|
||||
public ushort MinimumExtraParagraphs;
|
||||
|
||||
/// <summary>
|
||||
/// The number of paragraphs requested by the program.
|
||||
/// If no free block is big enough, the biggest one possible is allocated. [0C]
|
||||
/// </summary>
|
||||
public ushort MaximumExtraParagraphs;
|
||||
|
||||
/// <summary>
|
||||
/// Relocatable segment address for SS. [0E]
|
||||
/// </summary>
|
||||
public ushort InitialSSValue;
|
||||
|
||||
/// <summary>
|
||||
/// Initial value for SP. [10]
|
||||
/// </summary>
|
||||
public ushort InitialSPValue;
|
||||
|
||||
/// <summary>
|
||||
/// When added to the sum of all other words in the file, the result should be zero. [12]
|
||||
/// </summary>
|
||||
public ushort Checksum;
|
||||
|
||||
/// <summary>
|
||||
/// Initial value for IP. [14]
|
||||
/// </summary>
|
||||
public ushort InitialIPValue;
|
||||
|
||||
/// <summary>
|
||||
/// Relocatable segment address for CS. [16]
|
||||
/// </summary>
|
||||
public ushort InitialCSValue;
|
||||
|
||||
/// <summary>
|
||||
/// The (absolute) offset to the relocation table. [18]
|
||||
/// </summary>
|
||||
public ushort RelocationTableAddr;
|
||||
|
||||
/// <summary>
|
||||
/// Value used for overlay management.
|
||||
/// If zero, this is the main executable. [1A]
|
||||
/// </summary>
|
||||
public ushort OverlayNumber;
|
||||
|
||||
#endregion
|
||||
|
||||
#region PE Extensions
|
||||
|
||||
/// <summary>
|
||||
/// Reserved words [1C]
|
||||
/// </summary>
|
||||
public ushort[] Reserved1;
|
||||
|
||||
/// <summary>
|
||||
/// Defined by name but no other information is given; typically zeroes [24]
|
||||
/// </summary>
|
||||
public ushort OEMIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// Defined by name but no other information is given; typically zeroes [26]
|
||||
/// </summary>
|
||||
public ushort OEMInformation;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved words [28]
|
||||
/// </summary>
|
||||
public ushort[] Reserved2;
|
||||
|
||||
/// <summary>
|
||||
/// Starting address of the PE header [3C]
|
||||
/// </summary>
|
||||
public int NewExeHeaderAddr;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// All data after the last item in the header but before the new EXE header address
|
||||
/// </summary>
|
||||
public byte[] ExecutableData;
|
||||
|
||||
public static MSDOSExecutableHeader Deserialize(Stream stream, bool asStub = true)
|
||||
{
|
||||
MSDOSExecutableHeader idh = new MSDOSExecutableHeader();
|
||||
|
||||
idh.Magic = stream.ReadUInt16();
|
||||
idh.LastPageBytes = stream.ReadUInt16();
|
||||
idh.Pages = stream.ReadUInt16();
|
||||
idh.Relocations = stream.ReadUInt16();
|
||||
idh.HeaderParagraphSize = stream.ReadUInt16();
|
||||
idh.MinimumExtraParagraphs = stream.ReadUInt16();
|
||||
idh.MaximumExtraParagraphs = stream.ReadUInt16();
|
||||
idh.InitialSSValue = stream.ReadUInt16();
|
||||
idh.InitialSPValue = stream.ReadUInt16();
|
||||
idh.Checksum = stream.ReadUInt16();
|
||||
idh.InitialIPValue = stream.ReadUInt16();
|
||||
idh.InitialCSValue = stream.ReadUInt16();
|
||||
idh.RelocationTableAddr = stream.ReadUInt16();
|
||||
idh.OverlayNumber = stream.ReadUInt16();
|
||||
|
||||
// If we're not reading as a stub, return now
|
||||
if (!asStub)
|
||||
return idh;
|
||||
|
||||
idh.Reserved1 = new ushort[Constants.ERES1WDS];
|
||||
for (int i = 0; i < Constants.ERES1WDS; i++)
|
||||
{
|
||||
idh.Reserved1[i] = stream.ReadUInt16();
|
||||
}
|
||||
|
||||
idh.OEMIdentifier = stream.ReadUInt16();
|
||||
idh.OEMInformation = stream.ReadUInt16();
|
||||
idh.Reserved2 = new ushort[Constants.ERES2WDS];
|
||||
for (int i = 0; i < Constants.ERES2WDS; i++)
|
||||
{
|
||||
idh.Reserved2[i] = stream.ReadUInt16();
|
||||
}
|
||||
|
||||
idh.NewExeHeaderAddr = stream.ReadInt32();
|
||||
idh.ExecutableData = stream.ReadBytes(idh.NewExeHeaderAddr - (int)stream.Position);
|
||||
|
||||
return idh;
|
||||
}
|
||||
|
||||
public static MSDOSExecutableHeader Deserialize(byte[] content, ref int offset, bool asStub = true)
|
||||
{
|
||||
MSDOSExecutableHeader idh = new MSDOSExecutableHeader();
|
||||
|
||||
idh.Magic = content.ReadUInt16(ref offset);
|
||||
idh.LastPageBytes = content.ReadUInt16(ref offset);
|
||||
idh.Pages = content.ReadUInt16(ref offset);
|
||||
idh.Relocations = content.ReadUInt16(ref offset);
|
||||
idh.HeaderParagraphSize = content.ReadUInt16(ref offset);
|
||||
idh.MinimumExtraParagraphs = content.ReadUInt16(ref offset);
|
||||
idh.MaximumExtraParagraphs = content.ReadUInt16(ref offset);
|
||||
idh.InitialSSValue = content.ReadUInt16(ref offset);
|
||||
idh.InitialSPValue = content.ReadUInt16(ref offset);
|
||||
idh.Checksum = content.ReadUInt16(ref offset);
|
||||
idh.InitialIPValue = content.ReadUInt16(ref offset);
|
||||
idh.InitialCSValue = content.ReadUInt16(ref offset);
|
||||
idh.RelocationTableAddr = content.ReadUInt16(ref offset);
|
||||
idh.OverlayNumber = content.ReadUInt16(ref offset);
|
||||
|
||||
// If we're not reading as a stub, return now
|
||||
if (!asStub)
|
||||
return idh;
|
||||
|
||||
idh.Reserved1 = new ushort[Constants.ERES1WDS];
|
||||
for (int i = 0; i < Constants.ERES1WDS; i++)
|
||||
{
|
||||
idh.Reserved1[i] = content.ReadUInt16(ref offset);
|
||||
}
|
||||
|
||||
idh.OEMIdentifier = content.ReadUInt16(ref offset);
|
||||
idh.OEMInformation = content.ReadUInt16(ref offset);
|
||||
idh.Reserved2 = new ushort[Constants.ERES2WDS];
|
||||
for (int i = 0; i < Constants.ERES2WDS; i++)
|
||||
{
|
||||
idh.Reserved2[i] = content.ReadUInt16(ref offset);
|
||||
}
|
||||
|
||||
idh.NewExeHeaderAddr = content.ReadInt32(ref offset);
|
||||
idh.ExecutableData = content.ReadBytes(ref offset, idh.NewExeHeaderAddr - offset);
|
||||
|
||||
return idh;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Headers
|
||||
{
|
||||
/// <summary>
|
||||
/// The NE header is a relatively large structure with multiple characteristics.
|
||||
/// Because of the age of the format some items are unclear in meaning.
|
||||
/// </summary>
|
||||
/// <remarks>http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm</remarks>
|
||||
public class NewExecutableHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// Signature word. [00]
|
||||
/// "N" is low-order byte.
|
||||
/// "E" is high-order byte.
|
||||
/// </summary>
|
||||
public ushort Magic;
|
||||
|
||||
/// <summary>
|
||||
/// Version number of the linker. [02]
|
||||
/// </summary>
|
||||
public byte LinkerVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Revision number of the linker. [03]
|
||||
/// </summary>
|
||||
public byte LinkerRevision;
|
||||
|
||||
/// <summary>
|
||||
/// Entry Table file offset, relative to the beginning of the segmented EXE header. [04]
|
||||
/// </summary>
|
||||
public ushort EntryTableOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Number of bytes in the entry table. [06]
|
||||
/// </summary>
|
||||
public ushort EntryTableSize;
|
||||
|
||||
/// <summary>
|
||||
/// 32-bit CRC of entire contents of file. [08]
|
||||
/// These words are taken as 00 during the calculation.
|
||||
/// </summary>
|
||||
public uint CrcChecksum;
|
||||
|
||||
/// <summary>
|
||||
/// Program flags, bitmapped [0C]
|
||||
/// </summary>
|
||||
public byte ProgramFlags;
|
||||
|
||||
/// <summary>
|
||||
/// Application flags, bitmapped [0D]
|
||||
/// </summary>
|
||||
public byte ApplicationFlags;
|
||||
|
||||
/// <summary>
|
||||
/// Automatic data segment number [0E]
|
||||
/// </summary>
|
||||
public ushort Autodata;
|
||||
|
||||
/// <summary>
|
||||
/// Initial heap allocation [10]
|
||||
/// </summary>
|
||||
public ushort InitialHeapAlloc;
|
||||
|
||||
/// <summary>
|
||||
/// Initial stack allocation [12]
|
||||
/// </summary>
|
||||
public ushort InitialStackAlloc;
|
||||
|
||||
/// <summary>
|
||||
/// CS:IP entry point, CS is index into segment table [14]
|
||||
/// </summary>
|
||||
public uint InitialCSIPSetting;
|
||||
|
||||
/// <summary>
|
||||
/// SS:SP inital stack pointer, SS is index into segment table [18]
|
||||
/// </summary>
|
||||
public uint InitialSSSPSetting;
|
||||
|
||||
/// <summary>
|
||||
/// Number of segments in segment table [1C]
|
||||
/// </summary>
|
||||
public ushort FileSegmentCount;
|
||||
|
||||
/// <summary>
|
||||
/// Entries in Module Reference Table [1E]
|
||||
/// </summary>
|
||||
public ushort ModuleReferenceTableSize;
|
||||
|
||||
/// <summary>
|
||||
/// Size of non-resident name table [20]
|
||||
/// </summary>
|
||||
public ushort NonResidentNameTableSize;
|
||||
|
||||
/// <summary>
|
||||
/// Offset of Segment Table [22]
|
||||
/// </summary>
|
||||
public ushort SegmentTableOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Offset of Resource Table [24]
|
||||
/// </summary>
|
||||
public ushort ResourceTableOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Offset of resident name table [26]
|
||||
/// </summary>
|
||||
public ushort ResidentNameTableOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Offset of Module Reference Table [28]
|
||||
/// </summary>
|
||||
public ushort ModuleReferenceTableOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Offset of Imported Names Table [2A]
|
||||
/// </summary>
|
||||
public ushort ImportedNamesTableOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Offset of Non-resident Names Table [2C]
|
||||
/// </summary>
|
||||
public uint NonResidentNamesTableOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Count of moveable entry points listed in entry table [30]
|
||||
/// </summary>
|
||||
public ushort MovableEntriesCount;
|
||||
|
||||
/// <summary>
|
||||
/// File allignment size shift count (0-9 (default 512 byte pages)) [32]
|
||||
/// </summary>
|
||||
public ushort SegmentAlignmentShiftCount;
|
||||
|
||||
/// <summary>
|
||||
/// Count of resource table entries [34]
|
||||
/// </summary>
|
||||
public ushort ResourceEntriesCount;
|
||||
|
||||
/// <summary>
|
||||
/// Target operating system [36]
|
||||
/// </summary>
|
||||
public byte TargetOperatingSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Other OS/2 flags [37]
|
||||
/// </summary>
|
||||
public byte AdditionalFlags;
|
||||
|
||||
/// <summary>
|
||||
/// Offset to return thunks or start of gangload area [38]
|
||||
/// </summary>
|
||||
public ushort ReturnThunkOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Offset to segment reference thunks or size of gangload area [3A]
|
||||
/// </summary>
|
||||
public ushort SegmentReferenceThunkOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum code swap area size [3C]
|
||||
/// </summary>
|
||||
public ushort MinCodeSwapAreaSize;
|
||||
|
||||
/// <summary>
|
||||
/// Windows SDK revison number [3E]
|
||||
/// </summary>
|
||||
public byte WindowsSDKRevision;
|
||||
|
||||
/// <summary>
|
||||
/// Windows SDK version number [3F]
|
||||
/// </summary>
|
||||
public byte WindowsSDKVersion;
|
||||
|
||||
public static NewExecutableHeader Deserialize(Stream stream)
|
||||
{
|
||||
var neh = new NewExecutableHeader();
|
||||
|
||||
neh.Magic = stream.ReadUInt16();
|
||||
neh.LinkerVersion = stream.ReadByteValue();
|
||||
neh.LinkerRevision = stream.ReadByteValue();
|
||||
neh.EntryTableOffset = stream.ReadUInt16();
|
||||
neh.EntryTableSize = stream.ReadUInt16();
|
||||
neh.CrcChecksum = stream.ReadUInt32();
|
||||
neh.ProgramFlags = stream.ReadByteValue();
|
||||
neh.ApplicationFlags = stream.ReadByteValue();
|
||||
neh.Autodata = stream.ReadUInt16();
|
||||
neh.InitialHeapAlloc = stream.ReadUInt16();
|
||||
neh.InitialStackAlloc = stream.ReadUInt16();
|
||||
neh.InitialCSIPSetting = stream.ReadUInt32();
|
||||
neh.InitialSSSPSetting = stream.ReadUInt32();
|
||||
neh.FileSegmentCount = stream.ReadUInt16();
|
||||
neh.ModuleReferenceTableSize = stream.ReadUInt16();
|
||||
neh.NonResidentNameTableSize = stream.ReadUInt16();
|
||||
neh.SegmentTableOffset = stream.ReadUInt16();
|
||||
neh.ResourceTableOffset = stream.ReadUInt16();
|
||||
neh.ResidentNameTableOffset = stream.ReadUInt16();
|
||||
neh.ModuleReferenceTableOffset = stream.ReadUInt16();
|
||||
neh.ImportedNamesTableOffset = stream.ReadUInt16();
|
||||
neh.NonResidentNamesTableOffset = stream.ReadUInt32();
|
||||
neh.MovableEntriesCount = stream.ReadUInt16();
|
||||
neh.SegmentAlignmentShiftCount = stream.ReadUInt16();
|
||||
neh.ResourceEntriesCount = stream.ReadUInt16();
|
||||
neh.TargetOperatingSystem = stream.ReadByteValue();
|
||||
neh.AdditionalFlags = stream.ReadByteValue();
|
||||
neh.ReturnThunkOffset = stream.ReadUInt16();
|
||||
neh.SegmentReferenceThunkOffset = stream.ReadUInt16();
|
||||
neh.MinCodeSwapAreaSize = stream.ReadUInt16();
|
||||
neh.WindowsSDKRevision = stream.ReadByteValue();
|
||||
neh.WindowsSDKVersion = stream.ReadByteValue();
|
||||
|
||||
return neh;
|
||||
}
|
||||
|
||||
public static NewExecutableHeader Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var neh = new NewExecutableHeader();
|
||||
|
||||
neh.Magic = content.ReadUInt16(ref offset);
|
||||
neh.LinkerVersion = content.ReadByte(ref offset);
|
||||
neh.LinkerRevision = content.ReadByte(ref offset);
|
||||
neh.EntryTableOffset = content.ReadUInt16(ref offset);
|
||||
neh.EntryTableSize = content.ReadUInt16(ref offset);
|
||||
neh.CrcChecksum = content.ReadUInt32(ref offset);
|
||||
neh.ProgramFlags = content.ReadByte(ref offset);
|
||||
neh.ApplicationFlags = content.ReadByte(ref offset);
|
||||
neh.Autodata = content.ReadUInt16(ref offset);
|
||||
neh.InitialHeapAlloc = content.ReadUInt16(ref offset);
|
||||
neh.InitialStackAlloc = content.ReadUInt16(ref offset);
|
||||
neh.InitialCSIPSetting = content.ReadUInt32(ref offset);
|
||||
neh.InitialSSSPSetting = content.ReadUInt32(ref offset);
|
||||
neh.FileSegmentCount = content.ReadUInt16(ref offset);
|
||||
neh.ModuleReferenceTableSize = content.ReadUInt16(ref offset);
|
||||
neh.NonResidentNameTableSize = content.ReadUInt16(ref offset);
|
||||
neh.SegmentTableOffset = content.ReadUInt16(ref offset);
|
||||
neh.ResourceTableOffset = content.ReadUInt16(ref offset);
|
||||
neh.ResidentNameTableOffset = content.ReadUInt16(ref offset);
|
||||
neh.ModuleReferenceTableOffset = content.ReadUInt16(ref offset);
|
||||
neh.ImportedNamesTableOffset = content.ReadUInt16(ref offset);
|
||||
neh.NonResidentNamesTableOffset = content.ReadUInt32(ref offset);
|
||||
neh.MovableEntriesCount = content.ReadUInt16(ref offset);
|
||||
neh.SegmentAlignmentShiftCount = content.ReadUInt16(ref offset);
|
||||
neh.ResourceEntriesCount = content.ReadUInt16(ref offset);
|
||||
neh.TargetOperatingSystem = content.ReadByte(ref offset);
|
||||
neh.AdditionalFlags = content.ReadByte(ref offset);
|
||||
neh.ReturnThunkOffset = content.ReadUInt16(ref offset);
|
||||
neh.SegmentReferenceThunkOffset = content.ReadUInt16(ref offset);
|
||||
neh.MinCodeSwapAreaSize = content.ReadUInt16(ref offset);
|
||||
neh.WindowsSDKRevision = content.ReadByte(ref offset);
|
||||
neh.WindowsSDKVersion = content.ReadByte(ref offset);
|
||||
|
||||
return neh;
|
||||
}
|
||||
}
|
||||
}
|
||||
370
BurnOutSharp/ExecutableType/Microsoft/Headers/OptionalHeader.cs
Normal file
370
BurnOutSharp/ExecutableType/Microsoft/Headers/OptionalHeader.cs
Normal file
@@ -0,0 +1,370 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Headers
|
||||
{
|
||||
/// <summary>
|
||||
/// Every image file has an optional header that provides information to the loader.
|
||||
/// This header is optional in the sense that some files (specifically, object files) do not have it.
|
||||
/// For image files, this header is required. An object file can have an optional header, but generally
|
||||
/// this header has no function in an object file except to increase its size.
|
||||
///
|
||||
/// Note that the size of the optional header is not fixed.
|
||||
/// The SizeOfOptionalHeader field in the COFF header must be used to validate that a probe into the file
|
||||
/// for a particular data directory does not go beyond SizeOfOptionalHeader.
|
||||
///
|
||||
/// The NumberOfRvaAndSizes field of the optional header should also be used to ensure that no probe for
|
||||
/// a particular data directory entry goes beyond the optional header.
|
||||
/// In addition, it is important to validate the optional header magic number for format compatibility.
|
||||
/// </summary>
|
||||
public class OptionalHeader
|
||||
{
|
||||
#region Standard Fields
|
||||
|
||||
/// <summary>
|
||||
/// The unsigned integer that identifies the state of the image file.
|
||||
/// The most common number is 0x10B, which identifies it as a normal executable file.
|
||||
/// 0x107 identifies it as a ROM image, and 0x20B identifies it as a PE32+ executable.
|
||||
/// </summary>
|
||||
public OptionalHeaderType Magic;
|
||||
|
||||
/// <summary>
|
||||
/// The linker major version number.
|
||||
/// </summary>
|
||||
public byte MajorLinkerVersion;
|
||||
|
||||
/// <summary>
|
||||
/// The linker minor version number.
|
||||
/// </summary>
|
||||
public byte MinorLinkerVersion;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the code (text) section, or the sum of all code sections if there are multiple sections.
|
||||
/// </summary>
|
||||
public uint SizeOfCode;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the initialized data section, or the sum of all such sections if there are multiple data sections.
|
||||
/// </summary>
|
||||
public uint SizeOfInitializedData;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the uninitialized data section (BSS), or the sum of all such sections if there are multiple BSS sections.
|
||||
/// </summary>
|
||||
public uint SizeOfUninitializedData;
|
||||
|
||||
/// <summary>
|
||||
/// The address of the entry point relative to the image base when the executable file is loaded into memory.
|
||||
/// For program images, this is the starting address.
|
||||
/// For device drivers, this is the address of the initialization function.
|
||||
// An entry point is optional for DLLs.
|
||||
/// When no entry point is present, this field must be zero.
|
||||
/// </summary>
|
||||
public uint AddressOfEntryPoint;
|
||||
|
||||
/// <summary>
|
||||
/// The address that is relative to the image base of the beginning-of-code section when it is loaded into memory.
|
||||
/// </summary>
|
||||
public uint BaseOfCode;
|
||||
|
||||
/// <summary>
|
||||
/// The address that is relative to the image base of the beginning-of-data section when it is loaded into memory.
|
||||
/// </summary>
|
||||
public uint BaseOfData;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Windows-Specific Fields
|
||||
|
||||
/// <summary>
|
||||
/// The preferred address of the first byte of image when loaded into memory; must be a multiple of 64 K.
|
||||
/// The default for DLLs is 0x10000000.
|
||||
/// The default for Windows CE EXEs is 0x00010000.
|
||||
/// The default for Windows NT, Windows 2000, Windows XP, Windows 95, Windows 98, and Windows Me is 0x00400000.
|
||||
/// </summary>
|
||||
public uint ImageBasePE32;
|
||||
|
||||
/// <summary>
|
||||
/// The preferred address of the first byte of image when loaded into memory; must be a multiple of 64 K.
|
||||
/// The default for DLLs is 0x10000000.
|
||||
/// The default for Windows CE EXEs is 0x00010000.
|
||||
/// The default for Windows NT, Windows 2000, Windows XP, Windows 95, Windows 98, and Windows Me is 0x00400000.
|
||||
/// </summary>
|
||||
public ulong ImageBasePE32Plus;
|
||||
|
||||
/// <summary>
|
||||
/// The alignment (in bytes) of sections when they are loaded into memory.
|
||||
/// It must be greater than or equal to FileAlignment.
|
||||
/// The default is the page size for the architecture.
|
||||
/// </summary>
|
||||
public uint SectionAlignment;
|
||||
|
||||
/// <summary>
|
||||
/// The alignment factor (in bytes) that is used to align the raw data of sections in the image file.
|
||||
/// The value should be a power of 2 between 512 and 64 K, inclusive.
|
||||
/// The default is 512.
|
||||
/// If the SectionAlignment is less than the architecture's page size, then FileAlignment must match SectionAlignment.
|
||||
/// </summary>
|
||||
public uint FileAlignment;
|
||||
|
||||
/// <summary>
|
||||
/// The major version number of the required operating system.
|
||||
/// </summary>
|
||||
public ushort MajorOperatingSystemVersion;
|
||||
|
||||
/// <summary>
|
||||
/// The minor version number of the required operating system.
|
||||
/// </summary>
|
||||
public ushort MinorOperatingSystemVersion;
|
||||
|
||||
/// <summary>
|
||||
/// The major version number of the image.
|
||||
/// </summary>
|
||||
public ushort MajorImageVersion;
|
||||
|
||||
/// <summary>
|
||||
/// The minor version number of the image.
|
||||
/// </summary>
|
||||
public ushort MinorImageVersion;
|
||||
|
||||
/// <summary>
|
||||
/// The major version number of the subsystem.
|
||||
/// </summary>
|
||||
public ushort MajorSubsystemVersion;
|
||||
|
||||
/// <summary>
|
||||
/// The minor version number of the subsystem.
|
||||
/// </summary>
|
||||
public ushort MinorSubsystemVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved, must be zero.
|
||||
/// </summary>
|
||||
public uint Reserved1;
|
||||
|
||||
/// <summary>
|
||||
/// The size (in bytes) of the image, including all headers, as the image is loaded in memory.
|
||||
/// It must be a multiple of SectionAlignment.
|
||||
/// </summary>
|
||||
public uint SizeOfImage;
|
||||
|
||||
/// <summary>
|
||||
/// The combined size of an MS-DOS stub, PE header, and section headers rounded up to a multiple of FileAlignment.
|
||||
/// </summary>
|
||||
public uint SizeOfHeaders;
|
||||
|
||||
/// <summary>
|
||||
/// The image file checksum.
|
||||
/// The algorithm for computing the checksum is incorporated into IMAGHELP.DLL.
|
||||
/// The following are checked for validation at load time: all drivers, any DLL loaded at boot time, and any DLL that is loaded into a critical Windows process.
|
||||
/// </summary>
|
||||
public uint CheckSum;
|
||||
|
||||
/// <summary>
|
||||
/// The subsystem that is required to run this image.
|
||||
/// </summary>
|
||||
public WindowsSubsystem Subsystem;
|
||||
|
||||
/// <summary>
|
||||
/// DLL Characteristics
|
||||
/// </summary>
|
||||
public DllCharacteristics DllCharacteristics;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the stack to reserve.
|
||||
/// Only SizeOfStackCommit is committed; the rest is made available one page at a time until the reserve size is reached.
|
||||
/// </summary>
|
||||
public uint SizeOfStackReservePE32;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the stack to reserve.
|
||||
/// Only SizeOfStackCommit is committed; the rest is made available one page at a time until the reserve size is reached.
|
||||
/// </summary>
|
||||
public ulong SizeOfStackReservePE32Plus;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the stack to commit.
|
||||
/// </summary>
|
||||
public uint SizeOfStackCommitPE32;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the stack to commit.
|
||||
/// </summary>
|
||||
public ulong SizeOfStackCommitPE32Plus;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the local heap space to reserve.
|
||||
/// Only SizeOfHeapCommit is committed; the rest is made available one page at a time until the reserve size is reached.
|
||||
/// </summary>
|
||||
public uint SizeOfHeapReservePE32;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the local heap space to reserve.
|
||||
/// Only SizeOfHeapCommit is committed; the rest is made available one page at a time until the reserve size is reached.
|
||||
/// </summary>
|
||||
public ulong SizeOfHeapReservePE32Plus;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the local heap space to commit.
|
||||
/// </summary>
|
||||
public uint SizeOfHeapCommitPE32;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the local heap space to commit.
|
||||
/// </summary>
|
||||
public ulong SizeOfHeapCommitPE32Plus;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved, must be zero.
|
||||
/// </summary>
|
||||
public uint LoaderFlags;
|
||||
|
||||
/// <summary>
|
||||
/// The number of data-directory entries in the remainder of the optional header.
|
||||
/// Each describes a location and size.
|
||||
/// </summary>
|
||||
public uint NumberOfRvaAndSizes;
|
||||
|
||||
/// <summary>
|
||||
/// Data-directory entries following the optional header
|
||||
/// </summary>
|
||||
public DataDirectoryHeader[] DataDirectories;
|
||||
|
||||
#endregion
|
||||
|
||||
public static OptionalHeader Deserialize(Stream stream)
|
||||
{
|
||||
var ioh = new OptionalHeader();
|
||||
|
||||
ioh.Magic = (OptionalHeaderType)stream.ReadUInt16();
|
||||
ioh.MajorLinkerVersion = stream.ReadByteValue();
|
||||
ioh.MinorLinkerVersion = stream.ReadByteValue();
|
||||
ioh.SizeOfCode = stream.ReadUInt32();
|
||||
ioh.SizeOfInitializedData = stream.ReadUInt32();
|
||||
ioh.SizeOfUninitializedData = stream.ReadUInt32();
|
||||
ioh.AddressOfEntryPoint = stream.ReadUInt32();
|
||||
ioh.BaseOfCode = stream.ReadUInt32();
|
||||
|
||||
// Only standard PE32 has this value
|
||||
if (ioh.Magic == OptionalHeaderType.PE32)
|
||||
ioh.BaseOfData = stream.ReadUInt32();
|
||||
|
||||
// PE32+ has an 8-byte value here
|
||||
if (ioh.Magic == OptionalHeaderType.PE32Plus)
|
||||
ioh.ImageBasePE32Plus = stream.ReadUInt64();
|
||||
else
|
||||
ioh.ImageBasePE32 = stream.ReadUInt32();
|
||||
|
||||
ioh.SectionAlignment = stream.ReadUInt32();
|
||||
ioh.FileAlignment = stream.ReadUInt32();
|
||||
ioh.MajorOperatingSystemVersion = stream.ReadUInt16();
|
||||
ioh.MinorOperatingSystemVersion = stream.ReadUInt16();
|
||||
ioh.MajorImageVersion = stream.ReadUInt16();
|
||||
ioh.MinorImageVersion = stream.ReadUInt16();
|
||||
ioh.MajorSubsystemVersion = stream.ReadUInt16();
|
||||
ioh.MinorSubsystemVersion = stream.ReadUInt16();
|
||||
ioh.Reserved1 = stream.ReadUInt32();
|
||||
ioh.SizeOfImage = stream.ReadUInt32();
|
||||
ioh.SizeOfHeaders = stream.ReadUInt32();
|
||||
ioh.CheckSum = stream.ReadUInt32();
|
||||
ioh.Subsystem = (WindowsSubsystem)stream.ReadUInt16();
|
||||
ioh.DllCharacteristics = (DllCharacteristics)stream.ReadUInt16();
|
||||
|
||||
// PE32+ uses 8-byte values
|
||||
if (ioh.Magic == OptionalHeaderType.PE32Plus)
|
||||
{
|
||||
ioh.SizeOfStackReservePE32Plus = stream.ReadUInt64();
|
||||
ioh.SizeOfStackCommitPE32Plus = stream.ReadUInt64();
|
||||
ioh.SizeOfHeapReservePE32Plus = stream.ReadUInt64();
|
||||
ioh.SizeOfHeapCommitPE32Plus = stream.ReadUInt64();
|
||||
}
|
||||
else
|
||||
{
|
||||
ioh.SizeOfStackReservePE32 = stream.ReadUInt32();
|
||||
ioh.SizeOfStackCommitPE32 = stream.ReadUInt32();
|
||||
ioh.SizeOfHeapReservePE32 = stream.ReadUInt32();
|
||||
ioh.SizeOfHeapCommitPE32 = stream.ReadUInt32();
|
||||
}
|
||||
|
||||
ioh.LoaderFlags = stream.ReadUInt32();
|
||||
ioh.NumberOfRvaAndSizes = stream.ReadUInt32();
|
||||
ioh.DataDirectories = new DataDirectoryHeader[Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
|
||||
for (int i = 0; i < Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++)
|
||||
{
|
||||
ioh.DataDirectories[i] = DataDirectoryHeader.Deserialize(stream);
|
||||
}
|
||||
|
||||
return ioh;
|
||||
}
|
||||
|
||||
public static OptionalHeader Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var ioh = new OptionalHeader();
|
||||
|
||||
ioh.Magic = (OptionalHeaderType)content.ReadUInt16(ref offset);
|
||||
ioh.MajorLinkerVersion = content[offset]; offset++;
|
||||
ioh.MinorLinkerVersion = content[offset]; offset++;
|
||||
ioh.SizeOfCode = content.ReadUInt32(ref offset);
|
||||
ioh.SizeOfInitializedData = content.ReadUInt32(ref offset);
|
||||
ioh.SizeOfUninitializedData = content.ReadUInt32(ref offset);
|
||||
ioh.AddressOfEntryPoint = content.ReadUInt32(ref offset);
|
||||
ioh.BaseOfCode = content.ReadUInt32(ref offset);
|
||||
|
||||
// Only standard PE32 has this value
|
||||
if (ioh.Magic == OptionalHeaderType.PE32)
|
||||
ioh.BaseOfData = content.ReadUInt32(ref offset);
|
||||
|
||||
// PE32+ has an 8-bit value here
|
||||
if (ioh.Magic == OptionalHeaderType.PE32Plus)
|
||||
{
|
||||
ioh.ImageBasePE32Plus = content.ReadUInt64(ref offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
ioh.ImageBasePE32 = content.ReadUInt32(ref offset);
|
||||
}
|
||||
|
||||
ioh.SectionAlignment = content.ReadUInt32(ref offset);
|
||||
ioh.FileAlignment = content.ReadUInt32(ref offset);
|
||||
ioh.MajorOperatingSystemVersion = content.ReadUInt16(ref offset);
|
||||
ioh.MinorOperatingSystemVersion = content.ReadUInt16(ref offset);
|
||||
ioh.MajorImageVersion = content.ReadUInt16(ref offset);
|
||||
ioh.MinorImageVersion = content.ReadUInt16(ref offset);
|
||||
ioh.MajorSubsystemVersion = content.ReadUInt16(ref offset);
|
||||
ioh.MinorSubsystemVersion = content.ReadUInt16(ref offset);
|
||||
ioh.Reserved1 = content.ReadUInt32(ref offset);
|
||||
ioh.SizeOfImage = content.ReadUInt32(ref offset);
|
||||
ioh.SizeOfHeaders = content.ReadUInt32(ref offset);
|
||||
ioh.CheckSum = content.ReadUInt32(ref offset);
|
||||
ioh.Subsystem = (WindowsSubsystem)content.ReadUInt16(ref offset);
|
||||
ioh.DllCharacteristics = (DllCharacteristics)content.ReadUInt16(ref offset);
|
||||
|
||||
// PE32+ uses 8-byte values
|
||||
if (ioh.Magic == OptionalHeaderType.PE32Plus)
|
||||
{
|
||||
ioh.SizeOfStackReservePE32Plus = content.ReadUInt64(ref offset);
|
||||
ioh.SizeOfStackCommitPE32Plus = content.ReadUInt64(ref offset);
|
||||
ioh.SizeOfHeapReservePE32Plus = content.ReadUInt64(ref offset);
|
||||
ioh.SizeOfHeapCommitPE32Plus = content.ReadUInt64(ref offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
ioh.SizeOfStackReservePE32 = content.ReadUInt32(ref offset);
|
||||
ioh.SizeOfStackCommitPE32 = content.ReadUInt32(ref offset);
|
||||
ioh.SizeOfHeapReservePE32 = content.ReadUInt32(ref offset);
|
||||
ioh.SizeOfHeapCommitPE32 = content.ReadUInt32(ref offset);
|
||||
}
|
||||
|
||||
ioh.LoaderFlags = content.ReadUInt32(ref offset);
|
||||
ioh.NumberOfRvaAndSizes = content.ReadUInt32(ref offset);
|
||||
ioh.DataDirectories = new DataDirectoryHeader[Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
|
||||
for (int i = 0; i < Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++)
|
||||
{
|
||||
ioh.DataDirectories[i] = DataDirectoryHeader.Deserialize(content, ref offset);
|
||||
}
|
||||
|
||||
return ioh;
|
||||
}
|
||||
}
|
||||
}
|
||||
129
BurnOutSharp/ExecutableType/Microsoft/Headers/SectionHeader.cs
Normal file
129
BurnOutSharp/ExecutableType/Microsoft/Headers/SectionHeader.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Headers
|
||||
{
|
||||
/// <summary>
|
||||
/// Each row of the section table is, in effect, a section header.
|
||||
/// This table immediately follows the optional header, if any.
|
||||
/// This positioning is required because the file header does not contain a direct pointer to the section table.
|
||||
/// Instead, the location of the section table is determined by calculating the location of the first byte after the headers.
|
||||
/// Make sure to use the size of the optional header as specified in the file header.
|
||||
/// </summary>
|
||||
public class SectionHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// An 8-byte, null-padded UTF-8 encoded string.
|
||||
/// If the string is exactly 8 characters long, there is no terminating null.
|
||||
/// For longer names, this field contains a slash (/) that is followed by an ASCII representation of a decimal number
|
||||
/// that is an offset into the string table.
|
||||
/// Executable images do not use a string table and do not support section names longer than 8 characters.
|
||||
/// Long names in object files are truncated if they are emitted to an executable file.
|
||||
/// </summary>
|
||||
public byte[] Name;
|
||||
|
||||
/// <summary>
|
||||
/// The total size of the section when loaded into memory.
|
||||
/// If this value is greater than SizeOfRawData, the section is zero-padded.
|
||||
/// This field is valid only for executable images and should be set to zero for object files.
|
||||
/// </summary>
|
||||
public uint VirtualSize;
|
||||
|
||||
/// <summary>
|
||||
/// For executable images, the address of the first byte of the section relative to the image base when the section
|
||||
/// is loaded into memory.
|
||||
/// For object files, this field is the address of the first byte before relocation is applied; for simplicity,
|
||||
/// compilers should set this to zero.
|
||||
/// Otherwise, it is an arbitrary value that is subtracted from offsets during relocation.
|
||||
/// </summary>
|
||||
public uint VirtualAddress;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the section (for object files) or the size of the initialized data on disk (for image files).
|
||||
/// For executable images, this must be a multiple of FileAlignment from the optional header.
|
||||
/// If this is less than VirtualSize, the remainder of the section is zero-filled.
|
||||
/// Because the SizeOfRawData field is rounded but the VirtualSize field is not, it is possible for SizeOfRawData
|
||||
/// to be greater than VirtualSize as well.
|
||||
/// When a section contains only uninitialized data, this field should be zero.
|
||||
/// </summary>
|
||||
public uint SizeOfRawData;
|
||||
|
||||
/// <summary>
|
||||
/// The file pointer to the first page of the section within the COFF file.
|
||||
/// For executable images, this must be a multiple of FileAlignment from the optional header.
|
||||
/// For object files, the value should be aligned on a 4-byte boundary for best performance.
|
||||
/// When a section contains only uninitialized data, this field should be zero.
|
||||
/// </summary>
|
||||
public uint PointerToRawData;
|
||||
|
||||
/// <summary>
|
||||
/// The file pointer to the beginning of relocation entries for the section.
|
||||
/// This is set to zero for executable images or if there are no relocations.
|
||||
/// </summary>
|
||||
public uint PointerToRelocations;
|
||||
|
||||
/// <summary>
|
||||
/// The file pointer to the beginning of line-number entries for the section.
|
||||
/// This is set to zero if there are no COFF line numbers.
|
||||
/// This value should be zero for an image because COFF debugging information is deprecated.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public uint PointerToLinenumbers;
|
||||
|
||||
/// <summary>
|
||||
/// The number of relocation entries for the section.
|
||||
/// This is set to zero for executable images.
|
||||
/// </summary>
|
||||
public ushort NumberOfRelocations;
|
||||
|
||||
/// <summary>
|
||||
/// The number of line-number entries for the section.
|
||||
/// This value should be zero for an image because COFF debugging information is deprecated.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public ushort NumberOfLinenumbers;
|
||||
|
||||
/// <summary>
|
||||
/// The flags that describe the characteristics of the section.
|
||||
/// </summary>
|
||||
public SectionCharacteristics Characteristics;
|
||||
|
||||
public static SectionHeader Deserialize(Stream stream)
|
||||
{
|
||||
var ish = new SectionHeader();
|
||||
|
||||
ish.Name = stream.ReadBytes(Constants.IMAGE_SIZEOF_SHORT_NAME);
|
||||
ish.VirtualSize = stream.ReadUInt32();
|
||||
ish.VirtualAddress = stream.ReadUInt32();
|
||||
ish.SizeOfRawData = stream.ReadUInt32();
|
||||
ish.PointerToRawData = stream.ReadUInt32();
|
||||
ish.PointerToRelocations = stream.ReadUInt32();
|
||||
ish.PointerToLinenumbers = stream.ReadUInt32();
|
||||
ish.NumberOfRelocations = stream.ReadUInt16();
|
||||
ish.NumberOfLinenumbers = stream.ReadUInt16();
|
||||
ish.Characteristics = (SectionCharacteristics)stream.ReadUInt32();
|
||||
|
||||
return ish;
|
||||
}
|
||||
|
||||
public static SectionHeader Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var ish = new SectionHeader();
|
||||
|
||||
ish.Name = new byte[Constants.IMAGE_SIZEOF_SHORT_NAME];
|
||||
Array.Copy(content, offset, ish.Name, 0, Constants.IMAGE_SIZEOF_SHORT_NAME); offset += Constants.IMAGE_SIZEOF_SHORT_NAME;
|
||||
ish.VirtualSize = content.ReadUInt32(ref offset);
|
||||
ish.VirtualAddress = content.ReadUInt32(ref offset);
|
||||
ish.SizeOfRawData = content.ReadUInt32(ref offset);
|
||||
ish.PointerToRawData = content.ReadUInt32(ref offset);
|
||||
ish.PointerToRelocations = content.ReadUInt32(ref offset);
|
||||
ish.PointerToLinenumbers = content.ReadUInt32(ref offset);
|
||||
ish.NumberOfRelocations = content.ReadUInt16(ref offset);
|
||||
ish.NumberOfLinenumbers = content.ReadUInt16(ref offset);
|
||||
ish.Characteristics = (SectionCharacteristics)content.ReadUInt32(ref offset);
|
||||
|
||||
return ish;
|
||||
}
|
||||
}
|
||||
}
|
||||
97
BurnOutSharp/ExecutableType/Microsoft/NewExecutable.cs
Normal file
97
BurnOutSharp/ExecutableType/Microsoft/NewExecutable.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Headers;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft
|
||||
{
|
||||
/// <summary>
|
||||
/// The WIN-NE executable format, designed for Windows 3.x, was the "NE", or "New Executable" format.
|
||||
/// Again, a 16bit format, it alleviated the maximum size restrictions that the MZ format had.
|
||||
/// </summary>
|
||||
public class NewExecutable
|
||||
{
|
||||
#region Headers
|
||||
|
||||
/// <summary>
|
||||
/// he DOS stub is a valid MZ exe.
|
||||
/// This enables the develper to package both an MS-DOS and Win16 version of the program,
|
||||
/// but normally just prints "This Program requires Microsoft Windows".
|
||||
/// The e_lfanew field (offset 0x3C) points to the NE header.
|
||||
// </summary>
|
||||
public MSDOSExecutableHeader DOSStubHeader;
|
||||
|
||||
/// <summary>
|
||||
/// The NE header is a relatively large structure with multiple characteristics.
|
||||
/// Because of the age of the format some items are unclear in meaning.
|
||||
/// </summary>
|
||||
public NewExecutableHeader NewExecutableHeader;
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Add more and more parts of a standard NE executable, not just the header
|
||||
// TODO: Tables? What about the tables?
|
||||
// TODO: Implement the rest of the structures found at http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm
|
||||
// (Left off at RESIDENT-NAME TABLE)
|
||||
|
||||
public static NewExecutable Deserialize(Stream stream)
|
||||
{
|
||||
NewExecutable nex = new NewExecutable();
|
||||
|
||||
try
|
||||
{
|
||||
// Attempt to read the DOS header first
|
||||
nex.DOSStubHeader = MSDOSExecutableHeader.Deserialize(stream);
|
||||
stream.Seek(nex.DOSStubHeader.NewExeHeaderAddr, SeekOrigin.Begin);
|
||||
if (nex.DOSStubHeader.Magic != Constants.IMAGE_DOS_SIGNATURE)
|
||||
return null;
|
||||
|
||||
// If the new header address is invalid for the file, it's not a NE
|
||||
if (nex.DOSStubHeader.NewExeHeaderAddr >= stream.Length)
|
||||
return null;
|
||||
|
||||
// Then attempt to read the NE header
|
||||
nex.NewExecutableHeader = NewExecutableHeader.Deserialize(stream);
|
||||
if (nex.NewExecutableHeader.Magic != Constants.IMAGE_OS2_SIGNATURE)
|
||||
return null;
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//Console.WriteLine($"Errored out on a file: {ex}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return nex;
|
||||
}
|
||||
|
||||
public static NewExecutable Deserialize(byte[] content, int offset)
|
||||
{
|
||||
NewExecutable nex = new NewExecutable();
|
||||
|
||||
try
|
||||
{
|
||||
// Attempt to read the DOS header first
|
||||
nex.DOSStubHeader = MSDOSExecutableHeader.Deserialize(content, ref offset);
|
||||
offset = nex.DOSStubHeader.NewExeHeaderAddr;
|
||||
if (nex.DOSStubHeader.Magic != Constants.IMAGE_DOS_SIGNATURE)
|
||||
return null;
|
||||
|
||||
// If the new header address is invalid for the file, it's not a PE
|
||||
if (nex.DOSStubHeader.NewExeHeaderAddr >= content.Length)
|
||||
return null;
|
||||
|
||||
// Then attempt to read the NE header
|
||||
nex.NewExecutableHeader = NewExecutableHeader.Deserialize(content, ref offset);
|
||||
if (nex.NewExecutableHeader.Magic != Constants.IMAGE_OS2_SIGNATURE)
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//Console.WriteLine($"Errored out on a file: {ex}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return nex;
|
||||
}
|
||||
}
|
||||
}
|
||||
526
BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs
Normal file
526
BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs
Normal file
@@ -0,0 +1,526 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Headers;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Sections;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft
|
||||
{
|
||||
/// <summary>
|
||||
/// The PE file header consists of a Microsoft MS-DOS stub, the PE signature, the COFF file header, and an optional header.
|
||||
/// A COFF object file header consists of a COFF file header and an optional header.
|
||||
/// In both cases, the file headers are followed immediately by section headers.
|
||||
/// </summary>
|
||||
public class PortableExecutable
|
||||
{
|
||||
#region Headers
|
||||
|
||||
/// <summary>
|
||||
/// The MS-DOS stub is a valid application that runs under MS-DOS.
|
||||
/// It is placed at the front of the EXE image.
|
||||
/// The linker places a default stub here, which prints out the message "This program cannot be run in DOS mode" when the image is run in MS-DOS.
|
||||
/// The user can specify a different stub by using the /STUB linker option.
|
||||
/// At location 0x3c, the stub has the file offset to the PE signature.
|
||||
/// This information enables Windows to properly execute the image file, even though it has an MS-DOS stub.
|
||||
/// This file offset is placed at location 0x3c during linking.
|
||||
// </summary>
|
||||
public MSDOSExecutableHeader DOSStubHeader;
|
||||
|
||||
/// <summary>
|
||||
/// At the beginning of an object file, or immediately after the signature of an image file, is a standard COFF file header in the following format.
|
||||
/// Note that the Windows loader limits the number of sections to 96.
|
||||
/// </summary>
|
||||
public CommonObjectFileFormatHeader ImageFileHeader;
|
||||
|
||||
/// <summary>
|
||||
/// Every image file has an optional header that provides information to the loader.
|
||||
/// This header is optional in the sense that some files (specifically, object files) do not have it.
|
||||
/// For image files, this header is required.
|
||||
/// An object file can have an optional header, but generally this header has no function in an object file except to increase its size.
|
||||
/// </summary>
|
||||
public OptionalHeader OptionalHeader;
|
||||
|
||||
/// <summary>
|
||||
/// Each row of the section table is, in effect, a section header.
|
||||
/// This table immediately follows the optional header, if any.
|
||||
/// This positioning is required because the file header does not contain a direct pointer to the section table.
|
||||
/// Instead, the location of the section table is determined by calculating the location of the first byte after the headers.
|
||||
/// Make sure to use the size of the optional header as specified in the file header.
|
||||
/// </summary>
|
||||
public SectionHeader[] SectionTable;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tables
|
||||
|
||||
/// <summary>
|
||||
/// The export data section, named .edata, contains information about symbols that other images can access through dynamic linking.
|
||||
/// Exported symbols are generally found in DLLs, but DLLs can also import symbols.
|
||||
/// </summary>
|
||||
public ExportDataSection ExportTable;
|
||||
|
||||
/// <summary>
|
||||
/// All image files that import symbols, including virtually all executable (EXE) files, have an .idata section.
|
||||
/// </summary>
|
||||
public ImportDataSection ImportTable;
|
||||
|
||||
/// <summary>
|
||||
/// Resources are indexed by a multiple-level binary-sorted tree structure.
|
||||
/// The general design can incorporate 2**31 levels.
|
||||
/// By convention, however, Windows uses three levels
|
||||
/// </summary>
|
||||
public ResourceSection ResourceSection;
|
||||
|
||||
// TODO: Add more and more parts of a standard PE executable, not just the header
|
||||
// TODO: Add data directory table information here instead of in IMAGE_OPTIONAL_HEADER
|
||||
|
||||
#endregion
|
||||
|
||||
#region Raw Section Data
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#special-sections
|
||||
// Here is a list of standard sections that are used in various protections:
|
||||
// Y - .bss *1 protection Uninitialized data (free format)
|
||||
// X - .data 14 protections Initialized data (free format)
|
||||
// X - .edata *1 protection Export tables
|
||||
// X - .idata *1 protection Import tables
|
||||
// X - .rdata 11 protections Read-only initialized data
|
||||
// - .rsrc *1 protection Resource directory [Mostly taken care of, last protection needs research]
|
||||
// X - .text 6 protections Executable code (free format)
|
||||
// Y - .tls *1 protection Thread-local storage (object only)
|
||||
//
|
||||
// Here is a list of non-standard sections whose contents are read by various protections:
|
||||
// X - CODE 2 protections SafeDisc, WTM CD Protect
|
||||
// X - .dcrtext *1 protection JoWood
|
||||
// X - .grand *1 protection CD-Cops / DVD-Cops
|
||||
// X - .init *1 protection SolidShield
|
||||
// - .pec2 *1 protection PE Compact [Unconfirmed]
|
||||
// X - .txt2 *1 protection SafeDisc
|
||||
//
|
||||
// Here is a list of non-standard sections whose data is not read by various protections:
|
||||
// - .brick 1 protection StarForce
|
||||
// - .cenega 1 protection Cenega ProtectDVD
|
||||
// - .ext 1 protection JoWood
|
||||
// - HC09 1 protection JoWood
|
||||
// - .icd* 1 protection CodeLock
|
||||
// - .ldr 1 protection 3PLock
|
||||
// - .ldt 1 protection 3PLock
|
||||
// - .nicode 1 protection Armadillo
|
||||
// - .NOS0 1 protection UPX (NOS Variant) [Used as endpoint]
|
||||
// - .NOS1 1 protection UPX (NOS Variant) [Used as endpoint]
|
||||
// - .pec1 1 protection PE Compact
|
||||
// - .securom 1 protection SecuROM
|
||||
// - .sforce 1 protection StarForce
|
||||
// - stxt371 1 protection SafeDisc
|
||||
// - stxt774 1 protection SafeDisc
|
||||
// - .UPX0 1 protection UPX [Used as endpoint]
|
||||
// - .UPX1 1 protection UPX [Used as endpoint]
|
||||
// - .vob.pcd 1 protection VOB ProtectCD
|
||||
// - _winzip_ 1 protection WinZip SFX
|
||||
// - XPROT 1 protection JoWood
|
||||
//
|
||||
// * => Only used by 1 protection so it may be read in by that protection specifically
|
||||
|
||||
/// <summary>
|
||||
/// .data/DATA - Initialized data (free format)
|
||||
/// </summary>
|
||||
public byte[] DataSectionRaw;
|
||||
|
||||
/// <summary>
|
||||
/// .edata - Export tables
|
||||
/// </summary>
|
||||
/// <remarks>Replace with ExportDataSection</remarks>
|
||||
public byte[] ExportDataSectionRaw;
|
||||
|
||||
/// <summary>
|
||||
/// .idata - Import tables
|
||||
/// </summary>
|
||||
/// <remarks>Replace with ImportDataSection</remarks>
|
||||
public byte[] ImportDataSectionRaw;
|
||||
|
||||
/// <summary>
|
||||
/// .rdata - Read-only initialized data
|
||||
/// </summary>
|
||||
public byte[] ResourceDataSectionRaw;
|
||||
|
||||
/// <summary>
|
||||
/// .text - Executable code (free format)
|
||||
/// </summary>
|
||||
public byte[] TextSectionRaw;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a section is contained within the section table
|
||||
/// </summary>
|
||||
/// <param name="sectionName">Name of the section to check for</param>
|
||||
/// <param name="exact">True to enable exact matching of names, false for starts-with</param>
|
||||
/// <returns>True if the section is in the executable, false otherwise</returns>
|
||||
public bool ContainsSection(string sectionName, bool exact = false)
|
||||
{
|
||||
// Get all section names first
|
||||
string[] sectionNames = GetSectionNames();
|
||||
if (sectionNames == null)
|
||||
return false;
|
||||
|
||||
// If we're checking exactly, return only exact matches (with nulls trimmed)
|
||||
if (exact)
|
||||
return sectionNames.Any(n => n.Trim('\0').Equals(sectionName));
|
||||
|
||||
// Otherwise, check if section name starts with the value
|
||||
else
|
||||
return sectionNames.Any(n => n.Trim('\0').StartsWith(sectionName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a virtual address to a physical one
|
||||
/// </summary>
|
||||
/// <param name="virtualAddress">Virtual address to convert</param>
|
||||
/// <param name="sections">Array of sections to check against</param>
|
||||
/// <returns>Physical address, 0 on error</returns>
|
||||
public static uint ConvertVirtualAddress(uint virtualAddress, SectionHeader[] sections)
|
||||
{
|
||||
// Loop through all of the sections
|
||||
for (int i = 0; i < sections.Length; i++)
|
||||
{
|
||||
// If the section is invalid, just skip it
|
||||
if (sections[i] == null)
|
||||
continue;
|
||||
|
||||
// If the section "starts" at 0, just skip it
|
||||
if (sections[i].PointerToRawData == 0)
|
||||
continue;
|
||||
|
||||
// Attempt to derive the physical address from the current section
|
||||
var section = sections[i];
|
||||
if (virtualAddress >= section.VirtualAddress && virtualAddress <= section.VirtualAddress + section.VirtualSize)
|
||||
return section.PointerToRawData + virtualAddress - section.VirtualAddress;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the first section based on name, if possible
|
||||
/// </summary>
|
||||
/// <param name="sectionName">Name of the section to check for</param>
|
||||
/// <param name="exact">True to enable exact matching of names, false for starts-with</param>
|
||||
/// <returns>Section data on success, null on error</returns>
|
||||
public SectionHeader GetFirstSection(string sectionName, bool exact = false)
|
||||
{
|
||||
// If we have no sections, we can't do anything
|
||||
if (SectionTable == null || !SectionTable.Any())
|
||||
return null;
|
||||
|
||||
// If we're checking exactly, return only exact matches (with nulls trimmed)
|
||||
if (exact)
|
||||
return SectionTable.FirstOrDefault(s => Encoding.ASCII.GetString(s.Name).Trim('\0').Equals(sectionName));
|
||||
|
||||
// Otherwise, check if section name starts with the value
|
||||
else
|
||||
return SectionTable.FirstOrDefault(s => Encoding.ASCII.GetString(s.Name).Trim('\0').StartsWith(sectionName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the last section based on name, if possible
|
||||
/// </summary>
|
||||
/// <param name="sectionName">Name of the section to check for</param>
|
||||
/// <param name="exact">True to enable exact matching of names, false for starts-with</param>
|
||||
/// <returns>Section data on success, null on error</returns>
|
||||
public SectionHeader GetLastSection(string sectionName, bool exact = false)
|
||||
{
|
||||
// If we have no sections, we can't do anything
|
||||
if (SectionTable == null || !SectionTable.Any())
|
||||
return null;
|
||||
|
||||
// If we're checking exactly, return only exact matches (with nulls trimmed)
|
||||
if (exact)
|
||||
return SectionTable.LastOrDefault(s => Encoding.ASCII.GetString(s.Name).Trim('\0').Equals(sectionName));
|
||||
|
||||
// Otherwise, check if section name starts with the value
|
||||
else
|
||||
return SectionTable.LastOrDefault(s => Encoding.ASCII.GetString(s.Name).Trim('\0').StartsWith(sectionName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of section names
|
||||
/// </summary>
|
||||
public string[] GetSectionNames()
|
||||
{
|
||||
// Invalid table means no names are accessible
|
||||
if (SectionTable == null || SectionTable.Length == 0)
|
||||
return null;
|
||||
|
||||
return SectionTable.Select(s => Encoding.ASCII.GetString(s.Name)).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Print all sections, including their start and end addresses
|
||||
/// </summary>
|
||||
public void PrintAllSections()
|
||||
{
|
||||
foreach (var section in SectionTable)
|
||||
{
|
||||
string sectionName = Encoding.ASCII.GetString(section.Name).Trim('\0');
|
||||
int sectionAddr = (int)section.PointerToRawData;
|
||||
int sectionEnd = sectionAddr + (int)section.VirtualSize;
|
||||
Console.WriteLine($"{sectionName}: {sectionAddr} -> {sectionEnd}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the raw bytes from a section, if possible
|
||||
/// </summary>
|
||||
public byte[] ReadRawSection(Stream stream, string sectionName, bool force = false, bool first = true, int offset = 0)
|
||||
{
|
||||
// Special cases for non-forced, non-offset data
|
||||
if (!force && offset == 0)
|
||||
{
|
||||
switch (sectionName)
|
||||
{
|
||||
case ".data":
|
||||
return DataSectionRaw;
|
||||
case ".edata":
|
||||
return ExportDataSectionRaw;
|
||||
case ".idata":
|
||||
return ImportDataSectionRaw;
|
||||
case ".rdata":
|
||||
return ResourceDataSectionRaw;
|
||||
case ".text":
|
||||
return TextSectionRaw;
|
||||
}
|
||||
}
|
||||
|
||||
var section = first ? GetFirstSection(sectionName, true) : GetLastSection(sectionName, true);
|
||||
if (section == null)
|
||||
return null;
|
||||
|
||||
lock (stream)
|
||||
{
|
||||
int startingIndex = (int)Math.Max(section.PointerToRawData + offset, 0);
|
||||
int readLength = (int)Math.Min(section.VirtualSize - offset, stream.Length);
|
||||
|
||||
long originalPosition = stream.Position;
|
||||
stream.Seek(startingIndex, SeekOrigin.Begin);
|
||||
byte[] sectionData = stream.ReadBytes(readLength);
|
||||
stream.Seek(originalPosition, SeekOrigin.Begin);
|
||||
return sectionData;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the raw bytes from a section, if possible
|
||||
/// </summary>
|
||||
public byte[] ReadRawSection(byte[] content, string sectionName, bool force = false, bool first = true, int offset = 0)
|
||||
{
|
||||
// Special cases for non-forced, non-offset data
|
||||
if (!force && offset == 0)
|
||||
{
|
||||
switch (sectionName)
|
||||
{
|
||||
case ".data":
|
||||
return DataSectionRaw;
|
||||
case ".edata":
|
||||
return ExportDataSectionRaw;
|
||||
case ".idata":
|
||||
return ImportDataSectionRaw;
|
||||
case ".rdata":
|
||||
return ResourceDataSectionRaw;
|
||||
case ".text":
|
||||
return TextSectionRaw;
|
||||
}
|
||||
}
|
||||
|
||||
var section = first ? GetFirstSection(sectionName, true) : GetLastSection(sectionName, true);
|
||||
if (section == null)
|
||||
return null;
|
||||
|
||||
int startingIndex = (int)Math.Max(section.PointerToRawData + offset, 0);
|
||||
int readLength = (int)Math.Min(section.VirtualSize - offset, content.Length);
|
||||
|
||||
return content.ReadBytes(ref startingIndex, readLength);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static PortableExecutable Deserialize(Stream stream)
|
||||
{
|
||||
PortableExecutable pex = new PortableExecutable();
|
||||
|
||||
try
|
||||
{
|
||||
// Attempt to read the DOS header first
|
||||
pex.DOSStubHeader = MSDOSExecutableHeader.Deserialize(stream); stream.Seek(pex.DOSStubHeader.NewExeHeaderAddr, SeekOrigin.Begin);
|
||||
if (pex.DOSStubHeader.Magic != Constants.IMAGE_DOS_SIGNATURE)
|
||||
return null;
|
||||
|
||||
// If the new header address is invalid for the file, it's not a PE
|
||||
if (pex.DOSStubHeader.NewExeHeaderAddr >= stream.Length)
|
||||
return null;
|
||||
|
||||
// Then attempt to read the PE header
|
||||
pex.ImageFileHeader = CommonObjectFileFormatHeader.Deserialize(stream);
|
||||
if (pex.ImageFileHeader.Signature != Constants.IMAGE_NT_SIGNATURE)
|
||||
return null;
|
||||
|
||||
// If the optional header is supposed to exist, read that as well
|
||||
if (pex.ImageFileHeader.SizeOfOptionalHeader > 0)
|
||||
pex.OptionalHeader = OptionalHeader.Deserialize(stream);
|
||||
|
||||
// Then read in the section table
|
||||
pex.SectionTable = new SectionHeader[pex.ImageFileHeader.NumberOfSections];
|
||||
for (int i = 0; i < pex.ImageFileHeader.NumberOfSections; i++)
|
||||
{
|
||||
pex.SectionTable[i] = SectionHeader.Deserialize(stream);
|
||||
}
|
||||
|
||||
#region Structured Tables
|
||||
|
||||
// // Export Table
|
||||
// var table = pex.GetLastSection(".edata", true);
|
||||
// if (table != null && table.VirtualSize > 0)
|
||||
// {
|
||||
// stream.Seek((int)table.PointerToRawData, SeekOrigin.Begin);
|
||||
// pex.ExportTable = ExportDataSection.Deserialize(stream, pex.SectionTable);
|
||||
// }
|
||||
|
||||
// // Import Table
|
||||
// table = pex.GetSection(".idata", true);
|
||||
// if (table != null && table.VirtualSize > 0)
|
||||
// {
|
||||
// stream.Seek((int)table.PointerToRawData, SeekOrigin.Begin);
|
||||
// pex.ImportTable = ImportDataSection.Deserialize(stream, pex.OptionalHeader.Magic == OptionalHeaderType.PE32Plus, hintCount: 0);
|
||||
// }
|
||||
|
||||
// Resource Table
|
||||
var table = pex.GetLastSection(".rsrc", true);
|
||||
if (table != null && table.VirtualSize > 0)
|
||||
{
|
||||
stream.Seek((int)table.PointerToRawData, SeekOrigin.Begin);
|
||||
pex.ResourceSection = ResourceSection.Deserialize(stream, pex.SectionTable);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Freeform Sections
|
||||
|
||||
// Data Section
|
||||
pex.DataSectionRaw = pex.ReadRawSection(stream, ".data", force: true, first: false) ?? pex.ReadRawSection(stream, "DATA", force: true, first: false);
|
||||
|
||||
// Export Table
|
||||
pex.ExportDataSectionRaw = pex.ReadRawSection(stream, ".edata", force: true, first: false);
|
||||
|
||||
// Import Table
|
||||
pex.ImportDataSectionRaw = pex.ReadRawSection(stream, ".idata", force: true, first: false);
|
||||
|
||||
// Resource Data Section
|
||||
pex.ResourceDataSectionRaw = pex.ReadRawSection(stream, ".rdata", force: true, first: false);
|
||||
|
||||
// Text Section
|
||||
pex.TextSectionRaw = pex.ReadRawSection(stream, ".text", force: true, first: false);
|
||||
|
||||
#endregion
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//Console.WriteLine($"Errored out on a file: {ex}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return pex;
|
||||
}
|
||||
|
||||
public static PortableExecutable Deserialize(byte[] content, int offset)
|
||||
{
|
||||
PortableExecutable pex = new PortableExecutable();
|
||||
|
||||
try
|
||||
{
|
||||
// Attempt to read the DOS header first
|
||||
pex.DOSStubHeader = MSDOSExecutableHeader.Deserialize(content, ref offset);
|
||||
offset = pex.DOSStubHeader.NewExeHeaderAddr;
|
||||
if (pex.DOSStubHeader.Magic != Constants.IMAGE_DOS_SIGNATURE)
|
||||
return null;
|
||||
|
||||
// If the new header address is invalid for the file, it's not a PE
|
||||
if (pex.DOSStubHeader.NewExeHeaderAddr >= content.Length)
|
||||
return null;
|
||||
|
||||
// Then attempt to read the PE header
|
||||
pex.ImageFileHeader = CommonObjectFileFormatHeader.Deserialize(content, ref offset);
|
||||
if (pex.ImageFileHeader.Signature != Constants.IMAGE_NT_SIGNATURE)
|
||||
return null;
|
||||
|
||||
// If the optional header is supposed to exist, read that as well
|
||||
if (pex.ImageFileHeader.SizeOfOptionalHeader > 0)
|
||||
pex.OptionalHeader = OptionalHeader.Deserialize(content, ref offset);
|
||||
|
||||
// Then read in the section table
|
||||
pex.SectionTable = new SectionHeader[pex.ImageFileHeader.NumberOfSections];
|
||||
for (int i = 0; i < pex.ImageFileHeader.NumberOfSections; i++)
|
||||
{
|
||||
pex.SectionTable[i] = SectionHeader.Deserialize(content, ref offset);
|
||||
}
|
||||
|
||||
#region Structured Tables
|
||||
|
||||
// // Export Table
|
||||
// var table = pex.GetLastSection(".edata", true);
|
||||
// if (table != null && table.VirtualSize > 0)
|
||||
// {
|
||||
// int tableAddress = (int)table.PointerToRawData;
|
||||
// pex.ExportTable = ExportDataSection.Deserialize(content, ref tableAddress, pex.SectionTable);
|
||||
// }
|
||||
|
||||
// // Import Table
|
||||
// table = pex.GetSection(".idata", true);
|
||||
// if (table != null && table.VirtualSize > 0)
|
||||
// {
|
||||
// int tableAddress = (int)table.PointerToRawData;
|
||||
// pex.ImportTable = ImportDataSection.Deserialize(content, tableAddress, pex.OptionalHeader.Magic == OptionalHeaderType.PE32Plus, hintCount: 0);
|
||||
// }
|
||||
|
||||
// Resource Table
|
||||
var table = pex.GetLastSection(".rsrc", true);
|
||||
if (table != null && table.VirtualSize > 0)
|
||||
{
|
||||
int tableAddress = (int)table.PointerToRawData;
|
||||
pex.ResourceSection = ResourceSection.Deserialize(content, ref tableAddress, pex.SectionTable);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Freeform Sections
|
||||
|
||||
// Data Section
|
||||
pex.DataSectionRaw = pex.ReadRawSection(content, ".data", force: true, first: false) ?? pex.ReadRawSection(content, "DATA", force: true, first: false);
|
||||
|
||||
// Export Table
|
||||
pex.ExportDataSectionRaw = pex.ReadRawSection(content, ".edata", force: true, first: false);
|
||||
|
||||
// Import Table
|
||||
pex.ImportDataSectionRaw = pex.ReadRawSection(content, ".idata", force: true, first: false);
|
||||
|
||||
// Resource Data Section
|
||||
pex.ResourceDataSectionRaw = pex.ReadRawSection(content, ".rdata", force: true, first: false);
|
||||
|
||||
// Text Section
|
||||
pex.TextSectionRaw = pex.ReadRawSection(content, ".text", force: true, first: false);
|
||||
|
||||
#endregion
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//Console.WriteLine($"Errored out on a file: {ex}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return pex;
|
||||
}
|
||||
}
|
||||
}
|
||||
148
BurnOutSharp/ExecutableType/Microsoft/Resources/FixedFileInfo.cs
Normal file
148
BurnOutSharp/ExecutableType/Microsoft/Resources/FixedFileInfo.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Resources
|
||||
{
|
||||
public class FixedFileInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the value 0xFEEF04BD.
|
||||
/// This is used with the szKey member of the VS_VERSIONINFO structure when searching a file for the VS_FIXEDFILEINFO structure.
|
||||
/// </summary>
|
||||
public uint Signature;
|
||||
|
||||
/// <summary>
|
||||
/// The binary version number of this structure.
|
||||
/// The high-order word of this member contains the major version number, and the low-order word contains the minor version number.
|
||||
/// </summary>
|
||||
public uint StrucVersion;
|
||||
|
||||
/// <summary>
|
||||
/// The most significant 32 bits of the file's binary version number.
|
||||
/// This member is used with dwFileVersionLS to form a 64-bit value used for numeric comparisons.
|
||||
/// </summary>
|
||||
public uint FileVersionMS;
|
||||
|
||||
/// <summary>
|
||||
/// The least significant 32 bits of the file's binary version number.
|
||||
/// This member is used with dwFileVersionMS to form a 64-bit value used for numeric comparisons.
|
||||
/// </summary>
|
||||
public uint FileVersionLS;
|
||||
|
||||
/// <summary>
|
||||
/// The most significant 32 bits of the binary version number of the product with which this file was distributed.
|
||||
/// This member is used with dwProductVersionLS to form a 64-bit value used for numeric comparisons.
|
||||
/// </summary>
|
||||
public uint ProductVersionMS;
|
||||
|
||||
/// <summary>
|
||||
/// The least significant 32 bits of the binary version number of the product with which this file was distributed.
|
||||
/// This member is used with dwProductVersionMS to form a 64-bit value used for numeric comparisons.
|
||||
/// </summary>
|
||||
public uint ProductVersionLS;
|
||||
|
||||
/// <summary>
|
||||
/// Contains a bitmask that specifies the valid bits in dwFileFlags.
|
||||
/// A bit is valid only if it was defined when the file was created.
|
||||
/// </summary>
|
||||
public uint FileFlagsMask;
|
||||
|
||||
/// <summary>
|
||||
/// Contains a bitmask that specifies the Boolean attributes of the file. This member can include one or more of the following values.
|
||||
/// </summary>
|
||||
public FileInfoFileFlags FileFlags;
|
||||
|
||||
/// <summary>
|
||||
/// The operating system for which this file was designed. This member can be one of the following values.
|
||||
///
|
||||
/// An application can combine these values to indicate that the file was designed for one operating system running on another.
|
||||
/// The following dwFileOS values are examples of this, but are not a complete list.
|
||||
/// </summary>
|
||||
public FileInfoOS FileOS;
|
||||
|
||||
/// <summary>
|
||||
/// The general type of file. This member can be one of the following values. All other values are reserved.
|
||||
/// </summary>
|
||||
public FileInfoFileType FileType;
|
||||
|
||||
/// <summary>
|
||||
/// The function of the file. The possible values depend on the value of dwFileType.
|
||||
/// For all values of dwFileType not described in the following list, dwFileSubtype is zero.
|
||||
///
|
||||
/// If dwFileType is VFT_DRV, dwFileSubtype can be one of the following values.
|
||||
///
|
||||
/// If dwFileType is VFT_FONT, dwFileSubtype can be one of the following values.
|
||||
///
|
||||
/// If dwFileType is VFT_VXD, dwFileSubtype contains the virtual device identifier included in the virtual device control block.
|
||||
/// All dwFileSubtype values not listed here are reserved.
|
||||
/// </summary>
|
||||
public FileInfoFileSubtype FileSubtype;
|
||||
|
||||
/// <summary>
|
||||
/// The most significant 32 bits of the file's 64-bit binary creation date and time stamp.
|
||||
/// </summary>
|
||||
public uint FileDateMS;
|
||||
|
||||
/// <summary>
|
||||
/// The least significant 32 bits of the file's 64-bit binary creation date and time stamp.
|
||||
/// </summary>
|
||||
public uint FileDateLS;
|
||||
|
||||
public static FixedFileInfo Deserialize(Stream stream)
|
||||
{
|
||||
FixedFileInfo ffi = new FixedFileInfo();
|
||||
|
||||
ushort temp;
|
||||
while ((temp = stream.ReadUInt16()) == 0x0000);
|
||||
stream.Seek(-2, SeekOrigin.Current);
|
||||
|
||||
ffi.Signature = stream.ReadUInt32();
|
||||
ffi.StrucVersion = stream.ReadUInt32();
|
||||
ffi.FileVersionMS = stream.ReadUInt32();
|
||||
ffi.FileVersionLS = stream.ReadUInt32();
|
||||
ffi.ProductVersionMS = stream.ReadUInt32();
|
||||
ffi.ProductVersionLS = stream.ReadUInt32();
|
||||
ffi.FileFlagsMask = stream.ReadUInt32();
|
||||
ffi.FileFlags = (FileInfoFileFlags)stream.ReadUInt32();
|
||||
ffi.FileOS = (FileInfoOS)stream.ReadUInt32();
|
||||
ffi.FileType = (FileInfoFileType)stream.ReadUInt32();
|
||||
ffi.FileSubtype = (FileInfoFileSubtype)stream.ReadUInt32();
|
||||
ffi.FileDateMS = stream.ReadUInt32();
|
||||
ffi.FileDateLS = stream.ReadUInt32();
|
||||
|
||||
return ffi;
|
||||
}
|
||||
|
||||
public static FixedFileInfo Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
FixedFileInfo ffi = new FixedFileInfo();
|
||||
|
||||
ushort temp;
|
||||
bool padded = false;
|
||||
while ((temp = content.ReadUInt16(ref offset)) == 0x0000)
|
||||
{
|
||||
padded = true;
|
||||
}
|
||||
|
||||
if (padded)
|
||||
offset -= 2;
|
||||
|
||||
ffi.Signature = content.ReadUInt32(ref offset);
|
||||
ffi.StrucVersion = content.ReadUInt32(ref offset);
|
||||
ffi.FileVersionMS = content.ReadUInt32(ref offset);
|
||||
ffi.FileVersionLS = content.ReadUInt32(ref offset);
|
||||
ffi.ProductVersionMS = content.ReadUInt32(ref offset);
|
||||
ffi.ProductVersionLS = content.ReadUInt32(ref offset);
|
||||
ffi.FileFlagsMask = content.ReadUInt32(ref offset);
|
||||
ffi.FileFlags = (FileInfoFileFlags)content.ReadUInt32(ref offset);
|
||||
ffi.FileOS = (FileInfoOS)content.ReadUInt32(ref offset);
|
||||
ffi.FileType = (FileInfoFileType)content.ReadUInt32(ref offset);
|
||||
ffi.FileSubtype = (FileInfoFileSubtype)content.ReadUInt32(ref offset);
|
||||
ffi.FileDateMS = content.ReadUInt32(ref offset);
|
||||
ffi.FileDateLS = content.ReadUInt32(ref offset);
|
||||
|
||||
return ffi;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Resources
|
||||
{
|
||||
/// <summary>
|
||||
/// If you use the Var structure to list the languages your application or DLL supports instead of using multiple version resources,
|
||||
/// use the Value member to contain an array of DWORD values indicating the language and code page combinations supported by this file.
|
||||
/// The low-order word of each DWORD must contain a Microsoft language identifier, and the high-order word must contain the IBM code page number.
|
||||
/// Either high-order or low-order word can be zero, indicating that the file is language or code page independent.
|
||||
/// If the Var structure is omitted, the file will be interpreted as both language and code page independent.
|
||||
/// </summary>
|
||||
public class LanguageCodePage
|
||||
{
|
||||
/// <summary>
|
||||
/// The low-order word of each DWORD must contain a Microsoft language identifier
|
||||
/// </summary>
|
||||
public ushort MicrosoftLanguageIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// The high-order word must contain the IBM code page number
|
||||
/// </summary>
|
||||
public ushort IBMCodePageNumber;
|
||||
|
||||
public static LanguageCodePage Deserialize(Stream stream)
|
||||
{
|
||||
LanguageCodePage lcp = new LanguageCodePage();
|
||||
|
||||
lcp.MicrosoftLanguageIdentifier = stream.ReadUInt16();
|
||||
lcp.IBMCodePageNumber = stream.ReadUInt16();
|
||||
|
||||
return lcp;
|
||||
}
|
||||
|
||||
public static LanguageCodePage Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
LanguageCodePage lcp = new LanguageCodePage();
|
||||
|
||||
lcp.MicrosoftLanguageIdentifier = content.ReadUInt16(ref offset);
|
||||
lcp.IBMCodePageNumber = content.ReadUInt16(ref offset);
|
||||
|
||||
return lcp;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
BurnOutSharp/ExecutableType/Microsoft/Resources/Resource.cs
Normal file
58
BurnOutSharp/ExecutableType/Microsoft/Resources/Resource.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Resources
|
||||
{
|
||||
public class Resource
|
||||
{
|
||||
/// <summary>
|
||||
/// The length, in bytes, of the resource structure.
|
||||
/// This length does not include any padding that aligns any subsequent version resource data on a 32-bit boundary.
|
||||
/// </summary>
|
||||
public ushort Length;
|
||||
|
||||
/// <summary>
|
||||
/// The length, in bytes, of the Value member.
|
||||
/// This value is zero if there is no Value member associated with the current version structure.
|
||||
/// </summary>
|
||||
public ushort ValueLength;
|
||||
|
||||
/// <summary>
|
||||
/// The type of data in the version resource.
|
||||
/// This member is 1 if the version resource contains text data and 0 if the version resource contains binary data.
|
||||
/// </summary>
|
||||
public ushort Type;
|
||||
|
||||
/// <summary>
|
||||
/// A Unicode string representing the key
|
||||
/// </summary>
|
||||
public string Key;
|
||||
|
||||
public static Resource Deserialize(Stream stream)
|
||||
{
|
||||
Resource r = new Resource();
|
||||
|
||||
while ((r.Length = stream.ReadUInt16()) == 0x0000);
|
||||
|
||||
r.ValueLength = stream.ReadUInt16();
|
||||
r.Type = stream.ReadUInt16();
|
||||
r.Key = stream.ReadString(Encoding.Unicode);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public static Resource Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
Resource r = new Resource();
|
||||
|
||||
while ((r.Length = content.ReadUInt16(ref offset)) == 0x0000);
|
||||
|
||||
r.ValueLength = content.ReadUInt16(ref offset);
|
||||
r.Type = content.ReadUInt16(ref offset);
|
||||
r.Key = content.ReadString(ref offset, Encoding.Unicode);
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.IO;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Resources
|
||||
{
|
||||
public class StringFileInfo : Resource
|
||||
{
|
||||
/// <summary>
|
||||
/// An array of one or more StringTable structures.
|
||||
/// Each StringTable structure's szKey member indicates the appropriate language and code page for displaying the text in that StringTable structure.
|
||||
/// </summary>
|
||||
public StringTable Children;
|
||||
|
||||
public StringFileInfo(Resource resource)
|
||||
{
|
||||
this.Length = resource?.Length ?? default;
|
||||
this.ValueLength = resource?.ValueLength ?? default;
|
||||
this.Type = resource?.Type ?? default;
|
||||
this.Key = resource?.Key ?? default;
|
||||
}
|
||||
|
||||
public static new StringFileInfo Deserialize(Stream stream)
|
||||
{
|
||||
Resource resource = Resource.Deserialize(stream);
|
||||
if (resource.Key != "StringFileInfo")
|
||||
return null;
|
||||
|
||||
StringFileInfo sfi = new StringFileInfo(resource);
|
||||
sfi.Children = StringTable.Deserialize(stream);
|
||||
|
||||
return sfi;
|
||||
}
|
||||
|
||||
public static new StringFileInfo Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
Resource resource = Resource.Deserialize(content, ref offset);
|
||||
if (resource.Key != "StringFileInfo")
|
||||
return null;
|
||||
|
||||
StringFileInfo sfi = new StringFileInfo(resource);
|
||||
sfi.Children = StringTable.Deserialize(content, ref offset);
|
||||
|
||||
return sfi;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Resources
|
||||
{
|
||||
public class StringStruct : Resource
|
||||
{
|
||||
/// <summary>
|
||||
/// Typically contains a list of languages that the application or DLL supports.
|
||||
/// </summary>
|
||||
public string Value;
|
||||
|
||||
public StringStruct(Resource resource)
|
||||
{
|
||||
this.Length = resource?.Length ?? default;
|
||||
this.ValueLength = resource?.ValueLength ?? default;
|
||||
this.Type = resource?.Type ?? default;
|
||||
this.Key = resource?.Key ?? default;
|
||||
}
|
||||
|
||||
public static new StringStruct Deserialize(Stream stream)
|
||||
{
|
||||
Resource resource = Resource.Deserialize(stream);
|
||||
StringStruct s = new StringStruct(resource);
|
||||
stream.Seek(stream.Position % 4 == 0 ? 0 : 4 - (stream.Position % 4), SeekOrigin.Current);
|
||||
s.Value = new string(stream.ReadChars(s.ValueLength));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public static new StringStruct Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
Resource resource = Resource.Deserialize(content, ref offset);
|
||||
StringStruct s = new StringStruct(resource);
|
||||
offset += offset % 4 == 0 ? 0 : 4 - (offset % 4);
|
||||
s.Value = Encoding.Unicode.GetString(content, offset, s.ValueLength * 2); offset += s.ValueLength * 2;
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Resources
|
||||
{
|
||||
public class StringTable : Resource
|
||||
{
|
||||
/// <summary>
|
||||
/// An array of one or more String structures.
|
||||
/// </summary>
|
||||
public StringStruct[] Children;
|
||||
|
||||
public StringTable(Resource resource)
|
||||
{
|
||||
this.Length = resource?.Length ?? default;
|
||||
this.ValueLength = resource?.ValueLength ?? default;
|
||||
this.Type = resource?.Type ?? default;
|
||||
this.Key = resource?.Key ?? default;
|
||||
}
|
||||
|
||||
public static new StringTable Deserialize(Stream stream)
|
||||
{
|
||||
long originalPosition = stream.Position;
|
||||
Resource resource = Resource.Deserialize(stream);
|
||||
if (resource.Key.Length != 8)
|
||||
return null;
|
||||
|
||||
StringTable st = new StringTable(resource);
|
||||
|
||||
var tempValue = new List<StringStruct>();
|
||||
while (stream.Position - originalPosition < st.Length)
|
||||
{
|
||||
tempValue.Add(StringStruct.Deserialize(stream));
|
||||
}
|
||||
|
||||
st.Children = tempValue.ToArray();
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
public static new StringTable Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
int originalPosition = offset;
|
||||
Resource resource = Resource.Deserialize(content, ref offset);
|
||||
if (resource.Key.Length != 8)
|
||||
return null;
|
||||
|
||||
StringTable st = new StringTable(resource);
|
||||
|
||||
var tempValue = new List<StringStruct>();
|
||||
while (offset - originalPosition < st.Length)
|
||||
{
|
||||
tempValue.Add(StringStruct.Deserialize(content, ref offset));
|
||||
}
|
||||
|
||||
st.Children = tempValue.ToArray();
|
||||
|
||||
return st;
|
||||
}
|
||||
}
|
||||
}
|
||||
67
BurnOutSharp/ExecutableType/Microsoft/Resources/Var.cs
Normal file
67
BurnOutSharp/ExecutableType/Microsoft/Resources/Var.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Resources
|
||||
{
|
||||
public class Var : Resource
|
||||
{
|
||||
/// <summary>
|
||||
/// An array of one or more values that are language and code page identifier pairs.
|
||||
///
|
||||
/// If you use the Var structure to list the languages your application or DLL supports instead of using multiple version resources,
|
||||
/// use the Value member to contain an array of DWORD values indicating the language and code page combinations supported by this file.
|
||||
/// The low-order word of each DWORD must contain a Microsoft language identifier, and the high-order word must contain the IBM code page number.
|
||||
/// Either high-order or low-order word can be zero, indicating that the file is language or code page independent.
|
||||
/// If the Var structure is omitted, the file will be interpreted as both language and code page independent.
|
||||
/// </summary>
|
||||
public LanguageCodePage[] Value;
|
||||
|
||||
public Var(Resource resource)
|
||||
{
|
||||
this.Length = resource?.Length ?? default;
|
||||
this.ValueLength = resource?.ValueLength ?? default;
|
||||
this.Type = resource?.Type ?? default;
|
||||
this.Key = resource?.Key ?? default;
|
||||
}
|
||||
|
||||
public static new Var Deserialize(Stream stream)
|
||||
{
|
||||
long originalPosition = stream.Position;
|
||||
Resource resource = Resource.Deserialize(stream);
|
||||
if (resource.Key != "Translation")
|
||||
return null;
|
||||
|
||||
Var v = new Var(resource);
|
||||
|
||||
var tempValue = new List<LanguageCodePage>();
|
||||
while (stream.Position - originalPosition < v.Length)
|
||||
{
|
||||
tempValue.Add(LanguageCodePage.Deserialize(stream));
|
||||
}
|
||||
|
||||
v.Value = tempValue.ToArray();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public static new Var Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
int originalPosition = offset;
|
||||
Resource resource = Resource.Deserialize(content, ref offset);
|
||||
if (resource.Key != "Translation")
|
||||
return null;
|
||||
|
||||
Var v = new Var(resource);
|
||||
|
||||
var tempValue = new List<LanguageCodePage>();
|
||||
while (offset - originalPosition < v.Length)
|
||||
{
|
||||
tempValue.Add(LanguageCodePage.Deserialize(content, ref offset));
|
||||
}
|
||||
|
||||
v.Value = tempValue.ToArray();
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.IO;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Resources
|
||||
{
|
||||
public class VarFileInfo : Resource
|
||||
{
|
||||
/// <summary>
|
||||
/// Typically contains a list of languages that the application or DLL supports.
|
||||
/// </summary>
|
||||
public Var Children;
|
||||
|
||||
public VarFileInfo(Resource resource)
|
||||
{
|
||||
this.Length = resource?.Length ?? default;
|
||||
this.ValueLength = resource?.ValueLength ?? default;
|
||||
this.Type = resource?.Type ?? default;
|
||||
this.Key = resource?.Key ?? default;
|
||||
}
|
||||
|
||||
public static new VarFileInfo Deserialize(Stream stream)
|
||||
{
|
||||
Resource resource = Resource.Deserialize(stream);
|
||||
if (resource.Key != "VarFileInfo")
|
||||
return null;
|
||||
|
||||
VarFileInfo vfi = new VarFileInfo(resource);
|
||||
vfi.Children = Var.Deserialize(stream);
|
||||
|
||||
return vfi;
|
||||
}
|
||||
|
||||
public static new VarFileInfo Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
Resource resource = Resource.Deserialize(content, ref offset);
|
||||
if (resource.Key != "VarFileInfo")
|
||||
return null;
|
||||
|
||||
VarFileInfo vfi = new VarFileInfo(resource);
|
||||
vfi.Children = Var.Deserialize(content, ref offset);
|
||||
|
||||
return vfi;
|
||||
}
|
||||
}
|
||||
}
|
||||
129
BurnOutSharp/ExecutableType/Microsoft/Resources/VersionInfo.cs
Normal file
129
BurnOutSharp/ExecutableType/Microsoft/Resources/VersionInfo.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using System.IO;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Resources
|
||||
{
|
||||
public class VersionInfo : Resource
|
||||
{
|
||||
/// <summary>
|
||||
/// Arbitrary data associated with this VS_VERSIONINFO structure.
|
||||
/// The wValueLength member specifies the length of this member;
|
||||
/// if wValueLength is zero, this member does not exist.
|
||||
/// </summary>
|
||||
public FixedFileInfo Value;
|
||||
|
||||
/// <summary>
|
||||
/// An array of zero or one StringFileInfo structures, and zero or one VarFileInfo structures
|
||||
/// that are children of the current VS_VERSIONINFO structure.
|
||||
/// </summary>
|
||||
public StringFileInfo ChildrenStringFileInfo;
|
||||
|
||||
/// <summary>
|
||||
/// An array of zero or one StringFileInfo structures, and zero or one VarFileInfo structures
|
||||
/// that are children of the current VS_VERSIONINFO structure.
|
||||
/// </summary>
|
||||
public VarFileInfo ChildrenVarFileInfo;
|
||||
|
||||
public VersionInfo(Resource resource)
|
||||
{
|
||||
this.Length = resource?.Length ?? default;
|
||||
this.ValueLength = resource?.ValueLength ?? default;
|
||||
this.Type = resource?.Type ?? default;
|
||||
this.Key = resource?.Key ?? default;
|
||||
}
|
||||
|
||||
public static new VersionInfo Deserialize(Stream stream)
|
||||
{
|
||||
long originalPosition = stream.Position;
|
||||
Resource resource = Resource.Deserialize(stream);
|
||||
if (resource.Key != "VS_VERSION_INFO")
|
||||
return null;
|
||||
|
||||
VersionInfo vi = new VersionInfo(resource);
|
||||
|
||||
if (vi.ValueLength > 0)
|
||||
vi.Value = FixedFileInfo.Deserialize(stream);
|
||||
|
||||
if (stream.Position - originalPosition > vi.Length)
|
||||
return vi;
|
||||
|
||||
long preChildOffset = stream.Position;
|
||||
Resource firstChild = Resource.Deserialize(stream);
|
||||
if (firstChild.Key == "StringFileInfo")
|
||||
{
|
||||
stream.Seek(preChildOffset, SeekOrigin.Begin);
|
||||
vi.ChildrenStringFileInfo = StringFileInfo.Deserialize(stream);
|
||||
}
|
||||
else if (firstChild.Key == "VarFileInfo")
|
||||
{
|
||||
stream.Seek(preChildOffset, SeekOrigin.Begin);
|
||||
vi.ChildrenVarFileInfo = VarFileInfo.Deserialize(stream);
|
||||
}
|
||||
|
||||
if (stream.Position - originalPosition > vi.Length)
|
||||
return vi;
|
||||
|
||||
preChildOffset = stream.Position;
|
||||
Resource secondChild = Resource.Deserialize(stream);
|
||||
if (secondChild.Key == "StringFileInfo")
|
||||
{
|
||||
stream.Seek(preChildOffset, SeekOrigin.Begin);
|
||||
vi.ChildrenStringFileInfo = StringFileInfo.Deserialize(stream);
|
||||
}
|
||||
else if (secondChild.Key == "VarFileInfo")
|
||||
{
|
||||
stream.Seek(preChildOffset, SeekOrigin.Begin);
|
||||
vi.ChildrenVarFileInfo = VarFileInfo.Deserialize(stream);
|
||||
}
|
||||
|
||||
return vi;
|
||||
}
|
||||
|
||||
public static new VersionInfo Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
int originalOffset = offset;
|
||||
Resource resource = Resource.Deserialize(content, ref offset);
|
||||
if (resource.Key != "VS_VERSION_INFO")
|
||||
return null;
|
||||
|
||||
VersionInfo vi = new VersionInfo(resource);
|
||||
|
||||
if (vi.ValueLength > 0)
|
||||
vi.Value = FixedFileInfo.Deserialize(content, ref offset);
|
||||
|
||||
if (offset - originalOffset > vi.Length)
|
||||
return vi;
|
||||
|
||||
int preChildOffset = offset;
|
||||
Resource firstChild = Resource.Deserialize(content, ref offset);
|
||||
if (firstChild.Key == "StringFileInfo")
|
||||
{
|
||||
offset = preChildOffset;
|
||||
vi.ChildrenStringFileInfo = StringFileInfo.Deserialize(content, ref offset);
|
||||
}
|
||||
else if (firstChild.Key == "VarFileInfo")
|
||||
{
|
||||
offset = preChildOffset;
|
||||
vi.ChildrenVarFileInfo = VarFileInfo.Deserialize(content, ref offset);
|
||||
}
|
||||
|
||||
if (offset - originalOffset > vi.Length)
|
||||
return vi;
|
||||
|
||||
|
||||
preChildOffset = offset;
|
||||
Resource secondChild = Resource.Deserialize(content, ref offset);
|
||||
if (secondChild.Key == "StringFileInfo")
|
||||
{
|
||||
offset = preChildOffset;
|
||||
vi.ChildrenStringFileInfo = StringFileInfo.Deserialize(content, ref offset);
|
||||
}
|
||||
else if (secondChild.Key == "VarFileInfo")
|
||||
{
|
||||
offset = preChildOffset;
|
||||
vi.ChildrenVarFileInfo = VarFileInfo.Deserialize(content, ref offset);
|
||||
}
|
||||
|
||||
return vi;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Tables;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Sections
|
||||
{
|
||||
/// <summary>
|
||||
/// The .pdata section contains an array of function table entries that are used for exception handling.
|
||||
/// It is pointed to by the exception table entry in the image data directory.
|
||||
/// The entries must be sorted according to the function addresses (the first field in each structure) before being emitted into the final image.
|
||||
/// The target platform determines which of the three function table entry format variations described below is used.
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#the-edata-section-image-only</remarks>
|
||||
public class ExceptionHandlingSection
|
||||
{
|
||||
/// <summary>
|
||||
/// Array of function table entries that are used for exception handling
|
||||
/// </summary>
|
||||
public FunctionTable FunctionTable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Entries;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Headers;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Tables;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Sections
|
||||
{
|
||||
/// <summary>
|
||||
/// The export data section, named .edata, contains information about symbols that other images can access through dynamic linking.
|
||||
/// Exported symbols are generally found in DLLs, but DLLs can also import symbols.
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#the-edata-section-image-only</remarks>
|
||||
public class ExportDataSection
|
||||
{
|
||||
/// <summary>
|
||||
/// A table with just one row (unlike the debug directory).
|
||||
/// This table indicates the locations and sizes of the other export tables.
|
||||
/// </summary>
|
||||
public ExportDirectoryTable ExportDirectoryTable;
|
||||
|
||||
/// <summary>
|
||||
/// An array of RVAs of exported symbols.
|
||||
/// These are the actual addresses of the exported functions and data within the executable code and data sections.
|
||||
/// Other image files can import a symbol by using an index to this table (an ordinal) or, optionally, by using the public name that corresponds to the ordinal if a public name is defined.
|
||||
/// </summary>
|
||||
public ExportAddressTableEntry[] ExportAddressTable;
|
||||
|
||||
/// <summary>
|
||||
/// An array of pointers to the public export names, sorted in ascending order.
|
||||
/// </summary>
|
||||
public uint[] ExportNamePointerTable;
|
||||
|
||||
/// <summary>
|
||||
/// An array of the ordinals that correspond to members of the name pointer table.
|
||||
/// The correspondence is by position; therefore, the name pointer table and the ordinal table must have the same number of members.
|
||||
/// Each ordinal is an index into the export address table.
|
||||
/// </summary>
|
||||
public ExportOrdinalTable OrdinalTable;
|
||||
|
||||
public static ExportDataSection Deserialize(Stream stream, SectionHeader[] sections)
|
||||
{
|
||||
long originalPosition = stream.Position;
|
||||
var eds = new ExportDataSection();
|
||||
|
||||
eds.ExportDirectoryTable = ExportDirectoryTable.Deserialize(stream);
|
||||
|
||||
stream.Seek((int)PortableExecutable.ConvertVirtualAddress(eds.ExportDirectoryTable.ExportAddressTableRVA, sections), SeekOrigin.Begin);
|
||||
eds.ExportAddressTable = new ExportAddressTableEntry[(int)eds.ExportDirectoryTable.AddressTableEntries];
|
||||
for (int i = 0; i < eds.ExportAddressTable.Length; i++)
|
||||
{
|
||||
eds.ExportAddressTable[i] = ExportAddressTableEntry.Deserialize(stream, sections);
|
||||
}
|
||||
|
||||
stream.Seek((int)PortableExecutable.ConvertVirtualAddress(eds.ExportDirectoryTable.NamePointerRVA, sections), SeekOrigin.Begin);
|
||||
eds.ExportNamePointerTable = new uint[(int)eds.ExportDirectoryTable.NumberOfNamePointers];
|
||||
for (int i = 0; i < eds.ExportNamePointerTable.Length; i++)
|
||||
{
|
||||
eds.ExportNamePointerTable[i] = stream.ReadUInt32();
|
||||
}
|
||||
|
||||
stream.Seek((int)PortableExecutable.ConvertVirtualAddress(eds.ExportDirectoryTable.OrdinalTableRVA, sections), SeekOrigin.Begin);
|
||||
// eds.OrdinalTable = ExportOrdinalTable.Deserialize(stream, count: 0); // TODO: Figure out where this count comes from
|
||||
|
||||
return eds;
|
||||
}
|
||||
|
||||
public static ExportDataSection Deserialize(byte[] content, ref int offset, SectionHeader[] sections)
|
||||
{
|
||||
int originalPosition = offset;
|
||||
var eds = new ExportDataSection();
|
||||
|
||||
eds.ExportDirectoryTable = ExportDirectoryTable.Deserialize(content, ref offset);
|
||||
|
||||
offset = (int)PortableExecutable.ConvertVirtualAddress(eds.ExportDirectoryTable.ExportAddressTableRVA, sections);
|
||||
eds.ExportAddressTable = new ExportAddressTableEntry[(int)eds.ExportDirectoryTable.AddressTableEntries];
|
||||
for (int i = 0; i < eds.ExportAddressTable.Length; i++)
|
||||
{
|
||||
eds.ExportAddressTable[i] = ExportAddressTableEntry.Deserialize(content, ref offset, sections);
|
||||
}
|
||||
|
||||
offset = (int)PortableExecutable.ConvertVirtualAddress(eds.ExportDirectoryTable.NamePointerRVA, sections);
|
||||
eds.ExportNamePointerTable = new uint[(int)eds.ExportDirectoryTable.NumberOfNamePointers];
|
||||
for (int i = 0; i < eds.ExportNamePointerTable.Length; i++)
|
||||
{
|
||||
eds.ExportNamePointerTable[i] = content.ReadUInt32(ref offset);
|
||||
}
|
||||
|
||||
offset = (int)PortableExecutable.ConvertVirtualAddress(eds.ExportDirectoryTable.OrdinalTableRVA, sections);
|
||||
// eds.OrdinalTable = ExportOrdinalTable.Deserialize(content, ref offset, count: 0); // TODO: Figure out where this count comes from
|
||||
|
||||
return eds;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Tables;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Sections
|
||||
{
|
||||
/// <summary>
|
||||
/// All image files that import symbols, including virtually all executable (EXE) files, have an .idata section.
|
||||
/// A typical file layout for the import information follows:
|
||||
/// Directory Table
|
||||
/// Null Directory Entry
|
||||
/// DLL1 Import Lookup Table
|
||||
/// Null
|
||||
/// DLL2 Import Lookup Table
|
||||
/// Null
|
||||
/// DLL3 Import Lookup Table
|
||||
/// Null
|
||||
/// Hint-Name Table
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#the-idata-section</remarks>
|
||||
public class ImportDataSection
|
||||
{
|
||||
/// <summary>
|
||||
/// Import directory table
|
||||
/// </summary>
|
||||
public ImportDirectoryTable ImportDirectoryTable;
|
||||
|
||||
/// <summary>
|
||||
/// Import lookup tables
|
||||
/// </summary>
|
||||
public ImportLookupTable[] ImportLookupTables;
|
||||
|
||||
/// <summary>
|
||||
/// Hint/Name table
|
||||
/// </summary>
|
||||
public HintNameTable HintNameTable;
|
||||
|
||||
public static ImportDataSection Deserialize(Stream stream, bool pe32plus, int hintCount)
|
||||
{
|
||||
var ids = new ImportDataSection();
|
||||
|
||||
ids.ImportDirectoryTable = ImportDirectoryTable.Deserialize(stream);
|
||||
|
||||
List<ImportLookupTable> tempLookupTables = new List<ImportLookupTable>();
|
||||
while (true)
|
||||
{
|
||||
var tempLookupTable = ImportLookupTable.Deserialize(stream, pe32plus);
|
||||
if (tempLookupTable.EntriesPE32 == null && tempLookupTable.EntriesPE32Plus == null)
|
||||
break;
|
||||
|
||||
tempLookupTables.Add(tempLookupTable);
|
||||
}
|
||||
|
||||
ids.HintNameTable = HintNameTable.Deserialize(stream, hintCount);
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
public static ImportDataSection Deserialize(byte[] content, ref int offset, bool pe32plus, int hintCount)
|
||||
{
|
||||
var ids = new ImportDataSection();
|
||||
|
||||
ids.ImportDirectoryTable = ImportDirectoryTable.Deserialize(content, ref offset);
|
||||
|
||||
List<ImportLookupTable> tempLookupTables = new List<ImportLookupTable>();
|
||||
while (true)
|
||||
{
|
||||
var tempLookupTable = ImportLookupTable.Deserialize(content, ref offset, pe32plus);
|
||||
if (tempLookupTable.EntriesPE32 == null && tempLookupTable.EntriesPE32Plus == null)
|
||||
break;
|
||||
|
||||
tempLookupTables.Add(tempLookupTable);
|
||||
}
|
||||
|
||||
ids.HintNameTable = HintNameTable.Deserialize(content, ref offset, hintCount);
|
||||
|
||||
return ids;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Headers;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Tables;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Sections
|
||||
{
|
||||
/// <summary>
|
||||
/// A series of resource directory tables relates all of the levels in the following way:
|
||||
// Each directory table is followed by a series of directory entries that give the name or
|
||||
// identifier (ID) for that level (Type, Name, or Language level) and an address of either
|
||||
// a data description or another directory table. If the address points to a data description,
|
||||
// then the data is a leaf in the tree. If the address points to another directory table,
|
||||
// then that table lists directory entries at the next level down
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#the-rsrc-section</remarks>
|
||||
public class ResourceSection
|
||||
{
|
||||
/// <summary>
|
||||
/// A table with just one row (unlike the debug directory).
|
||||
/// This table indicates the locations and sizes of the other export tables.
|
||||
/// </summary>
|
||||
public ResourceDirectoryTable ResourceDirectoryTable;
|
||||
|
||||
public static ResourceSection Deserialize(Stream stream, SectionHeader[] sections)
|
||||
{
|
||||
var rs = new ResourceSection();
|
||||
|
||||
long sectionStart = stream.Position;
|
||||
rs.ResourceDirectoryTable = ResourceDirectoryTable.Deserialize(stream, sectionStart, sections);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
public static ResourceSection Deserialize(byte[] content, ref int offset, SectionHeader[] sections)
|
||||
{
|
||||
var rs = new ResourceSection();
|
||||
|
||||
long sectionStart = offset;
|
||||
rs.ResourceDirectoryTable = ResourceDirectoryTable.Deserialize(content, ref offset, sectionStart, sections);
|
||||
|
||||
return rs;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Tables
|
||||
{
|
||||
/// <summary>
|
||||
/// The export symbol information begins with the export directory table, which describes the remainder of the export symbol information.
|
||||
/// The export directory table contains address information that is used to resolve imports to the entry points within this image.
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-directory-table</remarks>
|
||||
public class ExportDirectoryTable
|
||||
{
|
||||
/// <summary>
|
||||
/// Reserved, must be 0.
|
||||
/// </summary>
|
||||
public uint ExportFlags;
|
||||
|
||||
/// <summary>
|
||||
/// The time and date that the export data was created.
|
||||
/// </summary>
|
||||
public uint TimeDateStamp;
|
||||
|
||||
/// <summary>
|
||||
/// The major version number. The major and minor version numbers can be set by the user.
|
||||
/// </summary>
|
||||
public ushort MajorVersion;
|
||||
|
||||
/// <summary>
|
||||
/// The minor version number.
|
||||
/// </summary>
|
||||
public ushort MinorVersion;
|
||||
|
||||
/// <summary>
|
||||
/// The address of the ASCII string that contains the name of the DLL.
|
||||
/// This address is relative to the image base.
|
||||
/// </summary>
|
||||
public uint NameRVA; // TODO: Read this into a separate field
|
||||
|
||||
/// <summary>
|
||||
/// The starting ordinal number for exports in this image.
|
||||
/// This field specifies the starting ordinal number for the export address table.
|
||||
/// It is usually set to 1.
|
||||
/// </summary>
|
||||
public uint OrdinalBase;
|
||||
|
||||
/// <summary>
|
||||
/// The number of entries in the export address table.
|
||||
/// </summary>
|
||||
public uint AddressTableEntries;
|
||||
|
||||
/// <summary>
|
||||
/// The number of entries in the name pointer table.
|
||||
/// This is also the number of entries in the ordinal table.
|
||||
/// </summary>
|
||||
public uint NumberOfNamePointers;
|
||||
|
||||
/// <summary>
|
||||
/// The address of the export address table, relative to the image base.
|
||||
/// </summary>
|
||||
public uint ExportAddressTableRVA;
|
||||
|
||||
/// <summary>
|
||||
/// The address of the export name pointer table, relative to the image base.
|
||||
/// The table size is given by the Number of Name Pointers field.
|
||||
/// </summary>
|
||||
public uint NamePointerRVA;
|
||||
|
||||
/// <summary>
|
||||
/// The address of the ordinal table, relative to the image base.
|
||||
/// </summary>
|
||||
public uint OrdinalTableRVA;
|
||||
|
||||
public static ExportDirectoryTable Deserialize(Stream stream)
|
||||
{
|
||||
var edt = new ExportDirectoryTable();
|
||||
|
||||
edt.ExportFlags = stream.ReadUInt32();
|
||||
edt.TimeDateStamp = stream.ReadUInt32();
|
||||
edt.MajorVersion = stream.ReadUInt16();
|
||||
edt.MinorVersion = stream.ReadUInt16();
|
||||
edt.NameRVA = stream.ReadUInt32();
|
||||
edt.OrdinalBase = stream.ReadUInt32();
|
||||
edt.AddressTableEntries = stream.ReadUInt32();
|
||||
edt.NumberOfNamePointers = stream.ReadUInt32();
|
||||
edt.ExportAddressTableRVA = stream.ReadUInt32();
|
||||
edt.NamePointerRVA = stream.ReadUInt32();
|
||||
edt.OrdinalTableRVA = stream.ReadUInt32();
|
||||
|
||||
return edt;
|
||||
}
|
||||
|
||||
public static ExportDirectoryTable Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var edt = new ExportDirectoryTable();
|
||||
|
||||
edt.ExportFlags = content.ReadUInt32(ref offset);
|
||||
edt.TimeDateStamp = content.ReadUInt32(ref offset);
|
||||
edt.MajorVersion = content.ReadUInt16(ref offset);
|
||||
edt.MinorVersion = content.ReadUInt16(ref offset);
|
||||
edt.NameRVA = content.ReadUInt32(ref offset);
|
||||
edt.OrdinalBase = content.ReadUInt32(ref offset);
|
||||
edt.AddressTableEntries = content.ReadUInt32(ref offset);
|
||||
edt.NumberOfNamePointers = content.ReadUInt32(ref offset);
|
||||
edt.ExportAddressTableRVA = content.ReadUInt32(ref offset);
|
||||
edt.NamePointerRVA = content.ReadUInt32(ref offset);
|
||||
edt.OrdinalTableRVA = content.ReadUInt32(ref offset);
|
||||
|
||||
return edt;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Tables
|
||||
{
|
||||
/// <summary>
|
||||
/// The export ordinal table is an array of 16-bit unbiased indexes into the export address table.
|
||||
/// Ordinals are biased by the Ordinal Base field of the export directory table.
|
||||
/// In other words, the ordinal base must be subtracted from the ordinals to obtain true indexes into the export address table.
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-ordinal-table</remarks>
|
||||
public class ExportOrdinalTable
|
||||
{
|
||||
/// <remarks>Number of entries is defined externally</remarks>
|
||||
public ushort[] Entries;
|
||||
|
||||
public static ExportOrdinalTable Deserialize(Stream stream, int count)
|
||||
{
|
||||
var edt = new ExportOrdinalTable();
|
||||
|
||||
edt.Entries = new ushort[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
edt.Entries[i] = stream.ReadUInt16();
|
||||
}
|
||||
|
||||
return edt;
|
||||
}
|
||||
|
||||
public static ExportOrdinalTable Deserialize(byte[] content, ref int offset, int count)
|
||||
{
|
||||
var edt = new ExportOrdinalTable();
|
||||
|
||||
edt.Entries = new ushort[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
edt.Entries[i] = content.ReadUInt16(ref offset);
|
||||
}
|
||||
|
||||
return edt;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Entries;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Tables
|
||||
{
|
||||
/// <summary>
|
||||
/// The .pdata section contains an array of function table entries that are used for exception handling.
|
||||
/// It is pointed to by the exception table entry in the image data directory.
|
||||
/// The entries must be sorted according to the function addresses (the first field in each structure) before being emitted into the final image.
|
||||
/// The target platform determines which of the three function table entry format variations described below is used.
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#the-pdata-section</remarks>
|
||||
public class FunctionTable
|
||||
{
|
||||
/// <remarks>Number of entries is defined externally</remarks>
|
||||
public FunctionTableEntry[] Entries;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Entries;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Tables
|
||||
{
|
||||
/// <summary>
|
||||
/// One hint/name table suffices for the entire import section.
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#hintname-table</remarks>
|
||||
public class HintNameTable
|
||||
{
|
||||
/// <remarks>Number of entries is defined externally</remarks>
|
||||
public HintNameTableEntry[] Entries;
|
||||
|
||||
public static HintNameTable Deserialize(Stream stream, int count)
|
||||
{
|
||||
var hnt = new HintNameTable();
|
||||
|
||||
hnt.Entries = new HintNameTableEntry[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
hnt.Entries[i] = HintNameTableEntry.Deserialize(stream);
|
||||
}
|
||||
|
||||
return hnt;
|
||||
}
|
||||
|
||||
public static HintNameTable Deserialize(byte[] content, ref int offset, int count)
|
||||
{
|
||||
var hnt = new HintNameTable();
|
||||
|
||||
hnt.Entries = new HintNameTableEntry[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
hnt.Entries[i] = HintNameTableEntry.Deserialize(content, ref offset);
|
||||
offset += 2 + hnt.Entries[i].Name.Length + hnt.Entries[i].Pad;
|
||||
}
|
||||
|
||||
return hnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Entries;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Tables
|
||||
{
|
||||
/// <summary>
|
||||
/// The structure and content of the import address table are identical to those of the import lookup table, until the file is bound.
|
||||
/// During binding, the entries in the import address table are overwritten with the 32-bit (for PE32) or 64-bit (for PE32+) addresses of the symbols that are being imported.
|
||||
/// These addresses are the actual memory addresses of the symbols, although technically they are still called "virtual addresses."
|
||||
/// The loader typically processes the binding.
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-address-table</remarks>
|
||||
public class ImportAddressTable
|
||||
{
|
||||
/// <remarks>Number of entries is known after parsing</remarks>
|
||||
public ImportAddressTableEntry[] Entries;
|
||||
|
||||
public static ImportAddressTable Deserialize(Stream stream)
|
||||
{
|
||||
var iat = new ImportAddressTable();
|
||||
|
||||
List<ImportAddressTableEntry> tempEntries = new List<ImportAddressTableEntry>();
|
||||
while (true)
|
||||
{
|
||||
var entry = ImportAddressTableEntry.Deserialize(stream);
|
||||
tempEntries.Add(entry);
|
||||
if (entry.IsNull())
|
||||
break;
|
||||
}
|
||||
|
||||
iat.Entries = tempEntries.ToArray();
|
||||
return iat;
|
||||
}
|
||||
|
||||
public static ImportAddressTable Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var iat = new ImportAddressTable();
|
||||
|
||||
List<ImportAddressTableEntry> tempEntries = new List<ImportAddressTableEntry>();
|
||||
while (true)
|
||||
{
|
||||
var entry = ImportAddressTableEntry.Deserialize(content, ref offset);
|
||||
tempEntries.Add(entry);
|
||||
if (entry.IsNull())
|
||||
break;
|
||||
}
|
||||
|
||||
iat.Entries = tempEntries.ToArray();
|
||||
return iat;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Entries;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Tables
|
||||
{
|
||||
/// <summary>
|
||||
/// The import information begins with the import directory table, which describes the remainder of the import information.
|
||||
/// The import directory table contains address information that is used to resolve fixup references to the entry points within a DLL image.
|
||||
/// The import directory table consists of an array of import directory entries, one entry for each DLL to which the image refers.
|
||||
/// The last directory entry is empty (filled with null values), which indicates the end of the directory table.
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-directory-table</remarks>
|
||||
public class ImportDirectoryTable
|
||||
{
|
||||
/// <remarks>Number of entries is known after parsing</remarks>
|
||||
public ImportDirectoryTableEntry[] Entries;
|
||||
|
||||
public static ImportDirectoryTable Deserialize(Stream stream)
|
||||
{
|
||||
var idt = new ImportDirectoryTable();
|
||||
|
||||
List<ImportDirectoryTableEntry> tempEntries = new List<ImportDirectoryTableEntry>();
|
||||
while (true)
|
||||
{
|
||||
var entry = ImportDirectoryTableEntry.Deserialize(stream);
|
||||
tempEntries.Add(entry);
|
||||
if (entry.IsNull())
|
||||
break;
|
||||
}
|
||||
|
||||
idt.Entries = tempEntries.ToArray();
|
||||
return idt;
|
||||
}
|
||||
|
||||
public static ImportDirectoryTable Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var idt = new ImportDirectoryTable();
|
||||
|
||||
List<ImportDirectoryTableEntry> tempEntries = new List<ImportDirectoryTableEntry>();
|
||||
while (true)
|
||||
{
|
||||
var entry = ImportDirectoryTableEntry.Deserialize(content, ref offset);
|
||||
tempEntries.Add(entry);
|
||||
if (entry.IsNull())
|
||||
break;
|
||||
}
|
||||
|
||||
idt.Entries = tempEntries.ToArray();
|
||||
return idt;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Tables
|
||||
{
|
||||
/// <summary>
|
||||
/// An import lookup table is an array of 32-bit numbers for PE32 or an array of 64-bit numbers for PE32+.
|
||||
/// Each entry uses the bit-field format that is described in the following table.
|
||||
/// In this format, bit 31 is the most significant bit for PE32 and bit 63 is the most significant bit for PE32+.
|
||||
/// The collection of these entries describes all imports from a given DLL.
|
||||
/// The last entry is set to zero (NULL) to indicate the end of the table.
|
||||
/// </summary>
|
||||
/// <remarks>https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-lookup-table</remarks>
|
||||
public class ImportLookupTable
|
||||
{
|
||||
/// <remarks>Number of entries is known after parsing</remarks>
|
||||
public uint[] EntriesPE32;
|
||||
|
||||
/// <remarks>Number of entries is known after parsing</remarks>
|
||||
public ulong[] EntriesPE32Plus;
|
||||
|
||||
public static ImportLookupTable Deserialize(Stream stream, bool pe32plus)
|
||||
{
|
||||
var ilt = new ImportLookupTable();
|
||||
|
||||
// PE32+ has 8-byte values
|
||||
if (pe32plus)
|
||||
{
|
||||
List<ulong> tempEntries = new List<ulong>();
|
||||
while (true)
|
||||
{
|
||||
ulong bitfield = stream.ReadUInt64();
|
||||
tempEntries.Add(bitfield);
|
||||
if (bitfield == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (tempEntries.Count > 0)
|
||||
ilt.EntriesPE32Plus = tempEntries.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
List<uint> tempEntries = new List<uint>();
|
||||
while (true)
|
||||
{
|
||||
uint bitfield = stream.ReadUInt32();
|
||||
tempEntries.Add(bitfield);
|
||||
if (bitfield == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (tempEntries.Count > 0)
|
||||
ilt.EntriesPE32 = tempEntries.ToArray();
|
||||
}
|
||||
|
||||
return ilt;
|
||||
}
|
||||
|
||||
public static ImportLookupTable Deserialize(byte[] content, ref int offset, bool pe32plus)
|
||||
{
|
||||
var ilt = new ImportLookupTable();
|
||||
|
||||
// PE32+ has 8-byte values
|
||||
if (pe32plus)
|
||||
{
|
||||
List<ulong> tempEntries = new List<ulong>();
|
||||
while (true)
|
||||
{
|
||||
ulong bitfield = content.ReadUInt64(ref offset);
|
||||
tempEntries.Add(bitfield);
|
||||
if (bitfield == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (tempEntries.Count > 0)
|
||||
ilt.EntriesPE32Plus = tempEntries.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
List<uint> tempEntries = new List<uint>();
|
||||
while (true)
|
||||
{
|
||||
uint bitfield = content.ReadUInt32(ref offset);
|
||||
tempEntries.Add(bitfield);
|
||||
if (bitfield == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (tempEntries.Count > 0)
|
||||
ilt.EntriesPE32 = tempEntries.ToArray();
|
||||
}
|
||||
|
||||
return ilt;
|
||||
}
|
||||
}
|
||||
}
|
||||
102
BurnOutSharp/ExecutableType/Microsoft/Tables/NEResourceTable.cs
Normal file
102
BurnOutSharp/ExecutableType/Microsoft/Tables/NEResourceTable.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Entries;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Tables
|
||||
{
|
||||
/// <summary>
|
||||
/// The resource table follows the segment table and contains entries for
|
||||
/// each resource in the executable file. The resource table consists of
|
||||
/// an alignment shift count, followed by a table of resource records. The
|
||||
/// resource records define the type ID for a set of resources. Each
|
||||
/// resource record contains a table of resource entries of the defined
|
||||
/// type. The resource entry defines the resource ID or name ID for the
|
||||
/// resource. It also defines the location and size of the resource.
|
||||
/// </summary>
|
||||
/// <remarks>http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm</remarks>
|
||||
public class NEResourceTable
|
||||
{
|
||||
/// <summary>
|
||||
/// Alignment shift count for resource data.
|
||||
/// </summary>
|
||||
public ushort AlignmentShiftCount;
|
||||
|
||||
/// <summary>
|
||||
/// A table of resource type information blocks.
|
||||
/// </summary>
|
||||
public ResourceTypeInformationBlock[] TypeInformationBlocks;
|
||||
|
||||
/// <summary>
|
||||
/// Resource type and name strings are stored at the end of the
|
||||
/// resource table. Note that these strings are NOT null terminated and
|
||||
/// are case sensitive.
|
||||
/// </summary>
|
||||
public NEResourceNameString[] TypeAndNameStrings;
|
||||
|
||||
public static NEResourceTable Deserialize(Stream stream)
|
||||
{
|
||||
var nrt = new NEResourceTable();
|
||||
|
||||
nrt.AlignmentShiftCount = stream.ReadUInt16();
|
||||
var typeInformationBlocks = new List<ResourceTypeInformationBlock>();
|
||||
while (true)
|
||||
{
|
||||
var block = ResourceTypeInformationBlock.Deserialize(stream);
|
||||
if (block.TypeID == 0)
|
||||
break;
|
||||
|
||||
typeInformationBlocks.Add(block);
|
||||
}
|
||||
|
||||
nrt.TypeInformationBlocks = typeInformationBlocks.ToArray();
|
||||
|
||||
var typeAndNameStrings = new List<NEResourceNameString>();
|
||||
while (true)
|
||||
{
|
||||
var str = NEResourceNameString.Deserialize(stream);
|
||||
if (str.Length == 0)
|
||||
break;
|
||||
|
||||
typeAndNameStrings.Add(str);
|
||||
}
|
||||
|
||||
nrt.TypeAndNameStrings = typeAndNameStrings.ToArray();
|
||||
|
||||
return nrt;
|
||||
}
|
||||
|
||||
public static NEResourceTable Deserialize(byte[] content, ref int offset)
|
||||
{
|
||||
var nrt = new NEResourceTable();
|
||||
|
||||
nrt.AlignmentShiftCount = content.ReadUInt16(ref offset);
|
||||
var typeInformationBlocks = new List<ResourceTypeInformationBlock>();
|
||||
while (true)
|
||||
{
|
||||
var block = ResourceTypeInformationBlock.Deserialize(content, ref offset);
|
||||
if (block.TypeID == 0)
|
||||
break;
|
||||
|
||||
typeInformationBlocks.Add(block);
|
||||
}
|
||||
|
||||
nrt.TypeInformationBlocks = typeInformationBlocks.ToArray();
|
||||
|
||||
var typeAndNameStrings = new List<NEResourceNameString>();
|
||||
while (true)
|
||||
{
|
||||
var str = NEResourceNameString.Deserialize(content, ref offset);
|
||||
if (str.Length == 0)
|
||||
break;
|
||||
|
||||
typeAndNameStrings.Add(str);
|
||||
}
|
||||
|
||||
nrt.TypeAndNameStrings = typeAndNameStrings.ToArray();
|
||||
|
||||
return nrt;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Entries;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.Headers;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.ExecutableType.Microsoft.Tables
|
||||
{
|
||||
/// <summary>
|
||||
/// Each resource directory table has the following format.
|
||||
/// This data structure should be considered the heading of a table
|
||||
/// because the table actually consists of directory entries and this structure
|
||||
/// </summary>
|
||||
public class ResourceDirectoryTable
|
||||
{
|
||||
/// <summary>
|
||||
/// Resource flags.
|
||||
/// This field is reserved for future use.
|
||||
/// It is currently set to zero.
|
||||
/// </summary>
|
||||
public uint Characteristics;
|
||||
|
||||
/// <summary>
|
||||
/// The time that the resource data was created by the resource compiler.
|
||||
/// </summary>
|
||||
public uint TimeDateStamp;
|
||||
|
||||
/// <summary>
|
||||
/// The major version number, set by the user.
|
||||
/// </summary>
|
||||
public ushort MajorVersion;
|
||||
|
||||
/// <summary>
|
||||
/// The minor version number, set by the user.
|
||||
/// </summary>
|
||||
public ushort MinorVersion;
|
||||
|
||||
/// <summary>
|
||||
/// The number of directory entries immediately following
|
||||
/// the table that use strings to identify Type, Name, or
|
||||
/// Language entries (depending on the level of the table).
|
||||
/// </summary>
|
||||
public ushort NumberOfNamedEntries;
|
||||
|
||||
/// <summary>
|
||||
/// The number of directory entries immediately following
|
||||
/// the Name entries that use numeric IDs for Type, Name,
|
||||
/// or Language entries.
|
||||
/// </summary>
|
||||
public ushort NumberOfIdEntries;
|
||||
|
||||
/// <summary>
|
||||
/// The directory entries immediately following
|
||||
/// the table that use strings to identify Type, Name, or
|
||||
/// Language entries (depending on the level of the table).
|
||||
/// </summary>
|
||||
public ResourceDirectoryTableEntry[] NamedEntries;
|
||||
|
||||
/// <summary>
|
||||
/// The directory entries immediately following
|
||||
/// the Name entries that use numeric IDs for Type, Name,
|
||||
/// or Language entries.
|
||||
/// </summary>
|
||||
public ResourceDirectoryTableEntry[] IdEntries;
|
||||
|
||||
public static ResourceDirectoryTable Deserialize(Stream stream, long sectionStart, SectionHeader[] sections)
|
||||
{
|
||||
var rdt = new ResourceDirectoryTable();
|
||||
|
||||
rdt.Characteristics = stream.ReadUInt32();
|
||||
rdt.TimeDateStamp = stream.ReadUInt32();
|
||||
rdt.MajorVersion = stream.ReadUInt16();
|
||||
rdt.MinorVersion = stream.ReadUInt16();
|
||||
rdt.NumberOfNamedEntries = stream.ReadUInt16();
|
||||
rdt.NumberOfIdEntries = stream.ReadUInt16();
|
||||
|
||||
rdt.NamedEntries = new ResourceDirectoryTableEntry[rdt.NumberOfNamedEntries];
|
||||
for (int i = 0; i < rdt.NumberOfNamedEntries; i++)
|
||||
{
|
||||
rdt.NamedEntries[i] = ResourceDirectoryTableEntry.Deserialize(stream, sectionStart, sections);
|
||||
}
|
||||
|
||||
rdt.IdEntries = new ResourceDirectoryTableEntry[rdt.NumberOfIdEntries];
|
||||
for (int i = 0; i < rdt.NumberOfIdEntries; i++)
|
||||
{
|
||||
rdt.IdEntries[i] = ResourceDirectoryTableEntry.Deserialize(stream, sectionStart, sections);
|
||||
}
|
||||
|
||||
return rdt;
|
||||
}
|
||||
|
||||
public static ResourceDirectoryTable Deserialize(byte[] content, ref int offset, long sectionStart, SectionHeader[] sections)
|
||||
{
|
||||
var rdt = new ResourceDirectoryTable();
|
||||
|
||||
rdt.Characteristics = content.ReadUInt32(ref offset);
|
||||
rdt.TimeDateStamp = content.ReadUInt32(ref offset);
|
||||
rdt.MajorVersion = content.ReadUInt16(ref offset);
|
||||
rdt.MinorVersion = content.ReadUInt16(ref offset);
|
||||
rdt.NumberOfNamedEntries = content.ReadUInt16(ref offset);
|
||||
rdt.NumberOfIdEntries = content.ReadUInt16(ref offset);
|
||||
|
||||
rdt.NamedEntries = new ResourceDirectoryTableEntry[rdt.NumberOfNamedEntries];
|
||||
for (int i = 0; i < rdt.NumberOfNamedEntries; i++)
|
||||
{
|
||||
rdt.NamedEntries[i] = ResourceDirectoryTableEntry.Deserialize(content, ref offset, sectionStart, sections);
|
||||
}
|
||||
|
||||
rdt.IdEntries = new ResourceDirectoryTableEntry[rdt.NumberOfIdEntries];
|
||||
for (int i = 0; i < rdt.NumberOfIdEntries; i++)
|
||||
{
|
||||
rdt.IdEntries[i] = ResourceDirectoryTableEntry.Deserialize(content, ref offset, sectionStart, sections);
|
||||
}
|
||||
|
||||
return rdt;
|
||||
}
|
||||
}
|
||||
}
|
||||
1663
BurnOutSharp/External/HLLib/HLExtract.Net/HLLib.cs
vendored
1663
BurnOutSharp/External/HLLib/HLExtract.Net/HLLib.cs
vendored
File diff suppressed because it is too large
Load Diff
1158
BurnOutSharp/External/HLLib/HLExtract.Net/Program.cs
vendored
1158
BurnOutSharp/External/HLLib/HLExtract.Net/Program.cs
vendored
File diff suppressed because it is too large
Load Diff
1
BurnOutSharp/External/LessIO
vendored
Submodule
1
BurnOutSharp/External/LessIO
vendored
Submodule
Submodule BurnOutSharp/External/LessIO added at 1def7d19cc
414
BurnOutSharp/External/StormLibSharp/MpqArchive.cs
vendored
414
BurnOutSharp/External/StormLibSharp/MpqArchive.cs
vendored
@@ -1,414 +0,0 @@
|
||||
using StormLibSharp.Native;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace StormLibSharp
|
||||
{
|
||||
public class MpqArchive : IDisposable
|
||||
{
|
||||
private MpqArchiveSafeHandle _handle;
|
||||
private List<MpqFileStream> _openFiles = new List<MpqFileStream>();
|
||||
private FileAccess _accessType;
|
||||
private List<MpqArchiveCompactingEventHandler> _compactCallbacks = new List<MpqArchiveCompactingEventHandler>();
|
||||
private SFILE_COMPACT_CALLBACK _compactCallback;
|
||||
|
||||
#region Constructors / Factories
|
||||
public MpqArchive(string filePath, FileAccess accessType)
|
||||
{
|
||||
_accessType = accessType;
|
||||
SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsFile;
|
||||
if (accessType == FileAccess.Read)
|
||||
flags |= SFileOpenArchiveFlags.AccessReadOnly;
|
||||
else
|
||||
flags |= SFileOpenArchiveFlags.AccessReadWriteShare;
|
||||
|
||||
// constant 2 = SFILE_OPEN_HARD_DISK_FILE
|
||||
if (!NativeMethods.SFileOpenArchive(filePath, 2, flags, out _handle))
|
||||
throw new Win32Exception(); // Implicitly calls GetLastError
|
||||
}
|
||||
|
||||
public MpqArchive(MemoryMappedFile file, FileAccess accessType)
|
||||
{
|
||||
_accessType = accessType;
|
||||
string fileName = Win32Methods.GetFileNameOfMemoryMappedFile(file);
|
||||
if (fileName == null)
|
||||
throw new ArgumentException("Could not retrieve the name of the file to initialize.");
|
||||
|
||||
SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsMemoryMapped;
|
||||
if (accessType == FileAccess.Read)
|
||||
flags |= SFileOpenArchiveFlags.AccessReadOnly;
|
||||
else
|
||||
flags |= SFileOpenArchiveFlags.AccessReadWriteShare;
|
||||
|
||||
// constant 2 = SFILE_OPEN_HARD_DISK_FILE
|
||||
if (!NativeMethods.SFileOpenArchive(fileName, 2, flags, out _handle))
|
||||
throw new Win32Exception(); // Implicitly calls GetLastError
|
||||
}
|
||||
|
||||
private MpqArchive(string filePath, MpqArchiveVersion version, MpqFileStreamAttributes listfileAttributes, MpqFileStreamAttributes attributesFileAttributes, int maxFileCount)
|
||||
{
|
||||
if (maxFileCount < 0)
|
||||
throw new ArgumentException("maxFileCount");
|
||||
|
||||
SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsFile | SFileOpenArchiveFlags.AccessReadWriteShare;
|
||||
flags |= (SFileOpenArchiveFlags)version;
|
||||
|
||||
//SFILE_CREATE_MPQ create = new SFILE_CREATE_MPQ()
|
||||
//{
|
||||
// cbSize = unchecked((uint)Marshal.SizeOf(typeof(SFILE_CREATE_MPQ))),
|
||||
// dwMaxFileCount = unchecked((uint)maxFileCount),
|
||||
// dwMpqVersion = (uint)version,
|
||||
// dwFileFlags1 = (uint)listfileAttributes,
|
||||
// dwFileFlags2 = (uint)attributesFileAttributes,
|
||||
// dwStreamFlags = (uint)flags,
|
||||
//};
|
||||
|
||||
//if (!NativeMethods.SFileCreateArchive2(filePath, ref create, out _handle))
|
||||
// throw new Win32Exception();
|
||||
if (!NativeMethods.SFileCreateArchive(filePath, (uint)flags, int.MaxValue, out _handle))
|
||||
throw new Win32Exception();
|
||||
}
|
||||
|
||||
public static MpqArchive CreateNew(string mpqPath, MpqArchiveVersion version)
|
||||
{
|
||||
return CreateNew(mpqPath, version, MpqFileStreamAttributes.None, MpqFileStreamAttributes.None, int.MaxValue);
|
||||
}
|
||||
|
||||
public static MpqArchive CreateNew(string mpqPath, MpqArchiveVersion version, MpqFileStreamAttributes listfileAttributes,
|
||||
MpqFileStreamAttributes attributesFileAttributes, int maxFileCount)
|
||||
{
|
||||
return new MpqArchive(mpqPath, version, listfileAttributes, attributesFileAttributes, maxFileCount);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
// TODO: Move to common location.
|
||||
// This is a global setting, not per-archive setting.
|
||||
|
||||
//public int Locale
|
||||
//{
|
||||
// get
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
// set
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
//}
|
||||
|
||||
public long MaxFileCount
|
||||
{
|
||||
get
|
||||
{
|
||||
VerifyHandle();
|
||||
return NativeMethods.SFileGetMaxFileCount(_handle);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < 0 || value > uint.MaxValue)
|
||||
throw new ArgumentException("value");
|
||||
VerifyHandle();
|
||||
|
||||
if (!NativeMethods.SFileSetMaxFileCount(_handle, unchecked((uint)value)))
|
||||
throw new Win32Exception();
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifyHandle()
|
||||
{
|
||||
if (_handle == null || _handle.IsInvalid)
|
||||
throw new ObjectDisposedException("MpqArchive");
|
||||
}
|
||||
|
||||
public bool IsPatchedArchive
|
||||
{
|
||||
get
|
||||
{
|
||||
VerifyHandle();
|
||||
return NativeMethods.SFileIsPatchedArchive(_handle);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
VerifyHandle();
|
||||
if (!NativeMethods.SFileFlushArchive(_handle))
|
||||
throw new Win32Exception();
|
||||
}
|
||||
|
||||
public int AddListFile(string listfileContents)
|
||||
{
|
||||
VerifyHandle();
|
||||
return NativeMethods.SFileAddListFile(_handle, listfileContents);
|
||||
}
|
||||
|
||||
public void AddFileFromDisk(string filePath, string archiveName)
|
||||
{
|
||||
VerifyHandle();
|
||||
|
||||
if (!NativeMethods.SFileAddFile(_handle, filePath, archiveName, 0))
|
||||
throw new Win32Exception();
|
||||
}
|
||||
|
||||
public void Compact(string listfile)
|
||||
{
|
||||
VerifyHandle();
|
||||
if (!NativeMethods.SFileCompactArchive(_handle, listfile, false))
|
||||
throw new Win32Exception();
|
||||
}
|
||||
|
||||
private void _OnCompact(IntPtr pvUserData, uint dwWorkType, ulong bytesProcessed, ulong totalBytes)
|
||||
{
|
||||
MpqArchiveCompactingEventArgs args = new MpqArchiveCompactingEventArgs(dwWorkType, bytesProcessed, totalBytes);
|
||||
OnCompacting(args);
|
||||
}
|
||||
|
||||
protected virtual void OnCompacting(MpqArchiveCompactingEventArgs e)
|
||||
{
|
||||
foreach (var cb in _compactCallbacks)
|
||||
{
|
||||
cb(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public event MpqArchiveCompactingEventHandler Compacting
|
||||
{
|
||||
add
|
||||
{
|
||||
VerifyHandle();
|
||||
_compactCallback = _OnCompact;
|
||||
if (!NativeMethods.SFileSetCompactCallback(_handle, _compactCallback, IntPtr.Zero))
|
||||
throw new Win32Exception();
|
||||
|
||||
_compactCallbacks.Add(value);
|
||||
}
|
||||
remove
|
||||
{
|
||||
_compactCallbacks.Remove(value);
|
||||
|
||||
VerifyHandle();
|
||||
if (_compactCallbacks.Count == 0)
|
||||
{
|
||||
if (!NativeMethods.SFileSetCompactCallback(_handle, null, IntPtr.Zero))
|
||||
{
|
||||
// Don't do anything here. Remove shouldn't fail hard.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Determine if SFileGetAttributes/SFileSetAttributes/SFileUpdateFileAttributes deserves a projection.
|
||||
// It's unclear - these seem to affect the (attributes) file but I can't figure out exactly what that means.
|
||||
|
||||
public void AddPatchArchive(string patchPath)
|
||||
{
|
||||
VerifyHandle();
|
||||
|
||||
if (!NativeMethods.SFileOpenPatchArchive(_handle, patchPath, null, 0))
|
||||
throw new Win32Exception();
|
||||
}
|
||||
|
||||
public void AddPatchArchives(IEnumerable<string> patchPaths)
|
||||
{
|
||||
if (patchPaths == null)
|
||||
throw new ArgumentNullException("patchPaths");
|
||||
|
||||
VerifyHandle();
|
||||
|
||||
foreach (string path in patchPaths)
|
||||
{
|
||||
// Don't sublet to AddPatchArchive to avoid having to repeatedly call VerifyHandle()
|
||||
if (!NativeMethods.SFileOpenPatchArchive(_handle, path, null, 0))
|
||||
throw new Win32Exception();
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasFile(string fileToFind)
|
||||
{
|
||||
VerifyHandle();
|
||||
|
||||
return NativeMethods.SFileHasFile(_handle, fileToFind);
|
||||
}
|
||||
|
||||
public MpqFileStream OpenFile(string fileName)
|
||||
{
|
||||
VerifyHandle();
|
||||
|
||||
MpqFileSafeHandle fileHandle;
|
||||
if (!NativeMethods.SFileOpenFileEx(_handle, fileName, 0, out fileHandle))
|
||||
throw new Win32Exception();
|
||||
|
||||
MpqFileStream fs = new MpqFileStream(fileHandle, _accessType, this);
|
||||
_openFiles.Add(fs);
|
||||
return fs;
|
||||
}
|
||||
|
||||
public void ExtractFile(string fileToExtract, string destinationPath)
|
||||
{
|
||||
VerifyHandle();
|
||||
|
||||
if (!NativeMethods.SFileExtractFile(_handle, fileToExtract, destinationPath, 0))
|
||||
throw new Win32Exception();
|
||||
}
|
||||
|
||||
public MpqFileVerificationResults VerifyFile(string fileToVerify)
|
||||
{
|
||||
VerifyHandle();
|
||||
|
||||
return (MpqFileVerificationResults)NativeMethods.SFileVerifyFile(_handle, fileToVerify, 0);
|
||||
}
|
||||
|
||||
// TODO: Consider SFileVerifyRawData
|
||||
|
||||
public MpqArchiveVerificationResult VerifyArchive()
|
||||
{
|
||||
VerifyHandle();
|
||||
|
||||
return (MpqArchiveVerificationResult)NativeMethods.SFileVerifyArchive(_handle);
|
||||
}
|
||||
|
||||
|
||||
#region IDisposable implementation
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
~MpqArchive()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Release owned files first.
|
||||
if (_openFiles != null)
|
||||
{
|
||||
foreach (var file in _openFiles)
|
||||
{
|
||||
file.Dispose();
|
||||
}
|
||||
|
||||
_openFiles.Clear();
|
||||
_openFiles = null;
|
||||
}
|
||||
|
||||
// Release
|
||||
if (_handle != null && !_handle.IsInvalid)
|
||||
{
|
||||
_handle.Close();
|
||||
_handle = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void RemoveOwnedFile(MpqFileStream file)
|
||||
{
|
||||
_openFiles.Remove(file);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public enum MpqArchiveVersion
|
||||
{
|
||||
Version1 = 0,
|
||||
Version2 = 0x01000000,
|
||||
Version3 = 0x02000000,
|
||||
Version4 = 0x03000000,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum MpqFileStreamAttributes
|
||||
{
|
||||
None = 0x0,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum MpqFileVerificationResults
|
||||
{
|
||||
/// <summary>
|
||||
/// There were no errors with the file.
|
||||
/// </summary>
|
||||
Verified = 0,
|
||||
/// <summary>
|
||||
/// Failed to open the file
|
||||
/// </summary>
|
||||
Error = 0x1,
|
||||
/// <summary>
|
||||
/// Failed to read all data from the file
|
||||
/// </summary>
|
||||
ReadError = 0x2,
|
||||
/// <summary>
|
||||
/// File has sector CRC
|
||||
/// </summary>
|
||||
HasSectorCrc = 0x4,
|
||||
/// <summary>
|
||||
/// Sector CRC check failed
|
||||
/// </summary>
|
||||
SectorCrcError = 0x8,
|
||||
/// <summary>
|
||||
/// File has CRC32
|
||||
/// </summary>
|
||||
HasChecksum = 0x10,
|
||||
/// <summary>
|
||||
/// CRC32 check failed
|
||||
/// </summary>
|
||||
ChecksumError = 0x20,
|
||||
/// <summary>
|
||||
/// File has data MD5
|
||||
/// </summary>
|
||||
HasMd5 = 0x40,
|
||||
/// <summary>
|
||||
/// MD5 check failed
|
||||
/// </summary>
|
||||
Md5Error = 0x80,
|
||||
/// <summary>
|
||||
/// File has raw data MD5
|
||||
/// </summary>
|
||||
HasRawMd5 = 0x100,
|
||||
/// <summary>
|
||||
/// Raw MD5 check failed
|
||||
/// </summary>
|
||||
RawMd5Error = 0x200,
|
||||
}
|
||||
|
||||
public enum MpqArchiveVerificationResult
|
||||
{
|
||||
/// <summary>
|
||||
/// There is no signature in the MPQ
|
||||
/// </summary>
|
||||
NoSignature = 0,
|
||||
/// <summary>
|
||||
/// There was an error during verifying signature (like no memory)
|
||||
/// </summary>
|
||||
VerificationFailed = 1,
|
||||
/// <summary>
|
||||
/// There is a weak signature and sign check passed
|
||||
/// </summary>
|
||||
WeakSignatureVerified = 2,
|
||||
/// <summary>
|
||||
/// There is a weak signature but sign check failed
|
||||
/// </summary>
|
||||
WeakSignatureFailed = 3,
|
||||
/// <summary>
|
||||
/// There is a strong signature and sign check passed
|
||||
/// </summary>
|
||||
StrongSignatureVerified = 4,
|
||||
/// <summary>
|
||||
/// There is a strong signature but sign check failed
|
||||
/// </summary>
|
||||
StrongSignatureFailed = 5,
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace StormLibSharp
|
||||
{
|
||||
public delegate void MpqArchiveCompactingEventHandler(MpqArchive sender, MpqArchiveCompactingEventArgs e);
|
||||
|
||||
public class MpqArchiveCompactingEventArgs : EventArgs
|
||||
{
|
||||
internal MpqArchiveCompactingEventArgs(uint dwWorkType, ulong processed, ulong total)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
WorkType = (MpqCompactingWorkType)dwWorkType;
|
||||
BytesProcessed = (long)processed;
|
||||
TotalBytes = (long)total;
|
||||
}
|
||||
}
|
||||
|
||||
public MpqCompactingWorkType WorkType
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public long BytesProcessed
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public long TotalBytes
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
|
||||
public enum MpqCompactingWorkType
|
||||
{
|
||||
CheckingFiles = 1,
|
||||
CheckingHashTable = 2,
|
||||
CopyingNonMpqData = 3,
|
||||
CompactingFiles = 4,
|
||||
ClosingArchive = 5,
|
||||
}
|
||||
}
|
||||
185
BurnOutSharp/External/StormLibSharp/MpqFileStream.cs
vendored
185
BurnOutSharp/External/StormLibSharp/MpqFileStream.cs
vendored
@@ -1,185 +0,0 @@
|
||||
using StormLibSharp.Native;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace StormLibSharp
|
||||
{
|
||||
public class MpqFileStream : Stream
|
||||
{
|
||||
private MpqFileSafeHandle _handle;
|
||||
private FileAccess _accessType;
|
||||
private MpqArchive _owner;
|
||||
|
||||
internal MpqFileStream(MpqFileSafeHandle handle, FileAccess accessType, MpqArchive owner)
|
||||
{
|
||||
_handle = handle;
|
||||
_accessType = accessType;
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
private void VerifyHandle()
|
||||
{
|
||||
if (_handle == null || _handle.IsInvalid || _handle.IsClosed)
|
||||
throw new ObjectDisposedException("MpqFileStream");
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { VerifyHandle(); return true; }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { VerifyHandle(); return true; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { VerifyHandle(); return _accessType != FileAccess.Read; }
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
VerifyHandle();
|
||||
|
||||
_owner.Flush();
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
VerifyHandle();
|
||||
|
||||
uint high = 0;
|
||||
uint low = NativeMethods.SFileGetFileSize(_handle, ref high);
|
||||
|
||||
ulong val = (high << 32) | low;
|
||||
return unchecked((long)val);
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
VerifyHandle();
|
||||
|
||||
return NativeMethods.SFileGetFilePointer(_handle);
|
||||
}
|
||||
set
|
||||
{
|
||||
Seek(value, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
public override unsafe int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException("buffer");
|
||||
if (offset > buffer.Length || (offset + count) > buffer.Length)
|
||||
throw new ArgumentException();
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
|
||||
VerifyHandle();
|
||||
|
||||
bool success;
|
||||
uint read;
|
||||
fixed (byte* pb = &buffer[offset])
|
||||
{
|
||||
NativeOverlapped overlapped = default(NativeOverlapped);
|
||||
success = NativeMethods.SFileReadFile(_handle, new IntPtr(pb), unchecked((uint)count), out read, ref overlapped);
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
int lastError = Win32Methods.GetLastError();
|
||||
if (lastError != 38) // EOF
|
||||
throw new Win32Exception(lastError);
|
||||
}
|
||||
|
||||
return unchecked((int)read);
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
VerifyHandle();
|
||||
|
||||
uint low, high;
|
||||
low = unchecked((uint)(offset & 0xffffffffu));
|
||||
high = unchecked((uint)(offset >> 32));
|
||||
return NativeMethods.SFileSetFilePointer(_handle, low, ref high, (uint)origin);
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override unsafe void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
VerifyHandle();
|
||||
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException("buffer");
|
||||
if (offset > buffer.Length || (offset + count) > buffer.Length)
|
||||
throw new ArgumentException();
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
|
||||
VerifyHandle();
|
||||
|
||||
bool success;
|
||||
fixed (byte* pb = &buffer[offset])
|
||||
{
|
||||
success = NativeMethods.SFileWriteFile(_handle, new IntPtr(pb), unchecked((uint)count), 0u);
|
||||
}
|
||||
|
||||
if (!success)
|
||||
throw new Win32Exception();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
if (_handle != null && !_handle.IsInvalid)
|
||||
{
|
||||
_handle.Close();
|
||||
_handle = null;
|
||||
}
|
||||
|
||||
if (_owner != null)
|
||||
{
|
||||
_owner.RemoveOwnedFile(this);
|
||||
_owner = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Seems like the right place for SFileGetFileInfo, but will need to determine
|
||||
// what value add these features have except for sophisticated debugging purposes
|
||||
// (like in Ladis' MPQ Editor app).
|
||||
|
||||
public int ChecksumCrc32
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] GetMd5Hash()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace StormLibSharp.Native
|
||||
{
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate void SFILE_DOWNLOAD_CALLBACK(IntPtr pvUserData, ulong byteOffset, uint dwTotalBytes);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate void SFILE_COMPACT_CALLBACK(IntPtr pvUserData, uint dwWorkType, ulong bytesProcessed, ulong totalBytes);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate void SFILE_ADDFILE_CALLBACK(IntPtr pvUserData, uint dwBytesWritte, uint dwTotalBytes, bool bFinalCall);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace StormLibSharp.Native
|
||||
{
|
||||
internal sealed class MpqArchiveSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
public MpqArchiveSafeHandle(IntPtr handle)
|
||||
: base(true)
|
||||
{
|
||||
this.SetHandle(handle);
|
||||
}
|
||||
|
||||
public MpqArchiveSafeHandle()
|
||||
: base(true) { }
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
return NativeMethods.SFileCloseArchive(this.handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace StormLibSharp.Native
|
||||
{
|
||||
internal sealed class MpqFileSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
public MpqFileSafeHandle(IntPtr handle)
|
||||
: base(true)
|
||||
{
|
||||
this.SetHandle(handle);
|
||||
}
|
||||
|
||||
public MpqFileSafeHandle()
|
||||
: base(true)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
return NativeMethods.SFileCloseFile(this.handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,497 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace StormLibSharp.Native
|
||||
{
|
||||
internal static class NativeMethods
|
||||
{
|
||||
private const string STORMLIB = "stormlib.dll";
|
||||
|
||||
#region Functions for manipulation with StormLib global flags
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern uint SFileGetLocale();
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern uint SFileSetLocale(uint lcNewLocale);
|
||||
#endregion
|
||||
|
||||
#region Functions for archive manipulation
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileOpenArchive(
|
||||
[MarshalAs(UnmanagedType.LPTStr)] string szMpqName,
|
||||
uint dwPriority,
|
||||
SFileOpenArchiveFlags dwFlags,
|
||||
out MpqArchiveSafeHandle phMpq
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileCreateArchive(
|
||||
[MarshalAs(UnmanagedType.LPTStr)] string szMpqName,
|
||||
uint dwCreateFlags,
|
||||
uint dwMaxFileCount,
|
||||
out MpqArchiveSafeHandle phMpq
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileCreateArchive2(
|
||||
[MarshalAs(UnmanagedType.LPTStr)] string szMpqName,
|
||||
ref SFILE_CREATE_MPQ pCreateInfo,
|
||||
out MpqArchiveSafeHandle phMpq
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileSetDownloadCallback(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.FunctionPtr)] SFILE_DOWNLOAD_CALLBACK pfnCallback,
|
||||
IntPtr pvUserData
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileFlushArchive(MpqArchiveSafeHandle hMpq);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileCloseArchive(IntPtr hMpq);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileCloseArchive(MpqArchiveSafeHandle hMpq);
|
||||
#endregion
|
||||
|
||||
#region Adds another listfile into MPQ.
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern int SFileAddListFile(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szListFile
|
||||
);
|
||||
#endregion
|
||||
|
||||
#region Archive compacting
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileSetCompactCallback(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
SFILE_COMPACT_CALLBACK compactCB,
|
||||
IntPtr pvUserData
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileCompactArchive(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szListFile,
|
||||
bool bReserved
|
||||
);
|
||||
#endregion
|
||||
|
||||
#region Maximum file count
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern uint SFileGetMaxFileCount(MpqArchiveSafeHandle hMpq);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileSetMaxFileCount(MpqArchiveSafeHandle hMpq, uint dwMaxFileCount);
|
||||
#endregion
|
||||
|
||||
#region Changing (attributes) file
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern uint SFileGetAttributes(MpqArchiveSafeHandle hMpq);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileSetAttributes(MpqArchiveSafeHandle hMpq, uint dwFlags);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileUpdateFileAttributes(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szFileName
|
||||
);
|
||||
#endregion
|
||||
|
||||
#region Functions for manipulation with patch archives
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileOpenPatchArchive(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPTStr)] string szPatchMpqName,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szPatchPathPrefix,
|
||||
uint dwFlags
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileIsPatchedArchive(MpqArchiveSafeHandle hMpq);
|
||||
#endregion
|
||||
|
||||
#region Functions for file manipulation
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileHasFile(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szFileName
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileOpenFileEx(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szFileName,
|
||||
uint dwSearchScope,
|
||||
out MpqFileSafeHandle phFile
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern uint SFileGetFileSize(MpqFileSafeHandle hFile, ref uint pdwFileSizeHigh);
|
||||
|
||||
public static unsafe uint SFileGetFilePointer(
|
||||
MpqFileSafeHandle hFile
|
||||
)
|
||||
{
|
||||
if (hFile.IsInvalid || hFile.IsClosed)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
IntPtr handle = hFile.DangerousGetHandle();
|
||||
_TMPQFileHeader* header = (_TMPQFileHeader*)handle.ToPointer();
|
||||
return header->dwFilePos;
|
||||
}
|
||||
|
||||
//public static unsafe uint SFileGetFileSize(
|
||||
// MpqFileSafeHandle hFile
|
||||
// )
|
||||
//{
|
||||
// if (hFile.IsInvalid || hFile.IsClosed)
|
||||
// throw new InvalidOperationException();
|
||||
|
||||
// IntPtr handle = hFile.DangerousGetHandle();
|
||||
// _TMPQFileHeader* header = (_TMPQFileHeader*)handle.ToPointer();
|
||||
// return header->pFileEntry->dwFileSize;
|
||||
//}
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern uint SFileSetFilePointer(
|
||||
MpqFileSafeHandle hFile,
|
||||
uint lFilePos,
|
||||
ref uint plFilePosHigh,
|
||||
uint dwMoveMethod
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileReadFile(
|
||||
MpqFileSafeHandle hFile,
|
||||
IntPtr lpBuffer,
|
||||
uint dwToRead,
|
||||
out uint pdwRead,
|
||||
ref System.Threading.NativeOverlapped lpOverlapped
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileCloseFile(IntPtr hFile);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileCloseFile(MpqFileSafeHandle hFile);
|
||||
|
||||
#region Retrieving info about a file in the archive
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileGetFileInfo(
|
||||
IntPtr hMpqOrFile,
|
||||
SFileInfoClass InfoClass,
|
||||
IntPtr pvFileInfo,
|
||||
uint cbFileInfoSize,
|
||||
out uint pcbLengthNeeded
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileGetFileInfo(
|
||||
MpqArchiveSafeHandle hMpqOrFile,
|
||||
SFileInfoClass InfoClass,
|
||||
IntPtr pvFileInfo,
|
||||
uint cbFileInfoSize,
|
||||
out uint pcbLengthNeeded
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileGetFileInfo(
|
||||
MpqFileSafeHandle hMpqOrFile,
|
||||
SFileInfoClass InfoClass,
|
||||
IntPtr pvFileInfo,
|
||||
uint cbFileInfoSize,
|
||||
out uint pcbLengthNeeded
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileGetFileName(
|
||||
MpqFileSafeHandle hFile,
|
||||
[MarshalAs(UnmanagedType.LPStr)] out string szFileName
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileFreeFileInfo(
|
||||
IntPtr pvFileInfo,
|
||||
SFileInfoClass infoClass
|
||||
);
|
||||
#endregion
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileExtractFile(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szToExtract,
|
||||
[MarshalAs(UnmanagedType.LPTStr)] string szExtracted,
|
||||
uint dwSearchScope
|
||||
);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions for file and archive verification
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileGetFileChecksums(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szFileName,
|
||||
out uint pdwCrc32,
|
||||
IntPtr pMD5
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern uint SFileVerifyFile(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szFileName,
|
||||
uint dwFlags
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern int SFileVerifyRawData(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
uint dwWhatToVerify,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szFileName
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern uint SFileVerifyArchive(MpqArchiveSafeHandle hMpq);
|
||||
#endregion
|
||||
|
||||
#region Functions for file searching
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern IntPtr SFileFindFirstFile(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szMask,
|
||||
out _SFILE_FIND_DATA lpFindFileData,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szListFile
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileFindNextFile(
|
||||
IntPtr hFind,
|
||||
[In, Out] ref _SFILE_FIND_DATA lpFindFileData
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileFindClose(IntPtr hFind);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern IntPtr SListFileFindFirstFile(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szListFile,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szMask,
|
||||
[In, Out] ref _SFILE_FIND_DATA lpFindFileData
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SListFileFindNextFile(
|
||||
IntPtr hFind,
|
||||
[In, Out] ref _SFILE_FIND_DATA lpFindFileData
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SListFileFindClose(IntPtr hFind);
|
||||
#endregion
|
||||
|
||||
#region Locale support
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern int SFileEnumLocales(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szFileName,
|
||||
IntPtr plcLocales,
|
||||
ref uint pdwMaxLocales,
|
||||
uint dwSearchScope
|
||||
);
|
||||
#endregion
|
||||
|
||||
#region Support for adding files to the MPQ
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileCreateFile(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szArchiveName,
|
||||
ulong fileTime,
|
||||
uint dwFileSize,
|
||||
uint lcLocale,
|
||||
uint dwFlags,
|
||||
out IntPtr phFile
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileWriteFile(
|
||||
MpqFileSafeHandle hFile,
|
||||
IntPtr pvData,
|
||||
uint dwSize,
|
||||
uint dwCompression
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileFinishFile(MpqFileSafeHandle hFile);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileAddFileEx(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPTStr)] string szFileName,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szArchivedName,
|
||||
uint dwFlags,
|
||||
uint dwCompression,
|
||||
uint dwCompressionNext
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileAddFile(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPTStr)] string szFileName,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szArchivedName,
|
||||
uint dwFlags
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileAddWave(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPTStr)] string szFileName,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szArchivedName,
|
||||
uint dwFlags,
|
||||
uint dwQuality
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileRemoveFile(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szFileName,
|
||||
uint dwSearchScope
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileRenameFile(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szOldFileName,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szNewFileName
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileSetFileLocale(
|
||||
MpqFileSafeHandle hFile,
|
||||
uint lcNewLocale
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileSetDataCompression(uint DataCompression);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern bool SFileSetAddFileCallback(
|
||||
MpqArchiveSafeHandle hMpq,
|
||||
SFILE_ADDFILE_CALLBACK AddFileCB,
|
||||
IntPtr pvUserData
|
||||
);
|
||||
#endregion
|
||||
|
||||
#region Compression and decompression
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern int SCompImplode(
|
||||
IntPtr pvOutBuffer,
|
||||
ref int pcbOutBuffer,
|
||||
IntPtr pvInBuffer,
|
||||
int cbInBuffer
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern int SCompExplode(
|
||||
IntPtr pvOutBuffer,
|
||||
ref int pcbOutBuffer,
|
||||
IntPtr pvInBuffer,
|
||||
int cbInBuffer
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern int SCompCompress(
|
||||
IntPtr pvOutBuffer,
|
||||
ref int pcbOutBuffer,
|
||||
IntPtr pvInBuffer,
|
||||
int cbInBuffer,
|
||||
uint uCompressionMask,
|
||||
int nCmpType,
|
||||
int nCmpLevel
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern int SCompDecompress(
|
||||
IntPtr pvOutBuffer,
|
||||
ref int pcbOutBuffer,
|
||||
IntPtr pvInBuffer,
|
||||
int cbInBuffer
|
||||
);
|
||||
|
||||
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
|
||||
public static extern int SCompDecompress2(
|
||||
IntPtr pvOutBuffer,
|
||||
ref int pcbOutBuffer,
|
||||
IntPtr pvInBuffer,
|
||||
int cbInBuffer
|
||||
);
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#pragma warning disable 0169,0649
|
||||
internal struct SFILE_CREATE_MPQ
|
||||
{
|
||||
public uint cbSize;
|
||||
public uint dwMpqVersion;
|
||||
private IntPtr pvUserData;
|
||||
private uint cbUserData;
|
||||
public uint dwStreamFlags;
|
||||
public uint dwFileFlags1;
|
||||
public uint dwFileFlags2;
|
||||
public uint dwAttrFlags;
|
||||
public uint dwSectorSize;
|
||||
public uint dwRawChunkSize;
|
||||
public uint dwMaxFileCount;
|
||||
}
|
||||
|
||||
internal unsafe struct _SFILE_FIND_DATA
|
||||
{
|
||||
public fixed char cFileName[260]; // Full name of the found file
|
||||
|
||||
public IntPtr szPlainName; // Plain name of the found file
|
||||
public uint dwHashIndex; // Hash table index for the file
|
||||
public uint dwBlockIndex; // Block table index for the file
|
||||
public uint dwFileSize; // File size in bytes
|
||||
public uint dwFileFlags; // MPQ file flags
|
||||
public uint dwCompSize; // Compressed file size
|
||||
public uint dwFileTimeLo; // Low 32-bits of the file time (0 if not present)
|
||||
public uint dwFileTimeHi; // High 32-bits of the file time (0 if not present)
|
||||
public uint lcLocale; // Locale version
|
||||
}
|
||||
|
||||
internal unsafe struct _TFileEntry
|
||||
{
|
||||
public ulong FileNameHash;
|
||||
public ulong ByteOffset;
|
||||
public ulong FileTime;
|
||||
public uint dwHashIndex;
|
||||
public uint dwFileSize;
|
||||
public uint dwCmpSize;
|
||||
public uint dwFlags;
|
||||
public ushort lcLocale;
|
||||
public ushort wPlatform;
|
||||
public uint dwCrc32;
|
||||
public fixed byte md5[16];
|
||||
public IntPtr szFileName;
|
||||
}
|
||||
|
||||
// Provides enough of _TMPQFile to get to the file size and current position.
|
||||
internal unsafe struct _TMPQFileHeader
|
||||
{
|
||||
public IntPtr pStream;
|
||||
public IntPtr ha;
|
||||
public _TFileEntry* pFileEntry;
|
||||
public uint dwFileKey;
|
||||
public uint dwFilePos;
|
||||
}
|
||||
#pragma warning restore 0169,0649
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace StormLibSharp.Native
|
||||
{
|
||||
internal enum SFileInfoClass
|
||||
{
|
||||
// Info classes for archives
|
||||
SFileMpqFileName, // Name of the archive file (TCHAR [])
|
||||
SFileMpqStreamBitmap, // Array of bits, each bit means availability of one block (BYTE [])
|
||||
SFileMpqUserDataOffset, // Offset of the user data header (ULONGLONG)
|
||||
SFileMpqUserDataHeader, // Raw (unfixed) user data header (TMPQUserData)
|
||||
SFileMpqUserData, // MPQ USer data, without the header (BYTE [])
|
||||
SFileMpqHeaderOffset, // Offset of the MPQ header (ULONGLONG)
|
||||
SFileMpqHeaderSize, // Fixed size of the MPQ header
|
||||
SFileMpqHeader, // Raw (unfixed) archive header (TMPQHeader)
|
||||
SFileMpqHetTableOffset, // Offset of the HET table, relative to MPQ header (ULONGLONG)
|
||||
SFileMpqHetTableSize, // Compressed size of the HET table (ULONGLONG)
|
||||
SFileMpqHetHeader, // HET table header (TMPQHetHeader)
|
||||
SFileMpqHetTable, // HET table as pointer. Must be freed using SFileFreeFileInfo
|
||||
SFileMpqBetTableOffset, // Offset of the BET table, relative to MPQ header (ULONGLONG)
|
||||
SFileMpqBetTableSize, // Compressed size of the BET table (ULONGLONG)
|
||||
SFileMpqBetHeader, // BET table header, followed by the flags (TMPQBetHeader + DWORD[])
|
||||
SFileMpqBetTable, // BET table as pointer. Must be freed using SFileFreeFileInfo
|
||||
SFileMpqHashTableOffset, // Hash table offset, relative to MPQ header (ULONGLONG)
|
||||
SFileMpqHashTableSize64, // Compressed size of the hash table (ULONGLONG)
|
||||
SFileMpqHashTableSize, // Size of the hash table, in entries (DWORD)
|
||||
SFileMpqHashTable, // Raw (unfixed) hash table (TMPQBlock [])
|
||||
SFileMpqBlockTableOffset, // Block table offset, relative to MPQ header (ULONGLONG)
|
||||
SFileMpqBlockTableSize64, // Compressed size of the block table (ULONGLONG)
|
||||
SFileMpqBlockTableSize, // Size of the block table, in entries (DWORD)
|
||||
SFileMpqBlockTable, // Raw (unfixed) block table (TMPQBlock [])
|
||||
SFileMpqHiBlockTableOffset, // Hi-block table offset, relative to MPQ header (ULONGLONG)
|
||||
SFileMpqHiBlockTableSize64, // Compressed size of the hi-block table (ULONGLONG)
|
||||
SFileMpqHiBlockTable, // The hi-block table (USHORT [])
|
||||
SFileMpqSignatures, // Signatures present in the MPQ (DWORD)
|
||||
SFileMpqStrongSignatureOffset, // Byte offset of the strong signature, relative to begin of the file (ULONGLONG)
|
||||
SFileMpqStrongSignatureSize, // Size of the strong signature (DWORD)
|
||||
SFileMpqStrongSignature, // The strong signature (BYTE [])
|
||||
SFileMpqArchiveSize64, // Archive size from the header (ULONGLONG)
|
||||
SFileMpqArchiveSize, // Archive size from the header (DWORD)
|
||||
SFileMpqMaxFileCount, // Max number of files in the archive (DWORD)
|
||||
SFileMpqFileTableSize, // Number of entries in the file table (DWORD)
|
||||
SFileMpqSectorSize, // Sector size (DWORD)
|
||||
SFileMpqNumberOfFiles, // Number of files (DWORD)
|
||||
SFileMpqRawChunkSize, // Size of the raw data chunk for MD5
|
||||
SFileMpqStreamFlags, // Stream flags (DWORD)
|
||||
SFileMpqIsReadOnly, // Nonzero if the MPQ is read only (DWORD)
|
||||
|
||||
// Info classes for files
|
||||
SFileInfoPatchChain, // Chain of patches where the file is (TCHAR [])
|
||||
SFileInfoFileEntry, // The file entry for the file (TFileEntry)
|
||||
SFileInfoHashEntry, // Hash table entry for the file (TMPQHash)
|
||||
SFileInfoHashIndex, // Index of the hash table entry (DWORD)
|
||||
SFileInfoNameHash1, // The first name hash in the hash table (DWORD)
|
||||
SFileInfoNameHash2, // The second name hash in the hash table (DWORD)
|
||||
SFileInfoNameHash3, // 64-bit file name hash for the HET/BET tables (ULONGLONG)
|
||||
SFileInfoLocale, // File locale (DWORD)
|
||||
SFileInfoFileIndex, // Block index (DWORD)
|
||||
SFileInfoByteOffset, // File position in the archive (ULONGLONG)
|
||||
SFileInfoFileTime, // File time (ULONGLONG)
|
||||
SFileInfoFileSize, // Size of the file (DWORD)
|
||||
SFileInfoCompressedSize, // Compressed file size (DWORD)
|
||||
SFileInfoFlags, // File flags from (DWORD)
|
||||
SFileInfoEncryptionKey, // File encryption key
|
||||
SFileInfoEncryptionKeyRaw, // Unfixed value of the file key
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace StormLibSharp.Native
|
||||
{
|
||||
[Flags]
|
||||
internal enum SFileOpenArchiveFlags : uint
|
||||
{
|
||||
None = 0,
|
||||
TypeIsFile = None,
|
||||
TypeIsMemoryMapped = 1,
|
||||
TypeIsHttp = 2,
|
||||
|
||||
AccessReadOnly = 0x100,
|
||||
AccessReadWriteShare = 0x200,
|
||||
AccessUseBitmap = 0x400,
|
||||
|
||||
DontOpenListfile = 0x10000,
|
||||
DontOpenAttributes = 0x20000,
|
||||
DontSearchHeader = 0x40000,
|
||||
ForceVersion1 = 0x80000,
|
||||
CheckSectorCRC = 0x100000,
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace StormLibSharp.Native
|
||||
{
|
||||
internal static class Win32Methods
|
||||
{
|
||||
[DllImport("kernel32", ExactSpelling = false, SetLastError = true)]
|
||||
public static extern uint GetMappedFileName(
|
||||
IntPtr hProcess,
|
||||
IntPtr fileHandle,
|
||||
IntPtr lpFilename,
|
||||
uint nSize
|
||||
);
|
||||
|
||||
[DllImport("kernel32", ExactSpelling = false, SetLastError = true)]
|
||||
public static extern uint GetFinalPathNameByHandle(
|
||||
IntPtr hFile,
|
||||
IntPtr lpszFilePath,
|
||||
uint cchFilePath,
|
||||
uint dwFlags
|
||||
);
|
||||
|
||||
[DllImport("kernel32", SetLastError = false, ExactSpelling = false)]
|
||||
public static extern int GetLastError();
|
||||
|
||||
public static string GetFileNameOfMemoryMappedFile(MemoryMappedFile file)
|
||||
{
|
||||
const uint size = 522;
|
||||
IntPtr path = Marshal.AllocCoTaskMem(unchecked((int)size)); // MAX_PATH + 1 char
|
||||
|
||||
string result = null;
|
||||
try
|
||||
{
|
||||
// constant 0x2 = VOLUME_NAME_NT
|
||||
uint test = GetFinalPathNameByHandle(file.SafeMemoryMappedFileHandle.DangerousGetHandle(), path, size, 0x2);
|
||||
if (test != 0)
|
||||
throw new Win32Exception();
|
||||
|
||||
result = Marshal.PtrToStringAuto(path);
|
||||
}
|
||||
catch
|
||||
{
|
||||
uint test = GetMappedFileName(Process.GetCurrentProcess().Handle, file.SafeMemoryMappedFileHandle.DangerousGetHandle(), path, size);
|
||||
if (test != 0)
|
||||
throw new Win32Exception();
|
||||
|
||||
result = Marshal.PtrToStringAuto(path);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
BurnOutSharp/External/hllib
vendored
Submodule
1
BurnOutSharp/External/hllib
vendored
Submodule
Submodule BurnOutSharp/External/hllib added at 2063e3e0cd
1
BurnOutSharp/External/libmspack4n
vendored
Submodule
1
BurnOutSharp/External/libmspack4n
vendored
Submodule
Submodule BurnOutSharp/External/libmspack4n added at 7213a361f8
222
BurnOutSharp/External/psxt001z/LibCrypt.cs
vendored
222
BurnOutSharp/External/psxt001z/LibCrypt.cs
vendored
@@ -1,146 +1,128 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace BurnOutSharp.External.psxt001z
|
||||
{
|
||||
/// <summary>
|
||||
/// LibCrypt detection code
|
||||
/// Originally written by Dremora: https://github.com/Dremora/psxt001z
|
||||
/// Ported and changed by darksabre76
|
||||
/// </summary>
|
||||
public class LibCrypt
|
||||
/// <summary>
|
||||
/// LibCrypt detection code
|
||||
/// Originally written by Dremora: https://github.com/Dremora/psxt001z
|
||||
/// Ported and changed by darksabre76
|
||||
/// </summary>
|
||||
public class LibCrypt
|
||||
{
|
||||
public static bool CheckSubfile(string subFilePath)
|
||||
{
|
||||
// Check the file exists first
|
||||
if (!File.Exists(subFilePath))
|
||||
{
|
||||
Console.WriteLine($"{subFilePath} could not be found");
|
||||
return false;
|
||||
}
|
||||
// Check the file exists first
|
||||
if (!File.Exists(subFilePath))
|
||||
return false;
|
||||
|
||||
// Check the extension is a subfile
|
||||
string ext = Path.GetExtension(subFilePath).TrimStart('.').ToLowerInvariant();
|
||||
if (ext != "sub")
|
||||
{
|
||||
Console.WriteLine($"{ext}: unknown file extension");
|
||||
return false;
|
||||
}
|
||||
// Check the extension is a subfile
|
||||
string ext = Path.GetExtension(subFilePath).TrimStart('.').ToLowerInvariant();
|
||||
if (ext != "sub")
|
||||
return false;
|
||||
|
||||
// Open and check the subfile for LibCrypt
|
||||
try
|
||||
{
|
||||
using (FileStream subfile = File.OpenRead(subFilePath))
|
||||
{
|
||||
return CheckSubfile(subfile);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine($"Error processing {subFilePath}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Open and check the subfile for LibCrypt
|
||||
try
|
||||
{
|
||||
using (FileStream subfile = File.OpenRead(subFilePath))
|
||||
{
|
||||
return CheckSubfile(subfile);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CheckSubfile(Stream subfile)
|
||||
{
|
||||
// Check the length is valid for subfiles
|
||||
long size = subfile.Length;
|
||||
if (size % 96 != 0)
|
||||
{
|
||||
Console.WriteLine($"Wrong size");
|
||||
return false;
|
||||
}
|
||||
public static bool CheckSubfile(Stream subfile)
|
||||
{
|
||||
// Check the length is valid for subfiles
|
||||
long size = subfile.Length;
|
||||
if (size % 96 != 0)
|
||||
return false;
|
||||
|
||||
// Persistent values
|
||||
byte[] buffer = new byte[16];
|
||||
byte[] sub = new byte[16];
|
||||
int tpos = 0;
|
||||
int modifiedSectors = 0;
|
||||
// Persistent values
|
||||
byte[] buffer = new byte[16];
|
||||
byte[] sub = new byte[16];
|
||||
int tpos = 0;
|
||||
int modifiedSectors = 0;
|
||||
|
||||
// Check each sector for modifications
|
||||
for (uint sector = 150; sector < ((size / 96) + 150); sector++)
|
||||
{
|
||||
subfile.Seek(12, SeekOrigin.Current);
|
||||
if (subfile.Read(buffer, 0, 12) == 0)
|
||||
return modifiedSectors != 0;
|
||||
// Check each sector for modifications
|
||||
for (uint sector = 150; sector < ((size / 96) + 150); sector++)
|
||||
{
|
||||
subfile.Seek(12, SeekOrigin.Current);
|
||||
if (subfile.Read(buffer, 0, 12) == 0)
|
||||
return modifiedSectors != 0;
|
||||
|
||||
subfile.Seek(72, SeekOrigin.Current);
|
||||
subfile.Seek(72, SeekOrigin.Current);
|
||||
|
||||
// New track
|
||||
if ((btoi(buffer[1]) == (btoi(sub[1]) + 1)) && (buffer[2] == 0 || buffer[2] == 1))
|
||||
{
|
||||
Array.Copy(buffer, sub, 6);
|
||||
tpos = ((btoi((byte)(buffer[3] * 60)) + btoi(buffer[4])) * 75) + btoi(buffer[5]);
|
||||
}
|
||||
// New track
|
||||
if ((btoi(buffer[1]) == (btoi(sub[1]) + 1)) && (buffer[2] == 0 || buffer[2] == 1))
|
||||
{
|
||||
Array.Copy(buffer, sub, 6);
|
||||
tpos = ((btoi((byte)(buffer[3] * 60)) + btoi(buffer[4])) * 75) + btoi(buffer[5]);
|
||||
}
|
||||
|
||||
// New index
|
||||
else if (btoi(buffer[2]) == (btoi(sub[2]) + 1) && buffer[1] == sub[1])
|
||||
{
|
||||
Array.Copy(buffer, 2, sub, 2, 4);
|
||||
tpos = ((btoi((byte)(buffer[3] * 60)) + btoi(buffer[4])) * 75) + btoi(buffer[5]);
|
||||
}
|
||||
// New index
|
||||
else if (btoi(buffer[2]) == (btoi(sub[2]) + 1) && buffer[1] == sub[1])
|
||||
{
|
||||
Array.Copy(buffer, 2, sub, 2, 4);
|
||||
tpos = ((btoi((byte)(buffer[3] * 60)) + btoi(buffer[4])) * 75) + btoi(buffer[5]);
|
||||
}
|
||||
|
||||
// MSF1 [3-5]
|
||||
else
|
||||
{
|
||||
if (sub[2] == 0)
|
||||
tpos--;
|
||||
else
|
||||
tpos++;
|
||||
// MSF1 [3-5]
|
||||
else
|
||||
{
|
||||
if (sub[2] == 0)
|
||||
tpos--;
|
||||
else
|
||||
tpos++;
|
||||
|
||||
sub[3] = itob((byte)(tpos / 60 / 75));
|
||||
sub[4] = itob((byte)((tpos / 75) % 60));
|
||||
sub[5] = itob((byte)(tpos % 75));
|
||||
}
|
||||
sub[3] = itob((byte)(tpos / 60 / 75));
|
||||
sub[4] = itob((byte)((tpos / 75) % 60));
|
||||
sub[5] = itob((byte)(tpos % 75));
|
||||
}
|
||||
|
||||
// MSF2 [7-9]
|
||||
sub[7] = itob((byte)(sector / 60 / 75));
|
||||
sub[8] = itob((byte)((sector / 75) % 60));
|
||||
sub[9] = itob((byte)(sector % 75));
|
||||
// MSF2 [7-9]
|
||||
sub[7] = itob((byte)(sector / 60 / 75));
|
||||
sub[8] = itob((byte)((sector / 75) % 60));
|
||||
sub[9] = itob((byte)(sector % 75));
|
||||
|
||||
// CRC-16 [10-11]
|
||||
ushort crc = CRC16.Calculate(sub, 0, 10);
|
||||
byte[] crcBytes = BitConverter.GetBytes(crc);
|
||||
sub[10] = crcBytes[0];
|
||||
sub[11] = crcBytes[1];
|
||||
// CRC-16 [10-11]
|
||||
ushort crc = CRC16.Calculate(sub, 0, 10);
|
||||
byte[] crcBytes = BitConverter.GetBytes(crc);
|
||||
sub[10] = crcBytes[0];
|
||||
sub[11] = crcBytes[1];
|
||||
|
||||
// TODO: This *was* a memcmp, but that's harder to do. Fix this for C# later
|
||||
if (buffer[10] != sub[10] && buffer[11] != sub[11] && (buffer[3] != sub[3] || buffer[4] != sub[4] || buffer[5] != sub[5] || buffer[7] != sub[7] || buffer[8] != sub[8] || buffer[9] != sub[9]))
|
||||
{
|
||||
if (buffer[3] != sub[3] || buffer[4] != sub[4] || buffer[5] != sub[5] || buffer[7] != sub[7] || buffer[8] != sub[8] || buffer[9] != sub[9] || buffer[10] != sub[10] || buffer[11] != sub[11])
|
||||
{
|
||||
Console.Write($"MSF: {sub[7]:x}:{sub[8]:x}:{sub[9]:x} Q-Data: {buffer[0]:x}{buffer[1]:x}{buffer[2]:x} {buffer[3]:x}:{buffer[4]:x}:{buffer[5]:x} {buffer[6]:x} {buffer[7]:x}:{buffer[8]:x}:{buffer[9]:x} {buffer[10]:x}{buffer[11]:x} xor {crc ^ ((buffer[10] << 8) + buffer[11]):x} % {CRC16.Calculate(buffer, 0, 10) ^ ((buffer[10] << 8) + buffer[11]):x}");
|
||||
//Console.Write($"\nMSF: {sub[7]:x}:{sub[8]:x}:{sub[9]:x} Q-Data: {sub[0]:x}{sub[1]:x}{sub[2]:x} {sub[3]:x}:{sub[4]:x}:{sub[5]:x} {sub[6]:x} {sub[7]:x}:{sub[8]:x}:{sub[9]:x} {sub[10]:x}{sub[11]:x}");
|
||||
if (buffer[3] != sub[3] && buffer[7] != sub[7] && buffer[4] == sub[4] && buffer[8] == sub[8] && buffer[5] == sub[5] && buffer[9] == sub[9])
|
||||
Console.Write($" P1 xor {buffer[3] ^ sub[3]:x} {buffer[7] ^ sub[7]:x}");
|
||||
else if (buffer[3] == sub[3] && buffer[7] == sub[7] && buffer[4] != sub[4] && buffer[8] != sub[8] && buffer[5] == sub[5] && buffer[9] == sub[9])
|
||||
Console.Write($" P2 xor {buffer[4] ^ sub[4]:x} {buffer[8] ^ sub[8]:x}");
|
||||
else if (buffer[3] == sub[3] && buffer[7] == sub[7] && buffer[4] == sub[4] && buffer[8] == sub[8] && buffer[5] != sub[5] && buffer[9] != sub[9])
|
||||
Console.Write($" P3 xor {buffer[5] ^ sub[5]:x} {buffer[9] ^ sub[9]:x}");
|
||||
else
|
||||
Console.Write(" ?");
|
||||
// If any byte (except position 6) is different, it's a modified sector
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
if (i == 6)
|
||||
continue;
|
||||
|
||||
Console.Write("\n");
|
||||
modifiedSectors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (buffer[i] != sub[i])
|
||||
{
|
||||
modifiedSectors++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"Number of modified sectors: {modifiedSectors}");
|
||||
return modifiedSectors != 0;
|
||||
}
|
||||
return modifiedSectors != 0;
|
||||
}
|
||||
|
||||
private static byte btoi(byte b)
|
||||
{
|
||||
/* BCD to u_char */
|
||||
return (byte)((b) / 16 * 10 + (b) % 16);
|
||||
}
|
||||
private static byte btoi(byte b)
|
||||
{
|
||||
/* BCD to u_char */
|
||||
return (byte)((b) / 16 * 10 + (b) % 16);
|
||||
}
|
||||
|
||||
private static byte itob(byte i)
|
||||
{
|
||||
/* u_char to BCD */
|
||||
return (byte)((i) / 10 * 16 + (i) % 10);
|
||||
}
|
||||
}
|
||||
private static byte itob(byte i)
|
||||
{
|
||||
/* u_char to BCD */
|
||||
return (byte)((i) / 10 * 16 + (i) % 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
BurnOutSharp/External/stormlibsharp
vendored
Submodule
1
BurnOutSharp/External/stormlibsharp
vendored
Submodule
Submodule BurnOutSharp/External/stormlibsharp added at 3bf1375317
@@ -1,26 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Tools;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
internal class BFPK
|
||||
public class BFPK : IScannable
|
||||
{
|
||||
public static bool ShouldScan(byte[] magic)
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
if (magic.StartsWith(new byte[] { 0x42, 0x46, 0x50, 0x4b }))
|
||||
if (magic.StartsWith(new byte?[] { 0x42, 0x46, 0x50, 0x4b }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<string> Scan(Stream stream, bool includePosition = false)
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
List<string> protections = new List<string>();
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// If the BFPK file itself fails
|
||||
try
|
||||
{
|
||||
@@ -87,36 +100,31 @@ namespace BurnOutSharp.FileType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string protection = ProtectionFind.ScanContent(tempFile, includePosition);
|
||||
|
||||
// If tempfile cleanup fails
|
||||
try
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (!string.IsNullOrEmpty(protection))
|
||||
protections.Add(tempFile);
|
||||
}
|
||||
catch { }
|
||||
|
||||
|
||||
br.BaseStream.Seek(current, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return protections;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.BZip2;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
internal class BZip2
|
||||
public class BZip2 : IScannable
|
||||
{
|
||||
public static bool ShouldScan(byte[] magic)
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
if (magic.StartsWith(new byte[] { 0x42, 0x52, 0x68 }))
|
||||
if (magic.StartsWith(new byte?[] { 0x42, 0x52, 0x68 }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<string> Scan(Stream stream, bool includePosition = false)
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
List<string> protections = new List<string>();
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
// If the 7-zip file itself fails
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// If the BZip2 file itself fails
|
||||
try
|
||||
{
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
@@ -31,37 +44,33 @@ namespace BurnOutSharp.FileType
|
||||
// If an individual entry fails
|
||||
try
|
||||
{
|
||||
string tempfile = Path.Combine(tempPath, Guid.NewGuid().ToString());
|
||||
using (FileStream fs = File.OpenWrite(tempfile))
|
||||
string tempFile = Path.Combine(tempPath, Guid.NewGuid().ToString());
|
||||
using (FileStream fs = File.OpenWrite(tempFile))
|
||||
{
|
||||
bz2File.CopyTo(fs);
|
||||
}
|
||||
|
||||
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
|
||||
|
||||
// If tempfile cleanup fails
|
||||
try
|
||||
{
|
||||
File.Delete(tempfile);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (!string.IsNullOrEmpty(protection))
|
||||
protections.Add($"\r\n{protection}");
|
||||
}
|
||||
catch { }
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return protections;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,254 +1,160 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using BurnOutSharp.ProtectionType;
|
||||
using System.Threading.Tasks;
|
||||
using BurnOutSharp.ExecutableType.Microsoft;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
internal class Executable
|
||||
public class Executable : IScannable
|
||||
{
|
||||
public static bool ShouldScan(byte[] magic)
|
||||
/// <summary>
|
||||
/// Cache for all IContentCheck types
|
||||
/// </summary>
|
||||
private static readonly IEnumerable<IContentCheck> contentCheckClasses = InitContentCheckClasses();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
// DOS MZ executable file format (and descendants)
|
||||
if (magic.StartsWith(new byte[] { 0x4d, 0x5a }))
|
||||
if (magic.StartsWith(new byte?[] { 0x4d, 0x5a }))
|
||||
return true;
|
||||
|
||||
// Executable and Linkable Format
|
||||
if (magic.StartsWith(new byte[] { 0x7f, 0x45, 0x4c, 0x46 }))
|
||||
if (magic.StartsWith(new byte?[] { 0x7f, 0x45, 0x4c, 0x46 }))
|
||||
return true;
|
||||
|
||||
// Mach-O binary (32-bit)
|
||||
if (magic.StartsWith(new byte[] { 0xfe, 0xed, 0xfa, 0xce }))
|
||||
if (magic.StartsWith(new byte?[] { 0xfe, 0xed, 0xfa, 0xce }))
|
||||
return true;
|
||||
|
||||
// Mach-O binary (32-bit, reverse byte ordering scheme)
|
||||
if (magic.StartsWith(new byte[] { 0xce, 0xfa, 0xed, 0xfe }))
|
||||
if (magic.StartsWith(new byte?[] { 0xce, 0xfa, 0xed, 0xfe }))
|
||||
return true;
|
||||
|
||||
// Mach-O binary (64-bit)
|
||||
if (magic.StartsWith(new byte[] { 0xfe, 0xed, 0xfa, 0xcf }))
|
||||
if (magic.StartsWith(new byte?[] { 0xfe, 0xed, 0xfa, 0xcf }))
|
||||
return true;
|
||||
|
||||
// Mach-O binary (64-bit, reverse byte ordering scheme)
|
||||
if (magic.StartsWith(new byte[] { 0xcf, 0xfa, 0xed, 0xfe }))
|
||||
if (magic.StartsWith(new byte?[] { 0xcf, 0xfa, 0xed, 0xfe }))
|
||||
return true;
|
||||
|
||||
// Prefrred Executable File Format
|
||||
if (magic.StartsWith(new byte[] { 0x4a, 0x6f, 0x79, 0x21, 0x70, 0x65, 0x66, 0x66 }))
|
||||
if (magic.StartsWith(new byte?[] { 0x4a, 0x6f, 0x79, 0x21, 0x70, 0x65, 0x66, 0x66 }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<string> Scan(Stream stream, string file = null, bool includePosition = false)
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// Files can be protected in multiple ways
|
||||
var protections = new ConcurrentDictionary<string, ConcurrentQueue<string>>();
|
||||
|
||||
// Load the current file content
|
||||
byte[] fileContent = null;
|
||||
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
|
||||
try
|
||||
{
|
||||
fileContent = br.ReadBytes((int)stream.Length);
|
||||
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
|
||||
{
|
||||
fileContent = br.ReadBytes((int)stream.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Utilities.AppendToDictionary(protections, file, "[Out of memory attempting to open]");
|
||||
return protections;
|
||||
}
|
||||
|
||||
// If we can, seek to the beginning of the stream
|
||||
if (stream.CanSeek)
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
// TODO: Start moving toward reading from the stream directly. In theory,
|
||||
// deserialization can be done at this point, and if all of the sections are populated
|
||||
// properly, nearly all of the content checks can be dealt with without having
|
||||
// to take up as much memory as it does right now reading into the fileContent
|
||||
// byte array
|
||||
|
||||
// Files can be protected in multiple ways
|
||||
List<string> protections = new List<string>();
|
||||
List<string> subProtections = new List<string>();
|
||||
string protection;
|
||||
// Create PortableExecutable and NewExecutable objects for use in the checks
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
PortableExecutable pex = PortableExecutable.Deserialize(fileContent, 0);
|
||||
NewExecutable nex = NewExecutable.Deserialize(fileContent, 0);
|
||||
|
||||
// 3PLock
|
||||
protection = ThreePLock.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
// Create PortableExecutable and NewExecutable objects for use in the checks
|
||||
// PortableExecutable pex = PortableExecutable.Deserialize(stream);
|
||||
// stream.Seek(0, SeekOrigin.Begin);
|
||||
// NewExecutable nex = NewExecutable.Deserialize(stream);
|
||||
// stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// ActiveMARK
|
||||
protection = ActiveMARK.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
// Iterate through all content checks
|
||||
Parallel.ForEach(contentCheckClasses, contentCheckClass =>
|
||||
{
|
||||
// Track if any protection is found
|
||||
bool foundProtection = false;
|
||||
|
||||
// Alpha-ROM
|
||||
protection = AlphaROM.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
// Check using custom content checks first
|
||||
string protection = contentCheckClass.CheckContents(file, fileContent, scanner.IncludeDebug, pex, nex);
|
||||
foundProtection |= !string.IsNullOrWhiteSpace(protection);
|
||||
if (ShouldAddProtection(contentCheckClass, scanner, protection))
|
||||
Utilities.AppendToDictionary(protections, file, protection);
|
||||
|
||||
// Armadillo
|
||||
protection = Armadillo.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// CD-Cops
|
||||
protection = CDCops.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// CD-Lock
|
||||
protection = CDLock.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// CDSHiELD SE
|
||||
protection = CDSHiELDSE.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// CD Check
|
||||
protection = CDCheck.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// Cenega ProtectDVD
|
||||
protection = CengaProtectDVD.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// Code Lock
|
||||
protection = CodeLock.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// CopyKiller
|
||||
protection = CopyKiller.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// Cucko (EA Custom)
|
||||
protection = Cucko.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// dotFuscator
|
||||
protection = dotFuscator.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// DVD-Cops
|
||||
protection = DVDCops.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// EA CdKey Registration Module
|
||||
protection = EACdKey.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// EXE Stealth
|
||||
protection = EXEStealth.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// Games for Windows - Live
|
||||
protection = GFWL.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// Impulse Reactor
|
||||
protection = ImpulseReactor.CheckContents(file, fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// Inno Setup
|
||||
protection = InnoSetup.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// JoWooD X-Prot
|
||||
protection = JoWooDXProt.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// Key-Lock (Dongle)
|
||||
protection = KeyLock.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// LaserLock
|
||||
protection = LaserLock.CheckContents(file, fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// PE Compact
|
||||
protection = PECompact.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// ProtectDisc
|
||||
protection = ProtectDisc.CheckContents(file, fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// Ring PROTECH
|
||||
protection = RingPROTECH.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// SafeDisc / SafeCast
|
||||
protection = SafeDisc.CheckContents(file, fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// SafeLock
|
||||
protection = SafeLock.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// SecuROM
|
||||
protection = SecuROM.CheckContents(file, fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// SmartE
|
||||
protection = SmartE.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// SolidShield
|
||||
protection = SolidShield.CheckContents(file, fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// StarForce
|
||||
protection = StarForce.CheckContents(file, fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// SVK Protector
|
||||
protection = SVKProtector.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// Sysiphus / Sysiphus DVD
|
||||
protection = Sysiphus.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// TAGES
|
||||
protection = Tages.CheckContents(file, fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// VOB ProtectCD/DVD
|
||||
protection = VOBProtectCDDVD.CheckContents(file, fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// Wise Installer
|
||||
subProtections = WiseInstaller.CheckContents(file, fileContent, includePosition);
|
||||
if (subProtections != null && subProtections.Count > 0)
|
||||
protections.AddRange(subProtections);
|
||||
|
||||
// WTM CD Protect
|
||||
protection = WTMCDProtect.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
|
||||
// Xtreme-Protector
|
||||
protection = XtremeProtector.CheckContents(fileContent, includePosition);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
protections.Add(protection);
|
||||
// If we have an IScannable implementation
|
||||
if (contentCheckClass is IScannable scannable)
|
||||
{
|
||||
if (file != null && !string.IsNullOrEmpty(protection))
|
||||
{
|
||||
var subProtections = scannable.Scan(scanner, null, file);
|
||||
Utilities.PrependToKeys(subProtections, file);
|
||||
Utilities.AppendToDictionary(protections, subProtections);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize all IContentCheck implementations
|
||||
/// </summary>
|
||||
private static IEnumerable<IContentCheck> InitContentCheckClasses()
|
||||
{
|
||||
return Assembly.GetExecutingAssembly().GetTypes()
|
||||
.Where(t => t.IsClass && t.GetInterface(nameof(IContentCheck)) != null)
|
||||
.Select(t => Activator.CreateInstance(t) as IContentCheck);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if a protection should be added or not
|
||||
/// </summary>
|
||||
/// <param name="contentCheckClass">Class that was last used to check</param>
|
||||
/// <param name="scanner">Scanner object for state tracking</param>
|
||||
/// <param name="protection">The protection result to be checked</param>
|
||||
private bool ShouldAddProtection(IContentCheck contentCheckClass, Scanner scanner, string protection)
|
||||
{
|
||||
// If we have a valid content check based on settings
|
||||
if (!contentCheckClass.GetType().Namespace.ToLowerInvariant().Contains("packertype") || scanner.ScanPackers)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.GZip;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
internal class GZIP
|
||||
public class GZIP : IScannable
|
||||
{
|
||||
public static bool ShouldScan(byte[] magic)
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
if (magic.StartsWith(new byte[] { 0x1f, 0x8b }))
|
||||
if (magic.StartsWith(new byte?[] { 0x1f, 0x8b }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<string> Scan(Stream stream, bool includePosition = false)
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
List<string> protections = new List<string>();
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// If the gzip file itself fails
|
||||
try
|
||||
{
|
||||
@@ -37,34 +50,31 @@ namespace BurnOutSharp.FileType
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
string tempfile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempfile);
|
||||
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
|
||||
|
||||
// If tempfile cleanup fails
|
||||
try
|
||||
{
|
||||
File.Delete(tempfile);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (!string.IsNullOrEmpty(protection))
|
||||
protections.Add($"\r\n{entry.Key} - {protection}");
|
||||
string tempFile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return protections;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
296
BurnOutSharp/FileType/IniFile.cs
Normal file
296
BurnOutSharp/FileType/IniFile.cs
Normal file
@@ -0,0 +1,296 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
public class IniFile : IDictionary<string, string>
|
||||
{
|
||||
private Dictionary<string, string> _keyValuePairs = new Dictionary<string, string>();
|
||||
|
||||
public string this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_keyValuePairs == null)
|
||||
_keyValuePairs = new Dictionary<string, string>();
|
||||
|
||||
key = key.ToLowerInvariant();
|
||||
if (_keyValuePairs.ContainsKey(key))
|
||||
return _keyValuePairs[key];
|
||||
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_keyValuePairs == null)
|
||||
_keyValuePairs = new Dictionary<string, string>();
|
||||
|
||||
key = key.ToLowerInvariant();
|
||||
_keyValuePairs[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty INI file
|
||||
/// </summary>
|
||||
public IniFile()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate an INI file from path
|
||||
/// </summary>
|
||||
public IniFile(string path)
|
||||
{
|
||||
this.Parse(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate an INI file from stream
|
||||
/// </summary>
|
||||
public IniFile(Stream stream)
|
||||
{
|
||||
this.Parse(stream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add or update a key and value to the INI file
|
||||
/// </summary>
|
||||
public void AddOrUpdate(string key, string value)
|
||||
{
|
||||
_keyValuePairs[key.ToLowerInvariant()] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a key from the INI file
|
||||
/// </summary>
|
||||
public void Remove(string key)
|
||||
{
|
||||
_keyValuePairs.Remove(key.ToLowerInvariant());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an INI file based on the path
|
||||
/// </summary>
|
||||
public bool Parse(string path)
|
||||
{
|
||||
// If we don't have a file, we can't read it
|
||||
if (!File.Exists(path))
|
||||
return false;
|
||||
|
||||
using (var fileStream = File.OpenRead(path))
|
||||
{
|
||||
return Parse(fileStream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an INI file from a stream
|
||||
/// </summary>
|
||||
public bool Parse(Stream stream)
|
||||
{
|
||||
// If the stream is invalid or unreadable, we can't process it
|
||||
if (stream == null || !stream.CanRead || stream.Position >= stream.Length - 1)
|
||||
return false;
|
||||
|
||||
// Keys are case-insensitive by default
|
||||
try
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(stream))
|
||||
{
|
||||
string section = string.Empty;
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
string line = sr.ReadLine().Trim();
|
||||
|
||||
// Comments start with ';'
|
||||
if (line.StartsWith(";"))
|
||||
{
|
||||
// No-op, we don't process comments
|
||||
}
|
||||
|
||||
// Section titles are surrounded by square brackets
|
||||
else if (line.StartsWith("["))
|
||||
{
|
||||
section = line.TrimStart('[').TrimEnd(']');
|
||||
}
|
||||
|
||||
// Valid INI lines are in the format key=value
|
||||
else if (line.Contains("="))
|
||||
{
|
||||
// Split the line by '=' for key-value pairs
|
||||
string[] data = line.Split('=');
|
||||
|
||||
// If the value field contains an '=', we need to put them back in
|
||||
string key = data[0].Trim();
|
||||
string value = string.Join("=", data.Skip(1)).Trim();
|
||||
|
||||
// Section names are prepended to the key with a '.' separating
|
||||
if (!string.IsNullOrEmpty(section))
|
||||
key = $"{section}.{key}";
|
||||
|
||||
// Set or overwrite keys in the returned dictionary
|
||||
_keyValuePairs[key.ToLowerInvariant()] = value;
|
||||
}
|
||||
|
||||
// All other lines are ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was, just catch and return
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an INI file to a path
|
||||
/// </summary>
|
||||
public bool Write(string path)
|
||||
{
|
||||
// If we don't have a valid dictionary with values, we can't write out
|
||||
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
|
||||
return false;
|
||||
|
||||
using (var fileStream = File.OpenWrite(path))
|
||||
{
|
||||
return Write(fileStream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an INI file to a stream
|
||||
/// </summary>
|
||||
public bool Write(Stream stream)
|
||||
{
|
||||
// If we don't have a valid dictionary with values, we can't write out
|
||||
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
|
||||
return false;
|
||||
|
||||
// If the stream is invalid or unwritable, we can't output to it
|
||||
if (stream == null || !stream.CanWrite || stream.Position >= stream.Length - 1)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
using (StreamWriter sw = new StreamWriter(stream))
|
||||
{
|
||||
// Order the dictionary by keys to link sections together
|
||||
var orderedKeyValuePairs = _keyValuePairs.OrderBy(kvp => kvp.Key);
|
||||
|
||||
string section = string.Empty;
|
||||
foreach (var keyValuePair in orderedKeyValuePairs)
|
||||
{
|
||||
// Extract the key and value
|
||||
string key = keyValuePair.Key;
|
||||
string value = keyValuePair.Value;
|
||||
|
||||
// We assume '.' is a section name separator
|
||||
if (key.Contains('.'))
|
||||
{
|
||||
// Split the key by '.'
|
||||
string[] data = keyValuePair.Key.Split('.');
|
||||
|
||||
// If the key contains an '.', we need to put them back in
|
||||
string newSection = data[0].Trim();
|
||||
key = string.Join(".", data.Skip(1)).Trim();
|
||||
|
||||
// If we have a new section, write it out
|
||||
if (!string.Equals(newSection, section, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
sw.WriteLine($"[{newSection}]");
|
||||
section = newSection;
|
||||
}
|
||||
}
|
||||
|
||||
// Now write out the key and value in a standardized way
|
||||
sw.WriteLine($"{key}={value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was, just catch and return
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region IDictionary Impelementations
|
||||
|
||||
public ICollection<string> Keys => ((IDictionary<string, string>)_keyValuePairs).Keys;
|
||||
|
||||
public ICollection<string> Values => ((IDictionary<string, string>)_keyValuePairs).Values;
|
||||
|
||||
public int Count => ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Count;
|
||||
|
||||
public bool IsReadOnly => ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).IsReadOnly;
|
||||
|
||||
public void Add(string key, string value)
|
||||
{
|
||||
((IDictionary<string, string>)_keyValuePairs).Add(key.ToLowerInvariant(), value);
|
||||
}
|
||||
|
||||
bool IDictionary<string, string>.Remove(string key)
|
||||
{
|
||||
return ((IDictionary<string, string>)_keyValuePairs).Remove(key.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public bool TryGetValue(string key, out string value)
|
||||
{
|
||||
return ((IDictionary<string, string>)_keyValuePairs).TryGetValue(key.ToLowerInvariant(), out value);
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<string, string> item)
|
||||
{
|
||||
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
|
||||
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Add(newItem);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Clear();
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<string, string> item)
|
||||
{
|
||||
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
|
||||
return ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Contains(newItem);
|
||||
}
|
||||
|
||||
public bool ContainsKey(string key)
|
||||
{
|
||||
return _keyValuePairs.ContainsKey(key.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
|
||||
{
|
||||
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<string, string> item)
|
||||
{
|
||||
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
|
||||
return ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Remove(newItem);
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<KeyValuePair<string, string>>)_keyValuePairs).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable)_keyValuePairs).GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
101
BurnOutSharp/FileType/InstallShieldArchiveV3.cs
Normal file
101
BurnOutSharp/FileType/InstallShieldArchiveV3.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using BurnOutSharp.Tools;
|
||||
using UnshieldSharp.Archive;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
public class InstallShieldArchiveV3 : IScannable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
if (magic.StartsWith(new byte?[] { 0x13, 0x5D, 0x65, 0x8C }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// Get the name of the first cabinet file or header
|
||||
string directory = Path.GetDirectoryName(file);
|
||||
string noExtension = Path.GetFileNameWithoutExtension(file);
|
||||
string filenamePattern = Path.Combine(directory, noExtension);
|
||||
filenamePattern = new Regex(@"\d+$").Replace(filenamePattern, string.Empty);
|
||||
|
||||
bool cabinetHeaderExists = File.Exists(Path.Combine(directory, filenamePattern + "1.hdr"));
|
||||
bool shouldScanCabinet = cabinetHeaderExists
|
||||
? file.Equals(Path.Combine(directory, filenamePattern + "1.hdr"), StringComparison.OrdinalIgnoreCase)
|
||||
: file.Equals(Path.Combine(directory, filenamePattern + "1.cab"), StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// If we have the first file
|
||||
if (shouldScanCabinet)
|
||||
{
|
||||
// If the cab file itself fails
|
||||
try
|
||||
{
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
UnshieldSharp.Archive.InstallShieldArchiveV3 archive = new UnshieldSharp.Archive.InstallShieldArchiveV3(file);
|
||||
foreach (CompressedFile cfile in archive.Files.Select(kvp => kvp.Value))
|
||||
{
|
||||
// If an individual entry fails
|
||||
try
|
||||
{
|
||||
string tempFile = Path.Combine(tempPath, cfile.FullPath);
|
||||
if (!Directory.Exists(Path.GetDirectoryName(tempFile)))
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(tempFile));
|
||||
|
||||
(byte[] fileContents, string error) = archive.Extract(cfile.FullPath);
|
||||
if (!string.IsNullOrWhiteSpace(error))
|
||||
continue;
|
||||
|
||||
using (FileStream fs = File.OpenWrite(tempFile))
|
||||
{
|
||||
fs.Write(fileContents, 0, fileContents.Length);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnshieldSharp;
|
||||
using BurnOutSharp.Tools;
|
||||
using UnshieldSharp.Cabinet;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
internal class InstallShieldCAB
|
||||
public class InstallShieldCAB : IScannable
|
||||
{
|
||||
public static bool ShouldScan(byte[] magic)
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
if (magic.StartsWith(new byte[] { 0x49, 0x53, 0x63 }))
|
||||
if (magic.StartsWith(new byte?[] { 0x49, 0x53, 0x63 }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
public static List<string> Scan(string file, bool includePosition = false)
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
List<string> protections = new List<string>();
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// Get the name of the first cabinet file or header
|
||||
string directory = Path.GetDirectoryName(file);
|
||||
string noExtension = Path.GetFileNameWithoutExtension(file);
|
||||
@@ -41,42 +54,37 @@ namespace BurnOutSharp.FileType
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
UnshieldCabinet cabfile = UnshieldCabinet.Open(file);
|
||||
InstallShieldCabinet cabfile = InstallShieldCabinet.Open(file);
|
||||
for (int i = 0; i < cabfile.FileCount; i++)
|
||||
{
|
||||
// If an individual entry fails
|
||||
try
|
||||
{
|
||||
string tempFile = Path.Combine(tempPath, cabfile.FileName(i));
|
||||
if (cabfile.FileSave(i, tempFile))
|
||||
{
|
||||
string protection = ProtectionFind.ScanContent(tempFile, includePosition);
|
||||
|
||||
// If tempfile cleanup fails
|
||||
try
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (!string.IsNullOrEmpty(protection))
|
||||
protections.Add($"\r\n{cabfile.FileName(i)} - {protection}");
|
||||
}
|
||||
cabfile.FileSave(i, tempFile);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return protections;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
using StormLibSharp;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
internal class MPQ
|
||||
public class MPQ : IScannable
|
||||
{
|
||||
public static bool ShouldScan(byte[] magic)
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
if (magic.StartsWith(new byte[] { 0x4d, 0x50, 0x51, 0x1a }))
|
||||
if (magic.StartsWith(new byte?[] { 0x4d, 0x50, 0x51, 0x1a }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
public static List<string> Scan(string file, bool includePosition = false)
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
List<string> protections = new List<string>();
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// If the mpq file itself fails
|
||||
try
|
||||
{
|
||||
@@ -28,51 +41,55 @@ namespace BurnOutSharp.FileType
|
||||
|
||||
using (MpqArchive mpqArchive = new MpqArchive(file, FileAccess.Read))
|
||||
{
|
||||
// Try to open the listfile
|
||||
string listfile = null;
|
||||
MpqFileStream listStream = mpqArchive.OpenFile("(listfile)");
|
||||
bool canRead = listStream.CanRead;
|
||||
|
||||
// If we can't read the listfile, we just return
|
||||
if (!listStream.CanRead)
|
||||
return null;
|
||||
|
||||
// Read the listfile in for processing
|
||||
using (StreamReader sr = new StreamReader(listStream))
|
||||
{
|
||||
listfile = sr.ReadToEnd();
|
||||
Console.WriteLine(listfile);
|
||||
}
|
||||
|
||||
string sub = string.Empty;
|
||||
while ((sub = listfile) != null)
|
||||
// Split the listfile by newlines
|
||||
string[] listfileLines = listfile.Replace("\r\n", "\n").Split('\n');
|
||||
|
||||
// Loop over each entry
|
||||
foreach (string sub in listfileLines)
|
||||
{
|
||||
// If an individual entry fails
|
||||
try
|
||||
{
|
||||
string tempfile = Path.Combine(tempPath, sub);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(tempfile));
|
||||
mpqArchive.ExtractFile(sub, tempfile);
|
||||
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
|
||||
|
||||
// If tempfile cleanup fails
|
||||
try
|
||||
{
|
||||
File.Delete(tempfile);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (!string.IsNullOrEmpty(protection))
|
||||
protections.Add($"\r\n{sub} - {protection}");
|
||||
string tempFile = Path.Combine(tempPath, sub);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(tempFile));
|
||||
mpqArchive.ExtractFile(sub, tempFile);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return protections;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
67
BurnOutSharp/FileType/MSI.cs
Normal file
67
BurnOutSharp/FileType/MSI.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
using WixToolset.Dtf.WindowsInstaller;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
public class MSI : IScannable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
if (magic.StartsWith(new byte?[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// If the MSI file itself fails
|
||||
try
|
||||
{
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
using (Database msidb = new Database(file, DatabaseOpenMode.ReadOnly))
|
||||
{
|
||||
msidb.ExportAll(tempPath);
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
using LibMSPackN;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
internal class MicrosoftCAB
|
||||
// Specification available at http://download.microsoft.com/download/5/0/1/501ED102-E53F-4CE0-AA6B-B0F93629DDC6/Exchange/%5BMS-CAB%5D.pdf
|
||||
public class MicrosoftCAB : IScannable
|
||||
{
|
||||
public static bool ShouldScan(byte[] magic)
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
if (magic.StartsWith(new byte[] { 0x4d, 0x53, 0x43, 0x46 }))
|
||||
if (magic.StartsWith(new byte?[] { 0x4d, 0x53, 0x43, 0x46 }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
public static List<string> Scan(string file, bool includePosition = false)
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
List<string> protections = new List<string>();
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// If the cab file itself fails
|
||||
try
|
||||
{
|
||||
@@ -33,34 +47,32 @@ namespace BurnOutSharp.FileType
|
||||
// If an individual entry fails
|
||||
try
|
||||
{
|
||||
string tempfile = Path.Combine(tempPath, sub.Filename);
|
||||
sub.ExtractTo(tempfile);
|
||||
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
|
||||
|
||||
// If tempfile cleanup fails
|
||||
try
|
||||
{
|
||||
File.Delete(tempfile);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (!string.IsNullOrEmpty(protection))
|
||||
protections.Add($"\r\n{sub.Filename} - {protection}");
|
||||
// The trim here is for some very odd and stubborn files
|
||||
string tempFile = Path.Combine(tempPath, sub.Filename.TrimEnd('.'));
|
||||
sub.ExtractTo(tempFile);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return protections;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Zip;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
internal class PKZIP
|
||||
public class PKZIP : IScannable
|
||||
{
|
||||
public static bool ShouldScan(byte[] magic)
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
// PKZIP
|
||||
if (magic.StartsWith(new byte[] { 0x50, 0x4b, 0x03, 0x04 }))
|
||||
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x03, 0x04 }))
|
||||
return true;
|
||||
|
||||
// PKZIP (Empty Archive)
|
||||
if (magic.StartsWith(new byte[] { 0x50, 0x4b, 0x05, 0x06 }))
|
||||
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x05, 0x06 }))
|
||||
return true;
|
||||
|
||||
// PKZIP (Spanned Archive)
|
||||
if (magic.StartsWith(new byte[] { 0x50, 0x4b, 0x07, 0x08 }))
|
||||
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x07, 0x08 }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<string> Scan(Stream stream, bool includePosition = false)
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
List<string> protections = new List<string>();
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// If the zip file itself fails
|
||||
try
|
||||
{
|
||||
@@ -46,34 +59,31 @@ namespace BurnOutSharp.FileType
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
string tempfile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempfile);
|
||||
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
|
||||
|
||||
// If tempfile cleanup fails
|
||||
try
|
||||
{
|
||||
File.Delete(tempfile);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (!string.IsNullOrEmpty(protection))
|
||||
protections.Add($"\r\n{entry.Key} - {protection}");
|
||||
string tempFile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return protections;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Rar;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
internal class RAR
|
||||
public class RAR : IScannable
|
||||
{
|
||||
public static bool ShouldScan(byte[] magic)
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
// RAR archive version 1.50 onwards
|
||||
if (magic.StartsWith(new byte[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 }))
|
||||
if (magic.StartsWith(new byte?[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 }))
|
||||
return true;
|
||||
|
||||
// RAR archive version 5.0 onwards
|
||||
if (magic.StartsWith(new byte[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 }))
|
||||
if (magic.StartsWith(new byte?[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<string> Scan(Stream stream, bool includePosition = false)
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
List<string> protections = new List<string>();
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// If the rar file itself fails
|
||||
try
|
||||
{
|
||||
@@ -42,34 +55,31 @@ namespace BurnOutSharp.FileType
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
string tempfile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempfile);
|
||||
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
|
||||
|
||||
// If tempfile cleanup fails
|
||||
try
|
||||
{
|
||||
File.Delete(tempfile);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (!string.IsNullOrEmpty(protection))
|
||||
protections.Add($"\r\n{entry.Key} - {protection}");
|
||||
string tempFile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return protections;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.SevenZip;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
internal class SevenZip
|
||||
public class SevenZip : IScannable
|
||||
{
|
||||
public static bool ShouldScan(byte[] magic)
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
if (magic.StartsWith(new byte[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c }))
|
||||
if (magic.StartsWith(new byte?[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<string> Scan(Stream stream, bool includePosition = false)
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
List<string> protections = new List<string>();
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// If the 7-zip file itself fails
|
||||
try
|
||||
{
|
||||
@@ -37,34 +50,31 @@ namespace BurnOutSharp.FileType
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
string tempfile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempfile);
|
||||
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
|
||||
|
||||
// If tempfile cleanup fails
|
||||
try
|
||||
{
|
||||
File.Delete(tempfile);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (!string.IsNullOrEmpty(protection))
|
||||
protections.Add($"\r\n{entry.Key} - {protection}");
|
||||
string tempFile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return protections;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Tar;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
internal class TapeArchive
|
||||
public class TapeArchive : IScannable
|
||||
{
|
||||
public static bool ShouldScan(byte[] magic)
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
if (magic.StartsWith(new byte[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30 }))
|
||||
if (magic.StartsWith(new byte?[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30 }))
|
||||
return true;
|
||||
|
||||
if (magic.StartsWith(new byte[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00 }))
|
||||
if (magic.StartsWith(new byte?[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00 }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<string> Scan(Stream stream, bool includePosition = false)
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
List<string> protections = new List<string>();
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// If the tar file itself fails
|
||||
try
|
||||
{
|
||||
@@ -40,34 +53,31 @@ namespace BurnOutSharp.FileType
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
string tempfile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempfile);
|
||||
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
|
||||
|
||||
// If tempfile cleanup fails
|
||||
try
|
||||
{
|
||||
File.Delete(tempfile);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (!string.IsNullOrEmpty(protection))
|
||||
protections.Add($"\r\n{entry.Key} - {protection}");
|
||||
string tempFile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return protections;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Tools;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
internal class Textfile
|
||||
public class Textfile : IScannable
|
||||
{
|
||||
public static bool ShouldScan(byte[] magic, string extension)
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
return ShouldScan(magic, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a file signature or extension matches one of the expected values
|
||||
/// </summary>
|
||||
/// <param name="magic">Byte array representing the file header</param>
|
||||
/// <param name="extension">Extension for the file being checked</param>
|
||||
/// <returns>True if the signature is valid, false otherwise</returns>
|
||||
public bool ShouldScan(byte[] magic, string extension)
|
||||
{
|
||||
// Rich Text File
|
||||
if (magic.StartsWith(new byte[] { 0x7b, 0x5c, 0x72, 0x74, 0x66, 0x31 }))
|
||||
if (magic.StartsWith(new byte?[] { 0x7b, 0x5c, 0x72, 0x74, 0x66, 0x31 }))
|
||||
return true;
|
||||
|
||||
// HTML
|
||||
if (magic.StartsWith(new byte[] { 0x3c, 0x68, 0x74, 0x6d, 0x6c }))
|
||||
if (magic.StartsWith(new byte?[] { 0x3c, 0x68, 0x74, 0x6d, 0x6c }))
|
||||
return true;
|
||||
|
||||
// HTML and XML
|
||||
if (magic.StartsWith(new byte[] { 0x3c, 0x21, 0x44, 0x4f, 0x43, 0x54, 0x59, 0x50, 0x45 }))
|
||||
if (magic.StartsWith(new byte?[] { 0x3c, 0x21, 0x44, 0x4f, 0x43, 0x54, 0x59, 0x50, 0x45 }))
|
||||
return true;
|
||||
|
||||
// Microsoft Office File (old)
|
||||
if (magic.StartsWith(new byte[] { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }))
|
||||
if (magic.StartsWith(new byte?[] { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }))
|
||||
return true;
|
||||
|
||||
// Generic textfile (no header)
|
||||
if (string.Equals(extension, "txt", StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(extension?.TrimStart('.'), "txt", StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<string> Scan(Stream stream, bool includePosition = false)
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
List<string> protections = new List<string>();
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// Files can be protected in multiple ways
|
||||
var protections = new ConcurrentDictionary<string, ConcurrentQueue<string>>();
|
||||
|
||||
try
|
||||
{
|
||||
// Load the current file content
|
||||
string fileContent = null;
|
||||
using (StreamReader sr = new StreamReader(stream, Encoding.Default, false, 1024 * 1024, true))
|
||||
using (var sr = new StreamReader(stream, Encoding.Default, true, 1024 * 1024, true))
|
||||
{
|
||||
fileContent = sr.ReadToEnd();
|
||||
}
|
||||
|
||||
// CD-Key
|
||||
if (fileContent.Contains("a valid serial number is required")
|
||||
|| fileContent.Contains("serial number is located"))
|
||||
{
|
||||
protections.Add("CD-Key / Serial");
|
||||
}
|
||||
if (fileContent.Contains("a valid serial number is required"))
|
||||
Utilities.AppendToDictionary(protections, file, "CD-Key / Serial");
|
||||
else if (fileContent.Contains("serial number is located"))
|
||||
Utilities.AppendToDictionary(protections, file, "CD-Key / Serial");
|
||||
|
||||
// MediaMax
|
||||
if (fileContent.Contains("MediaMax technology"))
|
||||
Utilities.AppendToDictionary(protections, file, "MediaMax CD-3");
|
||||
|
||||
// XCP
|
||||
if (fileContent.Contains("http://cp.sonybmg.com/xcp/"))
|
||||
Utilities.AppendToDictionary(protections, file, "XCP");
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,25 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
using SharpCompress.Compressors.Xz;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
{
|
||||
internal class XZ
|
||||
public class XZ : IScannable
|
||||
{
|
||||
public static bool ShouldScan(byte[] magic)
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
if (magic.StartsWith(new byte[] { 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00 }))
|
||||
if (magic.StartsWith(new byte?[] { 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00 }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<string> Scan(Stream stream, bool includePosition = false)
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
List<string> protections = new List<string>();
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
// If the 7-zip file itself fails
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
// If the xz file itself fails
|
||||
try
|
||||
{
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
@@ -30,37 +43,33 @@ namespace BurnOutSharp.FileType
|
||||
// If an individual entry fails
|
||||
try
|
||||
{
|
||||
string tempfile = Path.Combine(tempPath, Guid.NewGuid().ToString());
|
||||
using (FileStream fs = File.OpenWrite(tempfile))
|
||||
string tempFile = Path.Combine(tempPath, Guid.NewGuid().ToString());
|
||||
using (FileStream fs = File.OpenWrite(tempFile))
|
||||
{
|
||||
xzFile.CopyTo(fs);
|
||||
}
|
||||
|
||||
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
|
||||
|
||||
// If tempfile cleanup fails
|
||||
try
|
||||
{
|
||||
File.Delete(tempfile);
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (!string.IsNullOrEmpty(protection))
|
||||
protections.Add($"\r\n{protection}");
|
||||
}
|
||||
catch { }
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return protections;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
BurnOutSharp/IContentCheck.cs
Normal file
19
BurnOutSharp/IContentCheck.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using BurnOutSharp.ExecutableType.Microsoft;
|
||||
|
||||
namespace BurnOutSharp
|
||||
{
|
||||
// TODO: This should either include an override that takes a Stream instead of the byte[]
|
||||
// OR have a completely separate check for when it's an executable specifically
|
||||
internal interface IContentCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a path for protections based on file contents
|
||||
/// </summary>
|
||||
/// <param name="pex">PortableExecutable representing the read-in file</param>
|
||||
/// <param name="file">File to check for protection indicators</param>
|
||||
/// <param name="fileContent">Byte array representing the file contents</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>String containing any protections found in the file</returns>
|
||||
string CheckContents(string file, byte[] fileContent, bool includeDebug, PortableExecutable pex, NewExecutable nex);
|
||||
}
|
||||
}
|
||||
23
BurnOutSharp/IPathCheck.cs
Normal file
23
BurnOutSharp/IPathCheck.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BurnOutSharp
|
||||
{
|
||||
internal interface IPathCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a file path for protections based on path name
|
||||
/// </summary>
|
||||
/// <param name="path">Path to check for protection indicators</param>
|
||||
/// <param name="files">Enumerable of strings representing files in a directory</param>
|
||||
/// <remarks>This can do some limited content checking as well, but it's suggested to use IContentCheck instead, if possible</remarks>
|
||||
ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files);
|
||||
|
||||
/// <summary>
|
||||
/// Check a file path for protections based on path name
|
||||
/// </summary>
|
||||
/// <param name="path">Path to check for protection indicators</param>
|
||||
/// <remarks>This can do some limited content checking as well, but it's suggested to use IContentCheck instead, if possible</remarks>
|
||||
string CheckFilePath(string path);
|
||||
}
|
||||
}
|
||||
33
BurnOutSharp/IScannable.cs
Normal file
33
BurnOutSharp/IScannable.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
|
||||
namespace BurnOutSharp
|
||||
{
|
||||
internal interface IScannable
|
||||
{
|
||||
/// <summary>
|
||||
/// Determine if a file signature matches one of the expected values
|
||||
/// </summary>
|
||||
/// <param name="magic">Byte array representing the file header</param>
|
||||
/// <returns>True if the signature is valid, false otherwise</returns>
|
||||
bool ShouldScan(byte[] magic);
|
||||
|
||||
/// <summary>
|
||||
/// Scan a file for all internal protections
|
||||
/// </summary>
|
||||
/// <param name="scanner">Scanner object for state tracking</param>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <returns>Dictionary mapping paths to protection lists</returns>
|
||||
/// <remarks>Ideally, this should just point to the other scan implementation</remarks>
|
||||
ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file);
|
||||
|
||||
/// <summary>
|
||||
/// Scan a stream for all internal protections
|
||||
/// </summary>
|
||||
/// <param name="scanner">Scanner object for state tracking</param>
|
||||
/// <param name="stream">Stream representing the input file</param>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <returns>Dictionary mapping paths to protection lists</returns>
|
||||
ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string filename);
|
||||
}
|
||||
}
|
||||
104
BurnOutSharp/Matching/ContentMatch.cs
Normal file
104
BurnOutSharp/Matching/ContentMatch.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
namespace BurnOutSharp.Matching
|
||||
{
|
||||
/// <summary>
|
||||
/// Content matching criteria
|
||||
/// </summary>
|
||||
public class ContentMatch : IMatch<byte?[]>
|
||||
{
|
||||
/// <summary>
|
||||
/// Content to match
|
||||
/// </summary>
|
||||
public byte?[] Needle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Starting index for matching
|
||||
/// </summary>
|
||||
public int Start { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ending index for matching
|
||||
/// </summary>
|
||||
public int End { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="needle">Byte array representing the search</param>
|
||||
/// <param name="start">Optional starting index</param>
|
||||
/// <param name="end">Optional ending index</param>
|
||||
public ContentMatch(byte?[] needle, int start = -1, int end = -1)
|
||||
{
|
||||
Needle = needle;
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
|
||||
#region Matching
|
||||
|
||||
/// <summary>
|
||||
/// Get if this match can be found in a stack
|
||||
/// </summary>
|
||||
/// <param name="stack">Array to search for the given content</param>
|
||||
/// <param name="reverse">True to search from the end of the array, false from the start</param>
|
||||
/// <returns>Tuple of success and found position</returns>
|
||||
public (bool success, int position) Match(byte[] stack, bool reverse = false)
|
||||
{
|
||||
// If either array is null or empty, we can't do anything
|
||||
if (stack == null || stack.Length == 0 || Needle == null || Needle.Length == 0)
|
||||
return (false, -1);
|
||||
|
||||
// If the needle array is larger than the stack array, it can't be contained within
|
||||
if (Needle.Length > stack.Length)
|
||||
return (false, -1);
|
||||
|
||||
// If start or end are not set properly, set them to defaults
|
||||
if (Start < 0)
|
||||
Start = 0;
|
||||
if (End < 0)
|
||||
End = stack.Length - Needle.Length;
|
||||
|
||||
for (int i = reverse ? End : Start; reverse ? i > Start : i < End; i += reverse ? -1 : 1)
|
||||
{
|
||||
// If we somehow have an invalid end and we haven't matched, return
|
||||
if (i > stack.Length)
|
||||
return (false, -1);
|
||||
|
||||
// Check to see if the values are equal
|
||||
if (EqualAt(stack, i))
|
||||
return (true, i);
|
||||
}
|
||||
|
||||
return (false, -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get if a stack at a certain index is equal to a needle
|
||||
/// </summary>
|
||||
/// <param name="stack">Array to search for the given content</param>
|
||||
/// <param name="index">Starting index to check equality</param>
|
||||
/// <returns>True if the needle matches the stack at a given index</returns>
|
||||
private bool EqualAt(byte[] stack, int index)
|
||||
{
|
||||
// If the index is invalid, we can't do anything
|
||||
if (index < 0)
|
||||
return false;
|
||||
|
||||
// If we're too close to the end of the stack, return false
|
||||
if (Needle.Length >= stack.Length - index)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < Needle.Length; i++)
|
||||
{
|
||||
// A null value is a wildcard
|
||||
if (Needle[i] == null)
|
||||
continue;
|
||||
else if (stack[i + index] != Needle[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
109
BurnOutSharp/Matching/ContentMatchSet.cs
Normal file
109
BurnOutSharp/Matching/ContentMatchSet.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BurnOutSharp.Matching
|
||||
{
|
||||
/// <summary>
|
||||
/// A set of content matches that work together
|
||||
/// </summary>
|
||||
public class ContentMatchSet : MatchSet<ContentMatch, byte?[]>
|
||||
{
|
||||
/// <summary>
|
||||
/// Function to get a content version
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A content version method takes the file path, the file contents,
|
||||
/// and a list of found positions and returns a single string. That
|
||||
/// string is either a version string, in which case it will be appended
|
||||
/// to the protection name, or `null`, in which case it will cause
|
||||
/// the protection to be omitted.
|
||||
/// </remarks>
|
||||
public Func<string, byte[], List<int>, string> GetVersion { get; set; }
|
||||
|
||||
#region Constructors
|
||||
|
||||
public ContentMatchSet(byte?[] needle, string protectionName)
|
||||
: this(new List<byte?[]> { needle }, null, protectionName) { }
|
||||
|
||||
public ContentMatchSet(List<byte?[]> needles, string protectionName)
|
||||
: this(needles, null, protectionName) { }
|
||||
|
||||
public ContentMatchSet(byte?[] needle, Func<string, byte[], List<int>, string> getVersion, string protectionName)
|
||||
: this(new List<byte?[]> { needle }, getVersion, protectionName) { }
|
||||
|
||||
public ContentMatchSet(List<byte?[]> needles, Func<string, byte[], List<int>, string> getVersion, string protectionName)
|
||||
: this(needles.Select(n => new ContentMatch(n)).ToList(), getVersion, protectionName) { }
|
||||
|
||||
public ContentMatchSet(ContentMatch needle, string protectionName)
|
||||
: this(new List<ContentMatch>() { needle }, null, protectionName) { }
|
||||
|
||||
public ContentMatchSet(List<ContentMatch> needles, string protectionName)
|
||||
: this(needles, null, protectionName) { }
|
||||
|
||||
public ContentMatchSet(ContentMatch needle, Func<string, byte[], List<int>, string> getVersion, string protectionName)
|
||||
: this(new List<ContentMatch>() { needle }, getVersion, protectionName) { }
|
||||
|
||||
public ContentMatchSet(List<ContentMatch> needles, Func<string, byte[], List<int>, string> getVersion, string protectionName)
|
||||
{
|
||||
Matchers = needles;
|
||||
GetVersion = getVersion;
|
||||
ProtectionName = protectionName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Matching
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether all content matches pass
|
||||
/// </summary>
|
||||
/// <param name="fileContent">Byte array representing the file contents</param>
|
||||
/// <returns>Tuple of passing status and matching positions</returns>
|
||||
public (bool, List<int>) MatchesAll(byte[] fileContent)
|
||||
{
|
||||
// If no content matches are defined, we fail out
|
||||
if (Matchers == null || !Matchers.Any())
|
||||
return (false, new List<int>());
|
||||
|
||||
// Initialize the position list
|
||||
List<int> positions = new List<int>();
|
||||
|
||||
// Loop through all content matches and make sure all pass
|
||||
foreach (var contentMatch in Matchers)
|
||||
{
|
||||
(bool match, int position) = contentMatch.Match(fileContent);
|
||||
if (!match)
|
||||
return (false, new List<int>());
|
||||
else
|
||||
positions.Add(position);
|
||||
}
|
||||
|
||||
return (true, positions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether any content matches pass
|
||||
/// </summary>
|
||||
/// <param name="fileContent">Byte array representing the file contents</param>
|
||||
/// <returns>Tuple of passing status and first matching position</returns>
|
||||
public (bool, int) MatchesAny(byte[] fileContent)
|
||||
{
|
||||
// If no content matches are defined, we fail out
|
||||
if (Matchers == null || !Matchers.Any())
|
||||
return (false, -1);
|
||||
|
||||
// Loop through all content matches and make sure all pass
|
||||
foreach (var contentMatch in Matchers)
|
||||
{
|
||||
(bool match, int position) = contentMatch.Match(fileContent);
|
||||
if (match)
|
||||
return (true, position);
|
||||
}
|
||||
|
||||
return (false, -1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
7
BurnOutSharp/Matching/IMatch.cs
Normal file
7
BurnOutSharp/Matching/IMatch.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace BurnOutSharp.Matching
|
||||
{
|
||||
public interface IMatch<T>
|
||||
{
|
||||
T Needle { get; set; }
|
||||
}
|
||||
}
|
||||
20
BurnOutSharp/Matching/MatchSet.cs
Normal file
20
BurnOutSharp/Matching/MatchSet.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BurnOutSharp.Matching
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper for a single set of matching criteria
|
||||
/// </summary>
|
||||
public abstract class MatchSet<T, U> where T : IMatch<U>
|
||||
{
|
||||
/// <summary>
|
||||
/// Set of all matchers
|
||||
/// </summary>
|
||||
public IEnumerable<T> Matchers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the protection to show
|
||||
/// </summary>
|
||||
public string ProtectionName { get; set; }
|
||||
}
|
||||
}
|
||||
238
BurnOutSharp/Matching/MatchUtil.cs
Normal file
238
BurnOutSharp/Matching/MatchUtil.cs
Normal file
@@ -0,0 +1,238 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BurnOutSharp.Matching
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for matching
|
||||
/// </summary>
|
||||
internal static class MatchUtil
|
||||
{
|
||||
#region Content Matching
|
||||
|
||||
/// <summary>
|
||||
/// Get all content matches for a given list of matchers
|
||||
/// </summary>
|
||||
/// <param name="file">File to check for matches</param>
|
||||
/// <param name="fileContent">Byte array representing the file contents</param>
|
||||
/// <param name="matchers">Enumerable of ContentMatchSets to be run on the file</param>
|
||||
/// <param name="includeDebug">True to include positional data, false otherwise</param>
|
||||
/// <returns>List of strings representing the matched protections, null or empty otherwise</returns>
|
||||
public static ConcurrentQueue<string> GetAllMatches(
|
||||
string file,
|
||||
byte[] fileContent,
|
||||
IEnumerable<ContentMatchSet> matchers,
|
||||
bool includeDebug = false)
|
||||
{
|
||||
return FindAllMatches(file, fileContent, matchers, includeDebug, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get first content match for a given list of matchers
|
||||
/// </summary>
|
||||
/// <param name="file">File to check for matches</param>
|
||||
/// <param name="fileContent">Byte array representing the file contents</param>
|
||||
/// <param name="matchers">Enumerable of ContentMatchSets to be run on the file</param>
|
||||
/// <param name="includeDebug">True to include positional data, false otherwise</param>
|
||||
/// <returns>String representing the matched protection, null otherwise</returns>
|
||||
public static string GetFirstMatch(
|
||||
string file,
|
||||
byte[] fileContent,
|
||||
IEnumerable<ContentMatchSet> matchers,
|
||||
bool includeDebug = false)
|
||||
{
|
||||
var contentMatches = FindAllMatches(file, fileContent, matchers, includeDebug, true);
|
||||
if (contentMatches == null || !contentMatches.Any())
|
||||
return null;
|
||||
|
||||
return contentMatches.First();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the required set of content matches on a per Matcher basis
|
||||
/// </summary>
|
||||
/// <param name="file">File to check for matches</param>
|
||||
/// <param name="fileContent">Byte array representing the file contents</param>
|
||||
/// <param name="matchers">Enumerable of ContentMatchSets to be run on the file</param>
|
||||
/// <param name="includeDebug">True to include positional data, false otherwise</param>
|
||||
/// <param name="stopAfterFirst">True to stop after the first match, false otherwise</param>
|
||||
/// <returns>List of strings representing the matched protections, null or empty otherwise</returns>
|
||||
private static ConcurrentQueue<string> FindAllMatches(
|
||||
string file,
|
||||
byte[] fileContent,
|
||||
IEnumerable<ContentMatchSet> matchers,
|
||||
bool includeDebug,
|
||||
bool stopAfterFirst)
|
||||
{
|
||||
// If there's no mappings, we can't match
|
||||
if (matchers == null || !matchers.Any())
|
||||
return null;
|
||||
|
||||
// Initialize the queue of matched protections
|
||||
var matchedProtections = new ConcurrentQueue<string>();
|
||||
|
||||
// Loop through and try everything otherwise
|
||||
foreach (var matcher in matchers)
|
||||
{
|
||||
// Determine if the matcher passes
|
||||
(bool passes, List<int> positions) = matcher.MatchesAll(fileContent);
|
||||
if (!passes)
|
||||
continue;
|
||||
|
||||
// Format the list of all positions found
|
||||
string positionsString = string.Join(", ", positions);
|
||||
|
||||
// If we there is no version method, just return the protection name
|
||||
if (matcher.GetVersion == null)
|
||||
{
|
||||
matchedProtections.Enqueue((matcher.ProtectionName ?? "Unknown Protection") + (includeDebug ? $" (Index {positionsString})" : string.Empty));
|
||||
}
|
||||
|
||||
// Otherwise, invoke the version method
|
||||
else
|
||||
{
|
||||
// A null version returned means the check didn't pass at the version step
|
||||
string version = matcher.GetVersion(file, fileContent, positions);
|
||||
if (version == null)
|
||||
continue;
|
||||
|
||||
matchedProtections.Enqueue($"{matcher.ProtectionName ?? "Unknown Protection"} {version}".TrimEnd() + (includeDebug ? $" (Index {positionsString})" : string.Empty));
|
||||
}
|
||||
|
||||
// If we're stopping after the first protection, bail out here
|
||||
if (stopAfterFirst)
|
||||
return matchedProtections;
|
||||
}
|
||||
|
||||
return matchedProtections;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Path Matching
|
||||
|
||||
/// <summary>
|
||||
/// Get all path matches for a given list of matchers
|
||||
/// </summary>
|
||||
/// <param name="file">File path to check for matches</param>
|
||||
/// <param name="matchers">Enumerable of PathMatchSets to be run on the file</param>
|
||||
/// <param name="any">True if any path match is a success, false if all have to match</param>
|
||||
/// <returns>List of strings representing the matched protections, null or empty otherwise</returns>
|
||||
public static ConcurrentQueue<string> GetAllMatches(string file, IEnumerable<PathMatchSet> matchers, bool any = false)
|
||||
{
|
||||
return FindAllMatches(new List<string> { file }, matchers, any, false);
|
||||
}
|
||||
|
||||
// <summary>
|
||||
/// Get all path matches for a given list of matchers
|
||||
/// </summary>
|
||||
/// <param name="files">File paths to check for matches</param>
|
||||
/// <param name="matchers">Enumerable of PathMatchSets to be run on the file</param>
|
||||
/// <param name="any">True if any path match is a success, false if all have to match</param>
|
||||
/// <returns>List of strings representing the matched protections, null or empty otherwise</returns>
|
||||
public static ConcurrentQueue<string> GetAllMatches(IEnumerable<string> files, IEnumerable<PathMatchSet> matchers, bool any = false)
|
||||
{
|
||||
return FindAllMatches(files, matchers, any, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get first path match for a given list of matchers
|
||||
/// </summary>
|
||||
/// <param name="file">File path to check for matches</param>
|
||||
/// <param name="matchers">Enumerable of PathMatchSets to be run on the file</param>
|
||||
/// <param name="any">True if any path match is a success, false if all have to match</param>
|
||||
/// <returns>String representing the matched protection, null otherwise</returns>
|
||||
public static string GetFirstMatch(string file, IEnumerable<PathMatchSet> matchers, bool any = false)
|
||||
{
|
||||
var contentMatches = FindAllMatches(new List<string> { file }, matchers, any, true);
|
||||
if (contentMatches == null || !contentMatches.Any())
|
||||
return null;
|
||||
|
||||
return contentMatches.First();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get first path match for a given list of matchers
|
||||
/// </summary>
|
||||
/// <param name="files">File paths to check for matches</param>
|
||||
/// <param name="matchers">Enumerable of PathMatchSets to be run on the file</param>
|
||||
/// <param name="any">True if any path match is a success, false if all have to match</param>
|
||||
/// <returns>String representing the matched protection, null otherwise</returns>
|
||||
public static string GetFirstMatch(IEnumerable<string> files, IEnumerable<PathMatchSet> matchers, bool any = false)
|
||||
{
|
||||
var contentMatches = FindAllMatches(files, matchers, any, true);
|
||||
if (contentMatches == null || !contentMatches.Any())
|
||||
return null;
|
||||
|
||||
return contentMatches.First();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the required set of path matches on a per Matcher basis
|
||||
/// </summary>
|
||||
/// <param name="files">File paths to check for matches</param>
|
||||
/// <param name="matchers">Enumerable of PathMatchSets to be run on the file</param>
|
||||
/// <param name="any">True if any path match is a success, false if all have to match</param>
|
||||
/// <param name="stopAfterFirst">True to stop after the first match, false otherwise</param>
|
||||
/// <returns>List of strings representing the matched protections, null or empty otherwise</returns>
|
||||
private static ConcurrentQueue<string> FindAllMatches(IEnumerable<string> files, IEnumerable<PathMatchSet> matchers, bool any, bool stopAfterFirst)
|
||||
{
|
||||
// If there's no mappings, we can't match
|
||||
if (matchers == null || !matchers.Any())
|
||||
return new ConcurrentQueue<string>();
|
||||
|
||||
// Initialize the list of matched protections
|
||||
var matchedProtections = new ConcurrentQueue<string>();
|
||||
|
||||
// Loop through and try everything otherwise
|
||||
foreach (var matcher in matchers)
|
||||
{
|
||||
// Determine if the matcher passes
|
||||
bool passes;
|
||||
string firstMatchedString;
|
||||
if (any)
|
||||
{
|
||||
(bool anyPasses, string matchedString) = matcher.MatchesAny(files);
|
||||
passes = anyPasses;
|
||||
firstMatchedString = matchedString;
|
||||
}
|
||||
else
|
||||
{
|
||||
(bool allPasses, List<string> matchedStrings) = matcher.MatchesAll(files);
|
||||
passes = allPasses;
|
||||
firstMatchedString = matchedStrings.FirstOrDefault();
|
||||
}
|
||||
|
||||
// If we don't have a pass, just continue
|
||||
if (!passes)
|
||||
continue;
|
||||
|
||||
// If we there is no version method, just return the protection name
|
||||
if (matcher.GetVersion == null)
|
||||
{
|
||||
matchedProtections.Enqueue(matcher.ProtectionName ?? "Unknown Protection");
|
||||
}
|
||||
|
||||
// Otherwise, invoke the version method
|
||||
else
|
||||
{
|
||||
// A null version returned means the check didn't pass at the version step
|
||||
string version = matcher.GetVersion(firstMatchedString, files);
|
||||
if (version == null)
|
||||
continue;
|
||||
|
||||
matchedProtections.Enqueue($"{matcher.ProtectionName ?? "Unknown Protection"} {version}".TrimEnd());
|
||||
}
|
||||
|
||||
// If we're stopping after the first protection, bail out here
|
||||
if (stopAfterFirst)
|
||||
return matchedProtections;
|
||||
}
|
||||
|
||||
return matchedProtections;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
71
BurnOutSharp/Matching/PathMatch.cs
Normal file
71
BurnOutSharp/Matching/PathMatch.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BurnOutSharp.Matching
|
||||
{
|
||||
/// <summary>
|
||||
/// Path matching criteria
|
||||
/// </summary>
|
||||
public class PathMatch : IMatch<string>
|
||||
{
|
||||
/// <summary>
|
||||
/// String to match
|
||||
/// </summary>
|
||||
public string Needle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Match exact casing instead of invariant
|
||||
/// </summary>
|
||||
public bool MatchExact { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Match that values end with the needle and not just contains
|
||||
/// </summary>
|
||||
public bool UseEndsWith { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="needle">String representing the search</param>
|
||||
/// <param name="matchExact">True to match exact casing, false otherwise</param>
|
||||
/// <param name="useEndsWith">True to match the end only, false for all contents</param>
|
||||
public PathMatch(string needle, bool matchExact = false, bool useEndsWith = false)
|
||||
{
|
||||
Needle = needle;
|
||||
MatchExact = matchExact;
|
||||
UseEndsWith = useEndsWith;
|
||||
}
|
||||
|
||||
#region Matching
|
||||
|
||||
/// <summary>
|
||||
/// Get if this match can be found in a stack
|
||||
/// </summary>
|
||||
/// <param name="stack">List of strings to search for the given content</param>
|
||||
/// <returns>Tuple of success and matched item</returns>
|
||||
public (bool, string) Match(IEnumerable<string> stack)
|
||||
{
|
||||
// If either array is null or empty, we can't do anything
|
||||
if (stack == null || !stack.Any() || Needle == null || Needle.Length == 0)
|
||||
return (false, null);
|
||||
|
||||
// Preprocess the needle, if necessary
|
||||
string procNeedle = MatchExact ? Needle : Needle.ToLowerInvariant();
|
||||
|
||||
foreach (string stackItem in stack)
|
||||
{
|
||||
// Preprocess the stack item, if necessary
|
||||
string procStackItem = MatchExact ? stackItem : stackItem.ToLowerInvariant();
|
||||
|
||||
if (UseEndsWith && procStackItem.EndsWith(procNeedle))
|
||||
return (true, stackItem);
|
||||
else if (!UseEndsWith && procStackItem.Contains(procNeedle))
|
||||
return (true, stackItem);
|
||||
}
|
||||
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
108
BurnOutSharp/Matching/PathMatchSet.cs
Normal file
108
BurnOutSharp/Matching/PathMatchSet.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BurnOutSharp.Matching
|
||||
{
|
||||
/// <summary>
|
||||
/// A set of path matches that work together
|
||||
/// </summary>
|
||||
public class PathMatchSet : MatchSet<PathMatch, string>
|
||||
{
|
||||
/// <summary>
|
||||
/// Function to get a path version for this Matcher
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A path version method takes the matched path and an enumerable of files
|
||||
/// and returns a single string. That string is either a version string,
|
||||
/// in which case it will be appended to the protection name, or `null`,
|
||||
/// in which case it will cause the protection to be omitted.
|
||||
/// </remarks>
|
||||
public Func<string, IEnumerable<string>, string> GetVersion { get; set; }
|
||||
|
||||
#region Constructors
|
||||
|
||||
public PathMatchSet(string needle, string protectionName)
|
||||
: this(new List<string> { needle }, null, protectionName) { }
|
||||
|
||||
public PathMatchSet(List<string> needles, string protectionName)
|
||||
: this(needles, null, protectionName) { }
|
||||
|
||||
public PathMatchSet(string needle, Func<string, IEnumerable<string>, string> getVersion, string protectionName)
|
||||
: this(new List<string> { needle }, getVersion, protectionName) { }
|
||||
|
||||
public PathMatchSet(List<string> needles, Func<string, IEnumerable<string>, string> getVersion, string protectionName)
|
||||
: this(needles.Select(n => new PathMatch(n)).ToList(), getVersion, protectionName) { }
|
||||
|
||||
public PathMatchSet(PathMatch needle, string protectionName)
|
||||
: this(new List<PathMatch>() { needle }, null, protectionName) { }
|
||||
|
||||
public PathMatchSet(List<PathMatch> needles, string protectionName)
|
||||
: this(needles, null, protectionName) { }
|
||||
|
||||
public PathMatchSet(PathMatch needle, Func<string, IEnumerable<string>, string> getVersion, string protectionName)
|
||||
: this(new List<PathMatch>() { needle }, getVersion, protectionName) { }
|
||||
|
||||
public PathMatchSet(List<PathMatch> needles, Func<string, IEnumerable<string>, string> getVersion, string protectionName)
|
||||
{
|
||||
Matchers = needles;
|
||||
GetVersion = getVersion;
|
||||
ProtectionName = protectionName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Matching
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether all path matches pass
|
||||
/// </summary>
|
||||
/// <param name="stack">List of strings to try to match</param>
|
||||
/// <returns>Tuple of passing status and matching values</returns>
|
||||
public (bool, List<string>) MatchesAll(IEnumerable<string> stack)
|
||||
{
|
||||
// If no path matches are defined, we fail out
|
||||
if (Matchers == null || !Matchers.Any())
|
||||
return (false, new List<string>());
|
||||
|
||||
// Initialize the value list
|
||||
List<string> values = new List<string>();
|
||||
|
||||
// Loop through all path matches and make sure all pass
|
||||
foreach (var pathMatch in Matchers)
|
||||
{
|
||||
(bool match, string value) = pathMatch.Match(stack);
|
||||
if (!match)
|
||||
return (false, new List<string>());
|
||||
else
|
||||
values.Add(value);
|
||||
}
|
||||
|
||||
return (true, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether any path matches pass
|
||||
/// </summary>
|
||||
/// <param name="stack">List of strings to try to match</param>
|
||||
/// <returns>Tuple of passing status and first matching value</returns>
|
||||
public (bool, string) MatchesAny(IEnumerable<string> stack)
|
||||
{
|
||||
// If no path matches are defined, we fail out
|
||||
if (Matchers == null || !Matchers.Any())
|
||||
return (false, null);
|
||||
|
||||
// Loop through all path matches and make sure all pass
|
||||
foreach (var pathMatch in Matchers)
|
||||
{
|
||||
(bool match, string value) = pathMatch.Match(stack);
|
||||
if (match)
|
||||
return (true, value);
|
||||
}
|
||||
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
40
BurnOutSharp/PackerType/AdvancedInstaller.cs
Normal file
40
BurnOutSharp/PackerType/AdvancedInstaller.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
using BurnOutSharp.ExecutableType.Microsoft;
|
||||
using BurnOutSharp.Matching;
|
||||
|
||||
namespace BurnOutSharp.PackerType
|
||||
{
|
||||
// TODO: Add extraction and verify that all versions are detected
|
||||
public class AdvancedInstaller : IContentCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string CheckContents(string file, byte[] fileContent, bool includeDebug, PortableExecutable pex, NewExecutable nex)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex?.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .rdata section, if it exists
|
||||
if (pex.ResourceDataSectionRaw != null)
|
||||
{
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
// Software\Caphyon\Advanced Installer
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
|
||||
0x5C, 0x43, 0x61, 0x70, 0x68, 0x79, 0x6F, 0x6E,
|
||||
0x5C, 0x41, 0x64, 0x76, 0x61, 0x6E, 0x63, 0x65,
|
||||
0x64, 0x20, 0x49, 0x6E, 0x73, 0x74, 0x61, 0x6C,
|
||||
0x6C, 0x65, 0x72
|
||||
}, "Caphyon Advanced Installer"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(file, pex.ResourceDataSectionRaw, matchers, includeDebug);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
BurnOutSharp/PackerType/Armadillo.cs
Normal file
44
BurnOutSharp/PackerType/Armadillo.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BurnOutSharp.ExecutableType.Microsoft;
|
||||
using BurnOutSharp.Matching;
|
||||
|
||||
namespace BurnOutSharp.PackerType
|
||||
{
|
||||
// TODO: Add version checking, if possible
|
||||
public class Armadillo : IContentCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string CheckContents(string file, byte[] fileContent, bool includeDebug, PortableExecutable pex, NewExecutable nex)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex?.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .nicode section, if it exists
|
||||
bool nicodeSection = pex.ContainsSection(".nicode", exact: true);
|
||||
if (nicodeSection)
|
||||
return "Armadillo";
|
||||
|
||||
// Loop through all "extension" sections -- usually .data1 or .text1
|
||||
foreach (var section in sections.Where(s => s != null && Encoding.ASCII.GetString(s.Name).Trim('\0').EndsWith("1")))
|
||||
{
|
||||
string sectionName = Encoding.ASCII.GetString(section.Name).Trim('\0');
|
||||
var sectionRaw = pex.ReadRawSection(fileContent, sectionName);
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
// ARMDEBUG
|
||||
new ContentMatchSet(new byte?[] { 0x41, 0x52, 0x4D, 0x44, 0x45, 0x42, 0x55, 0x47 }, $"Armadillo"),
|
||||
};
|
||||
|
||||
string match = MatchUtil.GetFirstMatch(file, sectionRaw, matchers, includeDebug);
|
||||
if (!string.IsNullOrWhiteSpace(match))
|
||||
return match;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
64
BurnOutSharp/PackerType/CExe.cs
Normal file
64
BurnOutSharp/PackerType/CExe.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft;
|
||||
using BurnOutSharp.Matching;
|
||||
|
||||
namespace BurnOutSharp.PackerType
|
||||
{
|
||||
// The official website for CExe also includes the source code (which does have to be retrieved by the Wayback Machine)
|
||||
// http://www.scottlu.com/Content/CExe.html
|
||||
public class CExe : IContentCheck, IScannable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic) => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string CheckContents(string file, byte[] fileContent, bool includeDebug, PortableExecutable pex, NewExecutable nex)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var stub = pex?.DOSStubHeader;
|
||||
if (stub == null)
|
||||
return null;
|
||||
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
// %Wo<57>a6.<2E>a6.<2E>a6.<2E>a6.<2E>{6.<2E>.).<2E>f6.<2E><>).<2E>`6.<2E><>0.<2E>`6.<2E>
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x25, 0x57, 0x6F, 0xC1, 0x61, 0x36, 0x01, 0x92,
|
||||
0x61, 0x36, 0x01, 0x92, 0x61, 0x36, 0x01, 0x92,
|
||||
0x61, 0x36, 0x00, 0x92, 0x7B, 0x36, 0x01, 0x92,
|
||||
0x03, 0x29, 0x12, 0x92, 0x66, 0x36, 0x01, 0x92,
|
||||
0x89, 0x29, 0x0A, 0x92, 0x60, 0x36, 0x01, 0x92,
|
||||
0xD9, 0x30, 0x07, 0x92, 0x60, 0x36, 0x01, 0x92
|
||||
}, "CExe")
|
||||
};
|
||||
|
||||
string match = MatchUtil.GetFirstMatch(file, pex.DOSStubHeader.ExecutableData, matchers, includeDebug);
|
||||
if (!string.IsNullOrWhiteSpace(match))
|
||||
return match;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
// TODO: Add extraction if viable
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
BurnOutSharp/PackerType/EXEStealth.cs
Normal file
52
BurnOutSharp/PackerType/EXEStealth.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using BurnOutSharp.ExecutableType.Microsoft;
|
||||
|
||||
namespace BurnOutSharp.PackerType
|
||||
{
|
||||
// TODO: Figure out how to more granularly determine versions like PiD,
|
||||
// at least for the 2.41 -> 2.75 range
|
||||
// TODO: Detect 3.15 and up (maybe looking for `Metamorphism`)
|
||||
public class EXEStealth : IContentCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string CheckContents(string file, byte[] fileContent, bool includeDebug, PortableExecutable pex, NewExecutable nex)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex?.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the ExeS/EXES section, if it exists
|
||||
bool exesSection = pex.ContainsSection("ExeS", exact: true) || pex.ContainsSection("EXES", exact: true);
|
||||
if (exesSection)
|
||||
return "EXE Stealth 2.41-2.75";
|
||||
|
||||
// Get the mtw section, if it exists
|
||||
bool mtwSection = pex.ContainsSection("mtw", exact: true);
|
||||
if (mtwSection)
|
||||
return "EXE Stealth 1.1";
|
||||
|
||||
// Get the rsrr section, if it exists
|
||||
bool rsrrSection = pex.ContainsSection("rsrr", exact: true);
|
||||
if (rsrrSection)
|
||||
return "EXE Stealth 2.76";
|
||||
|
||||
return null;
|
||||
|
||||
// TODO: Could not be confirmed with any sample, so disabling for now
|
||||
// var contentMatchSets = new List<ContentMatchSet>
|
||||
// {
|
||||
// // ??[[__[[_ + (char)0x00 + {{ + (char)0x0 + (char)0x00 + {{ + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x0 + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x00 + ?;??;??
|
||||
// new ContentMatchSet(new byte?[]
|
||||
// {
|
||||
// 0x3F, 0x3F, 0x5B, 0x5B, 0x5F, 0x5F, 0x5B, 0x5B,
|
||||
// 0x5F, 0x00, 0x7B, 0x7B, 0x00, 0x00, 0x7B, 0x7B,
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x20, 0x3F, 0x3B, 0x3F, 0x3F, 0x3B, 0x3F,
|
||||
// 0x3F
|
||||
// }, "EXE Stealth"),
|
||||
// };
|
||||
|
||||
// return MatchUtil.GetFirstMatch(file, fileContent, contentMatchSets, includeDebug);
|
||||
}
|
||||
}
|
||||
}
|
||||
142
BurnOutSharp/PackerType/InnoSetup.cs
Normal file
142
BurnOutSharp/PackerType/InnoSetup.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BurnOutSharp.ExecutableType.Microsoft;
|
||||
using BurnOutSharp.Matching;
|
||||
|
||||
namespace BurnOutSharp.PackerType
|
||||
{
|
||||
public class InnoSetup : IContentCheck, IScannable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic) => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string CheckContents(string file, byte[] fileContent, bool includeDebug, PortableExecutable pex, NewExecutable nex)
|
||||
{
|
||||
// Try to read the contents as a PE executable
|
||||
if (pex != null)
|
||||
{
|
||||
var sections = pex?.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the DATA/.data section, if it exists
|
||||
if (pex.DataSectionRaw != null)
|
||||
{
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
// Inno Setup Setup Data (
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x49, 0x6E, 0x6E, 0x6F, 0x20, 0x53, 0x65, 0x74,
|
||||
0x75, 0x70, 0x20, 0x53, 0x65, 0x74, 0x75, 0x70,
|
||||
0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x28
|
||||
}, GetVersion, "Inno Setup"),
|
||||
};
|
||||
|
||||
string match = MatchUtil.GetFirstMatch(file, pex.DataSectionRaw, matchers, includeDebug);
|
||||
if (!string.IsNullOrWhiteSpace(match))
|
||||
return match;
|
||||
}
|
||||
|
||||
// Get the DOS stub from the executable, if possible
|
||||
var stub = pex?.DOSStubHeader;
|
||||
if (stub == null)
|
||||
return null;
|
||||
|
||||
// Check for "Inno" in the reserved words
|
||||
if (stub.Reserved2[4] == 0x6E49 && stub.Reserved2[5] == 0x6F6E)
|
||||
{
|
||||
string version = GetOldVersion(file, fileContent);
|
||||
if (!string.IsNullOrWhiteSpace(version))
|
||||
return $"Inno Setup {version}";
|
||||
|
||||
return "Inno Setup (Unknown Version)";
|
||||
}
|
||||
}
|
||||
|
||||
// Try to read the contents as an NE executable
|
||||
if (nex != null)
|
||||
{
|
||||
// Get the DOS stub from the executable, if possible
|
||||
var stub = nex?.DOSStubHeader;
|
||||
if (stub == null)
|
||||
return null;
|
||||
|
||||
// Check for "Inno" in the reserved words
|
||||
if (stub.Reserved2[4] == 0x6E49 && stub.Reserved2[5] == 0x6F6E)
|
||||
{
|
||||
string version = GetOldVersion(file, fileContent);
|
||||
if (!string.IsNullOrWhiteSpace(version))
|
||||
return $"Inno Setup {version}";
|
||||
|
||||
return "Inno Setup (Unknown Version)";
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
}
|
||||
|
||||
// TOOO: Add Inno Setup extraction
|
||||
// https://github.com/dscharrer/InnoExtract
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetVersion(string file, byte[] fileContent, List<int> positions)
|
||||
{
|
||||
try
|
||||
{
|
||||
int index = positions[0];
|
||||
index += 23;
|
||||
|
||||
var versionBytes = new ReadOnlySpan<byte>(fileContent, index, 16).ToArray();
|
||||
var onlyVersion = versionBytes.TakeWhile(b => b != ')').ToArray();
|
||||
|
||||
index += onlyVersion.Length + 2;
|
||||
var unicodeBytes = new ReadOnlySpan<byte>(fileContent, index, 3).ToArray();
|
||||
string version = Encoding.ASCII.GetString(onlyVersion);
|
||||
|
||||
if (unicodeBytes.SequenceEqual(new byte[] { 0x28, 0x75, 0x29 }))
|
||||
return (version + " (Unicode)");
|
||||
|
||||
return version;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "(Unknown Version)";
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetOldVersion(string file, byte[] fileContent)
|
||||
{
|
||||
// TODO: Only sample is an NE executable - verify if this is in PE as well or where in the NE this lives
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
// "rDlPtS02" + (char)0x87 + "eVx"
|
||||
new ContentMatchSet(new byte?[] { 0x72, 0x44, 0x6C, 0x50, 0x74, 0x53, 0x30, 0x32, 0x87, 0x65, 0x56, 0x78 }, "1.2.16 or earlier"),
|
||||
};
|
||||
|
||||
string match = MatchUtil.GetFirstMatch(file, fileContent, matchers, false);
|
||||
return match ?? "Unknown 1.X";
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user