mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-02-04 13:45:28 +00:00
Compare commits
1263 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4a781acc7 | ||
|
|
eb7bbdde52 | ||
|
|
f470263196 | ||
|
|
3822cc41f2 | ||
|
|
f04cf25fa9 | ||
|
|
eb8b9daea8 | ||
|
|
33ecc246dc | ||
|
|
7d55c8224a | ||
|
|
0ccae4e4b7 | ||
|
|
1798371513 | ||
|
|
0858437196 | ||
|
|
ece758bf03 | ||
|
|
d9661cd03d | ||
|
|
682529d7ba | ||
|
|
27ef24636c | ||
|
|
f5f3d3d29a | ||
|
|
550fb70952 | ||
|
|
7d6248a3bf | ||
|
|
4575da77bd | ||
|
|
a85aa08117 | ||
|
|
667207761c | ||
|
|
92097222b0 | ||
|
|
5ec90b290a | ||
|
|
5de78ef552 | ||
|
|
57a5531fbd | ||
|
|
f7a4b6b43c | ||
|
|
3de56083d8 | ||
|
|
f998a578cc | ||
|
|
0fc7ce2e07 | ||
|
|
156df10e37 | ||
|
|
9490d06509 | ||
|
|
b8b70a3848 | ||
|
|
42df482ffa | ||
|
|
058dfaeb37 | ||
|
|
1622bcbe60 | ||
|
|
72629ea3a4 | ||
|
|
4ba3c3e3ad | ||
|
|
56c1fd31d4 | ||
|
|
9c27da72bb | ||
|
|
e5dc66b140 | ||
|
|
41e10a4150 | ||
|
|
3c4381049b | ||
|
|
3188c6e922 | ||
|
|
1bfb6cda08 | ||
|
|
86feb930a8 | ||
|
|
5587c79ac5 | ||
|
|
dc2a2e10de | ||
|
|
a2fdcb4f6f | ||
|
|
a355670af9 | ||
|
|
3489c67e2b | ||
|
|
99a64942ea | ||
|
|
5eab12946f | ||
|
|
683cfb6306 | ||
|
|
9a3fde0518 | ||
|
|
26d19aab37 | ||
|
|
08564ed607 | ||
|
|
0f6378cd2c | ||
|
|
34a78fc4b9 | ||
|
|
c9ee45c1d2 | ||
|
|
13f5b4f79b | ||
|
|
e5d0c5bdc4 | ||
|
|
6220382531 | ||
|
|
165896e335 | ||
|
|
284d0ea108 | ||
|
|
b04feab5da | ||
|
|
4e0442d526 | ||
|
|
d8aa4d230d | ||
|
|
07a7fd05d2 | ||
|
|
671e1ee2b6 | ||
|
|
d627e8a4c9 | ||
|
|
6ee90b28cd | ||
|
|
8dc2b019b6 | ||
|
|
f151563e28 | ||
|
|
edbbbb011d | ||
|
|
e3fd44134b | ||
|
|
71fb7318e3 | ||
|
|
aba4395139 | ||
|
|
1fd69f101f | ||
|
|
aa4c72fb90 | ||
|
|
150b9a0d67 | ||
|
|
b08921a94a | ||
|
|
3406f3c4b3 | ||
|
|
ffca156209 | ||
|
|
b842599006 | ||
|
|
86b639b04f | ||
|
|
570602aac6 | ||
|
|
4989956a91 | ||
|
|
14849f45da | ||
|
|
28ebc14fe1 | ||
|
|
54cb996fce | ||
|
|
8df58fa4d4 | ||
|
|
422add9827 | ||
|
|
59435903eb | ||
|
|
95ee417e00 | ||
|
|
df913372bf | ||
|
|
2f1c76b7f9 | ||
|
|
18e57c8182 | ||
|
|
b4e2117c4b | ||
|
|
1bb5ff9e18 | ||
|
|
a46cae469d | ||
|
|
b564ff214d | ||
|
|
0744a10de0 | ||
|
|
343ca9497e | ||
|
|
861958527d | ||
|
|
a6b9dca291 | ||
|
|
18c05cb49d | ||
|
|
ed3e58af6c | ||
|
|
e3eed76826 | ||
|
|
7eb86b223f | ||
|
|
a4ee4529ca | ||
|
|
abc68d8503 | ||
|
|
aaff4bad1b | ||
|
|
d5c81857c3 | ||
|
|
c2594cdd2d | ||
|
|
2412042cef | ||
|
|
1f5c1a8100 | ||
|
|
78cc67f30e | ||
|
|
5b78ba5621 | ||
|
|
c4734cfc3d | ||
|
|
dd45384226 | ||
|
|
3e75d9fa3b | ||
|
|
aa690ab602 | ||
|
|
7432100139 | ||
|
|
29fabb44eb | ||
|
|
ad776d4189 | ||
|
|
4cf12c76a8 | ||
|
|
39185f5ddd | ||
|
|
03477327c4 | ||
|
|
29fa0d1ac7 | ||
|
|
7eca23a7f3 | ||
|
|
f0c90bb332 | ||
|
|
0af67e5802 | ||
|
|
6ab9f730f9 | ||
|
|
258238bcc0 | ||
|
|
3936a15ef7 | ||
|
|
f6dbb349c4 | ||
|
|
3c69e02cfc | ||
|
|
531e634e62 | ||
|
|
f9c0c42b26 | ||
|
|
83aebbbfbd | ||
|
|
3847e5e9dc | ||
|
|
a72bb7e332 | ||
|
|
839791f467 | ||
|
|
469356e8c1 | ||
|
|
66e8eb985c | ||
|
|
90223e6c94 | ||
|
|
2f2cf76d7b | ||
|
|
558fee2200 | ||
|
|
a82abc05ec | ||
|
|
74df37597a | ||
|
|
1581023c01 | ||
|
|
c0d1260656 | ||
|
|
969d103c2c | ||
|
|
e5e3f3e3ef | ||
|
|
c1ee399262 | ||
|
|
74ee9932a7 | ||
|
|
e70f8d7220 | ||
|
|
ceba351372 | ||
|
|
ad4082c531 | ||
|
|
115ea02822 | ||
|
|
f876a4e4a6 | ||
|
|
be114f60d3 | ||
|
|
b2594f8148 | ||
|
|
f58ada3dde | ||
|
|
bc4f07970d | ||
|
|
25d6822283 | ||
|
|
3b22262c21 | ||
|
|
314fc1e3fc | ||
|
|
5742749dec | ||
|
|
c55fffeb7b | ||
|
|
e469dc38bf | ||
|
|
553703c30e | ||
|
|
3fd093f9b4 | ||
|
|
4946d4e7ff | ||
|
|
491fc0f71c | ||
|
|
fe6627f1ba | ||
|
|
edffa3c7cc | ||
|
|
a66d62bfbc | ||
|
|
9321b8f221 | ||
|
|
cd0863ac56 | ||
|
|
24a73e8bfd | ||
|
|
46eaa7db1e | ||
|
|
0eab7fd555 | ||
|
|
dba476d8bb | ||
|
|
b10b4d6658 | ||
|
|
2959fdbe9a | ||
|
|
9c0d100c2a | ||
|
|
03ca0faf2e | ||
|
|
cbaf004e25 | ||
|
|
bbe4fb610c | ||
|
|
650115f722 | ||
|
|
1afcbe3182 | ||
|
|
8aa90dbc49 | ||
|
|
0781524669 | ||
|
|
3b3cb7a862 | ||
|
|
810d20d95c | ||
|
|
de578511bf | ||
|
|
f1ec025950 | ||
|
|
0c58ecc548 | ||
|
|
d2a73a153b | ||
|
|
eae2e3366b | ||
|
|
afb04c99c0 | ||
|
|
1d3bd2f8b1 | ||
|
|
3f52c24713 | ||
|
|
ae1417a343 | ||
|
|
871a3e6366 | ||
|
|
3457b807cb | ||
|
|
027f295d21 | ||
|
|
63e6d1e285 | ||
|
|
2193095f70 | ||
|
|
074694298f | ||
|
|
ce4d32b053 | ||
|
|
a25af3940c | ||
|
|
9d1a2db45a | ||
|
|
5cdf269a3e | ||
|
|
b9d90ec35d | ||
|
|
5fc1d3254d | ||
|
|
d61bae8e61 | ||
|
|
f8f53869ae | ||
|
|
f5146a6e35 | ||
|
|
faf96b9375 | ||
|
|
2228e344f6 | ||
|
|
9955bdcab1 | ||
|
|
4586d49a3f | ||
|
|
1f4e24452a | ||
|
|
090bac4d59 | ||
|
|
59bedf5fce | ||
|
|
1bbc541957 | ||
|
|
1bb0107ceb | ||
|
|
10dad356cd | ||
|
|
22b6971e51 | ||
|
|
3203b56ef6 | ||
|
|
d6db84152f | ||
|
|
b7afad5a4a | ||
|
|
9d6c53f631 | ||
|
|
aa7b02dfc3 | ||
|
|
379ffaf61a | ||
|
|
1bdfccddbc | ||
|
|
c83cdd590c | ||
|
|
f4770374a7 | ||
|
|
72880e93bc | ||
|
|
6c9cd72948 | ||
|
|
2e71ef4635 | ||
|
|
04cd1098ea | ||
|
|
e76ce64568 | ||
|
|
8fe84abef3 | ||
|
|
1b1fa53547 | ||
|
|
5019407f35 | ||
|
|
83ba19eccb | ||
|
|
936bf38521 | ||
|
|
f54b0d2bbb | ||
|
|
0e32abc76c | ||
|
|
94cb06a3bd | ||
|
|
907aea443e | ||
|
|
385922723c | ||
|
|
3061c2f009 | ||
|
|
5c0ccbde35 | ||
|
|
7b998de2ca | ||
|
|
b0d49f52a5 | ||
|
|
6f9bcc2111 | ||
|
|
0fb0ecd28a | ||
|
|
6194d88aec | ||
|
|
b02c3121fe | ||
|
|
580db0cb65 | ||
|
|
6bcdc0e3c6 | ||
|
|
9b4fd91717 | ||
|
|
9421249b8e | ||
|
|
e823cbaee5 | ||
|
|
c34618554b | ||
|
|
6ab7b4a004 | ||
|
|
5b6bf3b73e | ||
|
|
2a30a13f5f | ||
|
|
3c05a112ca | ||
|
|
66af2d83b8 | ||
|
|
fc3be76657 | ||
|
|
259a91dd77 | ||
|
|
e957b29bae | ||
|
|
33060d9787 | ||
|
|
71ca79a456 | ||
|
|
fc744d241a | ||
|
|
78b5b3dbc1 | ||
|
|
cb3846261a | ||
|
|
9a3dcf70de | ||
|
|
670c78302f | ||
|
|
f4cb97b3bf | ||
|
|
be6d44ed04 | ||
|
|
cfc9092479 | ||
|
|
d674ae5b1f | ||
|
|
864972e575 | ||
|
|
7d97850cb0 | ||
|
|
3e33f098a6 | ||
|
|
adf9ce5e2a | ||
|
|
0c5dff71e9 | ||
|
|
715f773672 | ||
|
|
a54b89d380 | ||
|
|
2085c306ab | ||
|
|
98b99da0bc | ||
|
|
e00238b24e | ||
|
|
aef2b756c9 | ||
|
|
f1f1e20a3e | ||
|
|
62a2fdeaa6 | ||
|
|
37c1852058 | ||
|
|
99c4a08d83 | ||
|
|
9d4bc6bfab | ||
|
|
71fd5af48e | ||
|
|
995521b789 | ||
|
|
5031985883 | ||
|
|
5759090291 | ||
|
|
008c1c89fb | ||
|
|
624eb40315 | ||
|
|
aaa12ae817 | ||
|
|
d37c90878c | ||
|
|
668a631c11 | ||
|
|
f06c0f4553 | ||
|
|
a0b13a6e6f | ||
|
|
1bd9f3fd88 | ||
|
|
93ba88a35f | ||
|
|
c86b1251a3 | ||
|
|
6f6954b270 | ||
|
|
1d4ed425f1 | ||
|
|
ba657e28ad | ||
|
|
7575353597 | ||
|
|
10d3c09cfa | ||
|
|
5001c4a881 | ||
|
|
7ecd0d1893 | ||
|
|
ae802d5d75 | ||
|
|
3b4266246d | ||
|
|
a801e720b2 | ||
|
|
a52d45f7c2 | ||
|
|
57eaa1f04c | ||
|
|
6cc2cc5be2 | ||
|
|
ebcdc08a77 | ||
|
|
7aebdf56fc | ||
|
|
6de36eb71c | ||
|
|
5ebd392c5b | ||
|
|
5fc2029725 | ||
|
|
45e4a01fc1 | ||
|
|
c4ea7891ea | ||
|
|
de871fb8c1 | ||
|
|
c322eebb98 | ||
|
|
ea6b0f1ca3 | ||
|
|
a35a9a4ab6 | ||
|
|
9a93c7b15d | ||
|
|
43eed75635 | ||
|
|
74ea6e6002 | ||
|
|
91e2157622 | ||
|
|
87961e5451 | ||
|
|
4ff203f393 | ||
|
|
410b2bef2b | ||
|
|
a9792fdff1 | ||
|
|
92e36527fd | ||
|
|
aa1d7d475c | ||
|
|
91185c4fe1 | ||
|
|
c527b1911f | ||
|
|
d1501b2e3e | ||
|
|
03ac117844 | ||
|
|
515be8b025 | ||
|
|
c3479450f5 | ||
|
|
611aa3229c | ||
|
|
5a865f3d08 | ||
|
|
9d8d5c23c8 | ||
|
|
3fed1a3282 | ||
|
|
f47387c3a4 | ||
|
|
133272acb7 | ||
|
|
c49ae98df5 | ||
|
|
a1672a9bc0 | ||
|
|
ffc2e23b2f | ||
|
|
c481e73418 | ||
|
|
06b3124b62 | ||
|
|
d7d81665a0 | ||
|
|
24c542c22f | ||
|
|
ec616fcdac | ||
|
|
24e9455733 | ||
|
|
58aaf46a0e | ||
|
|
54e92fe9c8 | ||
|
|
4bb83c5d86 | ||
|
|
2f9280460e | ||
|
|
30e8e79cf7 | ||
|
|
7bb0d4f39a | ||
|
|
046814b7c4 | ||
|
|
cf00348d46 | ||
|
|
8466edf80f | ||
|
|
ccdf539ed4 | ||
|
|
aeee6e9cda | ||
|
|
2af21cb245 | ||
|
|
96fb5a2f93 | ||
|
|
1eaefb16ba | ||
|
|
1c972a29a7 | ||
|
|
aa33c083fe | ||
|
|
a9454e96ed | ||
|
|
fb6fa85cd3 | ||
|
|
3c12bdc212 | ||
|
|
5eeee760f7 | ||
|
|
cfe889d5b3 | ||
|
|
3045c41eda | ||
|
|
d194ef9dd8 | ||
|
|
aeb379f307 | ||
|
|
cae0edfb49 | ||
|
|
e97558db1f | ||
|
|
06ecc1a571 | ||
|
|
adcf9ee27e | ||
|
|
a8c759be55 | ||
|
|
ecf97de439 | ||
|
|
f134de6609 | ||
|
|
d91ce3100b | ||
|
|
20de40014d | ||
|
|
a7f9a99d10 | ||
|
|
4bdfbabaab | ||
|
|
5465c57d96 | ||
|
|
5082c8f705 | ||
|
|
509fff4684 | ||
|
|
9d2c14f809 | ||
|
|
b023549b87 | ||
|
|
0983073538 | ||
|
|
815acf2ce0 | ||
|
|
6b8596466b | ||
|
|
1e5bb7df64 | ||
|
|
e920bfc69c | ||
|
|
a85c6f4028 | ||
|
|
a75bc15f29 | ||
|
|
659aa30fb3 | ||
|
|
54a11916d2 | ||
|
|
47f423d092 | ||
|
|
5c5e68e31d | ||
|
|
9e917e2bb9 | ||
|
|
4bd4d2f395 | ||
|
|
6406248840 | ||
|
|
3d8134bbd3 | ||
|
|
15310a6c47 | ||
|
|
450a8cd5bd | ||
|
|
088f99942f | ||
|
|
3e97bd8d2d | ||
|
|
866051c975 | ||
|
|
0be437f3cf | ||
|
|
11f3dec65d | ||
|
|
912d151164 | ||
|
|
863678f850 | ||
|
|
b82a6a8c5d | ||
|
|
ab2daf2a80 | ||
|
|
f734e3a58a | ||
|
|
212981fda9 | ||
|
|
c64e138ba7 | ||
|
|
c43353d126 | ||
|
|
d439ba9592 | ||
|
|
b7f06f0b59 | ||
|
|
60d666f8be | ||
|
|
4ff4c2afef | ||
|
|
655b8385f9 | ||
|
|
439c141c2c | ||
|
|
95755b930d | ||
|
|
9cf54c1f2d | ||
|
|
e118418a23 | ||
|
|
a3567d6eb2 | ||
|
|
a359143bc7 | ||
|
|
77b7a24d85 | ||
|
|
338b2593d5 | ||
|
|
ded5e27355 | ||
|
|
ee1b005d96 | ||
|
|
cd2673d1ba | ||
|
|
41d77085ae | ||
|
|
67059b2e43 | ||
|
|
f6157ef79b | ||
|
|
6e5d517e82 | ||
|
|
1dca51988a | ||
|
|
3c064bad55 | ||
|
|
d60a3ce05a | ||
|
|
f68438ff8c | ||
|
|
d351f1d08e | ||
|
|
70bdb8f37d | ||
|
|
3cd713e078 | ||
|
|
454655de5a | ||
|
|
268ed1a6a6 | ||
|
|
a42ce601b8 | ||
|
|
756b37ef6c | ||
|
|
ab7e708e02 | ||
|
|
326d916c0b | ||
|
|
db09bd931b | ||
|
|
d1e9eb90f1 | ||
|
|
20a5c4c78d | ||
|
|
499f9888b1 | ||
|
|
7bb3364b43 | ||
|
|
473cbc5694 | ||
|
|
e32b24c9f6 | ||
|
|
777fdc14c8 | ||
|
|
ffbb01c25c | ||
|
|
47380c2c1c | ||
|
|
51e9121a6b | ||
|
|
94f51d518d | ||
|
|
8fdc17b239 | ||
|
|
42bb29185f | ||
|
|
05ae0f4e80 | ||
|
|
092374a143 | ||
|
|
3cfb60430a | ||
|
|
370cc68fa4 | ||
|
|
8fe5046c19 | ||
|
|
37e7604441 | ||
|
|
7651b34855 | ||
|
|
4bf89b1d5f | ||
|
|
0287284909 | ||
|
|
a8453b3f21 | ||
|
|
2552564953 | ||
|
|
0d4d19559a | ||
|
|
52f4132ccb | ||
|
|
cb6440662b | ||
|
|
6293895611 | ||
|
|
f564fb6e9e | ||
|
|
3f2adfcf62 | ||
|
|
2c979f291e | ||
|
|
7e7b2ee64a | ||
|
|
87108405a8 | ||
|
|
9fb055cbff | ||
|
|
e690f0137e | ||
|
|
87c08d6fbd | ||
|
|
8c164d776e | ||
|
|
964271b4e1 | ||
|
|
e99ba48f07 | ||
|
|
62b1627b04 | ||
|
|
3a54997d42 | ||
|
|
7d95a43b4b | ||
|
|
23ea8710c0 | ||
|
|
0b62a52991 | ||
|
|
1143c8a8b7 | ||
|
|
a5b66caae6 | ||
|
|
b0b87d05fd | ||
|
|
cb3c666f64 | ||
|
|
12fdae7944 | ||
|
|
e76bc70ec6 | ||
|
|
e78bb8cb41 | ||
|
|
5153e73f42 | ||
|
|
7f36ff8a2b | ||
|
|
99a8a39dda | ||
|
|
adbf983e65 | ||
|
|
d7639495ac | ||
|
|
fbe09d9082 | ||
|
|
70468b72c3 | ||
|
|
90f4af1121 | ||
|
|
aa37449bbf | ||
|
|
c835e04722 | ||
|
|
29b999b8ed | ||
|
|
9ddd6cc317 | ||
|
|
3a694f0e31 | ||
|
|
080cbda588 | ||
|
|
fd066e8aae | ||
|
|
0fd0cf689a | ||
|
|
f85adda24c | ||
|
|
2d1e8e02aa | ||
|
|
371fbee7a4 | ||
|
|
a5bb95e7c1 | ||
|
|
53b5a443fe | ||
|
|
a230871f75 | ||
|
|
f560ce17e8 | ||
|
|
b96329bd33 | ||
|
|
913f7802de | ||
|
|
a9f61ed51e | ||
|
|
04c0835228 | ||
|
|
af7ff05ecf | ||
|
|
4ccf80189e | ||
|
|
b417229ee6 | ||
|
|
61457582b3 | ||
|
|
ecc1613f49 | ||
|
|
5ea89eefe8 | ||
|
|
661808826a | ||
|
|
342f78ffd0 | ||
|
|
cc6a65d5e4 | ||
|
|
068ee76983 | ||
|
|
b980b33019 | ||
|
|
4327ce7848 | ||
|
|
2c813e7b3d | ||
|
|
d69746f7ef | ||
|
|
ce8f73d30d | ||
|
|
d6602ac8a8 | ||
|
|
6478af03e7 | ||
|
|
f086e63914 | ||
|
|
6fbb6bd8dc | ||
|
|
008629b61f | ||
|
|
b7704dbe57 | ||
|
|
5aff2d0b1b | ||
|
|
771bbeed6a | ||
|
|
53dd1e9aa5 | ||
|
|
6b7ed456ac | ||
|
|
5e2185dffd | ||
|
|
b5d318013b | ||
|
|
07a926e50c | ||
|
|
78bbb63c11 | ||
|
|
1fd613c2b2 | ||
|
|
792833ebc8 | ||
|
|
7392fce770 | ||
|
|
6c621c743d | ||
|
|
50a7883958 | ||
|
|
3882db6fc6 | ||
|
|
c91691f79b | ||
|
|
17fbf1163d | ||
|
|
9a1bbd7e0d | ||
|
|
bf6f3bad46 | ||
|
|
b99b2a53cf | ||
|
|
1fec5c15d4 | ||
|
|
a8b13e60b6 | ||
|
|
4c0c44de6b | ||
|
|
af0623beea | ||
|
|
761b418d21 | ||
|
|
0316edb8cb | ||
|
|
48cf417d60 | ||
|
|
83af2926aa | ||
|
|
ddfa820004 | ||
|
|
80986978cb | ||
|
|
68283554e9 | ||
|
|
0fcedfafbe | ||
|
|
d075ce4ebd | ||
|
|
3c6fcb4e4d | ||
|
|
5d37b7947e | ||
|
|
537b5c83f2 | ||
|
|
44d2c90aea | ||
|
|
af0c984b08 | ||
|
|
e9f01c7a10 | ||
|
|
0c468e3489 | ||
|
|
aff43b7625 | ||
|
|
dc53ebd378 | ||
|
|
7a7518d92a | ||
|
|
ee182b8ea7 | ||
|
|
c1bf48480c | ||
|
|
b5bb88553c | ||
|
|
9b9fc09bdf | ||
|
|
5709c88232 | ||
|
|
522c9fba08 | ||
|
|
3a7b15f231 | ||
|
|
cb2c96ef7d | ||
|
|
f89f691ee3 | ||
|
|
a0f59b774a | ||
|
|
65adf2109d | ||
|
|
92da1695bf | ||
|
|
e46011beff | ||
|
|
e733dea80e | ||
|
|
6a27161322 | ||
|
|
6bb4193167 | ||
|
|
52c34250ff | ||
|
|
cd72cccbaa | ||
|
|
ad9689440a | ||
|
|
dd193352e2 | ||
|
|
a376041be4 | ||
|
|
7d14eb35ba | ||
|
|
fdad3b0c87 | ||
|
|
8ebaa59b5f | ||
|
|
6037be404c | ||
|
|
1ead65126f | ||
|
|
cfa1bc8875 | ||
|
|
cfbac7a9ab | ||
|
|
6f62777033 | ||
|
|
0a523d74d5 | ||
|
|
907d0eb724 | ||
|
|
edcf20885c | ||
|
|
bb838c5b1f | ||
|
|
fa90618200 | ||
|
|
b421d8f818 | ||
|
|
c1151b7e93 | ||
|
|
b1d6b1be9c | ||
|
|
cb45405e09 | ||
|
|
6ef9f2856a | ||
|
|
c14300ffbb | ||
|
|
42b4c40d87 | ||
|
|
1c78dac79f | ||
|
|
bb6a045dd3 | ||
|
|
b273d8314a | ||
|
|
015b895177 | ||
|
|
fce9ce4eb4 | ||
|
|
e6463adb65 | ||
|
|
b07fd29753 | ||
|
|
2416a035c7 | ||
|
|
c2c125fd29 | ||
|
|
580bf0494d | ||
|
|
eefc52d1dd | ||
|
|
c28de855e2 | ||
|
|
5e2d7505da | ||
|
|
f550c96541 | ||
|
|
d222eec1b1 | ||
|
|
be129262da | ||
|
|
87080c906d | ||
|
|
cdeaf09ed6 | ||
|
|
b17f8dac7a | ||
|
|
ec758e5c18 | ||
|
|
350f9630df | ||
|
|
557c760197 | ||
|
|
895f40414d | ||
|
|
96fbb38b1c | ||
|
|
650d01d7be | ||
|
|
3977342a67 | ||
|
|
8a61f01e1b | ||
|
|
75a4371f36 | ||
|
|
86ee4786a0 | ||
|
|
ef710463ae | ||
|
|
e6b153bcbd | ||
|
|
9c9eb8ca7b | ||
|
|
295f438ff1 | ||
|
|
cc1ad3e690 | ||
|
|
3ebb3822dd | ||
|
|
6e22bd4c8d | ||
|
|
1027956892 | ||
|
|
a46d52ddbb | ||
|
|
fd5e78eb8a | ||
|
|
8a326cbb91 | ||
|
|
09a7893021 | ||
|
|
9c10af58d8 | ||
|
|
81fbe251ba | ||
|
|
5203f0ea57 | ||
|
|
51644c2178 | ||
|
|
f326a20019 | ||
|
|
daea4ea460 | ||
|
|
c9c14bcebf | ||
|
|
9dc21c01f1 | ||
|
|
555dbd592c | ||
|
|
5e027b75b2 | ||
|
|
01a504dab7 | ||
|
|
70eeaaac28 | ||
|
|
0397d529bb | ||
|
|
25bed747f2 | ||
|
|
d17b90e782 | ||
|
|
7868e22a95 | ||
|
|
81f6e2057e | ||
|
|
1c3e37ee2b | ||
|
|
27756db621 | ||
|
|
7f71b04ef7 | ||
|
|
c48522e6c0 | ||
|
|
fbf629dd8b | ||
|
|
404b2889ff | ||
|
|
5ccb9d16a8 | ||
|
|
66b562f24b | ||
|
|
ef25b88717 | ||
|
|
f22f7273a9 | ||
|
|
ea9902c946 | ||
|
|
051c38c6df | ||
|
|
6b832026b4 | ||
|
|
81d7151f8f | ||
|
|
283672e909 | ||
|
|
94dfba4b4f | ||
|
|
2b66efd11b | ||
|
|
14fb3f5758 | ||
|
|
9354f0f092 | ||
|
|
c3636a0743 | ||
|
|
e07b66812c | ||
|
|
c1d231db60 | ||
|
|
757ab1f228 | ||
|
|
6e42e7fb6b | ||
|
|
92f88efd4e | ||
|
|
7ba2194d97 | ||
|
|
a5f2e2f5c8 | ||
|
|
8658c24ef0 | ||
|
|
0cded076e4 | ||
|
|
135c0b6d38 | ||
|
|
769fe42a7a | ||
|
|
ab6fcd73e0 | ||
|
|
3278420d72 | ||
|
|
98395387a7 | ||
|
|
180a097213 | ||
|
|
18ac00080a | ||
|
|
4da713702a | ||
|
|
68be17de66 | ||
|
|
18cdf9d7ed | ||
|
|
c389ea1e49 | ||
|
|
1f65b0352d | ||
|
|
0e63b6638c | ||
|
|
fcda1f119b | ||
|
|
bb130849ee | ||
|
|
7b209eec6c | ||
|
|
9e7a84d2d6 | ||
|
|
baf43ea307 | ||
|
|
f524f0da3e | ||
|
|
49781c47a9 | ||
|
|
aa11ce807a | ||
|
|
702115c55a | ||
|
|
17cb1bf9b0 | ||
|
|
590c4e0a23 | ||
|
|
fd6196a880 | ||
|
|
2875f7ff7a | ||
|
|
50fe127a8d | ||
|
|
94ebe5b707 | ||
|
|
dc3914e976 | ||
|
|
ef2f037909 | ||
|
|
374f286585 | ||
|
|
34cd933f78 | ||
|
|
f8d533e592 | ||
|
|
21263cf0fd | ||
|
|
1a62ac2006 | ||
|
|
8f3d4d5fb2 | ||
|
|
81902455ff | ||
|
|
fbb33d9ef8 | ||
|
|
b025c7a7fa | ||
|
|
d8aec5aa97 | ||
|
|
3a862f343c | ||
|
|
69724cfb1c | ||
|
|
f75cdea678 | ||
|
|
32e650eff5 | ||
|
|
ab022f9049 | ||
|
|
7962e148fa | ||
|
|
efdf3a0691 | ||
|
|
000ab5e856 | ||
|
|
4873133c92 | ||
|
|
360bbef43a | ||
|
|
9a21f4987d | ||
|
|
63948767ef | ||
|
|
e2098f6f71 | ||
|
|
609c30da38 | ||
|
|
0896842268 | ||
|
|
ec9506b9eb | ||
|
|
526526975c | ||
|
|
098734c471 | ||
|
|
47df62534e | ||
|
|
7295001892 | ||
|
|
7c820b7fd2 | ||
|
|
36429cc1e9 | ||
|
|
a1c22ca9da | ||
|
|
0c37932631 | ||
|
|
dbf1f6dcca | ||
|
|
7bb26c0faf | ||
|
|
31a0b55556 | ||
|
|
64cc4785ca | ||
|
|
28391de50c | ||
|
|
b2ed69ab78 | ||
|
|
2f08940927 | ||
|
|
70928227e4 | ||
|
|
546bd70418 | ||
|
|
f2521a0110 | ||
|
|
02b83513a1 | ||
|
|
f26b2ff61b | ||
|
|
4d26535d07 | ||
|
|
01a365033e | ||
|
|
318a89a4bc | ||
|
|
b20f22fb92 | ||
|
|
fedf76e534 | ||
|
|
e0e16292eb | ||
|
|
b3c0e48bdd | ||
|
|
aded5ee03a | ||
|
|
16e71c910e | ||
|
|
bbe234b459 | ||
|
|
715b9eb156 | ||
|
|
4cc441afcf | ||
|
|
f79cd759bd | ||
|
|
1b232e4405 | ||
|
|
6d43afb258 | ||
|
|
a47c778b0e | ||
|
|
ddb82842bc | ||
|
|
199914b19f | ||
|
|
9fadd84597 | ||
|
|
95dd670c7c | ||
|
|
adc9def0c9 | ||
|
|
8dcc9d9b0e | ||
|
|
b5177b16ea | ||
|
|
f9b4693aae | ||
|
|
f2a479e35c | ||
|
|
1f40c2e052 | ||
|
|
b5c8d05814 | ||
|
|
f99634bc08 | ||
|
|
ab88e2f553 | ||
|
|
5465abe1ac | ||
|
|
b0df7a8f3b | ||
|
|
0d4fab100d | ||
|
|
8c5e10fd88 | ||
|
|
e8aef1596b | ||
|
|
386c86f04f | ||
|
|
3f5a66f170 | ||
|
|
a961d9534c | ||
|
|
d1919c18f5 | ||
|
|
afa8b24ba9 | ||
|
|
b793b74b32 | ||
|
|
65499d1f46 | ||
|
|
5f387cdb74 | ||
|
|
ed2e88c781 | ||
|
|
1cb3157110 | ||
|
|
a480b53787 | ||
|
|
405c895352 | ||
|
|
53dc251a0c | ||
|
|
d715072cbc | ||
|
|
aaee56f44e | ||
|
|
27ceb4ed48 | ||
|
|
2d51bd8f37 | ||
|
|
645a366dc5 | ||
|
|
756a74eda6 | ||
|
|
8052ee2afb | ||
|
|
6171c0defd | ||
|
|
56c27d0b8f | ||
|
|
9c173fd3a1 | ||
|
|
fa3ccf9953 | ||
|
|
229e645c8e | ||
|
|
b4606a8b84 | ||
|
|
d11782a87b | ||
|
|
4faaf3d251 | ||
|
|
89dea30211 | ||
|
|
7563634f53 | ||
|
|
d74dcc1d26 | ||
|
|
cd75fd0329 | ||
|
|
0390ee3363 | ||
|
|
64f52698c3 | ||
|
|
8bd4a87f0b | ||
|
|
4ad7c60443 | ||
|
|
d1a6c9be00 | ||
|
|
3c3fd1be50 | ||
|
|
92c5745ac8 | ||
|
|
792de5cda2 | ||
|
|
bfe2381896 | ||
|
|
ded5ecb3ed | ||
|
|
7895b40a77 | ||
|
|
3f2319093f | ||
|
|
3806b383c9 | ||
|
|
84a65e998f | ||
|
|
88049affb5 | ||
|
|
5fd59b9eac | ||
|
|
8288cc7f96 | ||
|
|
063b643597 | ||
|
|
ff4214877e | ||
|
|
847fa7d2ad | ||
|
|
886284b42f | ||
|
|
46776b461b | ||
|
|
e0150d7bb5 | ||
|
|
ad16260c53 | ||
|
|
3cfe9138a5 | ||
|
|
9e43babe0c | ||
|
|
4efbf54edf | ||
|
|
37121a1fd2 | ||
|
|
8a77a8a009 | ||
|
|
1aef137cb2 | ||
|
|
86707b28c0 | ||
|
|
5e42bae77f | ||
|
|
9396804379 | ||
|
|
5613cf9aae | ||
|
|
ff44c717a2 | ||
|
|
42faeb1402 | ||
|
|
7c243ac6ff | ||
|
|
85c6353cba | ||
|
|
c7d049efe2 | ||
|
|
97aabdca33 | ||
|
|
9562a0eaf7 | ||
|
|
04bffd4889 | ||
|
|
a9c71ced47 | ||
|
|
a03bf60ca5 | ||
|
|
02ee94f732 | ||
|
|
0a5ffd247c | ||
|
|
3f4f6a2d07 | ||
|
|
0d22f78b10 | ||
|
|
ca1e3e8e63 | ||
|
|
debb7cde2d | ||
|
|
40c35a5d50 | ||
|
|
971f769031 | ||
|
|
a1a3adfafa | ||
|
|
912b49ecc4 | ||
|
|
dcccd6e313 | ||
|
|
005529f959 | ||
|
|
3d5904c997 | ||
|
|
dcc8915dd2 | ||
|
|
4a589603e4 | ||
|
|
4f16b9d9e8 | ||
|
|
ce7ecc78cc | ||
|
|
9c8a677f13 | ||
|
|
39f2dd88aa | ||
|
|
3b4929a368 | ||
|
|
f290b8462d | ||
|
|
5ce4f3be56 | ||
|
|
a5158f04db | ||
|
|
2aa9f088a4 | ||
|
|
bb37c3f94c | ||
|
|
92b3f14d7b | ||
|
|
2dcd30e346 | ||
|
|
c4ed59dc46 | ||
|
|
81141fb2fa | ||
|
|
58c6f00a6c | ||
|
|
c1f4e42219 | ||
|
|
02b16843e5 | ||
|
|
86bfcc15f9 | ||
|
|
4fab4e71f7 | ||
|
|
b230462860 | ||
|
|
5f49b56c3d | ||
|
|
51482dd225 | ||
|
|
c1cb1a41a9 | ||
|
|
b496c79b34 | ||
|
|
fcad7db5ab | ||
|
|
e1bd26f712 | ||
|
|
0518184786 | ||
|
|
576e234216 | ||
|
|
3c750dac86 | ||
|
|
714cee586d | ||
|
|
cf9cc30d5e | ||
|
|
713b1c83e1 | ||
|
|
ddf289d747 | ||
|
|
ae1edf0f21 | ||
|
|
38665ddbf3 | ||
|
|
2af0f166eb | ||
|
|
aeaeff28d3 | ||
|
|
28948a0511 | ||
|
|
52d190e339 | ||
|
|
d47707c433 | ||
|
|
11bc46ae86 | ||
|
|
d229b23ea6 | ||
|
|
41dc7f7b14 | ||
|
|
dbd8b14cd2 | ||
|
|
3af8adb067 | ||
|
|
5baa470d54 | ||
|
|
1f5d5215f7 | ||
|
|
24c77ecd07 | ||
|
|
2264fc0172 | ||
|
|
00028ac59b | ||
|
|
ad5735a559 | ||
|
|
9411f5044a | ||
|
|
7293d55239 | ||
|
|
31dc347df4 | ||
|
|
d72d0d4dc2 | ||
|
|
7181dc9d5b | ||
|
|
95fa8681fe | ||
|
|
3a63755d96 | ||
|
|
5390970054 | ||
|
|
2b43f2b261 | ||
|
|
4330cd1aac | ||
|
|
fc9dd8a34d | ||
|
|
c64abc15c9 | ||
|
|
fca12c639c | ||
|
|
a1522aabd6 | ||
|
|
9be4b339f8 | ||
|
|
ce1c74aec3 | ||
|
|
e824428e0f | ||
|
|
2fd4a8a9b1 | ||
|
|
82de7e8b8e | ||
|
|
26831b4732 | ||
|
|
a6862925ca | ||
|
|
1e2ce169af | ||
|
|
dda9b3551a | ||
|
|
b1760d3541 | ||
|
|
2c1e087bc6 | ||
|
|
90d5bd52a2 | ||
|
|
26db75853b | ||
|
|
fe5a674518 | ||
|
|
2fe56cd6af | ||
|
|
f26e82d2bc | ||
|
|
65892f067a | ||
|
|
768717d7b3 | ||
|
|
f78b3daf8b | ||
|
|
8a6f481118 | ||
|
|
f420434fd3 | ||
|
|
8e73d7970f | ||
|
|
9699af93bc | ||
|
|
44ca0a94b7 | ||
|
|
b3bf008e31 | ||
|
|
ce5e2982d2 | ||
|
|
7d2edd315c | ||
|
|
8ae0452873 | ||
|
|
e0efc0d9ab | ||
|
|
3ce3b7ca2b | ||
|
|
6997608b63 | ||
|
|
cee7f12974 | ||
|
|
5b4a8d5775 | ||
|
|
a59bedec5d | ||
|
|
3c5d670924 | ||
|
|
7bab251915 | ||
|
|
57e47eee5d | ||
|
|
4f09c57755 | ||
|
|
7c1edab6ca | ||
|
|
f24004c949 | ||
|
|
c4bf3931e2 | ||
|
|
fe13562f3e | ||
|
|
64334d72ea | ||
|
|
a915980187 | ||
|
|
af882fa588 | ||
|
|
7fcaa16835 | ||
|
|
4d640f3cf2 | ||
|
|
25d495b1d0 | ||
|
|
7fd936c4a8 | ||
|
|
fe753fc4fd | ||
|
|
0a4763fcc1 | ||
|
|
e281faf664 | ||
|
|
dcb291c1c6 | ||
|
|
ecd1c93bb9 | ||
|
|
eeb555a6ce | ||
|
|
27d53abd10 | ||
|
|
91eef55173 | ||
|
|
f9e1518da6 | ||
|
|
5b974260cc | ||
|
|
554374b710 | ||
|
|
475669ac1b | ||
|
|
623d1e6a40 | ||
|
|
08fa4a997f | ||
|
|
4e21cf8494 | ||
|
|
2ebbda6852 | ||
|
|
010a6d6e42 | ||
|
|
3b1481879a | ||
|
|
3ddcc3884b | ||
|
|
260ab1ec89 | ||
|
|
69803a999f | ||
|
|
d4a75ed871 | ||
|
|
7394f14218 | ||
|
|
23cd7b9ebd | ||
|
|
477cfee78e | ||
|
|
750cecfdaf | ||
|
|
32a28fba32 | ||
|
|
fe926cbf9a | ||
|
|
d18e65ca6c | ||
|
|
ec67ca605c | ||
|
|
9cb3c963a1 | ||
|
|
8a4caf82bb | ||
|
|
7a5941cfa9 | ||
|
|
690c49ae1f | ||
|
|
c77c095893 | ||
|
|
98ddc65fa2 | ||
|
|
41a7c71b7d | ||
|
|
cb1d3d1db4 | ||
|
|
5ba2a31d7d | ||
|
|
0768a93bcb | ||
|
|
e690c6d0ff | ||
|
|
0c6bf406c1 | ||
|
|
95b5f12226 | ||
|
|
5b4b622834 | ||
|
|
b908b77a34 | ||
|
|
dbba310385 | ||
|
|
0a0ca9ba93 | ||
|
|
8aa574a7c4 | ||
|
|
37ac8c038f | ||
|
|
9b6456a80f | ||
|
|
f6ffd314b1 | ||
|
|
b569c6a6dd | ||
|
|
c84f416973 | ||
|
|
6ebc476d2b | ||
|
|
98c340d94d | ||
|
|
78d80918aa | ||
|
|
e8d7d6b4e7 | ||
|
|
53341b0dc0 | ||
|
|
f64c7d81ad | ||
|
|
197de59089 | ||
|
|
13eb37cc46 | ||
|
|
c21c0ff411 | ||
|
|
72f6af7019 | ||
|
|
6b14321505 | ||
|
|
4fcb719613 | ||
|
|
50915d9100 | ||
|
|
834792bc2d | ||
|
|
04b225711f | ||
|
|
eee4a75353 | ||
|
|
15d0df1a12 | ||
|
|
5c3e8c35c4 | ||
|
|
ac514fce30 | ||
|
|
f7343ea305 | ||
|
|
1435421c3c | ||
|
|
735c0fe367 | ||
|
|
af99cfa6f9 | ||
|
|
525ff009b6 | ||
|
|
ee46167320 | ||
|
|
6fc03403b4 | ||
|
|
760c481d39 | ||
|
|
2c4906534b | ||
|
|
2ed79f3f9c | ||
|
|
7e9be878c4 | ||
|
|
0a5ae3b090 | ||
|
|
70b8c1f9b7 | ||
|
|
b067b671db | ||
|
|
268ccac7e1 | ||
|
|
5e487bc4ba | ||
|
|
ffeda7d60b | ||
|
|
2779d1a489 | ||
|
|
1752815654 | ||
|
|
429ca400e5 | ||
|
|
984853bd66 | ||
|
|
ff2549d4b7 | ||
|
|
d33bb6b4bb | ||
|
|
a3c0eca063 | ||
|
|
10de4ac78e | ||
|
|
895394ebb9 | ||
|
|
349f8d936a | ||
|
|
bbc65391a1 | ||
|
|
751c248657 | ||
|
|
9052cd3cdd | ||
|
|
2736527fc1 | ||
|
|
0c7001acf6 | ||
|
|
9e9be5bf09 | ||
|
|
008e1ad27b | ||
|
|
3e5ae14a54 | ||
|
|
0c28833b14 | ||
|
|
75ef95c6bf | ||
|
|
b906f3c654 | ||
|
|
4b274a454b | ||
|
|
6554005742 | ||
|
|
9f04022afc | ||
|
|
fbab512975 | ||
|
|
c9b3a67c8b | ||
|
|
3169cd6591 | ||
|
|
b116e487d3 | ||
|
|
aa57044bb8 | ||
|
|
fdd578dad9 | ||
|
|
2801520546 | ||
|
|
caaf983b3d | ||
|
|
aaba13530c | ||
|
|
e05ec3bcee | ||
|
|
703a132a61 | ||
|
|
e55226e685 | ||
|
|
9a4e6de5f9 | ||
|
|
a4e55a328c | ||
|
|
2705685f07 | ||
|
|
b7fb17a79f | ||
|
|
ffeb73ab7c | ||
|
|
427dec56e4 | ||
|
|
94ce87d953 | ||
|
|
0dc4f0f11a | ||
|
|
a1d7e65ffb | ||
|
|
61702d9c2a | ||
|
|
5b08bef53f | ||
|
|
53a6588054 | ||
|
|
9855c0c13e | ||
|
|
c5d005bdeb | ||
|
|
1eb844c75b | ||
|
|
7e177f3cbf | ||
|
|
eb91cfbda1 | ||
|
|
54082c1fce | ||
|
|
b5caf6dacf | ||
|
|
f4d1ce5388 | ||
|
|
7d7ec69dc1 | ||
|
|
7208288c00 | ||
|
|
aff3745859 | ||
|
|
e103ddd216 | ||
|
|
41a4965775 | ||
|
|
49a06f513b | ||
|
|
1308f3684b | ||
|
|
c51eccac38 | ||
|
|
09157767bf | ||
|
|
32cc2c708a | ||
|
|
7f2de233fc | ||
|
|
7cb150606c | ||
|
|
87cac010eb | ||
|
|
03926754e7 | ||
|
|
65efda1a7a | ||
|
|
5941d4ca16 | ||
|
|
e77101af89 | ||
|
|
e766be6af9 | ||
|
|
95d1658324 | ||
|
|
9b24550738 | ||
|
|
7947568019 | ||
|
|
399ee98923 | ||
|
|
7b3b4a2ec5 | ||
|
|
09177da620 | ||
|
|
8392cfb2fa | ||
|
|
01face7315 | ||
|
|
5b6f4d65bf | ||
|
|
cd6f8f3db3 | ||
|
|
a9b07ddf1d | ||
|
|
f3710c575b | ||
|
|
1f5ab45a1e | ||
|
|
58d453db11 | ||
|
|
bd426b763c | ||
|
|
2e42efa71f | ||
|
|
58181bd723 | ||
|
|
dcef3115b8 | ||
|
|
4bdc5dc90f | ||
|
|
e33d6b3a0a | ||
|
|
d8ddaccf07 | ||
|
|
ca55ea16f0 | ||
|
|
cb42330e22 | ||
|
|
4e55cf0baa | ||
|
|
abec45c492 | ||
|
|
610e25b98a | ||
|
|
ad11e63338 | ||
|
|
440eb72ae4 | ||
|
|
c4553de302 | ||
|
|
32904b75e4 | ||
|
|
35a4771f89 | ||
|
|
bbb2e9391e | ||
|
|
9dc186f455 | ||
|
|
82b9c03a7e | ||
|
|
f4f9ba9efa | ||
|
|
495864e8e0 | ||
|
|
ac917df519 | ||
|
|
3b12fef948 | ||
|
|
a74b769aef |
43
.github/workflows/build_nupkg.yml
vendored
Normal file
43
.github/workflows/build_nupkg.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Nuget Pack
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Pack
|
||||
run: dotnet pack
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'Nuget Package'
|
||||
path: 'BinaryObjectScanner/bin/Release/*.nupkg'
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: 'BinaryObjectScanner/bin/Release/*.nupkg'
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
53
.github/workflows/build_programs.yml
vendored
Normal file
53
.github/workflows/build_programs.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Build Programs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
project: [ExtractionTool, ProtectionScan]
|
||||
runtime: [win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64]
|
||||
framework: [net8.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0]
|
||||
conf: [Debug] #[Release, Debug]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build
|
||||
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c ${{ matrix.conf == 'Release' && 'Release -p:DebugType=None -p:DebugSymbols=false' || 'Debug'}} --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8')) && '-p:PublishSingleFile=true' || ''}}
|
||||
|
||||
- name: Archive build
|
||||
run: zip -r ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip ${{ matrix.project }}/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}
|
||||
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
17
.github/workflows/check_pr.yml
vendored
Normal file
17
.github/workflows/check_pr.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Build PR
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Build
|
||||
run: dotnet build
|
||||
19
.gitmodules
vendored
19
.gitmodules
vendored
@@ -1,12 +1,9 @@
|
||||
[submodule "BurnOutSharp/External/stormlibsharp"]
|
||||
path = BurnOutSharp/External/stormlibsharp
|
||||
[submodule "BinaryObjectScanner/_EXTERNAL/stormlibsharp"]
|
||||
path = BinaryObjectScanner/_EXTERNAL/stormlibsharp
|
||||
url = https://github.com/robpaveza/stormlibsharp.git
|
||||
[submodule "HLLibSharp"]
|
||||
path = HLLibSharp
|
||||
url = https://github.com/mnadareski/HLLibSharp
|
||||
[submodule "LibMSPackSharp"]
|
||||
path = LibMSPackSharp
|
||||
url = https://github.com/mnadareski/LibMSPackSharp.git
|
||||
[submodule "Dtf"]
|
||||
path = Dtf
|
||||
url = https://github.com/wixtoolset/Dtf.git
|
||||
[submodule "BinaryObjectScanner/_EXTERNAL/libmspack4n"]
|
||||
path = BinaryObjectScanner/_EXTERNAL/libmspack4n
|
||||
url = https://github.com/activescott/libmspack4n.git
|
||||
[submodule "BinaryObjectScanner/_EXTERNAL/LessIO"]
|
||||
path = BinaryObjectScanner/_EXTERNAL/LessIO
|
||||
url = https://github.com/activescott/LessIO.git
|
||||
|
||||
19
.vscode/launch.json
vendored
19
.vscode/launch.json
vendored
@@ -5,14 +5,27 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": ".NET Core Launch (console)",
|
||||
"name": ".NET Core Launch (ProtectionScan)",
|
||||
"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",
|
||||
"program": "${workspaceFolder}/ProtectionScan/bin/Debug/net8.0/ProtectionScan.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/Test",
|
||||
"cwd": "${workspaceFolder}/ProtectionScan",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Launch (ExtractionTool)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/ExtractionTool/bin/Debug/net8.0/ExtractionTool.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/ExtractionTool",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"dotnet.defaultSolution": "BinaryObjectScanner.sln"
|
||||
}
|
||||
6
.vscode/tasks.json
vendored
6
.vscode/tasks.json
vendored
@@ -7,7 +7,7 @@
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/Test/Test.csproj",
|
||||
"${workspaceFolder}/BinaryObjectScanner.sln",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
@@ -19,7 +19,7 @@
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/Test/Test.csproj",
|
||||
"${workspaceFolder}/BinaryObjectScanner.sln",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
@@ -32,7 +32,7 @@
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"${workspaceFolder}/Test/Test.csproj",
|
||||
"${workspaceFolder}/BinaryObjectScanner.sln",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
|
||||
48
BinaryObjectScanner.sln
Normal file
48
BinaryObjectScanner.sln
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.1.32407.343
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{68D10531-99CB-40B1-8912-73FA286C9433}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
appveyor.yml = appveyor.yml
|
||||
Coding Guide.md = Coding Guide.md
|
||||
Developer Guide.md = Developer Guide.md
|
||||
LICENSE = LICENSE
|
||||
publish-nix.sh = publish-nix.sh
|
||||
publish-win.ps1 = publish-win.ps1
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BinaryObjectScanner", "BinaryObjectScanner\BinaryObjectScanner.csproj", "{341EA3F5-847C-4739-B86F-2B051FFE4EF2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProtectionScan", "ProtectionScan\ProtectionScan.csproj", "{14CC56E0-7D56-497C-BF3D-4C06FA169831}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtractionTool", "ExtractionTool\ExtractionTool.csproj", "{89767A19-043F-4251-805B-B2CBC48E2B79}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{341EA3F5-847C-4739-B86F-2B051FFE4EF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{341EA3F5-847C-4739-B86F-2B051FFE4EF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{341EA3F5-847C-4739-B86F-2B051FFE4EF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{341EA3F5-847C-4739-B86F-2B051FFE4EF2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{14CC56E0-7D56-497C-BF3D-4C06FA169831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{14CC56E0-7D56-497C-BF3D-4C06FA169831}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{14CC56E0-7D56-497C-BF3D-4C06FA169831}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{14CC56E0-7D56-497C-BF3D-4C06FA169831}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{89767A19-043F-4251-805B-B2CBC48E2B79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{89767A19-043F-4251-805B-B2CBC48E2B79}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{89767A19-043F-4251-805B-B2CBC48E2B79}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{89767A19-043F-4251-805B-B2CBC48E2B79}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {0B343DD2-8852-47B0-9647-DFCFBEDF933C}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
103
BinaryObjectScanner/BinaryObjectScanner.csproj
Normal file
103
BinaryObjectScanner/BinaryObjectScanner.csproj
Normal file
@@ -0,0 +1,103 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>3.1.16</Version>
|
||||
<!-- Mostly added due to external libraries -->
|
||||
<WarningsNotAsErrors>CS0162;CS0612;CS8600;CS8601;CS8602;CS8603;CS8604;CS8605;CS8618;CS8625;CS8634;CS8765;IL3000;NU5100</WarningsNotAsErrors>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Description>Protection scanning library</Description>
|
||||
<Copyright>Copyright (c)2018-2024 Matt Nadareski</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/BinaryObjectScanner</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>protection copy-protection scanning packer</PackageTags>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Set a build flag for Windows specifically -->
|
||||
<PropertyGroup Condition="'$(RuntimeIdentifier)'=='win-x86'">
|
||||
<DefineConstants>$(DefineConstants);WIN</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Exclude certain parts of external modules for by default -->
|
||||
<PropertyGroup>
|
||||
<DefaultItemExcludes>
|
||||
$(DefaultItemExcludes);
|
||||
**\AssemblyInfo.cs;
|
||||
_EXTERNAL\LessIO\src\LessIO.Tests\**;
|
||||
_EXTERNAL\libmspack4n\lib\**;
|
||||
_EXTERNAL\libmspack4n\libmspack4ntest\**;
|
||||
_EXTERNAL\stormlibsharp\lib\**;
|
||||
_EXTERNAL\stormlibsharp\src\TestConsole\**
|
||||
</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Exclude all StormLibSharp for .NET Framework 4.0 -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net40`))">
|
||||
<DefaultItemExcludes>
|
||||
$(DefaultItemExcludes);
|
||||
_EXTERNAL\stormlibsharp\src\**
|
||||
</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Exclude all external modules for .NET Framework 2.0, .NET Framework 3.5, or non-Windows builds -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR !$(RuntimeIdentifier.StartsWith(`win-x86`))">
|
||||
<DefaultItemExcludes>
|
||||
$(DefaultItemExcludes);
|
||||
_EXTERNAL\**
|
||||
</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- These are needed for dealing with native Windows DLLs -->
|
||||
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND $(RuntimeIdentifier.StartsWith(`win-x86`))">
|
||||
<Content Include="*.dll">
|
||||
<Pack>true</Pack>
|
||||
<PackagePath>contentFiles;content</PackagePath>
|
||||
<IncludeInPackage>true</IncludeInPackage>
|
||||
<CopyToOutput>true</CopyToOutput>
|
||||
<BuildAction>Content</BuildAction>
|
||||
<copyToOutput>true</copyToOutput>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Support for old .NET versions -->
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="MinThreadingBridge" Version="0.11.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`))">
|
||||
<PackageReference Include="OpenMcdf" Version="2.3.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
|
||||
<PackageReference Include="SharpCompress" Version="0.38.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net4`)) AND !$(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.Compression" Version="0.5.2" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.2.2" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.4.13" />
|
||||
<PackageReference Include="SabreTools.Matching" Version="1.3.3" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.4.11" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.6.9" />
|
||||
<PackageReference Include="UnshieldSharp" Version="1.8.5" />
|
||||
<PackageReference Include="WiseUnpacker" Version="1.4.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
26
BinaryObjectScanner/CheckDictionary.cs
Normal file
26
BinaryObjectScanner/CheckDictionary.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace BinaryObjectScanner
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a mapping from checker to detected protection
|
||||
/// </summary>
|
||||
#if NET20 || NET35
|
||||
public class CheckDictionary<T> : System.Collections.Generic.Dictionary<T, string> where T : notnull
|
||||
#else
|
||||
public class CheckDictionary<T> : System.Collections.Concurrent.ConcurrentDictionary<T, string> where T : notnull
|
||||
#endif
|
||||
{
|
||||
/// <inheritdoc cref="System.Collections.Generic.Dictionary{TKey, TValue}.Add(TKey, TValue)"/>
|
||||
/// <remarks>Handles the proper Add implementation</remarks>
|
||||
public void Append(T key, string? value)
|
||||
{
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
#if NET20 || NET35
|
||||
this[key] = value;
|
||||
#else
|
||||
TryAdd(key, value);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
178
BinaryObjectScanner/Data/StaticChecks.cs
Normal file
178
BinaryObjectScanner/Data/StaticChecks.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Data
|
||||
{
|
||||
internal static class StaticChecks
|
||||
{
|
||||
#region Public Collections
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IContentCheck types
|
||||
/// </summary>
|
||||
public static List<IContentCheck> ContentCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
contentCheckClasses ??= InitCheckClasses<IContentCheck>();
|
||||
return contentCheckClasses ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IExecutableCheck<LinearExecutable> types
|
||||
/// </summary>
|
||||
public static List<IExecutableCheck<LinearExecutable>> LinearExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
linearExecutableCheckClasses ??= InitCheckClasses<IExecutableCheck<LinearExecutable>>();
|
||||
return linearExecutableCheckClasses ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IExecutableCheck<MSDOS> types
|
||||
/// </summary>
|
||||
public static List<IExecutableCheck<MSDOS>> MSDOSExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
msdosExecutableCheckClasses ??= InitCheckClasses<IExecutableCheck<MSDOS>>();
|
||||
return msdosExecutableCheckClasses ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IExecutableCheck<NewExecutable> types
|
||||
/// </summary>
|
||||
public static List<IExecutableCheck<NewExecutable>> NewExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
newExecutableCheckClasses ??= InitCheckClasses<IExecutableCheck<NewExecutable>>();
|
||||
return newExecutableCheckClasses ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IPathCheck types
|
||||
/// </summary>
|
||||
public static List<IPathCheck> PathCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
pathCheckClasses ??= InitCheckClasses<IPathCheck>();
|
||||
return pathCheckClasses ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IExecutableCheck<PortableExecutable> types
|
||||
/// </summary>
|
||||
public static List<IExecutableCheck<PortableExecutable>> PortableExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
portableExecutableCheckClasses ??= InitCheckClasses<IExecutableCheck<PortableExecutable>>();
|
||||
return portableExecutableCheckClasses ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Instances
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IContentCheck types
|
||||
/// </summary>
|
||||
private static List<IContentCheck>? contentCheckClasses;
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IExecutableCheck<LinearExecutable> types
|
||||
/// </summary>
|
||||
private static List<IExecutableCheck<LinearExecutable>>? linearExecutableCheckClasses;
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IExecutableCheck<MSDOS> types
|
||||
/// </summary>
|
||||
private static List<IExecutableCheck<MSDOS>>? msdosExecutableCheckClasses;
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IExecutableCheck<NewExecutable> types
|
||||
/// </summary>
|
||||
private static List<IExecutableCheck<NewExecutable>>? newExecutableCheckClasses;
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IPathCheck types
|
||||
/// </summary>
|
||||
private static List<IPathCheck>? pathCheckClasses;
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IExecutableCheck<PortableExecutable> types
|
||||
/// </summary>
|
||||
private static List<IExecutableCheck<PortableExecutable>>? portableExecutableCheckClasses;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Initialize all implementations of a type
|
||||
/// </summary>
|
||||
private static List<T>? InitCheckClasses<T>() =>
|
||||
InitCheckClasses<T>(Assembly.GetExecutingAssembly()) ?? [];
|
||||
|
||||
/// <summary>
|
||||
/// Initialize all implementations of a type
|
||||
/// </summary>
|
||||
private static List<T>? InitCheckClasses<T>(Assembly assembly)
|
||||
{
|
||||
List<T> classTypes = [];
|
||||
|
||||
// If not all types can be loaded, use the ones that could be
|
||||
List<Type> assemblyTypes = [];
|
||||
try
|
||||
{
|
||||
assemblyTypes = assembly.GetTypes().ToList<Type>();
|
||||
}
|
||||
catch (ReflectionTypeLoadException rtle)
|
||||
{
|
||||
assemblyTypes = rtle.Types.Where(t => t != null)!.ToList<Type>();
|
||||
}
|
||||
|
||||
// Get information from the type param
|
||||
string interfaceName = typeof(T)!.FullName!;
|
||||
|
||||
// Loop through all types
|
||||
foreach (Type type in assemblyTypes)
|
||||
{
|
||||
// If the type isn't a class
|
||||
if (!type.IsClass)
|
||||
continue;
|
||||
|
||||
// If the type isn't a class or doesn't implement the interface
|
||||
bool interfaceFound = false;
|
||||
foreach (var ii in type.GetInterfaces())
|
||||
{
|
||||
if (ii.FullName != interfaceName)
|
||||
continue;
|
||||
|
||||
interfaceFound = true;
|
||||
break;
|
||||
}
|
||||
if (!interfaceFound)
|
||||
continue;
|
||||
|
||||
// Try to create a concrete instance of the type
|
||||
var instance = (T?)Activator.CreateInstance(type);
|
||||
if (instance != null)
|
||||
classTypes.Add(instance);
|
||||
}
|
||||
|
||||
return classTypes;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
BinaryObjectScanner/EnumerableExtensions.cs
Normal file
27
BinaryObjectScanner/EnumerableExtensions.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BinaryObjectScanner
|
||||
{
|
||||
internal static class EnumerableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrap iterating through an enumerable with an action
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// .NET Frameworks 2.0 and 3.5 process in series.
|
||||
/// .NET Frameworks 4.0 onward process in parallel.
|
||||
/// </remarks>
|
||||
public static void IterateWithAction<T>(this IEnumerable<T> source, Action<T> action)
|
||||
{
|
||||
#if NET20 || NET35
|
||||
foreach (var item in source)
|
||||
{
|
||||
action(item);
|
||||
}
|
||||
#else
|
||||
System.Threading.Tasks.Parallel.ForEach(source, action);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
72
BinaryObjectScanner/Factory.cs
Normal file
72
BinaryObjectScanner/Factory.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner
|
||||
{
|
||||
public static class Factory
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an instance of a detectable based on file type
|
||||
/// </summary>
|
||||
public static IDetectable? CreateDetectable(WrapperType fileType)
|
||||
{
|
||||
return fileType switch
|
||||
{
|
||||
WrapperType.AACSMediaKeyBlock => new FileType.AACSMediaKeyBlock(),
|
||||
WrapperType.BDPlusSVM => new FileType.BDPlusSVM(),
|
||||
//WrapperType.CIA => new FileType.CIA(),
|
||||
WrapperType.Executable => new FileType.Executable(),
|
||||
WrapperType.LDSCRYPT => new FileType.LDSCRYPT(),
|
||||
//WrapperType.N3DS => new FileType.N3DS(),
|
||||
//WrapperType.Nitro => new FileType.Nitro(),
|
||||
WrapperType.PlayJAudioFile => new FileType.PLJ(),
|
||||
WrapperType.RealArcadeInstaller => new FileType.RealArcadeInstaller(),
|
||||
WrapperType.RealArcadeMezzanine => new FileType.RealArcadeMezzanine(),
|
||||
WrapperType.SFFS => new FileType.SFFS(),
|
||||
WrapperType.Textfile => new FileType.Textfile(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of an extractable based on file type
|
||||
/// </summary>
|
||||
public static IExtractable? CreateExtractable(WrapperType fileType)
|
||||
{
|
||||
return fileType switch
|
||||
{
|
||||
WrapperType.BFPK => new FileType.BFPK(),
|
||||
WrapperType.BSP => new FileType.BSP(),
|
||||
WrapperType.BZip2 => new FileType.BZip2(),
|
||||
WrapperType.CFB => new FileType.CFB(),
|
||||
//WrapperType.CIA => new FileType.CIA(),
|
||||
WrapperType.GCF => new FileType.GCF(),
|
||||
WrapperType.GZIP => new FileType.GZIP(),
|
||||
WrapperType.InstallShieldArchiveV3 => new FileType.InstallShieldArchiveV3(),
|
||||
WrapperType.InstallShieldCAB => new FileType.InstallShieldCAB(),
|
||||
WrapperType.MicrosoftCAB => new FileType.MicrosoftCAB(),
|
||||
WrapperType.MicrosoftLZ => new FileType.MicrosoftLZ(),
|
||||
WrapperType.MoPaQ => new FileType.MPQ(),
|
||||
//WrapperType.N3DS => new FileType.N3DS(),
|
||||
//WrapperType.NCF => new FileType.NCF(),
|
||||
//WrapperType.Nitro => new FileType.Nitro(),
|
||||
WrapperType.PAK => new FileType.PAK(),
|
||||
WrapperType.PFF => new FileType.PFF(),
|
||||
WrapperType.PKZIP => new FileType.PKZIP(),
|
||||
//WrapperType.PlayJAudioFile => new FileType.PLJ(),
|
||||
//WrapperType.Quantum => new FileType.Quantum(),
|
||||
WrapperType.RAR => new FileType.RAR(),
|
||||
WrapperType.SevenZip => new FileType.SevenZip(),
|
||||
WrapperType.SFFS => new FileType.SFFS(),
|
||||
WrapperType.SGA => new FileType.SGA(),
|
||||
WrapperType.TapeArchive => new FileType.TapeArchive(),
|
||||
WrapperType.VBSP => new FileType.VBSP(),
|
||||
WrapperType.VPK => new FileType.VPK(),
|
||||
WrapperType.WAD => new FileType.WAD(),
|
||||
WrapperType.XZ => new FileType.XZ(),
|
||||
WrapperType.XZP => new FileType.XZP(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
49
BinaryObjectScanner/FileType/AACSMediaKeyBlock.cs
Normal file
49
BinaryObjectScanner/FileType/AACSMediaKeyBlock.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// AACS media key block
|
||||
/// </summary>
|
||||
public class AACSMediaKeyBlock : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the MKB file itself fails
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var mkb = SabreTools.Serialization.Wrappers.AACSMediaKeyBlock.Create(stream);
|
||||
if (mkb == null)
|
||||
return null;
|
||||
|
||||
// Derive the version, if possible
|
||||
var typeAndVersion = mkb.Model.Records?.FirstOrDefault(r => r?.RecordType == SabreTools.Models.AACS.RecordType.TypeAndVersion);
|
||||
if (typeAndVersion == null)
|
||||
return "AACS (Unknown Version)";
|
||||
else
|
||||
return $"AACS {(typeAndVersion as SabreTools.Models.AACS.TypeAndVersionRecord)?.VersionNumber}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
47
BinaryObjectScanner/FileType/BDPlusSVM.cs
Normal file
47
BinaryObjectScanner/FileType/BDPlusSVM.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// BD+ SVM
|
||||
/// </summary>
|
||||
public class BDPlusSVM : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the BD+ file itself fails
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var svm = SabreTools.Serialization.Wrappers.BDPlusSVM.Create(stream);
|
||||
if (svm == null)
|
||||
return null;
|
||||
|
||||
// Format the date
|
||||
string date = $"{svm.Model.Year:0000}/{svm.Model.Month:00}/{svm.Model.Day:00}";
|
||||
|
||||
// Return the formatted value
|
||||
return $"BD+ {date}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
138
BinaryObjectScanner/FileType/BFPK.cs
Normal file
138
BinaryObjectScanner/FileType/BFPK.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// BFPK custom archive format
|
||||
/// </summary>
|
||||
public class BFPK : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var bfpk = SabreTools.Serialization.Wrappers.BFPK.Create(stream);
|
||||
if (bfpk == null)
|
||||
return false;
|
||||
|
||||
// Extract all files
|
||||
Directory.CreateDirectory(outDir);
|
||||
ExtractAll(bfpk, outDir);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from the BFPK to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all files extracted, false otherwise</returns>
|
||||
public static bool ExtractAll(SabreTools.Serialization.Wrappers.BFPK item, string outputDirectory)
|
||||
{
|
||||
// If we have no files
|
||||
if (item.Model.Files == null || item.Model.Files.Length == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < item.Model.Files.Length; i++)
|
||||
{
|
||||
allExtracted &= ExtractFile(item, i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a file from the BFPK to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">File index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the file extracted, false otherwise</returns>
|
||||
public static bool ExtractFile(SabreTools.Serialization.Wrappers.BFPK item, int index, string outputDirectory)
|
||||
{
|
||||
// If we have no files
|
||||
if (item.Model.Files == null || item.Model.Files.Length == 0)
|
||||
return false;
|
||||
|
||||
// If we have an invalid index
|
||||
if (index < 0 || index >= item.Model.Files.Length)
|
||||
return false;
|
||||
|
||||
// Get the file information
|
||||
var file = item.Model.Files[index];
|
||||
if (file == null)
|
||||
return false;
|
||||
|
||||
// Get the read index and length
|
||||
int offset = file.Offset + 4;
|
||||
int compressedSize = file.CompressedSize;
|
||||
|
||||
// Some files can lack the length prefix
|
||||
if (compressedSize > item.GetEndOfFile())
|
||||
{
|
||||
offset -= 4;
|
||||
compressedSize = file.UncompressedSize;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Ensure the output directory exists
|
||||
Directory.CreateDirectory(outputDirectory);
|
||||
|
||||
// Create the output path
|
||||
string filePath = Path.Combine(outputDirectory, file.Name ?? $"file{index}");
|
||||
using FileStream fs = File.OpenWrite(filePath);
|
||||
|
||||
// Read the data block
|
||||
var data = item.ReadFromDataSource(offset, compressedSize);
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// If we have uncompressed data
|
||||
if (compressedSize == file.UncompressedSize)
|
||||
{
|
||||
fs.Write(data, 0, compressedSize);
|
||||
}
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
else
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
ZlibStream zs = new ZlibStream(ms, CompressionMode.Decompress);
|
||||
zs.CopyTo(fs);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
302
BinaryObjectScanner/FileType/BSP.cs
Normal file
302
BinaryObjectScanner/FileType/BSP.cs
Normal file
@@ -0,0 +1,302 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life Level
|
||||
/// </summary>
|
||||
public class BSP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var bsp = SabreTools.Serialization.Wrappers.BSP.Create(stream);
|
||||
if (bsp == null)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files
|
||||
Directory.CreateDirectory(outDir);
|
||||
ExtractAllLumps(bsp, outDir);
|
||||
ExtractAllTextures(bsp, outDir);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all lumps from the BSP to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all lumps extracted, false otherwise</returns>
|
||||
public static bool ExtractAllLumps(SabreTools.Serialization.Wrappers.BSP item, string outputDirectory)
|
||||
{
|
||||
// If we have no lumps
|
||||
if (item.Model.Lumps == null || item.Model.Lumps.Length == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all lumps to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < item.Model.Lumps.Length; i++)
|
||||
{
|
||||
allExtracted &= ExtractLump(item, i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a lump from the BSP to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">Lump index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the lump extracted, false otherwise</returns>
|
||||
public static bool ExtractLump(SabreTools.Serialization.Wrappers.BSP item, int index, string outputDirectory)
|
||||
{
|
||||
// If we have no lumps
|
||||
if (item.Model.Lumps == null || item.Model.Lumps.Length == 0)
|
||||
return false;
|
||||
|
||||
// If the lumps index is invalid
|
||||
if (index < 0 || index >= item.Model.Lumps.Length)
|
||||
return false;
|
||||
|
||||
// Get the lump
|
||||
var lump = item.Model.Lumps[index];
|
||||
if (lump == null)
|
||||
return false;
|
||||
|
||||
// Read the data
|
||||
var data = item.ReadFromDataSource((int)lump.Offset, (int)lump.Length);
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// Create the filename
|
||||
string filename = $"lump_{index}.bin";
|
||||
switch (index)
|
||||
{
|
||||
case SabreTools.Models.BSP.Constants.HL_BSP_LUMP_ENTITIES:
|
||||
filename = "entities.ent";
|
||||
break;
|
||||
case SabreTools.Models.BSP.Constants.HL_BSP_LUMP_TEXTUREDATA:
|
||||
filename = "texture_data.bin";
|
||||
break;
|
||||
}
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all textures from the BSP to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all textures extracted, false otherwise</returns>
|
||||
public static bool ExtractAllTextures(SabreTools.Serialization.Wrappers.BSP item, string outputDirectory)
|
||||
{
|
||||
// If we have no textures
|
||||
if (item.Model.TextureHeader?.Offsets == null || item.Model.TextureHeader.Offsets.Length == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all lumps to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < item.Model.TextureHeader.Offsets.Length; i++)
|
||||
{
|
||||
allExtracted &= ExtractTexture(item, i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a texture from the BSP to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">Lump index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the texture extracted, false otherwise</returns>
|
||||
public static bool ExtractTexture(SabreTools.Serialization.Wrappers.BSP item, int index, string outputDirectory)
|
||||
{
|
||||
// If we have no textures
|
||||
if (item.Model.Textures == null || item.Model.Textures.Length == 0)
|
||||
return false;
|
||||
|
||||
// If the texture index is invalid
|
||||
if (index < 0 || index >= item.Model.Textures.Length)
|
||||
return false;
|
||||
|
||||
// Get the texture
|
||||
var texture = item.Model.Textures[index];
|
||||
if (texture == null)
|
||||
return false;
|
||||
|
||||
// Read the data
|
||||
var data = CreateTextureData(texture);
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// Create the filename
|
||||
string filename = $"{texture.Name}.bmp";
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a bitmap from the texture and palette data
|
||||
/// </summary>
|
||||
/// <param name="texture">Texture object to format</param>
|
||||
/// <returns>Byte array representing the texture as a bitmap</returns>
|
||||
private static byte[]? CreateTextureData(SabreTools.Models.BSP.Texture texture)
|
||||
{
|
||||
// If there's no palette data
|
||||
if (texture.PaletteData == null || texture.PaletteData.Length == 0)
|
||||
return null;
|
||||
|
||||
// If there's no texture data
|
||||
if (texture.TextureData == null || texture.TextureData.Length == 0)
|
||||
return null;
|
||||
|
||||
// Create the bitmap file header
|
||||
var fileHeader = new SabreTools.Models.BMP.BITMAPFILEHEADER()
|
||||
{
|
||||
Type = ('M' << 8) | 'B',
|
||||
Size = 14 + 40 + (texture.PaletteSize * 4) + (texture.Width * texture.Height),
|
||||
OffBits = 14 + 40 + (texture.PaletteSize * 4),
|
||||
};
|
||||
|
||||
// Create the bitmap info header
|
||||
var infoHeader = new SabreTools.Models.BMP.BITMAPINFOHEADER
|
||||
{
|
||||
Size = 40,
|
||||
Width = (int)texture.Width,
|
||||
Height = (int)texture.Height,
|
||||
Planes = 1,
|
||||
BitCount = 8,
|
||||
SizeImage = 0,
|
||||
ClrUsed = texture.PaletteSize,
|
||||
ClrImportant = texture.PaletteSize,
|
||||
};
|
||||
|
||||
// Reformat the palette data
|
||||
byte[] paletteData = new byte[texture.PaletteSize * 4];
|
||||
for (uint i = 0; i < texture.PaletteSize; i++)
|
||||
{
|
||||
paletteData[i * 4 + 0] = texture.PaletteData[i * 3 + 2];
|
||||
paletteData[i * 4 + 1] = texture.PaletteData[i * 3 + 1];
|
||||
paletteData[i * 4 + 2] = texture.PaletteData[i * 3 + 0];
|
||||
paletteData[i * 4 + 3] = 0;
|
||||
}
|
||||
|
||||
// Reformat the pixel data
|
||||
byte[] pixelData = new byte[texture.Width * texture.Height];
|
||||
for (uint i = 0; i < texture.Width; i++)
|
||||
{
|
||||
for (uint j = 0; j < texture.Height; j++)
|
||||
{
|
||||
pixelData[i + ((texture.Height - 1 - j) * texture.Width)] = texture.TextureData[i + j * texture.Width];
|
||||
}
|
||||
}
|
||||
|
||||
// Build the file data
|
||||
List<byte> buffer = new List<byte>();
|
||||
|
||||
// Bitmap file header
|
||||
buffer.AddRange(BitConverter.GetBytes(fileHeader.Type));
|
||||
buffer.AddRange(BitConverter.GetBytes(fileHeader.Size));
|
||||
buffer.AddRange(BitConverter.GetBytes(fileHeader.Reserved1));
|
||||
buffer.AddRange(BitConverter.GetBytes(fileHeader.Reserved2));
|
||||
buffer.AddRange(BitConverter.GetBytes(fileHeader.OffBits));
|
||||
|
||||
// Bitmap info header
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.Size));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.Width));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.Height));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.Planes));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.BitCount));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.Compression));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.SizeImage));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.XPelsPerMeter));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.YPelsPerMeter));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.ClrUsed));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.ClrImportant));
|
||||
|
||||
// Palette data
|
||||
buffer.AddRange(paletteData);
|
||||
|
||||
// Pixel data
|
||||
buffer.AddRange(pixelData);
|
||||
|
||||
return buffer.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
58
BinaryObjectScanner/FileType/BZip2.cs
Normal file
58
BinaryObjectScanner/FileType/BZip2.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.BZip2;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// bzip2 archive
|
||||
/// </summary>
|
||||
public class BZip2 : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (stream == null || !stream.CanRead)
|
||||
return false;
|
||||
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
try
|
||||
{
|
||||
// Try opening the stream
|
||||
using var bz2File = new BZip2Stream(stream, CompressionMode.Decompress, true);
|
||||
|
||||
// Create the output file path
|
||||
Directory.CreateDirectory(outDir);
|
||||
string tempFile = Path.Combine(outDir, Guid.NewGuid().ToString());
|
||||
|
||||
// Extract the file
|
||||
using FileStream fs = File.OpenWrite(tempFile);
|
||||
bz2File.CopyTo(fs);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BurnOutSharp.Tools;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using OpenMcdf;
|
||||
#endif
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
public class MSI : IScannable
|
||||
/// <summary>
|
||||
/// Compound File Binary
|
||||
/// </summary>
|
||||
public class CFB : IExtractable
|
||||
{
|
||||
/// <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)
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
return false;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
// If the MSI file itself fails
|
||||
#if NET20 || NET35
|
||||
// Not supported for .NET Framework 2.0 or .NET Framework 3.5 due to library support
|
||||
return false;
|
||||
#else
|
||||
try
|
||||
{
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
using (CompoundFile msi = new CompoundFile(stream, CFSUpdateMode.ReadOnly, CFSConfiguration.Default))
|
||||
using var msi = new CompoundFile(stream, CFSUpdateMode.ReadOnly, CFSConfiguration.Default);
|
||||
msi.RootStorage.VisitEntries((e) =>
|
||||
{
|
||||
msi.RootStorage.VisitEntries((e) =>
|
||||
try
|
||||
{
|
||||
if (!e.IsStream)
|
||||
return;
|
||||
@@ -56,7 +48,10 @@ namespace BurnOutSharp.FileType
|
||||
if (strData == null)
|
||||
return;
|
||||
|
||||
string decoded = DecodeStreamName(e.Name).TrimEnd('\0');
|
||||
var decoded = DecodeStreamName(e.Name)?.TrimEnd('\0');
|
||||
if (decoded == null)
|
||||
return;
|
||||
|
||||
byte[] nameBytes = Encoding.UTF8.GetBytes(e.Name);
|
||||
|
||||
// UTF-8 encoding of 0x4840.
|
||||
@@ -68,42 +63,32 @@ namespace BurnOutSharp.FileType
|
||||
decoded = decoded.Replace(c, '_');
|
||||
}
|
||||
|
||||
string filename = Path.Combine(tempPath, decoded);
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(strData, 0, strData.Length);
|
||||
}
|
||||
}, recursive: true);
|
||||
}
|
||||
string tempFile = Path.Combine(outDir, decoded);
|
||||
var directoryName = Path.GetDirectoryName(tempFile);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
using Stream fs = File.OpenWrite(tempFile);
|
||||
fs.Write(strData, 0, strData.Length);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}, recursive: true);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <remarks>Adapted from LibMSI</remarks>
|
||||
private static string DecodeStreamName(string input)
|
||||
public static string? DecodeStreamName(string input)
|
||||
{
|
||||
if (input == null)
|
||||
return null;
|
||||
348
BinaryObjectScanner/FileType/Executable.cs
Normal file
348
BinaryObjectScanner/FileType/Executable.cs
Normal file
@@ -0,0 +1,348 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Data;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Executable or library
|
||||
/// </summary>
|
||||
public class Executable : IDetectable
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Determines if game engines are counted as detected protections or not
|
||||
/// </summary>
|
||||
public bool IncludeGameEngines { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if packers are counted as detected protections or not
|
||||
/// </summary>
|
||||
public bool IncludePackers { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// Get all non-nested protections
|
||||
var protections = DetectDict(stream, file, getProtections: null, includeDebug);
|
||||
if (protections.Count == 0)
|
||||
return null;
|
||||
|
||||
// Create the internal list
|
||||
var protectionList = new List<string>();
|
||||
foreach (string key in protections.Keys)
|
||||
{
|
||||
protectionList.AddRange(protections[key]);
|
||||
}
|
||||
|
||||
return string.Join(";", [.. protectionList]);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IDetectable.Detect(Stream, string, bool)"/>
|
||||
/// <remarks>
|
||||
/// Ideally, we wouldn't need to circumvent the proper handling of file types just for Executable,
|
||||
/// but due to the complexity of scanning, this is not currently possible.
|
||||
/// </remarks>
|
||||
public ProtectionDictionary DetectDict(Stream stream,
|
||||
string file,
|
||||
Func<string, ProtectionDictionary>? getProtections,
|
||||
bool includeDebug)
|
||||
{
|
||||
// Create the output dictionary
|
||||
var protections = new ProtectionDictionary();
|
||||
|
||||
// Try to create a wrapper for the proper executable type
|
||||
IWrapper? wrapper;
|
||||
try
|
||||
{
|
||||
wrapper = WrapperFactory.CreateExecutableWrapper(stream);
|
||||
if (wrapper == null)
|
||||
return protections;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return protections;
|
||||
}
|
||||
|
||||
// Only use generic content checks if we're in debug mode
|
||||
if (includeDebug)
|
||||
{
|
||||
var subProtections = RunContentChecks(file, stream, includeDebug);
|
||||
protections.Append(file, subProtections.Values);
|
||||
}
|
||||
|
||||
if (wrapper is MSDOS mz)
|
||||
{
|
||||
// Standard checks
|
||||
var subProtections = RunExecutableChecks(file, mz, StaticChecks.MSDOSExecutableCheckClasses, includeDebug);
|
||||
protections.Append(file, subProtections.Values);
|
||||
|
||||
// Extractable checks
|
||||
var extractedProtections = HandleExtractableProtections(file, mz, subProtections.Keys, getProtections, includeDebug);
|
||||
protections.Append(extractedProtections);
|
||||
}
|
||||
else if (wrapper is LinearExecutable lex)
|
||||
{
|
||||
// Standard checks
|
||||
var subProtections = RunExecutableChecks(file, lex, StaticChecks.LinearExecutableCheckClasses, includeDebug);
|
||||
protections.Append(file, subProtections.Values);
|
||||
|
||||
// Extractable checks
|
||||
var extractedProtections = HandleExtractableProtections(file, lex, subProtections.Keys, getProtections, includeDebug);
|
||||
protections.Append(extractedProtections);
|
||||
}
|
||||
else if (wrapper is NewExecutable nex)
|
||||
{
|
||||
// Standard checks
|
||||
var subProtections = RunExecutableChecks(file, nex, StaticChecks.NewExecutableCheckClasses, includeDebug);
|
||||
protections.Append(file, subProtections.Values);
|
||||
|
||||
// Extractable checks
|
||||
var extractedProtections = HandleExtractableProtections(file, nex, subProtections.Keys, getProtections, includeDebug);
|
||||
protections.Append(extractedProtections);
|
||||
}
|
||||
else if (wrapper is PortableExecutable pex)
|
||||
{
|
||||
// Standard checks
|
||||
var subProtections = RunExecutableChecks(file, pex, StaticChecks.PortableExecutableCheckClasses, includeDebug);
|
||||
protections.Append(file, subProtections.Values);
|
||||
|
||||
// Extractable checks
|
||||
var extractedProtections = HandleExtractableProtections(file, pex, subProtections.Keys, getProtections, includeDebug);
|
||||
protections.Append(extractedProtections);
|
||||
}
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
#region Check Runners
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single file based on all content check implementations
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the stream, for tracking</param>
|
||||
/// <param name="stream">Stream to scan the contents of</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections in file, empty on error</returns>
|
||||
public IDictionary<IContentCheck, string> RunContentChecks(string? file, Stream stream, bool includeDebug)
|
||||
{
|
||||
// Create the output dictionary
|
||||
var protections = new CheckDictionary<IContentCheck>();
|
||||
|
||||
// If we have an invalid file
|
||||
if (string.IsNullOrEmpty(file))
|
||||
return protections;
|
||||
else if (!File.Exists(file))
|
||||
return protections;
|
||||
|
||||
// Read the file contents
|
||||
byte[] fileContent = [];
|
||||
try
|
||||
{
|
||||
fileContent = stream.ReadBytes((int)stream.Length);
|
||||
if (fileContent == null)
|
||||
return protections;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return protections;
|
||||
}
|
||||
|
||||
// Iterate through all checks
|
||||
StaticChecks.ContentCheckClasses.IterateWithAction(checkClass =>
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
var protection = checkClass.CheckContents(file!, fileContent, includeDebug);
|
||||
if (string.IsNullOrEmpty(protection))
|
||||
return;
|
||||
|
||||
// If we are filtering on game engines
|
||||
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
|
||||
return;
|
||||
|
||||
// If we are filtering on packers
|
||||
if (CheckIfPacker(checkClass) && !IncludePackers)
|
||||
return;
|
||||
|
||||
protections.Append(checkClass, protection);
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single file based on all executable check implementations
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the executable, for tracking</param>
|
||||
/// <param name="exe">Executable to scan</param>
|
||||
/// <param name="checks">Set of checks to use</param>
|
||||
/// <param name="scanner">Scanner for handling recursive protections</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections in file, empty on error</returns>
|
||||
public IDictionary<U, string> RunExecutableChecks<T, U>(string file, T exe, List<U> checks, bool includeDebug)
|
||||
where T : WrapperBase
|
||||
where U : IExecutableCheck<T>
|
||||
{
|
||||
// Create the output dictionary
|
||||
var protections = new CheckDictionary<U>();
|
||||
|
||||
// Iterate through all checks
|
||||
checks.IterateWithAction(checkClass =>
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
var protection = checkClass.CheckExecutable(file, exe, includeDebug);
|
||||
if (string.IsNullOrEmpty(protection))
|
||||
return;
|
||||
|
||||
// If we are filtering on game engines
|
||||
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
|
||||
return;
|
||||
|
||||
// If we are filtering on packers
|
||||
if (CheckIfPacker(checkClass) && !IncludePackers)
|
||||
return;
|
||||
|
||||
protections.Append(checkClass, protection);
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle extractable protections, such as executable packers
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the stream, for tracking</param>
|
||||
/// <param name="exe">Executable to scan the contents of</param>
|
||||
/// <param name="checks">Set of classes returned from Exectuable scans</param>
|
||||
/// <param name="getProtections">Optional function for handling recursive protections</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections found from extraction, empty on error</returns>
|
||||
private static ProtectionDictionary HandleExtractableProtections<T, U>(string file,
|
||||
T exe,
|
||||
IEnumerable<U> checks,
|
||||
Func<string, ProtectionDictionary>? getProtections,
|
||||
bool includeDebug)
|
||||
where T : WrapperBase
|
||||
where U : IExecutableCheck<T>
|
||||
{
|
||||
// Create the output dictionary
|
||||
var protections = new ProtectionDictionary();
|
||||
|
||||
// If we have an invalid set of classes
|
||||
if (checks == null || !checks.Any())
|
||||
return protections;
|
||||
|
||||
// If we have any extractable packers
|
||||
var extractables = checks
|
||||
.Where(c => c is IExtractableExecutable<T>)
|
||||
.Select(c => c as IExtractableExecutable<T>);
|
||||
extractables.IterateWithAction(extractable =>
|
||||
{
|
||||
var subProtections = PerformExtractableCheck(extractable!, file, exe, getProtections, includeDebug);
|
||||
protections.Append(subProtections);
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle files based on an IExtractableExecutable implementation
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the stream, for tracking</param>
|
||||
/// <param name="exe">Executable to scan the contents of</param>
|
||||
/// <param name="impl">IExtractableExecutable class representing the file type</param>
|
||||
/// <param name="getProtections">Optional function for handling recursive protections</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections in path, empty on error</returns>
|
||||
private static ProtectionDictionary PerformExtractableCheck<T>(IExtractableExecutable<T> impl,
|
||||
string file,
|
||||
T exe,
|
||||
Func<string, ProtectionDictionary>? getProtections,
|
||||
bool includeDebug)
|
||||
where T : WrapperBase
|
||||
{
|
||||
// If we have an invalid extractable somehow
|
||||
if (impl == null)
|
||||
return [];
|
||||
|
||||
// If the extractable file itself fails
|
||||
try
|
||||
{
|
||||
// Extract and get the output path
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
bool extracted = impl.Extract(file, exe, tempPath, includeDebug);
|
||||
|
||||
// Collect and format all found protections
|
||||
ProtectionDictionary? subProtections = null;
|
||||
if (extracted && getProtections != null)
|
||||
subProtections = getProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(tempPath))
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
// Prepare the returned protections
|
||||
subProtections?.StripFromKeys(tempPath);
|
||||
subProtections?.PrependToKeys(file);
|
||||
return subProtections ?? [];
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if an implementation is a game engine using reflection
|
||||
/// </summary>
|
||||
/// <param name="impl">Implementation that was last used to check</param>
|
||||
private static bool CheckIfGameEngine(object impl)
|
||||
{
|
||||
return impl.GetType().Namespace?.ToLowerInvariant()?.Contains("gameengine") ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if an implementation is a packer using reflection
|
||||
/// </summary>
|
||||
/// <param name="impl">Implementation that was last used to check</param>
|
||||
private static bool CheckIfPacker(object impl)
|
||||
{
|
||||
return impl.GetType().Namespace?.ToLowerInvariant()?.Contains("packer") ?? false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
152
BinaryObjectScanner/FileType/GCF.cs
Normal file
152
BinaryObjectScanner/FileType/GCF.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life Game Cache File
|
||||
/// </summary>
|
||||
public class GCF : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var gcf = SabreTools.Serialization.Wrappers.GCF.Create(stream);
|
||||
if (gcf == null)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files
|
||||
Directory.CreateDirectory(outDir);
|
||||
ExtractAll(gcf, outDir);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from the GCF to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all files extracted, false otherwise</returns>
|
||||
public static bool ExtractAll(SabreTools.Serialization.Wrappers.GCF item, string outputDirectory)
|
||||
{
|
||||
// If we have no files
|
||||
if (item.Files == null || item.Files.Length == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < item.Files.Length; i++)
|
||||
{
|
||||
allExtracted &= ExtractFile(item, i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a file from the GCF to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">File index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the file extracted, false otherwise</returns>
|
||||
public static bool ExtractFile(SabreTools.Serialization.Wrappers.GCF item, int index, string outputDirectory)
|
||||
{
|
||||
// If we have no files
|
||||
if (item.Files == null || item.Files.Length == 0 || item.DataBlockOffsets == null)
|
||||
return false;
|
||||
|
||||
// If the files index is invalid
|
||||
if (index < 0 || index >= item.Files.Length)
|
||||
return false;
|
||||
|
||||
// Get the file
|
||||
var file = item.Files[index];
|
||||
if (file?.BlockEntries == null || file.Size == 0)
|
||||
return false;
|
||||
|
||||
// If the file is encrypted -- TODO: Revisit later
|
||||
if (file.Encrypted)
|
||||
return false;
|
||||
|
||||
// Get all data block offsets needed for extraction
|
||||
var dataBlockOffsets = new List<long>();
|
||||
for (int i = 0; i < file.BlockEntries.Length; i++)
|
||||
{
|
||||
var blockEntry = file.BlockEntries[i];
|
||||
if (blockEntry == null)
|
||||
continue;
|
||||
|
||||
uint dataBlockIndex = blockEntry.FirstDataBlockIndex;
|
||||
long blockEntrySize = blockEntry.FileDataSize;
|
||||
while (blockEntrySize > 0)
|
||||
{
|
||||
long dataBlockOffset = item.DataBlockOffsets[dataBlockIndex++];
|
||||
dataBlockOffsets.Add(dataBlockOffset);
|
||||
blockEntrySize -= item.Model.DataBlockHeader?.BlockSize ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the filename
|
||||
var filename = file.Path;
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename ?? $"file{index}");
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
// Now read the data sequentially and write out while we have data left
|
||||
long fileSize = file.Size;
|
||||
for (int i = 0; i < dataBlockOffsets.Count; i++)
|
||||
{
|
||||
int readSize = (int)Math.Min(item.Model.DataBlockHeader?.BlockSize ?? 0, fileSize);
|
||||
var data = item.ReadFromDataSource((int)dataBlockOffsets[i], readSize);
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
73
BinaryObjectScanner/FileType/GZIP.cs
Normal file
73
BinaryObjectScanner/FileType/GZIP.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.GZip;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// gzip archive
|
||||
/// </summary>
|
||||
public class GZIP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (stream == null || !stream.CanRead)
|
||||
return false;
|
||||
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
try
|
||||
{
|
||||
using var zipFile = GZipArchive.Open(stream);
|
||||
foreach (var entry in zipFile.Entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If the entry is a directory
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
// If the entry has an invalid key
|
||||
if (entry.Key == null)
|
||||
continue;
|
||||
|
||||
string tempFile = Path.Combine(outDir, entry.Key);
|
||||
var directoryName = Path.GetDirectoryName(tempFile);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
62
BinaryObjectScanner/FileType/InstallShieldArchiveV3.cs
Normal file
62
BinaryObjectScanner/FileType/InstallShieldArchiveV3.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using ISv3 = UnshieldSharp.Archive.InstallShieldArchiveV3;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// InstallShield archive v3
|
||||
/// </summary>
|
||||
public class InstallShieldArchiveV3 : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
var archive = new ISv3(file);
|
||||
foreach (var cfile in archive.Files)
|
||||
{
|
||||
try
|
||||
{
|
||||
string tempFile = Path.Combine(outDir, cfile.Key);
|
||||
var directoryName = Path.GetDirectoryName(tempFile);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
byte[]? fileContents = archive.Extract(cfile.Key, out string? error);
|
||||
if (fileContents == null || !string.IsNullOrEmpty(error))
|
||||
continue;
|
||||
|
||||
using (FileStream fs = File.OpenWrite(tempFile))
|
||||
{
|
||||
fs.Write(fileContents, 0, fileContents.Length);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
97
BinaryObjectScanner/FileType/InstallShieldCAB.cs
Normal file
97
BinaryObjectScanner/FileType/InstallShieldCAB.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using UnshieldSharp.Cabinet;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// InstallShield cabinet file
|
||||
/// </summary>
|
||||
public class InstallShieldCAB : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
// Get the name of the first cabinet file or header
|
||||
var directory = Path.GetDirectoryName(file);
|
||||
string noExtension = Path.GetFileNameWithoutExtension(file);
|
||||
|
||||
bool shouldScanCabinet;
|
||||
if (directory == null)
|
||||
{
|
||||
string filenamePattern = noExtension;
|
||||
filenamePattern = new Regex(@"\d+$").Replace(filenamePattern, string.Empty);
|
||||
bool cabinetHeaderExists = File.Exists(filenamePattern + "1.hdr");
|
||||
shouldScanCabinet = cabinetHeaderExists
|
||||
? file.Equals(filenamePattern + "1.hdr", StringComparison.OrdinalIgnoreCase)
|
||||
: file.Equals(filenamePattern + "1.cab", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
else
|
||||
{
|
||||
string filenamePattern = Path.Combine(directory, noExtension);
|
||||
filenamePattern = new Regex(@"\d+$").Replace(filenamePattern, string.Empty);
|
||||
bool cabinetHeaderExists = File.Exists(Path.Combine(directory, filenamePattern + "1.hdr"));
|
||||
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 anything but the first file
|
||||
if (!shouldScanCabinet)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
var cabfile = InstallShieldCabinet.Open(file);
|
||||
if (cabfile?.HeaderList == null)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < cabfile.HeaderList.FileCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if the file is valid first
|
||||
if (!cabfile.HeaderList.FileIsValid(i))
|
||||
continue;
|
||||
|
||||
string tempFile;
|
||||
try
|
||||
{
|
||||
string? filename = cabfile.HeaderList.GetFileName(i);
|
||||
tempFile = Path.Combine(outDir, filename ?? string.Empty);
|
||||
}
|
||||
catch
|
||||
{
|
||||
tempFile = Path.Combine(outDir, $"BAD_FILENAME{i}");
|
||||
}
|
||||
|
||||
cabfile.FileSave(i, tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
BinaryObjectScanner/FileType/LDSCRYPT.cs
Normal file
42
BinaryObjectScanner/FileType/LDSCRYPT.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Link Data Security encrypted file
|
||||
/// </summary>
|
||||
public class LDSCRYPT : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] magic = new byte[16];
|
||||
stream.Read(magic, 0, 16);
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x4C, 0x44, 0x53, 0x43, 0x52, 0x59, 0x50, 0x54 }))
|
||||
return "Link Data Security encrypted file";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
81
BinaryObjectScanner/FileType/MPQ.cs
Normal file
81
BinaryObjectScanner/FileType/MPQ.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if (NET452_OR_GREATER || NETCOREAPP) && WIN
|
||||
using StormLibSharp;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// MoPaQ game data archive
|
||||
/// </summary>
|
||||
public class MPQ : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
#if NET20 || NET35 || NET40 || !WIN
|
||||
// Not supported for old .NET due to feature requirements
|
||||
// Not supported in non-Windows builds due to DLL requirements
|
||||
return false;
|
||||
#else
|
||||
try
|
||||
{
|
||||
// Try to open the archive and listfile
|
||||
var mpqArchive = new MpqArchive(file, FileAccess.Read);
|
||||
string? listfile = null;
|
||||
MpqFileStream listStream = mpqArchive.OpenFile("(listfile)");
|
||||
|
||||
// If we can't read the listfile, we just return
|
||||
if (!listStream.CanRead)
|
||||
return false;
|
||||
|
||||
// Read the listfile in for processing
|
||||
using (var sr = new StreamReader(listStream))
|
||||
{
|
||||
listfile = sr.ReadToEnd();
|
||||
}
|
||||
|
||||
// Split the listfile by newlines
|
||||
string[] listfileLines = listfile.Replace("\r\n", "\n").Split('\n');
|
||||
|
||||
// Loop over each entry
|
||||
foreach (string sub in listfileLines)
|
||||
{
|
||||
try
|
||||
{
|
||||
string tempFile = Path.Combine(outDir, sub);
|
||||
var directoryName = Path.GetDirectoryName(tempFile);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
mpqArchive.ExtractFile(sub, tempFile);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
if (includeDebug) System.Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
if (includeDebug) System.Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
65
BinaryObjectScanner/FileType/MicrosoftCAB.cs
Normal file
65
BinaryObjectScanner/FileType/MicrosoftCAB.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if (NET40_OR_GREATER || NETCOREAPP) && WIN
|
||||
using LibMSPackN;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Microsoft cabinet file
|
||||
/// </summary>
|
||||
/// <remarks>Specification available at <see href="http://download.microsoft.com/download/5/0/1/501ED102-E53F-4CE0-AA6B-B0F93629DDC6/Exchange/%5BMS-CAB%5D.pdf"/></remarks>
|
||||
/// <see href="https://github.com/wine-mirror/wine/tree/master/dlls/cabinet"/>
|
||||
public class MicrosoftCAB : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
#if NET20 || NET35 || !WIN
|
||||
// Not supported for old .NET due to feature requirements
|
||||
// Not supported in non-Windows builds due to DLL requirements
|
||||
return false;
|
||||
#else
|
||||
try
|
||||
{
|
||||
// Loop over each entry
|
||||
var cabArchive = new MSCabinet(file);
|
||||
foreach (var compressedFile in cabArchive.GetFiles())
|
||||
{
|
||||
try
|
||||
{
|
||||
string tempFile = Path.Combine(outDir, compressedFile.Filename);
|
||||
var directoryName = Path.GetDirectoryName(tempFile);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
compressedFile.ExtractTo(tempFile);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
if (includeDebug) System.Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
if (includeDebug) System.Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
63
BinaryObjectScanner/FileType/MicrosoftLZ.cs
Normal file
63
BinaryObjectScanner/FileType/MicrosoftLZ.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Compression.LZ;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Microsoft LZ-compressed Files (LZ32)
|
||||
/// </summary>
|
||||
/// <remarks>This is treated like an archive type due to the packing style</remarks>
|
||||
public class MicrosoftLZ : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = Decompressor.Decompress(stream);
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// Create the temp filename
|
||||
string tempFile = "temp.bin";
|
||||
if (!string.IsNullOrEmpty(file))
|
||||
{
|
||||
var expandedFilePath = Decompressor.GetExpandedName(file, out _);
|
||||
if (expandedFilePath != null)
|
||||
tempFile = Path.GetFileName(expandedFilePath).TrimEnd('\0');
|
||||
if (tempFile.EndsWith(".ex"))
|
||||
tempFile += "e";
|
||||
else if (tempFile.EndsWith(".dl"))
|
||||
tempFile += "l";
|
||||
}
|
||||
|
||||
tempFile = Path.Combine(outDir, tempFile);
|
||||
var directoryName = Path.GetDirectoryName(tempFile);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
using Stream tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
|
||||
tempStream.Write(data, 0, data.Length);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
124
BinaryObjectScanner/FileType/PAK.cs
Normal file
124
BinaryObjectScanner/FileType/PAK.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life Package File
|
||||
/// </summary>
|
||||
public class PAK : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var pak = SabreTools.Serialization.Wrappers.PAK.Create(stream);
|
||||
if (pak == null)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files
|
||||
Directory.CreateDirectory(outDir);
|
||||
ExtractAll(pak, outDir);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from the PAK to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all files extracted, false otherwise</returns>
|
||||
public static bool ExtractAll(SabreTools.Serialization.Wrappers.PAK item, string outputDirectory)
|
||||
{
|
||||
// If we have no directory items
|
||||
if (item.Model.DirectoryItems == null || item.Model.DirectoryItems.Length == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < item.Model.DirectoryItems.Length; i++)
|
||||
{
|
||||
allExtracted &= ExtractFile(item, i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a file from the PAK to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">File index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the file extracted, false otherwise</returns>
|
||||
public static bool ExtractFile(SabreTools.Serialization.Wrappers.PAK item, int index, string outputDirectory)
|
||||
{
|
||||
// If we have no directory items
|
||||
if (item.Model.DirectoryItems == null || item.Model.DirectoryItems.Length == 0)
|
||||
return false;
|
||||
|
||||
// If the directory item index is invalid
|
||||
if (index < 0 || index >= item.Model.DirectoryItems.Length)
|
||||
return false;
|
||||
|
||||
// Get the directory item
|
||||
var directoryItem = item.Model.DirectoryItems[index];
|
||||
if (directoryItem == null)
|
||||
return false;
|
||||
|
||||
// Read the item data
|
||||
var data = item.ReadFromDataSource((int)directoryItem.ItemOffset, (int)directoryItem.ItemLength);
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// Create the filename
|
||||
var filename = directoryItem.ItemName;
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename ?? $"file{index}");
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
117
BinaryObjectScanner/FileType/PFF.cs
Normal file
117
BinaryObjectScanner/FileType/PFF.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// NovaLogic Game Archive Format
|
||||
/// </summary>
|
||||
public class PFF : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var pff = SabreTools.Serialization.Wrappers.PFF.Create(stream);
|
||||
if (pff == null)
|
||||
return false;
|
||||
|
||||
// Extract all files
|
||||
Directory.CreateDirectory(outDir);
|
||||
ExtractAll(pff, outDir);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all segments from the PFF to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all segments extracted, false otherwise</returns>
|
||||
public static bool ExtractAll(SabreTools.Serialization.Wrappers.PFF item, string outputDirectory)
|
||||
{
|
||||
// If we have no segments
|
||||
if (item.Model.Segments == null || item.Model.Segments.Length == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < item.Model.Segments.Length; i++)
|
||||
{
|
||||
allExtracted &= ExtractSegment(item, i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a segment from the PFF to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">Segment index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the segment extracted, false otherwise</returns>
|
||||
public static bool ExtractSegment(SabreTools.Serialization.Wrappers.PFF item, int index, string outputDirectory)
|
||||
{
|
||||
// If we have no segments
|
||||
if (item.Model.Header?.NumberOfFiles == null || item.Model.Header.NumberOfFiles == 0 || item.Model.Segments == null || item.Model.Segments.Length == 0)
|
||||
return false;
|
||||
|
||||
// If we have an invalid index
|
||||
if (index < 0 || index >= item.Model.Segments.Length)
|
||||
return false;
|
||||
|
||||
// Get the segment information
|
||||
var file = item.Model.Segments[index];
|
||||
if (file == null)
|
||||
return false;
|
||||
|
||||
// Get the read index and length
|
||||
int offset = (int)file.FileLocation;
|
||||
int size = (int)file.FileSize;
|
||||
|
||||
try
|
||||
{
|
||||
// Ensure the output directory exists
|
||||
Directory.CreateDirectory(outputDirectory);
|
||||
|
||||
// Create the output path
|
||||
string filePath = Path.Combine(outputDirectory, file.FileName ?? $"file{index}");
|
||||
using (FileStream fs = File.OpenWrite(filePath))
|
||||
{
|
||||
// Read the data block
|
||||
var data = item.ReadFromDataSource(offset, size);
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// Write the data -- TODO: Compressed data?
|
||||
fs.Write(data, 0, size);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
87
BinaryObjectScanner/FileType/PKZIP.cs
Normal file
87
BinaryObjectScanner/FileType/PKZIP.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Readers;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// PKWARE ZIP archive and derivatives
|
||||
/// </summary>
|
||||
public class PKZIP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
=> Extract(file, outDir, lookForHeader: false, includeDebug);
|
||||
|
||||
/// <inheritdoc cref="IExtractable.Extract(string, string, bool)"/>
|
||||
public bool Extract(string file, string outDir, bool lookForHeader, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, lookForHeader, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
=> Extract(stream, file, outDir, lookForHeader: false, includeDebug);
|
||||
|
||||
/// <inheritdoc cref="IExtractable.Extract(Stream?, string, string, bool)"/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool lookForHeader, bool includeDebug)
|
||||
{
|
||||
if (stream == null || !stream.CanRead)
|
||||
return false;
|
||||
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
try
|
||||
{
|
||||
var readerOptions = new ReaderOptions() { LookForHeader = lookForHeader };
|
||||
using var zipFile = ZipArchive.Open(stream, readerOptions);
|
||||
foreach (var entry in zipFile.Entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If the entry is a directory
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
// If the entry has an invalid key
|
||||
if (entry.Key == null)
|
||||
continue;
|
||||
|
||||
// If the entry is partial due to an incomplete multi-part archive, skip it
|
||||
if (!entry.IsComplete)
|
||||
continue;
|
||||
|
||||
string tempFile = Path.Combine(outDir, entry.Key);
|
||||
var directoryName = Path.GetDirectoryName(tempFile);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
42
BinaryObjectScanner/FileType/PLJ.cs
Normal file
42
BinaryObjectScanner/FileType/PLJ.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// PlayJ audio file
|
||||
/// </summary>
|
||||
public class PLJ : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] magic = new byte[16];
|
||||
stream.Read(magic, 0, 16);
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0xFF, 0x9D, 0x53, 0x4B }))
|
||||
return "PlayJ Audio File";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
153
BinaryObjectScanner/FileType/Quantum.cs
Normal file
153
BinaryObjectScanner/FileType/Quantum.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Quantum Archive
|
||||
/// </summary>
|
||||
public class Quantum : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var quantum = SabreTools.Serialization.Wrappers.Quantum.Create(stream);
|
||||
if (quantum == null)
|
||||
return false;
|
||||
|
||||
// Extract all files
|
||||
Directory.CreateDirectory(outDir);
|
||||
ExtractAll(quantum, outDir);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from the Quantum archive to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all files extracted, false otherwise</returns>
|
||||
public static bool ExtractAll(SabreTools.Serialization.Wrappers.Quantum item, string outputDirectory)
|
||||
{
|
||||
// If we have no files
|
||||
if (item.Model.FileList == null || item.Model.FileList.Length == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < item.Model.FileList.Length; i++)
|
||||
{
|
||||
allExtracted &= ExtractFile(item, i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a file from the Quantum archive to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">File index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the file extracted, false otherwise</returns>
|
||||
public static bool ExtractFile(SabreTools.Serialization.Wrappers.Quantum item, int index, string outputDirectory)
|
||||
{
|
||||
// If we have no files
|
||||
if (item.Model.Header == null || item.Model.Header.FileCount == 0 || item.Model.FileList == null || item.Model.FileList.Length == 0)
|
||||
return false;
|
||||
|
||||
// If we have an invalid index
|
||||
if (index < 0 || index >= item.Model.FileList.Length)
|
||||
return false;
|
||||
|
||||
// Get the file information
|
||||
var fileDescriptor = item.Model.FileList[index];
|
||||
|
||||
// Read the entire compressed data
|
||||
int compressedDataOffset = (int)item.Model.CompressedDataOffset;
|
||||
int compressedDataLength = item.GetEndOfFile() - compressedDataOffset;
|
||||
var compressedData = item.ReadFromDataSource(compressedDataOffset, compressedDataLength);
|
||||
|
||||
// TODO: Figure out decompression
|
||||
// - Single-file archives seem to work
|
||||
// - Single-file archives with files that span a window boundary seem to work
|
||||
// - The first files in each archive seem to work
|
||||
return false;
|
||||
|
||||
// // Setup the decompression state
|
||||
// State state = new State();
|
||||
// Decompressor.InitState(state, TableSize, CompressionFlags);
|
||||
|
||||
// // Decompress the entire array
|
||||
// int decompressedDataLength = (int)FileList.Sum(fd => fd.ExpandedFileSize);
|
||||
// byte[] decompressedData = new byte[decompressedDataLength];
|
||||
// Decompressor.Decompress(state, compressedData.Length, compressedData, decompressedData.Length, decompressedData);
|
||||
|
||||
// // Read the data
|
||||
// int offset = (int)FileList.Take(index).Sum(fd => fd.ExpandedFileSize);
|
||||
// byte[] data = new byte[fileDescriptor.ExpandedFileSize];
|
||||
// Array.Copy(decompressedData, offset, data, 0, data.Length);
|
||||
|
||||
// // Loop through all files before the current
|
||||
// for (int i = 0; i < index; i++)
|
||||
// {
|
||||
// // Decompress the next block of data
|
||||
// byte[] tempData = new byte[FileList[i].ExpandedFileSize];
|
||||
// int lastRead = Decompressor.Decompress(state, compressedData.Length, compressedData, tempData.Length, tempData);
|
||||
// compressedData = new ReadOnlySpan<byte>(compressedData, (lastRead), compressedData.Length - (lastRead)).ToArray();
|
||||
// }
|
||||
|
||||
// // Read the data
|
||||
// byte[] data = new byte[fileDescriptor.ExpandedFileSize];
|
||||
// _ = Decompressor.Decompress(state, compressedData.Length, compressedData, data.Length, data);
|
||||
|
||||
// // Create the filename
|
||||
// string filename = fileDescriptor.FileName;
|
||||
|
||||
// // If we have an invalid output directory
|
||||
// if (string.IsNullOrEmpty(outputDirectory))
|
||||
// return false;
|
||||
|
||||
// // Create the full output path
|
||||
// filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// // Ensure the output directory is created
|
||||
// Directory.CreateDirectory(Path.GetDirectoryName(filename));
|
||||
|
||||
// // Try to write the data
|
||||
// try
|
||||
// {
|
||||
// // Open the output file for writing
|
||||
// using (Stream fs = File.OpenWrite(filename))
|
||||
// {
|
||||
// fs.Write(data, 0, data.Length);
|
||||
// }
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
90
BinaryObjectScanner/FileType/RAR.cs
Normal file
90
BinaryObjectScanner/FileType/RAR.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Rar;
|
||||
using SharpCompress.Readers;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// RAR archive
|
||||
/// </summary>
|
||||
public class RAR : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
=> Extract(file, outDir, lookForHeader: false, includeDebug);
|
||||
|
||||
/// <inheritdoc cref="IExtractable.Extract(string, string, bool)"/>
|
||||
public bool Extract(string file, string outDir, bool lookForHeader, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, lookForHeader, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
=> Extract(stream, file, outDir, lookForHeader: false, includeDebug);
|
||||
|
||||
/// <inheritdoc cref="IExtractable.Extract(Stream?, string, string, bool)"/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool lookForHeader, bool includeDebug)
|
||||
{
|
||||
if (stream == null || !stream.CanRead)
|
||||
return false;
|
||||
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
try
|
||||
{
|
||||
var readerOptions = new ReaderOptions() { LookForHeader = lookForHeader };
|
||||
using RarArchive rarFile = RarArchive.Open(stream, readerOptions);
|
||||
if (!rarFile.IsComplete)
|
||||
return false;
|
||||
|
||||
foreach (var entry in rarFile.Entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If the entry is a directory
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
// If the entry has an invalid key
|
||||
if (entry.Key == null)
|
||||
continue;
|
||||
|
||||
// If we have a partial entry due to an incomplete multi-part archive, skip it
|
||||
if (!entry.IsComplete)
|
||||
continue;
|
||||
|
||||
string tempFile = Path.Combine(outDir, entry.Key);
|
||||
var directoryName = Path.GetDirectoryName(tempFile);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
46
BinaryObjectScanner/FileType/RealArcadeInstaller.cs
Normal file
46
BinaryObjectScanner/FileType/RealArcadeInstaller.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// RealArcade Installer. Known to use the ".rgs" file extension.
|
||||
///
|
||||
/// TODO: Add further parsing, game ID and name should be possible to parse.
|
||||
/// </summary>
|
||||
public class RealArcadeInstaller : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] magic = new byte[16];
|
||||
stream.Read(magic, 0, 16);
|
||||
|
||||
// RASGI2.0
|
||||
// Found in the ".rgs" files in IA item "Nova_RealArcadeCD_USA".
|
||||
if (magic.StartsWith(new byte?[] { 0x52, 0x41, 0x53, 0x47, 0x49, 0x32, 0x2E, 0x30 }))
|
||||
return "RealArcade Installer";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
BinaryObjectScanner/FileType/RealArcadeMezzanine.cs
Normal file
46
BinaryObjectScanner/FileType/RealArcadeMezzanine.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// RealArcade Mezzanine files, which contain metadata. Known to use the ".mez" file extension.
|
||||
///
|
||||
/// TODO: Add further parsing, game ID should be possible to parse.
|
||||
/// </summary>
|
||||
public class RealArcadeMezzanine : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] magic = new byte[16];
|
||||
stream.Read(magic, 0, 16);
|
||||
|
||||
// XZip2.0
|
||||
// Found in the ".mez" files in IA item "Nova_RealArcadeCD_USA".
|
||||
if (magic.StartsWith(new byte?[] { 0x58, 0x5A, 0x69, 0x70, 0x32, 0x2E, 0x30 }))
|
||||
return "RealArcade Mezzanine";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
BinaryObjectScanner/FileType/SFFS.cs
Normal file
59
BinaryObjectScanner/FileType/SFFS.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// StarForce Filesystem file
|
||||
/// </summary>
|
||||
/// <see href="https://forum.xentax.com/viewtopic.php?f=21&t=2084"/>
|
||||
public class SFFS : IExtractable, IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] magic = new byte[16];
|
||||
stream.Read(magic, 0, 16);
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x53, 0x46, 0x46, 0x53 }))
|
||||
return "StarForce Filesystem Container";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
277
BinaryObjectScanner/FileType/SGA.cs
Normal file
277
BinaryObjectScanner/FileType/SGA.cs
Normal file
@@ -0,0 +1,277 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Compression.zlib;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// SGA game archive
|
||||
/// </summary>
|
||||
public class SGA : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var sga = SabreTools.Serialization.Wrappers.SGA.Create(stream);
|
||||
if (sga == null)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files
|
||||
Directory.CreateDirectory(outDir);
|
||||
ExtractAll(sga, outDir);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from the SGA to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all files extracted, false otherwise</returns>
|
||||
public static bool ExtractAll(SabreTools.Serialization.Wrappers.SGA item, string outputDirectory)
|
||||
{
|
||||
// Get the number of files
|
||||
int filesLength;
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4: filesLength = (item.Model.Directory as SabreTools.Models.SGA.Directory4)?.Files?.Length ?? 0; break;
|
||||
case 5: filesLength = (item.Model.Directory as SabreTools.Models.SGA.Directory5)?.Files?.Length ?? 0; break;
|
||||
case 6: filesLength = (item.Model.Directory as SabreTools.Models.SGA.Directory6)?.Files?.Length ?? 0; break;
|
||||
case 7: filesLength = (item.Model.Directory as SabreTools.Models.SGA.Directory7)?.Files?.Length ?? 0; break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
// If we have no files
|
||||
if (filesLength == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < filesLength; i++)
|
||||
{
|
||||
allExtracted &= ExtractFile(item, i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a file from the SGA to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">File index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the file extracted, false otherwise</returns>
|
||||
public static bool ExtractFile(SabreTools.Serialization.Wrappers.SGA item, int index, string outputDirectory)
|
||||
{
|
||||
// Get the number of files
|
||||
int filesLength;
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4: filesLength = (item.Model.Directory as SabreTools.Models.SGA.Directory4)?.Files?.Length ?? 0; break;
|
||||
case 5: filesLength = (item.Model.Directory as SabreTools.Models.SGA.Directory5)?.Files?.Length ?? 0; break;
|
||||
case 6: filesLength = (item.Model.Directory as SabreTools.Models.SGA.Directory6)?.Files?.Length ?? 0; break;
|
||||
case 7: filesLength = (item.Model.Directory as SabreTools.Models.SGA.Directory7)?.Files?.Length ?? 0; break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
// If we have no files
|
||||
if (filesLength == 0)
|
||||
return false;
|
||||
|
||||
// If the files index is invalid
|
||||
if (index < 0 || index >= filesLength)
|
||||
return false;
|
||||
|
||||
// Get the files
|
||||
object? file;
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4: file = (item.Model.Directory as SabreTools.Models.SGA.Directory4)?.Files?[index]; break;
|
||||
case 5: file = (item.Model.Directory as SabreTools.Models.SGA.Directory5)?.Files?[index]; break;
|
||||
case 6: file = (item.Model.Directory as SabreTools.Models.SGA.Directory6)?.Files?[index]; break;
|
||||
case 7: file = (item.Model.Directory as SabreTools.Models.SGA.Directory7)?.Files?[index]; break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
if (file == null)
|
||||
return false;
|
||||
|
||||
// Create the filename
|
||||
var filename = string.Empty;
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4:
|
||||
case 5: filename = (file as SabreTools.Models.SGA.File4)?.Name; break;
|
||||
case 6: filename = (file as SabreTools.Models.SGA.File6)?.Name; break;
|
||||
case 7: filename = (file as SabreTools.Models.SGA.File7)?.Name; break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
// Loop through and get all parent directories
|
||||
var parentNames = new List<string?> { filename };
|
||||
|
||||
// Get the parent directory
|
||||
var folder = default(object);
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4: folder = (item.Model.Directory as SabreTools.Models.SGA.Directory4)?.Folders?.FirstOrDefault(f => f != null && index >= f.FileStartIndex && index <= f.FileEndIndex); break;
|
||||
case 5: folder = (item.Model.Directory as SabreTools.Models.SGA.Directory5)?.Folders?.FirstOrDefault(f => f != null && index >= f.FileStartIndex && index <= f.FileEndIndex); break;
|
||||
case 6: folder = (item.Model.Directory as SabreTools.Models.SGA.Directory6)?.Folders?.FirstOrDefault(f => f != null && index >= f.FileStartIndex && index <= f.FileEndIndex); break;
|
||||
case 7: folder = (item.Model.Directory as SabreTools.Models.SGA.Directory7)?.Folders?.FirstOrDefault(f => f != null && index >= f.FileStartIndex && index <= f.FileEndIndex); break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
// If we have a parent folder
|
||||
if (folder != null)
|
||||
{
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4: parentNames.Add((folder as SabreTools.Models.SGA.Folder4)?.Name); break;
|
||||
case 5:
|
||||
case 6:
|
||||
case 7: parentNames.Add((folder as SabreTools.Models.SGA.Folder5)?.Name); break;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Should the section name/alias be used in the path as well?
|
||||
|
||||
// Reverse and assemble the filename
|
||||
parentNames.Reverse();
|
||||
#if NET20 || NET35
|
||||
var parentNamesArray = parentNames.Cast<string>().ToArray();
|
||||
filename = parentNamesArray[0];
|
||||
for (int i = 1; i < parentNamesArray.Length; i++)
|
||||
{
|
||||
filename = Path.Combine(filename, parentNamesArray[i]);
|
||||
}
|
||||
#else
|
||||
filename = Path.Combine(parentNames.Cast<string>().ToArray());
|
||||
#endif
|
||||
|
||||
// Get the file offset
|
||||
long fileOffset;
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4:
|
||||
case 5: fileOffset = (file as SabreTools.Models.SGA.File4)?.Offset ?? 0; break;
|
||||
case 6: fileOffset = (file as SabreTools.Models.SGA.File6)?.Offset ?? 0; break;
|
||||
case 7: fileOffset = (file as SabreTools.Models.SGA.File7)?.Offset ?? 0; break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
// Adjust the file offset
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4: fileOffset += (item.Model.Header as SabreTools.Models.SGA.Header4)?.FileDataOffset ?? 0; break;
|
||||
case 5: fileOffset += (item.Model.Header as SabreTools.Models.SGA.Header4)?.FileDataOffset ?? 0; break;
|
||||
case 6: fileOffset += (item.Model.Header as SabreTools.Models.SGA.Header6)?.FileDataOffset ?? 0; break;
|
||||
case 7: fileOffset += (item.Model.Header as SabreTools.Models.SGA.Header6)?.FileDataOffset ?? 0; break;
|
||||
default: return false;
|
||||
};
|
||||
|
||||
// Get the file sizes
|
||||
long fileSize, outputFileSize;
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4:
|
||||
case 5:
|
||||
fileSize = (file as SabreTools.Models.SGA.File4)?.SizeOnDisk ?? 0;
|
||||
outputFileSize = (file as SabreTools.Models.SGA.File4)?.Size ?? 0;
|
||||
break;
|
||||
case 6:
|
||||
fileSize = (file as SabreTools.Models.SGA.File6)?.SizeOnDisk ?? 0;
|
||||
outputFileSize = (file as SabreTools.Models.SGA.File6)?.Size ?? 0;
|
||||
break;
|
||||
case 7:
|
||||
fileSize = (file as SabreTools.Models.SGA.File7)?.SizeOnDisk ?? 0;
|
||||
outputFileSize = (file as SabreTools.Models.SGA.File7)?.Size ?? 0;
|
||||
break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
// Read the compressed data directly
|
||||
var compressedData = item.ReadFromDataSource((int)fileOffset, (int)fileSize);
|
||||
if (compressedData == null)
|
||||
return false;
|
||||
|
||||
// If the compressed and uncompressed sizes match
|
||||
byte[] data;
|
||||
if (fileSize == outputFileSize)
|
||||
{
|
||||
data = compressedData;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Inflate the data into the buffer
|
||||
var zstream = new ZLib.z_stream_s();
|
||||
data = new byte[outputFileSize];
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* payloadPtr = compressedData)
|
||||
fixed (byte* dataPtr = data)
|
||||
{
|
||||
zstream.next_in = payloadPtr;
|
||||
zstream.avail_in = (uint)compressedData.Length;
|
||||
zstream.total_in = (uint)compressedData.Length;
|
||||
zstream.next_out = dataPtr;
|
||||
zstream.avail_out = (uint)data.Length;
|
||||
zstream.total_out = 0;
|
||||
|
||||
ZLib.inflateInit_(zstream, ZLib.zlibVersion(), compressedData.Length);
|
||||
int zret = ZLib.inflate(zstream, 1);
|
||||
ZLib.inflateEnd(zstream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using Stream fs = File.OpenWrite(filename);
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
87
BinaryObjectScanner/FileType/SevenZip.cs
Normal file
87
BinaryObjectScanner/FileType/SevenZip.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.SevenZip;
|
||||
using SharpCompress.Readers;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// 7-zip archive
|
||||
/// </summary>
|
||||
public class SevenZip : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
=> Extract(file, outDir, lookForHeader: false, includeDebug);
|
||||
|
||||
/// <inheritdoc cref="IExtractable.Extract(string, string, bool)"/>
|
||||
public bool Extract(string file, string outDir, bool lookForHeader, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, lookForHeader, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
=> Extract(stream, file, outDir, lookForHeader: false, includeDebug);
|
||||
|
||||
/// <inheritdoc cref="IExtractable.Extract(Stream?, string, string, bool)"/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool lookForHeader, bool includeDebug)
|
||||
{
|
||||
if (stream == null || !stream.CanRead)
|
||||
return false;
|
||||
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
try
|
||||
{
|
||||
var readerOptions = new ReaderOptions() { LookForHeader = lookForHeader };
|
||||
using var sevenZip = SevenZipArchive.Open(stream, readerOptions);
|
||||
foreach (var entry in sevenZip.Entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If the entry is a directory
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
// If the entry has an invalid key
|
||||
if (entry.Key == null)
|
||||
continue;
|
||||
|
||||
// If we have a partial entry due to an incomplete multi-part archive, skip it
|
||||
if (!entry.IsComplete)
|
||||
continue;
|
||||
|
||||
string tempFile = Path.Combine(outDir, entry.Key);
|
||||
var directoryName = Path.GetDirectoryName(tempFile);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
77
BinaryObjectScanner/FileType/TapeArchive.cs
Normal file
77
BinaryObjectScanner/FileType/TapeArchive.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Tar;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Tape archive
|
||||
/// </summary>
|
||||
public class TapeArchive : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (stream == null || !stream.CanRead)
|
||||
return false;
|
||||
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
try
|
||||
{
|
||||
using var tarFile = TarArchive.Open(stream);
|
||||
foreach (var entry in tarFile.Entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If the entry is a directory
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
// If the entry has an invalid key
|
||||
if (entry.Key == null)
|
||||
continue;
|
||||
|
||||
// If we have a partial entry due to an incomplete multi-part archive, skip it
|
||||
if (!entry.IsComplete)
|
||||
continue;
|
||||
|
||||
string tempFile = Path.Combine(outDir, entry.Key);
|
||||
var directoryName = Path.GetDirectoryName(tempFile);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
187
BinaryObjectScanner/FileType/Textfile.cs
Normal file
187
BinaryObjectScanner/FileType/Textfile.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Various generic textfile formats
|
||||
/// </summary>
|
||||
public class Textfile : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// Files can be protected in multiple ways
|
||||
var protections = new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
// Load the current file content
|
||||
var fileContent = string.Empty;
|
||||
#if NET20 || NET35 || NET40
|
||||
using (var sr = new StreamReader(stream, Encoding.Default, true, 1024 * 1024))
|
||||
#else
|
||||
using (var sr = new StreamReader(stream, Encoding.Default, true, 1024 * 1024, true))
|
||||
#endif
|
||||
{
|
||||
fileContent = sr.ReadToEnd();
|
||||
}
|
||||
|
||||
// AegiSoft License Manager
|
||||
// Found in "setup.ins" (Redump entry 73521/IA item "Nova_HoyleCasino99USA").
|
||||
if (fileContent.Contains("Failed to load the AegiSoft License Manager install program."))
|
||||
protections.Add("AegiSoft License Manager");
|
||||
|
||||
// CD-Key
|
||||
if (fileContent.Contains("a valid serial number is required"))
|
||||
protections.Add("CD-Key / Serial");
|
||||
else if (fileContent.Contains("serial number is located"))
|
||||
protections.Add("CD-Key / Serial");
|
||||
// Found in "Setup.Ins" ("Word Point 2002" in IA item "advantage-language-french-beginner-langmaster-2005").
|
||||
else if (fileContent.Contains("Please enter a valid registration number"))
|
||||
protections.Add("CD-Key / Serial");
|
||||
|
||||
// Channelware
|
||||
// Found in "README.TXT" in Redump entry 116358.
|
||||
if (fileContent.Contains("This application is a Channelware-activated product."))
|
||||
protections.Add("Channelware");
|
||||
// Found in "Swr.dat" in the "TOYSTORY" installation folder from Redump entry 12354.
|
||||
if (fileContent.Contains("cwsw.com/authts"))
|
||||
protections.Add("Channelware");
|
||||
|
||||
// CopyKiller
|
||||
// Found in "autorun.dat" in CopyKiller versions 3.62 and 3.64.
|
||||
if (fileContent.Contains("CopyKiller CD-Protection V3.6x"))
|
||||
protections.Add("CopyKiller V3.62-V3.64");
|
||||
// Found in "autorun.dat" in CopyKiller versions 3.99 and 3.99a.
|
||||
else if (fileContent.Contains("CopyKiller V4 CD / DVD-Protection"))
|
||||
protections.Add("CopyKiller V3.99+");
|
||||
// Found in "engine.wzc" in CopyKiller versions 3.62 and 3.64.
|
||||
else if (fileContent.Contains("CopyKiller V3.6x Protection Engine"))
|
||||
protections.Add("CopyKiller V3.62-V3.64");
|
||||
// Found in "engine.wzc" in CopyKiller versions 3.99 and 3.99a.
|
||||
else if (fileContent.Contains("CopyKiller V3.99x Protection Engine"))
|
||||
protections.Add("CopyKiller V3.99+");
|
||||
|
||||
// Freelock
|
||||
// Found in "FILE_ID.DIZ" distributed with Freelock.
|
||||
if (fileContent.Contains("FREELOCK 1.0"))
|
||||
protections.Add("Freelock 1.0");
|
||||
else if (fileContent.Contains("FREELOCK 1.2"))
|
||||
protections.Add("Freelock 1.2");
|
||||
else if (fileContent.Contains("FREELOCK 1.2a"))
|
||||
protections.Add("Freelock 1.2a");
|
||||
else if (fileContent.Contains("FREELOCK 1.3"))
|
||||
protections.Add("Freelock 1.3");
|
||||
else if (fileContent.Contains("FREELOCK"))
|
||||
protections.Add("Freelock");
|
||||
|
||||
// MediaCloQ
|
||||
if (fileContent.Contains("SunnComm MediaCloQ"))
|
||||
protections.Add("MediaCloQ");
|
||||
else if (fileContent.Contains("http://download.mediacloq.com/"))
|
||||
protections.Add("MediaCloQ");
|
||||
else if (fileContent.Contains("http://www.sunncomm.com/mediacloq/"))
|
||||
protections.Add("MediaCloQ");
|
||||
|
||||
// MediaMax
|
||||
if (fileContent.Contains("MediaMax technology"))
|
||||
protections.Add("MediaMax CD-3");
|
||||
else if (fileContent.Contains("exclusive Cd3 technology"))
|
||||
protections.Add("MediaMax CD-3");
|
||||
else if (fileContent.Contains("<PROTECTION-VENDOR>MediaMAX</PROTECTION-VENDOR>"))
|
||||
protections.Add("MediaMax CD-3");
|
||||
else if (fileContent.Contains("MediaMax(tm)"))
|
||||
protections.Add("MediaMax CD-3");
|
||||
|
||||
// phenoProtect
|
||||
// Found in Redump entry 84082.
|
||||
if (fileContent.Contains("phenoProtect"))
|
||||
protections.Add("phenoProtect");
|
||||
// Additional check to minimize overmatching.
|
||||
if (fileContent.Contains("InstallSHIELD Software Coporation"))
|
||||
// Found in Redump entry 102493.
|
||||
if (fileContent.Contains("COPYPROTECTION_FAILEDR"))
|
||||
protections.Add("phenoProtect");
|
||||
|
||||
// Rainbow Sentinel
|
||||
// Found in "SENTW95.HLP" and "SENTINEL.HLP" in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]".
|
||||
if (fileContent.Contains("Rainbow Sentinel Driver Help"))
|
||||
protections.Add("Rainbow Sentinel");
|
||||
// Found in "\disc4\cad\sdcc_200.zip\DISK1\_USER1.HDR\Language_Independent_Intel_32_Files\SNTNLUSB.INF" in "CICA 32 For Windows CD-ROM (Walnut Creek) (October 1999) (Disc 4).iso" in IA item "CICA_32_For_Windows_CD-ROM_Walnut_Creek_October_1999".
|
||||
if (fileContent.Contains("SNTNLUSB.SvcDesc=\"Rainbow Security Device\""))
|
||||
protections.Add("Rainbow Sentinel USB Driver");
|
||||
if (fileContent.Contains("SntUsb95.SvcDesc=\"Rainbow Security Device\""))
|
||||
protections.Add("Rainbow Sentinel USB Driver");
|
||||
|
||||
// Found in "OEMSETUP.INF" in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]".
|
||||
if (fileContent.Contains("Sentinel Driver Disk"))
|
||||
protections.Add("Rainbow Sentinel");
|
||||
|
||||
// SafeCast
|
||||
// Found in "AdlmLog.xml" in IA item game-programming-in-c-start-to-finish-2006 after installing "3dsMax8_Demo.zip".
|
||||
if (fileContent.Contains("<NAME>SAFECAST</NAME>"))
|
||||
protections.Add("SafeCast");
|
||||
|
||||
// SafeDisc
|
||||
// TODO: Add better version parsing.
|
||||
// Found in "Info.plist" in Redump entries 23983, 42762, 72713, 73070, and 89603.
|
||||
if (fileContent.Contains("<string>com.europevisionmacro.SafeDiscDVD</string>"))
|
||||
{
|
||||
if (fileContent.Contains("<string>2.90.032</string>"))
|
||||
protections.Add("SafeDiscDVD for Macintosh 2.90.032");
|
||||
else
|
||||
protections.Add("SafeDiscDVD for Macintosh (Unknown Version - Please report to us on GitHub)");
|
||||
}
|
||||
|
||||
// Found in "Info.plist" in Redump entry 89649.
|
||||
if (fileContent.Contains("<string>com.macrovisioneurope.SafeDiscLT</string>"))
|
||||
{
|
||||
// TODO: Investigate why "CFBundleGetInfoString" and "CFBundleShortVersionString" say version 2.70.020, but "CFBundleVersion" says version 2.70.010.
|
||||
if (fileContent.Contains("<string>2.70.020</string"))
|
||||
protections.Add("SafeDiscLT for Macintosh 2.70.020");
|
||||
else
|
||||
protections.Add("SafeDiscLT for Macintosh (Unknown Version - Please report to us on GitHub)");
|
||||
}
|
||||
|
||||
|
||||
// The full line from a sample is as follows:
|
||||
//
|
||||
// The files securom_v7_01.dat and securom_v7_01.bak have been created during the installation of a SecuROM protected application.
|
||||
//
|
||||
// TODO: Use the filenames in this line to get the version out of it
|
||||
|
||||
// SecuROM
|
||||
if (fileContent.Contains("SecuROM protected application"))
|
||||
protections.Add("SecuROM");
|
||||
|
||||
// Steam
|
||||
if (fileContent.Contains("All use of the Program is governed by the terms of the Steam Agreement as described below."))
|
||||
protections.Add("Steam");
|
||||
|
||||
// XCP
|
||||
if (fileContent.Contains("http://cp.sonybmg.com/xcp/"))
|
||||
protections.Add("XCP");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return string.Join(";", [.. protections]);
|
||||
}
|
||||
}
|
||||
}
|
||||
133
BinaryObjectScanner/FileType/VBSP.cs
Normal file
133
BinaryObjectScanner/FileType/VBSP.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life 2 Level
|
||||
/// </summary>
|
||||
public class VBSP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var vbsp = SabreTools.Serialization.Wrappers.VBSP.Create(stream);
|
||||
if (vbsp == null)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files
|
||||
Directory.CreateDirectory(outDir);
|
||||
ExtractAllLumps(vbsp, outDir);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all lumps from the VBSP to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all lumps extracted, false otherwise</returns>
|
||||
public static bool ExtractAllLumps(SabreTools.Serialization.Wrappers.VBSP item, string outputDirectory)
|
||||
{
|
||||
// If we have no lumps
|
||||
if (item.Model.Header?.Lumps == null || item.Model.Header.Lumps.Length == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all lumps to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < item.Model.Header.Lumps.Length; i++)
|
||||
{
|
||||
allExtracted &= ExtractLump(item, i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a lump from the VBSP to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">Lump index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the lump extracted, false otherwise</returns>
|
||||
public static bool ExtractLump(SabreTools.Serialization.Wrappers.VBSP item, int index, string outputDirectory)
|
||||
{
|
||||
// If we have no lumps
|
||||
if (item.Model.Header?.Lumps == null || item.Model.Header.Lumps.Length == 0)
|
||||
return false;
|
||||
|
||||
// If the lumps index is invalid
|
||||
if (index < 0 || index >= item.Model.Header.Lumps.Length)
|
||||
return false;
|
||||
|
||||
// Get the lump
|
||||
var lump = item.Model.Header.Lumps[index];
|
||||
if (lump == null)
|
||||
return false;
|
||||
|
||||
// Read the data
|
||||
var data = item.ReadFromDataSource((int)lump.Offset, (int)lump.Length);
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// Create the filename
|
||||
string filename = $"lump_{index}.bin";
|
||||
switch (index)
|
||||
{
|
||||
case SabreTools.Models.VBSP.Constants.HL_VBSP_LUMP_ENTITIES:
|
||||
filename = "entities.ent";
|
||||
break;
|
||||
case SabreTools.Models.VBSP.Constants.HL_VBSP_LUMP_PAKFILE:
|
||||
filename = "pakfile.zip";
|
||||
break;
|
||||
}
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
182
BinaryObjectScanner/FileType/VPK.cs
Normal file
182
BinaryObjectScanner/FileType/VPK.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Valve Package File
|
||||
/// </summary>
|
||||
public class VPK : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var vpk = SabreTools.Serialization.Wrappers.VPK.Create(stream);
|
||||
if (vpk == null)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files
|
||||
Directory.CreateDirectory(outDir);
|
||||
ExtractAll(vpk, outDir);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from the VPK to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all files extracted, false otherwise</returns>
|
||||
public static bool ExtractAll(SabreTools.Serialization.Wrappers.VPK item, string outputDirectory)
|
||||
{
|
||||
// If we have no directory items
|
||||
if (item.Model.DirectoryItems == null || item.Model.DirectoryItems.Length == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < item.Model.DirectoryItems.Length; i++)
|
||||
{
|
||||
allExtracted &= ExtractFile(item, i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a file from the VPK to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">File index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the file extracted, false otherwise</returns>
|
||||
public static bool ExtractFile(SabreTools.Serialization.Wrappers.VPK item, int index, string outputDirectory)
|
||||
{
|
||||
// If we have no directory items
|
||||
if (item.Model.DirectoryItems == null || item.Model.DirectoryItems.Length == 0)
|
||||
return false;
|
||||
|
||||
// If the directory item index is invalid
|
||||
if (index < 0 || index >= item.Model.DirectoryItems.Length)
|
||||
return false;
|
||||
|
||||
// Get the directory item
|
||||
var directoryItem = item.Model.DirectoryItems[index];
|
||||
if (directoryItem?.DirectoryEntry == null)
|
||||
return false;
|
||||
|
||||
// If we have an item with no archive
|
||||
var data = new byte[0];
|
||||
if (directoryItem.DirectoryEntry.ArchiveIndex == SabreTools.Models.VPK.Constants.HL_VPK_NO_ARCHIVE)
|
||||
{
|
||||
if (directoryItem.PreloadData == null)
|
||||
return false;
|
||||
|
||||
data = directoryItem.PreloadData;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we have invalid archives
|
||||
if (item.ArchiveFilenames == null || item.ArchiveFilenames.Length == 0)
|
||||
return false;
|
||||
|
||||
// If we have an invalid index
|
||||
if (directoryItem.DirectoryEntry.ArchiveIndex < 0 || directoryItem.DirectoryEntry.ArchiveIndex >= item.ArchiveFilenames.Length)
|
||||
return false;
|
||||
|
||||
// Get the archive filename
|
||||
string archiveFileName = item.ArchiveFilenames[directoryItem.DirectoryEntry.ArchiveIndex];
|
||||
if (string.IsNullOrEmpty(archiveFileName))
|
||||
return false;
|
||||
|
||||
// If the archive doesn't exist
|
||||
if (!File.Exists(archiveFileName))
|
||||
return false;
|
||||
|
||||
// Try to open the archive
|
||||
var archiveStream = default(Stream);
|
||||
try
|
||||
{
|
||||
// Open the archive
|
||||
archiveStream = File.Open(archiveFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
// Seek to the data
|
||||
archiveStream.Seek(directoryItem.DirectoryEntry.EntryOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the directory item bytes
|
||||
data = archiveStream.ReadBytes((int)directoryItem.DirectoryEntry.EntryLength);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
archiveStream?.Close();
|
||||
}
|
||||
|
||||
// If we have preload data, prepend it
|
||||
if (data != null && directoryItem.PreloadData != null)
|
||||
data = directoryItem.PreloadData.Concat(data).ToArray();
|
||||
}
|
||||
|
||||
// If there is nothing to write out
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// Create the filename
|
||||
string filename = $"{directoryItem.Name}.{directoryItem.Extension}";
|
||||
if (!string.IsNullOrEmpty(directoryItem.Path))
|
||||
filename = Path.Combine(directoryItem.Path, filename);
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
124
BinaryObjectScanner/FileType/WAD.cs
Normal file
124
BinaryObjectScanner/FileType/WAD.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life Texture Package File
|
||||
/// </summary>
|
||||
public class WAD : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var wad = SabreTools.Serialization.Wrappers.WAD.Create(stream);
|
||||
if (wad == null)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files
|
||||
Directory.CreateDirectory(outDir);
|
||||
ExtractAllLumps(wad, outDir);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all lumps from the WAD to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all lumps extracted, false otherwise</returns>
|
||||
public static bool ExtractAllLumps(SabreTools.Serialization.Wrappers.WAD item, string outputDirectory)
|
||||
{
|
||||
// If we have no lumps
|
||||
if (item.Model.Lumps == null || item.Model.Lumps.Length == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all lumps to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < item.Model.Lumps.Length; i++)
|
||||
{
|
||||
allExtracted &= ExtractLump(item, i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a lump from the WAD to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">Lump index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the lump extracted, false otherwise</returns>
|
||||
public static bool ExtractLump(SabreTools.Serialization.Wrappers.WAD item, int index, string outputDirectory)
|
||||
{
|
||||
// If we have no lumps
|
||||
if (item.Model.Lumps == null || item.Model.Lumps.Length == 0)
|
||||
return false;
|
||||
|
||||
// If the lumps index is invalid
|
||||
if (index < 0 || index >= item.Model.Lumps.Length)
|
||||
return false;
|
||||
|
||||
// Get the lump
|
||||
var lump = item.Model.Lumps[index];
|
||||
if (lump == null)
|
||||
return false;
|
||||
|
||||
// Read the data -- TODO: Handle uncompressed lumps (see BSP.ExtractTexture)
|
||||
var data = item.ReadFromDataSource((int)lump.Offset, (int)lump.Length);
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// Create the filename
|
||||
string filename = $"{lump.Name}.lmp";
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
BinaryObjectScanner/FileType/XZ.cs
Normal file
54
BinaryObjectScanner/FileType/XZ.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Compressors.Xz;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// xz archive
|
||||
/// </summary>
|
||||
public class XZ : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
try
|
||||
{
|
||||
// Try opening the stream
|
||||
using var xzFile = new XZStream(stream);
|
||||
|
||||
// Create the output file path
|
||||
Directory.CreateDirectory(outDir);
|
||||
string tempFile = Path.Combine(outDir, Guid.NewGuid().ToString());
|
||||
|
||||
// Extract the file
|
||||
using FileStream fs = File.OpenWrite(tempFile);
|
||||
xzFile.CopyTo(fs);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
134
BinaryObjectScanner/FileType/XZP.cs
Normal file
134
BinaryObjectScanner/FileType/XZP.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// XBox Package File
|
||||
/// </summary>
|
||||
public class XZP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, outDir, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(Stream? stream, string file, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var xzp = SabreTools.Serialization.Wrappers.XZP.Create(stream);
|
||||
if (xzp == null)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files
|
||||
Directory.CreateDirectory(outDir);
|
||||
ExtractAll(xzp, outDir);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from the XZP to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all files extracted, false otherwise</returns>
|
||||
public static bool ExtractAll(SabreTools.Serialization.Wrappers.XZP item, string outputDirectory)
|
||||
{
|
||||
// If we have no directory entries
|
||||
if (item.Model.DirectoryEntries == null || item.Model.DirectoryEntries.Length == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all files to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < item.Model.DirectoryEntries.Length; i++)
|
||||
{
|
||||
allExtracted &= ExtractFile(item, i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a file from the XZP to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">File index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the file extracted, false otherwise</returns>
|
||||
public static bool ExtractFile(SabreTools.Serialization.Wrappers.XZP item, int index, string outputDirectory)
|
||||
{
|
||||
// If we have no directory entries
|
||||
if (item.Model.DirectoryEntries == null || item.Model.DirectoryEntries.Length == 0)
|
||||
return false;
|
||||
|
||||
// If we have no directory items
|
||||
if (item.Model.DirectoryItems == null || item.Model.DirectoryItems.Length == 0)
|
||||
return false;
|
||||
|
||||
// If the directory entry index is invalid
|
||||
if (index < 0 || index >= item.Model.DirectoryEntries.Length)
|
||||
return false;
|
||||
|
||||
// Get the directory entry
|
||||
var directoryEntry = item.Model.DirectoryEntries[index];
|
||||
if (directoryEntry == null)
|
||||
return false;
|
||||
|
||||
// Get the associated directory item
|
||||
var directoryItem = item.Model.DirectoryItems.Where(di => di?.FileNameCRC == directoryEntry.FileNameCRC).FirstOrDefault();
|
||||
if (directoryItem == null)
|
||||
return false;
|
||||
|
||||
// Load the item data
|
||||
var data = item.ReadFromDataSource((int)directoryEntry.EntryOffset, (int)directoryEntry.EntryLength);
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// Create the filename
|
||||
var filename = directoryItem.Name;
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename ?? $"file{index}");
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
BinaryObjectScanner/GameEngine/RenderWare.cs
Normal file
40
BinaryObjectScanner/GameEngine/RenderWare.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.GameEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// RenderWare (https://web.archive.org/web/20070214132346/http://www.renderware.com/) is an API and graphics engine created by Criterion in 1993.
|
||||
/// It appears that version 4.X was exclusively used by EA internally, with version 3.X being the final public version (https://sigmaco.org/3782-renderware/).
|
||||
/// It was available to use on many different platforms, with it being particularly useful for the PS2 (https://en.wikipedia.org/wiki/RenderWare).
|
||||
///
|
||||
/// Additional resources and documentation:
|
||||
/// RenderWare interview: https://web.archive.org/web/20031208124348/http://www.homelanfed.com/index.php?id=9856
|
||||
/// RenderWare V2.1 API reference: http://www.tnlc.com/rw/api/rwdoc.htm
|
||||
/// RenderWare 2 official docs: https://github.com/electronicarts/RenderWare3Docs
|
||||
/// RenderWare 3.7 SDK: https://github.com/sigmaco/rwsdk-v37-pc
|
||||
/// Wikipedia list of RenderWare games: https://en.wikipedia.org/wiki/Category:RenderWare_games
|
||||
/// </summary>
|
||||
public class RenderWare : IExecutableCheck<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Found in Redump entries 20138, 55823, and 102493.
|
||||
bool rwcsegSection = pex.ContainsSection("_rwcseg", exact: true);
|
||||
// Found in Redump entry 20138.
|
||||
bool rwdsegSection = pex.ContainsSection("_rwdseg", exact: true);
|
||||
|
||||
// TODO: Check if this indicates a specific version, or if these sections are present in multiple.
|
||||
if (rwcsegSection || rwdsegSection)
|
||||
return "RenderWare";
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
namespace BurnOutSharp.Interfaces
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a generic file for protection
|
||||
/// </summary>
|
||||
internal interface IContentCheck
|
||||
public interface IContentCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a path for protections based on file contents
|
||||
@@ -12,6 +12,6 @@
|
||||
/// <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);
|
||||
string? CheckContents(string file, byte[] fileContent, bool includeDebug);
|
||||
}
|
||||
}
|
||||
28
BinaryObjectScanner/Interfaces/IDetectable.cs
Normal file
28
BinaryObjectScanner/Interfaces/IDetectable.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.IO;
|
||||
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Mark a file type as being able to be detected
|
||||
/// </summary>
|
||||
public interface IDetectable
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if a file is detected as this file type
|
||||
/// </summary>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Detected file or protection type, null on error</returns>
|
||||
/// <remarks>Ideally, this should just point to the other detect implementation.</remarks>
|
||||
string? Detect(string file, bool includeDebug);
|
||||
|
||||
/// <summary>
|
||||
/// Check if a stream is detected as this file type
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream representing the input file</param>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Detected file or protection type, null on error</returns>
|
||||
string? Detect(Stream stream, string file, bool includeDebug);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
using BurnOutSharp.ExecutableType.Microsoft.NE;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BurnOutSharp.Interfaces
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a New Executable (NE) for protection
|
||||
/// Check an executable for protection
|
||||
/// </summary>
|
||||
internal interface INewExecutableCheck
|
||||
public interface IExecutableCheck<T> where T : WrapperBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a path for protections based on file contents
|
||||
/// </summary>
|
||||
/// <param name="file">File to check for protection indicators</param>
|
||||
/// <param name="nex">NewExecutable representing the read-in file</param>
|
||||
/// <param name="exe">Executable representing the read-in file</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>String containing any protections found in the file</returns>
|
||||
string CheckNewExecutable(string file, NewExecutable nex, bool includeDebug);
|
||||
string? CheckExecutable(string file, T exe, bool includeDebug);
|
||||
}
|
||||
}
|
||||
30
BinaryObjectScanner/Interfaces/IExtractable.cs
Normal file
30
BinaryObjectScanner/Interfaces/IExtractable.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System.IO;
|
||||
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Mark a file type as being able to be extracted
|
||||
/// </summary>
|
||||
public interface IExtractable
|
||||
{
|
||||
/// <summary>
|
||||
/// Extract a file to a temporary path, if possible
|
||||
/// </summary>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <param name="outDir">Path to the output directory</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Indicates if the extractable was successfully extracted</returns>
|
||||
/// <remarks>Ideally, this should just point to the other extract implementation.</remarks>
|
||||
bool Extract(string file, string outDir, bool includeDebug);
|
||||
|
||||
/// <summary>
|
||||
/// Extract a stream to a temporary path, if possible
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream representing the input file</param>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <param name="outDir">Path to the output directory</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Indicates if the extractable was successfully extracted</returns>
|
||||
bool Extract(Stream? stream, string file, string outDir, bool includeDebug);
|
||||
}
|
||||
}
|
||||
20
BinaryObjectScanner/Interfaces/IExtractableExecutable.cs
Normal file
20
BinaryObjectScanner/Interfaces/IExtractableExecutable.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Mark an executable type as being able to be extracted
|
||||
/// </summary>
|
||||
public interface IExtractableExecutable<T> : IExecutableCheck<T> where T : WrapperBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Extract an Executable to a path, if possible
|
||||
/// </summary>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <param name="exe">Executable representing the read-in file</param>
|
||||
/// <param name="outDir">Path to the output directory</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Path to extracted files, null on error</returns>
|
||||
bool Extract(string file, T exe, string outDir, bool includeDebug);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BurnOutSharp.Interfaces
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a file or directory path for protection
|
||||
@@ -9,7 +8,7 @@ namespace BurnOutSharp.Interfaces
|
||||
/// <remarks>
|
||||
/// These checks rely primarily on filenames and paths, not file contents
|
||||
/// </remarks>
|
||||
internal interface IPathCheck
|
||||
public interface IPathCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a file path for protections based on path name
|
||||
@@ -17,13 +16,13 @@ namespace BurnOutSharp.Interfaces
|
||||
/// <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 a content check instead, if possible</remarks>
|
||||
ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files);
|
||||
IEnumerable<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 a content check instead, if possible</remarks>
|
||||
string CheckFilePath(string path);
|
||||
string? CheckFilePath(string path);
|
||||
}
|
||||
}
|
||||
38
BinaryObjectScanner/Options.cs
Normal file
38
BinaryObjectScanner/Options.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
namespace BinaryObjectScanner
|
||||
{
|
||||
/// <summary>
|
||||
/// Scanning options
|
||||
/// </summary>
|
||||
public class Options
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether archives are decompressed and scanned
|
||||
/// </summary>
|
||||
public bool ScanArchives { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if content matches are used or not
|
||||
/// </summary>
|
||||
public bool ScanContents { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if game engines are counted as detected protections or not
|
||||
/// </summary>
|
||||
public bool ScanGameEngines { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if packers are counted as detected protections or not
|
||||
/// </summary>
|
||||
public bool ScanPackers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if path matches are used or not
|
||||
/// </summary>
|
||||
public bool ScanPaths { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if debug information is output or not
|
||||
/// </summary>
|
||||
public bool IncludeDebug { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,20 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.PE;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BurnOutSharp.Matching;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Content;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BurnOutSharp.PackerType
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction
|
||||
public class ASPack : IPortableExecutableCheck, IScannable
|
||||
public class ASPack : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic) => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex?.SectionTable;
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
@@ -26,25 +23,26 @@ namespace BurnOutSharp.PackerType
|
||||
if (aspackSection)
|
||||
return "ASPack 2.29";
|
||||
|
||||
// TODO: Re-enable all Entry Point checks after implementing
|
||||
// Use the entry point data, if it exists
|
||||
if (pex.EntryPointRaw != null)
|
||||
{
|
||||
var matchers = GenerateMatchers();
|
||||
string match = MatchUtil.GetFirstMatch(file, pex.EntryPointRaw, matchers, includeDebug);
|
||||
if (!string.IsNullOrWhiteSpace(match))
|
||||
return match;
|
||||
}
|
||||
// if (pex.EntryPointRaw != null)
|
||||
// {
|
||||
// var matchers = GenerateMatchers();
|
||||
// var match = MatchUtil.GetFirstMatch(file, pex.EntryPointRaw, matchers, includeDebug);
|
||||
// if (!string.IsNullOrEmpty(match))
|
||||
// return match;
|
||||
// }
|
||||
|
||||
// Get the .adata* section, if it exists
|
||||
var adataSection = pex.GetFirstSection(".adata", exact: false);
|
||||
if (adataSection != null)
|
||||
if (adataSection?.Name != null)
|
||||
{
|
||||
var adataSectionRaw = pex.ReadRawSection(adataSection.NameString);
|
||||
var adataSectionRaw = pex.GetFirstSectionData(Encoding.UTF8.GetString(adataSection.Name));
|
||||
if (adataSectionRaw != null)
|
||||
{
|
||||
var matchers = GenerateMatchers();
|
||||
string match = MatchUtil.GetFirstMatch(file, adataSectionRaw, matchers, includeDebug);
|
||||
if (!string.IsNullOrWhiteSpace(match))
|
||||
var match = MatchUtil.GetFirstMatch(file, adataSectionRaw, matchers, includeDebug);
|
||||
if (!string.IsNullOrEmpty(match))
|
||||
return match;
|
||||
}
|
||||
}
|
||||
@@ -53,21 +51,9 @@ namespace BurnOutSharp.PackerType
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -80,7 +66,7 @@ namespace BurnOutSharp.PackerType
|
||||
{
|
||||
#region No Wildcards (Long)
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x92, 0x1A, 0x44, 0x00, 0xB8, 0x8C, 0x1A,
|
||||
@@ -89,7 +75,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x80, 0xBD, 0xC4, 0x1D, 0x44,
|
||||
}, "ASPack 1.00b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0xD2, 0x2A, 0x44, 0x00, 0xB8, 0xCC, 0x2A,
|
||||
@@ -103,7 +89,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x44, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
|
||||
}, "ASPack 1.01b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0xD2, 0x2A, 0x44, 0x00, 0xB8, 0xCC, 0x2A,
|
||||
@@ -112,7 +98,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x80, 0xBD, 0x9C, 0x2E, 0x44
|
||||
}, "ASPack 1.01b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x96, 0x78, 0x43, 0x00, 0xB8, 0x90, 0x78,
|
||||
@@ -126,7 +112,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x43, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
|
||||
}, "ASPack 1.02b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x96, 0x78, 0x43, 0x00, 0xB8, 0x90, 0x78,
|
||||
@@ -136,7 +122,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x15, 0xFE, 0x85, 0x74, 0x7C, 0x43
|
||||
}, "ASPack 1.02b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x8A, 0x1C, 0x40, 0x00, 0xB9, 0x9E, 0x00,
|
||||
@@ -144,14 +130,14 @@ namespace BurnOutSharp.PackerType
|
||||
0x8B, 0xF7, 0x33
|
||||
}, "ASPack 1.02b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x96, 0x78, 0x43, 0x00, 0xB8, 0x90, 0x78,
|
||||
0x43, 0x00, 0x03, 0xC5
|
||||
}, "ASPack 1.02b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0xAE, 0x98, 0x43, 0x00, 0xB8, 0xA8, 0x98,
|
||||
@@ -160,7 +146,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x80, 0xBD, 0x0E, 0x9D, 0x43
|
||||
}, "ASPack 1.03b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0xCE, 0x3A, 0x44, 0x00, 0xB8, 0xC8, 0x3A,
|
||||
@@ -169,7 +155,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x80, 0xBD, 0xAC, 0x3E, 0x44
|
||||
}, "ASPack 1.05b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0xEA, 0xA8, 0x43, 0x00, 0xB8, 0xE4, 0xA8,
|
||||
@@ -183,7 +169,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x43, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
|
||||
}, "ASPack 1.06.01b (DLL)"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0xEA, 0xA8, 0x43, 0x00, 0xB8, 0xE4, 0xA8,
|
||||
@@ -193,7 +179,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x15, 0xFE, 0x85, 0x6E, 0xAD, 0x43
|
||||
}, "ASPack 1.06.01b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x3E, 0xD9, 0x43, 0x00, 0xB8, 0x38, 0xD9,
|
||||
@@ -207,7 +193,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x43, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
|
||||
}, "ASPack 1.07b (DLL)"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xEB, 0x03, 0x5D, 0xFF, 0xE5, 0xE8, 0xF8,
|
||||
0xFF, 0xFF, 0xFF, 0x81, 0xED, 0x1B, 0x6A, 0x44,
|
||||
@@ -215,7 +201,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x2B, 0x9D, 0x2A
|
||||
}, "ASPack 1.08"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x0A, 0x4A, 0x44, 0x00, 0xBB, 0x04, 0x4A,
|
||||
@@ -229,7 +215,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x53, 0x50, 0xFF, 0x95, 0x90, 0x51, 0x44, 0x00
|
||||
}, "ASPack 1.08.03"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x0A, 0x4A, 0x44, 0x00, 0xBB, 0x04, 0x4A,
|
||||
@@ -238,20 +224,20 @@ namespace BurnOutSharp.PackerType
|
||||
0x00, 0x89, 0x9D, 0xBB, 0x4E
|
||||
}, "ASPack 1.08.03"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x0A, 0x4A, 0x44, 0x00, 0xBB, 0x04, 0x4A,
|
||||
0x44, 0x00, 0x03, 0xDD
|
||||
}, "ASPack 1.08.03"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x72, 0x05, 0x00, 0x00, 0xEB, 0x33,
|
||||
0x87, 0xDB, 0x90, 0x00
|
||||
}, "ASPack 2.00.01"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9, 0xEB,
|
||||
0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01, 0x00,
|
||||
@@ -259,34 +245,34 @@ namespace BurnOutSharp.PackerType
|
||||
0xFF, 0x03, 0xDD, 0x81, 0xEB
|
||||
}, "ASPack 2.1"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x02, 0x00, 0x00, 0x00, 0xEB, 0x09,
|
||||
0x5D, 0x55, 0x81, 0xED, 0x39, 0x39, 0x44, 0x00,
|
||||
0xC3, 0xE9, 0x3D, 0x04, 0x00, 0x00
|
||||
}, "ASPack 2.11b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x02, 0x00, 0x00, 0x00, 0xEB, 0x09,
|
||||
0x5D, 0x55
|
||||
}, "ASPack 2.11b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x02, 0x00, 0x00, 0x00, 0xEB, 0x09,
|
||||
0x5D, 0x55, 0x81, 0xED, 0x39, 0x39, 0x44, 0x00,
|
||||
0xC3, 0xE9, 0x59, 0x04, 0x00, 0x00
|
||||
}, "ASPack 2.11c"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x02, 0x00, 0x00, 0x00, 0xCD, 0x20,
|
||||
0xE8, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x2B, 0xC9,
|
||||
0x58, 0x74, 0x02
|
||||
}, "ASPack 2.11d"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9, 0xEB,
|
||||
0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01
|
||||
@@ -296,7 +282,7 @@ namespace BurnOutSharp.PackerType
|
||||
|
||||
#region Wildcards (Long)
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, 0x3E, 0xD9, 0x43, null, 0xB8, 0x38, null,
|
||||
@@ -310,7 +296,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x43, null, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
|
||||
}, "ASPack 1.00b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, 0xD2, 0x2A, 0x44, null, 0xB8, 0xCC, 0x2A,
|
||||
@@ -319,7 +305,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x80, 0xBD, 0x9C, 0x2E, 0x44
|
||||
}, "ASPack 1.01b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, 0xCE, 0x3A, 0x44, null, 0xB8, 0xC8, 0x3A,
|
||||
@@ -328,7 +314,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x80, 0xBD, 0xAC, 0x3E, 0x44
|
||||
}, "ASPack 1.01b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x3E, 0xD9, 0x43, 0x00, 0xB8, 0x38, null,
|
||||
@@ -342,13 +328,13 @@ namespace BurnOutSharp.PackerType
|
||||
0x43, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
|
||||
}, "ASPack 1.02a -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, 0x06, null, null, null, 0x64, 0xA0, 0x23
|
||||
}, "ASPack 1.02a"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, 0x96, 0x78, 0x43, null, 0xB8, 0x90, 0x78,
|
||||
@@ -357,7 +343,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x80, 0xBD, 0x74, 0x7C, 0x43
|
||||
}, "ASPack 1.02b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, 0xAE, 0x98, 0x43, null, 0xB8, 0xA8, 0x98,
|
||||
@@ -366,7 +352,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x80, 0xBD, 0x0E, 0x9D, 0x43
|
||||
}, "ASPack 1.03b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, null, null, null, null, 0xE8, 0x0D, null,
|
||||
@@ -374,7 +360,7 @@ namespace BurnOutSharp.PackerType
|
||||
null, null, null, null, null, null, null, 0x58
|
||||
}, "ASPack 1.03b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, null, null, null, 0x00, 0xB8, null, null,
|
||||
@@ -383,7 +369,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x80, 0xBD, 0x08, 0x9D, null, 0x00, 0x00
|
||||
}, "ASPack 1.04b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, null, null, null, null, 0xB8, null, null,
|
||||
@@ -392,7 +378,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x80, 0xBD, 0x08, 0x9D
|
||||
}, "ASPack 1.04b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, null, null, null, null, 0xB8, null, null,
|
||||
@@ -401,7 +387,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x80, 0xBD, 0x01, 0xDE
|
||||
}, "ASPack 1.04b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, 0xEA, 0xA8, 0x43, null, 0xB8, 0xE4, 0xA8,
|
||||
@@ -410,28 +396,28 @@ namespace BurnOutSharp.PackerType
|
||||
0x80, 0xBD, 0x6E, 0xAD, 0x43
|
||||
}, "ASPack 1.06.1b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x90, 0x61, 0xBE, null, null, null, null, 0x8D,
|
||||
0xBE, null, null, null, null, 0x57, 0x83, 0xCD,
|
||||
0xFF
|
||||
}, "ASPack 1.06.1b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, null,
|
||||
null, null, null, null, null, 0xB8, null, null,
|
||||
null, null, 0x03, 0xC5
|
||||
}, "ASPack 1.07b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, null, null, null, null, 0x60, 0xE8, 0x2B,
|
||||
0x03, 0x00, 0x00
|
||||
}, "ASPack 1.07b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xEB, 0x0A, 0x5D, 0xEB, 0x02, 0xFF, 0x25,
|
||||
0x45, 0xFF, 0xE5, 0xE8, 0xE9, 0xE8, 0xF1, 0xFF,
|
||||
@@ -440,7 +426,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x2B, 0x9D
|
||||
}, "ASPack 1.08.01"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xEB, 0x0A, 0x5D, 0xEB, 0x02, 0xFF, 0x25,
|
||||
0x45, 0xFF, 0xE5, 0xE8, 0xE9, 0xE8, 0xF1, 0xFF,
|
||||
@@ -449,7 +435,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x2B, 0x9D
|
||||
}, "ASPack 1.08.01"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xEB, 0x0A, 0x5D, 0xEB, 0x02, 0xFF, 0x25,
|
||||
0x45, 0xFF, 0xE5, 0xE8, 0xE9, 0xE8, 0xF1, 0xFF,
|
||||
@@ -458,7 +444,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x2B, 0x9D, 0x72
|
||||
}, "ASPack 1.08.02"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, null,
|
||||
null, null, null, null, null, 0xBB, null, null,
|
||||
@@ -467,14 +453,14 @@ namespace BurnOutSharp.PackerType
|
||||
0x00, 0x89, 0x9D, 0xBB, 0x4E
|
||||
}, "ASPack 1.08.03"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, null,
|
||||
null, null, null, null, null, 0xBB, null, null,
|
||||
null, null, 0x03, 0xDD
|
||||
}, "ASPack 1.08.03"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x55, 0x57, 0x51, 0x53, 0xE8, null, null, null,
|
||||
null, 0x5D, 0x8B, 0xC5, 0x81, 0xED, null, null,
|
||||
@@ -483,27 +469,27 @@ namespace BurnOutSharp.PackerType
|
||||
null, 0x0F, 0xB6
|
||||
}, "ASPack 1.08.03"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE9, null, null, null, null, 0xEF, 0x40,
|
||||
0x03, 0xA7, 0x07, 0x8F, 0x07, 0x1C, 0x37, 0x5D,
|
||||
0x43, 0xA7, 0x04, 0xB9, 0x2C, 0x3A
|
||||
}, "ASPack 1.08.x"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x02, 0x00, 0x00, 0x00, 0xEB, 0x09,
|
||||
0x5D, 0x55, 0x81, 0xED, 0x39, 0x39, 0x44, 0x00,
|
||||
0xC3, 0xE9, null, 0x04, 0x00, 0x00
|
||||
}, "ASPack 2.11.x -> Alexey Solodovnikov"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
null, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9, 0xEB,
|
||||
0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01
|
||||
}, "ASPack 2.12 (without Poly) -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
null, 0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9,
|
||||
0xEB, 0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01,
|
||||
@@ -520,7 +506,7 @@ namespace BurnOutSharp.PackerType
|
||||
0xFF, 0x95, 0x48, 0x0F
|
||||
}, "ASPack 2.12b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9, 0xEB,
|
||||
0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01, 0x00,
|
||||
@@ -569,7 +555,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x08
|
||||
}, "ASPack 2.2 -> Alexey Solodovnikov & StarForce * 2009408"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
null, 0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9,
|
||||
0xEB, 0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01,
|
||||
@@ -582,7 +568,7 @@ namespace BurnOutSharp.PackerType
|
||||
|
||||
#region 2.xx (Long)
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0xA8, 0x03, 0x00, 0x00, 0x61, 0x75, 0x08, 0xB8,
|
||||
0x01, 0x00, 0x00, 0x00, 0xC2, 0x0C, 0x00, 0x68,
|
||||
@@ -591,7 +577,7 @@ namespace BurnOutSharp.PackerType
|
||||
0x00, 0x51, 0x50, 0xFF, 0x95
|
||||
}, "ASPack 2.xx"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0xA8, 0x03, null, null, 0x61, 0x75, 0x08, 0xB8,
|
||||
0x01, null, null, null, 0xC2, 0x0C, null, 0x68,
|
||||
@@ -604,55 +590,55 @@ namespace BurnOutSharp.PackerType
|
||||
|
||||
#region Short
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x75, 0x00, 0xE9 }, "ASPack 1.05b"),
|
||||
new(new byte?[] { 0x75, 0x00, 0xE9 }, "ASPack 1.05b"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
|
||||
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
|
||||
new(new byte?[] { 0x90, 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
|
||||
new(new byte?[] { 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
|
||||
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
|
||||
new(new byte?[] { 0x90, 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
|
||||
new(new byte?[] { 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08"),
|
||||
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
|
||||
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
|
||||
new(new byte?[] { 0x90, 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
|
||||
new(new byte?[] { 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
|
||||
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
|
||||
new(new byte?[] { 0x90, 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
|
||||
new(new byte?[] { 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08.02"),
|
||||
new(new byte?[] { 0x90, 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08.02"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08.02"),
|
||||
new(new byte?[] { 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08.02"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x60, 0xE8, 0x41, 0x06, 0x00, 0x00, 0xEB, 0x41 }, "ASPack 1.08.04"),
|
||||
new(new byte?[] { 0x60, 0xE8, 0x41, 0x06, 0x00, 0x00, 0xEB, 0x41 }, "ASPack 1.08.04"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x60, 0xE8, null, null, null, null, 0xEB }, "ASPack 1.08.04"),
|
||||
new(new byte?[] { 0x60, 0xE8, null, null, null, null, 0xEB }, "ASPack 1.08.04"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x60, 0xE8, 0x70, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.00"),
|
||||
new(new byte?[] { 0x60, 0xE8, 0x70, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.00"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x60, 0xE8, 0x48, 0x11, 0x00, 0x00, 0xC3, 0x83 }, "ASPack 2.00.00"),
|
||||
new(new byte?[] { 0x60, 0xE8, 0x48, 0x11, 0x00, 0x00, 0xC3, 0x83 }, "ASPack 2.00.00"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x60, 0xE8, 0x72, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.01"),
|
||||
new(new byte?[] { 0x60, 0xE8, 0x72, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.01"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x60, 0xE8, null, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.x -> Alexey Solodovnikov"),
|
||||
new(new byte?[] { 0x60, 0xE8, null, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.x -> Alexey Solodovnikov"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x60, 0xE9, 0x3D, 0x04, 0x00, 0x00 }, "ASPack 2.11"),
|
||||
new(new byte?[] { 0x60, 0xE9, 0x3D, 0x04, 0x00, 0x00 }, "ASPack 2.11"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x60, 0xE8, 0xF9, 0x11, 0x00, 0x00, 0xC3, 0x83 }, "ASPack 2.11"),
|
||||
new(new byte?[] { 0x60, 0xE8, 0xF9, 0x11, 0x00, 0x00, 0xC3, 0x83 }, "ASPack 2.11"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81, 0xED }, "ASPack 1.02b/1.08.03"),
|
||||
new(new byte?[] { 0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81, 0xED }, "ASPack 1.02b/1.08.03"),
|
||||
|
||||
#endregion
|
||||
};
|
||||
36
BinaryObjectScanner/Packer/AdvancedInstaller.cs
Normal file
36
BinaryObjectScanner/Packer/AdvancedInstaller.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction
|
||||
// TODO: Verify that all versions are detected
|
||||
public class AdvancedInstaller : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .rdata section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".rdata");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("Software\\Caphyon\\Advanced Installer")))
|
||||
return "Caphyon Advanced Installer";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.PE;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BurnOutSharp.Tools;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BurnOutSharp.PackerType
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// Created by IndigoRose (creators of Setup Factory), primarily to be used to create autorun menus for various media.
|
||||
// Official website: https://www.autoplay.org/
|
||||
// TODO: Add extraction
|
||||
public class AutoPlayMediaStudio : IPortableExecutableCheck, IScannable
|
||||
public class AutoPlayMediaStudio : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic) => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex?.SectionTable;
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Known to detect versions 5.0.0.3 - 8.1.0.0
|
||||
string name = pex.ProductName;
|
||||
var name = pex.ProductName;
|
||||
if (name?.StartsWith("AutoPlay Media Studio", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"AutoPlay Media Studio {GetVersion(pex)}";
|
||||
|
||||
@@ -39,34 +33,22 @@ namespace BurnOutSharp.PackerType
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetVersion(PortableExecutable pex)
|
||||
{
|
||||
// Check the product version explicitly
|
||||
string version = pex.ProductVersion;
|
||||
var version = pex.ProductVersion;
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return version;
|
||||
return version!;
|
||||
|
||||
// Check the internal versions
|
||||
version = Utilities.GetInternalVersion(pex);
|
||||
version = pex.GetInternalVersion();
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return version;
|
||||
return version!;
|
||||
|
||||
return "(Unknown Version)";
|
||||
}
|
||||
150
BinaryObjectScanner/Packer/CExe.cs
Normal file
150
BinaryObjectScanner/Packer/CExe.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Compression.zlib;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Content;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// 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
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class CExe : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// If there are exactly 2 resources with type 99
|
||||
if (pex.FindResourceByNamedType("99, ").Count() == 2)
|
||||
return "CExe";
|
||||
|
||||
if (pex.StubExecutableData != null)
|
||||
{
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
new(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")
|
||||
};
|
||||
|
||||
var match = MatchUtil.GetFirstMatch(file, pex.StubExecutableData, matchers, includeDebug);
|
||||
if (!string.IsNullOrEmpty(match))
|
||||
return match;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the first resource of type 99 with index 2
|
||||
var payload = pex.FindResourceByNamedType("99, 2").FirstOrDefault();
|
||||
if (payload == null || payload.Length == 0)
|
||||
return false;
|
||||
|
||||
// Determine which compression was used
|
||||
bool zlib = pex.FindResourceByNamedType("99, 1").Any();
|
||||
|
||||
// Create the output data buffer
|
||||
var data = new byte[0];
|
||||
|
||||
// If we had the decompression DLL included, it's zlib
|
||||
if (zlib)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Inflate the data into the buffer
|
||||
var zstream = new ZLib.z_stream_s();
|
||||
data = new byte[payload.Length * 4];
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* payloadPtr = payload)
|
||||
fixed (byte* dataPtr = data)
|
||||
{
|
||||
zstream.next_in = payloadPtr;
|
||||
zstream.avail_in = (uint)payload.Length;
|
||||
zstream.total_in = (uint)payload.Length;
|
||||
zstream.next_out = dataPtr;
|
||||
zstream.avail_out = (uint)data.Length;
|
||||
zstream.total_out = 0;
|
||||
|
||||
ZLib.inflateInit_(zstream, ZLib.zlibVersion(), payload.Length);
|
||||
int zret = ZLib.inflate(zstream, 1);
|
||||
ZLib.inflateEnd(zstream);
|
||||
}
|
||||
}
|
||||
|
||||
// Trim the buffer to the proper size
|
||||
uint read = zstream.total_out;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
data = new ReadOnlySpan<byte>(data, 0, (int)read).ToArray();
|
||||
#else
|
||||
var temp = new byte[read];
|
||||
Array.Copy(data, 0, temp, 0, read);
|
||||
data = temp;
|
||||
#endif
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Reset the data
|
||||
data = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, LZ is used
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
data = SabreTools.Compression.LZ.Decompressor.Decompress(payload);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Reset the data
|
||||
data = null;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have no data
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// Create the temp filename
|
||||
string tempFile = string.IsNullOrEmpty(file) ? "temp.sxe" : $"{Path.GetFileNameWithoutExtension(file)}.sxe";
|
||||
tempFile = Path.Combine(outDir, tempFile);
|
||||
var directoryName = Path.GetDirectoryName(tempFile);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Write the file data to a temp file
|
||||
var tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
|
||||
tempStream.Write(data, 0, data.Length);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
35
BinaryObjectScanner/Packer/DotFuscator.cs
Normal file
35
BinaryObjectScanner/Packer/DotFuscator.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction
|
||||
public class DotFuscator : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .text section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".text");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("DotfuscatorAttribute")))
|
||||
return "dotFuscator";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
106
BinaryObjectScanner/Packer/DotNetReactor.cs
Normal file
106
BinaryObjectScanner/Packer/DotNetReactor.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Content;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
/// <summary>
|
||||
/// .NET Reactor is a .NET obfuscator that was original released in 2004. https://web.archive.org/web/20040828162124/http://eziriz.com:80/
|
||||
/// It is currently still being updated and supported. https://www.eziriz.com/dotnet_reactor.htm
|
||||
/// While ProtectionID does detect .NET Reactor, it's currently unknown exactly how.
|
||||
/// It seems to simply check for the string "<PrivateImplementationDetails>" in specific, and currently unknown, conditions but appears to be prone to false positives.
|
||||
/// A "Demo/Nag Screen" version is available for free, and may be able to be used to make samples to improve detections. https://www.eziriz.com/reactor_download.htm
|
||||
///
|
||||
/// Resource that could be useful for extraction: https://github.com/SychicBoy/NETReactorSlayer
|
||||
/// </summary>
|
||||
public class DotNetReactor : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// TODO: Detect version
|
||||
// TODO: Further refine checks using https://github.com/horsicq/Detect-It-Easy/blob/075a70b1484d1d84d1dc37c86aac16188d5a84e7/db/PE/NetReactor.2.sg and https://github.com/cod3nym/detection-rules/blob/main/yara/dotnet/obf_net_reactor.yar
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .text section, if it exists
|
||||
if (pex.ContainsSection(".text"))
|
||||
{
|
||||
var textData = pex.GetFirstSectionData(".text");
|
||||
if (textData != null)
|
||||
{
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
// Adapted from https://github.com/cod3nym/detection-rules/blob/main/yara/dotnet/obf_net_reactor.yar and confirmed to work with "KalypsoLauncher.dll" from Redump entry 95617.
|
||||
// <PrivateImplementationDetails>{[8]-[4]-[4]-[4]-[12]}
|
||||
new(new byte?[]
|
||||
{
|
||||
0x3C, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65,
|
||||
0x49, 0x6D, 0x70, 0x6C, 0x65, 0x6D, 0x65, 0x6E,
|
||||
0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x44, 0x65,
|
||||
0x74, 0x61, 0x69, 0x6C, 0x73, 0x3E, 0x7B, null,
|
||||
null, null, null, null, null, null, null, 0x2D,
|
||||
null, null, null, null, 0x2D, null, null, null,
|
||||
null, 0x2D, null, null, null, null, 0x2D, null,
|
||||
null, null, null, null, null, null, null, null,
|
||||
null, null, null, 0x7D
|
||||
}, ".NET Reactor"),
|
||||
|
||||
// Modified from the previous detection to detect a presumably newer version of .NET Reactor found in "KalypsoLauncher.dll" version 2.0.4.2.
|
||||
// TODO: Check if this can/should be made more specific.
|
||||
// <PrivateImplementationDetails>.RSA
|
||||
new(new byte?[]
|
||||
{
|
||||
0x3C, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65,
|
||||
0x49, 0x6D, 0x70, 0x6C, 0x65, 0x6D, 0x65, 0x6E,
|
||||
0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x44, 0x65,
|
||||
0x74, 0x61, 0x69, 0x6C, 0x73, 0x3E, 0x00, 0x52,
|
||||
0x53, 0x41
|
||||
}, ".NET Reactor"),
|
||||
|
||||
// Adapted from https://github.com/cod3nym/detection-rules/blob/main/yara/dotnet/obf_net_reactor.yar and confirmed to work with "KalypsoLauncher.dll" from Redump entry 95617.
|
||||
// 3{.[9].-.[9].-.[9].}
|
||||
new(new byte?[]
|
||||
{
|
||||
0x33, 0x7B, 0x00, null, null, null, null, null,
|
||||
null, null, null, null, 0x00, 0x2D, 0x00, null,
|
||||
null, null, null, null, null, null, null, null,
|
||||
0x00, 0x2D, 0x00, null, null, null, null, null,
|
||||
null, null, null, null, 0x00, 0x2D, 0x00, null,
|
||||
null, null, null, null, null, null, null, null,
|
||||
0x00, 0x7D, 0x00
|
||||
}, ".NET Reactor (Unconfirmed - Please report to us on GitHub)"),
|
||||
|
||||
// Adapted from https://github.com/cod3nym/detection-rules/blob/main/yara/dotnet/obf_net_reactor.yar and confirmed to work with "KalypsoLauncher.dll" from Redump entry 95617.
|
||||
// <Module>{[8]-[4]-[4]-[4]-[12]}
|
||||
new(new byte?[]
|
||||
{
|
||||
0x3C, 0x4D, 0x6F, 0x64, 0x75, 0x6C, 0x65, 0x3E,
|
||||
0x7B, null, null, null, null, null, null, null,
|
||||
null, 0x2D, null, null, null, null, 0x2D, null,
|
||||
null, null, null, 0x2D, null, null, null, null,
|
||||
0x2D, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, 0x7D
|
||||
}, ".NET Reactor (Unconfirmed - Please report to us on GitHub)")
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(file, textData, matchers, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
// TODO: Add extraction
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,19 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.PE;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BurnOutSharp.Matching;
|
||||
using System.Collections.Generic;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Content;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BurnOutSharp.PackerType
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Figure out how to more granularly determine versions like PiD,
|
||||
// at least for the 2.41 -> 2.75 range
|
||||
// TODO: Figure out how to more granularly determine versions like PiD
|
||||
// TODO: Detect 3.15 and up (maybe looking for `Metamorphism`)
|
||||
// TODO: Add extraction
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class EXEStealth : IContentCheck, IPortableExecutableCheck, IScannable
|
||||
public class EXEStealth : IContentCheck, IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic) => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
{
|
||||
// TODO: Obtain a sample to find where this string is in a typical executable
|
||||
if (includeDebug)
|
||||
@@ -26,7 +21,7 @@ namespace BurnOutSharp.PackerType
|
||||
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?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x3F, 0x3F, 0x5B, 0x5B, 0x5F, 0x5F, 0x5B, 0x5B,
|
||||
0x5F, 0x00, 0x7B, 0x7B, 0x00, 0x00, 0x7B, 0x7B,
|
||||
@@ -43,13 +38,24 @@ namespace BurnOutSharp.PackerType
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex?.SectionTable;
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// The ExeS/EXES/*mtw sections seem to map to the Import Table
|
||||
// 2.6/2.51
|
||||
// `ExeStealth - www.webtoolmaster.com`
|
||||
// 2.72/2.73
|
||||
// `Shareware - ExeStealth`
|
||||
// `www.webtoolmaster.com`
|
||||
// 2.74
|
||||
// `Shareware - ExeStealth`
|
||||
// 2.76
|
||||
// `ExeStealth V2 Shareware not for public - This text not in registered version - www.webtoolmaster.com`
|
||||
|
||||
// Get the ExeS/EXES section, if it exists
|
||||
bool exesSection = pex.ContainsSection("ExeS", exact: true) || pex.ContainsSection("EXES", exact: true);
|
||||
if (exesSection)
|
||||
@@ -69,21 +75,9 @@ namespace BurnOutSharp.PackerType
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
BinaryObjectScanner/Packer/EmbeddedExecutable.cs
Normal file
82
BinaryObjectScanner/Packer/EmbeddedExecutable.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
/// <summary>
|
||||
/// Though not technically a packer, this detection is for any executables that include
|
||||
/// others in their resources in some uncompressed manner to be used at runtime.
|
||||
/// </summary>
|
||||
public class EmbeddedExecutable : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the resources that have an executable signature
|
||||
if (pex.ResourceData?.Any(kvp => kvp.Value is byte[] ba && ba.StartsWith(SabreTools.Models.MSDOS.Constants.SignatureBytes)) == true)
|
||||
return "Embedded Executable";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If there are no resources
|
||||
if (pex.ResourceData == null)
|
||||
return false;
|
||||
|
||||
// Get the resources that have an executable signature
|
||||
var resources = pex.ResourceData
|
||||
.Where(kvp => kvp.Value != null && kvp.Value is byte[])
|
||||
.Select(kvp => kvp.Value as byte[])
|
||||
.Where(b => b != null && b.StartsWith(SabreTools.Models.MSDOS.Constants.SignatureBytes))
|
||||
.ToList();
|
||||
|
||||
for (int i = 0; i < resources.Count; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the resource data
|
||||
var data = resources[i];
|
||||
if (data == null)
|
||||
continue;
|
||||
|
||||
// Create the temp filename
|
||||
string tempFile = $"embedded_resource_{i}.bin";
|
||||
tempFile = Path.Combine(outDir, tempFile);
|
||||
var directoryName = Path.GetDirectoryName(tempFile);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Write the resource data to a temp file
|
||||
using var tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
|
||||
tempStream?.Write(data, 0, data.Length);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
BinaryObjectScanner/Packer/GenteeInstaller.cs
Normal file
39
BinaryObjectScanner/Packer/GenteeInstaller.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class GenteeInstaller : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("Gentee installer")))
|
||||
return "Gentee Installer";
|
||||
|
||||
if (strs.Any(s => s.Contains("ginstall.dll")))
|
||||
return "Gentee Installer";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
39
BinaryObjectScanner/Packer/HyperTechCrackProof.cs
Normal file
39
BinaryObjectScanner/Packer/HyperTechCrackProof.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// CrackProof is a packer/obfuscator created by Japanese company HyperTech (https://www.hypertech.co.jp/products/windows/).
|
||||
// It is known to be used along with other DRM, such as Shury2 (Redump entry 97135) and BDL.
|
||||
// https://www.reddit.com/r/riseofincarnates/comments/m3vbnm/subreddit_revival_does_anyone_still_have_rise_of/
|
||||
// https://steamcommunity.com/app/310950/discussions/0/4224890554455490819/
|
||||
// https://github.com/horsicq/Detect-It-Easy/blob/63a1aa8bb23ca02d8a7fd5936db8dbc5c5d52dea/db/PE/HyperTech%20Crackproof.2.sg
|
||||
public class HyperTechCrackProof : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// This check may be overly limiting, as it excludes the sample provided to DiE (https://github.com/horsicq/Detect-It-Easy/issues/102).
|
||||
// TODO: Find further samples and invesitgate if the "peC" section is only present on specific versions.
|
||||
bool peCSection = pex.ContainsSection("peC", exact: true);
|
||||
bool importTableMatch = (pex.Model.ImportTable?.ImportDirectoryTable?.Any(idte => idte?.Name == "KeRnEl32.dLl") ?? false);
|
||||
|
||||
if (peCSection && importTableMatch)
|
||||
return "HyperTech CrackProof";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
86
BinaryObjectScanner/Packer/InnoSetup.cs
Normal file
86
BinaryObjectScanner/Packer/InnoSetup.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Content;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction - https://github.com/dscharrer/InnoExtract
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class InnoSetup : IExecutableCheck<NewExecutable>,
|
||||
IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
{
|
||||
// Check for "Inno" in the reserved words
|
||||
if (nex.Model.Stub?.Header?.Reserved2?[4] == 0x6E49 && nex.Model.Stub?.Header?.Reserved2?[5] == 0x6F6E)
|
||||
{
|
||||
string version = GetOldVersion(file, nex);
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return $"Inno Setup {version}";
|
||||
|
||||
return "Inno Setup (Unknown Version)";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
var str = strs.FirstOrDefault(s => s.StartsWith("Inno Setup Setup Data"));
|
||||
if (str != null)
|
||||
{
|
||||
return str.Replace("Inno Setup Setup Data", "Inno Setup")
|
||||
.Replace("(u)", "[Unicode]")
|
||||
.Replace("(", string.Empty)
|
||||
.Replace(")", string.Empty)
|
||||
.Replace("[Unicode]", "(Unicode)");
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GetOldVersion(string file, NewExecutable nex)
|
||||
{
|
||||
// Notes:
|
||||
// Look into `SETUPLDR` in the resident-name table
|
||||
// Look into `SETUPLDR.EXE` in the nonresident-name table
|
||||
|
||||
// TODO: Don't read entire file
|
||||
// TODO: Only 64 bytes at the end of the file is needed
|
||||
var data = nex.ReadArbitraryRange();
|
||||
if (data != null)
|
||||
{
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
// "rDlPtS02" + (char)0x87 + "eVx"
|
||||
new(new byte?[] { 0x72, 0x44, 0x6C, 0x50, 0x74, 0x53, 0x30, 0x32, 0x87, 0x65, 0x56, 0x78 }, "1.2.16 or earlier"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(file, data, matchers, false) ?? "Unknown 1.X";
|
||||
}
|
||||
|
||||
return "Unknown 1.X";
|
||||
}
|
||||
}
|
||||
}
|
||||
46
BinaryObjectScanner/Packer/InstallAnywhere.cs
Normal file
46
BinaryObjectScanner/Packer/InstallAnywhere.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction, which may be possible with the current libraries but needs to be investigated further.
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class InstallAnywhere : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var name= pex.FileDescription;
|
||||
if (name?.StartsWith("InstallAnywhere Self Extractor", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"InstallAnywhere {GetVersion(pex)}";
|
||||
|
||||
name = pex.ProductName;
|
||||
if (name?.StartsWith("InstallAnywhere", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"InstallAnywhere {GetVersion(pex)}";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private string GetVersion(PortableExecutable pex)
|
||||
{
|
||||
// Check the internal versions
|
||||
var version = pex.GetInternalVersion();
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return version!;
|
||||
|
||||
return "(Unknown Version)";
|
||||
}
|
||||
}
|
||||
}
|
||||
37
BinaryObjectScanner/Packer/InstallerVISE.cs
Normal file
37
BinaryObjectScanner/Packer/InstallerVISE.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction - https://github.com/Bioruebe/UniExtract2
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class InstallerVISE : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
//TODO: Add exact version detection for Windows builds, make sure versions before 3.X are detected as well, and detect the Mac builds.
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("ViseMain")))
|
||||
return "Installer VISE";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
BinaryObjectScanner/Packer/IntelInstallationFramework.cs
Normal file
41
BinaryObjectScanner/Packer/IntelInstallationFramework.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction, seems to primarily use MSZip compression.
|
||||
public class IntelInstallationFramework : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var name= pex.FileDescription;
|
||||
if (name?.Equals("Intel(R) Installation Framework", StringComparison.OrdinalIgnoreCase) == true
|
||||
|| name?.Equals("Intel Installation Framework", StringComparison.OrdinalIgnoreCase) == true)
|
||||
{
|
||||
return $"Intel Installation Framework {pex.GetInternalVersion()}";
|
||||
}
|
||||
|
||||
name = pex.ProductName;
|
||||
if (name?.Equals("Intel(R) Installation Framework", StringComparison.OrdinalIgnoreCase) == true
|
||||
|| name?.Equals("Intel Installation Framework", StringComparison.OrdinalIgnoreCase) == true)
|
||||
{
|
||||
return $"Intel Installation Framework {pex.GetInternalVersion()}";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
BinaryObjectScanner/Packer/MicrosoftCABSFX.cs
Normal file
65
BinaryObjectScanner/Packer/MicrosoftCABSFX.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction, which should be possible with LibMSPackN, but it refuses to extract due to SFX files lacking the typical CAB identifiers.
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class MicrosoftCABSFX : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var name= pex.InternalName;
|
||||
if (name?.Equals("Wextract", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"Microsoft CAB SFX {GetVersion(pex)}";
|
||||
|
||||
name = pex.OriginalFilename;
|
||||
if (name?.Equals("WEXTRACT.EXE", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"Microsoft CAB SFX {GetVersion(pex)}";
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("wextract_cleanup")))
|
||||
return $"Microsoft CAB SFX {GetVersion(pex)}";
|
||||
}
|
||||
|
||||
// Get the .text section strings, if they exist
|
||||
strs = pex.GetFirstSectionStrings(".text");
|
||||
if (strs != null)
|
||||
{
|
||||
// This detects a different but similar type of SFX that uses Microsoft CAB files.
|
||||
// Further research is needed to see if it's just a different version or entirely separate.
|
||||
if (strs.Any(s => s.Contains("MSCFu")))
|
||||
return $"Microsoft CAB SFX {GetVersion(pex)}";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private string GetVersion(PortableExecutable pex)
|
||||
{
|
||||
// Check the internal versions
|
||||
var version = pex.GetInternalVersion();
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return $"v{version}";
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
39
BinaryObjectScanner/Packer/NSIS.cs
Normal file
39
BinaryObjectScanner/Packer/NSIS.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction
|
||||
public class NSIS : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var description = pex.AssemblyDescription;
|
||||
if (!string.IsNullOrEmpty(description) && description!.StartsWith("Nullsoft Install System"))
|
||||
return $"NSIS {description.Substring("Nullsoft Install System".Length).Trim()}";
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("NullsoftInst")))
|
||||
return "NSIS";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
BinaryObjectScanner/Packer/NeoLite.cs
Normal file
44
BinaryObjectScanner/Packer/NeoLite.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
/// <summary>
|
||||
/// NeoLite (https://web.archive.org/web/20000815214147/http://www.neoworx.com/products/neolite/default.asp) was a packer created by NeoWorx.
|
||||
/// The most common version appears to be 2.0, with earlier versions existing but with no archived copies available.
|
||||
/// NeoWorx was acquired by McAfee in October 2001, who seemingly dropped support for NeoLite (https://web.archive.org/web/20020603224725/http://www.mcafee.com/myapps/neoworx/default.asp).
|
||||
///
|
||||
/// Additional references and documentation:
|
||||
/// NeoLite 2.0 evaluation installer: https://web.archive.org/web/20001012061916/http://www.neoworx.com/download/neolte20.exe
|
||||
/// PEiD scanning definitions that include NeoLite: https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
/// Website listing various packers, including NeoLite: http://protools.narod.ru/packers.htm
|
||||
/// </summary>
|
||||
public class NeoLite : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
// TODO: Find samples of NeoLite 1.X.
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .neolit section, if it exists.
|
||||
// TODO: Check if this section is also present in NeoLite 1.X.
|
||||
bool neolitSection = pex.ContainsSection(".neolit", exact: true);
|
||||
if (neolitSection)
|
||||
return "NeoLite";
|
||||
|
||||
// If more specific or additional checks are needed, "NeoLite Executable File Compressor" should be present
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,22 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.PE;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BurnOutSharp.PackerType
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Better version detection - https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
// TODO: Add extraction
|
||||
public class PECompact : IPortableExecutableCheck, IScannable
|
||||
public class PECompact : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic) => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex?.SectionTable;
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// 0x4F434550 is "PECO"
|
||||
if (pex.ImageFileHeader.PointerToSymbolTable == 0x4F434550)
|
||||
if (pex.Model.COFFFileHeader?.PointerToSymbolTable == 0x4F434550)
|
||||
return "PE Compact v1.x";
|
||||
|
||||
// TODO: Get more granular version detection. PiD is somehow able to detect version ranges based
|
||||
@@ -44,23 +39,11 @@ namespace BurnOutSharp.PackerType
|
||||
|
||||
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/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
BinaryObjectScanner/Packer/Petite.cs
Normal file
32
BinaryObjectScanner/Packer/Petite.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class PEtite : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .petite section, if it exists -- TODO: Is there a version number that can be found?
|
||||
bool petiteSection = pex.ContainsSection(".petite", exact: true);
|
||||
if (petiteSection)
|
||||
return "PEtite";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.PE;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BurnOutSharp.Tools;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BurnOutSharp.PackerType
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction, which is possible but the only tools available that can
|
||||
// do this seem to be Universal Extractor 2 and InstallExplorer (https://totalcmd.net/plugring/InstallExplorer.html)
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class SetupFactory : IPortableExecutableCheck, IScannable
|
||||
public class SetupFactory : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic) => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex?.SectionTable;
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Known to detect versions 7.0.5.1 - 9.1.0.0
|
||||
string name = pex.LegalCopyright;
|
||||
var name = pex.LegalCopyright;
|
||||
if (name?.StartsWith("Setup Engine", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"Setup Factory {GetVersion(pex)}";
|
||||
|
||||
@@ -44,34 +38,22 @@ namespace BurnOutSharp.PackerType
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetVersion(PortableExecutable pex)
|
||||
{
|
||||
// Check the product version explicitly
|
||||
string version = pex.ProductVersion;
|
||||
var version = pex.ProductVersion;
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return version;
|
||||
return version!;
|
||||
|
||||
// Check the internal versions
|
||||
version = Utilities.GetInternalVersion(pex);
|
||||
version = pex.GetInternalVersion();
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return version;
|
||||
return version!;
|
||||
|
||||
return "(Unknown Version)";
|
||||
}
|
||||
53
BinaryObjectScanner/Packer/SevenZipSFX.cs
Normal file
53
BinaryObjectScanner/Packer/SevenZipSFX.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
public class SevenZipSFX : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the assembly description, if possible
|
||||
if (pex.AssemblyDescription?.StartsWith("7-Zip Self-extracting Archive") == true)
|
||||
return $"7-Zip SFX {pex.AssemblyDescription.Substring("7-Zip Self-extracting Archive ".Length)}";
|
||||
|
||||
// Get the file description, if it exists
|
||||
if (pex.FileDescription?.Equals("7z SFX") == true)
|
||||
return "7-Zip SFX";
|
||||
if (pex.FileDescription?.Equals("7z Self-Extract Setup") == true)
|
||||
return "7-Zip SFX";
|
||||
|
||||
// Get the original filename, if it exists
|
||||
if (pex.OriginalFilename?.Equals("7z.sfx.exe") == true)
|
||||
return "7-Zip SFX";
|
||||
else if (pex.OriginalFilename?.Equals("7zS.sfx") == true)
|
||||
return "7-Zip SFX";
|
||||
|
||||
// Get the internal name, if it exists
|
||||
if (pex.InternalName?.Equals("7z.sfx") == true)
|
||||
return "7-Zip SFX";
|
||||
else if (pex.InternalName?.Equals("7zS.sfx") == true)
|
||||
return "7-Zip SFX";
|
||||
|
||||
// If any dialog boxes match
|
||||
if (pex.FindDialogByTitle("7-Zip self-extracting archive").Any())
|
||||
return "7-Zip SFX";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
var sevenZip = new FileType.SevenZip();
|
||||
return sevenZip.Extract(file, outDir, lookForHeader: true, includeDebug);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
BinaryObjectScanner/Packer/Shrinker.cs
Normal file
33
BinaryObjectScanner/Packer/Shrinker.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class Shrinker : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .shrink0 and .shrink2 sections, if they exist -- TODO: Confirm if both are needed or either/or is fine
|
||||
bool shrink0Section = pex.ContainsSection(".shrink0", true);
|
||||
bool shrink2Section = pex.ContainsSection(".shrink2", true);
|
||||
if (shrink0Section || shrink2Section)
|
||||
return "Shrinker";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
100
BinaryObjectScanner/Packer/UPX.cs
Normal file
100
BinaryObjectScanner/Packer/UPX.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class UPX : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
private static readonly Regex _oldUpxVersionMatch = new Regex(@"\$Id: UPX (.*?) Copyright \(C\)", RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex _upxVersionMatch = new Regex(@"^([0-9]\.[0-9]{2})$", RegexOptions.Compiled);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Check header padding strings
|
||||
if (pex.HeaderPaddingStrings?.Any() == true)
|
||||
{
|
||||
var match = pex.HeaderPaddingStrings.FirstOrDefault(s => s.Contains("UPX!"));
|
||||
//if (match != null)
|
||||
// return "UPX";
|
||||
|
||||
match = pex.HeaderPaddingStrings.FirstOrDefault(s => s.StartsWith("$Id: UPX"));
|
||||
if (match != null)
|
||||
{
|
||||
var regexMatch = _oldUpxVersionMatch.Match(match);
|
||||
if (regexMatch.Success)
|
||||
return $"UPX {regexMatch.Groups[1].Value}";
|
||||
else
|
||||
return "UPX (Unknown Version)";
|
||||
}
|
||||
|
||||
match = pex.HeaderPaddingStrings.FirstOrDefault(s => _upxVersionMatch.IsMatch(s));
|
||||
if (match != null && pex.HeaderPaddingStrings.Any(s => s == "UPX!"))
|
||||
{
|
||||
var regexMatch = _upxVersionMatch.Match(match);
|
||||
if (regexMatch.Success)
|
||||
return $"UPX {regexMatch.Groups[1].Value}";
|
||||
else
|
||||
return "UPX (Unknown Version)";
|
||||
}
|
||||
else if (match != null && pex.HeaderPaddingStrings.Any(s => s == "NOS "))
|
||||
{
|
||||
var regexMatch = _upxVersionMatch.Match(match);
|
||||
if (regexMatch.Success)
|
||||
return $"UPX (NOS Variant) {regexMatch.Groups[1].Value}";
|
||||
else
|
||||
return "UPX (NOS Variant) (Unknown Version)";
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string GetVersion(string file, byte[] fileContent, List<int> positions)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check the normal version location first
|
||||
int index = positions[0] - 5;
|
||||
string versionString = Encoding.ASCII.GetString(fileContent, index, 4);
|
||||
if (char.IsNumber(versionString[0]))
|
||||
return versionString;
|
||||
|
||||
// Check for the old-style string
|
||||
//
|
||||
// Example:
|
||||
// $Info: This file is packed with the UPX executable packer http://upx.tsx.org $
|
||||
// $Id: UPX 1.02 Copyright (C) 1996-2000 the UPX Team. All Rights Reserved. $
|
||||
// UPX!
|
||||
index = positions[0] - 67;
|
||||
versionString = Encoding.ASCII.GetString(fileContent, index, 4);
|
||||
if (char.IsNumber(versionString[0]))
|
||||
return versionString;
|
||||
|
||||
return "(Unknown Version)";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "(Unknown Version)";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
35
BinaryObjectScanner/Packer/WinRARSFX.cs
Normal file
35
BinaryObjectScanner/Packer/WinRARSFX.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
public class WinRARSFX : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var name = pex.AssemblyDescription;
|
||||
if (name?.Contains("WinRAR archiver") == true)
|
||||
return "WinRAR SFX";
|
||||
|
||||
var resources = pex.FindDialogByTitle("WinRAR self-extracting archive");
|
||||
if (resources.Any())
|
||||
return "WinRAR SFX";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
var rar = new FileType.RAR();
|
||||
return rar.Extract(file, outDir, lookForHeader: true, includeDebug);
|
||||
}
|
||||
}
|
||||
}
|
||||
763
BinaryObjectScanner/Packer/WinZipSFX.cs
Normal file
763
BinaryObjectScanner/Packer/WinZipSFX.cs
Normal file
@@ -0,0 +1,763 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
public class WinZipSFX : IExtractableExecutable<NewExecutable>, IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
{
|
||||
// If the resident-name table doesnt exist
|
||||
if (nex.Model.ResidentNameTable == null)
|
||||
return null;
|
||||
|
||||
// Check for the WinZip name strings
|
||||
bool winZipNameFound = nex.Model.ResidentNameTable
|
||||
.Select(rnte => rnte?.NameString == null ? string.Empty : Encoding.ASCII.GetString(rnte.NameString))
|
||||
.Any(s => s.Contains("WZ-SE-01"));
|
||||
winZipNameFound |= nex.Model.NonResidentNameTable?
|
||||
.Select(nrnte => nrnte?.NameString == null ? string.Empty : Encoding.ASCII.GetString(nrnte.NameString))
|
||||
.Any(s => s.Contains("WinZip(R) Self-Extractor")) ?? false;
|
||||
|
||||
// If we didn't find it
|
||||
if (!winZipNameFound)
|
||||
return null;
|
||||
|
||||
// Try to get a known version
|
||||
var version = GetNEHeaderVersion(nex);
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return $"WinZip SFX {version}";
|
||||
|
||||
return $"WinZip SFX Unknown Version (16-bit)";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Check the export directory table, if it exists
|
||||
if (pex.Model.ExportTable?.ExportDirectoryTable != null)
|
||||
{
|
||||
var version = GetPEExportDirectoryVersion(pex);
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return $"WinZip SFX {version}";
|
||||
}
|
||||
|
||||
// Get the _winzip_ section, if it exists
|
||||
if (pex.ContainsSection("_winzip_", exact: true))
|
||||
return "WinZip SFX Unknown Version (32-bit)";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Find a way to generically detect 2.X versions and improve exact version detection for SFX PE versions bundled with WinZip 11+
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, NewExecutable nex, string outDir, bool includeDebug)
|
||||
=> Extract(file, outDir, includeDebug);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
=> Extract(file, outDir, includeDebug);
|
||||
|
||||
/// <summary>
|
||||
/// Handle common extraction between executable types
|
||||
/// </summary>
|
||||
public static bool Extract(string file, string outDir, bool includeDebug)
|
||||
{
|
||||
var pkzip = new FileType.PKZIP();
|
||||
return pkzip.Extract(file, outDir, lookForHeader: true, includeDebug);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from the NE header value combinations
|
||||
/// </summary>
|
||||
/// TODO: Reduce the checks to only the ones that differ between versions
|
||||
/// TODO: Research to see if the versions are embedded elsewhere in these files
|
||||
private static string? GetNEHeaderVersion(NewExecutable nex)
|
||||
{
|
||||
#region 2.0 Variants
|
||||
|
||||
// 2.0 (MS-DOS/16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0086
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00012BE6
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000044B8
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "2.0 (MS-DOS/16-bit)";
|
||||
|
||||
// 2.0 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0086
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00013174
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000198
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "2.0 (16-bit)";
|
||||
|
||||
// Compact 2.0 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0080
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x000124A0
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0003
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006A
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000192
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Compact 2.0 (16-bit)";
|
||||
|
||||
// Software Installation 2.0 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x00CD
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x000136FA
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0005
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0097
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x00A3
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00AD
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001DF
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Software Installation 2.0 (16-bit)";
|
||||
|
||||
#endregion
|
||||
|
||||
#region 2.1 RC2 Variants
|
||||
|
||||
// 2.1 RC2 (MS-DOS/16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0086
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00013386
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000043C8
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "2.1 RC2 (MS-DOS/16-bit)";
|
||||
|
||||
// 2.1 RC2 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x00BE
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00013E56
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "2.1 RC2 (16-bit)";
|
||||
|
||||
// Compact 2.1 RC2 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0080
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00012B84
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0003
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006A
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000192
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Compact 2.1 RC2 (16-bit)";
|
||||
|
||||
// Software Installation 2.1 RC2 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x00BE
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x000143AC
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Software Installation 2.1 RC2 (16-bit)";
|
||||
|
||||
#endregion
|
||||
|
||||
#region 2.1 Variants
|
||||
|
||||
// 2.1 (MS-DOS/16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0086
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x3A00
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00013396
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000043C8
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "2.1 (MS-DOS/16-bit)";
|
||||
|
||||
// 2.1 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x00BE
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x3A00
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00013E7E
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "2.1 (16-bit)";
|
||||
|
||||
// Compact 2.1 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0080
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x3A00
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00012B90
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0003
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006A
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000192
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Compact 2.1 (16-bit)";
|
||||
|
||||
// Software Installation 2.1 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x00BE
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x3A00
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00014408
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Software Installation 2.1 (16-bit)";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Misc. Variants
|
||||
|
||||
// Personal Edition (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0086
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x0001317C
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000198
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Personal Edition (16-bit)";
|
||||
|
||||
// Personal Edition 32-bit (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x00BE
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x3C00
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00013E7C
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Personal Edition 32-bit (16-bit)";
|
||||
|
||||
// Personal Edition 32-bit Build 1260/1285 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x00C6
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x43DC
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x2708
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00014ADC
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0005
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A6
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D8
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Personal Edition 32-bit Build 1260/1285 (16-bit)";
|
||||
|
||||
#endregion
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from the PE export directory table value combinations
|
||||
/// </summary>
|
||||
/// TODO: Research to see if the versions are embedded elsewhere in these files
|
||||
private static string? GetPEExportDirectoryVersion(PortableExecutable pex)
|
||||
{
|
||||
string sfxFileName = pex.Model.ExportTable?.ExportDirectoryTable?.Name ?? string.Empty;
|
||||
uint sfxTimeDateStamp = pex.Model.ExportTable?.ExportDirectoryTable?.TimeDateStamp ?? uint.MaxValue;
|
||||
string assemblyVersion = pex.AssemblyVersion ?? "Unknown Version";
|
||||
|
||||
// Standard
|
||||
if (sfxFileName == "VW95SE.SFX" || sfxFileName == "ST32E.SFX"
|
||||
|| sfxFileName == "WZIPSE32.exe" || sfxFileName == "SI32LPG.SFX"
|
||||
|| sfxFileName == "ST32E.WZE")
|
||||
{
|
||||
return sfxTimeDateStamp switch
|
||||
{
|
||||
842636344 => "2.0 (32-bit)",
|
||||
865370756 => "2.1 RC2 (32-bit)",
|
||||
869059925 => "2.1 (32-bit)",
|
||||
979049321 => "2.2.4003",
|
||||
1149714685 => "3.0.7158",
|
||||
1185211734 => "3.1.7556",
|
||||
1185211920 => "3.1.7556",
|
||||
1235490556 => "4.0.8421",
|
||||
1235490757 => "4.0.8421",
|
||||
1235490687 => "4.0.8421",// 3.1.8421.0, SI32LPG?
|
||||
1257193383 => "4.0.8672",// 3.1.8672.0
|
||||
1257193543 => "4.0.8672",
|
||||
1470410848 => "4.0.12218",// 4.0.1221.0
|
||||
_ => $"{assemblyVersion} (32-bit)",
|
||||
};
|
||||
}
|
||||
|
||||
// Personal Edition
|
||||
if (sfxFileName == "VW95LE.SFX" || sfxFileName == "PE32E.SFX"
|
||||
|| sfxFileName == "wzsepe32.exe" || sfxFileName == "SI32PE.SFX"
|
||||
|| sfxFileName == "SI32LPE.SFX")
|
||||
{
|
||||
return sfxTimeDateStamp switch
|
||||
{
|
||||
845061601 => "Personal Edition (32-bit)",// TODO: Find version
|
||||
868303343 => "Personal Edition (32-bit)",// TODO: Find version
|
||||
868304170 => "Personal Edition (32-bit)",// TODO: Find version
|
||||
906039079 => "Personal Edition 2.2.1260 (32-bit)",
|
||||
906040543 => "Personal Edition 2.2.1260 (32-bit)",
|
||||
908628435 => "Personal Edition 2.2.1285 (32-bit)",
|
||||
908628785 => "Personal Edition 2.2.1285 (32-bit)",
|
||||
956165981 => "Personal Edition 2.2.3063",
|
||||
956166038 => "Personal Edition 2.2.3063",
|
||||
1006353695 => "Personal Edition 2.2.4325",
|
||||
1006353714 => "Personal Edition 2.2.4325",// 8.1.0.0
|
||||
1076515698 => "Personal Edition 2.2.6028",
|
||||
1076515784 => "Personal Edition 2.2.6028",// 9.0.6028.0
|
||||
1092688561 => "Personal Edition 2.2.6224",
|
||||
1092688645 => "Personal Edition 2.2.6224",// 9.0.6224.0
|
||||
1125074095 => "Personal Edition 2.2.6604",
|
||||
1125074162 => "Personal Edition 2.2.6604",// 10.0.6604.0
|
||||
1130153399 => "Personal Edition 2.2.6663",
|
||||
1130153428 => "Personal Edition 2.2.6663",// 10.0.6663.0
|
||||
1149714176 => "Personal Edition 3.0.7158",
|
||||
1163137967 => "Personal Edition 3.0.7305",
|
||||
1163137994 => "Personal Edition 3.0.7313",// 11.0.7313.0
|
||||
1176345383 => "Personal Edition 3.0.7452",
|
||||
1176345423 => "Personal Edition 3.1.7466",// 11.1.7466.0
|
||||
1184106698 => "Personal Edition 3.1.7556",
|
||||
1207280880 => "Personal Edition 4.0.8060",// 2.3.7382.0
|
||||
1207280892 => "Personal Edition 4.0.8094",// 11.2.8094.0
|
||||
1220904506 => "Personal Edition 4.0.8213",// 2.3.7382.0
|
||||
1220904518 => "Personal Edition 4.0.8252",// 12.0.8252.0
|
||||
1235490648 => "Personal Edition 4.0.8421",// 3.1.8421.0
|
||||
1242049399 => "Personal Edition 4.0.8497",// 12.1.8497.0
|
||||
1257193469 => "Personal Edition 4.0.8672",// 3.1.8672.0, SI32LPE?
|
||||
_ => $"Personal Edition {assemblyVersion} (32-bit)",
|
||||
};
|
||||
}
|
||||
|
||||
// Software Installation
|
||||
else if (sfxFileName == "VW95SRE.SFX" || sfxFileName == "SI32E.SFX"
|
||||
|| sfxFileName == "SI32E.WZE")
|
||||
{
|
||||
return sfxTimeDateStamp switch
|
||||
{
|
||||
842636381 => "Software Installation 2.0 (32-bit)",
|
||||
865370800 => "Software Installation 2.1 RC2 (32-bit)",
|
||||
869059963 => "Software Installation 2.1 (32-bit)",
|
||||
893107697 => "Software Installation 2.2.1110 (32-bit)",
|
||||
952007369 => "Software Installation 2.2.3063",
|
||||
1006352634 => "Software Installation 2.2.4325",// +Personal Edition?
|
||||
979049345 => "Software Installation 2.2.4403",
|
||||
1026227373 => "Software Installation 2.2.5196",// +Personal Edition?
|
||||
1090582390 => "Software Installation 2.2.6202",// +Personal Edition?
|
||||
1149714757 => "Software Installation 3.0.7158",
|
||||
1154357628 => "Software Installation 3.0.7212",
|
||||
1175234637 => "Software Installation 3.0.7454",
|
||||
1185211802 => "Software Installation 3.1.7556",
|
||||
1470410906 => "Software Installation 4.0.12218",// 4.0.1221.0
|
||||
_ => $"Software Installation {assemblyVersion} (32-bit)",
|
||||
};
|
||||
}
|
||||
|
||||
return sfxFileName switch
|
||||
{
|
||||
// Standard
|
||||
"VW95SE.SFX" => "Unknown Version (32-bit)",// TODO: Find starting version
|
||||
"ST32E.SFX" => "Unknown Version (32-bit)",// TODO: Find starting version
|
||||
"WZIPSE32.exe" => "Unknown Version (32-bit)",// TODO: Find starting version
|
||||
"SI32LPG.SFX" => "Unknown Version (32-bit)",// TODO: Find starting version
|
||||
"ST32E.WZE" => "Unknown Version (32-bit)",// TODO: Find starting version
|
||||
|
||||
// Personal Edition
|
||||
"VW95LE.SFX" => "Unknown Version before Personal Edition Build 1285 (32-bit)",
|
||||
"PE32E.SFX" => "Unknown Version after Personal Edition Build 1285 (32-bit)",
|
||||
"wzsepe32.exe" => "Unknown Version Personal Edition (32-bit)",// TODO: Find starting version
|
||||
"SI32PE.SFX" => "Unknown Version Personal Edition (32-bit)",// TODO: Find starting version
|
||||
"SI32LPE.SFX" => "Unknown Version Personal Edition (32-bit)",// TODO: Find starting version
|
||||
|
||||
// Software Installation
|
||||
"VW95SRE.SFX" => "Unknown Version before Software Installation 2.1 (32-bit)",
|
||||
"SI32E.SFX" => "Unknown Version after Software Installation 2.1 (32-bit)",
|
||||
"SI32E.WZE" => "Unknown Version Software Installation (32-bit)",// TODO: Find starting version
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
289
BinaryObjectScanner/Packer/WiseInstaller.cs
Normal file
289
BinaryObjectScanner/Packer/WiseInstaller.cs
Normal file
@@ -0,0 +1,289 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Content;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
using WiseUnpacker;
|
||||
using WiseUnpacker.EWISE;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class WiseInstaller : IExtractableExecutable<NewExecutable>, IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
{
|
||||
// If we match a known header
|
||||
if (MatchesNEVersion(nex) != null)
|
||||
return "Wise Installation Wizard Module";
|
||||
|
||||
// TODO: Investigate STUB.EXE in nonresident-name table
|
||||
|
||||
// TODO: Don't read entire file
|
||||
var data = nex.ReadArbitraryRange();
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
var neMatchSets = new List<ContentMatchSet>
|
||||
{
|
||||
// WiseInst
|
||||
new(new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x49, 0x6E, 0x73, 0x74 }, "Wise Installation Wizard Module"),
|
||||
|
||||
// WiseMain
|
||||
new(new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x4D, 0x61, 0x69, 0x6E }, "Wise Installation Wizard Module"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(file, data, neMatchSets, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// If we match a known header
|
||||
if (GetPEFormat(pex) != null)
|
||||
return "Wise Installation Wizard Module";
|
||||
|
||||
// TODO: Investigate STUB32.EXE in export directory table
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("WiseMain")))
|
||||
return "Wise Installation Wizard Module";
|
||||
}
|
||||
|
||||
// Get the .rdata section strings, if they exist
|
||||
strs = pex.GetFirstSectionStrings(".rdata");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("WiseMain")))
|
||||
return "Wise Installation Wizard Module";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, NewExecutable nex, string outDir, bool includeDebug)
|
||||
{
|
||||
Directory.CreateDirectory(outDir);
|
||||
|
||||
try
|
||||
{
|
||||
return Extractor.ExtractTo(file, outDir);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the matching PE format
|
||||
var format = GetPEFormat(pex);
|
||||
if (format == null)
|
||||
return false;
|
||||
|
||||
// Get the overlay data for easier reading
|
||||
int overlayOffset = 0, dataStart = 0;
|
||||
var overlayData = pex.OverlayData;
|
||||
if (overlayData == null)
|
||||
return false;
|
||||
|
||||
// Skip over the additional DLL name, if we expect it
|
||||
if (format.Dll)
|
||||
{
|
||||
// Read the name length
|
||||
byte dllNameLength = overlayData.ReadByte(ref overlayOffset);
|
||||
dataStart++;
|
||||
|
||||
// Read the name, if it exists
|
||||
if (dllNameLength != 0)
|
||||
{
|
||||
// Ignore the name for now
|
||||
_ = overlayData.ReadBytes(ref overlayOffset, dllNameLength);
|
||||
dataStart += dllNameLength;
|
||||
|
||||
// Named DLLs also have a DLL length that we ignore
|
||||
_ = overlayData.ReadUInt32(ref overlayOffset);
|
||||
dataStart += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if flags are consistent
|
||||
if (!format.NoCrc)
|
||||
{
|
||||
// Unlike WiseUnpacker, we ignore the flag value here
|
||||
_ = overlayData.ReadUInt32(ref overlayOffset);
|
||||
}
|
||||
|
||||
// Ensure that we have an archive end
|
||||
if (format.ArchiveEnd > 0)
|
||||
{
|
||||
overlayOffset = (int)(dataStart + format.ArchiveEnd);
|
||||
int archiveEndLoaded = overlayData.ReadInt32(ref overlayOffset);
|
||||
if (archiveEndLoaded != 0)
|
||||
format.ArchiveEnd = archiveEndLoaded;
|
||||
}
|
||||
|
||||
// Skip to the start of the archive
|
||||
overlayOffset = (int)(dataStart + format.ArchiveStart);
|
||||
|
||||
// Skip over the initialization text, if we expect it
|
||||
if (format.InitText)
|
||||
{
|
||||
int initTextLength = overlayData.ReadByte(ref overlayOffset);
|
||||
_ = overlayData.ReadBytes(ref overlayOffset, initTextLength);
|
||||
}
|
||||
|
||||
// Cache the current offset in the overlay as the "start of data"
|
||||
int offsetReal = overlayOffset;
|
||||
|
||||
// If the first entry is PKZIP, we assume it's an embedded zipfile
|
||||
var magic = overlayData.ReadBytes(ref overlayOffset, 4); overlayOffset -= 4;
|
||||
bool pkzip = magic?.StartsWith(new byte?[] { (byte)'P', (byte)'K' }) ?? false;
|
||||
|
||||
// Create the output directory
|
||||
Directory.CreateDirectory(outDir);
|
||||
|
||||
// If we have PKZIP
|
||||
if (pkzip)
|
||||
{
|
||||
string tempFile = Path.Combine(outDir, "WISEDATA.zip");
|
||||
using Stream tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
|
||||
tempStream.Write(overlayData, overlayOffset, overlayData.Length - overlayOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we have DEFLATE -- TODO: Port implementation here or use DeflateStream
|
||||
else
|
||||
{
|
||||
return Extractor.ExtractTo(file, outDir);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks an NE header to see if it matches a known signature
|
||||
/// </summary>
|
||||
/// <param name="nex">New executable to check</param>
|
||||
/// <returns>True if it matches a known version, false otherwise</returns>
|
||||
private FormatProperty? MatchesNEVersion(NewExecutable nex)
|
||||
{
|
||||
// TODO: Offset is _not_ the EXE header address, rather where the data starts. Fix this.
|
||||
switch (nex.Model.Stub?.Header?.NewExeHeaderAddr)
|
||||
{
|
||||
case 0x84b0:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x11, ArchiveEnd = -1, InitText = false, FilenamePosition = 0x04, NoCrc = true };
|
||||
|
||||
case 0x3e10:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x1e, ArchiveEnd = -1, InitText = false, FilenamePosition = 0x04, NoCrc = false };
|
||||
|
||||
case 0x3e50:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x1e, ArchiveEnd = -1, InitText = false, FilenamePosition = 0x04, NoCrc = false };
|
||||
|
||||
case 0x3c20:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x1e, ArchiveEnd = -1, InitText = false, FilenamePosition = 0x04, NoCrc = false };
|
||||
|
||||
case 0x3c30:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x22, ArchiveEnd = -1, InitText = false, FilenamePosition = 0x04, NoCrc = false };
|
||||
|
||||
case 0x3660:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x40, ArchiveEnd = 0x3c, InitText = false, FilenamePosition = 0x04, NoCrc = false };
|
||||
|
||||
case 0x36f0:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x48, ArchiveEnd = 0x44, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
case 0x3770:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
case 0x3780:
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
case 0x37b0:
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
case 0x37d0:
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
case 0x3c80:
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x5a, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
case 0x3bd0:
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x5a, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
case 0x3c10:
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x5a, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks a PE header to see if it matches a known signature
|
||||
/// </summary>
|
||||
/// <param name="pex">Portable executable to check</param>
|
||||
/// <returns>True if it matches a known version, false otherwise</returns>
|
||||
private FormatProperty? GetPEFormat(PortableExecutable pex)
|
||||
{
|
||||
if (pex.OverlayAddress == 0x6e00
|
||||
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3cf4
|
||||
&& pex.GetFirstSection(".data")?.VirtualSize == 0x1528)
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
else if (pex.OverlayAddress == 0x6e00
|
||||
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3cf4
|
||||
&& pex.GetFirstSection(".data")?.VirtualSize == 0x1568)
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
else if (pex.OverlayAddress == 0x6e00
|
||||
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3d54)
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
else if (pex.OverlayAddress == 0x6e00
|
||||
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3d44)
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
else if (pex.OverlayAddress == 0x6e00
|
||||
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3d04)
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
// Found in Binary.WiseCustomCalla
|
||||
else if (pex.OverlayAddress == 0x6200)
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x62, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
else if (pex.OverlayAddress == 0x3000)
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
else if (pex.OverlayAddress == 0x3800)
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x5a, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
else if (pex.OverlayAddress == 0x3a00)
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x5a, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
107
BinaryObjectScanner/Progress.cs
Normal file
107
BinaryObjectScanner/Progress.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
#if NET20 || NET35 || NET40
|
||||
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an IProgress{T} that invokes callbacks for each reported progress value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Specifies the type of the progress report value.</typeparam>
|
||||
/// <remarks>
|
||||
/// Any handler provided to the constructor or event handlers registered with
|
||||
/// the <see cref="ProgressChanged"/> event are invoked through a
|
||||
/// <see cref="SynchronizationContext"/> instance captured
|
||||
/// when the instance is constructed. If there is no current SynchronizationContext
|
||||
/// at the time of construction, the callbacks will be invoked on the ThreadPool.
|
||||
/// </remarks>
|
||||
/// <see href="https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Progress.cs"/>
|
||||
public class Progress<T> : IProgress<T> where T : EventArgs
|
||||
{
|
||||
/// <summary>The synchronization context captured upon construction. This will never be null.</summary>
|
||||
private readonly SynchronizationContext? _synchronizationContext;
|
||||
/// <summary>The handler specified to the constructor. This may be null.</summary>
|
||||
private readonly Action<T>? _handler;
|
||||
/// <summary>A cached delegate used to post invocation to the synchronization context.</summary>
|
||||
private readonly SendOrPostCallback _invokeHandlers;
|
||||
|
||||
/// <summary>Initializes the <see cref="Progress{T}"/>.</summary>
|
||||
public Progress()
|
||||
{
|
||||
// Capture the current synchronization context.
|
||||
// If there is no current context, we use a default instance targeting the ThreadPool.
|
||||
_synchronizationContext = SynchronizationContext.Current ?? ProgressStatics.DefaultContext;
|
||||
Debug.Assert(_synchronizationContext != null);
|
||||
_invokeHandlers = new SendOrPostCallback(InvokeHandlers);
|
||||
}
|
||||
|
||||
/// <summary>Initializes the <see cref="Progress{T}"/> with the specified callback.</summary>
|
||||
/// <param name="handler">
|
||||
/// A handler to invoke for each reported progress value. This handler will be invoked
|
||||
/// in addition to any delegates registered with the <see cref="ProgressChanged"/> event.
|
||||
/// Depending on the <see cref="SynchronizationContext"/> instance captured by
|
||||
/// the <see cref="Progress{T}"/> at construction, it's possible that this handler instance
|
||||
/// could be invoked concurrently with itself.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">The <paramref name="handler"/> is null (<see langword="Nothing" /> in Visual Basic).</exception>
|
||||
public Progress(Action<T> handler) : this()
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
/// <summary>Raised for each reported progress value.</summary>
|
||||
/// <remarks>
|
||||
/// Handlers registered with this event will be invoked on the
|
||||
/// <see cref="SynchronizationContext"/> captured when the instance was constructed.
|
||||
/// </remarks>
|
||||
public event EventHandler<T>? ProgressChanged;
|
||||
|
||||
/// <summary>Reports a progress change.</summary>
|
||||
/// <param name="value">The value of the updated progress.</param>
|
||||
protected virtual void OnReport(T value)
|
||||
{
|
||||
// If there's no handler, don't bother going through the sync context.
|
||||
// Inside the callback, we'll need to check again, in case
|
||||
// an event handler is removed between now and then.
|
||||
Action<T>? handler = _handler;
|
||||
EventHandler<T>? changedEvent = ProgressChanged;
|
||||
if (handler != null || changedEvent != null)
|
||||
{
|
||||
// Post the processing to the sync context.
|
||||
// (If T is a value type, it will get boxed here.)
|
||||
_synchronizationContext?.Post(_invokeHandlers, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Reports a progress change.</summary>
|
||||
/// <param name="value">The value of the updated progress.</param>
|
||||
void IProgress<T>.Report(T value) { OnReport(value); }
|
||||
|
||||
/// <summary>Invokes the action and event callbacks.</summary>
|
||||
/// <param name="state">The progress value.</param>
|
||||
private void InvokeHandlers(object? state)
|
||||
{
|
||||
T value = (T)state!;
|
||||
|
||||
Action<T>? handler = _handler;
|
||||
EventHandler<T>? changedEvent = ProgressChanged;
|
||||
|
||||
handler?.Invoke(value);
|
||||
changedEvent?.Invoke(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Holds static values for <see cref="Progress{T}"/>.</summary>
|
||||
/// <remarks>This avoids one static instance per type T.</remarks>
|
||||
internal static class ProgressStatics
|
||||
{
|
||||
/// <summary>A default synchronization context that targets the ThreadPool.</summary>
|
||||
internal static readonly SynchronizationContext DefaultContext = new();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
121
BinaryObjectScanner/Protection/ActiveMARK.cs
Normal file
121
BinaryObjectScanner/Protection/ActiveMARK.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Content;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
// TODO: Figure out how to get version numbers
|
||||
public class ActiveMARK : IContentCheck, IExecutableCheck<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
{
|
||||
// TODO: Obtain a sample to find where this string is in a typical executable
|
||||
if (includeDebug)
|
||||
{
|
||||
var contentMatchSets = new List<ContentMatchSet>
|
||||
{
|
||||
// " " + (char)0xC2 + (char)0x16 + (char)0x00 + (char)0xA8 + (char)0xC1 + (char)0x16 + (char)0x00 + (char)0xB8 + (char)0xC1 + (char)0x16 + (char)0x00 + (char)0x86 + (char)0xC8 + (char)0x16 + (char)0x00 + (char)0x9A + (char)0xC1 + (char)0x16 + (char)0x00 + (char)0x10 + (char)0xC2 + (char)0x16 + (char)0x00
|
||||
new(new byte?[]
|
||||
{
|
||||
0x20, 0xC2, 0x16, 0x00, 0xA8, 0xC1, 0x16, 0x00,
|
||||
0xB8, 0xC1, 0x16, 0x00, 0x86, 0xC8, 0x16, 0x00,
|
||||
0x9A, 0xC1, 0x16, 0x00, 0x10, 0xC2, 0x16, 0x00
|
||||
}, "ActiveMARK 5 (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchSets, includeDebug);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the entry point data, if it exists
|
||||
if (pex.EntryPointData != null)
|
||||
{
|
||||
// Found in "Zuma.exe"
|
||||
if (pex.EntryPointData.StartsWith(new byte?[] { 0x89, 0x25, 0x04, 0xF0, 0x86, 0x00, 0x68, 0x30 }))
|
||||
return "ActiveMark v5.3.1078 (Packer Version)";
|
||||
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
else if (pex.EntryPointData.StartsWith(new byte?[] { 0x89, 0x25, null, null, null, null, 0xEB }))
|
||||
return "ActiveMark -> Trymedia Systems Inc. (Unconfirmed - Please report to us on Github)";
|
||||
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
else if (pex.EntryPointData.StartsWith(new byte?[] { 0x89, 0x25, null, null, null, null, 0x33, 0xED, 0x55, 0x8B, 0xEC, 0xE8, null, null, null, null, 0x8B, 0xD0, 0x81, 0xE2, 0xFF, 0x00, 0x00, 0x00, 0x89, 0x15, null, null, null, null, 0x8B, 0xD0, 0xC1, 0xEA, 0x08, 0x81, 0xE2, 0xFF, 0x00, 0x00, 0x00, 0xA3, null, null, null, null, 0xD1, 0xE0, 0x0F, 0x93, 0xC3, 0x33, 0xC0, 0x8A, 0xC3, 0xA3, null, null, null, null, 0x68, 0xFF, 0x00, 0x00, 0x00, 0xE8, null, null, null, null, 0x6A, 0x00, 0xE8, null, null, null, null, 0xA3, null, null, null, null, 0xBB, null, null, null, null, 0xC7, 0x03, 0x44, 0x00, 0x00, 0x00 }))
|
||||
return "ActiveMark -> Trymedia Systems Inc. (Unconfirmed - Please report to us on Github)";
|
||||
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
else if (pex.EntryPointData.StartsWith(new byte?[] { 0x20, 0x2D, 0x2D, 0x4D, 0x50, 0x52, 0x4D, 0x4D, 0x47, 0x56, 0x41, 0x2D, 0x2D, 0x00, 0x75, 0x73, 0x65, 0x72, 0x33, 0x32, 0x2E, 0x64, 0x6C, 0x6C, 0x00, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x6F, 0x78, 0x41, 0x00, 0x54, 0x68, 0x69, 0x73, 0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x6E, 0x20, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67 }))
|
||||
return "ActiveMARK 5.x -> Trymedia Systems Inc. (h) (Unconfirmed - Please report to us on Github)";
|
||||
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
else if (pex.EntryPointData.StartsWith(new byte?[] { 0x20, 0x2D, 0x2D, 0x4D, 0x50, 0x52, 0x4D, 0x4D, 0x47, 0x56, 0x41, 0x2D, 0x2D, 0x00, 0x75, 0x73, 0x65, 0x72, 0x33, 0x32, 0x2E, 0x64, 0x6C, 0x6C, 0x00, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x6F, 0x78, 0x41, 0x00, 0x54, 0x68, 0x69, 0x73, 0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x6E, 0x20, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x65, 0x72, 0x20, 0x69, 0x6E, 0x20, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x2E, 0x0D, 0x0A, 0x50, 0x6C, 0x65, 0x61, 0x73, 0x65, 0x20, 0x75, 0x6E, 0x6C, 0x6F, 0x61, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x65, 0x72, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2E, 0x00, 0x57, 0x61, 0x72, 0x6E, 0x69, 0x6E, 0x67 }))
|
||||
return "ActiveMARK 5.x -> Trymedia Systems,Inc. (Unconfirmed - Please report to us on Github)";
|
||||
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
else if (pex.EntryPointData.StartsWith(new byte?[] { 0x79, 0x11, 0x7F, 0xAB, 0x9A, 0x4A, 0x83, 0xB5, 0xC9, 0x6B, 0x1A, 0x48, 0xF9, 0x27, 0xB4, 0x25 }))
|
||||
return "ActiveMARK[TM] (Unconfirmed - Please report to us on Github)";
|
||||
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
else if (pex.EntryPointData.StartsWith(new byte?[] { 0x79, 0x07, 0x0F, 0xB7, 0x07, 0x47, 0x50, 0x47, 0xB9, 0x57, 0x48, 0xF2, 0xAE, 0x55, 0xFF, 0x96, 0x84, null, 0x00, 0x00, 0x09, 0xC0, 0x74, 0x07, 0x89, 0x03, 0x83, 0xC3, 0x04, 0xEB, 0xD8, 0xFF, 0x96, 0x88, null, 0x00, 0x00, 0x61, 0xE9, null, null, null, 0xFF }))
|
||||
return "ActiveMARK[TM] R5.31.1140 -> Trymedia (Unconfirmed - Please report to us on Github)";
|
||||
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
else if (pex.EntryPointData.StartsWith(new byte?[] { 0xBE, 0x48, 0x01, 0x40, 0x00, 0xAD, 0x8B, 0xF8, 0x95, 0xA5, 0x33, 0xC0, 0x33, 0xC9, 0xAB, 0x48, 0xAB, 0xF7, 0xD8, 0xB1, 0x04, 0xF3, 0xAB, 0xC1, 0xE0, 0x0A, 0xB5, 0x1C, 0xF3, 0xAB, 0xAD, 0x50, 0x97, 0x51, 0xAD, 0x87, 0xF5, 0x58, 0x8D, 0x54, 0x86, 0x5C, 0xFF, 0xD5, 0x72, 0x5A, 0x2C, 0x03, 0x73, 0x02, 0xB0, 0x00, 0x3C, 0x07, 0x72, 0x02, 0x2C, 0x03, 0x50, 0x0F, 0xB6, 0x5F, 0xFF, 0xC1, 0xE3, 0x03, 0xB3, 0x00, 0x8D, 0x1C, 0x5B, 0x8D, 0x9C, 0x9E, 0x0C, 0x10, 0x00, 0x00, 0xB0, 0x01, 0x67, 0xE3, 0x29, 0x8B, 0xD7, 0x2B, 0x56, 0x0C, 0x8A, 0x2A, 0x33, 0xD2, 0x84, 0xE9, 0x0F, 0x95, 0xC6, 0x52, 0xFE, 0xC6, 0x8A, 0xD0, 0x8D, 0x14, 0x93, 0xFF, 0xD5, 0x5A, 0x9F, 0x12, 0xC0, 0xD0, 0xE9, 0x74, 0x0E, 0x9E, 0x1A, 0xF2, 0x74, 0xE4, 0xB4, 0x00, 0x33, 0xC9, 0xB5, 0x01, 0xFF, 0x55, 0xCC, 0x33, 0xC9, 0xE9, 0xDF, 0x00, 0x00, 0x00, 0x8B, 0x5E, 0x0C, 0x83, 0xC2, 0x30, 0xFF, 0xD5, 0x73, 0x50, 0x83, 0xC2, 0x30, 0xFF, 0xD5, 0x72, 0x1B, 0x83, 0xC2, 0x30, 0xFF, 0xD5, 0x72, 0x2B, 0x3C, 0x07, 0xB0, 0x09, 0x72, 0x02, 0xB0, 0x0B, 0x50, 0x8B, 0xC7, 0x2B, 0x46, 0x0C, 0xB1, 0x80, 0x8A, 0x00, 0xEB, 0xCF, 0x83, 0xC2, 0x60, 0xFF, 0xD5, 0x87, 0x5E, 0x10, 0x73, 0x0D, 0x83, 0xC2, 0x30, 0xFF, 0xD5, 0x87, 0x5E, 0x14, 0x73, 0x03, 0x87, 0x5E, 0x18, 0x3C, 0x07, 0xB0, 0x08, 0x72, 0x02, 0xB0, 0x0B, 0x50, 0x53, 0x8D, 0x96, 0x7C, 0x07, 0x00, 0x00, 0xFF, 0x55, 0xD0, 0x5B, 0x91, 0xEB, 0x77, 0x3C, 0x07, 0xB0, 0x07, 0x72, 0x02, 0xB0, 0x0A, 0x50, 0x87, 0x5E, 0x10, 0x87, 0x5E, 0x14, 0x89, 0x5E, 0x18, 0x8D, 0x96, 0xC4, 0x0B, 0x00, 0x00, 0xFF, 0x55, 0xD0, 0x50, 0x48 }))
|
||||
return "ActiveMARK 5.x -> Trymedia Systems,Inc. (h) (Unconfirmed - Please report to us on Github)";
|
||||
}
|
||||
|
||||
// Get the .data section strings, if they exist
|
||||
var strs = pex.GetLastSectionStrings(".data");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("MPRMMGVA"))
|
||||
&& strs.Any(s => s.Contains("This application cannot run with an active debugger in memory.")))
|
||||
{
|
||||
return "ActiveMARK 6.x";
|
||||
}
|
||||
}
|
||||
|
||||
// Get "REGISTRY, AMINTERNETPROTOCOL" resource items
|
||||
var resources = pex.FindResourceByNamedType("REGISTRY, AMINTERNETPROTOCOL");
|
||||
if (resources.Any())
|
||||
{
|
||||
bool match = resources
|
||||
.Select(r => r == null ? string.Empty : Encoding.ASCII.GetString(r))
|
||||
.Any(r => r.Contains("ActiveMARK"));
|
||||
if (match)
|
||||
return "ActiveMARK";
|
||||
}
|
||||
|
||||
// Get the overlay data, if it exists
|
||||
if (pex.OverlayStrings != null)
|
||||
{
|
||||
if (pex.OverlayStrings.Any(s => s.Contains("TMSAMVOH")))
|
||||
return "ActiveMARK";
|
||||
}
|
||||
|
||||
// Get the last .bss section strings, if they exist
|
||||
strs = pex.GetLastSectionStrings(".bss");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("TMSAMVOF")))
|
||||
return "ActiveMARK";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,30 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.PE;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BurnOutSharp.Matching;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Content;
|
||||
using SabreTools.Matching.Paths;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BurnOutSharp.ProtectionType
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// AegiSoft License Manager was made AegiSoft, which was later bought by Real Networks, the makes of RealArcade (https://www.crunchbase.com/organization/aegisoft).
|
||||
/// It allowed publishers to give users a time-based free trial of software.
|
||||
/// Based on "Asc006.exe", AegiSoft License Manager may also have been referred to as "Software-On-Demand License Manager", or it may just be a distinct component of the larger product.
|
||||
/// Based on "Asc005.dll" and "Asc006.exe", AegiSoft License Manager may also have been referred to as "Software-On-Demand License Manager", or it may just be a distinct component of the larger product.
|
||||
/// The single sample investigated was only able to run on Windows 9x (Redump entry 73521/IA item "Nova_HoyleCasino99USA").
|
||||
/// Based on the packaging from IA item "Nova_HoyleCasino99USA", it seems that additional software from "www.1-800-software.com" is likely to be protected with AegiSoft License Manager or other DRM.
|
||||
/// References and further information:
|
||||
/// https://pitchbook.com/profiles/company/118805-59
|
||||
/// https://web.archive.org/web/19990417191351/http://www.aegisoft.com:80/
|
||||
/// </summary>
|
||||
public class AegiSoft : IPathCheck, IPortableExecutableCheck
|
||||
public class AegiSoft : IExecutableCheck<PortableExecutable>, IPathCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex?.SectionTable;
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
@@ -33,22 +35,32 @@ namespace BurnOutSharp.ProtectionType
|
||||
// "Asc005.dll" has the Product Name "OrderWizard Dynamic Link Library".
|
||||
// "Asc006.exe" has the Product Name "AGENT Application".
|
||||
|
||||
// Get the .data section, if it exists
|
||||
if (pex.DataSectionRaw != null)
|
||||
// These are possibly identifying export name table strings
|
||||
// "Asc001.dll" has the strings "AscCheck" and "AscInstall"
|
||||
// "Asc002.dll" has the string "AscActivate"
|
||||
|
||||
// Get string table resources
|
||||
var resource = pex.FindStringTableByEntry("AegiSoft License Manager");
|
||||
if (resource.Any())
|
||||
return "AegiSoft License Manager";
|
||||
|
||||
// Get the .data/DATA section, if it exists
|
||||
var dataSectionRaw = pex.GetFirstSectionData(".data") ?? pex.GetFirstSectionData("DATA");
|
||||
if (dataSectionRaw != null)
|
||||
{
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
// Found in "Asc001.dll", "Asc002.dll", "Asc003.dll", "Asc005.dll", "Asc006.exe", and "AscLM.cpl" (Redump entry 73521/IA item "Nova_HoyleCasino99USA").
|
||||
// ÿÿÿÿ\\.\ASCLM
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x5C, 0x5C, 0x2E, 0x5C,
|
||||
0x41, 0x53, 0x43, 0x4C, 0x4D
|
||||
}, "AegiSoft License Manager"),
|
||||
};
|
||||
|
||||
string match = MatchUtil.GetFirstMatch(file, pex.DataSectionRaw, matchers, includeDebug);
|
||||
if (!string.IsNullOrWhiteSpace(match))
|
||||
var match = MatchUtil.GetFirstMatch(file, dataSectionRaw, matchers, includeDebug);
|
||||
if (!string.IsNullOrEmpty(match))
|
||||
return match;
|
||||
}
|
||||
|
||||
@@ -56,14 +68,14 @@ namespace BurnOutSharp.ProtectionType
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
public IEnumerable<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Found in Redump entry 73521/IA item "Nova_HoyleCasino99USA".
|
||||
new PathMatchSet(new PathMatch("AscLM.cpl", useEndsWith: true), "AegiSoft License Manager"),
|
||||
new PathMatchSet(new PathMatch("AscLM.vxd", useEndsWith: true), "AegiSoft License Manager"),
|
||||
new PathMatchSet(new PathMatch("AscLMd.vxd", useEndsWith: true), "AegiSoft License Manager"),
|
||||
new(new FilePathMatch("AscLM.cpl"), "AegiSoft License Manager"),
|
||||
new(new FilePathMatch("AscLM.vxd"), "AegiSoft License Manager"),
|
||||
new(new FilePathMatch("AscLMd.vxd"), "AegiSoft License Manager"),
|
||||
|
||||
// There are a few other files present, but the file names on their own may be too overmatching. Due to the small sample size, it's not sure if these files are always present together.
|
||||
// These files are "Asc001.dll", "Asc002.dll", "Asc003.dll", "Asc005.dll", and "Asc006.exe" (Found in Redump entry 73521/IA item "Nova_HoyleCasino99USA").
|
||||
@@ -73,17 +85,22 @@ namespace BurnOutSharp.ProtectionType
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string CheckFilePath(string path)
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Found in Redump entry 73521/IA item "Nova_HoyleCasino99USA".
|
||||
new PathMatchSet(new PathMatch("AscLM.cpl", useEndsWith: true), "AegiSoft License Manager"),
|
||||
new PathMatchSet(new PathMatch("AscLM.vxd", useEndsWith: true), "AegiSoft License Manager"),
|
||||
new PathMatchSet(new PathMatch("AscLMd.vxd", useEndsWith: true), "AegiSoft License Manager"),
|
||||
new(new FilePathMatch("AscLM.cpl"), "AegiSoft License Manager"),
|
||||
new(new FilePathMatch("AscLM.vxd"), "AegiSoft License Manager"),
|
||||
new(new FilePathMatch("AscLMd.vxd"), "AegiSoft License Manager"),
|
||||
|
||||
// There are a few other files present, but the file names on their own may be too overmatching. Due to the small sample size, it's not sure if these files are always present together.
|
||||
// These files are "Asc001.dll", "Asc002.dll", "Asc003.dll", "Asc005.dll", and "Asc006.exe" (Found in Redump entry 73521/IA item "Nova_HoyleCasino99USA").
|
||||
|
||||
// The "DATA.TAG" file in the "AgeiSoft" folder is an INI that includes:
|
||||
// Company=AegiSoft Corporation
|
||||
// Application=AegiSoft License Manager
|
||||
// Version=2.1
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace BurnOutSharp.ProtectionType
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class AlphaAudio
|
||||
{
|
||||
@@ -1,9 +1,9 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BurnOutSharp.Matching;
|
||||
using System.Collections.Generic;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Paths;
|
||||
|
||||
namespace BurnOutSharp.ProtectionType
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// Alpha-DVD is a DVD-Video copy protection created by SETTEC.
|
||||
@@ -16,22 +16,22 @@ namespace BurnOutSharp.ProtectionType
|
||||
public class AlphaDVD : IPathCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
public IEnumerable<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new PathMatchSet(new PathMatch("PlayDVD.exe", useEndsWith: true), "Alpha-DVD (Unconfirmed - Please report to us on Github)"),
|
||||
new(new FilePathMatch("PlayDVD.exe"), "Alpha-DVD (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string CheckFilePath(string path)
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new PathMatchSet(new PathMatch("PlayDVD.exe", useEndsWith: true), "Alpha-DVD (Unconfirmed - Please report to us on Github"),
|
||||
new(new FilePathMatch("PlayDVD.exe"), "Alpha-DVD (Unconfirmed - Please report to us on Github"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
92
BinaryObjectScanner/Protection/AlphaROM.cs
Normal file
92
BinaryObjectScanner/Protection/AlphaROM.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// Alpha-ROM is a form of copy protection created by SETTEC. It is known to make use of twin sectors as well as region locking.
|
||||
/// Later forms of Alpha-ROM appear to be digital only, and it's currently unsure what forms of protection the digital only version includes, except that it does make use of region locking.
|
||||
/// It seems that Alpha-ROM was used in Visual Novels using certain game engines, most notably RealLive and Siglus (https://forums.fuwanovel.net/topic/20927-cannot-crack-siglus-engine-with-alpharom/).
|
||||
/// Not every Siglus engine game uses Alpha-ROM (Source: https://sample9.dmm.co.jp/digital/pcgame/vsat_0263/vsat_0263t.zip {Official trial mirror}).
|
||||
/// Not every RealLive engine game uses Alpha-ROM (Source: IA item "Kanon_Standard_Edition_Japan").
|
||||
/// Alpha-ROM also seems to have made use of something called "Alpha-DPS" for non-executable data files (http://www.gonsuke.co.jp/protect.html).
|
||||
/// Example of Alpha-ROM (official trial download mirrors):
|
||||
/// (Siglus Engine)
|
||||
/// http://suezou.dyndns.org/dl2018/key/summer_pokets/Summer_Pockets_trial.zip
|
||||
/// http://mirror.studio-ramble.com/upload/300/201103/RewriteTE_Ver200.zip
|
||||
/// http://suezou.dyndns.org/dl2012/tone-works/hatsukoi1-1/hatsukoi_tr_web.zip
|
||||
/// (RealLive Engine)
|
||||
/// http://suezou.dyndns.org/dl2020/hadashi/princess_heart_link/phl_trial.exe
|
||||
/// https://archive.org/details/little-busters-regular-edition-iso-only-2007
|
||||
/// Games that may have Alpha-ROM:
|
||||
/// http://cpdb.kemuri-net.com/ (Protection database that includes many different protections, including Alpha-ROM).
|
||||
/// https://w.atwiki.jp/tirasinoura/pages/3.html (List of games with Alpha-ROM, and explains some version differences).
|
||||
/// https://vndb.org/r?f=fwSiglusEngine- (VNs made with an engine known to use Alpha-ROM).
|
||||
/// https://vndb.org/r?f=fwRealLive- (VNs made with an engine known to use Alpha-ROM).
|
||||
/// References and further information:
|
||||
/// http://hhg.sakura.ne.jp/cd-dvd/dust/alpha/alpha_index.htm
|
||||
/// https://www.weblio.jp/content/Alpha-ROM
|
||||
/// https://ameblo.jp/michael-j-fox/entry-10046574609.html
|
||||
/// http://s2000.yokinihakarae.com/sub03-10-2(DVD).html
|
||||
/// https://www.cdmediaworld.com/hardware/cdrom/cd_protections_alpha.shtml
|
||||
/// Special thanks to Bestest for researching this protection and helping make further improvements possible!
|
||||
/// </summary>
|
||||
|
||||
// TODO: Alternative string possibilities:
|
||||
// - \AlphaDiscLog.txt
|
||||
// - \SETTEC
|
||||
// - AlphaROM
|
||||
// - SETTEC0000SETTEC1111
|
||||
// - SOFTWARE\SETTEC
|
||||
// TODO: Are there version numbers?
|
||||
public class AlphaROM : IExecutableCheck<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// TODO: Add support for detecting Alpha-ROM found in older games made with the RealLive engine.
|
||||
// TODO: Add version detection for Alpha-ROM.
|
||||
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("\\SETTEC")))
|
||||
return "Alpha-ROM";
|
||||
|
||||
if (strs.Any(s => s.Contains("SETTEC0000")))
|
||||
return "Alpha-ROM";
|
||||
}
|
||||
|
||||
// Get the .rdata section strings, if they exist
|
||||
strs = pex.GetFirstSectionStrings(".rdata");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("This Game is Japan Only")))
|
||||
return "Alpha-ROM";
|
||||
// Found in "Filechk.exe" in Redump entry 115358.
|
||||
if (strs.Any(s => s.Contains("AlphaCheck.exe")))
|
||||
return "Alpha-ROM";
|
||||
// Found in "Uninstall.exe" in Redump entry 115358.
|
||||
if (strs.Any(s => s.Contains("AlphaCheck.dat")))
|
||||
return "Alpha-ROM";
|
||||
}
|
||||
|
||||
// Get the overlay data, if it exists
|
||||
if (pex.OverlayStrings != null)
|
||||
{
|
||||
// Found in Redump entry 84122.
|
||||
if (pex.OverlayStrings.Any(s => s.Contains("SETTEC0000")))
|
||||
return "Alpha-ROM";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
BinaryObjectScanner/Protection/Armadillo.cs
Normal file
60
BinaryObjectScanner/Protection/Armadillo.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// Armadillo was a license manager, packer, and DRM by "The Silicon Realm Toolworks": https://web.archive.org/web/20030203101931/http://www.siliconrealms.com/armadillo.shtml
|
||||
/// They were later bought by Digital River, and updated their website: https://web.archive.org/web/20031203021152/http://www.siliconrealms.com/armadillo.shtml
|
||||
/// A new updated version named "SoftwarePassport" was released: https://web.archive.org/web/20040423044529/http://siliconrealms.com/softwarepassport/popup.shtml
|
||||
/// Later copy of the website, with SoftwarePassport being named instead of Armadillo: https://web.archive.org/web/20040804032608/http://www.siliconrealms.com/armadillo.shtml
|
||||
/// It appears as though both Armadillo and SoftwarePassport were being released at the same time, possibly with Armadillo acting as the core component and SoftwarePassport being supplementary: https://web.archive.org/web/20050619013312/http://siliconrealms.com/srt-news.shtml
|
||||
/// Digital River itself also advertised Armadillo at first: https://web.archive.org/web/20040116043029/http://www.digitalriver.com:80/corporate/solutions06.shtml
|
||||
/// But then only advertised SoftwarePassport once it was released: https://web.archive.org/web/20040604065907/http://www.digitalriver.com/corporate/solutions06.shtml
|
||||
/// </summary>
|
||||
|
||||
// TODO: Add extraction
|
||||
// TODO: Add version checking, if possible
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
|
||||
public class Armadillo : IExtractableExecutable<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.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
|
||||
if (pex.SectionNames != null)
|
||||
{
|
||||
foreach (var sectionName in pex.SectionNames.Where(s => s != null && s.EndsWith("1")))
|
||||
{
|
||||
// Get the section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(sectionName);
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("ARMDEBUG")))
|
||||
return "Armadillo";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string file, PortableExecutable pex, string outDir, bool includeDebug)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
BinaryObjectScanner/Protection/Bitpool.cs
Normal file
55
BinaryObjectScanner/Protection/Bitpool.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Paths;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// Bitpool is a copy protection found most commonly in German releases.
|
||||
/// It always has errors present on the disc (either between 1-4, or between 1,000-10,000+ depending on generation), and makes use of padded dummy files to prevent copying.
|
||||
/// <see href="https://github.com/TheRogueArchivist/DRML/blob/main/entries/Bitpool/Bitpool.md"/>
|
||||
/// </summary>
|
||||
public class Bitpool : IPathCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new(new FilePathMatch("bitpool.rsc"), "Bitpool"),
|
||||
new(new FilePathMatch("CD.IDX"), "Bitpool"),
|
||||
|
||||
// Completely empty file present on multiple discs with Bitpool (Redump entries 52626 and 50229).
|
||||
new(new FilePathMatch("LEADOUT.OFS"), "Bitpool"),
|
||||
|
||||
// A set of 4 identically sized (within the same game, not between games), corrupted/padded files present in several games (Redump entries 31782 and 35476).
|
||||
// Both examples with only having the first letter uppercase and as the whole file name being uppercase have been seen.
|
||||
new(
|
||||
[
|
||||
new FilePathMatch("Crc_a"),
|
||||
new FilePathMatch("Crc_b"),
|
||||
new FilePathMatch("Crc_c"),
|
||||
new FilePathMatch("Crc_d"),
|
||||
], "Bitpool"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new(new FilePathMatch("bitpool.rsc"), "Bitpool"),
|
||||
new(new FilePathMatch("CD.IDX"), "Bitpool"),
|
||||
|
||||
// Completely empty file present on multiple discs with Bitpool (Redump entries 52626 and 50229).
|
||||
new(new FilePathMatch("LEADOUT.OFS"), "Bitpool"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
160
BinaryObjectScanner/Protection/ByteShield.cs
Normal file
160
BinaryObjectScanner/Protection/ByteShield.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Paths;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// ByteShield, Inc. (https://web.archive.org/web/20070216191623/http://www.byteshield.net/) was founded in 2004 (https://www.apollo.io/companies/ByteShield--Inc-/54a1357069702d4494ab9b00).
|
||||
/// There is a website seemingly belonging to them that's been archived as early as 2004, but there doesn't appear to be anything useful on it (https://web.archive.org/web/20040615001350/http://byteshield.com/).
|
||||
/// The ByteShield DRM itself is online activation based, using randomly generated activation codes it refers to as DACs (https://web.archive.org/web/20080921231346/http://www.byteshield.net/byteshield_whitepaper_0005.pdf).
|
||||
/// It appears that ByteShield advertised itself around online web forums (https://gamedev.net/forums/topic/508082-net-copy-protection-c/508082/ and https://cboard.cprogramming.com/tech-board/106642-how-add-copy-protection-software.html).
|
||||
/// Patent relating to ByteShield: https://patentimages.storage.googleapis.com/ed/76/c7/d98a56aeeca2e9/US7716474.pdf and https://patents.google.com/patent/US20100212028.
|
||||
///
|
||||
/// Games known to use it:
|
||||
/// Line Rider 2: Unbound (https://fileforums.com/showthread.php?t=86909).
|
||||
/// Football Manager 2011 (https://community.sigames.com/forums/topic/189163-for-those-of-you-struggling-with-byteshield-activation-issues/).
|
||||
///
|
||||
/// Publishers known to use it:
|
||||
/// PAN Vision (http://web.archive.org/web/20120413160122/http://www.byteshield.net/ -> Company -> Milestones).
|
||||
/// JIAN (http://web.archive.org/web/20120413160122/http://www.byteshield.net/ -> Company -> Milestones).
|
||||
/// GamersGate (http://web.archive.org/web/20120413160122/http://www.byteshield.net/ -> Company -> Milestones).
|
||||
/// Akella (http://web.archive.org/web/20120413160122/http://www.byteshield.net/ -> Customers).
|
||||
/// All Interactive Distributuion (http://web.archive.org/web/20120413160122/http://www.byteshield.net/ -> Customers).
|
||||
/// Beowulf (http://web.archive.org/web/20120413160122/http://www.byteshield.net/ -> Customers).
|
||||
/// CroVortex (http://web.archive.org/web/20120413160122/http://www.byteshield.net/ -> Customers).
|
||||
/// N3V Games (http://web.archive.org/web/20120413160122/http://www.byteshield.net/ -> Customers).
|
||||
/// OnePlayS (http://web.archive.org/web/20120413160122/http://www.byteshield.net/ -> Customers).
|
||||
/// YAWMA (http://web.archive.org/web/20120413160122/http://www.byteshield.net/ -> Customers).
|
||||
///
|
||||
/// Further links and resources:
|
||||
/// https://www.cdmediaworld.com/hardware/cdrom/cd_protections_byteshield.shtml
|
||||
/// https://www.bcs.org/articles-opinion-and-research/is-there-anything-like-acceptable-drm/
|
||||
/// https://forums.auran.com/trainz/showthread.php?106673-Trainz-and-DRM/page22
|
||||
/// https://www.auran.com/planetauran/byteshield_drm.php
|
||||
/// https://www.ftc.gov/sites/default/files/documents/public_comments/ftc-town-hall-address-digital-rights-management-technologies-event-takes-place-wednesday-march-25/539814-00707.pdf
|
||||
/// https://www.gamesindustry.biz/byteshield-drm-system-now-protecting-over-200-games
|
||||
/// </summary>
|
||||
public class ByteShield : IExecutableCheck<PortableExecutable>, IPathCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
var name = pex.FileDescription;
|
||||
if (name?.Equals("ByteShield Client") == true)
|
||||
return $"ByteShield Activation Client {pex.GetInternalVersion()}";
|
||||
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
name = pex.InternalName;
|
||||
if (name?.Equals("ByteShield") == true)
|
||||
return $"ByteShield Activation Client {pex.GetInternalVersion()}";
|
||||
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
name = pex.OriginalFilename;
|
||||
if (name?.Equals("ByteShield.EXE") == true)
|
||||
return $"ByteShield Activation Client {pex.GetInternalVersion()}";
|
||||
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
name = pex.ProductName;
|
||||
if (name?.Equals("ByteShield Client") == true)
|
||||
return $"ByteShield Activation Client {pex.GetInternalVersion()}";
|
||||
|
||||
// Found in "ByteShield.dll" in Redump entry 6236
|
||||
name = pex.Model.ExportTable?.ExportDirectoryTable?.Name;
|
||||
if (name?.Equals("ByteShield Client") == true)
|
||||
return "ByteShield Component Module";
|
||||
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
var stMatch = pex.FindStringTableByEntry("ByteShield");
|
||||
if (stMatch.Any())
|
||||
return $"ByteShield Activation Client {pex.GetInternalVersion()}";
|
||||
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
var dbMatch = pex.FindDialogByTitle("About ByteShield");
|
||||
if (dbMatch.Any())
|
||||
return "ByteShield";
|
||||
|
||||
// TODO: See if the version number is anywhere else
|
||||
// TODO: Parse the version number out of the dialog box item
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
dbMatch = pex.FindDialogBoxByItemTitle("ByteShield Version 1.0");
|
||||
if (dbMatch.Any())
|
||||
return "ByteShield";
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
if (strs.Any(s => s?.Contains("ByteShield") == true))
|
||||
return "ByteShield";
|
||||
}
|
||||
|
||||
// Get the .rdata section strings, if they exist
|
||||
strs = pex.GetFirstSectionStrings(".rdata");
|
||||
if (strs != null)
|
||||
{
|
||||
// Found in "ByteShield.dll" in Redump entry 6236
|
||||
if (strs.Any(s => s?.Contains("Byte|Shield") == true))
|
||||
return "ByteShield Component Module";
|
||||
|
||||
// Found in "ByteShield.dll" in Redump entry 6236
|
||||
else if (strs.Any(s => s?.Contains("Byteshield0") == true))
|
||||
return "ByteShield Component Module";
|
||||
|
||||
// Found in "ByteShield.dll" in Redump entry 6236
|
||||
else if (strs.Any(s => s?.Contains("ByteShieldLoader") == true))
|
||||
return "ByteShield Component Module";
|
||||
}
|
||||
|
||||
// Get the .ret section strings, if they exist
|
||||
strs = pex.GetFirstSectionStrings(".ret");
|
||||
if (strs != null)
|
||||
{
|
||||
// TODO: Figure out if this specifically indicates if the file is encrypted
|
||||
// Found in "LineRider2.bbz" in Redump entry 6236
|
||||
if (strs.Any(s => s?.Contains("ByteShield") == true))
|
||||
return "ByteShield";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
{
|
||||
// TODO: Investigate reference to "bbz650.tmp" in "Byteshield.dll" (Redump entry 6236)
|
||||
// Files with the ".bbz" extension are associated with ByteShield, but the extenstion is known to be used in other places as well.
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new(new FilePathMatch("Byteshield.dll"), "ByteShield Component Module"),
|
||||
new(new FilePathMatch("Byteshield.ini"), "ByteShield"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
// TODO: Investigate reference to "bbz650.tmp" in "Byteshield.dll" (Redump entry 6236)
|
||||
// Files with the ".bbz" extension are associated with ByteShield, but the extenstion is known to be used in other places as well.
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new(new FilePathMatch("Byteshield.dll"), "ByteShield Component Module"),
|
||||
new(new FilePathMatch("Byteshield.ini"), "ByteShield"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
BinaryObjectScanner/Protection/CDCheck.cs
Normal file
33
BinaryObjectScanner/Protection/CDCheck.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class CDCheck : IExecutableCheck<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var name = pex.Comments;
|
||||
if (name?.Contains("CDCheck utlity for Microsoft Game Studios") == true)
|
||||
return "Microsoft Game Studios CD Check";
|
||||
|
||||
// To broad to be of use
|
||||
//name = pex.InternalName;
|
||||
//if (name?.Contains("CDCheck") == true)
|
||||
// return "Microsoft Game Studios CD Check";
|
||||
|
||||
// To broad to be of use
|
||||
//name = pex.OriginalFilename;
|
||||
//if (name?.Contains("CDCheck.exe") == true)
|
||||
// return "Microsoft Game Studios CD Check";
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
244
BinaryObjectScanner/Protection/CDDVDCops.cs
Normal file
244
BinaryObjectScanner/Protection/CDDVDCops.cs
Normal file
@@ -0,0 +1,244 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Content;
|
||||
using SabreTools.Matching.Paths;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// TODO: Investigate "Cops Copylock II" (https://www.cbmstuff.com/forum/showthread.php?tid=488).
|
||||
/// TODO: Investigate additional products mentioned on the Link Data Security website (https://www.linkdatasecurity.com/index.htm#/protection-products/overview).
|
||||
/// `AgentHugo.exe`
|
||||
/// Embedded PE executable in one of the NE sections
|
||||
/// `AgentHugo.exe` / `NE.EXE` (1.46) / `NETINST.EXE` (1.48) / `NETINST.QZ_`
|
||||
/// Embedded PKZIP archive that may contain the CD-Cops files
|
||||
/// `CDCOPS.DLL` (1.46) / `CDCOPS.DLL` (1.48)
|
||||
/// `WINCOPS.INI`
|
||||
///
|
||||
/// TODO: Investigate if "DVD-Cops" is a separate product, or simply what CD-Cops is referred to when used on a DVD.
|
||||
///
|
||||
/// Known versions of CD-Cops:
|
||||
/// * 1.08 (Unconfirmed) (Redump entry 84517).
|
||||
/// * 1,13[sic] (Confirmed) ("FGP.exe" in IA item "flaklypa-grand-prix-gullutgave-2cd"/Redump entries 108167-108168 patched with https://web.archive.org/web/20040307124358/http://www.caprino.no:80/download/fgpgold_upd4.exe).
|
||||
/// * 1.21 (Unconfirmed) (Redump entry 91713).
|
||||
/// * 1,22[sic] (Confirmed) ("FGP.exe" in IA item "flaklypa-grand-prix-gullutgave-2cd"/Redump entries 108167-108168 patched with https://web.archive.org/web/20030430194917/http://www.caprino.no:80/download/fgpgold_upd2.exe).
|
||||
/// * 1,28[sic] (Confirmed) ("RunMenu.exe" in IA item "Faculty_Edition_People_Problems_and_Power_by_Joseph_Unekis_Textbytes").
|
||||
/// * 1,31[sic] (Confirmed) ("FGP.exe" in IA item "flaklypa-grand-prix-gullutgave-2cd"/Redump entries 108167-108168).
|
||||
/// * 1.31 (Confirmed) ("FGP.exe" in IA item "flaklypa-grand-prix-gullutgave-2cd"/Redump entries 108167-108168 patched with Patch 11).
|
||||
/// * 1.46 (Confirmed) ("FGP.exe" in IA item "flaklypa-grand-prix-gullutgave-2cd"/Redump entries 108167-108168 patched with https://web.archive.org/web/20210103064517/http://www.caprino.no/download/FGPGOLD_UPD12.exe)
|
||||
/// * 1,63[sic] (Confirmed) ("FGP.exe" in IA item "flaklypa-grand-prix-gullutgave-2cd"/Redump entries 108167-108168 patched with https://web.archive.org/web/20060926082522/http://www.caprino.no:80/download/fgpgold_upd7.exe).
|
||||
/// * 1.72 (Confirmed) ("h3blade.exe" in Redump entry 85077).
|
||||
/// * 1.73 (Confirmed) ("WETFLIPPER.EXE" in IA item "LULA_Erotic_Pinball_-_Windows95_Eng).
|
||||
/// * 1,81[sic] (Confirmed) ("FGP.exe" in IA item "flaklypa-grand-prix-gullutgave-2cd"/Redump entries 108167-108168 patched with https://web.archive.org/web/20030308040529/http://www.caprino.no:80/download/fgpgold_upd1.exe).
|
||||
/// * 2.03 (Confirmed) ("HyperBowl.exe" in IA item "hyperbowl_20190626").
|
||||
///
|
||||
/// Known versions of DVD-Cops:
|
||||
/// * 1.69 (Confirmed) ("FGP.exe" in IA item "flaklypa-grand-prix-dvd"/Redump entry 108169).
|
||||
///
|
||||
/// Known samples of CD-Cops include:
|
||||
/// * IA item "der-brockhaus-multimedial-2002-premium".
|
||||
/// * IA item "der-brockhaus-multimedial-2003-premium".
|
||||
/// * IA item "SCIENCESENCYCLOPEDIAV2.0ARISSCD1".
|
||||
/// * IA item "SCIENCESENCYCLOPEDIAV2.0ARISSCD2".
|
||||
/// * IA item "Triada_Russian_DVD_Complete_Collection_of_Erotic_Games".
|
||||
/// * IA item "LULA_Erotic_Pinball_-_Windows95_Eng".
|
||||
/// * IA item "flaklypa-grand-prix-gullutgave-2cd"/Redump entries 108167-108168.
|
||||
/// * Patches for "flaklypa-grand-prix-gullutgave-2cd"/Redump entries 108167-108168, found at https://web.archive.org/web/*/http://www.caprino.no/download/* (FGPGOLD_UPD files).
|
||||
/// * IA item "hyperbowl_20190626"/"hyperbowl-arcade-edition".
|
||||
/// * Redump entries 51403(?), 84517, and 85077.
|
||||
///
|
||||
/// Known samples of DVD-Cops include:
|
||||
/// * IA item "flaklypa-grand-prix-dvd"/Redump entry 108169.
|
||||
///
|
||||
/// Known samples of WEB-Cops include:
|
||||
/// * https://web.archive.org/web/20120616074941/http://icm.games.tucows.com/files2/HyperDemo-109a.exe
|
||||
///
|
||||
/// A sample of CD-Cops that makes use of encrypted PDFs (LDSCRYPT) can be found in IA item "Faculty_Edition_People_Problems_and_Power_by_Joseph_Unekis_Textbytes".
|
||||
///
|
||||
/// List of applications that have CD/DVD/WEB-Cops relating to a Windows update: https://www.betaarchive.com/wiki/index.php/Microsoft_KB_Archive/924867
|
||||
/// </summary>
|
||||
|
||||
public class CDDVDCops : IContentCheck, IExecutableCheck<NewExecutable>, IExecutableCheck<PortableExecutable>, IPathCheck
|
||||
{
|
||||
// TODO: Investigate reference to "CD32COPS.DLL" in "WETFLIPP.QZ_" in IA item "Triada_Russian_DVD_Complete_Collection_of_Erotic_Games".
|
||||
/// <inheritdoc/>
|
||||
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
{
|
||||
// TODO: Obtain a sample to find where this string is in a typical executable
|
||||
if (includeDebug)
|
||||
{
|
||||
var contentMatchSets = new List<ContentMatchSet>
|
||||
{
|
||||
// TODO: Remove from here once it's confirmed that no PE executables contain this string
|
||||
// CD-Cops, ver.
|
||||
new(new byte?[]
|
||||
{
|
||||
0x43, 0x44, 0x2D, 0x43, 0x6F, 0x70, 0x73, 0x2C,
|
||||
0x20, 0x20, 0x76, 0x65, 0x72, 0x2E, 0x20
|
||||
}, GetVersion, "CD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
|
||||
// // DVD-Cops, ver.
|
||||
new(new byte?[]
|
||||
{
|
||||
0x44, 0x56, 0x44, 0x2D, 0x43, 0x6F, 0x70, 0x73,
|
||||
0x2C, 0x20, 0x20, 0x76, 0x65, 0x72, 0x2E, 0x20
|
||||
}, GetVersion, "DVD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchSets, includeDebug);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
{
|
||||
// TODO: Don't read entire file
|
||||
var data = nex.ReadArbitraryRange();
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
// TODO: Figure out what NE section this lives in
|
||||
var neMatchSets = new List<ContentMatchSet>
|
||||
{
|
||||
// CD-Cops, ver.
|
||||
// Found in "h3blade.exe" in Redump entry 85077.
|
||||
new(new byte?[]
|
||||
{
|
||||
0x43, 0x44, 0x2D, 0x43, 0x6F, 0x70, 0x73, 0x2C,
|
||||
0x20, 0x20, 0x76, 0x65, 0x72, 0x2E, 0x20
|
||||
}, GetVersion, "CD-Cops"),
|
||||
};
|
||||
|
||||
var match = MatchUtil.GetFirstMatch(file, data, neMatchSets, includeDebug);
|
||||
if (!string.IsNullOrEmpty(match))
|
||||
return match;
|
||||
|
||||
// Check the imported-name table
|
||||
// Found in "h3blade.exe" in Redump entry 85077.
|
||||
bool importedNameTableEntries = nex.Model.ImportedNameTable?
|
||||
.Select(kvp => kvp.Value)
|
||||
.Select(inte => inte?.NameString == null ? string.Empty : Encoding.ASCII.GetString(inte.NameString))
|
||||
.Any(s => s.Contains("CDCOPS")) ?? false;
|
||||
if (importedNameTableEntries)
|
||||
return "CD-Cops";
|
||||
|
||||
// Check the nonresident-name table
|
||||
// Found in "CDCOPS.DLL" in Redump entry 85077.
|
||||
bool nonresidentNameTableEntries = nex.Model.NonResidentNameTable?
|
||||
.Select(nrnte => nrnte?.NameString == null ? string.Empty : Encoding.ASCII.GetString(nrnte.NameString))
|
||||
.Any(s => s.Contains("CDcops assembly-language DLL")) ?? false;
|
||||
if (nonresidentNameTableEntries)
|
||||
return "CD-Cops";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the stub executable data, if it exists
|
||||
if (pex.StubExecutableData != null)
|
||||
{
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
// WEBCOPS
|
||||
// Found in "HyperBowl.C_S" in https://web.archive.org/web/20120616074941/http://icm.games.tucows.com/files2/HyperDemo-109a.exe.
|
||||
new(new byte?[]
|
||||
{
|
||||
0x57, 0x45, 0x42, 0x43, 0x4F, 0x50, 0x53
|
||||
}, "WEB-Cops")
|
||||
};
|
||||
|
||||
var match = MatchUtil.GetFirstMatch(file, pex.StubExecutableData, matchers, includeDebug);
|
||||
if (!string.IsNullOrEmpty(match))
|
||||
return match;
|
||||
}
|
||||
|
||||
// Get the .grand section, if it exists
|
||||
// Found in "AGENTHUG.QZ_" in Redump entry 84517 and "h3blade.QZ_" in Redump entry 85077.
|
||||
bool grandSection = pex.ContainsSection(".grand", exact: true);
|
||||
if (grandSection)
|
||||
return "CD/DVD/WEB-Cops";
|
||||
|
||||
// Get the UNICops section, if it exists
|
||||
// Found in "FGP.exe" in IA item "flaklypa-grand-prix-dvd"/Redump entry 108169.
|
||||
bool UNICopsSection = pex.ContainsSection("UNICops", exact: true);
|
||||
if (UNICopsSection)
|
||||
return "UNI-Cops";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
{
|
||||
// TODO: Original had "CDCOPS.DLL" required and all the rest in a combined OR
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// A 400+ MB file called "WASTE.DAT" that is fully 00 padded can be found in IA item "Faculty_Edition_People_Problems_and_Power_by_Joseph_Unekis_Textbytes".
|
||||
// Presumably used to increase the amount of data written to the disc to allow DPM checking to be used for the protection. It's unknown if this file is used on any other protected discs.
|
||||
|
||||
// Found in Redump entry 84517.
|
||||
new(new FilePathMatch("CDCOPS.DLL"), "CD-Cops"),
|
||||
new(new PathMatch(".W_X", matchExact: true, useEndsWith: true), "CD/DVD-Cops"),
|
||||
new(new PathMatch(".QZ_", matchExact: true, useEndsWith: true), "CD/DVD-Cops"),
|
||||
|
||||
new(new PathMatch(".GZ_", matchExact: true, useEndsWith: true), "CD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
new(new PathMatch(".Qz", matchExact: true, useEndsWith: true), "CD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// A 400+ MB file called "WASTE.DAT" that is fully 00 padded can be found in IA item "Faculty_Edition_People_Problems_and_Power_by_Joseph_Unekis_Textbytes".
|
||||
// Presumably used to increase the amount of data written to the disc to allow DPM checking to be used for the protection. It's unknown if this file is used on any other protected discs.
|
||||
|
||||
// Found in Redump entry 84517.
|
||||
new(new FilePathMatch("CDCOPS.DLL"), "CD-Cops"),
|
||||
new(new PathMatch(".W_X", matchExact: true, useEndsWith: true), "CD/DVD-Cops"),
|
||||
new(new PathMatch(".QZ_", matchExact: true, useEndsWith: true), "CD/DVD-Cops"),
|
||||
|
||||
new(new PathMatch(".GZ_", matchExact: true, useEndsWith: true), "CD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
new(new PathMatch(".Qz", matchExact: true, useEndsWith: true), "CD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
|
||||
public static string? GetVersion(string file, byte[]? fileContent, List<int> positions)
|
||||
{
|
||||
// If we have no content
|
||||
if (fileContent == null)
|
||||
return null;
|
||||
|
||||
#if NET20 || NET35 || NET40
|
||||
byte[] versionBytes = new byte[4];
|
||||
Array.Copy(fileContent, positions[0] + 15, versionBytes, 0, 4);
|
||||
char[] version = versionBytes.Select(b => (char)b).ToArray();
|
||||
#else
|
||||
char[] version = new ArraySegment<byte>(fileContent, positions[0] + 15, 4).Select(b => (char)b).ToArray();
|
||||
#endif
|
||||
if (version[0] == 0x00)
|
||||
return string.Empty;
|
||||
|
||||
return new string(version);
|
||||
}
|
||||
}
|
||||
}
|
||||
81
BinaryObjectScanner/Protection/CDGuard.cs
Normal file
81
BinaryObjectScanner/Protection/CDGuard.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Paths;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// CD-Guard is a DRM from Russia that's similar to CD-Cops and may be related to StarForce, meaning it likely needs DPM.
|
||||
/// It may have been developed by Russobit-M, though the same source also says that StarForce was created by the same company as well, which seems unlikely (https://m.linkdatasecurity.com/pnews6.htm).
|
||||
/// Others online have been confused by this as well (https://forum.ixbt.com/topic.cgi?id=31:009712).
|
||||
/// A game referred to as having CD-Guard by http://lastboss.ru/games/RUS/randevu-s-neznakomkoi-2 that was published by Russobit-M is known to have an early version of StarForce (Redump entry 97088).
|
||||
/// The FAQ on the game's official website indicates that StarForce specifically is present (https://web.archive.org/web/20011220224222/http://www.aha.ru/~exe_soft/russian/exesoft.htm).
|
||||
/// It's unknown for sure if there were two separate versions of this game that contained separate protections, or if the game never actually contained CD-Guard, or if CD-Guard was an early name for the StarForce line of products.
|
||||
/// There is a re-release of an earlier game by the same developer that seems to include both CD-Guard and StarForce drivers, with the CD-Guard driver seemingly not used during installation, nor installed onto the system (IA item "pahgeby-he3hakomkou").
|
||||
///
|
||||
/// Additional resources and references:
|
||||
/// https://gamecopyworld.com/games/pc_omikron.shtml
|
||||
/// https://forum.ixbt.com/topic.cgi?id=31:3985
|
||||
/// </summary>
|
||||
public class CDGuard : IExecutableCheck<PortableExecutable>, IPathCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// TODO: Investigate the numerous ".guard" sections present in "Randevu.exe" in Redump entry 97142.
|
||||
|
||||
// Get the export directory table
|
||||
if (pex.Model.ExportTable?.ExportDirectoryTable != null)
|
||||
{
|
||||
// Found in "cdguard.dll" in Redump entry 97142 and IA item "pahgeby-he3hakomkou".
|
||||
bool match = pex.Model.ExportTable.ExportDirectoryTable.Name?.Equals("cdguard.dll", StringComparison.OrdinalIgnoreCase) == true;
|
||||
if (match)
|
||||
return "CD-Guard Copy Protection System";
|
||||
}
|
||||
|
||||
// Get the import directory table
|
||||
if (pex.Model.ImportTable?.ImportDirectoryTable != null)
|
||||
{
|
||||
// Found in "Randevu.exe" in Redump entry 97142.
|
||||
bool match = pex.Model.ImportTable.ImportDirectoryTable.Any(idte => idte?.Name != null && idte.Name.Equals("cdguard.dll", StringComparison.OrdinalIgnoreCase));
|
||||
if (match)
|
||||
return "CD-Guard Copy Protection System";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Found in Redump entry 97142.
|
||||
new(new FilePathMatch("cdguard.dll"), "CD-Guard Copy Protection System"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Found in Redump entry 97142.
|
||||
new(new FilePathMatch("cdguard.dll"), "CD-Guard Copy Protection System"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
BinaryObjectScanner/Protection/CDKey.cs
Normal file
24
BinaryObjectScanner/Protection/CDKey.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class CDKey : IExecutableCheck<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var name = pex.InternalName;
|
||||
if (name?.Equals("CDKey", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return "CD-Key / Serial";
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.PE;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BurnOutSharp.Matching;
|
||||
using System.Collections.Generic;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Content;
|
||||
using SabreTools.Matching.Paths;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BurnOutSharp.ProtectionType
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// CD-Lock (https://www.cdmediaworld.com/hardware/cdrom/cd_protections_dummy_files.shtml) is a copy protection most commonly used in Europe and active from 1998-2001 that seems to have a resemblance to Bitpool in a few ways.
|
||||
@@ -24,25 +25,26 @@ namespace BurnOutSharp.ProtectionType
|
||||
/// Possible false positives include Redump entries 51241, 51373, 54397, 76437.
|
||||
/// Confirmed to be present on Redump entries 24287, 31615, 34448, 35967, 36627, 37700, 37788, 43221, 55788, and 66749.
|
||||
/// </summary>
|
||||
public class CDLock : IPathCheck, IPortableExecutableCheck
|
||||
public class CDLock : IExecutableCheck<PortableExecutable>, IPathCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex?.SectionTable;
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .data section, if it exists
|
||||
if (pex.DataSectionRaw != null)
|
||||
// Get the .data/DATA section, if it exists
|
||||
var dataSectionRaw = pex.GetFirstSectionData(".data") ?? pex.GetFirstSectionData("DATA");
|
||||
if (dataSectionRaw != null)
|
||||
{
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
// Found in game executables protected with CD-Lock (Redump entries 24287 and 31615).
|
||||
// TODO: Check for possible false postives (Redump entry 97942).
|
||||
// 2 + (char)0xF2 + (char)0x02 + (char)0x82 + (char)0xC3 + (char)0xBC + (char)0x0B + $ + (char)0x99 + (char)0xAD + 'C + (char)0xE4 + (char)0x9D + st + (char)0x99 + (char)0xFA + 2$ + (char)0x9D + )4 + (char)0xFF + t
|
||||
new ContentMatchSet(new byte?[]
|
||||
new(new byte?[]
|
||||
{
|
||||
0x32, 0xF2, 0x02, 0x82, 0xC3, 0xBC, 0x0B, 0x24,
|
||||
0x99, 0xAD, 0x27, 0x43, 0xE4, 0x9D, 0x73, 0x74,
|
||||
@@ -51,8 +53,8 @@ namespace BurnOutSharp.ProtectionType
|
||||
}, "CD-Lock"),
|
||||
};
|
||||
|
||||
string match = MatchUtil.GetFirstMatch(file, pex.DataSectionRaw, matchers, includeDebug);
|
||||
if (!string.IsNullOrWhiteSpace(match))
|
||||
var match = MatchUtil.GetFirstMatch(file, dataSectionRaw, matchers, includeDebug);
|
||||
if (!string.IsNullOrEmpty(match))
|
||||
return match;
|
||||
}
|
||||
|
||||
@@ -60,14 +62,14 @@ namespace BurnOutSharp.ProtectionType
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
public IEnumerable<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// TODO: Determine if there's any consistency in the naming of the additional AFP files.
|
||||
|
||||
// Found in every confirmed sample of CD-Lock, generally (but not always) appears to include markers relating to the additional AFP files present (Redump entries 24287 and 31615).
|
||||
new PathMatchSet(new PathMatch("CONFIG.AFP", useEndsWith: true), "CD-Lock"),
|
||||
new(new FilePathMatch("CONFIG.AFP"), "CD-Lock"),
|
||||
|
||||
// There is also a "$$$$$$$$.$$$" file present on some discs, but it isn't known if this is directly related to CD-Lock (Redump entries 37788 and 43221).
|
||||
};
|
||||
@@ -76,14 +78,14 @@ namespace BurnOutSharp.ProtectionType
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string CheckFilePath(string path)
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// TODO: Determine if there's any consistency in the naming of the additional AFP files.
|
||||
|
||||
// Found in every confirmed sample of CD-Lock, generally (but not always) appears to include markers relating to the additional AFP files present (Redump entries 24287 and 31615).
|
||||
new PathMatchSet(new PathMatch("CONFIG.AFP", useEndsWith: true), "CD-Lock"),
|
||||
new(new FilePathMatch("CONFIG.AFP"), "CD-Lock"),
|
||||
|
||||
// There is also a "$$$$$$$$.$$$" file present on some discs, but it isn't known if this is directly related to CD-Lock (Redump entries 37788 and 43221).
|
||||
};
|
||||
63
BinaryObjectScanner/Protection/CDProtector.cs
Normal file
63
BinaryObjectScanner/Protection/CDProtector.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Paths;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// CD-Protector is a form of DRM that allows users to create their own copy protected discs.
|
||||
/// It prevents copying via "Phantom Trax", intended to confuse dumping software, and by obfuscating a specified EXE.
|
||||
/// The official website seems to be https://web.archive.org/web/20000302173822/http://surf.to/nrgcrew.
|
||||
/// The author's site should be https://members.xoom.it/_XOOM/Dudez/index.htm, but no captures of this site appear to be functional.
|
||||
/// Instructions on how this software can be used: https://3dnews.ru/166065
|
||||
/// Download: https://www.cdmediaworld.com/hardware/cdrom/cd_utils_3.shtml#CD-Protector
|
||||
/// TODO: See if any of the older versions of CD-Protector are archived, and check if they need to be detected differently.
|
||||
/// </summary>
|
||||
public class CDProtector : IPathCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// These are the main files used by CD-Protector, which should all be present in every protected disc.
|
||||
// "_cdp16.dll" and "_cdp32.dll" are actually renamed WAV files.
|
||||
// "_cdp32.dat" is actually an archive that contains the original executable.
|
||||
// Another EXE is created, with the name of the original executable. I'm not sure what this executable does, but it appears to be compressed with NeoLite.
|
||||
// TODO: Invesitage if this EXE itself can be detected in any way.
|
||||
new(new FilePathMatch("_cdp16.dat"), "CD-Protector"),
|
||||
new(new FilePathMatch("_cdp16.dll"), "CD-Protector"),
|
||||
new(new FilePathMatch("_cdp32.dat"), "CD-Protector"),
|
||||
new(new FilePathMatch("_cdp32.dll"), "CD-Protector"),
|
||||
|
||||
// This is the "Phantom Trax" file generated by CD-Protector, intended to be burned to a protected CD as an audio track.
|
||||
new(new FilePathMatch("Track#1 - Track#2 Cd-Protector.wav"), "CD-Protector"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// These are the main files used by CD-Protector, which should all be present in every protected disc.
|
||||
// "_cdp16.dll" and "_cdp32.dll" are actually renamed WAV files.
|
||||
// "_cdp32.dat" is actually an archive that contains the original executable.
|
||||
// Another EXE is created, with the name of the original executable. I'm not sure what this executable does, but it appears to be compressed with NeoLite.
|
||||
// TODO: Invesitage if this EXE itself can be detected in any way.
|
||||
new(new FilePathMatch("_cdp16.dat"), "CD-Protector"),
|
||||
new(new FilePathMatch("_cdp16.dll"), "CD-Protector"),
|
||||
new(new FilePathMatch("_cdp32.dat"), "CD-Protector"),
|
||||
new(new FilePathMatch("_cdp32.dll"), "CD-Protector"),
|
||||
|
||||
// This is the "Phantom Trax" file generated by CD-Protector, intended to be burned to a protected CD as an audio track.
|
||||
new(new FilePathMatch("Track#1 - Track#2 Cd-Protector.wav"), "CD-Protector"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
BinaryObjectScanner/Protection/CDSHiELDSE.cs
Normal file
37
BinaryObjectScanner/Protection/CDSHiELDSE.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class CDSHiELDSE : IExecutableCheck<PortableExecutable>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// TODO: Indicates Hypertech Crack Proof as well?
|
||||
//// Get the import directory table
|
||||
//if (pex.Model.ImportTable?.ImportDirectoryTable != null)
|
||||
//{
|
||||
// bool match = pex.Model.ImportTable.ImportDirectoryTable.Any(idte => idte.Name == "KeRnEl32.dLl");
|
||||
// if (match)
|
||||
// return "CDSHiELD SE";
|
||||
//}
|
||||
|
||||
// Get the code/CODE section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings("code") ?? pex.GetFirstSectionStrings("CODE");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("~0017.tmp")))
|
||||
return "CDSHiELD SE";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
BinaryObjectScanner/Protection/CDX.cs
Normal file
41
BinaryObjectScanner/Protection/CDX.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Paths;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
// Nothing is currently known about this DRM. One program may possibly have included it, as it has been listed as including these files in the installion directory (https://www.advanceduninstaller.com/Visit-Gallery-2-90896afd3151ed9660dddc23b892863f-application.htm).
|
||||
// Unfortunately, this program and developer are so obscure, I'm not able to find any relevant further information on them whatsoever.
|
||||
// The only source of valuable information currently known is a forum post about a user attempting to crack this DRM (https://forum.p30world.com/showthread.php?t=413264).
|
||||
// Every attachment, upload, or photo from the forum thread are offline and unarchived.
|
||||
public class CDX : IPathCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
{
|
||||
// TODO: Verify if these are OR or AND
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new(new FilePathMatch("CHKCDX16.DLL"), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
new(new FilePathMatch("CHKCDX32.DLL"), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
new(new FilePathMatch("CHKCDXNT.DLL"), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new(new FilePathMatch("CHKCDX16.DLL"), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
new(new FilePathMatch("CHKCDX32.DLL"), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
new(new FilePathMatch("CHKCDXNT.DLL"), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
BinaryObjectScanner/Protection/CactusDataShield.cs
Normal file
34
BinaryObjectScanner/Protection/CactusDataShield.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Content;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class CactusDataShield : IContentCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
{
|
||||
// TODO: Limit these checks to Mac binaries
|
||||
// TODO: Obtain a sample to find where this string is in a typical executable
|
||||
if (includeDebug)
|
||||
{
|
||||
var contentMatchSets = new List<ContentMatchSet>
|
||||
{
|
||||
// CDSPlayer
|
||||
new(new byte?[] { 0x43, 0x44, 0x53, 0x50, 0x6C, 0x61, 0x79, 0x65, 0x72 }, "Cactus Data Shield 200"),
|
||||
|
||||
// yucca.cds
|
||||
new(new byte?[] { 0x79, 0x75, 0x63, 0x63, 0x61, 0x2E, 0x63, 0x64, 0x73 }, "Cactus Data Shield 200"),
|
||||
};
|
||||
|
||||
if (contentMatchSets != null && contentMatchSets.Any())
|
||||
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchSets, includeDebug);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,42 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.PE;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BurnOutSharp.Matching;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Matching.Paths;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BurnOutSharp.ProtectionType
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// Cenega ProtectDVD is a protection seemingly created by the publisher Cenega for use with their games.
|
||||
/// Games using this protection aren't able to be run from an ISO file, and presumably use DMI as a protection feature.
|
||||
/// Games using this protection aren't able to be run from an ISO file, and presumably use DPM as a protection feature.
|
||||
/// <see href="https://github.com/TheRogueArchivist/DRML/blob/main/entries/Cenega_ProtectDVD/Cenega_ProtectDVD.md"/>
|
||||
/// </summary>
|
||||
public class CengaProtectDVD : IPathCheck, IPortableExecutableCheck
|
||||
public class CengaProtectDVD : IExecutableCheck<PortableExecutable>, IPathCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
public string? CheckExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex?.SectionTable;
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .cenega section, if it exists. Seems to be found in the protected game executable (Redump entry 31422).
|
||||
// Get the export directory table
|
||||
if (pex.Model.ExportTable?.ExportDirectoryTable != null)
|
||||
{
|
||||
// Found in "cenega.dll" in IA item "speed-pack".
|
||||
bool match = pex.Model.ExportTable.ExportDirectoryTable.Name?.Equals("ProtectDVD.dll", StringComparison.OrdinalIgnoreCase) == true;
|
||||
if (match)
|
||||
return "Cenega ProtectDVD";
|
||||
}
|
||||
|
||||
// Get the .cenega section, if it exists. Seems to be found in the protected game executable ("game.exe" in Redump entry 31422 and "Classic Car Racing.exe" in IA item "speed-pack").
|
||||
bool cenegaSection = pex.ContainsSection(".cenega", exact: true);
|
||||
if (cenegaSection)
|
||||
return "Cenega ProtectDVD";
|
||||
|
||||
// Get the .cenega0 through .cenega2 sections, if they exists. Found in cenega.dll (Redump entry 31422).
|
||||
// Get the .cenega0 through .cenega2 sections, if they exists. Found in "cenega.dll" in Redump entry 31422 and IA item "speed-pack".
|
||||
cenegaSection = pex.ContainsSection(".cenega0", exact: true);
|
||||
if (cenegaSection)
|
||||
return "Cenega ProtectDVD";
|
||||
@@ -42,26 +53,26 @@ namespace BurnOutSharp.ProtectionType
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
public IEnumerable<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Seems likely to be present in most, if not all discs protected with Cenega ProtectDVD, but unable to confirm due to only having a small sample size.
|
||||
// References the existence of a "ProtectDVD.dll", which has not yet been located (Redump entry 31422).
|
||||
new PathMatchSet(new PathMatch("cenega.dll", useEndsWith: true), "Cenega ProtectDVD"),
|
||||
// Found in Redump entry 31422 and IA item "speed-pack".
|
||||
new(new FilePathMatch("cenega.dll"), "Cenega ProtectDVD"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string CheckFilePath(string path)
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Seems likely to be present in most, if not all discs protected with Cenega ProtectDVD, but unable to confirm due to only having a small sample size.
|
||||
// References the existence of a "ProtectDVD.dll", which has not yet been located (Redump entry 31422).
|
||||
new PathMatchSet(new PathMatch("cenega.dll", useEndsWith: true), "Cenega ProtectDVD"),
|
||||
// Found in Redump entry 31422 and IA item "speed-pack".
|
||||
new(new FilePathMatch("cenega.dll"), "Cenega ProtectDVD"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user