mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-02-04 05:35:49 +00:00
Compare commits
1213 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
ea17ad1eae | ||
|
|
6087e6e0c6 | ||
|
|
535f14bc54 | ||
|
|
6b3d42b81f | ||
|
|
e645b8e1c0 | ||
|
|
955c8685b6 | ||
|
|
6184ae22aa | ||
|
|
4e2c5313f3 | ||
|
|
137f7fcc01 | ||
|
|
d771c4931c | ||
|
|
61a2181924 | ||
|
|
baad181e5f | ||
|
|
1b0fcdcf32 | ||
|
|
9fa1ef1d2e | ||
|
|
e2492c9e5b | ||
|
|
297514ef17 | ||
|
|
2acb0a037c | ||
|
|
c38eb0cc71 | ||
|
|
258369bb9e | ||
|
|
8cba9529d7 | ||
|
|
10296f40b4 | ||
|
|
73d085deac | ||
|
|
c7261c342a | ||
|
|
14d905fba3 | ||
|
|
1f66edc201 | ||
|
|
ade95c3210 | ||
|
|
8a419f50db | ||
|
|
52efca767e | ||
|
|
421dfa2591 | ||
|
|
76183c529c | ||
|
|
463506d1e8 | ||
|
|
1f2a187f55 | ||
|
|
40de5c4d0a | ||
|
|
5e72acb44c | ||
|
|
fab3c935f8 | ||
|
|
a498513662 | ||
|
|
3eaebffff1 | ||
|
|
280ae5babe | ||
|
|
5bf64d46bd | ||
|
|
6fb510c852 | ||
|
|
20223eea87 | ||
|
|
3f796e4e0e | ||
|
|
74732a1b50 | ||
|
|
f252681364 | ||
|
|
82cb0f934d | ||
|
|
7dc5644d21 | ||
|
|
313cc2bfb8 | ||
|
|
f24a8763fd | ||
|
|
47845a2409 | ||
|
|
42da9f4a82 | ||
|
|
e9fa86343a | ||
|
|
8ccf2272d6 | ||
|
|
8fb42bc12d | ||
|
|
6202ee5d5c | ||
|
|
655a8adb1c | ||
|
|
d7fcc99fc2 | ||
|
|
8601f373bd | ||
|
|
3c783fdc68 | ||
|
|
a75388cf17 | ||
|
|
7968a79fe6 | ||
|
|
8502924083 | ||
|
|
af95ca08c3 | ||
|
|
06995b75d6 | ||
|
|
1a2be26c72 | ||
|
|
a2a583e317 | ||
|
|
289a55ca21 | ||
|
|
598f625ed1 | ||
|
|
d604a6d784 | ||
|
|
cf34a0adee | ||
|
|
083ded8a7e | ||
|
|
1517a66724 | ||
|
|
5385de0f0a | ||
|
|
43729b53f0 | ||
|
|
e4aa618f0b | ||
|
|
4953673caf | ||
|
|
35c6d4f36c | ||
|
|
9108fa5a11 | ||
|
|
dc97feae39 | ||
|
|
beac29c650 | ||
|
|
21a041dad6 | ||
|
|
26eee23511 | ||
|
|
0915c7eccd | ||
|
|
e2e65bfbdf | ||
|
|
033f2e0a4e | ||
|
|
71ee0863eb | ||
|
|
3203c3ac83 | ||
|
|
1733f60a0f | ||
|
|
b01abdcce3 | ||
|
|
53c90533e3 | ||
|
|
03bd7bd1f5 | ||
|
|
637579b0fc | ||
|
|
bd40ca6d9d | ||
|
|
3c1623cb22 | ||
|
|
66da32b5c1 | ||
|
|
c21b64b5bd | ||
|
|
e968eeea96 | ||
|
|
c14005dcc4 | ||
|
|
84c6c257df | ||
|
|
e28b930f85 | ||
|
|
9dac2eb91b | ||
|
|
2209f362fa | ||
|
|
614413ab76 | ||
|
|
35c15d0ff8 | ||
|
|
b521df2ad4 | ||
|
|
778fe106f9 | ||
|
|
9171014dcd | ||
|
|
7ee74d9b81 | ||
|
|
826011f532 | ||
|
|
adb349932f | ||
|
|
f4f13c03fe | ||
|
|
e29e87444e | ||
|
|
b233b3c17b | ||
|
|
a8fca77331 | ||
|
|
5e4ee07646 | ||
|
|
52b2a9bef8 | ||
|
|
dc7f8da52f | ||
|
|
d46b0768a0 | ||
|
|
f136af3457 | ||
|
|
2e2b6068c5 | ||
|
|
7a29d8a8f9 | ||
|
|
ae0c47066e | ||
|
|
11188c9488 | ||
|
|
4d5d2d8690 | ||
|
|
1860a863b8 | ||
|
|
8dbf6d9362 | ||
|
|
e3e8a1170f | ||
|
|
b22641b7ab | ||
|
|
d3d75b9e58 | ||
|
|
6083c64b87 | ||
|
|
4185367a49 | ||
|
|
c027b1798a | ||
|
|
76e52292a8 | ||
|
|
dd5f7c4e0b | ||
|
|
6e70991e86 | ||
|
|
f1803b9f3a | ||
|
|
9e0abe7c5d | ||
|
|
1a01102252 | ||
|
|
ce849d312f | ||
|
|
71da8a53c0 | ||
|
|
b8f7d00dc9 | ||
|
|
7e69a56892 | ||
|
|
8cedd3e469 | ||
|
|
d5e60439fb | ||
|
|
1a0e16a05b | ||
|
|
bee6d2b885 | ||
|
|
5d515c7ddd | ||
|
|
013301fa99 | ||
|
|
152ae8bf6c | ||
|
|
439f71ef5c | ||
|
|
66c7afe20b | ||
|
|
6a609ea3f5 | ||
|
|
6752d0cfa3 | ||
|
|
1d26b06592 | ||
|
|
9ca24a3053 | ||
|
|
76ca87f7a9 | ||
|
|
c913b10286 | ||
|
|
768c77a6bc | ||
|
|
20d90d6a60 | ||
|
|
54a48e0729 | ||
|
|
15c05c65e7 | ||
|
|
4dbaa415c5 | ||
|
|
1a0b83a9f1 | ||
|
|
4316427980 | ||
|
|
21ecaca761 | ||
|
|
7022a957d6 | ||
|
|
32cfcbff0a | ||
|
|
283fa425d4 | ||
|
|
d37eac02d5 | ||
|
|
c3e0b99002 | ||
|
|
9e49703bc5 | ||
|
|
98a7149cc9 | ||
|
|
394b4e70fb | ||
|
|
b29198b3d4 | ||
|
|
044c398fa7 | ||
|
|
ae7087e8d0 | ||
|
|
90bc78982c | ||
|
|
2ce8bda394 | ||
|
|
ac0ed050dc | ||
|
|
e4aadb3794 | ||
|
|
80626f3b9e | ||
|
|
93263dfeb9 | ||
|
|
51e5f82cc2 | ||
|
|
b69c5cf928 | ||
|
|
e45b593c7b | ||
|
|
c245ebb80a | ||
|
|
cd38407e4e | ||
|
|
f34b1ba5cf | ||
|
|
08c97d291e | ||
|
|
3ea294f4d9 | ||
|
|
ddb2be278a | ||
|
|
b6b5dc4efa | ||
|
|
218fecb273 | ||
|
|
b23a504c7b | ||
|
|
c54181e9ac | ||
|
|
7e100a261c | ||
|
|
38e1154bad | ||
|
|
901804e9e4 | ||
|
|
dfee4a8d76 | ||
|
|
295b86fbd0 | ||
|
|
177543a51c | ||
|
|
dfff702e5d | ||
|
|
c0ad427b5e | ||
|
|
2f68f95d80 | ||
|
|
a8ba104d0f | ||
|
|
6f9e92d222 | ||
|
|
1e20c1b147 | ||
|
|
c16946ace7 | ||
|
|
9d7cc4012c | ||
|
|
a44bdf9013 | ||
|
|
f9f2e0d932 | ||
|
|
2dd3e21ea6 | ||
|
|
81bb47b634 | ||
|
|
c8efc1430a | ||
|
|
7883638f0a | ||
|
|
e930be12c8 | ||
|
|
c45ae4b693 | ||
|
|
478f28b513 | ||
|
|
aac3c391db | ||
|
|
802734b515 | ||
|
|
ef212fc8d9 | ||
|
|
ee85f2f6f0 | ||
|
|
9a160b3127 | ||
|
|
4486c5ed62 |
16
.gitmodules
vendored
16
.gitmodules
vendored
@@ -1,15 +1,3 @@
|
||||
[submodule "BurnOutSharp/External/LessIO"]
|
||||
path = BurnOutSharp/External/LessIO
|
||||
url = https://github.com/activescott/LessIO.git
|
||||
[submodule "BurnOutSharp/External/libmspack4n"]
|
||||
path = BurnOutSharp/External/libmspack4n
|
||||
url = https://github.com/activescott/libmspack4n.git
|
||||
[submodule "BurnOutSharp/External/hllib"]
|
||||
path = BurnOutSharp/External/hllib
|
||||
url = https://github.com/RavuAlHemio/hllib.git
|
||||
[submodule "BurnOutSharp/External/stormlibsharp"]
|
||||
path = BurnOutSharp/External/stormlibsharp
|
||||
[submodule "BinaryObjectScanner/_EXTERNAL/stormlibsharp"]
|
||||
path = BinaryObjectScanner/_EXTERNAL/stormlibsharp
|
||||
url = https://github.com/robpaveza/stormlibsharp.git
|
||||
[submodule "BurnOutSharp/External/WixToolset"]
|
||||
path = WixToolset
|
||||
url = https://github.com/wixtoolset/Dtf.git
|
||||
|
||||
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@@ -5,12 +5,12 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": ".NET Core Launch (console)",
|
||||
"name": ".NET Core Launch (Test)",
|
||||
"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}/Test/bin/Debug/net6.0/Test.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/Test",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
|
||||
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"
|
||||
],
|
||||
|
||||
@@ -3,22 +3,20 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.1.32407.343
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BurnOutSharp", "BurnOutSharp\BurnOutSharp.csproj", "{1DA4212E-6071-4951-B45D-BB74A7838246}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "Test\Test.csproj", "{88735BA2-778D-4192-8EB2-FFF6843719E2}"
|
||||
EndProject
|
||||
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.bat = publish-win.bat
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WixToolset", "WixToolset", "{09D405CA-CF15-4929-8408-C970F0656C62}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{182E02A8-5E8E-4140-9C9B-61049C33E921}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.WindowsInstaller", "WixToolset\src\WixToolset.Dtf.WindowsInstaller\WixToolset.Dtf.WindowsInstaller.csproj", "{B3537EB7-CEF6-4D90-A041-47626442A656}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BinaryObjectScanner", "BinaryObjectScanner\BinaryObjectScanner.csproj", "{341EA3F5-847C-4739-B86F-2B051FFE4EF2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -26,26 +24,18 @@ Global
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{1DA4212E-6071-4951-B45D-BB74A7838246}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1DA4212E-6071-4951-B45D-BB74A7838246}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1DA4212E-6071-4951-B45D-BB74A7838246}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1DA4212E-6071-4951-B45D-BB74A7838246}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{88735BA2-778D-4192-8EB2-FFF6843719E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{88735BA2-778D-4192-8EB2-FFF6843719E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{88735BA2-778D-4192-8EB2-FFF6843719E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{88735BA2-778D-4192-8EB2-FFF6843719E2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B3537EB7-CEF6-4D90-A041-47626442A656}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B3537EB7-CEF6-4D90-A041-47626442A656}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B3537EB7-CEF6-4D90-A041-47626442A656}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B3537EB7-CEF6-4D90-A041-47626442A656}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{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
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{182E02A8-5E8E-4140-9C9B-61049C33E921} = {09D405CA-CF15-4929-8408-C970F0656C62}
|
||||
{B3537EB7-CEF6-4D90-A041-47626442A656} = {182E02A8-5E8E-4140-9C9B-61049C33E921}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {0B343DD2-8852-47B0-9647-DFCFBEDF933C}
|
||||
EndGlobalSection
|
||||
68
BinaryObjectScanner/BinaryObjectScanner.csproj
Normal file
68
BinaryObjectScanner/BinaryObjectScanner.csproj
Normal file
@@ -0,0 +1,68 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Version>2.9.0</Version>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<PackageId>BurnOutSharp</PackageId> <!-- Temporary until official renaming -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Description>Protection scanning library</Description>
|
||||
<Copyright>Copyright (c)2018-2023 Matt Nadareski</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/mnadareski/BurnOutSharp</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>protection copy-protection scanning packer</PackageTags>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'!='net48'">
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- These are needed for dealing with submodules -->
|
||||
<PropertyGroup>
|
||||
<DefaultItemExcludes>
|
||||
$(DefaultItemExcludes);
|
||||
**\AssemblyInfo.cs;
|
||||
_EXTERNAL\stormlibsharp\lib\**;
|
||||
_EXTERNAL\stormlibsharp\src\TestConsole\**
|
||||
</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- These are needed for dealing with native Windows DLLs -->
|
||||
<ItemGroup>
|
||||
<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>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenMcdf" Version="2.3.0" />
|
||||
<PackageReference Include="SabreTools.Compression" Version="0.1.1" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.1.1" />
|
||||
<PackageReference Include="SabreTools.Matching" Version="1.1.1" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.1.5" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.1.7" />
|
||||
<PackageReference Include="SharpCompress" Version="0.34.1" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />
|
||||
<PackageReference Include="UnshieldSharp" Version="1.6.9" />
|
||||
<PackageReference Include="WiseUnpacker" Version="1.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
78
BinaryObjectScanner/Factory.cs
Normal file
78
BinaryObjectScanner/Factory.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
|
||||
namespace BinaryObjectScanner
|
||||
{
|
||||
public static class Factory
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an instance of a detectable based on file type
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public static IDetectable CreateDetectable(SupportedFileType fileType)
|
||||
#else
|
||||
public static IDetectable? CreateDetectable(SupportedFileType fileType)
|
||||
#endif
|
||||
{
|
||||
switch (fileType)
|
||||
{
|
||||
case SupportedFileType.AACSMediaKeyBlock: return new BinaryObjectScanner.FileType.AACSMediaKeyBlock();
|
||||
case SupportedFileType.BDPlusSVM: return new BinaryObjectScanner.FileType.BDPlusSVM();
|
||||
//case SupportedFileType.CIA: return new BinaryObjectScanner.FileType.CIA();
|
||||
case SupportedFileType.Executable: return new BinaryObjectScanner.FileType.Executable();
|
||||
case SupportedFileType.LDSCRYPT: return new BinaryObjectScanner.FileType.LDSCRYPT();
|
||||
//case SupportedFileType.N3DS: return new BinaryObjectScanner.FileType.N3DS();
|
||||
//case SupportedFileType.Nitro: return new BinaryObjectScanner.FileType.Nitro();
|
||||
case SupportedFileType.PLJ: return new BinaryObjectScanner.FileType.PLJ();
|
||||
case SupportedFileType.SFFS: return new BinaryObjectScanner.FileType.SFFS();
|
||||
case SupportedFileType.Textfile: return new BinaryObjectScanner.FileType.Textfile();
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of an extractable based on file type
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public static IExtractable CreateExtractable(SupportedFileType fileType)
|
||||
#else
|
||||
public static IExtractable? CreateExtractable(SupportedFileType fileType)
|
||||
#endif
|
||||
{
|
||||
switch (fileType)
|
||||
{
|
||||
case SupportedFileType.BFPK: return new BinaryObjectScanner.FileType.BFPK();
|
||||
case SupportedFileType.BSP: return new BinaryObjectScanner.FileType.BSP();
|
||||
case SupportedFileType.BZip2: return new BinaryObjectScanner.FileType.BZip2();
|
||||
case SupportedFileType.CFB: return new BinaryObjectScanner.FileType.CFB();
|
||||
//case SupportedFileType.CIA: return new BinaryObjectScanner.FileType.CIA();
|
||||
case SupportedFileType.GCF: return new BinaryObjectScanner.FileType.GCF();
|
||||
case SupportedFileType.GZIP: return new BinaryObjectScanner.FileType.GZIP();
|
||||
case SupportedFileType.InstallShieldArchiveV3: return new BinaryObjectScanner.FileType.InstallShieldArchiveV3();
|
||||
case SupportedFileType.InstallShieldCAB: return new BinaryObjectScanner.FileType.InstallShieldCAB();
|
||||
case SupportedFileType.MicrosoftCAB: return new BinaryObjectScanner.FileType.MicrosoftCAB();
|
||||
case SupportedFileType.MicrosoftLZ: return new BinaryObjectScanner.FileType.MicrosoftLZ();
|
||||
case SupportedFileType.MPQ: return new BinaryObjectScanner.FileType.MPQ();
|
||||
//case SupportedFileType.N3DS: return new BinaryObjectScanner.FileType.N3DS();
|
||||
//case SupportedFileType.NCF: return new BinaryObjectScanner.FileType.NCF();
|
||||
//case SupportedFileType.Nitro: return new BinaryObjectScanner.FileType.Nitro();
|
||||
case SupportedFileType.PAK: return new BinaryObjectScanner.FileType.PAK();
|
||||
case SupportedFileType.PFF: return new BinaryObjectScanner.FileType.PFF();
|
||||
case SupportedFileType.PKZIP: return new BinaryObjectScanner.FileType.PKZIP();
|
||||
//case SupportedFileType.PLJ: return new BinaryObjectScanner.FileType.PLJ();
|
||||
//case SupportedFileType.Quantum: return new BinaryObjectScanner.FileType.Quantum();
|
||||
case SupportedFileType.RAR: return new BinaryObjectScanner.FileType.RAR();
|
||||
case SupportedFileType.SevenZip: return new BinaryObjectScanner.FileType.SevenZip();
|
||||
case SupportedFileType.SFFS: return new BinaryObjectScanner.FileType.SFFS();
|
||||
case SupportedFileType.SGA: return new BinaryObjectScanner.FileType.SGA();
|
||||
case SupportedFileType.TapeArchive: return new BinaryObjectScanner.FileType.TapeArchive();
|
||||
case SupportedFileType.VBSP: return new BinaryObjectScanner.FileType.VBSP();
|
||||
case SupportedFileType.VPK: return new BinaryObjectScanner.FileType.VPK();
|
||||
case SupportedFileType.WAD: return new BinaryObjectScanner.FileType.WAD();
|
||||
case SupportedFileType.XZ: return new BinaryObjectScanner.FileType.XZ();
|
||||
case SupportedFileType.XZP: return new BinaryObjectScanner.FileType.XZP();
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
BinaryObjectScanner/FileType/AACSMediaKeyBlock.cs
Normal file
59
BinaryObjectScanner/FileType/AACSMediaKeyBlock.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
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/>
|
||||
#if NET48
|
||||
public string Detect(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Detect(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
BinaryObjectScanner/FileType/BDPlusSVM.cs
Normal file
57
BinaryObjectScanner/FileType/BDPlusSVM.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// BD+ SVM
|
||||
/// </summary>
|
||||
public class BDPlusSVM : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Detect(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Detect(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
148
BinaryObjectScanner/FileType/BFPK.cs
Normal file
148
BinaryObjectScanner/FileType/BFPK.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// BFPK custom archive format
|
||||
/// </summary>
|
||||
public class BFPK : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var bfpk = SabreTools.Serialization.Wrappers.BFPK.Create(stream);
|
||||
if (bfpk == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Extract all files
|
||||
ExtractAll(bfpk, tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
ZlibStream zs = new ZlibStream(ms, CompressionMode.Decompress);
|
||||
zs.CopyTo(fs);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
319
BinaryObjectScanner/FileType/BSP.cs
Normal file
319
BinaryObjectScanner/FileType/BSP.cs
Normal file
@@ -0,0 +1,319 @@
|
||||
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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var bsp = SabreTools.Serialization.Wrappers.BSP.Create(stream);
|
||||
if (bsp == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
ExtractAllLumps(bsp, tempPath);
|
||||
ExtractAllTextures(bsp, tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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.IsNullOrWhiteSpace(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.IsNullOrWhiteSpace(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>
|
||||
#if NET48
|
||||
private static byte[] CreateTextureData(SabreTools.Models.BSP.Texture texture)
|
||||
#else
|
||||
private static byte[]? CreateTextureData(SabreTools.Models.BSP.Texture texture)
|
||||
#endif
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
64
BinaryObjectScanner/FileType/BZip2.cs
Normal file
64
BinaryObjectScanner/FileType/BZip2.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.BZip2;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// bzip2 archive
|
||||
/// </summary>
|
||||
public class BZip2 : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
using (BZip2Stream bz2File = new BZip2Stream(stream, CompressionMode.Decompress, true))
|
||||
{
|
||||
string tempFile = Path.Combine(tempPath, Guid.NewGuid().ToString());
|
||||
using (FileStream fs = File.OpenWrite(tempFile))
|
||||
{
|
||||
bz2File.CopyTo(fs);
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
164
BinaryObjectScanner/FileType/CFB.cs
Normal file
164
BinaryObjectScanner/FileType/CFB.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using OpenMcdf;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Compound File Binary
|
||||
/// </summary>
|
||||
public class CFB : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
using (CompoundFile msi = new CompoundFile(stream, CFSUpdateMode.ReadOnly, CFSConfiguration.Default))
|
||||
{
|
||||
msi.RootStorage.VisitEntries((e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!e.IsStream)
|
||||
return;
|
||||
|
||||
var str = msi.RootStorage.GetStream(e.Name);
|
||||
if (str == null)
|
||||
return;
|
||||
|
||||
byte[] strData = str.GetData();
|
||||
if (strData == null)
|
||||
return;
|
||||
|
||||
var decoded = DecodeStreamName(e.Name)?.TrimEnd('\0');
|
||||
if (decoded == null)
|
||||
return;
|
||||
|
||||
byte[] nameBytes = Encoding.UTF8.GetBytes(e.Name);
|
||||
|
||||
// UTF-8 encoding of 0x4840.
|
||||
if (nameBytes[0] == 0xe4 && nameBytes[1] == 0xa1 && nameBytes[2] == 0x80)
|
||||
decoded = decoded.Substring(3);
|
||||
|
||||
foreach (char c in Path.GetInvalidFileNameChars())
|
||||
{
|
||||
decoded = decoded.Replace(c, '_');
|
||||
}
|
||||
|
||||
string filename = Path.Combine(tempPath, decoded);
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(strData, 0, strData.Length);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}, recursive: true);
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks>Adapted from LibMSI</remarks>
|
||||
#if NET48
|
||||
public static string DecodeStreamName(string input)
|
||||
#else
|
||||
public static string? DecodeStreamName(string input)
|
||||
#endif
|
||||
{
|
||||
if (input == null)
|
||||
return null;
|
||||
|
||||
int count = 0;
|
||||
byte[] inputBytes = Encoding.UTF8.GetBytes(input);
|
||||
int p = 0; // inputBytes[0]
|
||||
|
||||
byte[] output = new byte[inputBytes.Length + 1];
|
||||
int q = 0; // output[0]
|
||||
while (p < inputBytes.Length && inputBytes[p] != 0)
|
||||
{
|
||||
int ch = inputBytes[p];
|
||||
if ((ch == 0xe3 && inputBytes[p + 1] >= 0xa0) || (ch == 0xe4 && inputBytes[p + 1] < 0xa0))
|
||||
{
|
||||
// UTF-8 encoding of 0x3800..0x47ff.
|
||||
output[q++] = (byte)Mime2Utf(inputBytes[p + 2] & 0x7f);
|
||||
output[q++] = (byte)Mime2Utf(inputBytes[p + 1] ^ 0xa0);
|
||||
p += 3;
|
||||
count += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == 0xe4 && inputBytes[p + 1] == 0xa0)
|
||||
{
|
||||
// UTF-8 encoding of 0x4800..0x483f.
|
||||
output[q++] = (byte)Mime2Utf(inputBytes[p + 2] & 0x7f);
|
||||
p += 3;
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
output[q++] = inputBytes[p++];
|
||||
if (ch >= 0xc1)
|
||||
output[q++] = inputBytes[p++];
|
||||
if (ch >= 0xe0)
|
||||
output[q++] = inputBytes[p++];
|
||||
if (ch >= 0xf0)
|
||||
output[q++] = inputBytes[p++];
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
output[q] = 0;
|
||||
return Encoding.ASCII.GetString(output);
|
||||
}
|
||||
|
||||
/// <remarks>Adapted from LibMSI</remarks>
|
||||
private static int Mime2Utf(int x)
|
||||
{
|
||||
if (x < 10)
|
||||
return x + '0';
|
||||
if (x < (10 + 26))
|
||||
return x - 10 + 'A';
|
||||
if (x < (10 + 26 + 26))
|
||||
return x - 10 - 26 + 'a';
|
||||
if (x == (10 + 26 + 26))
|
||||
return '.';
|
||||
return '_';
|
||||
}
|
||||
}
|
||||
}
|
||||
482
BinaryObjectScanner/FileType/Executable.cs
Normal file
482
BinaryObjectScanner/FileType/Executable.cs
Normal file
@@ -0,0 +1,482 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Executable or library
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Due to the complexity of executables, all extraction handling
|
||||
/// another class that is used by the scanner
|
||||
/// </remarks>
|
||||
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; }
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IContentCheck types
|
||||
/// </summary>
|
||||
public static IEnumerable<IContentCheck> ContentCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
if (contentCheckClasses == null)
|
||||
contentCheckClasses = InitCheckClasses<IContentCheck>();
|
||||
|
||||
return contentCheckClasses ?? Enumerable.Empty<IContentCheck>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all ILinearExecutableCheck types
|
||||
/// </summary>
|
||||
public static IEnumerable<ILinearExecutableCheck> LinearExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
if (linearExecutableCheckClasses == null)
|
||||
linearExecutableCheckClasses = InitCheckClasses<ILinearExecutableCheck>();
|
||||
|
||||
return linearExecutableCheckClasses ?? Enumerable.Empty<ILinearExecutableCheck>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IMSDOSExecutableCheck types
|
||||
/// </summary>
|
||||
public static IEnumerable<IMSDOSExecutableCheck> MSDOSExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
if (msdosExecutableCheckClasses == null)
|
||||
msdosExecutableCheckClasses = InitCheckClasses<IMSDOSExecutableCheck>();
|
||||
|
||||
return msdosExecutableCheckClasses ?? Enumerable.Empty<IMSDOSExecutableCheck>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all INewExecutableCheck types
|
||||
/// </summary>
|
||||
public static IEnumerable<INewExecutableCheck> NewExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
if (newExecutableCheckClasses == null)
|
||||
newExecutableCheckClasses = InitCheckClasses<INewExecutableCheck>();
|
||||
|
||||
return newExecutableCheckClasses ?? Enumerable.Empty<INewExecutableCheck>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IPortableExecutableCheck types
|
||||
/// </summary>
|
||||
public static IEnumerable<IPortableExecutableCheck> PortableExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
if (portableExecutableCheckClasses == null)
|
||||
portableExecutableCheckClasses = InitCheckClasses<IPortableExecutableCheck>();
|
||||
|
||||
return portableExecutableCheckClasses ?? Enumerable.Empty<IPortableExecutableCheck>();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Instances
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IContentCheck types
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private static IEnumerable<IContentCheck> contentCheckClasses;
|
||||
#else
|
||||
private static IEnumerable<IContentCheck>? contentCheckClasses;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all ILinearExecutableCheck types
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private static IEnumerable<ILinearExecutableCheck> linearExecutableCheckClasses;
|
||||
#else
|
||||
private static IEnumerable<ILinearExecutableCheck>? linearExecutableCheckClasses;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IMSDOSExecutableCheck types
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private static IEnumerable<IMSDOSExecutableCheck> msdosExecutableCheckClasses;
|
||||
#else
|
||||
private static IEnumerable<IMSDOSExecutableCheck>? msdosExecutableCheckClasses;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all INewExecutableCheck types
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private static IEnumerable<INewExecutableCheck> newExecutableCheckClasses;
|
||||
#else
|
||||
private static IEnumerable<INewExecutableCheck>? newExecutableCheckClasses;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IPortableExecutableCheck types
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private static IEnumerable<IPortableExecutableCheck> portableExecutableCheckClasses;
|
||||
#else
|
||||
private static IEnumerable<IPortableExecutableCheck>? portableExecutableCheckClasses;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Detect(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Detect(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Try to create a wrapper for the proper executable type
|
||||
var wrapper = WrapperFactory.CreateExecutableWrapper(stream);
|
||||
if (wrapper == null)
|
||||
return null;
|
||||
|
||||
// Create the internal queue
|
||||
var protections = new ConcurrentQueue<string>();
|
||||
|
||||
// Only use generic content checks if we're in debug mode
|
||||
if (includeDebug)
|
||||
{
|
||||
var subProtections = RunContentChecks(file, stream, includeDebug);
|
||||
if (subProtections != null)
|
||||
protections.AddRange(subProtections.Values.ToArray());
|
||||
}
|
||||
|
||||
if (wrapper is MSDOS mz)
|
||||
{
|
||||
var subProtections = RunMSDOSExecutableChecks(file, stream, mz, includeDebug);
|
||||
if (subProtections != null)
|
||||
protections.AddRange(subProtections.Values.ToArray());
|
||||
}
|
||||
else if (wrapper is LinearExecutable lex)
|
||||
{
|
||||
var subProtections = RunLinearExecutableChecks(file, stream, lex, includeDebug);
|
||||
if (subProtections != null)
|
||||
protections.AddRange(subProtections.Values.ToArray());
|
||||
}
|
||||
else if (wrapper is NewExecutable nex)
|
||||
{
|
||||
var subProtections = RunNewExecutableChecks(file, stream, nex, includeDebug);
|
||||
if (subProtections != null)
|
||||
protections.AddRange(subProtections.Values.ToArray());
|
||||
}
|
||||
else if (wrapper is PortableExecutable pex)
|
||||
{
|
||||
var subProtections = RunPortableExecutableChecks(file, stream, pex, includeDebug);
|
||||
if (subProtections != null)
|
||||
protections.AddRange(subProtections.Values.ToArray());
|
||||
}
|
||||
|
||||
return string.Join(";", 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, null on error</returns>
|
||||
#if NET48
|
||||
public ConcurrentDictionary<IContentCheck, string> RunContentChecks(string file, Stream stream, bool includeDebug)
|
||||
#else
|
||||
public ConcurrentDictionary<IContentCheck, string>? RunContentChecks(string? file, Stream stream, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// If we have an invalid file
|
||||
if (string.IsNullOrWhiteSpace(file))
|
||||
return null;
|
||||
else if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
// Read the file contents
|
||||
byte[] fileContent = new byte[0];
|
||||
try
|
||||
{
|
||||
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
|
||||
{
|
||||
fileContent = br.ReadBytes((int)stream.Length);
|
||||
if (fileContent == null)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create the output dictionary
|
||||
var protections = new ConcurrentDictionary<IContentCheck, string>();
|
||||
|
||||
// Iterate through all checks
|
||||
Parallel.ForEach(ContentCheckClasses, checkClass =>
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
var protection = checkClass.CheckContents(file, fileContent, includeDebug);
|
||||
if (string.IsNullOrWhiteSpace(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.TryAdd(checkClass, protection);
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single file based on all linear executable check implementations
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the executable, for tracking</param>
|
||||
/// <param name="lex">Executable to scan</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections in file, null on error</returns>
|
||||
public ConcurrentDictionary<ILinearExecutableCheck, string> RunLinearExecutableChecks(string file, Stream stream, LinearExecutable lex, bool includeDebug)
|
||||
{
|
||||
// Create the output dictionary
|
||||
var protections = new ConcurrentDictionary<ILinearExecutableCheck, string>();
|
||||
|
||||
// Iterate through all checks
|
||||
Parallel.ForEach(LinearExecutableCheckClasses, checkClass =>
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
var protection = checkClass.CheckLinearExecutable(file, lex, includeDebug);
|
||||
if (string.IsNullOrWhiteSpace(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.TryAdd(checkClass, protection);
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single file based on all MS-DOS executable check implementations
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the executable, for tracking</param>
|
||||
/// <param name="mz">Executable to scan</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections in file, null on error</returns>
|
||||
public ConcurrentDictionary<IMSDOSExecutableCheck, string> RunMSDOSExecutableChecks(string file, Stream stream, MSDOS mz, bool includeDebug)
|
||||
{
|
||||
// Create the output dictionary
|
||||
var protections = new ConcurrentDictionary<IMSDOSExecutableCheck, string>();
|
||||
|
||||
// Iterate through all checks
|
||||
Parallel.ForEach(MSDOSExecutableCheckClasses, checkClass =>
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
var protection = checkClass.CheckMSDOSExecutable(file, mz, includeDebug);
|
||||
if (string.IsNullOrWhiteSpace(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.TryAdd(checkClass, protection);
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single file based on all new executable check implementations
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the executable, for tracking</param>
|
||||
/// <param name="nex">Executable to scan</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections in file, null on error</returns>
|
||||
public ConcurrentDictionary<INewExecutableCheck, string> RunNewExecutableChecks(string file, Stream stream, NewExecutable nex, bool includeDebug)
|
||||
{
|
||||
// Create the output dictionary
|
||||
var protections = new ConcurrentDictionary<INewExecutableCheck, string>();
|
||||
|
||||
// Iterate through all checks
|
||||
Parallel.ForEach(NewExecutableCheckClasses, checkClass =>
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
var protection = checkClass.CheckNewExecutable(file, nex, includeDebug);
|
||||
if (string.IsNullOrWhiteSpace(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.TryAdd(checkClass, protection);
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single file based on all portable executable check implementations
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the executable, for tracking</param>
|
||||
/// <param name="pex">Executable to scan</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections in file, null on error</returns>
|
||||
public ConcurrentDictionary<IPortableExecutableCheck, string> RunPortableExecutableChecks(string file, Stream stream, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Create the output dictionary
|
||||
var protections = new ConcurrentDictionary<IPortableExecutableCheck, string>();
|
||||
|
||||
// Iterate through all checks
|
||||
Parallel.ForEach(PortableExecutableCheckClasses, checkClass =>
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
var protection = checkClass.CheckPortableExecutable(file, pex, includeDebug);
|
||||
if (string.IsNullOrWhiteSpace(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.TryAdd(checkClass, protection);
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initializers
|
||||
|
||||
/// <summary>
|
||||
/// Initialize all implementations of a type
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private static IEnumerable<T> InitCheckClasses<T>()
|
||||
#else
|
||||
private static IEnumerable<T>? InitCheckClasses<T>()
|
||||
#endif
|
||||
=> InitCheckClasses<T>(typeof(GameEngine._DUMMY).Assembly) ?? Enumerable.Empty<T>()
|
||||
.Concat(InitCheckClasses<T>(typeof(Packer._DUMMY).Assembly) ?? Enumerable.Empty<T>())
|
||||
.Concat(InitCheckClasses<T>(typeof(Protection._DUMMY).Assembly) ?? Enumerable.Empty<T>());
|
||||
|
||||
/// <summary>
|
||||
/// Initialize all implementations of a type
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private static IEnumerable<T> InitCheckClasses<T>(Assembly assembly)
|
||||
#else
|
||||
private static IEnumerable<T>? InitCheckClasses<T>(Assembly assembly)
|
||||
#endif
|
||||
{
|
||||
return assembly.GetTypes()?
|
||||
.Where(t => t.IsClass && t.GetInterface(typeof(T).Name) != null)?
|
||||
#if NET48
|
||||
.Select(t => (T)Activator.CreateInstance(t))
|
||||
#else
|
||||
.Select(t => (T?)Activator.CreateInstance(t))
|
||||
#endif
|
||||
.Cast<T>() ?? Array.Empty<T>();
|
||||
}
|
||||
|
||||
#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
|
||||
}
|
||||
}
|
||||
165
BinaryObjectScanner/FileType/GCF.cs
Normal file
165
BinaryObjectScanner/FileType/GCF.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var gcf = SabreTools.Serialization.Wrappers.GCF.Create(stream);
|
||||
if (gcf == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
ExtractAll(gcf, tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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.IsNullOrWhiteSpace(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
BinaryObjectScanner/FileType/GZIP.cs
Normal file
75
BinaryObjectScanner/FileType/GZIP.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.GZip;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// gzip archive
|
||||
/// </summary>
|
||||
public class GZIP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
using (GZipArchive zipFile = GZipArchive.Open(stream))
|
||||
{
|
||||
foreach (var entry in zipFile.Entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If we have a directory, skip it
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
string tempFile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
77
BinaryObjectScanner/FileType/InstallShieldArchiveV3.cs
Normal file
77
BinaryObjectScanner/FileType/InstallShieldArchiveV3.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using UnshieldSharp.Archive;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// InstallShield archive v3
|
||||
/// </summary>
|
||||
public class InstallShieldArchiveV3 : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
UnshieldSharp.Archive.InstallShieldArchiveV3 archive = new UnshieldSharp.Archive.InstallShieldArchiveV3(file);
|
||||
foreach (CompressedFile cfile in archive.Files.Select(kvp => kvp.Value))
|
||||
{
|
||||
try
|
||||
{
|
||||
string tempFile = Path.Combine(tempPath, cfile.FullPath);
|
||||
var directoryName = Path.GetDirectoryName(tempFile);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
(byte[] fileContents, string error) = archive.Extract(cfile.FullPath);
|
||||
if (!string.IsNullOrWhiteSpace(error))
|
||||
continue;
|
||||
|
||||
using (FileStream fs = File.OpenWrite(tempFile))
|
||||
{
|
||||
fs.Write(fileContents, 0, fileContents.Length);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
108
BinaryObjectScanner/FileType/InstallShieldCAB.cs
Normal file
108
BinaryObjectScanner/FileType/InstallShieldCAB.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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 null;
|
||||
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
InstallShieldCabinet cabfile = InstallShieldCabinet.Open(file);
|
||||
for (int i = 0; i < cabfile.FileCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if the file is valid first
|
||||
if (!cabfile.FileIsValid(i))
|
||||
continue;
|
||||
|
||||
string tempFile;
|
||||
try
|
||||
{
|
||||
string filename = cabfile.FileName(i);
|
||||
tempFile = Path.Combine(tempPath, filename);
|
||||
}
|
||||
catch
|
||||
{
|
||||
tempFile = Path.Combine(tempPath, $"BAD_FILENAME{i}");
|
||||
}
|
||||
|
||||
cabfile.FileSave(i, tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
BinaryObjectScanner/FileType/LDSCRYPT.cs
Normal file
52
BinaryObjectScanner/FileType/LDSCRYPT.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
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/>
|
||||
#if NET48
|
||||
public string Detect(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Detect(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Tools;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET48
|
||||
using StormLibSharp;
|
||||
#endif
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
public class MPQ : IScannable
|
||||
/// <summary>
|
||||
/// MoPaQ game data archive
|
||||
/// </summary>
|
||||
public class MPQ : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic)
|
||||
{
|
||||
if (magic.StartsWith(new byte?[] { 0x4d, 0x50, 0x51, 0x1a }))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// If the mpq file itself fails
|
||||
#if NET6_0_OR_GREATER
|
||||
// Not supported for .NET 6.0 due to Windows DLL requirements
|
||||
return null;
|
||||
#else
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
@@ -44,7 +51,7 @@ namespace BurnOutSharp.FileType
|
||||
// Try to open the listfile
|
||||
string listfile = null;
|
||||
MpqFileStream listStream = mpqArchive.OpenFile("(listfile)");
|
||||
|
||||
|
||||
// If we can't read the listfile, we just return
|
||||
if (!listStream.CanRead)
|
||||
return null;
|
||||
@@ -61,35 +68,27 @@ namespace BurnOutSharp.FileType
|
||||
// Loop over each entry
|
||||
foreach (string sub in listfileLines)
|
||||
{
|
||||
// If an individual entry fails
|
||||
try
|
||||
{
|
||||
string tempFile = Path.Combine(tempPath, sub);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(tempFile));
|
||||
mpqArchive.ExtractFile(sub, tempFile);
|
||||
}
|
||||
catch { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Remove temporary path references
|
||||
Utilities.StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
return tempPath;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return null;
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
41
BinaryObjectScanner/FileType/MicrosoftCAB.cs
Normal file
41
BinaryObjectScanner/FileType/MicrosoftCAB.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
77
BinaryObjectScanner/FileType/MicrosoftLZ.cs
Normal file
77
BinaryObjectScanner/FileType/MicrosoftLZ.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
var data = Decompressor.Decompress(stream);
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
// 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(tempPath, tempFile);
|
||||
|
||||
// Write the file data to a temp file
|
||||
using (Stream tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
|
||||
{
|
||||
tempStream.Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
137
BinaryObjectScanner/FileType/PAK.cs
Normal file
137
BinaryObjectScanner/FileType/PAK.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life Package File
|
||||
/// </summary>
|
||||
public class PAK : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var pak = SabreTools.Serialization.Wrappers.PAK.Create(stream);
|
||||
if (pak == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
ExtractAll(pak, tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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.IsNullOrWhiteSpace(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
130
BinaryObjectScanner/FileType/PFF.cs
Normal file
130
BinaryObjectScanner/FileType/PFF.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// NovaLogic Game Archive Format
|
||||
/// </summary>
|
||||
public class PFF : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var pff = SabreTools.Serialization.Wrappers.PFF.Create(stream);
|
||||
if (pff == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Extract all files
|
||||
ExtractAll(pff, tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
78
BinaryObjectScanner/FileType/PKZIP.cs
Normal file
78
BinaryObjectScanner/FileType/PKZIP.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Zip;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// PKWARE ZIP archive and derivatives
|
||||
/// </summary>
|
||||
public class PKZIP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
using (ZipArchive zipFile = ZipArchive.Open(stream))
|
||||
{
|
||||
foreach (var entry in zipFile.Entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If we have a directory, skip it
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
string tempFile = Path.Combine(tempPath, 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 tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
BinaryObjectScanner/FileType/PLJ.cs
Normal file
52
BinaryObjectScanner/FileType/PLJ.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// PlayJ audio file
|
||||
/// </summary>
|
||||
public class PLJ : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Detect(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Detect(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
166
BinaryObjectScanner/FileType/Quantum.cs
Normal file
166
BinaryObjectScanner/FileType/Quantum.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Quantum Archive
|
||||
/// </summary>
|
||||
public class Quantum : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var quantum = SabreTools.Serialization.Wrappers.Quantum.Create(stream);
|
||||
if (quantum == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Extract all files
|
||||
ExtractAll(quantum, tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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.IsNullOrWhiteSpace(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
BinaryObjectScanner/FileType/RAR.cs
Normal file
75
BinaryObjectScanner/FileType/RAR.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Rar;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// RAR archive
|
||||
/// </summary>
|
||||
public class RAR : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
using (RarArchive rarFile = RarArchive.Open(stream))
|
||||
{
|
||||
foreach (var entry in rarFile.Entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If we have a directory, skip it
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
string tempFile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
79
BinaryObjectScanner/FileType/SFFS.cs
Normal file
79
BinaryObjectScanner/FileType/SFFS.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
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/>
|
||||
#if NET48
|
||||
public string Detect(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Detect(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
283
BinaryObjectScanner/FileType/SGA.cs
Normal file
283
BinaryObjectScanner/FileType/SGA.cs
Normal file
@@ -0,0 +1,283 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// SGA game archive
|
||||
/// </summary>
|
||||
public class SGA : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var sga = SabreTools.Serialization.Wrappers.SGA.Create(stream);
|
||||
if (sga == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
ExtractAll(sga, tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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
|
||||
#if NET48
|
||||
object file;
|
||||
#else
|
||||
object? file;
|
||||
#endif
|
||||
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
|
||||
#if NET48
|
||||
var parentNames = new List<string> { filename };
|
||||
#else
|
||||
var parentNames = new List<string?> { filename };
|
||||
#endif
|
||||
|
||||
// Get the parent directory
|
||||
var folder = default(object);
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
#if NET48
|
||||
case 4: folder = (item.Model.Directory as SabreTools.Models.SGA.Directory4)?.Folders?.FirstOrDefault(f => index >= f.FileStartIndex && index <= f.FileEndIndex); break;
|
||||
case 5: folder = (item.Model.Directory as SabreTools.Models.SGA.Directory5)?.Folders?.FirstOrDefault(f => index >= f.FileStartIndex && index <= f.FileEndIndex); break;
|
||||
case 6: folder = (item.Model.Directory as SabreTools.Models.SGA.Directory6)?.Folders?.FirstOrDefault(f => index >= f.FileStartIndex && index <= f.FileEndIndex); break;
|
||||
case 7: folder = (item.Model.Directory as SabreTools.Models.SGA.Directory7)?.Folders?.FirstOrDefault(f => index >= f.FileStartIndex && index <= f.FileEndIndex); break;
|
||||
#else
|
||||
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;
|
||||
#endif
|
||||
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();
|
||||
filename = Path.Combine(parentNames.Cast<string>().ToArray());
|
||||
|
||||
// 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
|
||||
{
|
||||
// Decompress the data
|
||||
data = new byte[outputFileSize];
|
||||
Inflater inflater = new Inflater();
|
||||
inflater.SetInput(compressedData);
|
||||
inflater.Inflate(data);
|
||||
}
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrWhiteSpace(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
72
BinaryObjectScanner/FileType/SevenZip.cs
Normal file
72
BinaryObjectScanner/FileType/SevenZip.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.SevenZip;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// 7-zip archive
|
||||
/// </summary>
|
||||
public class SevenZip : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
using (SevenZipArchive sevenZipFile = SevenZipArchive.Open(stream))
|
||||
{
|
||||
foreach (var entry in sevenZipFile.Entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If we have a directory, skip it
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
string tempFile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
75
BinaryObjectScanner/FileType/TapeArchive.cs
Normal file
75
BinaryObjectScanner/FileType/TapeArchive.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Tar;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Tape archive
|
||||
/// </summary>
|
||||
public class TapeArchive : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
using (TarArchive tarFile = TarArchive.Open(stream))
|
||||
{
|
||||
foreach (var entry in tarFile.Entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If we have a directory, skip it
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
string tempFile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
BinaryObjectScanner/FileType/Textfile.cs
Normal file
133
BinaryObjectScanner/FileType/Textfile.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
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/>
|
||||
#if NET48
|
||||
public string Detect(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Detect(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Files can be protected in multiple ways
|
||||
var protections = new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
// Load the current file content
|
||||
var fileContent = string.Empty;
|
||||
using (var sr = new StreamReader(stream, Encoding.Default, true, 1024 * 1024, true))
|
||||
{
|
||||
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");
|
||||
|
||||
// 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
|
||||
if (fileContent.Contains("phenoProtect"))
|
||||
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 "OEMSETUP.INF" in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]".
|
||||
if (fileContent.Contains("Sentinel Driver Disk"))
|
||||
protections.Add("Rainbow Sentinel");
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
146
BinaryObjectScanner/FileType/VBSP.cs
Normal file
146
BinaryObjectScanner/FileType/VBSP.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life 2 Level
|
||||
/// </summary>
|
||||
public class VBSP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var vbsp = SabreTools.Serialization.Wrappers.VBSP.Create(stream);
|
||||
if (vbsp == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
ExtractAllLumps(vbsp, tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex.ToString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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.IsNullOrWhiteSpace(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
195
BinaryObjectScanner/FileType/VPK.cs
Normal file
195
BinaryObjectScanner/FileType/VPK.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.IO;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Valve Package File
|
||||
/// </summary>
|
||||
public class VPK : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var vpk = SabreTools.Serialization.Wrappers.VPK.Create(stream);
|
||||
if (vpk == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
ExtractAll(vpk, tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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.IsNullOrWhiteSpace(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.OpenRead(archiveFileName);
|
||||
|
||||
// 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.IsNullOrWhiteSpace(directoryItem.Path))
|
||||
filename = Path.Combine(directoryItem.Path, filename);
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrWhiteSpace(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
137
BinaryObjectScanner/FileType/WAD.cs
Normal file
137
BinaryObjectScanner/FileType/WAD.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life Texture Package File
|
||||
/// </summary>
|
||||
public class WAD : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var wad = SabreTools.Serialization.Wrappers.WAD.Create(stream);
|
||||
if (wad == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
ExtractAllLumps(wad, tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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.IsNullOrWhiteSpace(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
BinaryObjectScanner/FileType/XZ.cs
Normal file
60
BinaryObjectScanner/FileType/XZ.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Compressors.Xz;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// xz archive
|
||||
/// </summary>
|
||||
public class XZ : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
using (XZStream xzFile = new XZStream(stream))
|
||||
{
|
||||
string tempFile = Path.Combine(tempPath, Guid.NewGuid().ToString());
|
||||
using (FileStream fs = File.OpenWrite(tempFile))
|
||||
{
|
||||
xzFile.CopyTo(fs);
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
147
BinaryObjectScanner/FileType/XZP.cs
Normal file
147
BinaryObjectScanner/FileType/XZP.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// XBox Package File
|
||||
/// </summary>
|
||||
public class XZP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var xzp = SabreTools.Serialization.Wrappers.XZP.Create(stream);
|
||||
if (xzp == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
ExtractAll(xzp, tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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.IsNullOrWhiteSpace(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
BinaryObjectScanner/GameEngine/RenderWare.cs
Normal file
44
BinaryObjectScanner/GameEngine/RenderWare.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
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 : IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
7
BinaryObjectScanner/GameEngine/_DUMMY.cs
Normal file
7
BinaryObjectScanner/GameEngine/_DUMMY.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace BinaryObjectScanner.GameEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// This class exists for reflection purposes and should not be used
|
||||
/// </summary>
|
||||
public sealed class _DUMMY { }
|
||||
}
|
||||
265
BinaryObjectScanner/Handler.cs
Normal file
265
BinaryObjectScanner/Handler.cs
Normal file
@@ -0,0 +1,265 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Utilities.Dictionary;
|
||||
|
||||
namespace BinaryObjectScanner
|
||||
{
|
||||
internal static class Handler
|
||||
{
|
||||
#region Public Collections
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IPathCheck types
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public static IEnumerable<IPathCheck> PathCheckClasses
|
||||
#else
|
||||
public static IEnumerable<IPathCheck?> PathCheckClasses
|
||||
#endif
|
||||
{
|
||||
get
|
||||
{
|
||||
if (pathCheckClasses == null)
|
||||
pathCheckClasses = InitCheckClasses<IPathCheck>();
|
||||
|
||||
return pathCheckClasses;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Instances
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IPathCheck types
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private static IEnumerable<IPathCheck> pathCheckClasses;
|
||||
#else
|
||||
private static IEnumerable<IPathCheck?>? pathCheckClasses;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Multiple Implementation Wrappers
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single path based on all path check implementations
|
||||
/// </summary>
|
||||
/// <param name="path">Path of the file or directory to check</param>
|
||||
/// <param name="scanner">Scanner object to use for options and scanning</param>
|
||||
/// <returns>Set of protections in file, null on error</returns>
|
||||
#if NET48
|
||||
public static ConcurrentDictionary<string, ConcurrentQueue<string>> HandlePathChecks(string path, IEnumerable<string> files)
|
||||
#else
|
||||
public static ConcurrentDictionary<string, ConcurrentQueue<string>> HandlePathChecks(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
// Create the output dictionary
|
||||
var protections = new ConcurrentDictionary<string, ConcurrentQueue<string>>();
|
||||
|
||||
// Preprocess the list of files
|
||||
files = files?.Select(f => f.Replace('\\', '/'))?.ToList();
|
||||
|
||||
// Iterate through all checks
|
||||
Parallel.ForEach(PathCheckClasses, checkClass =>
|
||||
{
|
||||
var subProtections = checkClass?.PerformCheck(path, files);
|
||||
if (subProtections != null)
|
||||
AppendToDictionary(protections, path, subProtections);
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Single Implementation Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handle files based on an IDetectable implementation
|
||||
/// </summary>
|
||||
/// <param name="impl">IDetectable class representing the file type</param>
|
||||
/// <param name="fileName">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, null on error</returns>
|
||||
#if NET48
|
||||
public static ConcurrentQueue<string> HandleDetectable(IDetectable impl, string fileName, Stream stream, bool includeDebug)
|
||||
#else
|
||||
public static ConcurrentQueue<string>? HandleDetectable(IDetectable impl, string fileName, Stream stream, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
var protection = impl.Detect(stream, fileName, includeDebug);
|
||||
return ProcessProtectionString(protection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle files based on an IExtractable implementation
|
||||
/// </summary>
|
||||
/// <param name="impl">IDetectable class representing the file type</param>
|
||||
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
|
||||
/// <param name="stream">Stream to scan the contents of</param>
|
||||
/// <param name="scanner">Scanner object to use on extractable contents</param>
|
||||
/// <returns>Set of protections in file, null on error</returns>
|
||||
#if NET48
|
||||
public static ConcurrentDictionary<string, ConcurrentQueue<string>> HandleExtractable(IExtractable impl, string fileName, Stream stream, Scanner scanner)
|
||||
#else
|
||||
public static ConcurrentDictionary<string, ConcurrentQueue<string>>? HandleExtractable(IExtractable impl, string fileName, Stream? stream, Scanner scanner)
|
||||
#endif
|
||||
{
|
||||
// If the extractable file itself fails
|
||||
try
|
||||
{
|
||||
// Extract and get the output path
|
||||
var tempPath = impl.Extract(stream, fileName, scanner.IncludeDebug);
|
||||
if (tempPath == null)
|
||||
return null;
|
||||
|
||||
// Collect and format all found protections
|
||||
var subProtections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
// Prepare the returned protections
|
||||
StripFromKeys(subProtections, tempPath);
|
||||
PrependToKeys(subProtections, fileName);
|
||||
return subProtections;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle files based on an IPathCheck implementation
|
||||
/// </summary>
|
||||
/// <param name="impl">IPathCheck class representing the file type</param>
|
||||
/// <param name="path">Path of the file or directory to check</param>
|
||||
/// <returns>Set of protections in path, null on error</returns>
|
||||
#if NET48
|
||||
private static ConcurrentQueue<string> PerformCheck(this IPathCheck impl, string path, IEnumerable<string> files)
|
||||
#else
|
||||
private static ConcurrentQueue<string>? PerformCheck(this IPathCheck impl, string? path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
// If we have an invalid path
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return null;
|
||||
|
||||
// Setup the output dictionary
|
||||
var protections = new ConcurrentQueue<string>();
|
||||
|
||||
// If we have a file path
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var protection = impl.CheckFilePath(path);
|
||||
var subProtections = ProcessProtectionString(protection);
|
||||
if (subProtections != null)
|
||||
protections.AddRange(subProtections);
|
||||
}
|
||||
|
||||
// If we have a directory path
|
||||
if (Directory.Exists(path) && files?.Any() == true)
|
||||
{
|
||||
var subProtections = impl.CheckDirectoryPath(path, files);
|
||||
if (subProtections != null)
|
||||
protections.AddRange(subProtections);
|
||||
}
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initializers
|
||||
|
||||
/// <summary>
|
||||
/// Initialize all implementations of a type
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private static IEnumerable<T> InitCheckClasses<T>()
|
||||
#else
|
||||
private static IEnumerable<T?> InitCheckClasses<T>()
|
||||
#endif
|
||||
{
|
||||
return InitCheckClasses<T>(typeof(GameEngine._DUMMY).Assembly)
|
||||
.Concat(InitCheckClasses<T>(typeof(Packer._DUMMY).Assembly))
|
||||
.Concat(InitCheckClasses<T>(typeof(Protection._DUMMY).Assembly));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize all implementations of a type
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private static IEnumerable<T> InitCheckClasses<T>(Assembly assembly)
|
||||
#else
|
||||
private static IEnumerable<T?> InitCheckClasses<T>(Assembly assembly)
|
||||
#endif
|
||||
{
|
||||
return assembly.GetTypes()?
|
||||
.Where(t => t.IsClass && t.GetInterface(typeof(T).Name) != null)?
|
||||
#if NET48
|
||||
.Select(t => (T)Activator.CreateInstance(t)) ?? Array.Empty<T>();
|
||||
#else
|
||||
.Select(t => (T?)Activator.CreateInstance(t)) ?? Array.Empty<T>();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Process a protection string if it includes multiple protections
|
||||
/// </summary>
|
||||
/// <param name="protection">Protection string to process</param>
|
||||
/// <returns>Set of protections parsed, null on error</returns>
|
||||
#if NET48
|
||||
private static ConcurrentQueue<string> ProcessProtectionString(string protection)
|
||||
#else
|
||||
private static ConcurrentQueue<string>? ProcessProtectionString(string? protection)
|
||||
#endif
|
||||
{
|
||||
// If we have an invalid protection string
|
||||
if (string.IsNullOrWhiteSpace(protection))
|
||||
return null;
|
||||
|
||||
// Setup the output queue
|
||||
var protections = new ConcurrentQueue<string>();
|
||||
|
||||
// If we have an indicator of multiple protections
|
||||
if (protection.Contains(";"))
|
||||
{
|
||||
var splitProtections = protection.Split(';');
|
||||
protections.AddRange(splitProtections);
|
||||
}
|
||||
else
|
||||
{
|
||||
protections.Enqueue(protection);
|
||||
}
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
21
BinaryObjectScanner/Interfaces/IContentCheck.cs
Normal file
21
BinaryObjectScanner/Interfaces/IContentCheck.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a generic file for protection
|
||||
/// </summary>
|
||||
public interface IContentCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a path for protections based on file contents
|
||||
/// </summary>
|
||||
/// <param name="file">File to check for protection indicators</param>
|
||||
/// <param name="fileContent">Byte array representing the file contents</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>String containing any protections found in the file</returns>
|
||||
#if NET48
|
||||
string CheckContents(string file, byte[] fileContent, bool includeDebug);
|
||||
#else
|
||||
string? CheckContents(string file, byte[] fileContent, bool includeDebug);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
36
BinaryObjectScanner/Interfaces/IDetectable.cs
Normal file
36
BinaryObjectScanner/Interfaces/IDetectable.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
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>
|
||||
#if NET48
|
||||
string Detect(string file, bool includeDebug);
|
||||
#else
|
||||
string? Detect(string file, bool includeDebug);
|
||||
#endif
|
||||
|
||||
/// <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>
|
||||
#if NET48
|
||||
string Detect(Stream stream, string file, bool includeDebug);
|
||||
#else
|
||||
string? Detect(Stream stream, string file, bool includeDebug);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
38
BinaryObjectScanner/Interfaces/IExtractable.cs
Normal file
38
BinaryObjectScanner/Interfaces/IExtractable.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.IO;
|
||||
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Mark a file type as being able to be extracted
|
||||
/// </summary>
|
||||
/// TODO: Change to have output directory passed in
|
||||
/// TODO: Change to return a bool
|
||||
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="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Path to extracted files, null on error</returns>
|
||||
/// <remarks>Ideally, this should just point to the other extract implementation.</remarks>
|
||||
#if NET48
|
||||
string Extract(string file, bool includeDebug);
|
||||
#else
|
||||
string? Extract(string file, bool includeDebug);
|
||||
#endif
|
||||
|
||||
/// <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="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Path to extracted files, null on error</returns>
|
||||
#if NET48
|
||||
string Extract(Stream stream, string file, bool includeDebug);
|
||||
#else
|
||||
string? Extract(Stream? stream, string file, bool includeDebug);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
23
BinaryObjectScanner/Interfaces/ILinearExecutableCheck.cs
Normal file
23
BinaryObjectScanner/Interfaces/ILinearExecutableCheck.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a Linear Executable (LE) for protection
|
||||
/// </summary>
|
||||
public interface ILinearExecutableCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a path for protections based on file contents
|
||||
/// </summary>
|
||||
/// <param name="file">File to check for protection indicators</param>
|
||||
/// <param name="lex">LinearExecutable 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>
|
||||
#if NET48
|
||||
string CheckLinearExecutable(string file, LinearExecutable lex, bool includeDebug);
|
||||
#else
|
||||
string? CheckLinearExecutable(string file, LinearExecutable lex, bool includeDebug);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
23
BinaryObjectScanner/Interfaces/IMSDOSExecutableCheck.cs
Normal file
23
BinaryObjectScanner/Interfaces/IMSDOSExecutableCheck.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a MS-DOS Executable (MZ) for protection
|
||||
/// </summary>
|
||||
public interface IMSDOSExecutableCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a path for protections based on file contents
|
||||
/// </summary>
|
||||
/// <param name="file">File to check for protection indicators</param>
|
||||
/// <param name="mz">MSDOS 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>
|
||||
#if NET48
|
||||
string CheckMSDOSExecutable(string file, MSDOS mz, bool includeDebug);
|
||||
#else
|
||||
string? CheckMSDOSExecutable(string file, MSDOS mz, bool includeDebug);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
using BurnOutSharp.ExecutableType.Microsoft.NE;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BurnOutSharp
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
// TODO: This should either include an override that takes a Stream instead of the byte[]
|
||||
internal interface INEContentCheck
|
||||
/// <summary>
|
||||
/// Check a New Executable (NE) for protection
|
||||
/// </summary>
|
||||
public interface INewExecutableCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a path for protections based on file contents
|
||||
@@ -12,6 +14,10 @@ namespace BurnOutSharp
|
||||
/// <param name="nex">NewExecutable 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 CheckNEContents(string file, NewExecutable nex, bool includeDebug);
|
||||
#if NET48
|
||||
string CheckNewExecutable(string file, NewExecutable nex, bool includeDebug);
|
||||
#else
|
||||
string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,37 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BurnOutSharp
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
internal interface IPathCheck
|
||||
/// <summary>
|
||||
/// Check a file or directory path for protection
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These checks rely primarily on filenames and paths, not file contents
|
||||
/// </remarks>
|
||||
public interface IPathCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a file path for protections based on path name
|
||||
/// </summary>
|
||||
/// <param name="path">Path to check for protection indicators</param>
|
||||
/// <param name="files">Enumerable of strings representing files in a directory</param>
|
||||
/// <remarks>This can do some limited content checking as well, but it's suggested to use IContentCheck instead, if possible</remarks>
|
||||
/// <remarks>This can do some limited content checking as well, but it's suggested to use a content check instead, if possible</remarks>
|
||||
#if NET48
|
||||
ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files);
|
||||
#else
|
||||
ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Check a file path for protections based on path name
|
||||
/// </summary>
|
||||
/// <param name="path">Path to check for protection indicators</param>
|
||||
/// <remarks>This can do some limited content checking as well, but it's suggested to use IContentCheck instead, if possible</remarks>
|
||||
/// <remarks>This can do some limited content checking as well, but it's suggested to use a content check instead, if possible</remarks>
|
||||
#if NET48
|
||||
string CheckFilePath(string path);
|
||||
#else
|
||||
string? CheckFilePath(string path);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,23 @@
|
||||
using BurnOutSharp.ExecutableType.Microsoft.PE;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BurnOutSharp
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
// TODO: This should either include an override that takes a Stream instead of the byte[]
|
||||
internal interface IPEContentCheck
|
||||
/// <summary>
|
||||
/// Check a Portable Executable (PE) for protection
|
||||
/// </summary>
|
||||
public interface IPortableExecutableCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a path for protections based on file contents
|
||||
/// </summary>
|
||||
/// <param name="file">File to check for protection indicators</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <param name="pex">PortableExecutable 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 CheckPEContents(string file, PortableExecutable pex, bool includeDebug);
|
||||
#if NET48
|
||||
string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug);
|
||||
#else
|
||||
string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
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; }
|
||||
}
|
||||
}
|
||||
671
BinaryObjectScanner/Packer/ASPack.cs
Normal file
671
BinaryObjectScanner/Packer/ASPack.cs
Normal file
@@ -0,0 +1,671 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction
|
||||
public class ASPack : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .aspack section, if it exists
|
||||
bool aspackSection = pex.ContainsSection(".aspack", exact: true);
|
||||
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();
|
||||
// var match = MatchUtil.GetFirstMatch(file, pex.EntryPointRaw, matchers, includeDebug);
|
||||
// if (!string.IsNullOrWhiteSpace(match))
|
||||
// return match;
|
||||
// }
|
||||
|
||||
// Get the .adata* section, if it exists
|
||||
var adataSection = pex.GetFirstSection(".adata", exact: false);
|
||||
if (adataSection?.Name != null)
|
||||
{
|
||||
var adataSectionRaw = pex.GetFirstSectionData(Encoding.UTF8.GetString(adataSection.Name));
|
||||
if (adataSectionRaw != null)
|
||||
{
|
||||
var matchers = GenerateMatchers();
|
||||
var match = MatchUtil.GetFirstMatch(file, adataSectionRaw, matchers, includeDebug);
|
||||
if (!string.IsNullOrWhiteSpace(match))
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the set of matchers used for each section
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private List<ContentMatchSet> GenerateMatchers()
|
||||
{
|
||||
return new List<ContentMatchSet>
|
||||
{
|
||||
#region No Wildcards (Long)
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x92, 0x1A, 0x44, 0x00, 0xB8, 0x8C, 0x1A,
|
||||
0x44, 0x00, 0x03, 0xC5, 0x2B, 0x85, 0xCD, 0x1D,
|
||||
0x44, 0x00, 0x89, 0x85, 0xD9, 0x1D, 0x44, 0x00,
|
||||
0x80, 0xBD, 0xC4, 0x1D, 0x44,
|
||||
}, "ASPack 1.00b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0xD2, 0x2A, 0x44, 0x00, 0xB8, 0xCC, 0x2A,
|
||||
0x44, 0x00, 0x03, 0xC5, 0x2B, 0x85, 0xA5, 0x2E,
|
||||
0x44, 0x00, 0x89, 0x85, 0xB1, 0x2E, 0x44, 0x00,
|
||||
0x80, 0xBD, 0x9C, 0x2E, 0x44, 0x00, 0x00, 0x75,
|
||||
0x15, 0xFE, 0x85, 0x9C, 0x2E, 0x44, 0x00, 0xE8,
|
||||
0x1D, 0x00, 0x00, 0x00, 0xE8, 0xE4, 0x01, 0x00,
|
||||
0x00, 0xE8, 0x7A, 0x02, 0x00, 0x00, 0x8B, 0x85,
|
||||
0x9D, 0x2E, 0x44, 0x00, 0x03, 0x85, 0xB1, 0x2E,
|
||||
0x44, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
|
||||
}, "ASPack 1.01b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0xD2, 0x2A, 0x44, 0x00, 0xB8, 0xCC, 0x2A,
|
||||
0x44, 0x00, 0x03, 0xC5, 0x2B, 0x85, 0xA5, 0x2E,
|
||||
0x44, 0x00, 0x89, 0x85, 0xB1, 0x2E, 0x44, 0x00,
|
||||
0x80, 0xBD, 0x9C, 0x2E, 0x44
|
||||
}, "ASPack 1.01b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x96, 0x78, 0x43, 0x00, 0xB8, 0x90, 0x78,
|
||||
0x43, 0x00, 0x03, 0xC5, 0x2B, 0x85, 0x7D, 0x7C,
|
||||
0x43, 0x00, 0x89, 0x85, 0x89, 0x7C, 0x43, 0x00,
|
||||
0x80, 0xBD, 0x74, 0x7C, 0x43, 0x00, 0x00, 0x75,
|
||||
0x15, 0xFE, 0x85, 0x74, 0x7C, 0x43, 0x00, 0xE8,
|
||||
0x1D, 0x00, 0x00, 0x00, 0xE8, 0xF7, 0x01, 0x00,
|
||||
0x00, 0xE8, 0x8E, 0x02, 0x00, 0x00, 0x8B, 0x85,
|
||||
0x75, 0x7C, 0x43, 0x00, 0x03, 0x85, 0x89, 0x7C,
|
||||
0x43, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
|
||||
}, "ASPack 1.02b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x96, 0x78, 0x43, 0x00, 0xB8, 0x90, 0x78,
|
||||
0x43, 0x00, 0x03, 0xC5, 0x2B, 0x85, 0x7D, 0x7C,
|
||||
0x43, 0x00, 0x89, 0x85, 0x89, 0x7C, 0x43, 0x00,
|
||||
0x80, 0xBD, 0x74, 0x7C, 0x43, 0x00, 0x00, 0x75,
|
||||
0x15, 0xFE, 0x85, 0x74, 0x7C, 0x43
|
||||
}, "ASPack 1.02b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x8A, 0x1C, 0x40, 0x00, 0xB9, 0x9E, 0x00,
|
||||
0x00, 0x00, 0x8D, 0xBD, 0x4C, 0x23, 0x40, 0x00,
|
||||
0x8B, 0xF7, 0x33
|
||||
}, "ASPack 1.02b"),
|
||||
|
||||
new ContentMatchSet(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?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0xAE, 0x98, 0x43, 0x00, 0xB8, 0xA8, 0x98,
|
||||
0x43, 0x00, 0x03, 0xC5, 0x2B, 0x85, 0x18, 0x9D,
|
||||
0x43, 0x00, 0x89, 0x85, 0x24, 0x9D, 0x43, 0x00,
|
||||
0x80, 0xBD, 0x0E, 0x9D, 0x43
|
||||
}, "ASPack 1.03b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0xCE, 0x3A, 0x44, 0x00, 0xB8, 0xC8, 0x3A,
|
||||
0x44, 0x00, 0x03, 0xC5, 0x2B, 0x85, 0xB5, 0x3E,
|
||||
0x44, 0x00, 0x89, 0x85, 0xC1, 0x3E, 0x44, 0x00,
|
||||
0x80, 0xBD, 0xAC, 0x3E, 0x44
|
||||
}, "ASPack 1.05b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0xEA, 0xA8, 0x43, 0x00, 0xB8, 0xE4, 0xA8,
|
||||
0x43, 0x00, 0x03, 0xC5, 0x2B, 0x85, 0x78, 0xAD,
|
||||
0x43, 0x00, 0x89, 0x85, 0x84, 0xAD, 0x43, 0x00,
|
||||
0x80, 0xBD, 0x6E, 0xAD, 0x43, 0x00, 0x00, 0x75,
|
||||
0x15, 0xFE, 0x85, 0x6E, 0xAD, 0x43, 0x00, 0xE8,
|
||||
0x1D, 0x00, 0x00, 0x00, 0xE8, 0x73, 0x02, 0x00,
|
||||
0x00, 0xE8, 0x0A, 0x03, 0x00, 0x00, 0x8B, 0x85,
|
||||
0x70, 0xAD, 0x43, 0x00, 0x03, 0x85, 0x84, 0xAD,
|
||||
0x43, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
|
||||
}, "ASPack 1.06.01b (DLL)"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0xEA, 0xA8, 0x43, 0x00, 0xB8, 0xE4, 0xA8,
|
||||
0x43, 0x00, 0x03, 0xC5, 0x2B, 0x85, 0x78, 0xAD,
|
||||
0x43, 0x00, 0x89, 0x85, 0x84, 0xAD, 0x43, 0x00,
|
||||
0x80, 0xBD, 0x6E, 0xAD, 0x43, 0x00, 0x00, 0x75,
|
||||
0x15, 0xFE, 0x85, 0x6E, 0xAD, 0x43
|
||||
}, "ASPack 1.06.01b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x3E, 0xD9, 0x43, 0x00, 0xB8, 0x38, 0xD9,
|
||||
0x43, 0x00, 0x03, 0xC5, 0x2B, 0x85, 0x0B, 0xDE,
|
||||
0x43, 0x00, 0x89, 0x85, 0x17, 0xDE, 0x43, 0x00,
|
||||
0x80, 0xBD, 0x01, 0xDE, 0x43, 0x00, 0x00, 0x75,
|
||||
0x15, 0xFE, 0x85, 0x01, 0xDE, 0x43, 0x00, 0xE8,
|
||||
0x1D, 0x00, 0x00, 0x00, 0xE8, 0x79, 0x02, 0x00,
|
||||
0x00, 0xE8, 0x12, 0x03, 0x00, 0x00, 0x8B, 0x85,
|
||||
0x03, 0xDE, 0x43, 0x00, 0x03, 0x85, 0x17, 0xDE,
|
||||
0x43, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
|
||||
}, "ASPack 1.07b (DLL)"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xEB, 0x03, 0x5D, 0xFF, 0xE5, 0xE8, 0xF8,
|
||||
0xFF, 0xFF, 0xFF, 0x81, 0xED, 0x1B, 0x6A, 0x44,
|
||||
0x00, 0xBB, 0x10, 0x6A, 0x44, 0x00, 0x03, 0xDD,
|
||||
0x2B, 0x9D, 0x2A
|
||||
}, "ASPack 1.08"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x0A, 0x4A, 0x44, 0x00, 0xBB, 0x04, 0x4A,
|
||||
0x44, 0x00, 0x03, 0xDD, 0x2B, 0x9D, 0xB1, 0x50,
|
||||
0x44, 0x00, 0x83, 0xBD, 0xAC, 0x50, 0x44, 0x00,
|
||||
0x00, 0x89, 0x9D, 0xBB, 0x4E, 0x44, 0x00, 0x0F,
|
||||
0x85, 0x17, 0x05, 0x00, 0x00, 0x8D, 0x85, 0xD1,
|
||||
0x50, 0x44, 0x00, 0x50, 0xFF, 0x95, 0x94, 0x51,
|
||||
0x44, 0x00, 0x89, 0x85, 0xCD, 0x50, 0x44, 0x00,
|
||||
0x8B, 0xF8, 0x8D, 0x9D, 0xDE, 0x50, 0x44, 0x00,
|
||||
0x53, 0x50, 0xFF, 0x95, 0x90, 0x51, 0x44, 0x00
|
||||
}, "ASPack 1.08.03"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x0A, 0x4A, 0x44, 0x00, 0xBB, 0x04, 0x4A,
|
||||
0x44, 0x00, 0x03, 0xDD, 0x2B, 0x9D, 0xB1, 0x50,
|
||||
0x44, 0x00, 0x83, 0xBD, 0xAC, 0x50, 0x44, 0x00,
|
||||
0x00, 0x89, 0x9D, 0xBB, 0x4E
|
||||
}, "ASPack 1.08.03"),
|
||||
|
||||
new ContentMatchSet(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?[]
|
||||
{
|
||||
0x60, 0xE8, 0x72, 0x05, 0x00, 0x00, 0xEB, 0x33,
|
||||
0x87, 0xDB, 0x90, 0x00
|
||||
}, "ASPack 2.00.01"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9, 0xEB,
|
||||
0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01, 0x00,
|
||||
0x00, 0x00, 0xEB, 0x5D, 0xBB, 0xED, 0xFF, 0xFF,
|
||||
0xFF, 0x03, 0xDD, 0x81, 0xEB
|
||||
}, "ASPack 2.1"),
|
||||
|
||||
new ContentMatchSet(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?[]
|
||||
{
|
||||
0x60, 0xE8, 0x02, 0x00, 0x00, 0x00, 0xEB, 0x09,
|
||||
0x5D, 0x55
|
||||
}, "ASPack 2.11b"),
|
||||
|
||||
new ContentMatchSet(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?[]
|
||||
{
|
||||
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?[]
|
||||
{
|
||||
0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9, 0xEB,
|
||||
0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01
|
||||
}, "ASPack 2.12"),
|
||||
|
||||
#endregion
|
||||
|
||||
#region Wildcards (Long)
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, 0x3E, 0xD9, 0x43, null, 0xB8, 0x38, null,
|
||||
null, null, 0x03, 0xC5, 0x2B, 0x85, 0x0B, 0xDE,
|
||||
0x43, null, 0x89, 0x85, 0x17, 0xDE, 0x43, null,
|
||||
0x80, 0xBD, 0x01, 0xDE, 0x43, null, null, 0x75,
|
||||
0x15, 0xFE, 0x85, 0x01, 0xDE, 0x43, null, 0xE8,
|
||||
0x1D, null, null, null, 0xE8, 0x79, 0x02, null,
|
||||
null, 0xE8, 0x12, 0x03, null, null, 0x8B, 0x85,
|
||||
0x03, 0xDE, 0x43, null, 0x03, 0x85, 0x17, 0xDE,
|
||||
0x43, null, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
|
||||
}, "ASPack 1.00b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, 0xD2, 0x2A, 0x44, null, 0xB8, 0xCC, 0x2A,
|
||||
0x44, null, 0x03, 0xC5, 0x2B, 0x85, 0xA5, 0x2E,
|
||||
0x44, null, 0x89, 0x85, 0xB1, 0x2E, 0x44, null,
|
||||
0x80, 0xBD, 0x9C, 0x2E, 0x44
|
||||
}, "ASPack 1.01b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, 0xCE, 0x3A, 0x44, null, 0xB8, 0xC8, 0x3A,
|
||||
0x44, null, 0x03, 0xC5, 0x2B, 0x85, 0xB5, 0x3E,
|
||||
0x44, null, 0x89, 0x85, 0xC1, 0x3E, 0x44, null,
|
||||
0x80, 0xBD, 0xAC, 0x3E, 0x44
|
||||
}, "ASPack 1.01b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, 0x3E, 0xD9, 0x43, 0x00, 0xB8, 0x38, null,
|
||||
null, 0x00, 0x03, 0xC5, 0x2B, 0x85, 0x0B, 0xDE,
|
||||
0x43, 0x00, 0x89, 0x85, 0x17, 0xDE, 0x43, 0x00,
|
||||
0x80, 0xBD, 0x01, 0xDE, 0x43, 0x00, 0x00, 0x75,
|
||||
0x15, 0xFE, 0x85, 0x01, 0xDE, 0x43, 0x00, 0xE8,
|
||||
0x1D, 0x00, 0x00, 0x00, 0xE8, 0x79, 0x02, 0x00,
|
||||
0x00, 0xE8, 0x12, 0x03, 0x00, 0x00, 0x8B, 0x85,
|
||||
0x03, 0xDE, 0x43, 0x00, 0x03, 0x85, 0x17, 0xDE,
|
||||
0x43, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
|
||||
}, "ASPack 1.02a -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(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?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, 0x96, 0x78, 0x43, null, 0xB8, 0x90, 0x78,
|
||||
0x43, null, 0x03, 0xC5, 0x2B, 0x85, 0x7D, 0x7C,
|
||||
0x43, null, 0x89, 0x85, 0x89, 0x7C, 0x43, null,
|
||||
0x80, 0xBD, 0x74, 0x7C, 0x43
|
||||
}, "ASPack 1.02b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, 0xAE, 0x98, 0x43, null, 0xB8, 0xA8, 0x98,
|
||||
0x43, null, 0x03, 0xC5, 0x2B, 0x85, 0x18, 0x9D,
|
||||
0x43, null, 0x89, 0x85, 0x24, 0x9D, 0x43, null,
|
||||
0x80, 0xBD, 0x0E, 0x9D, 0x43
|
||||
}, "ASPack 1.03b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, null, null, null, null, 0xE8, 0x0D, null,
|
||||
null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null, 0x58
|
||||
}, "ASPack 1.03b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
|
||||
0xED, null, null, null, 0x00, 0xB8, null, null,
|
||||
null, 0x00, 0x03, 0xC5, 0x2B, 0x85, null, 0x12,
|
||||
0x9D, null, 0x89, 0x85, 0x1E, 0x9D, null, 0x00,
|
||||
0x80, 0xBD, 0x08, 0x9D, null, 0x00, 0x00
|
||||
}, "ASPack 1.04b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, null, null, null, null, 0xB8, null, null,
|
||||
null, null, 0x03, 0xC5, 0x2B, 0x85, null, 0x12,
|
||||
0x9D, null, 0x89, 0x85, 0x1E, 0x9D, null, null,
|
||||
0x80, 0xBD, 0x08, 0x9D
|
||||
}, "ASPack 1.04b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, null, null, null, null, 0xB8, null, null,
|
||||
null, null, 0x03, 0xC5, 0x2B, 0x85, null, 0x0B,
|
||||
0xDE, null, 0x89, 0x85, 0x17, 0xDE, null, null,
|
||||
0x80, 0xBD, 0x01, 0xDE
|
||||
}, "ASPack 1.04b"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, 0xEA, 0xA8, 0x43, null, 0xB8, 0xE4, 0xA8,
|
||||
0x43, null, 0x03, 0xC5, 0x2B, 0x85, 0x78, 0xAD,
|
||||
0x43, null, 0x89, 0x85, 0x84, 0xAD, 0x43, null,
|
||||
0x80, 0xBD, 0x6E, 0xAD, 0x43
|
||||
}, "ASPack 1.06.1b"),
|
||||
|
||||
new ContentMatchSet(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?[]
|
||||
{
|
||||
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?[]
|
||||
{
|
||||
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?[]
|
||||
{
|
||||
0x60, 0xEB, 0x0A, 0x5D, 0xEB, 0x02, 0xFF, 0x25,
|
||||
0x45, 0xFF, 0xE5, 0xE8, 0xE9, 0xE8, 0xF1, 0xFF,
|
||||
0xFF, 0xFF, 0xE9, 0x81, null, null, null, 0x44,
|
||||
0x00, 0xBB, 0x10, null, 0x44, 0x00, 0x03, 0xDD,
|
||||
0x2B, 0x9D
|
||||
}, "ASPack 1.08.01"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xEB, 0x0A, 0x5D, 0xEB, 0x02, 0xFF, 0x25,
|
||||
0x45, 0xFF, 0xE5, 0xE8, 0xE9, 0xE8, 0xF1, 0xFF,
|
||||
0xFF, 0xFF, 0xE9, 0x81, null, null, null, 0x44,
|
||||
null, 0xBB, 0x10, null, 0x44, null, 0x03, 0xDD,
|
||||
0x2B, 0x9D
|
||||
}, "ASPack 1.08.01"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xEB, 0x0A, 0x5D, 0xEB, 0x02, 0xFF, 0x25,
|
||||
0x45, 0xFF, 0xE5, 0xE8, 0xE9, 0xE8, 0xF1, 0xFF,
|
||||
0xFF, 0xFF, 0xE9, 0x81, 0xED, 0x23, 0x6A, 0x44,
|
||||
0x00, 0xBB, 0x10, null, 0x44, 0x00, 0x03, 0xDD,
|
||||
0x2B, 0x9D, 0x72
|
||||
}, "ASPack 1.08.02"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, null,
|
||||
null, null, null, null, null, 0xBB, null, null,
|
||||
null, null, 0x03, 0xDD, 0x2B, 0x9D, 0xB1, 0x50,
|
||||
0x44, 0x00, 0x83, 0xBD, 0xAC, 0x50, 0x44, 0x00,
|
||||
0x00, 0x89, 0x9D, 0xBB, 0x4E
|
||||
}, "ASPack 1.08.03"),
|
||||
|
||||
new ContentMatchSet(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?[]
|
||||
{
|
||||
0x55, 0x57, 0x51, 0x53, 0xE8, null, null, null,
|
||||
null, 0x5D, 0x8B, 0xC5, 0x81, 0xED, null, null,
|
||||
null, null, 0x2B, 0x85, null, null, null, null,
|
||||
0x83, 0xE8, 0x09, 0x89, 0x85, null, null, null,
|
||||
null, 0x0F, 0xB6
|
||||
}, "ASPack 1.08.03"),
|
||||
|
||||
new ContentMatchSet(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?[]
|
||||
{
|
||||
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?[]
|
||||
{
|
||||
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?[]
|
||||
{
|
||||
null, 0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9,
|
||||
0xEB, 0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01,
|
||||
0x00, 0x00, 0x00, 0xEB, 0x5D, 0xBB, 0xEC, 0xFF,
|
||||
0xFF, 0xFF, 0x03, 0xDD, 0x81, 0xEB, 0x00, null,
|
||||
null, 0x00, 0x83, 0xBD, 0x22, 0x04, 0x00, 0x00,
|
||||
0x00, 0x89, 0x9D, 0x22, 0x04, 0x00, 0x00, 0x0F,
|
||||
0x85, 0x65, 0x03, 0x00, 0x00, 0x8D, 0x85, 0x2E,
|
||||
0x04, 0x00, 0x00, 0x50, 0xFF, 0x95, 0x4C, 0x0F,
|
||||
0x00, 0x00, 0x89, 0x85, 0x26, 0x04, 0x00, 0x00,
|
||||
0x8B, 0xF8, 0x8D, 0x5D, 0x5E, 0x53, 0x50, 0xFF,
|
||||
0x95, 0x48, 0x0F, 0x00, 0x00, 0x89, 0x85, 0x4C,
|
||||
0x05, 0x00, 0x00, 0x8D, 0x5D, 0x6B, 0x53, 0x57,
|
||||
0xFF, 0x95, 0x48, 0x0F
|
||||
}, "ASPack 2.12b -> Solodovnikov Alexey"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9, 0xEB,
|
||||
0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01, 0x00,
|
||||
0x00, 0x00, 0xEB, 0x5D, 0xBB, 0xED, 0xFF, 0xFF,
|
||||
0xFF, 0x03, 0xDD, null, null, null, null, null,
|
||||
null, 0x83, 0xBD, 0x7D, 0x04, 0x00, 0x00, 0x00,
|
||||
0x89, 0x9D, 0x7D, 0x04, 0x00, 0x00, 0x0F, 0x85,
|
||||
0xC0, 0x03, 0x00, 0x00, 0x8D, 0x85, 0x89, 0x04,
|
||||
0x00, 0x00, 0x50, 0xFF, 0x95, 0x09, 0x0F, 0x00,
|
||||
0x00, 0x89, 0x85, 0x81, 0x04, 0x00, 0x00, 0x8B,
|
||||
0xF0, 0x8D, 0x7D, 0x51, 0x57, 0x56, 0xFF, 0x95,
|
||||
0x05, 0x0F, 0x00, 0x00, 0xAB, 0xB0, 0x00, 0xAE,
|
||||
0x75, 0xFD, 0x38, 0x07, 0x75, 0xEE, 0x8D, 0x45,
|
||||
0x7A, 0xFF, 0xE0, 0x56, 0x69, 0x72, 0x74, 0x75,
|
||||
0x61, 0x6C, 0x41, 0x6C, 0x6C, 0x6F, 0x63, 0x00,
|
||||
0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6C, 0x46,
|
||||
0x72, 0x65, 0x65, 0x00, 0x56, 0x69, 0x72, 0x74,
|
||||
0x75, 0x61, 0x6C, 0x50, 0x72, 0x6F, 0x74, 0x65,
|
||||
0x63, 0x74, 0x00, 0x00, 0x8B, 0x9D, 0x8D, 0x05,
|
||||
0x00, 0x00, 0x0B, 0xDB, 0x74, 0x0A, 0x8B, 0x03,
|
||||
0x87, 0x85, 0x91, 0x05, 0x00, 0x00, 0x89, 0x03,
|
||||
0x8D, 0xB5, 0xBD, 0x05, 0x00, 0x00, 0x83, 0x3E,
|
||||
0x00, 0x0F, 0x84, 0x15, 0x01, 0x00, 0x00, 0x6A,
|
||||
0x04, 0x68, 0x00, 0x10, 0x00, 0x00, 0x68, 0x00,
|
||||
0x18, 0x00, 0x00, 0x6A, 0x00, 0xFF, 0x55, 0x51,
|
||||
0x89, 0x85, 0x53, 0x01, 0x00, 0x00, 0x8B, 0x46,
|
||||
0x04, 0x05, 0x0E, 0x01, 0x00, 0x00, 0x6A, 0x04,
|
||||
0x68, 0x00, 0x10, 0x00, 0x00, 0x50, 0x6A, 0x00,
|
||||
0xFF, 0x55, 0x51, 0x89, 0x85, 0x4F, 0x01, 0x00,
|
||||
0x00, 0x56, 0x8B, 0x1E, 0x03, 0x9D, 0x7D, 0x04,
|
||||
0x00, 0x00, 0xFF, 0xB5, 0x53, 0x01, 0x00, 0x00,
|
||||
0xFF, 0x76, 0x04, 0x50, 0x53, 0xE8, 0x2D, 0x05,
|
||||
0x00, 0x00, 0xB3, 0x00, 0x80, 0xFB, 0x00, 0x75,
|
||||
0x5E, 0xFE, 0x85, 0xE9, 0x00, 0x00, 0x00, 0x8B,
|
||||
0x3E, 0x03, 0xBD, 0x7D, 0x04, 0x00, 0x00, 0xFF,
|
||||
0x37, 0xC6, 0x07, 0xC3, 0xFF, 0xD7, 0x8F, 0x07,
|
||||
0x50, 0x51, 0x56, 0x53, 0x8B, 0xC8, 0x83, 0xE9,
|
||||
0x06, 0x8B, 0xB5, 0x4F, 0x01, 0x00, 0x00, 0x33,
|
||||
0xDB, 0x0B, 0xC9, 0x74, 0x2E, 0x78, 0x2C, 0xAC,
|
||||
0x3C, 0xE8, 0x74, 0x0A, 0xEB, 0x00, 0x3C, 0xE9,
|
||||
0x74, 0x04, 0x43, 0x49, 0xEB, 0xEB, 0x8B, 0x06,
|
||||
0xEB, 0x00, null, null, null, 0x75, 0xF3, 0x24,
|
||||
0x00, 0xC1, 0xC0, 0x18, 0x2B, 0xC3, 0x89, 0x06,
|
||||
0x83, 0xC3, 0x05, 0x83, 0xC6, 0x04, 0x83, 0xE9,
|
||||
0x05, 0xEB, 0xCE, 0x5B, 0x5E, 0x59, 0x58, 0xEB,
|
||||
0x08
|
||||
}, "ASPack 2.2 -> Alexey Solodovnikov & StarForce * 2009408"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
null, 0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9,
|
||||
0xEB, 0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01,
|
||||
0x00, 0x00, 0x00, 0xEB, 0x5D, 0xBB, 0xEC, 0xFF,
|
||||
0xFF, 0xFF, 0x03, 0xDD, 0x81, 0xEB, 0x00, 0x40,
|
||||
0x1C, 0x00
|
||||
}, "ASPack 2.x (without Poly) -> Solodovnikov Alexey"),
|
||||
|
||||
#endregion
|
||||
|
||||
#region 2.xx (Long)
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0xA8, 0x03, 0x00, 0x00, 0x61, 0x75, 0x08, 0xB8,
|
||||
0x01, 0x00, 0x00, 0x00, 0xC2, 0x0C, 0x00, 0x68,
|
||||
0x00, 0x00, 0x00, 0x00, 0xC3, 0x8B, 0x85, 0x26,
|
||||
0x04, 0x00, 0x00, 0x8D, 0x8D, 0x3B, 0x04, 0x00,
|
||||
0x00, 0x51, 0x50, 0xFF, 0x95
|
||||
}, "ASPack 2.xx"),
|
||||
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0xA8, 0x03, null, null, 0x61, 0x75, 0x08, 0xB8,
|
||||
0x01, null, null, null, 0xC2, 0x0C, null, 0x68,
|
||||
null, null, null, null, 0xC3, 0x8B, 0x85, 0x26,
|
||||
0x04, null, null, 0x8D, 0x8D, 0x3B, 0x04, null,
|
||||
null, 0x51, 0x50, 0xFF, 0x95
|
||||
}, "ASPack 2.xx"),
|
||||
|
||||
#endregion
|
||||
|
||||
#region Short
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x75, 0x00, 0xE9 }, "ASPack 1.05b"),
|
||||
|
||||
new ContentMatchSet(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 ContentMatchSet(new byte?[] { 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
|
||||
|
||||
new ContentMatchSet(new byte?[] { 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
|
||||
|
||||
new ContentMatchSet(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 ContentMatchSet(new byte?[] { 0x90, 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
|
||||
|
||||
new ContentMatchSet(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 ContentMatchSet(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 ContentMatchSet(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 ContentMatchSet(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 ContentMatchSet(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 ContentMatchSet(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 ContentMatchSet(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 ContentMatchSet(new byte?[] { 0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81, 0xED }, "ASPack 1.02b/1.08.03"),
|
||||
|
||||
#endregion
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
62
BinaryObjectScanner/Packer/AdvancedInstaller.cs
Normal file
62
BinaryObjectScanner/Packer/AdvancedInstaller.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
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 : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
74
BinaryObjectScanner/Packer/Armadillo.cs
Normal file
74
BinaryObjectScanner/Packer/Armadillo.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction
|
||||
// TODO: Add version checking, if possible
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class Armadillo : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.PE;
|
||||
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/
|
||||
public class AutoPlayMediaStudio : IPEContentCheck, IScannable
|
||||
// TODO: Add extraction
|
||||
public class AutoPlayMediaStudio : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldScan(byte[] magic) => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string CheckPEContents(string file, PortableExecutable pex, bool includeDebug)
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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;
|
||||
if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("AutoPlay Media Studio", StringComparison.OrdinalIgnoreCase))
|
||||
var name = pex.ProductName;
|
||||
if (name?.StartsWith("AutoPlay Media Studio", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"AutoPlay Media Studio {GetVersion(pex)}";
|
||||
|
||||
// Currently too vague, may be re-enabled in the future
|
||||
/*
|
||||
name = Utilities.GetLegalCopyright(pex);
|
||||
if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("Runtime Engine", StringComparison.OrdinalIgnoreCase))
|
||||
if (name?.StartsWith("Runtime Engine", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"AutoPlay Media Studio {GetVersion(pex)}";
|
||||
*/
|
||||
|
||||
@@ -37,32 +38,40 @@ namespace BurnOutSharp.PackerType
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.OpenRead(file))
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
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;
|
||||
|
||||
// Check the internal versions
|
||||
version = Utilities.GetInternalVersion(pex);
|
||||
version = pex.GetInternalVersion();
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return version;
|
||||
|
||||
158
BinaryObjectScanner/Packer/CExe.cs
Normal file
158
BinaryObjectScanner/Packer/CExe.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
using SabreTools.Matching;
|
||||
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 : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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 ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x25, 0x57, 0x6F, 0xC1, 0x61, 0x36, 0x01, 0x92,
|
||||
0x61, 0x36, 0x01, 0x92, 0x61, 0x36, 0x01, 0x92,
|
||||
0x61, 0x36, 0x00, 0x92, 0x7B, 0x36, 0x01, 0x92,
|
||||
0x03, 0x29, 0x12, 0x92, 0x66, 0x36, 0x01, 0x92,
|
||||
0x89, 0x29, 0x0A, 0x92, 0x60, 0x36, 0x01, 0x92,
|
||||
0xD9, 0x30, 0x07, 0x92, 0x60, 0x36, 0x01, 0x92
|
||||
}, "CExe")
|
||||
};
|
||||
|
||||
var match = MatchUtil.GetFirstMatch(file, pex.StubExecutableData, matchers, includeDebug);
|
||||
if (!string.IsNullOrWhiteSpace(match))
|
||||
return match;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Parse into an executable again for easier extraction
|
||||
var pex = PortableExecutable.Create(stream);
|
||||
if (pex == null)
|
||||
return null;
|
||||
|
||||
// Get the first resource of type 99 with index 2
|
||||
var payload = pex.FindResourceByNamedType("99, 2").FirstOrDefault();
|
||||
if (payload == null || payload.Length == 0)
|
||||
return null;
|
||||
|
||||
// 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
|
||||
Inflater inflater = new Inflater();
|
||||
inflater.SetInput(payload);
|
||||
data = new byte[payload.Length * 4];
|
||||
int read = inflater.Inflate(data);
|
||||
|
||||
// Trim the buffer to the proper size
|
||||
data = new ReadOnlySpan<byte>(data, 0, read).ToArray();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Reset the data
|
||||
data = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, LZ is used via the Windows API
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
data = SabreTools.Compression.LZ.Decompressor.Decompress(payload);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Reset the data
|
||||
data = null;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have no data
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Create the temp filename
|
||||
string tempFile = string.IsNullOrEmpty(file) ? "temp.sxe" : $"{Path.GetFileNameWithoutExtension(file)}.sxe";
|
||||
tempFile = Path.Combine(tempPath, tempFile);
|
||||
|
||||
// Write the file data to a temp file
|
||||
using (Stream tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
|
||||
{
|
||||
tempStream.Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.NE;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.PE;
|
||||
using BurnOutSharp.Matching;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
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`)
|
||||
public class EXEStealth : IContentCheck, IPEContentCheck
|
||||
// TODO: Add extraction
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class EXEStealth : IContentCheck, IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string CheckContents(string file, byte[] fileContent, bool includeDebug, PortableExecutable pex, NewExecutable nex)
|
||||
#if NET48
|
||||
public string CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
#else
|
||||
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// TODO: Obtain a sample to find where this string is in a typical executable
|
||||
if (includeDebug)
|
||||
@@ -36,13 +42,28 @@ namespace BurnOutSharp.PackerType
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string CheckPEContents(string file, PortableExecutable pex, bool includeDebug)
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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)
|
||||
@@ -60,5 +81,31 @@ namespace BurnOutSharp.PackerType
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
110
BinaryObjectScanner/Packer/EmbeddedExecutable.cs
Normal file
110
BinaryObjectScanner/Packer/EmbeddedExecutable.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
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 : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Parse into an executable again for easier extraction
|
||||
var pex = PortableExecutable.Create(stream);
|
||||
if (pex?.ResourceData == null)
|
||||
return null;
|
||||
|
||||
// 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();
|
||||
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
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(tempPath, tempFile);
|
||||
|
||||
// Write the resource data to a temp file
|
||||
using (var tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
|
||||
{
|
||||
if (tempStream != null)
|
||||
tempStream.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
65
BinaryObjectScanner/Packer/GenteeInstaller.cs
Normal file
65
BinaryObjectScanner/Packer/GenteeInstaller.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
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 : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
64
BinaryObjectScanner/Packer/HyperTechCrackProof.cs
Normal file
64
BinaryObjectScanner/Packer/HyperTechCrackProof.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System.IO;
|
||||
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 : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
113
BinaryObjectScanner/Packer/InnoSetup.cs
Normal file
113
BinaryObjectScanner/Packer/InnoSetup.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
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 : IExtractable, INewExecutableCheck, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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.IsNullOrWhiteSpace(version))
|
||||
return $"Inno Setup {version}";
|
||||
|
||||
return "Inno Setup (Unknown Version)";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
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 ContentMatchSet(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";
|
||||
}
|
||||
}
|
||||
}
|
||||
71
BinaryObjectScanner/Packer/InstallAnywhere.cs
Normal file
71
BinaryObjectScanner/Packer/InstallAnywhere.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
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 : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetVersion(PortableExecutable pex)
|
||||
{
|
||||
// Check the internal versions
|
||||
var version = pex.GetInternalVersion();
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return version;
|
||||
|
||||
return "(Unknown Version)";
|
||||
}
|
||||
}
|
||||
}
|
||||
63
BinaryObjectScanner/Packer/InstallerVISE.cs
Normal file
63
BinaryObjectScanner/Packer/InstallerVISE.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
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 : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
//TODO: Add exact version detection for Windows builds, make sure versions before 3.X are detected as well, and detect the Mac builds.
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
66
BinaryObjectScanner/Packer/IntelInstallationFramework.cs
Normal file
66
BinaryObjectScanner/Packer/IntelInstallationFramework.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction, seems to primarily use MSZip compression.
|
||||
public class IntelInstallationFramework : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
90
BinaryObjectScanner/Packer/MicrosoftCABSFX.cs
Normal file
90
BinaryObjectScanner/Packer/MicrosoftCABSFX.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
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 : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetVersion(PortableExecutable pex)
|
||||
{
|
||||
// Check the internal versions
|
||||
var version = pex.GetInternalVersion();
|
||||
if (!string.IsNullOrWhiteSpace(version))
|
||||
return $"v{version}";
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
BinaryObjectScanner/Packer/NSIS.cs
Normal file
65
BinaryObjectScanner/Packer/NSIS.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction
|
||||
public class NSIS : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var description = pex.AssemblyDescription;
|
||||
if (!string.IsNullOrWhiteSpace(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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
70
BinaryObjectScanner/Packer/NeoLite.cs
Normal file
70
BinaryObjectScanner/Packer/NeoLite.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System.IO;
|
||||
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 : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
// TODO: Find samples of NeoLite 1.X.
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// TODO: Add extraction
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
74
BinaryObjectScanner/Packer/PECompact.cs
Normal file
74
BinaryObjectScanner/Packer/PECompact.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Better version detection - https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
// TODO: Add extraction
|
||||
public class PECompact : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// 0x4F434550 is "PECO"
|
||||
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
|
||||
// on the data in the file. This may be related to information in other fields
|
||||
|
||||
// Get the pec1 section, if it exists
|
||||
bool pec1Section = pex.ContainsSection("pec1", exact: true);
|
||||
if (pec1Section)
|
||||
return "PE Compact v1.x";
|
||||
|
||||
// Get the PEC2 section, if it exists -- TODO: Verify this comment since it's pulling the .text section
|
||||
var textSection = pex.GetFirstSection(".text", exact: true);
|
||||
if (textSection != null && textSection.PointerToRelocations == 0x32434550)
|
||||
{
|
||||
if (textSection.PointerToLinenumbers != 0)
|
||||
return $"PE Compact v{textSection.PointerToLinenumbers} (internal version)";
|
||||
|
||||
return "PE Compact v2.x (or newer)";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
BinaryObjectScanner/Packer/Petite.cs
Normal file
57
BinaryObjectScanner/Packer/Petite.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System.IO;
|
||||
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 : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
86
BinaryObjectScanner/Packer/SetupFactory.cs
Normal file
86
BinaryObjectScanner/Packer/SetupFactory.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
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 : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Known to detect versions 7.0.5.1 - 9.1.0.0
|
||||
var name = pex.LegalCopyright;
|
||||
if (name?.StartsWith("Setup Engine", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"Setup Factory {GetVersion(pex)}";
|
||||
|
||||
name = pex.ProductName;
|
||||
if (name?.StartsWith("Setup Factory", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"Setup Factory {GetVersion(pex)}";
|
||||
|
||||
// Known to detect version 5.0.1 - 6.0.1.3
|
||||
name = pex.FileDescription;
|
||||
if (name?.StartsWith("Setup Factory", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"Setup Factory {GetVersion(pex)}";
|
||||
|
||||
// Longer version of the check that can be used if false positves become an issue:
|
||||
// "Setup Factory is a trademark of Indigo Rose Corporation"
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetVersion(PortableExecutable pex)
|
||||
{
|
||||
// Check the product version explicitly
|
||||
var version = pex.ProductVersion;
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return version;
|
||||
|
||||
// Check the internal versions
|
||||
version = pex.GetInternalVersion();
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return version;
|
||||
|
||||
return "(Unknown Version)";
|
||||
}
|
||||
}
|
||||
}
|
||||
78
BinaryObjectScanner/Packer/SevenZipSFX.cs
Normal file
78
BinaryObjectScanner/Packer/SevenZipSFX.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction
|
||||
public class SevenZipSFX : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
BinaryObjectScanner/Packer/Shrinker.cs
Normal file
58
BinaryObjectScanner/Packer/Shrinker.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.IO;
|
||||
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 : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
125
BinaryObjectScanner/Packer/UPX.cs
Normal file
125
BinaryObjectScanner/Packer/UPX.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
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 : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
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/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
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)";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
98
BinaryObjectScanner/Packer/WinRARSFX.cs
Normal file
98
BinaryObjectScanner/Packer/WinRARSFX.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Rar;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
public class WinRARSFX : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Should be using stream instead of file, but stream fails to extract anything. My guess is that the executable portion of the archive is causing stream to fail, but not file.
|
||||
using (RarArchive zipFile = RarArchive.Open(file, new ReaderOptions() { LookForHeader = true }))
|
||||
{
|
||||
if (!zipFile.IsComplete)
|
||||
return null;
|
||||
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
foreach (var entry in zipFile.Entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If we have a directory, skip it
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
string tempFile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
900
BinaryObjectScanner/Packer/WinZipSFX.cs
Normal file
900
BinaryObjectScanner/Packer/WinZipSFX.cs
Normal file
@@ -0,0 +1,900 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Zip;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
public class WinZipSFX : IExtractable, INewExecutableCheck, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// If the resident-name table doesnt exist
|
||||
if (nex.Model.ResidentNameTable == null)
|
||||
return null;
|
||||
|
||||
// Check for the WinZip name string
|
||||
bool winZipNameFound = nex.Model.ResidentNameTable
|
||||
.Select(rnte => rnte?.NameString == null ? string.Empty : Encoding.ASCII.GetString(rnte.NameString))
|
||||
.Any(s => s.Contains("WZ-SE-01"));
|
||||
|
||||
// If we didn't find it
|
||||
if (!winZipNameFound)
|
||||
return null;
|
||||
|
||||
// Try to get a known version
|
||||
var version = GetNEHeaderVersion(nex);
|
||||
if (!string.IsNullOrWhiteSpace(version))
|
||||
return $"WinZip SFX {version}";
|
||||
|
||||
return $"WinZip SFX Unknown Version (16-bit)";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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.IsNullOrWhiteSpace(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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Should be using stream instead of file, but stream fails to extract anything. My guess is that the executable portion of the archive is causing stream to fail, but not file.
|
||||
using (ZipArchive zipFile = ZipArchive.Open(file))
|
||||
{
|
||||
if (!zipFile.IsComplete)
|
||||
return null;
|
||||
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
foreach (var entry in zipFile.Entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If we have a directory, skip it
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
string tempFile = Path.Combine(tempPath, entry.Key);
|
||||
entry.WriteToFile(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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
|
||||
#if NET48
|
||||
private string GetNEHeaderVersion(NewExecutable nex)
|
||||
#else
|
||||
private string? GetNEHeaderVersion(NewExecutable nex)
|
||||
#endif
|
||||
{
|
||||
#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
|
||||
#if NET48
|
||||
private string GetPEExportDirectoryVersion(PortableExecutable pex)
|
||||
#else
|
||||
private string? GetPEExportDirectoryVersion(PortableExecutable pex)
|
||||
#endif
|
||||
{
|
||||
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")
|
||||
{
|
||||
switch (sfxTimeDateStamp)
|
||||
{
|
||||
case 842636344:
|
||||
return "2.0 (32-bit)";
|
||||
case 865370756:
|
||||
return "2.1 RC2 (32-bit)";
|
||||
case 869059925:
|
||||
return "2.1 (32-bit)";
|
||||
case 979049321:
|
||||
return "2.2.4003";
|
||||
case 1149714685:
|
||||
return "3.0.7158";
|
||||
case 1185211734:
|
||||
return "3.1.7556";
|
||||
case 1185211920:
|
||||
return "3.1.7556";
|
||||
case 1235490556:
|
||||
return "4.0.8421";
|
||||
case 1235490757:
|
||||
return "4.0.8421";
|
||||
case 1235490687:
|
||||
return "4.0.8421"; // 3.1.8421.0, SI32LPG?
|
||||
case 1257193383:
|
||||
return "4.0.8672"; // 3.1.8672.0
|
||||
case 1257193543:
|
||||
return "4.0.8672";
|
||||
case 1470410848:
|
||||
return "4.0.12218"; // 4.0.1221.0
|
||||
default:
|
||||
return $"{assemblyVersion} (32-bit)";
|
||||
}
|
||||
}
|
||||
|
||||
// Personal Edition
|
||||
if (sfxFileName == "VW95LE.SFX" || sfxFileName == "PE32E.SFX"
|
||||
|| sfxFileName == "wzsepe32.exe" || sfxFileName == "SI32PE.SFX"
|
||||
|| sfxFileName == "SI32LPE.SFX")
|
||||
{
|
||||
switch (sfxTimeDateStamp)
|
||||
{
|
||||
case 845061601:
|
||||
return "Personal Edition (32-bit)"; // TODO: Find version
|
||||
case 868303343:
|
||||
return "Personal Edition (32-bit)"; // TODO: Find version
|
||||
case 868304170:
|
||||
return "Personal Edition (32-bit)"; // TODO: Find version
|
||||
case 906039079:
|
||||
return "Personal Edition 2.2.1260 (32-bit)";
|
||||
case 906040543:
|
||||
return "Personal Edition 2.2.1260 (32-bit)";
|
||||
case 908628435:
|
||||
return "Personal Edition 2.2.1285 (32-bit)";
|
||||
case 908628785:
|
||||
return "Personal Edition 2.2.1285 (32-bit)";
|
||||
case 956165981:
|
||||
return "Personal Edition 2.2.3063";
|
||||
case 956166038:
|
||||
return "Personal Edition 2.2.3063";
|
||||
case 1006353695:
|
||||
return "Personal Edition 2.2.4325";
|
||||
case 1006353714:
|
||||
return "Personal Edition 2.2.4325"; // 8.1.0.0
|
||||
case 1076515698:
|
||||
return "Personal Edition 2.2.6028";
|
||||
case 1076515784:
|
||||
return "Personal Edition 2.2.6028"; // 9.0.6028.0
|
||||
case 1092688561:
|
||||
return "Personal Edition 2.2.6224";
|
||||
case 1092688645:
|
||||
return "Personal Edition 2.2.6224"; // 9.0.6224.0
|
||||
case 1125074095:
|
||||
return "Personal Edition 2.2.6604";
|
||||
case 1125074162:
|
||||
return "Personal Edition 2.2.6604"; // 10.0.6604.0
|
||||
case 1130153399:
|
||||
return "Personal Edition 2.2.6663";
|
||||
case 1130153428:
|
||||
return "Personal Edition 2.2.6663"; // 10.0.6663.0
|
||||
case 1149714176:
|
||||
return "Personal Edition 3.0.7158";
|
||||
case 1163137967:
|
||||
return "Personal Edition 3.0.7305";
|
||||
case 1163137994:
|
||||
return "Personal Edition 3.0.7313"; // 11.0.7313.0
|
||||
case 1176345383:
|
||||
return "Personal Edition 3.0.7452";
|
||||
case 1176345423:
|
||||
return "Personal Edition 3.1.7466"; // 11.1.7466.0
|
||||
case 1184106698:
|
||||
return "Personal Edition 3.1.7556";
|
||||
case 1207280880:
|
||||
return "Personal Edition 4.0.8060"; // 2.3.7382.0
|
||||
case 1207280892:
|
||||
return "Personal Edition 4.0.8094"; // 11.2.8094.0
|
||||
case 1220904506:
|
||||
return "Personal Edition 4.0.8213"; // 2.3.7382.0
|
||||
case 1220904518:
|
||||
return "Personal Edition 4.0.8252"; // 12.0.8252.0
|
||||
case 1235490648:
|
||||
return "Personal Edition 4.0.8421"; // 3.1.8421.0
|
||||
case 1242049399:
|
||||
return "Personal Edition 4.0.8497"; // 12.1.8497.0
|
||||
case 1257193469:
|
||||
return "Personal Edition 4.0.8672"; // 3.1.8672.0, SI32LPE?
|
||||
default:
|
||||
return $"Personal Edition {assemblyVersion} (32-bit)";
|
||||
}
|
||||
}
|
||||
|
||||
// Software Installation
|
||||
else if (sfxFileName == "VW95SRE.SFX" || sfxFileName == "SI32E.SFX"
|
||||
|| sfxFileName == "SI32E.WZE")
|
||||
{
|
||||
switch (sfxTimeDateStamp)
|
||||
{
|
||||
case 842636381:
|
||||
return "Software Installation 2.0 (32-bit)";
|
||||
case 865370800:
|
||||
return "Software Installation 2.1 RC2 (32-bit)";
|
||||
case 869059963:
|
||||
return "Software Installation 2.1 (32-bit)";
|
||||
case 893107697:
|
||||
return "Software Installation 2.2.1110 (32-bit)";
|
||||
case 952007369:
|
||||
return "Software Installation 2.2.3063";
|
||||
case 1006352634:
|
||||
return "Software Installation 2.2.4325"; // +Personal Edition?
|
||||
case 979049345:
|
||||
return "Software Installation 2.2.4403";
|
||||
case 1026227373:
|
||||
return "Software Installation 2.2.5196"; // +Personal Edition?
|
||||
case 1090582390:
|
||||
return "Software Installation 2.2.6202"; // +Personal Edition?
|
||||
case 1149714757:
|
||||
return "Software Installation 3.0.7158";
|
||||
case 1154357628:
|
||||
return "Software Installation 3.0.7212";
|
||||
case 1175234637:
|
||||
return "Software Installation 3.0.7454";
|
||||
case 1185211802:
|
||||
return "Software Installation 3.1.7556";
|
||||
case 1470410906:
|
||||
return "Software Installation 4.0.12218"; // 4.0.1221.0
|
||||
default:
|
||||
return $"Software Installation {assemblyVersion} (32-bit)";
|
||||
}
|
||||
}
|
||||
|
||||
switch (sfxFileName)
|
||||
{
|
||||
// Standard
|
||||
case "VW95SE.SFX":
|
||||
return "Unknown Version (32-bit)"; // TODO: Find starting version
|
||||
case "ST32E.SFX":
|
||||
return "Unknown Version (32-bit)"; // TODO: Find starting version
|
||||
case "WZIPSE32.exe":
|
||||
return "Unknown Version (32-bit)"; // TODO: Find starting version
|
||||
case "SI32LPG.SFX":
|
||||
return "Unknown Version (32-bit)"; // TODO: Find starting version
|
||||
case "ST32E.WZE":
|
||||
return "Unknown Version (32-bit)"; // TODO: Find starting version
|
||||
|
||||
// Personal Edition
|
||||
case "VW95LE.SFX":
|
||||
return "Unknown Version before Personal Edition Build 1285 (32-bit)";
|
||||
case "PE32E.SFX":
|
||||
return "Unknown Version after Personal Edition Build 1285 (32-bit)";
|
||||
case "wzsepe32.exe":
|
||||
return "Unknown Version Personal Edition (32-bit)"; // TODO: Find starting version
|
||||
case "SI32PE.SFX":
|
||||
return "Unknown Version Personal Edition (32-bit)"; // TODO: Find starting version
|
||||
case "SI32LPE.SFX":
|
||||
return "Unknown Version Personal Edition (32-bit)"; // TODO: Find starting version
|
||||
|
||||
// Software Installation
|
||||
case "VW95SRE.SFX":
|
||||
return "Unknown Version before Software Installation 2.1 (32-bit)";
|
||||
case "SI32E.SFX":
|
||||
return "Unknown Version after Software Installation 2.1 (32-bit)";
|
||||
case "SI32E.WZE":
|
||||
return "Unknown Version Software Installation (32-bit)"; // TODO: Find starting version
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
442
BinaryObjectScanner/Packer/WiseInstaller.cs
Normal file
442
BinaryObjectScanner/Packer/WiseInstaller.cs
Normal file
@@ -0,0 +1,442 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.IO;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
using Wise = WiseUnpacker.WiseUnpacker;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class WiseInstaller : IExtractable, INewExecutableCheck, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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 ContentMatchSet(new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x49, 0x6E, 0x73, 0x74 }, "Wise Installation Wizard Module"),
|
||||
|
||||
// WiseMain
|
||||
new ContentMatchSet(new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x4D, 0x61, 0x69, 0x6E }, "Wise Installation Wizard Module"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(file, data, neMatchSets, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to parse as a New Executable
|
||||
var nex = NewExecutable.Create(stream);
|
||||
if (nex != null)
|
||||
return ExtractNewExecutable(nex, file, includeDebug);
|
||||
|
||||
// Try to parse as a Portable Executable
|
||||
var pex = PortableExecutable.Create(stream);
|
||||
if (pex != null)
|
||||
return ExtractPortableExecutable(pex, file, includeDebug);
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
#if NET48
|
||||
private FormatProperty MatchesNEVersion(NewExecutable nex)
|
||||
#else
|
||||
private FormatProperty? MatchesNEVersion(NewExecutable nex)
|
||||
#endif
|
||||
{
|
||||
// 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>
|
||||
#if NET48
|
||||
private FormatProperty GetPEFormat(PortableExecutable pex)
|
||||
#else
|
||||
private FormatProperty? GetPEFormat(PortableExecutable pex)
|
||||
#endif
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to extract Wise data from a New Executable
|
||||
/// </summary>
|
||||
/// <param name="nex">New executable to check</param>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>True if it matches a known version, false otherwise</returns>
|
||||
#if NET48
|
||||
private string ExtractNewExecutable(NewExecutable nex, string file, bool includeDebug)
|
||||
#else
|
||||
private string? ExtractNewExecutable(NewExecutable nex, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: Try to find where the file data lives and how to get it
|
||||
Wise unpacker = new Wise();
|
||||
if (!unpacker.ExtractTo(file, tempPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to extract Wise data from a Portable Executable
|
||||
/// </summary>
|
||||
/// <param name="pex">Portable executable to check</param>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>True if it matches a known version, false otherwise</returns>
|
||||
#if NET48
|
||||
private string ExtractPortableExecutable(PortableExecutable pex, string file, bool includeDebug)
|
||||
#else
|
||||
private string? ExtractPortableExecutable(PortableExecutable pex, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the matching PE format
|
||||
var format = GetPEFormat(pex);
|
||||
if (format == null)
|
||||
return null;
|
||||
|
||||
// Get the overlay data for easier reading
|
||||
int overlayOffset = 0, dataStart = 0;
|
||||
var overlayData = pex.OverlayData;
|
||||
if (overlayData == null)
|
||||
return null;
|
||||
|
||||
// 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 = dataStart + format.ArchiveEnd;
|
||||
int archiveEndLoaded = overlayData.ReadInt32(ref overlayOffset);
|
||||
if (archiveEndLoaded != 0)
|
||||
format.ArchiveEnd = archiveEndLoaded;
|
||||
}
|
||||
|
||||
// Skip to the start of the archive
|
||||
overlayOffset = 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;
|
||||
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// If we have PKZIP
|
||||
if (pkzip)
|
||||
{
|
||||
string tempFile = Path.Combine(tempPath, "WISEDATA.zip");
|
||||
using (Stream tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
|
||||
{
|
||||
tempStream.Write(overlayData, overlayOffset, overlayData.Length - overlayOffset);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have DEFLATE -- TODO: Port implementation here or use DeflateStream
|
||||
else
|
||||
{
|
||||
Wise unpacker = new Wise();
|
||||
if (!unpacker.ExtractTo(file, tempPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class representing the properties of each recognized Wise installer format
|
||||
/// </summary>
|
||||
/// <see href="https://github.com/mnadareski/WiseUnpacker/blob/master/WiseUnpacker/FormatProperty.cs"/>
|
||||
private class FormatProperty
|
||||
{
|
||||
/// <summary>
|
||||
/// Offset to the executable data
|
||||
/// </summary>
|
||||
public int ExecutableOffset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if this format includes a DLL at the start or not
|
||||
/// </summary>
|
||||
public bool Dll { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Offset within the data where the archive starts
|
||||
/// </summary>
|
||||
public int ArchiveStart { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Position in the archive head of the archive end
|
||||
/// </summary>
|
||||
public int ArchiveEnd { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Format includes initialization text
|
||||
/// </summary>
|
||||
public bool InitText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Position of the filename within the data
|
||||
/// </summary>
|
||||
public int FilenamePosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Format does not include a CRC
|
||||
/// </summary>
|
||||
public bool NoCrc { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
7
BinaryObjectScanner/Packer/_DUMMY.cs
Normal file
7
BinaryObjectScanner/Packer/_DUMMY.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
/// <summary>
|
||||
/// This class exists for reflection purposes and should not be used
|
||||
/// </summary>
|
||||
public sealed class _DUMMY { }
|
||||
}
|
||||
61
BinaryObjectScanner/Packer/dotFuscator.cs
Normal file
61
BinaryObjectScanner/Packer/dotFuscator.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Add extraction
|
||||
public class dotFuscator : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public string Extract(string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
#else
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
128
BinaryObjectScanner/Protection/ActiveMARK.cs
Normal file
128
BinaryObjectScanner/Protection/ActiveMARK.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
// TODO: Figure out how to get version numbers
|
||||
public class ActiveMARK : IContentCheck, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
#else
|
||||
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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 ContentMatchSet(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/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
120
BinaryObjectScanner/Protection/AegiSoft.cs
Normal file
120
BinaryObjectScanner/Protection/AegiSoft.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
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 "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
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// There are possibly identifying Product Names for some files used in AegiSoft License Manager, but they were deemed too overmatching to use for the time being (Found in Redump entry 73521/IA item "Nova_HoyleCasino99USA")..
|
||||
// "Asc001.dll" has the Product Name "Install Dynamic Link Library".
|
||||
// "Asc002.dll" has the Product Name "Transact Dynamic Link Library".
|
||||
// "Asc003.dll" has the Product Name "Uninstall Dynamic Link Library".
|
||||
// "Asc005.dll" has the Product Name "OrderWizard Dynamic Link Library".
|
||||
// "Asc006.exe" has the Product Name "AGENT Application".
|
||||
|
||||
// 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?[]
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x5C, 0x5C, 0x2E, 0x5C,
|
||||
0x41, 0x53, 0x43, 0x4C, 0x4D
|
||||
}, "AegiSoft License Manager"),
|
||||
};
|
||||
|
||||
var match = MatchUtil.GetFirstMatch(file, dataSectionRaw, matchers, includeDebug);
|
||||
if (!string.IsNullOrWhiteSpace(match))
|
||||
return match;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
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"),
|
||||
|
||||
// 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").
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files ?? System.Array.Empty<string>(), matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckFilePath(string path)
|
||||
#else
|
||||
public string? CheckFilePath(string path)
|
||||
#endif
|
||||
{
|
||||
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"),
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
BinaryObjectScanner/Protection/AlphaAudio.cs
Normal file
9
BinaryObjectScanner/Protection/AlphaAudio.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class AlphaAudio
|
||||
{
|
||||
// TODO: Implement - http://settec.net/eng/pro_cd.html
|
||||
// References and further information:
|
||||
// https://www.cdmediaworld.com/hardware/cdrom/cd_protections_alpha.shtml
|
||||
}
|
||||
}
|
||||
48
BinaryObjectScanner/Protection/AlphaDVD.cs
Normal file
48
BinaryObjectScanner/Protection/AlphaDVD.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// Alpha-DVD is a DVD-Video copy protection created by SETTEC.
|
||||
/// References and further information:
|
||||
/// http://www.gonsuke.co.jp/protect.html
|
||||
/// http://copy2.info/copy_protect.html
|
||||
/// http://s2000.yokinihakarae.com/sub03-10-2(DVD).html
|
||||
/// https://www.cdmediaworld.com/hardware/cdrom/cd_protections_alpha.shtml
|
||||
/// </summary>
|
||||
public class AlphaDVD : IPathCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new PathMatchSet(new PathMatch("PlayDVD.exe", useEndsWith: true), "Alpha-DVD (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files ?? System.Array.Empty<string>(), matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckFilePath(string path)
|
||||
#else
|
||||
public string? CheckFilePath(string path)
|
||||
#endif
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new PathMatchSet(new PathMatch("PlayDVD.exe", useEndsWith: true), "Alpha-DVD (Unconfirmed - Please report to us on Github"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
91
BinaryObjectScanner/Protection/AlphaROM.cs
Normal file
91
BinaryObjectScanner/Protection/AlphaROM.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System.Collections.Generic;
|
||||
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 : IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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";
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
63
BinaryObjectScanner/Protection/Bitpool.cs
Normal file
63
BinaryObjectScanner/Protection/Bitpool.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
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/>
|
||||
#if NET48
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new PathMatchSet(new PathMatch("bitpool.rsc", useEndsWith: true), "Bitpool"),
|
||||
new PathMatchSet(new FilePathMatch("CD.IDX"), "Bitpool"),
|
||||
|
||||
// Completely empty file present on multiple discs with Bitpool (Redump entries 52626 and 50229).
|
||||
new PathMatchSet(new PathMatch("LEADOUT.OFS", useEndsWith: true), "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 PathMatchSet(new List<PathMatch>
|
||||
{
|
||||
new FilePathMatch("Crc_a"),
|
||||
new FilePathMatch("Crc_b"),
|
||||
new FilePathMatch("Crc_c"),
|
||||
new FilePathMatch("Crc_d"),
|
||||
}, "Bitpool"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files ?? System.Array.Empty<string>(), matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckFilePath(string path)
|
||||
#else
|
||||
public string? CheckFilePath(string path)
|
||||
#endif
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new PathMatchSet(new PathMatch("bitpool.rsc", useEndsWith: true), "Bitpool"),
|
||||
new PathMatchSet(new FilePathMatch("CD.IDX"), "Bitpool"),
|
||||
|
||||
// Completely empty file present on multiple discs with Bitpool (Redump entries 52626 and 50229).
|
||||
new PathMatchSet(new PathMatch("LEADOUT.OFS", useEndsWith: true), "Bitpool"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
172
BinaryObjectScanner/Protection/ByteShield.cs
Normal file
172
BinaryObjectScanner/Protection/ByteShield.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
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 : IPortableExecutableCheck, IPathCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
// 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 PathMatchSet(new PathMatch("Byteshield.dll", useEndsWith: true), "ByteShield Component Module"),
|
||||
new PathMatchSet(new PathMatch("Byteshield.ini", useEndsWith: true), "ByteShield"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files ?? System.Array.Empty<string>(), matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckFilePath(string path)
|
||||
#else
|
||||
public string? CheckFilePath(string path)
|
||||
#endif
|
||||
{
|
||||
// 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 PathMatchSet(new PathMatch("Byteshield.dll", useEndsWith: true), "ByteShield Component Module"),
|
||||
new PathMatchSet(new PathMatch("Byteshield.ini", useEndsWith: true), "ByteShield"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
BinaryObjectScanner/Protection/CDCheck.cs
Normal file
37
BinaryObjectScanner/Protection/CDCheck.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class CDCheck : IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
262
BinaryObjectScanner/Protection/CDDVDCops.cs
Normal file
262
BinaryObjectScanner/Protection/CDDVDCops.cs
Normal file
@@ -0,0 +1,262 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Models.PortableExecutable;
|
||||
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, INewExecutableCheck, IPathCheck, IPortableExecutableCheck
|
||||
{
|
||||
// TODO: Investigate reference to "CD32COPS.DLL" in "WETFLIPP.QZ_" in IA item "Triada_Russian_DVD_Complete_Collection_of_Erotic_Games".
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
#else
|
||||
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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 ContentMatchSet(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 ContentMatchSet(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/>
|
||||
#if NET48
|
||||
public string CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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 ContentMatchSet(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/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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 ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x57, 0x45, 0x42, 0x43, 0x4F, 0x50, 0x53
|
||||
}, "WEB-Cops")
|
||||
};
|
||||
|
||||
var match = MatchUtil.GetFirstMatch(file, pex.StubExecutableData, matchers, includeDebug);
|
||||
if (!string.IsNullOrWhiteSpace(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/>
|
||||
#if NET48
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
// 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 PathMatchSet(new PathMatch("CDCOPS.DLL", useEndsWith: true), "CD-Cops"),
|
||||
new PathMatchSet(new PathMatch(".W_X", matchExact: true, useEndsWith: true), "CD/DVD-Cops"),
|
||||
new PathMatchSet(new PathMatch(".QZ_", matchExact: true, useEndsWith: true), "CD/DVD-Cops"),
|
||||
|
||||
new PathMatchSet(new PathMatch(".GZ_", matchExact: true, useEndsWith: true), "CD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
new PathMatchSet(new PathMatch(".Qz", matchExact: true, useEndsWith: true), "CD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files ?? System.Array.Empty<string>(), matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckFilePath(string path)
|
||||
#else
|
||||
public string? CheckFilePath(string path)
|
||||
#endif
|
||||
{
|
||||
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 PathMatchSet(new PathMatch("CDCOPS.DLL", useEndsWith: true), "CD-Cops"),
|
||||
new PathMatchSet(new PathMatch(".W_X", matchExact: true, useEndsWith: true), "CD/DVD-Cops"),
|
||||
new PathMatchSet(new PathMatch(".QZ_", matchExact: true, useEndsWith: true), "CD/DVD-Cops"),
|
||||
|
||||
new PathMatchSet(new PathMatch(".GZ_", matchExact: true, useEndsWith: true), "CD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
new PathMatchSet(new PathMatch(".Qz", matchExact: true, useEndsWith: true), "CD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
|
||||
#if NET48
|
||||
public static string GetVersion(string file, byte[] fileContent, List<int> positions)
|
||||
#else
|
||||
public static string? GetVersion(string file, byte[]? fileContent, List<int> positions)
|
||||
#endif
|
||||
{
|
||||
// If we have no content
|
||||
if (fileContent == null)
|
||||
return null;
|
||||
|
||||
char[] version = new ArraySegment<byte>(fileContent, positions[0] + 15, 4).Select(b => (char)b).ToArray();
|
||||
if (version[0] == 0x00)
|
||||
return string.Empty;
|
||||
|
||||
return new string(version);
|
||||
}
|
||||
}
|
||||
}
|
||||
93
BinaryObjectScanner/Protection/CDGuard.cs
Normal file
93
BinaryObjectScanner/Protection/CDGuard.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
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 : IPathCheck, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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/>
|
||||
#if NET48
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Found in Redump entry 97142.
|
||||
new PathMatchSet(new PathMatch("cdguard.dll", useEndsWith: true), "CD-Guard Copy Protection System"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files ?? System.Array.Empty<string>(), matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckFilePath(string path)
|
||||
#else
|
||||
public string? CheckFilePath(string path)
|
||||
#endif
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Found in Redump entry 97142.
|
||||
new PathMatchSet(new PathMatch("cdguard.dll", useEndsWith: true), "CD-Guard Copy Protection System"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
BinaryObjectScanner/Protection/CDKey.cs
Normal file
28
BinaryObjectScanner/Protection/CDKey.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class CDKey : IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
107
BinaryObjectScanner/Protection/CDLock.cs
Normal file
107
BinaryObjectScanner/Protection/CDLock.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
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.
|
||||
/// It makes use of padded dummy files with an AFP extension, with there being 4 such files on a disc.
|
||||
/// These files typically have a filename consistening of seemingly random 4 characters with an AFP extension, though there are known exceptions.
|
||||
/// These exceptions have "afp" files with the names "Gang1.afp", "Gang2.afp", "Gang3.afp", and "Gang4.afp" (Redump entries 37788 and 43221).
|
||||
/// There also always seems to be a "CONFIG.AFP" file present, which in most cases contains a marker relating to the other AFP files present on the disc.
|
||||
/// At least one game breaks this mould, having a "CONFIG.AFP" file present but is filled with 0x20 bytes instead of the expected data (Redump entries 37788 and 43221).
|
||||
/// Games protected with CD-Lock also appear to consistently have 3 additional tracks relating to the protection appended. These are 2 audio tracks, followed by a data track.
|
||||
/// Unlike Bitpool, it seems that these additional tracks are appended whether or not there are intentional audio tracks present on the disc as well. Compare Redump entries 37788 and 66321.
|
||||
/// It appears that this latter data track points to the data of the first data track, giving the appearance to some software that the disc contains 2 equally sized data tracks.
|
||||
/// It seems to me that this is intended to confuse dumping software, possibly attempting to lead it to believe that the disc has a larger capacity than it physically could have.
|
||||
/// https://inxs8.tripod.com/illegal_toc.htm seems to confirm that the software of the time especially seems to have had issues with this, though none of the technical information on the page seems to be particularly correct.
|
||||
/// Based off of Redump entries 31615 and 37700, it seems that there are cases where the main data track on two separate discs is identical, but the protection tracks differ. The reason for this is unknown.
|
||||
/// There are a few discs where one of the protection audio tracks is quite large, sometimes even larger than the main data track. The reason for this is also unknown (Redump entries 43221 and 66749).
|
||||
/// https://www.softpedia.com/get/Security/Encrypting/CD-Lock.shtml seems to be an unrelated program that protects CDs via encryption.
|
||||
/// 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
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return 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?[]
|
||||
{
|
||||
0x32, 0xF2, 0x02, 0x82, 0xC3, 0xBC, 0x0B, 0x24,
|
||||
0x99, 0xAD, 0x27, 0x43, 0xE4, 0x9D, 0x73, 0x74,
|
||||
0x99, 0xFA, 0x32, 0x24, 0x9D, 0x29, 0x34, 0xFF,
|
||||
0x74
|
||||
}, "CD-Lock"),
|
||||
};
|
||||
|
||||
var match = MatchUtil.GetFirstMatch(file, dataSectionRaw, matchers, includeDebug);
|
||||
if (!string.IsNullOrWhiteSpace(match))
|
||||
return match;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
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"),
|
||||
|
||||
// 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).
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files ?? System.Array.Empty<string>(), matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckFilePath(string path)
|
||||
#else
|
||||
public string? CheckFilePath(string path)
|
||||
#endif
|
||||
{
|
||||
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"),
|
||||
|
||||
// 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).
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
71
BinaryObjectScanner/Protection/CDProtector.cs
Normal file
71
BinaryObjectScanner/Protection/CDProtector.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
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/>
|
||||
#if NET48
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
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 PathMatchSet(new PathMatch("_cdp16.dat", useEndsWith: true), "CD-Protector"),
|
||||
new PathMatchSet(new PathMatch("_cdp16.dll", useEndsWith: true), "CD-Protector"),
|
||||
new PathMatchSet(new PathMatch("_cdp32.dat", useEndsWith: true), "CD-Protector"),
|
||||
new PathMatchSet(new PathMatch("_cdp32.dll", useEndsWith: true), "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 PathMatchSet(new PathMatch("Track#1 - Track#2 Cd-Protector.wav", useEndsWith: true), "CD-Protector"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files ?? System.Array.Empty<string>(), matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckFilePath(string path)
|
||||
#else
|
||||
public string? CheckFilePath(string path)
|
||||
#endif
|
||||
{
|
||||
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 PathMatchSet(new PathMatch("_cdp16.dat", useEndsWith: true), "CD-Protector"),
|
||||
new PathMatchSet(new PathMatch("_cdp16.dll", useEndsWith: true), "CD-Protector"),
|
||||
new PathMatchSet(new PathMatch("_cdp32.dat", useEndsWith: true), "CD-Protector"),
|
||||
new PathMatchSet(new PathMatch("_cdp32.dll", useEndsWith: true), "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 PathMatchSet(new PathMatch("Track#1 - Track#2 Cd-Protector.wav", useEndsWith: true), "CD-Protector"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
42
BinaryObjectScanner/Protection/CDSHiELDSE.cs
Normal file
42
BinaryObjectScanner/Protection/CDSHiELDSE.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class CDSHiELDSE : IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,42 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using BurnOutSharp.Matching;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BurnOutSharp.ProtectionType
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class CDX : IPathCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
// TODO: Verify if these are OR or AND
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new PathMatchSet(new PathMatch("CHKCDX16.DLL", useEndsWith: true), "CD-X"),
|
||||
new PathMatchSet(new PathMatch("CHKCDX32.DLL", useEndsWith: true), "CD-X"),
|
||||
new PathMatchSet(new PathMatch("CHKCDXNT.DLL", useEndsWith: true), "CD-X"),
|
||||
new PathMatchSet(new PathMatch("CHKCDX16.DLL", useEndsWith: true), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
new PathMatchSet(new PathMatch("CHKCDX32.DLL", useEndsWith: true), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
new PathMatchSet(new PathMatch("CHKCDXNT.DLL", useEndsWith: true), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
return MatchUtil.GetAllMatches(files ?? System.Array.Empty<string>(), matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckFilePath(string path)
|
||||
#else
|
||||
public string? CheckFilePath(string path)
|
||||
#endif
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new PathMatchSet(new PathMatch("CHKCDX16.DLL", useEndsWith: true), "CD-X"),
|
||||
new PathMatchSet(new PathMatch("CHKCDX32.DLL", useEndsWith: true), "CD-X"),
|
||||
new PathMatchSet(new PathMatch("CHKCDXNT.DLL", useEndsWith: true), "CD-X"),
|
||||
new PathMatchSet(new PathMatch("CHKCDX16.DLL", useEndsWith: true), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
new PathMatchSet(new PathMatch("CHKCDX32.DLL", useEndsWith: true), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
new PathMatchSet(new PathMatch("CHKCDXNT.DLL", useEndsWith: true), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
37
BinaryObjectScanner/Protection/CactusDataShield.cs
Normal file
37
BinaryObjectScanner/Protection/CactusDataShield.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class CactusDataShield : IContentCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
#else
|
||||
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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 ContentMatchSet(new byte?[] { 0x43, 0x44, 0x53, 0x50, 0x6C, 0x61, 0x79, 0x65, 0x72 }, "Cactus Data Shield 200"),
|
||||
|
||||
// yucca.cds
|
||||
new ContentMatchSet(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
93
BinaryObjectScanner/Protection/CenegaProtectDVD.cs
Normal file
93
BinaryObjectScanner/Protection/CenegaProtectDVD.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
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 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
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// 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" in Redump entry 31422 and IA item "speed-pack".
|
||||
cenegaSection = pex.ContainsSection(".cenega0", exact: true);
|
||||
if (cenegaSection)
|
||||
return "Cenega ProtectDVD";
|
||||
|
||||
cenegaSection = pex.ContainsSection(".cenega1", exact: true);
|
||||
if (cenegaSection)
|
||||
return "Cenega ProtectDVD";
|
||||
|
||||
cenegaSection = pex.ContainsSection(".cenega2", exact: true);
|
||||
if (cenegaSection)
|
||||
return "Cenega ProtectDVD";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
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.
|
||||
// Found in Redump entry 31422 and IA item "speed-pack".
|
||||
new PathMatchSet(new PathMatch("cenega.dll", useEndsWith: true), "Cenega ProtectDVD"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files ?? System.Array.Empty<string>(), matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckFilePath(string path)
|
||||
#else
|
||||
public string? CheckFilePath(string path)
|
||||
#endif
|
||||
{
|
||||
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.
|
||||
// Found in Redump entry 31422 and IA item "speed-pack".
|
||||
new PathMatchSet(new PathMatch("cenega.dll", useEndsWith: true), "Cenega ProtectDVD"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
98
BinaryObjectScanner/Protection/ChosenBytesCodeLock.cs
Normal file
98
BinaryObjectScanner/Protection/ChosenBytesCodeLock.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// Code-Lock by ChosenBytes (https://web.archive.org/web/20040225072021/http://chosenbytes.com/) is a form of DRM that protects Visual Basic and .NET programs.
|
||||
/// It requires the use of a registration code that is unique to each individual user (https://web.archive.org/web/20031210093259/http://www.chosenbytes.com/codemain.php).
|
||||
/// As an advertising point, ChosenBytes apparently held a contest with a cash prize to see if a protected file could be cracked (https://web.archive.org/web/20031106163334/http://www.chosenbytes.com/challenge.php).
|
||||
///
|
||||
/// Previous versions of BurnOutSharp incorrectly reported this DRM as "CodeLock / CodeLok / CopyLok". It was later discovered that due to the similar names, two entirely different DRM were erroneously lumped together.
|
||||
/// Not only is "CodeLok / CopyLok" an entirely separate form of DRM, but "CodeLock" (as opposed to "Code-Lock") appears to refer specifically to another unrelated DRM (https://web.archive.org/web/20031106033758/http://www.codelock.co.nz/).
|
||||
/// Also not to be confused with https://www.youtube.com/watch?v=AHqdgk0uJyc.
|
||||
///
|
||||
/// Code-Lock FAQ: https://web.archive.org/web/20041205165232/http://www.chosenbytes.com/codefaq.php
|
||||
/// Download (Version 2.35 trial): https://web.archive.org/web/20060220121200/http://www.chosenbytes.com:80/Code-Lock_cnet.zip
|
||||
/// </summary>
|
||||
public class ChosenBytesCodeLock : IPathCheck, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Found in "Code-Lock.ocx" in Code-Lock version 2.35.
|
||||
// Also worth noting is the File Description for this file, which is "A future for you, a challenge for the rest.".
|
||||
var name = pex.ProductName;
|
||||
if (name?.StartsWith("Code-Lock", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"ChosenBytes Code-Lock {pex.ProductVersion}";
|
||||
|
||||
// Get the .text section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".text");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("CODE-LOCK.OCX")))
|
||||
return "ChosenBytes Code-Lock";
|
||||
|
||||
if (strs.Any(s => s.Contains("Code-Lock.ocx")))
|
||||
return "ChosenBytes Code-Lock";
|
||||
|
||||
if (strs.Any(s => s.Contains("CodeLock.Secure")))
|
||||
return "ChosenBytes Code-Lock";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Found in the installation directory of Code-Lock version 2.35.
|
||||
new PathMatchSet(new PathMatch("Code-Lock.chm", useEndsWith: true), "ChosenBytes Code-Lock"),
|
||||
new PathMatchSet(new PathMatch("Code-Lock.DEP", useEndsWith: true), "ChosenBytes Code-Lock"),
|
||||
new PathMatchSet(new PathMatch("Code-Lock.ocx", useEndsWith: true), "ChosenBytes Code-Lock"),
|
||||
new PathMatchSet(new PathMatch("Code-Lock Wizard.exe", useEndsWith: true), "ChosenBytes Code-Lock"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files ?? System.Array.Empty<string>(), matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckFilePath(string path)
|
||||
#else
|
||||
public string? CheckFilePath(string path)
|
||||
#endif
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Found in the installation directory of Code-Lock version 2.35.
|
||||
new PathMatchSet(new PathMatch("Code-Lock.chm", useEndsWith: true), "ChosenBytes Code-Lock"),
|
||||
new PathMatchSet(new PathMatch("Code-Lock.DEP", useEndsWith: true), "ChosenBytes Code-Lock"),
|
||||
new PathMatchSet(new PathMatch("Code-Lock.ocx", useEndsWith: true), "ChosenBytes Code-Lock"),
|
||||
new PathMatchSet(new PathMatch("Code-Lock Wizard.exe", useEndsWith: true), "ChosenBytes Code-Lock"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,18 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.NE;
|
||||
using BurnOutSharp.ExecutableType.Microsoft.PE;
|
||||
using BurnOutSharp.Matching;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BurnOutSharp.ProtectionType
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class CopyKiller : IContentCheck, IPathCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string CheckContents(string file, byte[] fileContent, bool includeDebug, PortableExecutable pex, NewExecutable nex)
|
||||
#if NET48
|
||||
public string CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
#else
|
||||
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// TODO: Obtain a sample to find where this string is in a typical executable
|
||||
if (includeDebug)
|
||||
@@ -31,7 +34,11 @@ namespace BurnOutSharp.ProtectionType
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
// TODO: The following checks are overly broad and should be refined
|
||||
// TODO: Look into .PFF files as an indicator. At least one disc has those oversized files
|
||||
@@ -40,11 +47,15 @@ namespace BurnOutSharp.ProtectionType
|
||||
//new PathMatchSet(new PathMatch("Autorun.dat", useEndsWith: true), "CopyKiller"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
return MatchUtil.GetAllMatches(files ?? System.Array.Empty<string>(), matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckFilePath(string path)
|
||||
#else
|
||||
public string? CheckFilePath(string path)
|
||||
#endif
|
||||
{
|
||||
// TODO: The following checks are overly broad and should be refined
|
||||
// TODO: Look into .PFF files as an indicator. At least one disc has those oversized files
|
||||
47
BinaryObjectScanner/Protection/CopyLok.cs
Normal file
47
BinaryObjectScanner/Protection/CopyLok.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// CopyLok (AKA CodeLok) is a DRM created by PAN Technology (https://web.archive.org/web/19991117100625/http://www.pantechnology.com/).
|
||||
/// More specifically, it may have been created a spin-off division known as "Panlok" (https://cdmediaworld.com/hardware/cdrom/cd_protections_copylok.shtml).
|
||||
/// Though it may also be that "PanLok" was an alternate name for the program itself (https://gamecopyworld.com/games/pc_generic_copylok.shtml).
|
||||
/// There was a PanLok website, but it's unknown what content it had hosted (https://web.archive.org/web/20041215075727/http://www.panlok.com/).
|
||||
///
|
||||
/// CopyLok made use of bad sectors, using an average of 720-750 per disc, and appears to have been a form of ring protection (http://forum.redump.org/topic/29842/issues-dumping-pc-disc-with-code-lock-copy-protection/).
|
||||
/// At least one disc with CopyLok appears to contain an excerpt of the poem "Jabberwocky" by Lewis Carroll in the raw sector data (http://forum.redump.org/post/54050/#p54050).
|
||||
/// According to the Readme for poxylok (https://gf.wiretarget.com/copylok.htm), some version of Gangsters 2 may have this protection.
|
||||
///
|
||||
/// Previous versions of BurnOutSharp incorrectly reported this DRM as "CodeLock / CodeLok / CopyLok". It was later discovered that due to the similar names, two entirely different DRM were erroneously lumped together.
|
||||
/// "CodeLock" (in this case actually referring to "Code-Lock") is an entirely separate form of DRM, with the existing check now getting used separately.
|
||||
/// Also not to be confused with https://en.wikipedia.org/wiki/Rob_Northen_copylock.
|
||||
///
|
||||
/// COPYLOK trademark: https://www.trademarkelite.com/europe/trademark/trademark-detail/000618512/COPYLOK.
|
||||
/// </summary>
|
||||
public class CopyLok : IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// If there are more than 2 icd-prefixed sections, then we have a match
|
||||
// Though this is the same name that SafeDisc uses for protected executables, this seems to be a coincidence.
|
||||
// Found in Redump entries 31557, 31674, 31675, 31708, 38239, 44210, and 53929.
|
||||
int icdSectionCount = pex.SectionNames?.Count(s => s.StartsWith("icd")) ?? 0;
|
||||
if (icdSectionCount >= 2)
|
||||
return "CopyLok / CodeLok";
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
BinaryObjectScanner/Protection/CrypKey.cs
Normal file
51
BinaryObjectScanner/Protection/CrypKey.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
// http://www.crypkey.com/products/cdlock/cdmain.html
|
||||
// https://github.com/horsicq/Detect-It-Easy/blob/master/db/PE/CrypKey%20Installer.1.sg
|
||||
// https://github.com/horsicq/Detect-It-Easy/blob/master/db/PE/CrypKey.2.sg
|
||||
// https://github.com/wolfram77web/app-peid/blob/master/userdb.txt
|
||||
public class CrypKey : IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the CrypKey version from the VersionInfo, if it exists
|
||||
string version = pex.GetVersionInfoString("CrypKey Version") ?? string.Empty;
|
||||
|
||||
// Found in 'cki32k.dll'
|
||||
var name = pex.CompanyName;
|
||||
if (name?.StartsWith("CrypKey") == true)
|
||||
return $"CrypKey {version}".TrimEnd();
|
||||
|
||||
// Found in 'cki32k.dll'
|
||||
name = pex.FileDescription;
|
||||
if (name?.StartsWith("CrypKey") == true)
|
||||
return $"CrypKey {version}".TrimEnd();
|
||||
|
||||
// Found in 'cki32k.dll'
|
||||
name = pex.LegalCopyright;
|
||||
if (name?.Contains("CrypKey") == true)
|
||||
return $"CrypKey {version}".TrimEnd();
|
||||
|
||||
// Found in 'cki32k.dll'
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return $"CrypKey {version}".TrimEnd();
|
||||
|
||||
// TODO: Look into the `.loader`,`.wreloc`, `.widata`, and `.hooks` sections
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
BinaryObjectScanner/Protection/Cucko.cs
Normal file
51
BinaryObjectScanner/Protection/Cucko.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
// TODO: Do more research into the Cucko protection:
|
||||
// - Reference to `EASTL` and `EAStdC` are standard for EA products and does not indicate Cucko by itself
|
||||
// - There's little information outside of PiD detection that actually knows about Cucko
|
||||
// - Cucko is confirmed to, at least, use DMI checks.
|
||||
public class Cucko : IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// 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>
|
||||
{
|
||||
// Confirmed to detect most examples known of Cucko. The only known exception is the version of "TSLHost.dll" included on Redump entry 36119.
|
||||
// ŠU‰8...…™...ŠUŠ8T...
|
||||
new ContentMatchSet(new byte?[]
|
||||
{
|
||||
0x8A, 0x55, 0x89, 0x38, 0x14, 0x1E, 0x0F, 0x85,
|
||||
0x99, 0x00, 0x00, 0x00, 0x8A, 0x55, 0x8A, 0x38,
|
||||
0x54, 0x1E, 0x01, 0x0F
|
||||
}, "Cucko (EA Custom)")
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(file, textData, matchers, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user