mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-02-04 13:45:28 +00:00
Compare commits
1735 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
810d20d95c | ||
|
|
de578511bf | ||
|
|
f1ec025950 | ||
|
|
0c58ecc548 | ||
|
|
d2a73a153b | ||
|
|
eae2e3366b | ||
|
|
afb04c99c0 | ||
|
|
1d3bd2f8b1 | ||
|
|
3f52c24713 | ||
|
|
ae1417a343 | ||
|
|
871a3e6366 | ||
|
|
3457b807cb | ||
|
|
027f295d21 | ||
|
|
63e6d1e285 | ||
|
|
2193095f70 | ||
|
|
074694298f | ||
|
|
ce4d32b053 | ||
|
|
a25af3940c | ||
|
|
9d1a2db45a | ||
|
|
5cdf269a3e | ||
|
|
b9d90ec35d | ||
|
|
5fc1d3254d | ||
|
|
d61bae8e61 | ||
|
|
f8f53869ae | ||
|
|
f5146a6e35 | ||
|
|
faf96b9375 | ||
|
|
2228e344f6 | ||
|
|
9955bdcab1 | ||
|
|
4586d49a3f | ||
|
|
1f4e24452a | ||
|
|
090bac4d59 | ||
|
|
59bedf5fce | ||
|
|
1bbc541957 | ||
|
|
1bb0107ceb | ||
|
|
10dad356cd | ||
|
|
22b6971e51 | ||
|
|
3203b56ef6 | ||
|
|
d6db84152f | ||
|
|
b7afad5a4a | ||
|
|
9d6c53f631 | ||
|
|
aa7b02dfc3 | ||
|
|
379ffaf61a | ||
|
|
1bdfccddbc | ||
|
|
c83cdd590c | ||
|
|
f4770374a7 | ||
|
|
72880e93bc | ||
|
|
6c9cd72948 | ||
|
|
2e71ef4635 | ||
|
|
04cd1098ea | ||
|
|
e76ce64568 | ||
|
|
8fe84abef3 | ||
|
|
1b1fa53547 | ||
|
|
5019407f35 | ||
|
|
83ba19eccb | ||
|
|
936bf38521 | ||
|
|
f54b0d2bbb | ||
|
|
0e32abc76c | ||
|
|
94cb06a3bd | ||
|
|
907aea443e | ||
|
|
385922723c | ||
|
|
3061c2f009 | ||
|
|
5c0ccbde35 | ||
|
|
7b998de2ca | ||
|
|
b0d49f52a5 | ||
|
|
6f9bcc2111 | ||
|
|
0fb0ecd28a | ||
|
|
6194d88aec | ||
|
|
b02c3121fe | ||
|
|
580db0cb65 | ||
|
|
6bcdc0e3c6 | ||
|
|
9b4fd91717 | ||
|
|
9421249b8e | ||
|
|
e823cbaee5 | ||
|
|
c34618554b | ||
|
|
6ab7b4a004 | ||
|
|
5b6bf3b73e | ||
|
|
2a30a13f5f | ||
|
|
3c05a112ca | ||
|
|
66af2d83b8 | ||
|
|
fc3be76657 | ||
|
|
259a91dd77 | ||
|
|
e957b29bae | ||
|
|
33060d9787 | ||
|
|
71ca79a456 | ||
|
|
fc744d241a | ||
|
|
78b5b3dbc1 | ||
|
|
cb3846261a | ||
|
|
9a3dcf70de | ||
|
|
670c78302f | ||
|
|
f4cb97b3bf | ||
|
|
be6d44ed04 | ||
|
|
cfc9092479 | ||
|
|
d674ae5b1f | ||
|
|
864972e575 | ||
|
|
7d97850cb0 | ||
|
|
3e33f098a6 | ||
|
|
adf9ce5e2a | ||
|
|
0c5dff71e9 | ||
|
|
715f773672 | ||
|
|
a54b89d380 | ||
|
|
2085c306ab | ||
|
|
98b99da0bc | ||
|
|
e00238b24e | ||
|
|
aef2b756c9 | ||
|
|
f1f1e20a3e | ||
|
|
62a2fdeaa6 | ||
|
|
37c1852058 | ||
|
|
99c4a08d83 | ||
|
|
9d4bc6bfab | ||
|
|
71fd5af48e | ||
|
|
995521b789 | ||
|
|
5031985883 | ||
|
|
5759090291 | ||
|
|
008c1c89fb | ||
|
|
624eb40315 | ||
|
|
aaa12ae817 | ||
|
|
d37c90878c | ||
|
|
668a631c11 | ||
|
|
f06c0f4553 | ||
|
|
a0b13a6e6f | ||
|
|
1bd9f3fd88 | ||
|
|
93ba88a35f | ||
|
|
c86b1251a3 | ||
|
|
6f6954b270 | ||
|
|
1d4ed425f1 | ||
|
|
ba657e28ad | ||
|
|
7575353597 | ||
|
|
10d3c09cfa | ||
|
|
5001c4a881 | ||
|
|
7ecd0d1893 | ||
|
|
ae802d5d75 | ||
|
|
3b4266246d | ||
|
|
a801e720b2 | ||
|
|
a52d45f7c2 | ||
|
|
57eaa1f04c | ||
|
|
6cc2cc5be2 | ||
|
|
ebcdc08a77 | ||
|
|
7aebdf56fc | ||
|
|
6de36eb71c | ||
|
|
5ebd392c5b | ||
|
|
5fc2029725 | ||
|
|
45e4a01fc1 | ||
|
|
c4ea7891ea | ||
|
|
de871fb8c1 | ||
|
|
c322eebb98 | ||
|
|
ea6b0f1ca3 | ||
|
|
a35a9a4ab6 | ||
|
|
9a93c7b15d | ||
|
|
43eed75635 | ||
|
|
74ea6e6002 | ||
|
|
91e2157622 | ||
|
|
87961e5451 | ||
|
|
4ff203f393 | ||
|
|
410b2bef2b | ||
|
|
a9792fdff1 | ||
|
|
92e36527fd | ||
|
|
aa1d7d475c | ||
|
|
91185c4fe1 | ||
|
|
c527b1911f | ||
|
|
d1501b2e3e | ||
|
|
03ac117844 | ||
|
|
515be8b025 | ||
|
|
c3479450f5 | ||
|
|
611aa3229c | ||
|
|
5a865f3d08 | ||
|
|
9d8d5c23c8 | ||
|
|
3fed1a3282 | ||
|
|
f47387c3a4 | ||
|
|
133272acb7 | ||
|
|
c49ae98df5 | ||
|
|
a1672a9bc0 | ||
|
|
ffc2e23b2f | ||
|
|
c481e73418 | ||
|
|
06b3124b62 | ||
|
|
d7d81665a0 | ||
|
|
24c542c22f | ||
|
|
ec616fcdac | ||
|
|
24e9455733 | ||
|
|
58aaf46a0e | ||
|
|
54e92fe9c8 | ||
|
|
4bb83c5d86 | ||
|
|
2f9280460e | ||
|
|
30e8e79cf7 | ||
|
|
7bb0d4f39a | ||
|
|
046814b7c4 | ||
|
|
cf00348d46 | ||
|
|
8466edf80f | ||
|
|
ccdf539ed4 | ||
|
|
aeee6e9cda | ||
|
|
2af21cb245 | ||
|
|
96fb5a2f93 | ||
|
|
1eaefb16ba | ||
|
|
1c972a29a7 | ||
|
|
aa33c083fe | ||
|
|
a9454e96ed | ||
|
|
fb6fa85cd3 | ||
|
|
3c12bdc212 | ||
|
|
5eeee760f7 | ||
|
|
cfe889d5b3 | ||
|
|
3045c41eda | ||
|
|
d194ef9dd8 | ||
|
|
aeb379f307 | ||
|
|
cae0edfb49 | ||
|
|
e97558db1f | ||
|
|
06ecc1a571 | ||
|
|
adcf9ee27e | ||
|
|
a8c759be55 | ||
|
|
ecf97de439 | ||
|
|
f134de6609 | ||
|
|
d91ce3100b | ||
|
|
20de40014d | ||
|
|
a7f9a99d10 | ||
|
|
4bdfbabaab | ||
|
|
5465c57d96 | ||
|
|
5082c8f705 | ||
|
|
509fff4684 | ||
|
|
9d2c14f809 | ||
|
|
b023549b87 | ||
|
|
0983073538 | ||
|
|
815acf2ce0 | ||
|
|
6b8596466b | ||
|
|
1e5bb7df64 | ||
|
|
e920bfc69c | ||
|
|
a85c6f4028 | ||
|
|
a75bc15f29 | ||
|
|
659aa30fb3 | ||
|
|
54a11916d2 | ||
|
|
47f423d092 | ||
|
|
5c5e68e31d | ||
|
|
9e917e2bb9 | ||
|
|
4bd4d2f395 | ||
|
|
6406248840 | ||
|
|
3d8134bbd3 | ||
|
|
15310a6c47 | ||
|
|
450a8cd5bd | ||
|
|
088f99942f | ||
|
|
3e97bd8d2d | ||
|
|
866051c975 | ||
|
|
0be437f3cf | ||
|
|
11f3dec65d | ||
|
|
912d151164 | ||
|
|
863678f850 | ||
|
|
b82a6a8c5d | ||
|
|
ab2daf2a80 | ||
|
|
f734e3a58a | ||
|
|
212981fda9 | ||
|
|
c64e138ba7 | ||
|
|
c43353d126 | ||
|
|
d439ba9592 | ||
|
|
b7f06f0b59 | ||
|
|
60d666f8be | ||
|
|
4ff4c2afef | ||
|
|
655b8385f9 | ||
|
|
439c141c2c | ||
|
|
95755b930d | ||
|
|
9cf54c1f2d | ||
|
|
e118418a23 | ||
|
|
a3567d6eb2 | ||
|
|
a359143bc7 | ||
|
|
77b7a24d85 | ||
|
|
338b2593d5 | ||
|
|
ded5e27355 | ||
|
|
ee1b005d96 | ||
|
|
cd2673d1ba | ||
|
|
41d77085ae | ||
|
|
67059b2e43 | ||
|
|
f6157ef79b | ||
|
|
6e5d517e82 | ||
|
|
1dca51988a | ||
|
|
3c064bad55 | ||
|
|
d60a3ce05a | ||
|
|
f68438ff8c | ||
|
|
d351f1d08e | ||
|
|
70bdb8f37d | ||
|
|
3cd713e078 | ||
|
|
454655de5a | ||
|
|
268ed1a6a6 | ||
|
|
a42ce601b8 | ||
|
|
756b37ef6c | ||
|
|
ab7e708e02 | ||
|
|
326d916c0b | ||
|
|
db09bd931b | ||
|
|
d1e9eb90f1 | ||
|
|
20a5c4c78d | ||
|
|
499f9888b1 | ||
|
|
7bb3364b43 | ||
|
|
473cbc5694 | ||
|
|
e32b24c9f6 | ||
|
|
777fdc14c8 | ||
|
|
ffbb01c25c | ||
|
|
47380c2c1c | ||
|
|
51e9121a6b | ||
|
|
94f51d518d | ||
|
|
8fdc17b239 | ||
|
|
42bb29185f | ||
|
|
05ae0f4e80 | ||
|
|
092374a143 | ||
|
|
3cfb60430a | ||
|
|
370cc68fa4 | ||
|
|
8fe5046c19 | ||
|
|
37e7604441 | ||
|
|
7651b34855 | ||
|
|
4bf89b1d5f | ||
|
|
0287284909 | ||
|
|
a8453b3f21 | ||
|
|
2552564953 | ||
|
|
0d4d19559a | ||
|
|
52f4132ccb | ||
|
|
cb6440662b | ||
|
|
6293895611 | ||
|
|
f564fb6e9e | ||
|
|
3f2adfcf62 | ||
|
|
2c979f291e | ||
|
|
7e7b2ee64a | ||
|
|
87108405a8 | ||
|
|
9fb055cbff | ||
|
|
e690f0137e | ||
|
|
87c08d6fbd | ||
|
|
8c164d776e | ||
|
|
964271b4e1 | ||
|
|
e99ba48f07 | ||
|
|
62b1627b04 | ||
|
|
3a54997d42 | ||
|
|
7d95a43b4b | ||
|
|
23ea8710c0 | ||
|
|
0b62a52991 | ||
|
|
1143c8a8b7 | ||
|
|
a5b66caae6 | ||
|
|
b0b87d05fd | ||
|
|
cb3c666f64 | ||
|
|
12fdae7944 | ||
|
|
e76bc70ec6 | ||
|
|
e78bb8cb41 | ||
|
|
5153e73f42 | ||
|
|
7f36ff8a2b | ||
|
|
99a8a39dda | ||
|
|
adbf983e65 | ||
|
|
d7639495ac | ||
|
|
fbe09d9082 | ||
|
|
70468b72c3 | ||
|
|
90f4af1121 | ||
|
|
aa37449bbf | ||
|
|
c835e04722 | ||
|
|
29b999b8ed | ||
|
|
9ddd6cc317 | ||
|
|
3a694f0e31 | ||
|
|
080cbda588 | ||
|
|
fd066e8aae | ||
|
|
0fd0cf689a | ||
|
|
f85adda24c | ||
|
|
2d1e8e02aa | ||
|
|
371fbee7a4 | ||
|
|
a5bb95e7c1 | ||
|
|
53b5a443fe | ||
|
|
a230871f75 | ||
|
|
f560ce17e8 | ||
|
|
b96329bd33 | ||
|
|
913f7802de | ||
|
|
a9f61ed51e | ||
|
|
04c0835228 | ||
|
|
af7ff05ecf | ||
|
|
4ccf80189e | ||
|
|
b417229ee6 | ||
|
|
61457582b3 | ||
|
|
ecc1613f49 | ||
|
|
5ea89eefe8 | ||
|
|
661808826a | ||
|
|
342f78ffd0 | ||
|
|
cc6a65d5e4 | ||
|
|
068ee76983 | ||
|
|
b980b33019 | ||
|
|
4327ce7848 | ||
|
|
2c813e7b3d | ||
|
|
d69746f7ef | ||
|
|
ce8f73d30d | ||
|
|
d6602ac8a8 | ||
|
|
6478af03e7 | ||
|
|
f086e63914 | ||
|
|
6fbb6bd8dc | ||
|
|
008629b61f | ||
|
|
b7704dbe57 | ||
|
|
5aff2d0b1b | ||
|
|
771bbeed6a | ||
|
|
53dd1e9aa5 | ||
|
|
6b7ed456ac | ||
|
|
5e2185dffd | ||
|
|
b5d318013b | ||
|
|
07a926e50c | ||
|
|
78bbb63c11 | ||
|
|
1fd613c2b2 | ||
|
|
792833ebc8 | ||
|
|
7392fce770 | ||
|
|
6c621c743d | ||
|
|
50a7883958 | ||
|
|
3882db6fc6 | ||
|
|
c91691f79b | ||
|
|
17fbf1163d | ||
|
|
9a1bbd7e0d | ||
|
|
bf6f3bad46 | ||
|
|
b99b2a53cf | ||
|
|
1fec5c15d4 | ||
|
|
a8b13e60b6 | ||
|
|
4c0c44de6b | ||
|
|
af0623beea | ||
|
|
761b418d21 | ||
|
|
0316edb8cb | ||
|
|
48cf417d60 | ||
|
|
83af2926aa | ||
|
|
ddfa820004 | ||
|
|
80986978cb | ||
|
|
68283554e9 | ||
|
|
0fcedfafbe | ||
|
|
d075ce4ebd | ||
|
|
3c6fcb4e4d | ||
|
|
5d37b7947e | ||
|
|
537b5c83f2 | ||
|
|
44d2c90aea | ||
|
|
af0c984b08 | ||
|
|
e9f01c7a10 | ||
|
|
0c468e3489 | ||
|
|
aff43b7625 | ||
|
|
dc53ebd378 | ||
|
|
7a7518d92a | ||
|
|
ee182b8ea7 | ||
|
|
c1bf48480c | ||
|
|
b5bb88553c | ||
|
|
9b9fc09bdf | ||
|
|
5709c88232 | ||
|
|
522c9fba08 | ||
|
|
3a7b15f231 | ||
|
|
cb2c96ef7d | ||
|
|
f89f691ee3 | ||
|
|
a0f59b774a | ||
|
|
65adf2109d | ||
|
|
92da1695bf | ||
|
|
e46011beff | ||
|
|
e733dea80e | ||
|
|
6a27161322 | ||
|
|
6bb4193167 | ||
|
|
52c34250ff | ||
|
|
cd72cccbaa | ||
|
|
ad9689440a | ||
|
|
dd193352e2 | ||
|
|
a376041be4 | ||
|
|
7d14eb35ba | ||
|
|
fdad3b0c87 | ||
|
|
8ebaa59b5f | ||
|
|
6037be404c | ||
|
|
1ead65126f | ||
|
|
cfa1bc8875 | ||
|
|
cfbac7a9ab | ||
|
|
6f62777033 | ||
|
|
0a523d74d5 | ||
|
|
907d0eb724 | ||
|
|
edcf20885c | ||
|
|
bb838c5b1f | ||
|
|
fa90618200 | ||
|
|
b421d8f818 | ||
|
|
c1151b7e93 | ||
|
|
b1d6b1be9c | ||
|
|
cb45405e09 | ||
|
|
6ef9f2856a | ||
|
|
c14300ffbb | ||
|
|
42b4c40d87 | ||
|
|
1c78dac79f | ||
|
|
bb6a045dd3 | ||
|
|
b273d8314a | ||
|
|
015b895177 | ||
|
|
fce9ce4eb4 | ||
|
|
e6463adb65 | ||
|
|
b07fd29753 | ||
|
|
2416a035c7 | ||
|
|
c2c125fd29 | ||
|
|
580bf0494d | ||
|
|
eefc52d1dd | ||
|
|
c28de855e2 | ||
|
|
5e2d7505da | ||
|
|
f550c96541 | ||
|
|
d222eec1b1 | ||
|
|
be129262da | ||
|
|
87080c906d | ||
|
|
cdeaf09ed6 | ||
|
|
b17f8dac7a | ||
|
|
ec758e5c18 | ||
|
|
350f9630df | ||
|
|
557c760197 | ||
|
|
895f40414d | ||
|
|
96fbb38b1c | ||
|
|
650d01d7be | ||
|
|
3977342a67 | ||
|
|
8a61f01e1b | ||
|
|
75a4371f36 | ||
|
|
86ee4786a0 | ||
|
|
ef710463ae | ||
|
|
e6b153bcbd | ||
|
|
9c9eb8ca7b | ||
|
|
295f438ff1 | ||
|
|
cc1ad3e690 | ||
|
|
3ebb3822dd | ||
|
|
6e22bd4c8d | ||
|
|
1027956892 | ||
|
|
a46d52ddbb | ||
|
|
fd5e78eb8a | ||
|
|
8a326cbb91 | ||
|
|
09a7893021 | ||
|
|
9c10af58d8 | ||
|
|
81fbe251ba | ||
|
|
5203f0ea57 | ||
|
|
51644c2178 | ||
|
|
f326a20019 | ||
|
|
daea4ea460 | ||
|
|
c9c14bcebf | ||
|
|
9dc21c01f1 | ||
|
|
555dbd592c | ||
|
|
5e027b75b2 | ||
|
|
01a504dab7 | ||
|
|
70eeaaac28 | ||
|
|
0397d529bb | ||
|
|
25bed747f2 | ||
|
|
d17b90e782 | ||
|
|
7868e22a95 | ||
|
|
81f6e2057e | ||
|
|
1c3e37ee2b | ||
|
|
27756db621 | ||
|
|
7f71b04ef7 | ||
|
|
c48522e6c0 | ||
|
|
fbf629dd8b | ||
|
|
404b2889ff | ||
|
|
5ccb9d16a8 | ||
|
|
66b562f24b | ||
|
|
ef25b88717 | ||
|
|
f22f7273a9 | ||
|
|
ea9902c946 | ||
|
|
051c38c6df | ||
|
|
6b832026b4 | ||
|
|
81d7151f8f | ||
|
|
283672e909 | ||
|
|
94dfba4b4f | ||
|
|
2b66efd11b | ||
|
|
14fb3f5758 | ||
|
|
9354f0f092 | ||
|
|
c3636a0743 | ||
|
|
e07b66812c | ||
|
|
c1d231db60 | ||
|
|
757ab1f228 | ||
|
|
6e42e7fb6b | ||
|
|
92f88efd4e | ||
|
|
7ba2194d97 | ||
|
|
a5f2e2f5c8 | ||
|
|
8658c24ef0 | ||
|
|
0cded076e4 | ||
|
|
135c0b6d38 | ||
|
|
769fe42a7a | ||
|
|
ab6fcd73e0 | ||
|
|
3278420d72 | ||
|
|
98395387a7 | ||
|
|
180a097213 | ||
|
|
18ac00080a | ||
|
|
4da713702a | ||
|
|
68be17de66 | ||
|
|
18cdf9d7ed | ||
|
|
c389ea1e49 | ||
|
|
1f65b0352d | ||
|
|
0e63b6638c | ||
|
|
fcda1f119b | ||
|
|
bb130849ee | ||
|
|
7b209eec6c | ||
|
|
9e7a84d2d6 | ||
|
|
baf43ea307 | ||
|
|
f524f0da3e | ||
|
|
49781c47a9 | ||
|
|
aa11ce807a | ||
|
|
702115c55a | ||
|
|
17cb1bf9b0 | ||
|
|
590c4e0a23 | ||
|
|
fd6196a880 | ||
|
|
2875f7ff7a | ||
|
|
50fe127a8d | ||
|
|
94ebe5b707 | ||
|
|
dc3914e976 | ||
|
|
ef2f037909 | ||
|
|
374f286585 | ||
|
|
34cd933f78 | ||
|
|
f8d533e592 | ||
|
|
21263cf0fd | ||
|
|
1a62ac2006 | ||
|
|
8f3d4d5fb2 | ||
|
|
81902455ff | ||
|
|
fbb33d9ef8 | ||
|
|
b025c7a7fa | ||
|
|
d8aec5aa97 | ||
|
|
3a862f343c | ||
|
|
69724cfb1c | ||
|
|
f75cdea678 | ||
|
|
32e650eff5 | ||
|
|
ab022f9049 | ||
|
|
7962e148fa | ||
|
|
efdf3a0691 | ||
|
|
000ab5e856 | ||
|
|
4873133c92 | ||
|
|
360bbef43a | ||
|
|
9a21f4987d | ||
|
|
63948767ef | ||
|
|
e2098f6f71 | ||
|
|
609c30da38 | ||
|
|
0896842268 | ||
|
|
ec9506b9eb | ||
|
|
526526975c | ||
|
|
098734c471 | ||
|
|
47df62534e | ||
|
|
7295001892 | ||
|
|
7c820b7fd2 | ||
|
|
36429cc1e9 | ||
|
|
a1c22ca9da | ||
|
|
0c37932631 | ||
|
|
dbf1f6dcca | ||
|
|
7bb26c0faf | ||
|
|
31a0b55556 | ||
|
|
64cc4785ca | ||
|
|
28391de50c | ||
|
|
b2ed69ab78 | ||
|
|
2f08940927 | ||
|
|
70928227e4 | ||
|
|
546bd70418 | ||
|
|
f2521a0110 | ||
|
|
02b83513a1 | ||
|
|
f26b2ff61b | ||
|
|
4d26535d07 | ||
|
|
01a365033e | ||
|
|
318a89a4bc | ||
|
|
b20f22fb92 | ||
|
|
fedf76e534 | ||
|
|
e0e16292eb | ||
|
|
b3c0e48bdd | ||
|
|
aded5ee03a | ||
|
|
16e71c910e | ||
|
|
bbe234b459 | ||
|
|
715b9eb156 | ||
|
|
4cc441afcf | ||
|
|
f79cd759bd | ||
|
|
1b232e4405 | ||
|
|
6d43afb258 | ||
|
|
a47c778b0e | ||
|
|
ddb82842bc | ||
|
|
199914b19f | ||
|
|
9fadd84597 | ||
|
|
95dd670c7c | ||
|
|
adc9def0c9 | ||
|
|
8dcc9d9b0e | ||
|
|
b5177b16ea | ||
|
|
f9b4693aae | ||
|
|
f2a479e35c | ||
|
|
1f40c2e052 | ||
|
|
b5c8d05814 | ||
|
|
f99634bc08 | ||
|
|
ab88e2f553 | ||
|
|
5465abe1ac | ||
|
|
b0df7a8f3b | ||
|
|
0d4fab100d | ||
|
|
8c5e10fd88 | ||
|
|
e8aef1596b | ||
|
|
386c86f04f | ||
|
|
3f5a66f170 | ||
|
|
a961d9534c | ||
|
|
d1919c18f5 | ||
|
|
afa8b24ba9 | ||
|
|
b793b74b32 | ||
|
|
65499d1f46 | ||
|
|
5f387cdb74 | ||
|
|
ed2e88c781 | ||
|
|
1cb3157110 | ||
|
|
a480b53787 | ||
|
|
405c895352 | ||
|
|
53dc251a0c | ||
|
|
d715072cbc | ||
|
|
aaee56f44e | ||
|
|
27ceb4ed48 | ||
|
|
2d51bd8f37 | ||
|
|
645a366dc5 | ||
|
|
756a74eda6 | ||
|
|
8052ee2afb | ||
|
|
6171c0defd | ||
|
|
56c27d0b8f | ||
|
|
9c173fd3a1 | ||
|
|
fa3ccf9953 | ||
|
|
229e645c8e | ||
|
|
b4606a8b84 | ||
|
|
d11782a87b | ||
|
|
4faaf3d251 | ||
|
|
89dea30211 | ||
|
|
7563634f53 | ||
|
|
d74dcc1d26 | ||
|
|
cd75fd0329 | ||
|
|
0390ee3363 | ||
|
|
64f52698c3 | ||
|
|
8bd4a87f0b | ||
|
|
4ad7c60443 | ||
|
|
d1a6c9be00 | ||
|
|
3c3fd1be50 | ||
|
|
92c5745ac8 | ||
|
|
792de5cda2 | ||
|
|
bfe2381896 | ||
|
|
ded5ecb3ed | ||
|
|
7895b40a77 | ||
|
|
3f2319093f | ||
|
|
3806b383c9 | ||
|
|
84a65e998f | ||
|
|
88049affb5 | ||
|
|
5fd59b9eac | ||
|
|
8288cc7f96 | ||
|
|
063b643597 | ||
|
|
ff4214877e | ||
|
|
847fa7d2ad | ||
|
|
886284b42f | ||
|
|
46776b461b | ||
|
|
e0150d7bb5 | ||
|
|
ad16260c53 | ||
|
|
3cfe9138a5 | ||
|
|
9e43babe0c | ||
|
|
4efbf54edf | ||
|
|
37121a1fd2 | ||
|
|
8a77a8a009 | ||
|
|
1aef137cb2 | ||
|
|
86707b28c0 | ||
|
|
5e42bae77f | ||
|
|
9396804379 | ||
|
|
5613cf9aae | ||
|
|
ff44c717a2 | ||
|
|
42faeb1402 | ||
|
|
7c243ac6ff | ||
|
|
85c6353cba | ||
|
|
c7d049efe2 | ||
|
|
97aabdca33 | ||
|
|
9562a0eaf7 | ||
|
|
04bffd4889 | ||
|
|
a9c71ced47 | ||
|
|
a03bf60ca5 | ||
|
|
02ee94f732 | ||
|
|
0a5ffd247c | ||
|
|
3f4f6a2d07 | ||
|
|
0d22f78b10 | ||
|
|
ca1e3e8e63 | ||
|
|
debb7cde2d | ||
|
|
40c35a5d50 | ||
|
|
971f769031 | ||
|
|
a1a3adfafa | ||
|
|
912b49ecc4 | ||
|
|
dcccd6e313 | ||
|
|
005529f959 | ||
|
|
3d5904c997 | ||
|
|
dcc8915dd2 | ||
|
|
4a589603e4 | ||
|
|
4f16b9d9e8 | ||
|
|
ce7ecc78cc | ||
|
|
9c8a677f13 | ||
|
|
39f2dd88aa | ||
|
|
3b4929a368 | ||
|
|
f290b8462d | ||
|
|
5ce4f3be56 | ||
|
|
a5158f04db | ||
|
|
2aa9f088a4 | ||
|
|
bb37c3f94c | ||
|
|
92b3f14d7b | ||
|
|
2dcd30e346 | ||
|
|
c4ed59dc46 | ||
|
|
81141fb2fa | ||
|
|
58c6f00a6c | ||
|
|
c1f4e42219 | ||
|
|
02b16843e5 | ||
|
|
86bfcc15f9 | ||
|
|
4fab4e71f7 | ||
|
|
b230462860 | ||
|
|
5f49b56c3d | ||
|
|
51482dd225 | ||
|
|
c1cb1a41a9 | ||
|
|
b496c79b34 | ||
|
|
fcad7db5ab | ||
|
|
e1bd26f712 | ||
|
|
0518184786 | ||
|
|
576e234216 | ||
|
|
3c750dac86 | ||
|
|
714cee586d | ||
|
|
cf9cc30d5e | ||
|
|
713b1c83e1 | ||
|
|
ddf289d747 | ||
|
|
ae1edf0f21 | ||
|
|
38665ddbf3 | ||
|
|
2af0f166eb | ||
|
|
aeaeff28d3 | ||
|
|
28948a0511 | ||
|
|
52d190e339 | ||
|
|
d47707c433 | ||
|
|
11bc46ae86 | ||
|
|
d229b23ea6 | ||
|
|
41dc7f7b14 | ||
|
|
dbd8b14cd2 | ||
|
|
3af8adb067 | ||
|
|
5baa470d54 | ||
|
|
1f5d5215f7 | ||
|
|
24c77ecd07 | ||
|
|
2264fc0172 | ||
|
|
00028ac59b | ||
|
|
ad5735a559 | ||
|
|
9411f5044a | ||
|
|
7293d55239 | ||
|
|
31dc347df4 | ||
|
|
d72d0d4dc2 | ||
|
|
7181dc9d5b | ||
|
|
95fa8681fe | ||
|
|
3a63755d96 | ||
|
|
5390970054 | ||
|
|
2b43f2b261 | ||
|
|
4330cd1aac | ||
|
|
fc9dd8a34d | ||
|
|
c64abc15c9 | ||
|
|
fca12c639c | ||
|
|
a1522aabd6 | ||
|
|
9be4b339f8 | ||
|
|
ce1c74aec3 | ||
|
|
e824428e0f | ||
|
|
2fd4a8a9b1 | ||
|
|
82de7e8b8e | ||
|
|
26831b4732 | ||
|
|
a6862925ca | ||
|
|
1e2ce169af | ||
|
|
dda9b3551a | ||
|
|
b1760d3541 | ||
|
|
2c1e087bc6 | ||
|
|
90d5bd52a2 | ||
|
|
26db75853b | ||
|
|
fe5a674518 | ||
|
|
2fe56cd6af | ||
|
|
f26e82d2bc | ||
|
|
65892f067a | ||
|
|
768717d7b3 | ||
|
|
f78b3daf8b | ||
|
|
8a6f481118 | ||
|
|
f420434fd3 | ||
|
|
8e73d7970f | ||
|
|
9699af93bc | ||
|
|
44ca0a94b7 | ||
|
|
b3bf008e31 | ||
|
|
ce5e2982d2 | ||
|
|
7d2edd315c | ||
|
|
8ae0452873 | ||
|
|
e0efc0d9ab | ||
|
|
3ce3b7ca2b | ||
|
|
6997608b63 | ||
|
|
cee7f12974 | ||
|
|
5b4a8d5775 | ||
|
|
a59bedec5d | ||
|
|
3c5d670924 | ||
|
|
7bab251915 | ||
|
|
57e47eee5d | ||
|
|
4f09c57755 | ||
|
|
7c1edab6ca | ||
|
|
f24004c949 | ||
|
|
c4bf3931e2 | ||
|
|
fe13562f3e | ||
|
|
64334d72ea | ||
|
|
a915980187 | ||
|
|
af882fa588 | ||
|
|
7fcaa16835 | ||
|
|
4d640f3cf2 | ||
|
|
25d495b1d0 | ||
|
|
7fd936c4a8 | ||
|
|
fe753fc4fd | ||
|
|
0a4763fcc1 | ||
|
|
e281faf664 | ||
|
|
dcb291c1c6 | ||
|
|
ecd1c93bb9 | ||
|
|
eeb555a6ce | ||
|
|
27d53abd10 | ||
|
|
91eef55173 | ||
|
|
f9e1518da6 | ||
|
|
5b974260cc | ||
|
|
554374b710 | ||
|
|
475669ac1b | ||
|
|
623d1e6a40 | ||
|
|
08fa4a997f | ||
|
|
4e21cf8494 | ||
|
|
2ebbda6852 | ||
|
|
010a6d6e42 | ||
|
|
3b1481879a | ||
|
|
3ddcc3884b | ||
|
|
260ab1ec89 | ||
|
|
69803a999f | ||
|
|
d4a75ed871 | ||
|
|
7394f14218 | ||
|
|
23cd7b9ebd | ||
|
|
477cfee78e | ||
|
|
750cecfdaf | ||
|
|
32a28fba32 | ||
|
|
fe926cbf9a | ||
|
|
d18e65ca6c | ||
|
|
ec67ca605c | ||
|
|
9cb3c963a1 | ||
|
|
8a4caf82bb | ||
|
|
7a5941cfa9 | ||
|
|
690c49ae1f | ||
|
|
c77c095893 | ||
|
|
98ddc65fa2 | ||
|
|
41a7c71b7d | ||
|
|
cb1d3d1db4 | ||
|
|
5ba2a31d7d | ||
|
|
0768a93bcb | ||
|
|
e690c6d0ff | ||
|
|
0c6bf406c1 | ||
|
|
95b5f12226 | ||
|
|
5b4b622834 | ||
|
|
b908b77a34 | ||
|
|
dbba310385 | ||
|
|
0a0ca9ba93 | ||
|
|
8aa574a7c4 | ||
|
|
37ac8c038f | ||
|
|
9b6456a80f | ||
|
|
f6ffd314b1 | ||
|
|
b569c6a6dd | ||
|
|
c84f416973 | ||
|
|
6ebc476d2b | ||
|
|
98c340d94d | ||
|
|
78d80918aa | ||
|
|
e8d7d6b4e7 | ||
|
|
53341b0dc0 | ||
|
|
f64c7d81ad | ||
|
|
197de59089 | ||
|
|
13eb37cc46 | ||
|
|
c21c0ff411 | ||
|
|
72f6af7019 | ||
|
|
6b14321505 | ||
|
|
4fcb719613 | ||
|
|
50915d9100 | ||
|
|
834792bc2d | ||
|
|
04b225711f | ||
|
|
eee4a75353 | ||
|
|
15d0df1a12 | ||
|
|
5c3e8c35c4 | ||
|
|
ac514fce30 | ||
|
|
f7343ea305 | ||
|
|
1435421c3c | ||
|
|
735c0fe367 | ||
|
|
af99cfa6f9 | ||
|
|
525ff009b6 | ||
|
|
ee46167320 | ||
|
|
6fc03403b4 | ||
|
|
760c481d39 | ||
|
|
2c4906534b | ||
|
|
2ed79f3f9c | ||
|
|
7e9be878c4 | ||
|
|
0a5ae3b090 | ||
|
|
70b8c1f9b7 | ||
|
|
b067b671db | ||
|
|
268ccac7e1 | ||
|
|
5e487bc4ba | ||
|
|
ffeda7d60b | ||
|
|
2779d1a489 | ||
|
|
1752815654 | ||
|
|
429ca400e5 | ||
|
|
984853bd66 | ||
|
|
ff2549d4b7 | ||
|
|
d33bb6b4bb | ||
|
|
a3c0eca063 | ||
|
|
10de4ac78e | ||
|
|
895394ebb9 | ||
|
|
349f8d936a | ||
|
|
bbc65391a1 | ||
|
|
751c248657 | ||
|
|
9052cd3cdd | ||
|
|
2736527fc1 | ||
|
|
0c7001acf6 | ||
|
|
9e9be5bf09 | ||
|
|
008e1ad27b | ||
|
|
3e5ae14a54 | ||
|
|
0c28833b14 | ||
|
|
75ef95c6bf | ||
|
|
b906f3c654 | ||
|
|
4b274a454b | ||
|
|
6554005742 | ||
|
|
9f04022afc | ||
|
|
fbab512975 | ||
|
|
c9b3a67c8b | ||
|
|
3169cd6591 | ||
|
|
b116e487d3 | ||
|
|
aa57044bb8 | ||
|
|
fdd578dad9 | ||
|
|
2801520546 | ||
|
|
caaf983b3d | ||
|
|
aaba13530c | ||
|
|
e05ec3bcee | ||
|
|
703a132a61 | ||
|
|
e55226e685 | ||
|
|
9a4e6de5f9 | ||
|
|
a4e55a328c | ||
|
|
2705685f07 | ||
|
|
b7fb17a79f | ||
|
|
ffeb73ab7c | ||
|
|
427dec56e4 | ||
|
|
94ce87d953 | ||
|
|
0dc4f0f11a | ||
|
|
a1d7e65ffb | ||
|
|
61702d9c2a | ||
|
|
5b08bef53f | ||
|
|
53a6588054 | ||
|
|
9855c0c13e | ||
|
|
c5d005bdeb | ||
|
|
1eb844c75b | ||
|
|
7e177f3cbf | ||
|
|
eb91cfbda1 | ||
|
|
54082c1fce | ||
|
|
b5caf6dacf | ||
|
|
f4d1ce5388 | ||
|
|
7d7ec69dc1 | ||
|
|
7208288c00 | ||
|
|
aff3745859 | ||
|
|
e103ddd216 | ||
|
|
41a4965775 | ||
|
|
49a06f513b | ||
|
|
1308f3684b | ||
|
|
c51eccac38 | ||
|
|
09157767bf | ||
|
|
32cc2c708a | ||
|
|
7f2de233fc | ||
|
|
7cb150606c | ||
|
|
87cac010eb | ||
|
|
03926754e7 | ||
|
|
65efda1a7a | ||
|
|
5941d4ca16 | ||
|
|
e77101af89 | ||
|
|
e766be6af9 | ||
|
|
95d1658324 | ||
|
|
9b24550738 | ||
|
|
7947568019 | ||
|
|
399ee98923 | ||
|
|
7b3b4a2ec5 | ||
|
|
09177da620 | ||
|
|
8392cfb2fa | ||
|
|
01face7315 | ||
|
|
5b6f4d65bf | ||
|
|
cd6f8f3db3 | ||
|
|
a9b07ddf1d | ||
|
|
f3710c575b | ||
|
|
1f5ab45a1e | ||
|
|
58d453db11 | ||
|
|
bd426b763c | ||
|
|
2e42efa71f | ||
|
|
58181bd723 | ||
|
|
dcef3115b8 | ||
|
|
4bdc5dc90f | ||
|
|
e33d6b3a0a | ||
|
|
d8ddaccf07 | ||
|
|
ca55ea16f0 | ||
|
|
cb42330e22 | ||
|
|
4e55cf0baa | ||
|
|
abec45c492 | ||
|
|
610e25b98a | ||
|
|
ad11e63338 | ||
|
|
440eb72ae4 | ||
|
|
c4553de302 | ||
|
|
32904b75e4 | ||
|
|
35a4771f89 | ||
|
|
bbb2e9391e | ||
|
|
9dc186f455 | ||
|
|
82b9c03a7e | ||
|
|
f4f9ba9efa | ||
|
|
495864e8e0 | ||
|
|
ac917df519 | ||
|
|
3b12fef948 | ||
|
|
a74b769aef | ||
|
|
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 | ||
|
|
d6feab3958 | ||
|
|
2d2207a1ee | ||
|
|
89b86630d8 | ||
|
|
bcb1571a23 | ||
|
|
5d658ebe4a | ||
|
|
7d2de80e77 | ||
|
|
b933249ff7 | ||
|
|
61c09e3c97 | ||
|
|
32695ee6dd | ||
|
|
4b66cd8cd2 | ||
|
|
edc4cc1706 | ||
|
|
35acb77bf7 | ||
|
|
e970a7b4d9 | ||
|
|
f155291139 | ||
|
|
b0293419e1 | ||
|
|
09db225929 | ||
|
|
0c52b4e236 | ||
|
|
5dc30942ff | ||
|
|
cab200e893 | ||
|
|
c349f3a3c4 | ||
|
|
0acb29f2e9 | ||
|
|
b66e01f7b4 | ||
|
|
8d6d215e57 | ||
|
|
d54a90a034 | ||
|
|
e1e7172561 | ||
|
|
6606b388f6 | ||
|
|
b6c6c01358 | ||
|
|
6886c5a4a2 | ||
|
|
87546a3dc8 | ||
|
|
6e3028639a | ||
|
|
386da02e27 | ||
|
|
ec8c395ffa | ||
|
|
9b98215fc9 | ||
|
|
40e037fb2a | ||
|
|
17f8569a7e | ||
|
|
1105f36cee | ||
|
|
f9fcd8749b | ||
|
|
eef76d362a | ||
|
|
3b0e3693eb | ||
|
|
ba4c56997a | ||
|
|
ca4d08567d | ||
|
|
3211149996 | ||
|
|
5a7e60cabb | ||
|
|
46ff4b6ef9 | ||
|
|
dc252e8d86 | ||
|
|
133e29dc2e | ||
|
|
368cec4fc6 | ||
|
|
65eea4301d | ||
|
|
ceae505f4d | ||
|
|
a7e9164f4f | ||
|
|
3820546c07 | ||
|
|
0fa6673d21 | ||
|
|
0a486c2195 | ||
|
|
a723fbefc3 | ||
|
|
70e64e57dd | ||
|
|
edfc3c6c5d | ||
|
|
c4447fc505 | ||
|
|
a1d2292381 | ||
|
|
033fb0c1ac | ||
|
|
e80034abf1 | ||
|
|
27e4a6c452 | ||
|
|
914497b76f | ||
|
|
513e799aa3 | ||
|
|
fcbf006e4e | ||
|
|
bef26e0fd7 | ||
|
|
3dde84f683 | ||
|
|
74c6aa06e0 | ||
|
|
ffb529edb3 | ||
|
|
d1279a471c | ||
|
|
a7f406537e | ||
|
|
df7d5150c1 | ||
|
|
73e4569b3b | ||
|
|
30c249ce74 | ||
|
|
ec83669d7d | ||
|
|
e765fb6c0b | ||
|
|
76465d30ec | ||
|
|
71d3771c1d | ||
|
|
bfd9c12163 | ||
|
|
eb57065562 | ||
|
|
3875f3b8fb | ||
|
|
8c2bedd21e | ||
|
|
b199a6aa54 | ||
|
|
1b1f64c2de | ||
|
|
7b73cc9d9b | ||
|
|
d9d84a01e5 | ||
|
|
56f009ac56 | ||
|
|
96daf90ae8 | ||
|
|
b581cb3124 | ||
|
|
4b0e39b950 | ||
|
|
3a1c476edc | ||
|
|
0d62d5336c | ||
|
|
cf87279dfc | ||
|
|
0006f7932a | ||
|
|
841a39c6c7 | ||
|
|
60b12f25a6 | ||
|
|
f2b96b6c50 | ||
|
|
d2fad1ab29 | ||
|
|
6f6755b218 | ||
|
|
9a2f2e6f17 | ||
|
|
d9ca550e3b | ||
|
|
ec66e87ee6 | ||
|
|
53ce3aee74 | ||
|
|
1ecb06f020 | ||
|
|
3ce4ac785e | ||
|
|
1df157434d | ||
|
|
594f001dda | ||
|
|
c2c6bc268e | ||
|
|
7aa2207edd | ||
|
|
22aa1642a6 | ||
|
|
844a9686af | ||
|
|
8f929366b3 | ||
|
|
dfa0fab979 | ||
|
|
415d6c587f | ||
|
|
b1034b964e | ||
|
|
85d2382680 | ||
|
|
a6e694fe5d | ||
|
|
a579bfea1f | ||
|
|
ba97abed44 | ||
|
|
9fe6b101bd | ||
|
|
1345182eea | ||
|
|
a84ac8d3cc | ||
|
|
1eb07c52e5 | ||
|
|
0d75ee135c | ||
|
|
c915f29c05 | ||
|
|
38d35d1991 | ||
|
|
dbc841cb7f | ||
|
|
46f53221c9 | ||
|
|
708fd01d1e | ||
|
|
4aa3ba0545 | ||
|
|
57499002d2 | ||
|
|
630f628598 | ||
|
|
d66c890b71 | ||
|
|
2b5649588a | ||
|
|
5425578f78 | ||
|
|
8c39adcc04 | ||
|
|
7773b32847 | ||
|
|
143b261a67 | ||
|
|
25fc2b9b04 | ||
|
|
d6fd0c4d2c | ||
|
|
44c44be412 | ||
|
|
7b71d7b4bf | ||
|
|
2c2aee6797 | ||
|
|
afdd032f73 | ||
|
|
9d52ca4b4c | ||
|
|
1bc8fe7ff6 | ||
|
|
6ab7a06dd5 | ||
|
|
7195ed3587 | ||
|
|
214e8d41c7 | ||
|
|
bd9f583659 | ||
|
|
abbf0b7ff5 | ||
|
|
f2b9e3a31b | ||
|
|
73dd669c20 | ||
|
|
32390149f3 | ||
|
|
9e73d8762e | ||
|
|
09854b469e | ||
|
|
e817063e53 | ||
|
|
7cdf6a8c79 | ||
|
|
d87087dcfb | ||
|
|
7c27fcd8a4 | ||
|
|
56408ed9f4 | ||
|
|
bf385f0bbf | ||
|
|
2a6a2930c1 | ||
|
|
9f676732a4 | ||
|
|
44fac8cc92 | ||
|
|
e510915098 | ||
|
|
b779f2f546 | ||
|
|
5344de96b2 | ||
|
|
1e70d960ba | ||
|
|
e03808fbc5 | ||
|
|
373268a6a8 | ||
|
|
905d440367 | ||
|
|
5628cf8d73 | ||
|
|
4aaea417f0 | ||
|
|
892886b730 | ||
|
|
1028050464 | ||
|
|
af79b00bd6 | ||
|
|
dc9a581e1c | ||
|
|
4d800fd644 | ||
|
|
126e8827de | ||
|
|
23c79d4452 | ||
|
|
173fc69a08 | ||
|
|
0411278f1d | ||
|
|
bb1f9bdcdc | ||
|
|
9d5ab935de | ||
|
|
1df9d145e4 | ||
|
|
fba30949bd | ||
|
|
198c320ad8 | ||
|
|
e798ba1104 | ||
|
|
f8f02a54f6 | ||
|
|
da01668cbe | ||
|
|
95770c63af | ||
|
|
af6e5d7441 | ||
|
|
ae5bdcc97a | ||
|
|
0fc415fb34 | ||
|
|
0fe30392d8 | ||
|
|
77fc11289c | ||
|
|
9d3969d4ce | ||
|
|
2ba2756a8f | ||
|
|
53088b4e60 | ||
|
|
0dc83739e7 | ||
|
|
e8a205b221 | ||
|
|
02c3d3fb4a | ||
|
|
2d3d66f077 | ||
|
|
a5f21adeee | ||
|
|
cbb4cdddfa | ||
|
|
e6b898882d | ||
|
|
3bd7f5c890 | ||
|
|
39c20fd0cd | ||
|
|
21117e81a3 | ||
|
|
df172b49db | ||
|
|
1ae0f694de | ||
|
|
040aa8daf6 | ||
|
|
3b9aa2d45c | ||
|
|
8705cac648 | ||
|
|
5a4e3caea8 | ||
|
|
593d4a35b7 | ||
|
|
801eef5f37 | ||
|
|
a0ac0ea189 | ||
|
|
f249455b00 | ||
|
|
8dbb8d9fe1 | ||
|
|
ed698e05d8 | ||
|
|
47b189bf87 | ||
|
|
460eb78ecd | ||
|
|
ffcaf4d16b | ||
|
|
64de357257 | ||
|
|
cc3f6622b4 | ||
|
|
f0b66d4bfb | ||
|
|
9c32f663b0 | ||
|
|
e0e22d91e1 | ||
|
|
dbc72cb4c2 | ||
|
|
17d6c6aa6b | ||
|
|
7be5916041 | ||
|
|
2d8a25178e | ||
|
|
5195025849 | ||
|
|
c3c2fc6171 | ||
|
|
6fa5e9a67f | ||
|
|
834018b325 | ||
|
|
027388f587 | ||
|
|
6452d39de1 | ||
|
|
6d78e2fff7 | ||
|
|
56ae245305 | ||
|
|
76b16ca6d4 | ||
|
|
d0a174d71c | ||
|
|
8e62f12f61 | ||
|
|
621bcdf380 | ||
|
|
1b54dd92ab | ||
|
|
2b0a43ca3e | ||
|
|
81ce49c219 | ||
|
|
b287c7236b | ||
|
|
b63d4a3da0 | ||
|
|
e652e43cba | ||
|
|
e6b2be1738 | ||
|
|
d2606e21fe | ||
|
|
22235cbe84 | ||
|
|
6bd5fae1cd | ||
|
|
ebb20bbe5e | ||
|
|
82d7395b79 | ||
|
|
451cb04714 | ||
|
|
15e5feafef | ||
|
|
4d19bd27f0 | ||
|
|
2400f2d0ad | ||
|
|
ee0193eb71 | ||
|
|
eb76acb767 | ||
|
|
6c77cccf53 | ||
|
|
b4ab969f88 | ||
|
|
2de4f3f808 | ||
|
|
4b5d0980f7 | ||
|
|
2bdbad1ba6 | ||
|
|
3b634877d0 | ||
|
|
2996bbb18f | ||
|
|
c4ca27608b | ||
|
|
5a85ff2ad3 | ||
|
|
a27b3cc43f | ||
|
|
ea8f557097 | ||
|
|
7bbed5985b | ||
|
|
0ec6dfb287 | ||
|
|
3b753c137b | ||
|
|
6cde7b8bef | ||
|
|
d26a89b8ab | ||
|
|
3ab0bcc0ae | ||
|
|
7548646ba2 | ||
|
|
0b75c6f046 | ||
|
|
958d306f42 | ||
|
|
742b25e4dd | ||
|
|
43845cf722 | ||
|
|
a2a0e5c2ee | ||
|
|
93e8322ba5 | ||
|
|
8a07c9cf4e | ||
|
|
6049eda580 | ||
|
|
177641894e | ||
|
|
dc49335ace | ||
|
|
3dcce8a8ac | ||
|
|
04651d46d8 | ||
|
|
56aeded8eb | ||
|
|
97c9c7e5ed | ||
|
|
a891391879 | ||
|
|
5aae9b01d4 | ||
|
|
b74a370b11 | ||
|
|
5e560661d4 | ||
|
|
93e450c2bf | ||
|
|
cc762754c5 | ||
|
|
7065436033 | ||
|
|
debe091502 | ||
|
|
80905b56cd | ||
|
|
0a7cd8a69e | ||
|
|
c3957977a2 | ||
|
|
ff602c77ed | ||
|
|
3667a5b57a | ||
|
|
3ac57b1c0c | ||
|
|
957d82b2f7 | ||
|
|
6d0817ad15 | ||
|
|
5b10e6d614 | ||
|
|
2d39b8c532 | ||
|
|
2ae860e8ca | ||
|
|
9e21c28e52 | ||
|
|
2f5053b49f | ||
|
|
7024136919 | ||
|
|
c74b5b3d29 | ||
|
|
9c3201aa4b | ||
|
|
dfd1141635 | ||
|
|
1188cad5e6 | ||
|
|
65fa2f8481 | ||
|
|
475e0b9d91 | ||
|
|
b76d09aa20 | ||
|
|
9a931eae67 | ||
|
|
47caa714c4 | ||
|
|
6740011c11 | ||
|
|
732078f24d | ||
|
|
5218aaaeb1 | ||
|
|
6b9df94613 | ||
|
|
b1ac88fc20 | ||
|
|
ca83019a58 | ||
|
|
d68272a5bb | ||
|
|
6ab0fd5e3b | ||
|
|
a0034b8fa8 | ||
|
|
a884737242 | ||
|
|
c7b3776386 | ||
|
|
b387c77179 | ||
|
|
f6b58223de | ||
|
|
8960ad3b16 | ||
|
|
28e95f9eb7 | ||
|
|
0bf5065cbc | ||
|
|
579c9c0f84 | ||
|
|
b2e8b66eae | ||
|
|
80e71f43de | ||
|
|
e0497e6fee | ||
|
|
18a78f44c0 | ||
|
|
f9e7fd5725 | ||
|
|
e9c1a170ad | ||
|
|
9ce84c75dd | ||
|
|
0bd5339b78 | ||
|
|
e06b1987b9 | ||
|
|
c8b271ac76 | ||
|
|
1672c73a57 | ||
|
|
32f6e0e8fc | ||
|
|
5c21de5a0f | ||
|
|
9f40a8c4c0 | ||
|
|
c179f29e2e | ||
|
|
f9d6fce3bd | ||
|
|
aa83896963 | ||
|
|
7d13b8c9db | ||
|
|
921292e077 | ||
|
|
c3e7f0b99f | ||
|
|
b9cc5e9ada | ||
|
|
76d76b2bf2 | ||
|
|
532e912a2d | ||
|
|
8ada667dfe | ||
|
|
28a4f7ce82 | ||
|
|
3a66183d0e | ||
|
|
7f91346878 | ||
|
|
2af0dc4a8c | ||
|
|
5240f2eb70 | ||
|
|
f25800510b | ||
|
|
6400c954ef | ||
|
|
e43423d2c9 | ||
|
|
bc613a0413 | ||
|
|
e47a52dbe0 | ||
|
|
da165345b6 | ||
|
|
8ea54328ef | ||
|
|
a4aeebee68 | ||
|
|
95a61c3b28 | ||
|
|
9aadf5e948 | ||
|
|
129ac1bb78 | ||
|
|
e4278c55b7 | ||
|
|
7aca58a6c9 | ||
|
|
ea022de022 | ||
|
|
bb4f16d91f | ||
|
|
f1c165845f | ||
|
|
15ae2441c3 | ||
|
|
6b8f8957de | ||
|
|
cf9bd99f3d | ||
|
|
557114d92d | ||
|
|
d01826ffa4 | ||
|
|
7e3ef544f0 | ||
|
|
ab07eb96ce | ||
|
|
7c53eaab13 | ||
|
|
fad7d87282 | ||
|
|
578519169a | ||
|
|
fe106d23ec | ||
|
|
e1669f031f | ||
|
|
9bff6d5fe1 | ||
|
|
544aaed9da | ||
|
|
b6b7a5e7aa | ||
|
|
cdc4d509ee | ||
|
|
07882f7632 | ||
|
|
deb3c01362 | ||
|
|
a51b16aed2 | ||
|
|
0e7d98158e | ||
|
|
56d8518ee4 | ||
|
|
8897fd8650 | ||
|
|
6f811e43d0 | ||
|
|
a2888e3371 | ||
|
|
30d3312d87 | ||
|
|
854a257fd6 | ||
|
|
28344c6e3f | ||
|
|
11a82addd8 | ||
|
|
152f6c7051 | ||
|
|
bedbceafa7 | ||
|
|
2f19bf7ceb | ||
|
|
a39ae9facf | ||
|
|
56b234bc96 | ||
|
|
633fe23b80 | ||
|
|
2867ce2e9a | ||
|
|
ac0e5e95a9 | ||
|
|
e3bed19e79 | ||
|
|
73aae8118f | ||
|
|
b3671a430e | ||
|
|
54465ff4e7 | ||
|
|
52eef84374 | ||
|
|
f4310206e9 | ||
|
|
7cfa9649e4 | ||
|
|
c6eaafebbe | ||
|
|
df1e14b6c9 | ||
|
|
ad2d854969 | ||
|
|
61202a87fb | ||
|
|
9ebbeaed0f | ||
|
|
aebc139d52 | ||
|
|
0e82eea891 | ||
|
|
7ec76acf2f | ||
|
|
21f17791ff | ||
|
|
fff5f2610a | ||
|
|
d574fb5e44 | ||
|
|
d8aacbcc5d | ||
|
|
6467ef97d5 |
43
.github/workflows/build_nupkg.yml
vendored
Normal file
43
.github/workflows/build_nupkg.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Nuget Pack
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Pack
|
||||
run: dotnet pack
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'Nuget Package'
|
||||
path: 'BinaryObjectScanner/bin/Release/*.nupkg'
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: 'BinaryObjectScanner/bin/Release/*.nupkg'
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
53
.github/workflows/build_test.yml
vendored
Normal file
53
.github/workflows/build_test.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Build Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
project: [Test]
|
||||
runtime: [win-x86, win-x64, linux-x64, osx-x64] #[win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64]
|
||||
framework: [net8.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0]
|
||||
conf: [Release, Debug]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build
|
||||
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c ${{ matrix.conf == 'Release' && 'Release -p:DebugType=None -p:DebugSymbols=false' || 'Debug'}} --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8')) && '-p:PublishSingleFile=true' || ''}}
|
||||
|
||||
- name: Archive build
|
||||
run: zip -r ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip ${{ matrix.project }}/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}
|
||||
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
17
.github/workflows/check_pr.yml
vendored
Normal file
17
.github/workflows/check_pr.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Build PR
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Build
|
||||
run: dotnet build
|
||||
9
.gitmodules
vendored
Normal file
9
.gitmodules
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
[submodule "BinaryObjectScanner/_EXTERNAL/stormlibsharp"]
|
||||
path = BinaryObjectScanner/_EXTERNAL/stormlibsharp
|
||||
url = https://github.com/robpaveza/stormlibsharp.git
|
||||
[submodule "BinaryObjectScanner/_EXTERNAL/libmspack4n"]
|
||||
path = BinaryObjectScanner/_EXTERNAL/libmspack4n
|
||||
url = https://github.com/activescott/libmspack4n.git
|
||||
[submodule "BinaryObjectScanner/_EXTERNAL/LessIO"]
|
||||
path = BinaryObjectScanner/_EXTERNAL/LessIO
|
||||
url = https://github.com/activescott/LessIO.git
|
||||
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"
|
||||
],
|
||||
|
||||
@@ -1,32 +1,37 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29306.81
|
||||
# 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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{88735BA2-778D-4192-8EB2-FFF6843719E2}"
|
||||
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.ps1 = publish-win.ps1
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BinaryObjectScanner", "BinaryObjectScanner\BinaryObjectScanner.csproj", "{341EA3F5-847C-4739-B86F-2B051FFE4EF2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
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
|
||||
{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
|
||||
95
BinaryObjectScanner/BinaryObjectScanner.csproj
Normal file
95
BinaryObjectScanner/BinaryObjectScanner.csproj
Normal file
@@ -0,0 +1,95 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<!-- <TreatWarningsAsErrors>true</TreatWarningsAsErrors> --> <!-- Can't be enabled because of external code -->
|
||||
<Version>3.1.1</Version>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Description>Protection scanning library</Description>
|
||||
<Copyright>Copyright (c)2018-2024 Matt Nadareski</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/BinaryObjectScanner</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>protection copy-protection scanning packer</PackageTags>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Set a build flag for Windows specifically -->
|
||||
<PropertyGroup Condition="'$(RuntimeIdentifier)'=='win-x86'">
|
||||
<DefineConstants>$(DefineConstants);WIN</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Exclude certain parts of external modules for .NET Framework 4.5.2 and above -->
|
||||
<PropertyGroup>
|
||||
<DefaultItemExcludes>
|
||||
$(DefaultItemExcludes);
|
||||
**\AssemblyInfo.cs;
|
||||
_EXTERNAL\LessIO\src\LessIO.Tests\**;
|
||||
_EXTERNAL\libmspack4n\lib\**;
|
||||
_EXTERNAL\libmspack4n\libmspack4ntest\**;
|
||||
_EXTERNAL\stormlibsharp\lib\**;
|
||||
_EXTERNAL\stormlibsharp\src\TestConsole\**
|
||||
</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Exclude all external modules for .NET Framework 2.0, .NET Framework 3.5, .NET Framework 4.0 or non-Windows builds -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`)) OR !$(RuntimeIdentifier.StartsWith(`win-`))">
|
||||
<DefaultItemExcludes>
|
||||
$(DefaultItemExcludes);
|
||||
_EXTERNAL\**
|
||||
</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- These are needed for dealing with native Windows DLLs -->
|
||||
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`))">
|
||||
<Content Include="*.dll">
|
||||
<Pack>true</Pack>
|
||||
<PackagePath>contentFiles;content</PackagePath>
|
||||
<IncludeInPackage>true</IncludeInPackage>
|
||||
<CopyToOutput>true</CopyToOutput>
|
||||
<BuildAction>Content</BuildAction>
|
||||
<copyToOutput>true</copyToOutput>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Support for old .NET versions -->
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="MinThreadingBridge" Version="0.11.4" />
|
||||
<PackageReference Include="MinTasksExtensionsBridge" Version="0.3.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`))">
|
||||
<PackageReference Include="OpenMcdf" Version="2.3.1" />
|
||||
<PackageReference Include="UnshieldSharp" Version="1.7.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
|
||||
<PackageReference Include="SharpCompress" Version="0.36.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net4`)) AND !$(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.Compression" Version="0.3.1" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.1.4" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.3.2" />
|
||||
<PackageReference Include="SabreTools.Matching" Version="1.3.1" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.4.0" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.4.0" />
|
||||
<PackageReference Include="WiseUnpacker" Version="1.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
BIN
BinaryObjectScanner/CascLib.dll
Normal file
BIN
BinaryObjectScanner/CascLib.dll
Normal file
Binary file not shown.
70
BinaryObjectScanner/Factory.cs
Normal file
70
BinaryObjectScanner/Factory.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
|
||||
namespace BinaryObjectScanner
|
||||
{
|
||||
public static class Factory
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an instance of a detectable based on file type
|
||||
/// </summary>
|
||||
public static IDetectable? CreateDetectable(SupportedFileType fileType)
|
||||
{
|
||||
switch (fileType)
|
||||
{
|
||||
case SupportedFileType.AACSMediaKeyBlock: return new FileType.AACSMediaKeyBlock();
|
||||
case SupportedFileType.BDPlusSVM: return new FileType.BDPlusSVM();
|
||||
//case SupportedFileType.CIA: return new FileType.CIA();
|
||||
case SupportedFileType.Executable: return new FileType.Executable();
|
||||
case SupportedFileType.LDSCRYPT: return new FileType.LDSCRYPT();
|
||||
//case SupportedFileType.N3DS: return new FileType.N3DS();
|
||||
//case SupportedFileType.Nitro: return new FileType.Nitro();
|
||||
case SupportedFileType.PLJ: return new FileType.PLJ();
|
||||
case SupportedFileType.SFFS: return new FileType.SFFS();
|
||||
case SupportedFileType.Textfile: return new FileType.Textfile();
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of an extractable based on file type
|
||||
/// </summary>
|
||||
public static IExtractable? CreateExtractable(SupportedFileType fileType)
|
||||
{
|
||||
switch (fileType)
|
||||
{
|
||||
case SupportedFileType.BFPK: return new FileType.BFPK();
|
||||
case SupportedFileType.BSP: return new FileType.BSP();
|
||||
case SupportedFileType.BZip2: return new FileType.BZip2();
|
||||
case SupportedFileType.CFB: return new FileType.CFB();
|
||||
//case SupportedFileType.CIA: return new FileType.CIA();
|
||||
case SupportedFileType.GCF: return new FileType.GCF();
|
||||
case SupportedFileType.GZIP: return new FileType.GZIP();
|
||||
case SupportedFileType.InstallShieldArchiveV3: return new FileType.InstallShieldArchiveV3();
|
||||
case SupportedFileType.InstallShieldCAB: return new FileType.InstallShieldCAB();
|
||||
case SupportedFileType.MicrosoftCAB: return new FileType.MicrosoftCAB();
|
||||
case SupportedFileType.MicrosoftLZ: return new FileType.MicrosoftLZ();
|
||||
case SupportedFileType.MPQ: return new FileType.MPQ();
|
||||
//case SupportedFileType.N3DS: return new FileType.N3DS();
|
||||
//case SupportedFileType.NCF: return new FileType.NCF();
|
||||
//case SupportedFileType.Nitro: return new FileType.Nitro();
|
||||
case SupportedFileType.PAK: return new FileType.PAK();
|
||||
case SupportedFileType.PFF: return new FileType.PFF();
|
||||
case SupportedFileType.PKZIP: return new FileType.PKZIP();
|
||||
//case SupportedFileType.PLJ: return new FileType.PLJ();
|
||||
//case SupportedFileType.Quantum: return new FileType.Quantum();
|
||||
case SupportedFileType.RAR: return new FileType.RAR();
|
||||
case SupportedFileType.SevenZip: return new FileType.SevenZip();
|
||||
case SupportedFileType.SFFS: return new FileType.SFFS();
|
||||
case SupportedFileType.SGA: return new FileType.SGA();
|
||||
case SupportedFileType.TapeArchive: return new FileType.TapeArchive();
|
||||
case SupportedFileType.VBSP: return new FileType.VBSP();
|
||||
case SupportedFileType.VPK: return new FileType.VPK();
|
||||
case SupportedFileType.WAD: return new FileType.WAD();
|
||||
case SupportedFileType.XZ: return new FileType.XZ();
|
||||
case SupportedFileType.XZP: return new FileType.XZP();
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
BinaryObjectScanner/FileType/AACSMediaKeyBlock.cs
Normal file
51
BinaryObjectScanner/FileType/AACSMediaKeyBlock.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// AACS media key block
|
||||
/// </summary>
|
||||
public class AACSMediaKeyBlock : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the MKB file itself fails
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var mkb = SabreTools.Serialization.Wrappers.AACSMediaKeyBlock.Create(stream);
|
||||
if (mkb == null)
|
||||
return null;
|
||||
|
||||
// Derive the version, if possible
|
||||
var typeAndVersion = mkb.Model.Records?.FirstOrDefault(r => r?.RecordType == SabreTools.Models.AACS.RecordType.TypeAndVersion);
|
||||
if (typeAndVersion == null)
|
||||
return "AACS (Unknown Version)";
|
||||
else
|
||||
return $"AACS {(typeAndVersion as SabreTools.Models.AACS.TypeAndVersionRecord)?.VersionNumber}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
BinaryObjectScanner/FileType/BDPlusSVM.cs
Normal file
49
BinaryObjectScanner/FileType/BDPlusSVM.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// BD+ SVM
|
||||
/// </summary>
|
||||
public class BDPlusSVM : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the BD+ file itself fails
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
var svm = SabreTools.Serialization.Wrappers.BDPlusSVM.Create(stream);
|
||||
if (svm == null)
|
||||
return null;
|
||||
|
||||
// Format the date
|
||||
string date = $"{svm.Model.Year:0000}/{svm.Model.Month:00}/{svm.Model.Day:00}";
|
||||
|
||||
// Return the formatted value
|
||||
return $"BD+ {date}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
141
BinaryObjectScanner/FileType/BFPK.cs
Normal file
141
BinaryObjectScanner/FileType/BFPK.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// BFPK custom archive format
|
||||
/// </summary>
|
||||
public class BFPK : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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);
|
||||
}
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
else
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
ZlibStream zs = new ZlibStream(ms, CompressionMode.Decompress);
|
||||
zs.CopyTo(fs);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
307
BinaryObjectScanner/FileType/BSP.cs
Normal file
307
BinaryObjectScanner/FileType/BSP.cs
Normal file
@@ -0,0 +1,307 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life Level
|
||||
/// </summary>
|
||||
public class BSP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all textures from the BSP to an output directory
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if all textures extracted, false otherwise</returns>
|
||||
public static bool ExtractAllTextures(SabreTools.Serialization.Wrappers.BSP item, string outputDirectory)
|
||||
{
|
||||
// If we have no textures
|
||||
if (item.Model.TextureHeader?.Offsets == null || item.Model.TextureHeader.Offsets.Length == 0)
|
||||
return false;
|
||||
|
||||
// Loop through and extract all lumps to the output
|
||||
bool allExtracted = true;
|
||||
for (int i = 0; i < item.Model.TextureHeader.Offsets.Length; i++)
|
||||
{
|
||||
allExtracted &= ExtractTexture(item, i, outputDirectory);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a texture from the BSP to an output directory by index
|
||||
/// </summary>
|
||||
/// <param name="index">Lump index to extract</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <returns>True if the texture extracted, false otherwise</returns>
|
||||
public static bool ExtractTexture(SabreTools.Serialization.Wrappers.BSP item, int index, string outputDirectory)
|
||||
{
|
||||
// If we have no textures
|
||||
if (item.Model.Textures == null || item.Model.Textures.Length == 0)
|
||||
return false;
|
||||
|
||||
// If the texture index is invalid
|
||||
if (index < 0 || index >= item.Model.Textures.Length)
|
||||
return false;
|
||||
|
||||
// Get the texture
|
||||
var texture = item.Model.Textures[index];
|
||||
if (texture == null)
|
||||
return false;
|
||||
|
||||
// Read the data
|
||||
var data = CreateTextureData(texture);
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// Create the filename
|
||||
string filename = $"{texture.Name}.bmp";
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a bitmap from the texture and palette data
|
||||
/// </summary>
|
||||
/// <param name="texture">Texture object to format</param>
|
||||
/// <returns>Byte array representing the texture as a bitmap</returns>
|
||||
private static byte[]? CreateTextureData(SabreTools.Models.BSP.Texture texture)
|
||||
{
|
||||
// If there's no palette data
|
||||
if (texture.PaletteData == null || texture.PaletteData.Length == 0)
|
||||
return null;
|
||||
|
||||
// If there's no texture data
|
||||
if (texture.TextureData == null || texture.TextureData.Length == 0)
|
||||
return null;
|
||||
|
||||
// Create the bitmap file header
|
||||
var fileHeader = new SabreTools.Models.BMP.BITMAPFILEHEADER()
|
||||
{
|
||||
Type = ('M' << 8) | 'B',
|
||||
Size = 14 + 40 + (texture.PaletteSize * 4) + (texture.Width * texture.Height),
|
||||
OffBits = 14 + 40 + (texture.PaletteSize * 4),
|
||||
};
|
||||
|
||||
// Create the bitmap info header
|
||||
var infoHeader = new SabreTools.Models.BMP.BITMAPINFOHEADER
|
||||
{
|
||||
Size = 40,
|
||||
Width = (int)texture.Width,
|
||||
Height = (int)texture.Height,
|
||||
Planes = 1,
|
||||
BitCount = 8,
|
||||
SizeImage = 0,
|
||||
ClrUsed = texture.PaletteSize,
|
||||
ClrImportant = texture.PaletteSize,
|
||||
};
|
||||
|
||||
// Reformat the palette data
|
||||
byte[] paletteData = new byte[texture.PaletteSize * 4];
|
||||
for (uint i = 0; i < texture.PaletteSize; i++)
|
||||
{
|
||||
paletteData[i * 4 + 0] = texture.PaletteData[i * 3 + 2];
|
||||
paletteData[i * 4 + 1] = texture.PaletteData[i * 3 + 1];
|
||||
paletteData[i * 4 + 2] = texture.PaletteData[i * 3 + 0];
|
||||
paletteData[i * 4 + 3] = 0;
|
||||
}
|
||||
|
||||
// Reformat the pixel data
|
||||
byte[] pixelData = new byte[texture.Width * texture.Height];
|
||||
for (uint i = 0; i < texture.Width; i++)
|
||||
{
|
||||
for (uint j = 0; j < texture.Height; j++)
|
||||
{
|
||||
pixelData[i + ((texture.Height - 1 - j) * texture.Width)] = texture.TextureData[i + j * texture.Width];
|
||||
}
|
||||
}
|
||||
|
||||
// Build the file data
|
||||
List<byte> buffer = new List<byte>();
|
||||
|
||||
// Bitmap file header
|
||||
buffer.AddRange(BitConverter.GetBytes(fileHeader.Type));
|
||||
buffer.AddRange(BitConverter.GetBytes(fileHeader.Size));
|
||||
buffer.AddRange(BitConverter.GetBytes(fileHeader.Reserved1));
|
||||
buffer.AddRange(BitConverter.GetBytes(fileHeader.Reserved2));
|
||||
buffer.AddRange(BitConverter.GetBytes(fileHeader.OffBits));
|
||||
|
||||
// Bitmap info header
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.Size));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.Width));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.Height));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.Planes));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.BitCount));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.Compression));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.SizeImage));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.XPelsPerMeter));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.YPelsPerMeter));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.ClrUsed));
|
||||
buffer.AddRange(BitConverter.GetBytes(infoHeader.ClrImportant));
|
||||
|
||||
// Palette data
|
||||
buffer.AddRange(paletteData);
|
||||
|
||||
// Pixel data
|
||||
buffer.AddRange(pixelData);
|
||||
|
||||
return buffer.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
60
BinaryObjectScanner/FileType/BZip2.cs
Normal file
60
BinaryObjectScanner/FileType/BZip2.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.BZip2;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// bzip2 archive
|
||||
/// </summary>
|
||||
public class BZip2 : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
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;
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
157
BinaryObjectScanner/FileType/CFB.cs
Normal file
157
BinaryObjectScanner/FileType/CFB.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using OpenMcdf;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Compound File Binary
|
||||
/// </summary>
|
||||
public class CFB : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
#if NET20 || NET35
|
||||
// Not supported for .NET Framework 2.0 or .NET Framework 3.5 due to library support
|
||||
return null;
|
||||
#else
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <remarks>Adapted from LibMSI</remarks>
|
||||
public static string? DecodeStreamName(string input)
|
||||
{
|
||||
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 '_';
|
||||
}
|
||||
}
|
||||
}
|
||||
600
BinaryObjectScanner/FileType/Executable.cs
Normal file
600
BinaryObjectScanner/FileType/Executable.cs
Normal file
@@ -0,0 +1,600 @@
|
||||
using System;
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
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
|
||||
{
|
||||
contentCheckClasses ??= InitCheckClasses<IContentCheck>();
|
||||
return contentCheckClasses ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all ILinearExecutableCheck types
|
||||
/// </summary>
|
||||
public static IEnumerable<ILinearExecutableCheck> LinearExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
linearExecutableCheckClasses ??= InitCheckClasses<ILinearExecutableCheck>();
|
||||
return linearExecutableCheckClasses ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IMSDOSExecutableCheck types
|
||||
/// </summary>
|
||||
public static IEnumerable<IMSDOSExecutableCheck> MSDOSExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
msdosExecutableCheckClasses ??= InitCheckClasses<IMSDOSExecutableCheck>();
|
||||
return msdosExecutableCheckClasses ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all INewExecutableCheck types
|
||||
/// </summary>
|
||||
public static IEnumerable<INewExecutableCheck> NewExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
newExecutableCheckClasses ??= InitCheckClasses<INewExecutableCheck>();
|
||||
return newExecutableCheckClasses ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IPortableExecutableCheck types
|
||||
/// </summary>
|
||||
public static IEnumerable<IPortableExecutableCheck> PortableExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
portableExecutableCheckClasses ??= InitCheckClasses<IPortableExecutableCheck>();
|
||||
return portableExecutableCheckClasses ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Instances
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IContentCheck types
|
||||
/// </summary>
|
||||
private static IEnumerable<IContentCheck>? contentCheckClasses;
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all ILinearExecutableCheck types
|
||||
/// </summary>
|
||||
private static IEnumerable<ILinearExecutableCheck>? linearExecutableCheckClasses;
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IMSDOSExecutableCheck types
|
||||
/// </summary>
|
||||
private static IEnumerable<IMSDOSExecutableCheck>? msdosExecutableCheckClasses;
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all INewExecutableCheck types
|
||||
/// </summary>
|
||||
private static IEnumerable<INewExecutableCheck>? newExecutableCheckClasses;
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IPortableExecutableCheck types
|
||||
/// </summary>
|
||||
private static IEnumerable<IPortableExecutableCheck>? portableExecutableCheckClasses;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// Try to create a wrapper for the proper executable type
|
||||
var wrapper = WrapperFactory.CreateExecutableWrapper(stream);
|
||||
if (wrapper == null)
|
||||
return null;
|
||||
|
||||
// Create the internal queue
|
||||
#if NET20 || NET35
|
||||
var protections = new Queue<string>();
|
||||
#else
|
||||
var protections = new ConcurrentQueue<string>();
|
||||
#endif
|
||||
|
||||
// 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 NET20 || NET35
|
||||
public Dictionary<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.IsNullOrEmpty(file))
|
||||
return null;
|
||||
else if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
// Read the file contents
|
||||
byte[] fileContent = [];
|
||||
try
|
||||
{
|
||||
#if NET20 || NET35 || NET40
|
||||
using var br = new BinaryReader(stream, Encoding.Default);
|
||||
#else
|
||||
using var br = new BinaryReader(stream, Encoding.Default, true);
|
||||
#endif
|
||||
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
|
||||
#if NET20 || NET35
|
||||
var protections = new Dictionary<IContentCheck, string>();
|
||||
#else
|
||||
var protections = new ConcurrentDictionary<IContentCheck, string>();
|
||||
#endif
|
||||
|
||||
// Iterate through all checks
|
||||
#if NET20 || NET35
|
||||
foreach (var checkClass in ContentCheckClasses)
|
||||
#else
|
||||
Parallel.ForEach(ContentCheckClasses, checkClass =>
|
||||
#endif
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
var protection = checkClass.CheckContents(file!, fileContent, includeDebug);
|
||||
if (string.IsNullOrEmpty(protection))
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
// If we are filtering on game engines
|
||||
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
// If we are filtering on packers
|
||||
if (CheckIfPacker(checkClass) && !IncludePackers)
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
#if NET20 || NET35
|
||||
protections[checkClass] = protection!;
|
||||
}
|
||||
#else
|
||||
protections.TryAdd(checkClass, protection!);
|
||||
});
|
||||
#endif
|
||||
|
||||
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>
|
||||
#if NET20 || NET35
|
||||
public Dictionary<ILinearExecutableCheck, string> RunLinearExecutableChecks(string file, Stream stream, LinearExecutable lex, bool includeDebug)
|
||||
#else
|
||||
public ConcurrentDictionary<ILinearExecutableCheck, string> RunLinearExecutableChecks(string file, Stream stream, LinearExecutable lex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Create the output dictionary
|
||||
#if NET20 || NET35
|
||||
var protections = new Dictionary<ILinearExecutableCheck, string>();
|
||||
#else
|
||||
var protections = new ConcurrentDictionary<ILinearExecutableCheck, string>();
|
||||
#endif
|
||||
|
||||
// Iterate through all checks
|
||||
#if NET20 || NET35
|
||||
foreach (var checkClass in LinearExecutableCheckClasses)
|
||||
#else
|
||||
Parallel.ForEach(LinearExecutableCheckClasses, checkClass =>
|
||||
#endif
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
var protection = checkClass.CheckLinearExecutable(file, lex, includeDebug);
|
||||
if (string.IsNullOrEmpty(protection))
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
// If we are filtering on game engines
|
||||
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
// If we are filtering on packers
|
||||
if (CheckIfPacker(checkClass) && !IncludePackers)
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
#if NET20 || NET35
|
||||
protections[checkClass] = protection!;
|
||||
}
|
||||
#else
|
||||
protections.TryAdd(checkClass, protection!);
|
||||
});
|
||||
#endif
|
||||
|
||||
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>
|
||||
#if NET20 || NET35
|
||||
public Dictionary<IMSDOSExecutableCheck, string> RunMSDOSExecutableChecks(string file, Stream stream, MSDOS mz, bool includeDebug)
|
||||
#else
|
||||
public ConcurrentDictionary<IMSDOSExecutableCheck, string> RunMSDOSExecutableChecks(string file, Stream stream, MSDOS mz, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Create the output dictionary
|
||||
#if NET20 || NET35
|
||||
var protections = new Dictionary<IMSDOSExecutableCheck, string>();
|
||||
#else
|
||||
var protections = new ConcurrentDictionary<IMSDOSExecutableCheck, string>();
|
||||
#endif
|
||||
|
||||
// Iterate through all checks
|
||||
#if NET20 || NET35
|
||||
foreach (var checkClass in MSDOSExecutableCheckClasses)
|
||||
#else
|
||||
Parallel.ForEach(MSDOSExecutableCheckClasses, checkClass =>
|
||||
#endif
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
var protection = checkClass.CheckMSDOSExecutable(file, mz, includeDebug);
|
||||
if (string.IsNullOrEmpty(protection))
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
// If we are filtering on game engines
|
||||
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
// If we are filtering on packers
|
||||
if (CheckIfPacker(checkClass) && !IncludePackers)
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
#if NET20 || NET35
|
||||
protections[checkClass] = protection!;
|
||||
}
|
||||
#else
|
||||
protections.TryAdd(checkClass, protection!);
|
||||
});
|
||||
#endif
|
||||
|
||||
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>
|
||||
#if NET20 || NET35
|
||||
public Dictionary<INewExecutableCheck, string> RunNewExecutableChecks(string file, Stream stream, NewExecutable nex, bool includeDebug)
|
||||
#else
|
||||
public ConcurrentDictionary<INewExecutableCheck, string> RunNewExecutableChecks(string file, Stream stream, NewExecutable nex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Create the output dictionary
|
||||
#if NET20 || NET35
|
||||
var protections = new Dictionary<INewExecutableCheck, string>();
|
||||
#else
|
||||
var protections = new ConcurrentDictionary<INewExecutableCheck, string>();
|
||||
#endif
|
||||
|
||||
// Iterate through all checks
|
||||
#if NET20 || NET35
|
||||
foreach (var checkClass in NewExecutableCheckClasses)
|
||||
#else
|
||||
Parallel.ForEach(NewExecutableCheckClasses, checkClass =>
|
||||
#endif
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
var protection = checkClass.CheckNewExecutable(file, nex, includeDebug);
|
||||
if (string.IsNullOrEmpty(protection))
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
// If we are filtering on game engines
|
||||
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
// If we are filtering on packers
|
||||
if (CheckIfPacker(checkClass) && !IncludePackers)
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
#if NET20 || NET35
|
||||
protections[checkClass] = protection!;
|
||||
}
|
||||
#else
|
||||
protections.TryAdd(checkClass, protection!);
|
||||
});
|
||||
#endif
|
||||
|
||||
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>
|
||||
#if NET20 || NET35
|
||||
public Dictionary<IPortableExecutableCheck, string> RunPortableExecutableChecks(string file, Stream stream, PortableExecutable pex, bool includeDebug)
|
||||
#else
|
||||
public ConcurrentDictionary<IPortableExecutableCheck, string> RunPortableExecutableChecks(string file, Stream stream, PortableExecutable pex, bool includeDebug)
|
||||
#endif
|
||||
{
|
||||
// Create the output dictionary
|
||||
#if NET20 || NET35
|
||||
var protections = new Dictionary<IPortableExecutableCheck, string>();
|
||||
#else
|
||||
var protections = new ConcurrentDictionary<IPortableExecutableCheck, string>();
|
||||
#endif
|
||||
|
||||
// Iterate through all checks
|
||||
#if NET20 || NET35
|
||||
foreach (var checkClass in PortableExecutableCheckClasses)
|
||||
#else
|
||||
Parallel.ForEach(PortableExecutableCheckClasses, checkClass =>
|
||||
#endif
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
var protection = checkClass.CheckPortableExecutable(file, pex, includeDebug);
|
||||
if (string.IsNullOrEmpty(protection))
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
// If we are filtering on game engines
|
||||
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
// If we are filtering on packers
|
||||
if (CheckIfPacker(checkClass) && !IncludePackers)
|
||||
#if NET20 || NET35
|
||||
continue;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
|
||||
#if NET20 || NET35
|
||||
protections[checkClass] = protection!;
|
||||
}
|
||||
#else
|
||||
protections.TryAdd(checkClass, protection!);
|
||||
});
|
||||
#endif
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initializers
|
||||
|
||||
/// <summary>
|
||||
/// Initialize all implementations of a type
|
||||
/// </summary>
|
||||
private static IEnumerable<T>? InitCheckClasses<T>() =>
|
||||
InitCheckClasses<T>(Assembly.GetExecutingAssembly()) ?? [];
|
||||
|
||||
/// <summary>
|
||||
/// Initialize all implementations of a type
|
||||
/// </summary>
|
||||
private static IEnumerable<T>? InitCheckClasses<T>(Assembly assembly)
|
||||
{
|
||||
List<T> classTypes = [];
|
||||
|
||||
// If not all types can be loaded, use the ones that could be
|
||||
List<Type> assemblyTypes = [];
|
||||
try
|
||||
{
|
||||
assemblyTypes = assembly.GetTypes().ToList<Type>();
|
||||
}
|
||||
catch (ReflectionTypeLoadException rtle)
|
||||
{
|
||||
assemblyTypes = rtle.Types.Where(t => t != null)!.ToList<Type>();
|
||||
}
|
||||
|
||||
// Loop through all types
|
||||
foreach (Type type in assemblyTypes)
|
||||
{
|
||||
// If the type isn't a class or doesn't implement the interface
|
||||
if (!type.IsClass || type.GetInterface(typeof(T).Name) == null)
|
||||
continue;
|
||||
|
||||
// Try to create a concrete instance of the type
|
||||
var instance = (T?)Activator.CreateInstance(type);
|
||||
if (instance != null)
|
||||
classTypes.Add(instance);
|
||||
}
|
||||
|
||||
return classTypes;
|
||||
}
|
||||
|
||||
#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
|
||||
}
|
||||
}
|
||||
157
BinaryObjectScanner/FileType/GCF.cs
Normal file
157
BinaryObjectScanner/FileType/GCF.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life Game Cache File
|
||||
/// </summary>
|
||||
public class GCF : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename ?? $"file{index}");
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
// Now read the data sequentially and write out while we have data left
|
||||
long fileSize = file.Size;
|
||||
for (int i = 0; i < dataBlockOffsets.Count; i++)
|
||||
{
|
||||
int readSize = (int)Math.Min(item.Model.DataBlockHeader?.BlockSize ?? 0, fileSize);
|
||||
var data = item.ReadFromDataSource((int)dataBlockOffsets[i], readSize);
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
71
BinaryObjectScanner/FileType/GZIP.cs
Normal file
71
BinaryObjectScanner/FileType/GZIP.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.GZip;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// gzip archive
|
||||
/// </summary>
|
||||
public class GZIP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
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;
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
74
BinaryObjectScanner/FileType/InstallShieldArchiveV3.cs
Normal file
74
BinaryObjectScanner/FileType/InstallShieldArchiveV3.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using UnshieldSharp.Archive;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// InstallShield archive v3
|
||||
/// </summary>
|
||||
public class InstallShieldArchiveV3 : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
#if NET20 || NET35
|
||||
// Not supported for .NET Framework 2.0 or .NET Framework 3.5 due to library support
|
||||
return null;
|
||||
#else
|
||||
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 (fileContents == null || !string.IsNullOrEmpty(error))
|
||||
continue;
|
||||
|
||||
using (FileStream fs = File.OpenWrite(tempFile))
|
||||
{
|
||||
fs.Write(fileContents, 0, fileContents.Length);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using UnshieldSharp.Cabinet;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// InstallShield cabinet file
|
||||
/// </summary>
|
||||
public class InstallShieldCAB : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
#if NET20 || NET35
|
||||
// Not supported for .NET Framework 2.0 or .NET Framework 3.5 due to library support
|
||||
return null;
|
||||
#else
|
||||
// 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);
|
||||
|
||||
var cabfile = InstallShieldCabinet.Open(file);
|
||||
if (cabfile == null)
|
||||
return null;
|
||||
|
||||
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 ?? string.Empty);
|
||||
}
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
44
BinaryObjectScanner/FileType/LDSCRYPT.cs
Normal file
44
BinaryObjectScanner/FileType/LDSCRYPT.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Link Data Security encrypted file
|
||||
/// </summary>
|
||||
public class LDSCRYPT : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] magic = new byte[16];
|
||||
stream.Read(magic, 0, 16);
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x4C, 0x44, 0x53, 0x43, 0x52, 0x59, 0x50, 0x54 }))
|
||||
return "Link Data Security encrypted file";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if ((NETFRAMEWORK && !NET20 && !NET35 && !NET40) || NETCOREAPP) && WIN
|
||||
using StormLibSharp;
|
||||
#endif
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
internal class MPQ
|
||||
/// <summary>
|
||||
/// MoPaQ game data archive
|
||||
/// </summary>
|
||||
public class MPQ : IExtractable
|
||||
{
|
||||
public static bool ShouldScan(byte[] magic)
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (magic.StartsWith(new byte[] { 0x4d, 0x50, 0x51, 0x1a }))
|
||||
return true;
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
return false;
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
public static Dictionary<string, List<string>> Scan(Scanner scanner, string file)
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the mpq file itself fails
|
||||
#if NET20 || NET35 || NET40 || !WIN
|
||||
// Not supported for old .NET due to feature requirements
|
||||
// Not supported in non-Windows builds due to DLL requirements
|
||||
return null;
|
||||
#else
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
using (MpqArchive mpqArchive = new MpqArchive(file, FileAccess.Read))
|
||||
using (var mpqArchive = new MpqArchive(file, FileAccess.Read))
|
||||
{
|
||||
// Try to open the listfile
|
||||
string listfile = null;
|
||||
string? listfile = null;
|
||||
MpqFileStream listStream = mpqArchive.OpenFile("(listfile)");
|
||||
|
||||
|
||||
// If we can't read the listfile, we just return
|
||||
if (!listStream.CanRead)
|
||||
return null;
|
||||
|
||||
// Read the listfile in for processing
|
||||
using (StreamReader sr = new StreamReader(listStream))
|
||||
using (var sr = new StreamReader(listStream))
|
||||
{
|
||||
listfile = sr.ReadToEnd();
|
||||
}
|
||||
@@ -46,35 +59,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
|
||||
}
|
||||
}
|
||||
}
|
||||
70
BinaryObjectScanner/FileType/MicrosoftCAB.cs
Normal file
70
BinaryObjectScanner/FileType/MicrosoftCAB.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if ((NETFRAMEWORK && !NET20 && !NET35 && !NET40) || NETCOREAPP) && WIN
|
||||
using LibMSPackN;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Microsoft cabinet file
|
||||
/// </summary>
|
||||
/// <remarks>Specification available at <see href="http://download.microsoft.com/download/5/0/1/501ED102-E53F-4CE0-AA6B-B0F93629DDC6/Exchange/%5BMS-CAB%5D.pdf"/></remarks>
|
||||
/// <see href="https://github.com/wine-mirror/wine/tree/master/dlls/cabinet"/>
|
||||
public class MicrosoftCAB : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
#if NET20 || NET35 || NET40 || !WIN
|
||||
// Not supported for old .NET due to feature requirements
|
||||
// Not supported in non-Windows builds due to DLL requirements
|
||||
return null;
|
||||
#else
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
using (var cabArchive = new MSCabinet(file))
|
||||
{
|
||||
// Loop over each entry
|
||||
foreach (var compressedFile in cabArchive.GetFiles())
|
||||
{
|
||||
try
|
||||
{
|
||||
string tempFile = Path.Combine(tempPath, compressedFile.Filename);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(tempFile));
|
||||
compressedFile.ExtractTo(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
69
BinaryObjectScanner/FileType/MicrosoftLZ.cs
Normal file
69
BinaryObjectScanner/FileType/MicrosoftLZ.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Compression.LZ;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Microsoft LZ-compressed Files (LZ32)
|
||||
/// </summary>
|
||||
/// <remarks>This is treated like an archive type due to the packing style</remarks>
|
||||
public class MicrosoftLZ : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
129
BinaryObjectScanner/FileType/PAK.cs
Normal file
129
BinaryObjectScanner/FileType/PAK.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life Package File
|
||||
/// </summary>
|
||||
public class PAK : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename ?? $"file{index}");
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
122
BinaryObjectScanner/FileType/PFF.cs
Normal file
122
BinaryObjectScanner/FileType/PFF.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// NovaLogic Game Archive Format
|
||||
/// </summary>
|
||||
public class PFF : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
BinaryObjectScanner/FileType/PKZIP.cs
Normal file
74
BinaryObjectScanner/FileType/PKZIP.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Zip;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// PKWARE ZIP archive and derivatives
|
||||
/// </summary>
|
||||
public class PKZIP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
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;
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
44
BinaryObjectScanner/FileType/PLJ.cs
Normal file
44
BinaryObjectScanner/FileType/PLJ.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// PlayJ audio file
|
||||
/// </summary>
|
||||
public class PLJ : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] magic = new byte[16];
|
||||
stream.Read(magic, 0, 16);
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0xFF, 0x9D, 0x53, 0x4B }))
|
||||
return "PlayJ Audio File";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
158
BinaryObjectScanner/FileType/Quantum.cs
Normal file
158
BinaryObjectScanner/FileType/Quantum.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Quantum Archive
|
||||
/// </summary>
|
||||
public class Quantum : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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.IsNullOrEmpty(outputDirectory))
|
||||
// return false;
|
||||
|
||||
// // Create the full output path
|
||||
// filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// // Ensure the output directory is created
|
||||
// Directory.CreateDirectory(Path.GetDirectoryName(filename));
|
||||
|
||||
// // Try to write the data
|
||||
// try
|
||||
// {
|
||||
// // Open the output file for writing
|
||||
// using (Stream fs = File.OpenWrite(filename))
|
||||
// {
|
||||
// fs.Write(data, 0, data.Length);
|
||||
// }
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
71
BinaryObjectScanner/FileType/RAR.cs
Normal file
71
BinaryObjectScanner/FileType/RAR.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Rar;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// RAR archive
|
||||
/// </summary>
|
||||
public class RAR : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
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;
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
63
BinaryObjectScanner/FileType/SFFS.cs
Normal file
63
BinaryObjectScanner/FileType/SFFS.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// StarForce Filesystem file
|
||||
/// </summary>
|
||||
/// <see href="https://forum.xentax.com/viewtopic.php?f=21&t=2084"/>
|
||||
public class SFFS : IExtractable, IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] magic = new byte[16];
|
||||
stream.Read(magic, 0, 16);
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x53, 0x46, 0x46, 0x53 }))
|
||||
return "StarForce Filesystem Container";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
271
BinaryObjectScanner/FileType/SGA.cs
Normal file
271
BinaryObjectScanner/FileType/SGA.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// SGA game archive
|
||||
/// </summary>
|
||||
public class SGA : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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
|
||||
object? file;
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4: file = (item.Model.Directory as SabreTools.Models.SGA.Directory4)?.Files?[index]; break;
|
||||
case 5: file = (item.Model.Directory as SabreTools.Models.SGA.Directory5)?.Files?[index]; break;
|
||||
case 6: file = (item.Model.Directory as SabreTools.Models.SGA.Directory6)?.Files?[index]; break;
|
||||
case 7: file = (item.Model.Directory as SabreTools.Models.SGA.Directory7)?.Files?[index]; break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
if (file == null)
|
||||
return false;
|
||||
|
||||
// Create the filename
|
||||
var filename = string.Empty;
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4:
|
||||
case 5: filename = (file as SabreTools.Models.SGA.File4)?.Name; break;
|
||||
case 6: filename = (file as SabreTools.Models.SGA.File6)?.Name; break;
|
||||
case 7: filename = (file as SabreTools.Models.SGA.File7)?.Name; break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
// Loop through and get all parent directories
|
||||
var parentNames = new List<string?> { filename };
|
||||
|
||||
// Get the parent directory
|
||||
var folder = default(object);
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4: folder = (item.Model.Directory as SabreTools.Models.SGA.Directory4)?.Folders?.FirstOrDefault(f => f != null && index >= f.FileStartIndex && index <= f.FileEndIndex); break;
|
||||
case 5: folder = (item.Model.Directory as SabreTools.Models.SGA.Directory5)?.Folders?.FirstOrDefault(f => f != null && index >= f.FileStartIndex && index <= f.FileEndIndex); break;
|
||||
case 6: folder = (item.Model.Directory as SabreTools.Models.SGA.Directory6)?.Folders?.FirstOrDefault(f => f != null && index >= f.FileStartIndex && index <= f.FileEndIndex); break;
|
||||
case 7: folder = (item.Model.Directory as SabreTools.Models.SGA.Directory7)?.Folders?.FirstOrDefault(f => f != null && index >= f.FileStartIndex && index <= f.FileEndIndex); break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
// If we have a parent folder
|
||||
if (folder != null)
|
||||
{
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4: parentNames.Add((folder as SabreTools.Models.SGA.Folder4)?.Name); break;
|
||||
case 5:
|
||||
case 6:
|
||||
case 7: parentNames.Add((folder as SabreTools.Models.SGA.Folder5)?.Name); break;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Should the section name/alias be used in the path as well?
|
||||
|
||||
// Reverse and assemble the filename
|
||||
parentNames.Reverse();
|
||||
#if NET20 || NET35
|
||||
var parentNamesArray = parentNames.Cast<string>().ToArray();
|
||||
filename = parentNamesArray[0];
|
||||
for (int i = 1; i < parentNamesArray.Length; i++)
|
||||
{
|
||||
filename = Path.Combine(filename, parentNamesArray[i]);
|
||||
}
|
||||
#else
|
||||
filename = Path.Combine(parentNames.Cast<string>().ToArray());
|
||||
#endif
|
||||
|
||||
// Get the file offset
|
||||
long fileOffset;
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4:
|
||||
case 5: fileOffset = (file as SabreTools.Models.SGA.File4)?.Offset ?? 0; break;
|
||||
case 6: fileOffset = (file as SabreTools.Models.SGA.File6)?.Offset ?? 0; break;
|
||||
case 7: fileOffset = (file as SabreTools.Models.SGA.File7)?.Offset ?? 0; break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
// Adjust the file offset
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4: fileOffset += (item.Model.Header as SabreTools.Models.SGA.Header4)?.FileDataOffset ?? 0; break;
|
||||
case 5: fileOffset += (item.Model.Header as SabreTools.Models.SGA.Header4)?.FileDataOffset ?? 0; break;
|
||||
case 6: fileOffset += (item.Model.Header as SabreTools.Models.SGA.Header6)?.FileDataOffset ?? 0; break;
|
||||
case 7: fileOffset += (item.Model.Header as SabreTools.Models.SGA.Header6)?.FileDataOffset ?? 0; break;
|
||||
default: return false;
|
||||
};
|
||||
|
||||
// Get the file sizes
|
||||
long fileSize, outputFileSize;
|
||||
switch (item.Model.Header?.MajorVersion)
|
||||
{
|
||||
case 4:
|
||||
case 5:
|
||||
fileSize = (file as SabreTools.Models.SGA.File4)?.SizeOnDisk ?? 0;
|
||||
outputFileSize = (file as SabreTools.Models.SGA.File4)?.Size ?? 0;
|
||||
break;
|
||||
case 6:
|
||||
fileSize = (file as SabreTools.Models.SGA.File6)?.SizeOnDisk ?? 0;
|
||||
outputFileSize = (file as SabreTools.Models.SGA.File6)?.Size ?? 0;
|
||||
break;
|
||||
case 7:
|
||||
fileSize = (file as SabreTools.Models.SGA.File7)?.SizeOnDisk ?? 0;
|
||||
outputFileSize = (file as SabreTools.Models.SGA.File7)?.Size ?? 0;
|
||||
break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
// Read the compressed data directly
|
||||
var compressedData = item.ReadFromDataSource((int)fileOffset, (int)fileSize);
|
||||
if (compressedData == null)
|
||||
return false;
|
||||
|
||||
// If the compressed and uncompressed sizes match
|
||||
byte[] data;
|
||||
if (fileSize == outputFileSize)
|
||||
{
|
||||
data = compressedData;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Decompress the data
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
data = new byte[outputFileSize];
|
||||
Inflater inflater = new Inflater();
|
||||
inflater.SetInput(compressedData);
|
||||
inflater.Inflate(data);
|
||||
#else
|
||||
data = new byte[outputFileSize];
|
||||
#endif
|
||||
}
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using Stream fs = File.OpenWrite(filename);
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
68
BinaryObjectScanner/FileType/SevenZip.cs
Normal file
68
BinaryObjectScanner/FileType/SevenZip.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.SevenZip;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// 7-zip archive
|
||||
/// </summary>
|
||||
public class SevenZip : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
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;
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
71
BinaryObjectScanner/FileType/TapeArchive.cs
Normal file
71
BinaryObjectScanner/FileType/TapeArchive.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Tar;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Tape archive
|
||||
/// </summary>
|
||||
public class TapeArchive : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
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;
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
163
BinaryObjectScanner/FileType/Textfile.cs
Normal file
163
BinaryObjectScanner/FileType/Textfile.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Various generic textfile formats
|
||||
/// </summary>
|
||||
public class Textfile : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// Files can be protected in multiple ways
|
||||
var protections = new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
// Load the current file content
|
||||
var fileContent = string.Empty;
|
||||
#if NET20 || NET35 || NET40
|
||||
using (var sr = new StreamReader(stream, Encoding.Default, true, 1024 * 1024))
|
||||
#else
|
||||
using (var sr = new StreamReader(stream, Encoding.Default, true, 1024 * 1024, true))
|
||||
#endif
|
||||
{
|
||||
fileContent = sr.ReadToEnd();
|
||||
}
|
||||
|
||||
// AegiSoft License Manager
|
||||
// Found in "setup.ins" (Redump entry 73521/IA item "Nova_HoyleCasino99USA").
|
||||
if (fileContent.Contains("Failed to load the AegiSoft License Manager install program."))
|
||||
protections.Add("AegiSoft License Manager");
|
||||
|
||||
// CD-Key
|
||||
if (fileContent.Contains("a valid serial number is required"))
|
||||
protections.Add("CD-Key / Serial");
|
||||
else if (fileContent.Contains("serial number is located"))
|
||||
protections.Add("CD-Key / Serial");
|
||||
// Found in "Setup.Ins" ("Word Point 2002" in IA item "advantage-language-french-beginner-langmaster-2005").
|
||||
else if (fileContent.Contains("Please enter a valid registration number"))
|
||||
protections.Add("CD-Key / Serial");
|
||||
|
||||
// CopyKiller
|
||||
// Found in "autorun.dat" in CopyKiller versions 3.62 and 3.64.
|
||||
if (fileContent.Contains("CopyKiller CD-Protection V3.6x"))
|
||||
protections.Add("CopyKiller V3.62-V3.64");
|
||||
// Found in "autorun.dat" in CopyKiller versions 3.99 and 3.99a.
|
||||
else if (fileContent.Contains("CopyKiller V4 CD / DVD-Protection"))
|
||||
protections.Add("CopyKiller V3.99+");
|
||||
// Found in "engine.wzc" in CopyKiller versions 3.62 and 3.64.
|
||||
else if (fileContent.Contains("CopyKiller V3.6x Protection Engine"))
|
||||
protections.Add("CopyKiller V3.62-V3.64");
|
||||
// Found in "engine.wzc" in CopyKiller versions 3.99 and 3.99a.
|
||||
else if (fileContent.Contains("CopyKiller V3.99x Protection Engine"))
|
||||
protections.Add("CopyKiller V3.99+");
|
||||
|
||||
// Freelock
|
||||
// Found in "FILE_ID.DIZ" distributed with Freelock.
|
||||
if (fileContent.Contains("FREELOCK 1.0"))
|
||||
protections.Add("Freelock 1.0");
|
||||
else if (fileContent.Contains("FREELOCK 1.2"))
|
||||
protections.Add("Freelock 1.2");
|
||||
else if (fileContent.Contains("FREELOCK 1.2a"))
|
||||
protections.Add("Freelock 1.2a");
|
||||
else if (fileContent.Contains("FREELOCK 1.3"))
|
||||
protections.Add("Freelock 1.3");
|
||||
else if (fileContent.Contains("FREELOCK"))
|
||||
protections.Add("Freelock");
|
||||
|
||||
// MediaCloQ
|
||||
if (fileContent.Contains("SunnComm MediaCloQ"))
|
||||
protections.Add("MediaCloQ");
|
||||
else if (fileContent.Contains("http://download.mediacloq.com/"))
|
||||
protections.Add("MediaCloQ");
|
||||
else if (fileContent.Contains("http://www.sunncomm.com/mediacloq/"))
|
||||
protections.Add("MediaCloQ");
|
||||
|
||||
// MediaMax
|
||||
if (fileContent.Contains("MediaMax technology"))
|
||||
protections.Add("MediaMax CD-3");
|
||||
else if (fileContent.Contains("exclusive Cd3 technology"))
|
||||
protections.Add("MediaMax CD-3");
|
||||
else if (fileContent.Contains("<PROTECTION-VENDOR>MediaMAX</PROTECTION-VENDOR>"))
|
||||
protections.Add("MediaMax CD-3");
|
||||
else if (fileContent.Contains("MediaMax(tm)"))
|
||||
protections.Add("MediaMax CD-3");
|
||||
|
||||
// phenoProtect
|
||||
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");
|
||||
|
||||
// SafeDisc
|
||||
// TODO: Add better version parsing.
|
||||
// Found in "Info.plist" in Redump entries 23983, 42762, 72713, 73070, and 89603.
|
||||
if (fileContent.Contains("<string>com.europevisionmacro.SafeDiscDVD</string>"))
|
||||
{
|
||||
if (fileContent.Contains("<string>2.90.032</string>"))
|
||||
protections.Add("SafeDiscDVD for Macintosh 2.90.032");
|
||||
else
|
||||
protections.Add("SafeDiscDVD for Macintosh (Unknown Version - Please report to us on GitHub)");
|
||||
}
|
||||
|
||||
// Found in "Info.plist" in Redump entry 89649.
|
||||
if (fileContent.Contains("<string>com.macrovisioneurope.SafeDiscLT</string>"))
|
||||
{
|
||||
// TODO: Investigate why "CFBundleGetInfoString" and "CFBundleShortVersionString" say version 2.70.020, but "CFBundleVersion" says version 2.70.010.
|
||||
if (fileContent.Contains("<string>2.70.020</string"))
|
||||
protections.Add("SafeDiscLT for Macintosh 2.70.020");
|
||||
else
|
||||
protections.Add("SafeDiscLT for Macintosh (Unknown Version - Please report to us on GitHub)");
|
||||
}
|
||||
|
||||
|
||||
// The full line from a sample is as follows:
|
||||
//
|
||||
// The files securom_v7_01.dat and securom_v7_01.bak have been created during the installation of a SecuROM protected application.
|
||||
//
|
||||
// TODO: Use the filenames in this line to get the version out of it
|
||||
|
||||
// SecuROM
|
||||
if (fileContent.Contains("SecuROM protected application"))
|
||||
protections.Add("SecuROM");
|
||||
|
||||
// Steam
|
||||
if (fileContent.Contains("All use of the Program is governed by the terms of the Steam Agreement as described below."))
|
||||
protections.Add("Steam");
|
||||
|
||||
// XCP
|
||||
if (fileContent.Contains("http://cp.sonybmg.com/xcp/"))
|
||||
protections.Add("XCP");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return string.Join(";", [.. protections]);
|
||||
}
|
||||
}
|
||||
}
|
||||
138
BinaryObjectScanner/FileType/VBSP.cs
Normal file
138
BinaryObjectScanner/FileType/VBSP.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life 2 Level
|
||||
/// </summary>
|
||||
public class VBSP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
187
BinaryObjectScanner/FileType/VPK.cs
Normal file
187
BinaryObjectScanner/FileType/VPK.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
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/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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.IsNullOrEmpty(archiveFileName))
|
||||
return false;
|
||||
|
||||
// If the archive doesn't exist
|
||||
if (!File.Exists(archiveFileName))
|
||||
return false;
|
||||
|
||||
// Try to open the archive
|
||||
var archiveStream = default(Stream);
|
||||
try
|
||||
{
|
||||
// Open the archive
|
||||
archiveStream = File.Open(archiveFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
// Seek to the data
|
||||
archiveStream.Seek(directoryItem.DirectoryEntry.EntryOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the directory item bytes
|
||||
data = archiveStream.ReadBytes((int)directoryItem.DirectoryEntry.EntryLength);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
archiveStream?.Close();
|
||||
}
|
||||
|
||||
// If we have preload data, prepend it
|
||||
if (data != null && directoryItem.PreloadData != null)
|
||||
data = directoryItem.PreloadData.Concat(data).ToArray();
|
||||
}
|
||||
|
||||
// If there is nothing to write out
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// Create the filename
|
||||
string filename = $"{directoryItem.Name}.{directoryItem.Extension}";
|
||||
if (!string.IsNullOrEmpty(directoryItem.Path))
|
||||
filename = Path.Combine(directoryItem.Path, filename);
|
||||
|
||||
// If we have an invalid output directory
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
129
BinaryObjectScanner/FileType/WAD.cs
Normal file
129
BinaryObjectScanner/FileType/WAD.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life Texture Package File
|
||||
/// </summary>
|
||||
public class WAD : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
56
BinaryObjectScanner/FileType/XZ.cs
Normal file
56
BinaryObjectScanner/FileType/XZ.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Compressors.Xz;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// xz archive
|
||||
/// </summary>
|
||||
public class XZ : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
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;
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
139
BinaryObjectScanner/FileType/XZP.cs
Normal file
139
BinaryObjectScanner/FileType/XZP.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// XBox Package File
|
||||
/// </summary>
|
||||
public class XZP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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.IsNullOrEmpty(outputDirectory))
|
||||
return false;
|
||||
|
||||
// Create the full output path
|
||||
filename = Path.Combine(outputDirectory, filename ?? $"file{index}");
|
||||
|
||||
// Ensure the output directory is created
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null)
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Try to write the data
|
||||
try
|
||||
{
|
||||
// Open the output file for writing
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
BinaryObjectScanner/GameEngine/RenderWare.cs
Normal file
40
BinaryObjectScanner/GameEngine/RenderWare.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.GameEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// RenderWare (https://web.archive.org/web/20070214132346/http://www.renderware.com/) is an API and graphics engine created by Criterion in 1993.
|
||||
/// It appears that version 4.X was exclusively used by EA internally, with version 3.X being the final public version (https://sigmaco.org/3782-renderware/).
|
||||
/// It was available to use on many different platforms, with it being particularly useful for the PS2 (https://en.wikipedia.org/wiki/RenderWare).
|
||||
///
|
||||
/// Additional resources and documentation:
|
||||
/// RenderWare interview: https://web.archive.org/web/20031208124348/http://www.homelanfed.com/index.php?id=9856
|
||||
/// RenderWare V2.1 API reference: http://www.tnlc.com/rw/api/rwdoc.htm
|
||||
/// RenderWare 2 official docs: https://github.com/electronicarts/RenderWare3Docs
|
||||
/// RenderWare 3.7 SDK: https://github.com/sigmaco/rwsdk-v37-pc
|
||||
/// Wikipedia list of RenderWare games: https://en.wikipedia.org/wiki/Category:RenderWare_games
|
||||
/// </summary>
|
||||
public class RenderWare : IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Found in Redump entries 20138, 55823, and 102493.
|
||||
bool rwcsegSection = pex.ContainsSection("_rwcseg", exact: true);
|
||||
// Found in Redump entry 20138.
|
||||
bool rwdsegSection = pex.ContainsSection("_rwdseg", exact: true);
|
||||
|
||||
// TODO: Check if this indicates a specific version, or if these sections are present in multiple.
|
||||
if (rwcsegSection || rwdsegSection)
|
||||
return "RenderWare";
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
285
BinaryObjectScanner/Handler.cs
Normal file
285
BinaryObjectScanner/Handler.cs
Normal file
@@ -0,0 +1,285 @@
|
||||
using System;
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
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>
|
||||
public static IEnumerable<IPathCheck?> PathCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
pathCheckClasses ??= InitCheckClasses<IPathCheck>();
|
||||
return pathCheckClasses;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Instances
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IPathCheck types
|
||||
/// </summary>
|
||||
private static IEnumerable<IPathCheck?>? pathCheckClasses;
|
||||
|
||||
#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 NET20 || NET35
|
||||
public static Dictionary<string, Queue<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
|
||||
#if NET20 || NET35
|
||||
var protections = new Dictionary<string, Queue<string>>();
|
||||
#else
|
||||
var protections = new ConcurrentDictionary<string, ConcurrentQueue<string>>();
|
||||
#endif
|
||||
|
||||
// Preprocess the list of files
|
||||
files = files?.Select(f => f.Replace('\\', '/'))?.ToList();
|
||||
|
||||
// Iterate through all checks
|
||||
#if NET20 || NET35
|
||||
foreach (var checkClass in PathCheckClasses)
|
||||
#else
|
||||
Parallel.ForEach(PathCheckClasses, checkClass =>
|
||||
#endif
|
||||
{
|
||||
var subProtections = checkClass?.PerformCheck(path, files);
|
||||
if (subProtections != null)
|
||||
AppendToDictionary(protections, path, subProtections);
|
||||
#if NET20 || NET35
|
||||
}
|
||||
#else
|
||||
});
|
||||
#endif
|
||||
|
||||
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 NET20 || NET35
|
||||
public static Queue<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 NET20 || NET35
|
||||
public static Dictionary<string, Queue<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 NET20 || NET35
|
||||
private static Queue<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.IsNullOrEmpty(path))
|
||||
return null;
|
||||
|
||||
// Setup the output dictionary
|
||||
#if NET20 || NET35
|
||||
var protections = new Queue<string>();
|
||||
#else
|
||||
var protections = new ConcurrentQueue<string>();
|
||||
#endif
|
||||
|
||||
// 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>
|
||||
private static IEnumerable<T?> InitCheckClasses<T>() =>
|
||||
InitCheckClasses<T>(Assembly.GetExecutingAssembly());
|
||||
|
||||
/// <summary>
|
||||
/// Initialize all implementations of a type
|
||||
/// </summary>
|
||||
private static IEnumerable<T?> InitCheckClasses<T>(Assembly assembly)
|
||||
{
|
||||
List<T?> classTypes = [];
|
||||
|
||||
// If not all types can be loaded, use the ones that could be
|
||||
List<Type> assemblyTypes = [];
|
||||
try
|
||||
{
|
||||
assemblyTypes = assembly.GetTypes().ToList<Type>();
|
||||
}
|
||||
catch (ReflectionTypeLoadException rtle)
|
||||
{
|
||||
assemblyTypes = rtle.Types.Where(t => t != null)!.ToList<Type>();
|
||||
}
|
||||
|
||||
// Loop through all types
|
||||
foreach (Type type in assemblyTypes)
|
||||
{
|
||||
// If the type isn't a class or doesn't implement the interface
|
||||
if (!type.IsClass || type.GetInterface(typeof(T).Name) == null)
|
||||
continue;
|
||||
|
||||
// Try to create a concrete instance of the type
|
||||
var instance = (T?)Activator.CreateInstance(type);
|
||||
if (instance != null)
|
||||
classTypes.Add(instance);
|
||||
}
|
||||
|
||||
return classTypes;
|
||||
}
|
||||
|
||||
#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 NET20 || NET35
|
||||
private static Queue<string>? ProcessProtectionString(string? protection)
|
||||
#else
|
||||
private static ConcurrentQueue<string>? ProcessProtectionString(string? protection)
|
||||
#endif
|
||||
{
|
||||
// If we have an invalid protection string
|
||||
if (string.IsNullOrEmpty(protection))
|
||||
return null;
|
||||
|
||||
// Setup the output queue
|
||||
#if NET20 || NET35
|
||||
var protections = new Queue<string>();
|
||||
#else
|
||||
var protections = new ConcurrentQueue<string>();
|
||||
#endif
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
17
BinaryObjectScanner/Interfaces/IContentCheck.cs
Normal file
17
BinaryObjectScanner/Interfaces/IContentCheck.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
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>
|
||||
string? CheckContents(string file, byte[] fileContent, bool includeDebug);
|
||||
}
|
||||
}
|
||||
28
BinaryObjectScanner/Interfaces/IDetectable.cs
Normal file
28
BinaryObjectScanner/Interfaces/IDetectable.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.IO;
|
||||
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Mark a file type as being able to be detected
|
||||
/// </summary>
|
||||
public interface IDetectable
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if a file is detected as this file type
|
||||
/// </summary>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Detected file or protection type, null on error</returns>
|
||||
/// <remarks>Ideally, this should just point to the other detect implementation.</remarks>
|
||||
string? Detect(string file, bool includeDebug);
|
||||
|
||||
/// <summary>
|
||||
/// Check if a stream is detected as this file type
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream representing the input file</param>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Detected file or protection type, null on error</returns>
|
||||
string? Detect(Stream stream, string file, bool includeDebug);
|
||||
}
|
||||
}
|
||||
30
BinaryObjectScanner/Interfaces/IExtractable.cs
Normal file
30
BinaryObjectScanner/Interfaces/IExtractable.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System.IO;
|
||||
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Mark a file type as being able to be extracted
|
||||
/// </summary>
|
||||
/// 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>
|
||||
string? Extract(string file, bool includeDebug);
|
||||
|
||||
/// <summary>
|
||||
/// Extract a stream to a temporary path, if possible
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream representing the input file</param>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Path to extracted files, null on error</returns>
|
||||
string? Extract(Stream? stream, string file, bool includeDebug);
|
||||
}
|
||||
}
|
||||
19
BinaryObjectScanner/Interfaces/ILinearExecutableCheck.cs
Normal file
19
BinaryObjectScanner/Interfaces/ILinearExecutableCheck.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
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>
|
||||
string? CheckLinearExecutable(string file, LinearExecutable lex, bool includeDebug);
|
||||
}
|
||||
}
|
||||
19
BinaryObjectScanner/Interfaces/IMSDOSExecutableCheck.cs
Normal file
19
BinaryObjectScanner/Interfaces/IMSDOSExecutableCheck.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
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>
|
||||
string? CheckMSDOSExecutable(string file, MSDOS mz, bool includeDebug);
|
||||
}
|
||||
}
|
||||
19
BinaryObjectScanner/Interfaces/INewExecutableCheck.cs
Normal file
19
BinaryObjectScanner/Interfaces/INewExecutableCheck.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a New Executable (NE) for protection
|
||||
/// </summary>
|
||||
public interface INewExecutableCheck
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a path for protections based on file contents
|
||||
/// </summary>
|
||||
/// <param name="file">File to check for protection indicators</param>
|
||||
/// <param name="nex">NewExecutable representing the read-in file</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>String containing any protections found in the file</returns>
|
||||
string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug);
|
||||
}
|
||||
}
|
||||
35
BinaryObjectScanner/Interfaces/IPathCheck.cs
Normal file
35
BinaryObjectScanner/Interfaces/IPathCheck.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <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 a content check instead, if possible</remarks>
|
||||
#if NET20 || NET35
|
||||
Queue<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 a content check instead, if possible</remarks>
|
||||
string? CheckFilePath(string path);
|
||||
}
|
||||
}
|
||||
19
BinaryObjectScanner/Interfaces/IPortableExecutableCheck.cs
Normal file
19
BinaryObjectScanner/Interfaces/IPortableExecutableCheck.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <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="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? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug);
|
||||
}
|
||||
}
|
||||
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; }
|
||||
}
|
||||
}
|
||||
659
BinaryObjectScanner/Packer/ASPack.cs
Normal file
659
BinaryObjectScanner/Packer/ASPack.cs
Normal file
@@ -0,0 +1,659 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .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.IsNullOrEmpty(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.IsNullOrEmpty(match))
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x72, 0x05, 0x00, 0x00, 0xEB, 0x33,
|
||||
0x87, 0xDB, 0x90, 0x00
|
||||
}, "ASPack 2.00.01"),
|
||||
|
||||
new(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(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(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x02, 0x00, 0x00, 0x00, 0xEB, 0x09,
|
||||
0x5D, 0x55
|
||||
}, "ASPack 2.11b"),
|
||||
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x02, 0x00, 0x00, 0x00, 0xEB, 0x09,
|
||||
0x5D, 0x55, 0x81, 0xED, 0x39, 0x39, 0x44, 0x00,
|
||||
0xC3, 0xE9, 0x59, 0x04, 0x00, 0x00
|
||||
}, "ASPack 2.11c"),
|
||||
|
||||
new(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(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(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(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(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(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(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
|
||||
0xED, 0x06, null, null, null, 0x64, 0xA0, 0x23
|
||||
}, "ASPack 1.02a"),
|
||||
|
||||
new(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(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(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(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(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(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(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(new byte?[]
|
||||
{
|
||||
0x90, 0x61, 0xBE, null, null, null, null, 0x8D,
|
||||
0xBE, null, null, null, null, 0x57, 0x83, 0xCD,
|
||||
0xFF
|
||||
}, "ASPack 1.06.1b"),
|
||||
|
||||
new(new byte?[]
|
||||
{
|
||||
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, null,
|
||||
null, null, null, null, null, 0xB8, null, null,
|
||||
null, null, 0x03, 0xC5
|
||||
}, "ASPack 1.07b"),
|
||||
|
||||
new(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(new byte?[] { 0x75, 0x00, 0xE9 }, "ASPack 1.05b"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08.02"),
|
||||
|
||||
new(new byte?[] { 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08.02"),
|
||||
|
||||
new(new byte?[] { 0x60, 0xE8, 0x41, 0x06, 0x00, 0x00, 0xEB, 0x41 }, "ASPack 1.08.04"),
|
||||
|
||||
new(new byte?[] { 0x60, 0xE8, null, null, null, null, 0xEB }, "ASPack 1.08.04"),
|
||||
|
||||
new(new byte?[] { 0x60, 0xE8, 0x70, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.00"),
|
||||
|
||||
new(new byte?[] { 0x60, 0xE8, 0x48, 0x11, 0x00, 0x00, 0xC3, 0x83 }, "ASPack 2.00.00"),
|
||||
|
||||
new(new byte?[] { 0x60, 0xE8, 0x72, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.01"),
|
||||
|
||||
new(new byte?[] { 0x60, 0xE8, null, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.x -> Alexey Solodovnikov"),
|
||||
|
||||
new(new byte?[] { 0x60, 0xE9, 0x3D, 0x04, 0x00, 0x00 }, "ASPack 2.11"),
|
||||
|
||||
new(new byte?[] { 0x60, 0xE8, 0xF9, 0x11, 0x00, 0x00, 0xC3, 0x83 }, "ASPack 2.11"),
|
||||
|
||||
new(new byte?[] { 0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81, 0xED }, "ASPack 1.02b/1.08.03"),
|
||||
|
||||
#endregion
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
50
BinaryObjectScanner/Packer/AdvancedInstaller.cs
Normal file
50
BinaryObjectScanner/Packer/AdvancedInstaller.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .rdata section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".rdata");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("Software\\Caphyon\\Advanced Installer")))
|
||||
return "Caphyon Advanced Installer";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
62
BinaryObjectScanner/Packer/Armadillo.cs
Normal file
62
BinaryObjectScanner/Packer/Armadillo.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .nicode section, if it exists
|
||||
bool nicodeSection = pex.ContainsSection(".nicode", exact: true);
|
||||
if (nicodeSection)
|
||||
return "Armadillo";
|
||||
|
||||
// Loop through all "extension" sections -- usually .data1 or .text1
|
||||
if (pex.SectionNames != null)
|
||||
{
|
||||
foreach (var sectionName in pex.SectionNames.Where(s => s != null && s.EndsWith("1")))
|
||||
{
|
||||
// Get the section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(sectionName);
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("ARMDEBUG")))
|
||||
return "Armadillo";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
69
BinaryObjectScanner/Packer/AutoPlayMediaStudio.cs
Normal file
69
BinaryObjectScanner/Packer/AutoPlayMediaStudio.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// Created by IndigoRose (creators of Setup Factory), primarily to be used to create autorun menus for various media.
|
||||
// Official website: https://www.autoplay.org/
|
||||
// TODO: Add extraction
|
||||
public class AutoPlayMediaStudio : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Known to detect versions 5.0.0.3 - 8.1.0.0
|
||||
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 (name?.StartsWith("Runtime Engine", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"AutoPlay Media Studio {GetVersion(pex)}";
|
||||
*/
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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)";
|
||||
}
|
||||
}
|
||||
}
|
||||
150
BinaryObjectScanner/Packer/CExe.cs
Normal file
150
BinaryObjectScanner/Packer/CExe.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
#endif
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// If there are exactly 2 resources with type 99
|
||||
if (pex.FindResourceByNamedType("99, ").Count() == 2)
|
||||
return "CExe";
|
||||
|
||||
if (pex.StubExecutableData != null)
|
||||
{
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
new(new byte?[]
|
||||
{
|
||||
0x25, 0x57, 0x6F, 0xC1, 0x61, 0x36, 0x01, 0x92,
|
||||
0x61, 0x36, 0x01, 0x92, 0x61, 0x36, 0x01, 0x92,
|
||||
0x61, 0x36, 0x00, 0x92, 0x7B, 0x36, 0x01, 0x92,
|
||||
0x03, 0x29, 0x12, 0x92, 0x66, 0x36, 0x01, 0x92,
|
||||
0x89, 0x29, 0x0A, 0x92, 0x60, 0x36, 0x01, 0x92,
|
||||
0xD9, 0x30, 0x07, 0x92, 0x60, 0x36, 0x01, 0x92
|
||||
}, "CExe")
|
||||
};
|
||||
|
||||
var match = MatchUtil.GetFirstMatch(file, pex.StubExecutableData, matchers, includeDebug);
|
||||
if (!string.IsNullOrEmpty(match))
|
||||
return match;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
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();
|
||||
#else
|
||||
data = null;
|
||||
#endif
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
95
BinaryObjectScanner/Packer/EXEStealth.cs
Normal file
95
BinaryObjectScanner/Packer/EXEStealth.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
// TODO: Figure out how to more granularly determine versions like PiD
|
||||
// TODO: Detect 3.15 and up (maybe looking for `Metamorphism`)
|
||||
// TODO: Add extraction
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
public class EXEStealth : IContentCheck, IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
{
|
||||
// TODO: Obtain a sample to find where this string is in a typical executable
|
||||
if (includeDebug)
|
||||
{
|
||||
var contentMatchSets = new List<ContentMatchSet>
|
||||
{
|
||||
// ??[[__[[_ + (char)0x00 + {{ + (char)0x0 + (char)0x00 + {{ + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x0 + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x00 + ?;??;??
|
||||
new(new byte?[]
|
||||
{
|
||||
0x3F, 0x3F, 0x5B, 0x5B, 0x5F, 0x5F, 0x5B, 0x5B,
|
||||
0x5F, 0x00, 0x7B, 0x7B, 0x00, 0x00, 0x7B, 0x7B,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x20, 0x3F, 0x3B, 0x3F, 0x3F, 0x3B, 0x3F,
|
||||
0x3F
|
||||
}, "EXE Stealth"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchSets, includeDebug);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
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)
|
||||
return "EXE Stealth 2.41-2.75";
|
||||
|
||||
// Get the mtw section, if it exists
|
||||
bool mtwSection = pex.ContainsSection("mtw", exact: true);
|
||||
if (mtwSection)
|
||||
return "EXE Stealth 1.1";
|
||||
|
||||
// Get the rsrr section, if it exists
|
||||
bool rsrrSection = pex.ContainsSection("rsrr", exact: true);
|
||||
if (rsrrSection)
|
||||
return "EXE Stealth 2.76";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
98
BinaryObjectScanner/Packer/EmbeddedExecutable.cs
Normal file
98
BinaryObjectScanner/Packer/EmbeddedExecutable.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the resources that have an executable signature
|
||||
if (pex.ResourceData?.Any(kvp => kvp.Value is byte[] ba && ba.StartsWith(SabreTools.Models.MSDOS.Constants.SignatureBytes)) == true)
|
||||
return "Embedded Executable";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
BinaryObjectScanner/Packer/GenteeInstaller.cs
Normal file
53
BinaryObjectScanner/Packer/GenteeInstaller.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("Gentee installer")))
|
||||
return "Gentee Installer";
|
||||
|
||||
if (strs.Any(s => s.Contains("ginstall.dll")))
|
||||
return "Gentee Installer";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
BinaryObjectScanner/Packer/HyperTechCrackProof.cs
Normal file
52
BinaryObjectScanner/Packer/HyperTechCrackProof.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// This check may be overly limiting, as it excludes the sample provided to DiE (https://github.com/horsicq/Detect-It-Easy/issues/102).
|
||||
// TODO: Find further samples and invesitgate if the "peC" section is only present on specific versions.
|
||||
bool peCSection = pex.ContainsSection("peC", exact: true);
|
||||
bool importTableMatch = (pex.Model.ImportTable?.ImportDirectoryTable?.Any(idte => idte?.Name == "KeRnEl32.dLl") ?? false);
|
||||
|
||||
if (peCSection && importTableMatch)
|
||||
return "HyperTech CrackProof";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
97
BinaryObjectScanner/Packer/InnoSetup.cs
Normal file
97
BinaryObjectScanner/Packer/InnoSetup.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
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/>
|
||||
public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
{
|
||||
// Check for "Inno" in the reserved words
|
||||
if (nex.Model.Stub?.Header?.Reserved2?[4] == 0x6E49 && nex.Model.Stub?.Header?.Reserved2?[5] == 0x6F6E)
|
||||
{
|
||||
string version = GetOldVersion(file, nex);
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return $"Inno Setup {version}";
|
||||
|
||||
return "Inno Setup (Unknown Version)";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
var str = strs.FirstOrDefault(s => s.StartsWith("Inno Setup Setup Data"));
|
||||
if (str != null)
|
||||
{
|
||||
return str.Replace("Inno Setup Setup Data", "Inno Setup")
|
||||
.Replace("(u)", "[Unicode]")
|
||||
.Replace("(", string.Empty)
|
||||
.Replace(")", string.Empty)
|
||||
.Replace("[Unicode]", "(Unicode)");
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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(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";
|
||||
}
|
||||
}
|
||||
}
|
||||
59
BinaryObjectScanner/Packer/InstallAnywhere.cs
Normal file
59
BinaryObjectScanner/Packer/InstallAnywhere.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var name= pex.FileDescription;
|
||||
if (name?.StartsWith("InstallAnywhere Self Extractor", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"InstallAnywhere {GetVersion(pex)}";
|
||||
|
||||
name = pex.ProductName;
|
||||
if (name?.StartsWith("InstallAnywhere", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"InstallAnywhere {GetVersion(pex)}";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetVersion(PortableExecutable pex)
|
||||
{
|
||||
// Check the internal versions
|
||||
var version = pex.GetInternalVersion();
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return version!;
|
||||
|
||||
return "(Unknown Version)";
|
||||
}
|
||||
}
|
||||
}
|
||||
51
BinaryObjectScanner/Packer/InstallerVISE.cs
Normal file
51
BinaryObjectScanner/Packer/InstallerVISE.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("ViseMain")))
|
||||
return "Installer VISE";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
BinaryObjectScanner/Packer/IntelInstallationFramework.cs
Normal file
54
BinaryObjectScanner/Packer/IntelInstallationFramework.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var name= pex.FileDescription;
|
||||
if (name?.Equals("Intel(R) Installation Framework", StringComparison.OrdinalIgnoreCase) == true
|
||||
|| name?.Equals("Intel Installation Framework", StringComparison.OrdinalIgnoreCase) == true)
|
||||
{
|
||||
return $"Intel Installation Framework {pex.GetInternalVersion()}";
|
||||
}
|
||||
|
||||
name = pex.ProductName;
|
||||
if (name?.Equals("Intel(R) Installation Framework", StringComparison.OrdinalIgnoreCase) == true
|
||||
|| name?.Equals("Intel Installation Framework", StringComparison.OrdinalIgnoreCase) == true)
|
||||
{
|
||||
return $"Intel Installation Framework {pex.GetInternalVersion()}";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
BinaryObjectScanner/Packer/MicrosoftCABSFX.cs
Normal file
78
BinaryObjectScanner/Packer/MicrosoftCABSFX.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var name= pex.InternalName;
|
||||
if (name?.Equals("Wextract", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"Microsoft CAB SFX {GetVersion(pex)}";
|
||||
|
||||
name = pex.OriginalFilename;
|
||||
if (name?.Equals("WEXTRACT.EXE", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return $"Microsoft CAB SFX {GetVersion(pex)}";
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("wextract_cleanup")))
|
||||
return $"Microsoft CAB SFX {GetVersion(pex)}";
|
||||
}
|
||||
|
||||
// Get the .text section strings, if they exist
|
||||
strs = pex.GetFirstSectionStrings(".text");
|
||||
if (strs != null)
|
||||
{
|
||||
// This detects a different but similar type of SFX that uses Microsoft CAB files.
|
||||
// Further research is needed to see if it's just a different version or entirely separate.
|
||||
if (strs.Any(s => s.Contains("MSCFu")))
|
||||
return $"Microsoft CAB SFX {GetVersion(pex)}";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetVersion(PortableExecutable pex)
|
||||
{
|
||||
// Check the internal versions
|
||||
var version = pex.GetInternalVersion();
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return $"v{version}";
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
BinaryObjectScanner/Packer/NSIS.cs
Normal file
53
BinaryObjectScanner/Packer/NSIS.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var description = pex.AssemblyDescription;
|
||||
if (!string.IsNullOrEmpty(description) && description!.StartsWith("Nullsoft Install System"))
|
||||
return $"NSIS {description.Substring("Nullsoft Install System".Length).Trim()}";
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("NullsoftInst")))
|
||||
return "NSIS";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
BinaryObjectScanner/Packer/NeoLite.cs
Normal file
58
BinaryObjectScanner/Packer/NeoLite.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .neolit section, if it exists.
|
||||
// TODO: Check if this section is also present in NeoLite 1.X.
|
||||
bool neolitSection = pex.ContainsSection(".neolit", exact: true);
|
||||
if (neolitSection)
|
||||
return "NeoLite";
|
||||
|
||||
// If more specific or additional checks are needed, "NeoLite Executable File Compressor" should be present
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
// TODO: Add extraction
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
62
BinaryObjectScanner/Packer/PECompact.cs
Normal file
62
BinaryObjectScanner/Packer/PECompact.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// 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/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
45
BinaryObjectScanner/Packer/Petite.cs
Normal file
45
BinaryObjectScanner/Packer/Petite.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .petite section, if it exists -- TODO: Is there a version number that can be found?
|
||||
bool petiteSection = pex.ContainsSection(".petite", exact: true);
|
||||
if (petiteSection)
|
||||
return "PEtite";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
74
BinaryObjectScanner/Packer/SetupFactory.cs
Normal file
74
BinaryObjectScanner/Packer/SetupFactory.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// 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/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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)";
|
||||
}
|
||||
}
|
||||
}
|
||||
66
BinaryObjectScanner/Packer/SevenZipSFX.cs
Normal file
66
BinaryObjectScanner/Packer/SevenZipSFX.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the assembly description, if possible
|
||||
if (pex.AssemblyDescription?.StartsWith("7-Zip Self-extracting Archive") == true)
|
||||
return $"7-Zip SFX {pex.AssemblyDescription.Substring("7-Zip Self-extracting Archive ".Length)}";
|
||||
|
||||
// Get the file description, if it exists
|
||||
if (pex.FileDescription?.Equals("7z SFX") == true)
|
||||
return "7-Zip SFX";
|
||||
if (pex.FileDescription?.Equals("7z Self-Extract Setup") == true)
|
||||
return "7-Zip SFX";
|
||||
|
||||
// Get the original filename, if it exists
|
||||
if (pex.OriginalFilename?.Equals("7z.sfx.exe") == true)
|
||||
return "7-Zip SFX";
|
||||
else if (pex.OriginalFilename?.Equals("7zS.sfx") == true)
|
||||
return "7-Zip SFX";
|
||||
|
||||
// Get the internal name, if it exists
|
||||
if (pex.InternalName?.Equals("7z.sfx") == true)
|
||||
return "7-Zip SFX";
|
||||
else if (pex.InternalName?.Equals("7zS.sfx") == true)
|
||||
return "7-Zip SFX";
|
||||
|
||||
// If any dialog boxes match
|
||||
if (pex.FindDialogByTitle("7-Zip self-extracting archive").Any())
|
||||
return "7-Zip SFX";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
BinaryObjectScanner/Packer/Shrinker.cs
Normal file
46
BinaryObjectScanner/Packer/Shrinker.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .shrink0 and .shrink2 sections, if they exist -- TODO: Confirm if both are needed or either/or is fine
|
||||
bool shrink0Section = pex.ContainsSection(".shrink0", true);
|
||||
bool shrink2Section = pex.ContainsSection(".shrink2", true);
|
||||
if (shrink0Section || shrink2Section)
|
||||
return "Shrinker";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
113
BinaryObjectScanner/Packer/UPX.cs
Normal file
113
BinaryObjectScanner/Packer/UPX.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Check header padding strings
|
||||
if (pex.HeaderPaddingStrings?.Any() == true)
|
||||
{
|
||||
var match = pex.HeaderPaddingStrings.FirstOrDefault(s => s.Contains("UPX!"));
|
||||
//if (match != null)
|
||||
// return "UPX";
|
||||
|
||||
match = pex.HeaderPaddingStrings.FirstOrDefault(s => s.StartsWith("$Id: UPX"));
|
||||
if (match != null)
|
||||
{
|
||||
var regexMatch = _oldUpxVersionMatch.Match(match);
|
||||
if (regexMatch.Success)
|
||||
return $"UPX {regexMatch.Groups[1].Value}";
|
||||
else
|
||||
return "UPX (Unknown Version)";
|
||||
}
|
||||
|
||||
match = pex.HeaderPaddingStrings.FirstOrDefault(s => _upxVersionMatch.IsMatch(s));
|
||||
if (match != null && pex.HeaderPaddingStrings.Any(s => s == "UPX!"))
|
||||
{
|
||||
var regexMatch = _upxVersionMatch.Match(match);
|
||||
if (regexMatch.Success)
|
||||
return $"UPX {regexMatch.Groups[1].Value}";
|
||||
else
|
||||
return "UPX (Unknown Version)";
|
||||
}
|
||||
else if (match != null && pex.HeaderPaddingStrings.Any(s => s == "NOS "))
|
||||
{
|
||||
var regexMatch = _upxVersionMatch.Match(match);
|
||||
if (regexMatch.Success)
|
||||
return $"UPX (NOS Variant) {regexMatch.Groups[1].Value}";
|
||||
else
|
||||
return "UPX (NOS Variant) (Unknown Version)";
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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)";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
90
BinaryObjectScanner/Packer/WinRARSFX.cs
Normal file
90
BinaryObjectScanner/Packer/WinRARSFX.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Rar;
|
||||
using SharpCompress.Readers;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
public class WinRARSFX : IExtractable, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var name = pex.AssemblyDescription;
|
||||
if (name?.Contains("WinRAR archiver") == true)
|
||||
return "WinRAR SFX";
|
||||
|
||||
var resources = pex.FindDialogByTitle("WinRAR self-extracting archive");
|
||||
if (resources.Any())
|
||||
return "WinRAR SFX";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
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;
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
804
BinaryObjectScanner/Packer/WinZipSFX.cs
Normal file
804
BinaryObjectScanner/Packer/WinZipSFX.cs
Normal file
@@ -0,0 +1,804 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Zip;
|
||||
#endif
|
||||
|
||||
namespace BinaryObjectScanner.Packer
|
||||
{
|
||||
public class WinZipSFX : IExtractable, INewExecutableCheck, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
{
|
||||
// If the resident-name table doesnt exist
|
||||
if (nex.Model.ResidentNameTable == null)
|
||||
return null;
|
||||
|
||||
// Check for the WinZip name 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.IsNullOrEmpty(version))
|
||||
return $"WinZip SFX {version}";
|
||||
|
||||
return $"WinZip SFX Unknown Version (16-bit)";
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Check the export directory table, if it exists
|
||||
if (pex.Model.ExportTable?.ExportDirectoryTable != null)
|
||||
{
|
||||
var version = GetPEExportDirectoryVersion(pex);
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
return $"WinZip SFX {version}";
|
||||
}
|
||||
|
||||
// Get the _winzip_ section, if it exists
|
||||
if (pex.ContainsSection("_winzip_", exact: true))
|
||||
return "WinZip SFX Unknown Version (32-bit)";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Find a way to generically detect 2.X versions and improve exact version detection for SFX PE versions bundled with WinZip 11+
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
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;
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from the NE header value combinations
|
||||
/// </summary>
|
||||
/// TODO: Reduce the checks to only the ones that differ between versions
|
||||
/// TODO: Research to see if the versions are embedded elsewhere in these files
|
||||
private string? GetNEHeaderVersion(NewExecutable nex)
|
||||
{
|
||||
#region 2.0 Variants
|
||||
|
||||
// 2.0 (MS-DOS/16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0086
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00012BE6
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000044B8
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "2.0 (MS-DOS/16-bit)";
|
||||
|
||||
// 2.0 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0086
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00013174
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000198
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "2.0 (16-bit)";
|
||||
|
||||
// Compact 2.0 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0080
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x000124A0
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0003
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006A
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000192
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Compact 2.0 (16-bit)";
|
||||
|
||||
// Software Installation 2.0 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x00CD
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x000136FA
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0005
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0097
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x00A3
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00AD
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001DF
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Software Installation 2.0 (16-bit)";
|
||||
|
||||
#endregion
|
||||
|
||||
#region 2.1 RC2 Variants
|
||||
|
||||
// 2.1 RC2 (MS-DOS/16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0086
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00013386
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000043C8
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "2.1 RC2 (MS-DOS/16-bit)";
|
||||
|
||||
// 2.1 RC2 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x00BE
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00013E56
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "2.1 RC2 (16-bit)";
|
||||
|
||||
// Compact 2.1 RC2 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0080
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00012B84
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0003
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006A
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000192
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Compact 2.1 RC2 (16-bit)";
|
||||
|
||||
// Software Installation 2.1 RC2 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x00BE
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x000143AC
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Software Installation 2.1 RC2 (16-bit)";
|
||||
|
||||
#endregion
|
||||
|
||||
#region 2.1 Variants
|
||||
|
||||
// 2.1 (MS-DOS/16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0086
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x3A00
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00013396
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000043C8
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "2.1 (MS-DOS/16-bit)";
|
||||
|
||||
// 2.1 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x00BE
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x3A00
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00013E7E
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "2.1 (16-bit)";
|
||||
|
||||
// Compact 2.1 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0080
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x3A00
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00012B90
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0003
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006A
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000192
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Compact 2.1 (16-bit)";
|
||||
|
||||
// Software Installation 2.1 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x00BE
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x3A00
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00014408
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Software Installation 2.1 (16-bit)";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Misc. Variants
|
||||
|
||||
// Personal Edition (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x0086
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x4000
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x0001317C
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000198
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Personal Edition (16-bit)";
|
||||
|
||||
// Personal Edition 32-bit (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x00BE
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x3C00
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00013E7C
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Personal Edition 32-bit (16-bit)";
|
||||
|
||||
// Personal Edition 32-bit Build 1260/1285 (16-bit)
|
||||
if (nex.Model.Header?.LinkerVersion == 0x11
|
||||
&& nex.Model.Header?.LinkerRevision == 0x20
|
||||
&& nex.Model.Header?.EntryTableOffset == 0x00C6
|
||||
&& nex.Model.Header?.EntryTableSize == 0x0002
|
||||
&& nex.Model.Header?.CrcChecksum == 0x00000000
|
||||
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
|
||||
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
|
||||
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
|
||||
&& nex.Model.Header?.InitialHeapAlloc == 0x43DC
|
||||
&& nex.Model.Header?.InitialStackAlloc == 0x2708
|
||||
&& nex.Model.Header?.InitialCSIPSetting == 0x00014ADC
|
||||
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
|
||||
&& nex.Model.Header?.FileSegmentCount == 0x0003
|
||||
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0005
|
||||
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
|
||||
&& nex.Model.Header?.SegmentTableOffset == 0x0040
|
||||
&& nex.Model.Header?.ResourceTableOffset == 0x0058
|
||||
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
|
||||
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
|
||||
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A6
|
||||
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D8
|
||||
&& nex.Model.Header?.MovableEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
|
||||
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
|
||||
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
|
||||
&& nex.Model.Header?.AdditionalFlags == 0x00
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
|
||||
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
|
||||
&& nex.Model.Header?.WindowsSDKRevision == 0x00
|
||||
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
|
||||
return "Personal Edition 32-bit Build 1260/1285 (16-bit)";
|
||||
|
||||
#endregion
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from the PE export directory table value combinations
|
||||
/// </summary>
|
||||
/// TODO: Research to see if the versions are embedded elsewhere in these files
|
||||
private string? GetPEExportDirectoryVersion(PortableExecutable pex)
|
||||
{
|
||||
string sfxFileName = pex.Model.ExportTable?.ExportDirectoryTable?.Name ?? string.Empty;
|
||||
uint sfxTimeDateStamp = pex.Model.ExportTable?.ExportDirectoryTable?.TimeDateStamp ?? uint.MaxValue;
|
||||
string assemblyVersion = pex.AssemblyVersion ?? "Unknown Version";
|
||||
|
||||
// Standard
|
||||
if (sfxFileName == "VW95SE.SFX" || sfxFileName == "ST32E.SFX"
|
||||
|| sfxFileName == "WZIPSE32.exe" || sfxFileName == "SI32LPG.SFX"
|
||||
|| sfxFileName == "ST32E.WZE")
|
||||
{
|
||||
return sfxTimeDateStamp switch
|
||||
{
|
||||
842636344 => "2.0 (32-bit)",
|
||||
865370756 => "2.1 RC2 (32-bit)",
|
||||
869059925 => "2.1 (32-bit)",
|
||||
979049321 => "2.2.4003",
|
||||
1149714685 => "3.0.7158",
|
||||
1185211734 => "3.1.7556",
|
||||
1185211920 => "3.1.7556",
|
||||
1235490556 => "4.0.8421",
|
||||
1235490757 => "4.0.8421",
|
||||
1235490687 => "4.0.8421",// 3.1.8421.0, SI32LPG?
|
||||
1257193383 => "4.0.8672",// 3.1.8672.0
|
||||
1257193543 => "4.0.8672",
|
||||
1470410848 => "4.0.12218",// 4.0.1221.0
|
||||
_ => $"{assemblyVersion} (32-bit)",
|
||||
};
|
||||
}
|
||||
|
||||
// Personal Edition
|
||||
if (sfxFileName == "VW95LE.SFX" || sfxFileName == "PE32E.SFX"
|
||||
|| sfxFileName == "wzsepe32.exe" || sfxFileName == "SI32PE.SFX"
|
||||
|| sfxFileName == "SI32LPE.SFX")
|
||||
{
|
||||
return sfxTimeDateStamp switch
|
||||
{
|
||||
845061601 => "Personal Edition (32-bit)",// TODO: Find version
|
||||
868303343 => "Personal Edition (32-bit)",// TODO: Find version
|
||||
868304170 => "Personal Edition (32-bit)",// TODO: Find version
|
||||
906039079 => "Personal Edition 2.2.1260 (32-bit)",
|
||||
906040543 => "Personal Edition 2.2.1260 (32-bit)",
|
||||
908628435 => "Personal Edition 2.2.1285 (32-bit)",
|
||||
908628785 => "Personal Edition 2.2.1285 (32-bit)",
|
||||
956165981 => "Personal Edition 2.2.3063",
|
||||
956166038 => "Personal Edition 2.2.3063",
|
||||
1006353695 => "Personal Edition 2.2.4325",
|
||||
1006353714 => "Personal Edition 2.2.4325",// 8.1.0.0
|
||||
1076515698 => "Personal Edition 2.2.6028",
|
||||
1076515784 => "Personal Edition 2.2.6028",// 9.0.6028.0
|
||||
1092688561 => "Personal Edition 2.2.6224",
|
||||
1092688645 => "Personal Edition 2.2.6224",// 9.0.6224.0
|
||||
1125074095 => "Personal Edition 2.2.6604",
|
||||
1125074162 => "Personal Edition 2.2.6604",// 10.0.6604.0
|
||||
1130153399 => "Personal Edition 2.2.6663",
|
||||
1130153428 => "Personal Edition 2.2.6663",// 10.0.6663.0
|
||||
1149714176 => "Personal Edition 3.0.7158",
|
||||
1163137967 => "Personal Edition 3.0.7305",
|
||||
1163137994 => "Personal Edition 3.0.7313",// 11.0.7313.0
|
||||
1176345383 => "Personal Edition 3.0.7452",
|
||||
1176345423 => "Personal Edition 3.1.7466",// 11.1.7466.0
|
||||
1184106698 => "Personal Edition 3.1.7556",
|
||||
1207280880 => "Personal Edition 4.0.8060",// 2.3.7382.0
|
||||
1207280892 => "Personal Edition 4.0.8094",// 11.2.8094.0
|
||||
1220904506 => "Personal Edition 4.0.8213",// 2.3.7382.0
|
||||
1220904518 => "Personal Edition 4.0.8252",// 12.0.8252.0
|
||||
1235490648 => "Personal Edition 4.0.8421",// 3.1.8421.0
|
||||
1242049399 => "Personal Edition 4.0.8497",// 12.1.8497.0
|
||||
1257193469 => "Personal Edition 4.0.8672",// 3.1.8672.0, SI32LPE?
|
||||
_ => $"Personal Edition {assemblyVersion} (32-bit)",
|
||||
};
|
||||
}
|
||||
|
||||
// Software Installation
|
||||
else if (sfxFileName == "VW95SRE.SFX" || sfxFileName == "SI32E.SFX"
|
||||
|| sfxFileName == "SI32E.WZE")
|
||||
{
|
||||
return sfxTimeDateStamp switch
|
||||
{
|
||||
842636381 => "Software Installation 2.0 (32-bit)",
|
||||
865370800 => "Software Installation 2.1 RC2 (32-bit)",
|
||||
869059963 => "Software Installation 2.1 (32-bit)",
|
||||
893107697 => "Software Installation 2.2.1110 (32-bit)",
|
||||
952007369 => "Software Installation 2.2.3063",
|
||||
1006352634 => "Software Installation 2.2.4325",// +Personal Edition?
|
||||
979049345 => "Software Installation 2.2.4403",
|
||||
1026227373 => "Software Installation 2.2.5196",// +Personal Edition?
|
||||
1090582390 => "Software Installation 2.2.6202",// +Personal Edition?
|
||||
1149714757 => "Software Installation 3.0.7158",
|
||||
1154357628 => "Software Installation 3.0.7212",
|
||||
1175234637 => "Software Installation 3.0.7454",
|
||||
1185211802 => "Software Installation 3.1.7556",
|
||||
1470410906 => "Software Installation 4.0.12218",// 4.0.1221.0
|
||||
_ => $"Software Installation {assemblyVersion} (32-bit)",
|
||||
};
|
||||
}
|
||||
|
||||
return sfxFileName switch
|
||||
{
|
||||
// Standard
|
||||
"VW95SE.SFX" => "Unknown Version (32-bit)",// TODO: Find starting version
|
||||
"ST32E.SFX" => "Unknown Version (32-bit)",// TODO: Find starting version
|
||||
"WZIPSE32.exe" => "Unknown Version (32-bit)",// TODO: Find starting version
|
||||
"SI32LPG.SFX" => "Unknown Version (32-bit)",// TODO: Find starting version
|
||||
"ST32E.WZE" => "Unknown Version (32-bit)",// TODO: Find starting version
|
||||
|
||||
// Personal Edition
|
||||
"VW95LE.SFX" => "Unknown Version before Personal Edition Build 1285 (32-bit)",
|
||||
"PE32E.SFX" => "Unknown Version after Personal Edition Build 1285 (32-bit)",
|
||||
"wzsepe32.exe" => "Unknown Version Personal Edition (32-bit)",// TODO: Find starting version
|
||||
"SI32PE.SFX" => "Unknown Version Personal Edition (32-bit)",// TODO: Find starting version
|
||||
"SI32LPE.SFX" => "Unknown Version Personal Edition (32-bit)",// TODO: Find starting version
|
||||
|
||||
// Software Installation
|
||||
"VW95SRE.SFX" => "Unknown Version before Software Installation 2.1 (32-bit)",
|
||||
"SI32E.SFX" => "Unknown Version after Software Installation 2.1 (32-bit)",
|
||||
"SI32E.WZE" => "Unknown Version Software Installation (32-bit)",// TODO: Find starting version
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
410
BinaryObjectScanner/Packer/WiseInstaller.cs
Normal file
410
BinaryObjectScanner/Packer/WiseInstaller.cs
Normal file
@@ -0,0 +1,410 @@
|
||||
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/>
|
||||
public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
{
|
||||
// If we match a known header
|
||||
if (MatchesNEVersion(nex) != null)
|
||||
return "Wise Installation Wizard Module";
|
||||
|
||||
// TODO: Investigate STUB.EXE in nonresident-name table
|
||||
|
||||
// TODO: Don't read entire file
|
||||
var data = nex.ReadArbitraryRange();
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
var neMatchSets = new List<ContentMatchSet>
|
||||
{
|
||||
// WiseInst
|
||||
new(new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x49, 0x6E, 0x73, 0x74 }, "Wise Installation Wizard Module"),
|
||||
|
||||
// WiseMain
|
||||
new(new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x4D, 0x61, 0x69, 0x6E }, "Wise Installation Wizard Module"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(file, data, neMatchSets, includeDebug);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// If we match a known header
|
||||
if (GetPEFormat(pex) != null)
|
||||
return "Wise Installation Wizard Module";
|
||||
|
||||
// TODO: Investigate STUB32.EXE in export directory table
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("WiseMain")))
|
||||
return "Wise Installation Wizard Module";
|
||||
}
|
||||
|
||||
// Get the .rdata section strings, if they exist
|
||||
strs = pex.GetFirstSectionStrings(".rdata");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("WiseMain")))
|
||||
return "Wise Installation Wizard Module";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
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>
|
||||
private FormatProperty? MatchesNEVersion(NewExecutable nex)
|
||||
{
|
||||
// TODO: Offset is _not_ the EXE header address, rather where the data starts. Fix this.
|
||||
switch (nex.Model.Stub?.Header?.NewExeHeaderAddr)
|
||||
{
|
||||
case 0x84b0:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x11, ArchiveEnd = -1, InitText = false, FilenamePosition = 0x04, NoCrc = true };
|
||||
|
||||
case 0x3e10:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x1e, ArchiveEnd = -1, InitText = false, FilenamePosition = 0x04, NoCrc = false };
|
||||
|
||||
case 0x3e50:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x1e, ArchiveEnd = -1, InitText = false, FilenamePosition = 0x04, NoCrc = false };
|
||||
|
||||
case 0x3c20:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x1e, ArchiveEnd = -1, InitText = false, FilenamePosition = 0x04, NoCrc = false };
|
||||
|
||||
case 0x3c30:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x22, ArchiveEnd = -1, InitText = false, FilenamePosition = 0x04, NoCrc = false };
|
||||
|
||||
case 0x3660:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x40, ArchiveEnd = 0x3c, InitText = false, FilenamePosition = 0x04, NoCrc = false };
|
||||
|
||||
case 0x36f0:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x48, ArchiveEnd = 0x44, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
case 0x3770:
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
case 0x3780:
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
case 0x37b0:
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
case 0x37d0:
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
case 0x3c80:
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x5a, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
case 0x3bd0:
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x5a, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
case 0x3c10:
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x5a, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks a PE header to see if it matches a known signature
|
||||
/// </summary>
|
||||
/// <param name="pex">Portable executable to check</param>
|
||||
/// <returns>True if it matches a known version, false otherwise</returns>
|
||||
private FormatProperty? GetPEFormat(PortableExecutable pex)
|
||||
{
|
||||
if (pex.OverlayAddress == 0x6e00
|
||||
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3cf4
|
||||
&& pex.GetFirstSection(".data")?.VirtualSize == 0x1528)
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
else if (pex.OverlayAddress == 0x6e00
|
||||
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3cf4
|
||||
&& pex.GetFirstSection(".data")?.VirtualSize == 0x1568)
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
else if (pex.OverlayAddress == 0x6e00
|
||||
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3d54)
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
else if (pex.OverlayAddress == 0x6e00
|
||||
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3d44)
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
else if (pex.OverlayAddress == 0x6e00
|
||||
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3d04)
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
// Found in Binary.WiseCustomCalla
|
||||
else if (pex.OverlayAddress == 0x6200)
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x62, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
else if (pex.OverlayAddress == 0x3000)
|
||||
return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
else if (pex.OverlayAddress == 0x3800)
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x5a, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
else if (pex.OverlayAddress == 0x3a00)
|
||||
return new FormatProperty { Dll = true, ArchiveStart = 0x5a, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
private string? ExtractNewExecutable(NewExecutable nex, string file, bool includeDebug)
|
||||
{
|
||||
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>
|
||||
private string? ExtractPortableExecutable(PortableExecutable pex, string file, bool includeDebug)
|
||||
{
|
||||
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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
49
BinaryObjectScanner/Packer/dotFuscator.cs
Normal file
49
BinaryObjectScanner/Packer/dotFuscator.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .text section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".text");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("DotfuscatorAttribute")))
|
||||
return "dotFuscator";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? Extract(Stream? stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
107
BinaryObjectScanner/Progress.cs
Normal file
107
BinaryObjectScanner/Progress.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
#if NET20 || NET35 || NET40
|
||||
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an IProgress{T} that invokes callbacks for each reported progress value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Specifies the type of the progress report value.</typeparam>
|
||||
/// <remarks>
|
||||
/// Any handler provided to the constructor or event handlers registered with
|
||||
/// the <see cref="ProgressChanged"/> event are invoked through a
|
||||
/// <see cref="SynchronizationContext"/> instance captured
|
||||
/// when the instance is constructed. If there is no current SynchronizationContext
|
||||
/// at the time of construction, the callbacks will be invoked on the ThreadPool.
|
||||
/// </remarks>
|
||||
/// <see href="https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Progress.cs"/>
|
||||
public class Progress<T> : IProgress<T> where T : EventArgs
|
||||
{
|
||||
/// <summary>The synchronization context captured upon construction. This will never be null.</summary>
|
||||
private readonly SynchronizationContext _synchronizationContext;
|
||||
/// <summary>The handler specified to the constructor. This may be null.</summary>
|
||||
private readonly Action<T>? _handler;
|
||||
/// <summary>A cached delegate used to post invocation to the synchronization context.</summary>
|
||||
private readonly SendOrPostCallback _invokeHandlers;
|
||||
|
||||
/// <summary>Initializes the <see cref="Progress{T}"/>.</summary>
|
||||
public Progress()
|
||||
{
|
||||
// Capture the current synchronization context.
|
||||
// If there is no current context, we use a default instance targeting the ThreadPool.
|
||||
_synchronizationContext = SynchronizationContext.Current ?? ProgressStatics.DefaultContext;
|
||||
Debug.Assert(_synchronizationContext != null);
|
||||
_invokeHandlers = new SendOrPostCallback(InvokeHandlers);
|
||||
}
|
||||
|
||||
/// <summary>Initializes the <see cref="Progress{T}"/> with the specified callback.</summary>
|
||||
/// <param name="handler">
|
||||
/// A handler to invoke for each reported progress value. This handler will be invoked
|
||||
/// in addition to any delegates registered with the <see cref="ProgressChanged"/> event.
|
||||
/// Depending on the <see cref="SynchronizationContext"/> instance captured by
|
||||
/// the <see cref="Progress{T}"/> at construction, it's possible that this handler instance
|
||||
/// could be invoked concurrently with itself.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">The <paramref name="handler"/> is null (<see langword="Nothing" /> in Visual Basic).</exception>
|
||||
public Progress(Action<T> handler) : this()
|
||||
{
|
||||
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
/// <summary>Raised for each reported progress value.</summary>
|
||||
/// <remarks>
|
||||
/// Handlers registered with this event will be invoked on the
|
||||
/// <see cref="SynchronizationContext"/> captured when the instance was constructed.
|
||||
/// </remarks>
|
||||
public event EventHandler<T>? ProgressChanged;
|
||||
|
||||
/// <summary>Reports a progress change.</summary>
|
||||
/// <param name="value">The value of the updated progress.</param>
|
||||
protected virtual void OnReport(T value)
|
||||
{
|
||||
// If there's no handler, don't bother going through the sync context.
|
||||
// Inside the callback, we'll need to check again, in case
|
||||
// an event handler is removed between now and then.
|
||||
Action<T>? handler = _handler;
|
||||
EventHandler<T>? changedEvent = ProgressChanged;
|
||||
if (handler != null || changedEvent != null)
|
||||
{
|
||||
// Post the processing to the sync context.
|
||||
// (If T is a value type, it will get boxed here.)
|
||||
_synchronizationContext.Post(_invokeHandlers, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Reports a progress change.</summary>
|
||||
/// <param name="value">The value of the updated progress.</param>
|
||||
void IProgress<T>.Report(T value) { OnReport(value); }
|
||||
|
||||
/// <summary>Invokes the action and event callbacks.</summary>
|
||||
/// <param name="state">The progress value.</param>
|
||||
private void InvokeHandlers(object? state)
|
||||
{
|
||||
T value = (T)state!;
|
||||
|
||||
Action<T>? handler = _handler;
|
||||
EventHandler<T>? changedEvent = ProgressChanged;
|
||||
|
||||
handler?.Invoke(value);
|
||||
changedEvent?.Invoke(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Holds static values for <see cref="Progress{T}"/>.</summary>
|
||||
/// <remarks>This avoids one static instance per type T.</remarks>
|
||||
internal static class ProgressStatics
|
||||
{
|
||||
/// <summary>A default synchronization context that targets the ThreadPool.</summary>
|
||||
internal static readonly SynchronizationContext DefaultContext = new();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
120
BinaryObjectScanner/Protection/ActiveMARK.cs
Normal file
120
BinaryObjectScanner/Protection/ActiveMARK.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
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/>
|
||||
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
{
|
||||
// TODO: Obtain a sample to find where this string is in a typical executable
|
||||
if (includeDebug)
|
||||
{
|
||||
var contentMatchSets = new List<ContentMatchSet>
|
||||
{
|
||||
// " " + (char)0xC2 + (char)0x16 + (char)0x00 + (char)0xA8 + (char)0xC1 + (char)0x16 + (char)0x00 + (char)0xB8 + (char)0xC1 + (char)0x16 + (char)0x00 + (char)0x86 + (char)0xC8 + (char)0x16 + (char)0x00 + (char)0x9A + (char)0xC1 + (char)0x16 + (char)0x00 + (char)0x10 + (char)0xC2 + (char)0x16 + (char)0x00
|
||||
new(new byte?[]
|
||||
{
|
||||
0x20, 0xC2, 0x16, 0x00, 0xA8, 0xC1, 0x16, 0x00,
|
||||
0xB8, 0xC1, 0x16, 0x00, 0x86, 0xC8, 0x16, 0x00,
|
||||
0x9A, 0xC1, 0x16, 0x00, 0x10, 0xC2, 0x16, 0x00
|
||||
}, "ActiveMARK 5 (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchSets, includeDebug);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the entry point data, if it exists
|
||||
if (pex.EntryPointData != null)
|
||||
{
|
||||
// Found in "Zuma.exe"
|
||||
if (pex.EntryPointData.StartsWith(new byte?[] { 0x89, 0x25, 0x04, 0xF0, 0x86, 0x00, 0x68, 0x30 }))
|
||||
return "ActiveMark v5.3.1078 (Packer Version)";
|
||||
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
else if (pex.EntryPointData.StartsWith(new byte?[] { 0x89, 0x25, null, null, null, null, 0xEB }))
|
||||
return "ActiveMark -> Trymedia Systems Inc. (Unconfirmed - Please report to us on Github)";
|
||||
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
else if (pex.EntryPointData.StartsWith(new byte?[] { 0x89, 0x25, null, null, null, null, 0x33, 0xED, 0x55, 0x8B, 0xEC, 0xE8, null, null, null, null, 0x8B, 0xD0, 0x81, 0xE2, 0xFF, 0x00, 0x00, 0x00, 0x89, 0x15, null, null, null, null, 0x8B, 0xD0, 0xC1, 0xEA, 0x08, 0x81, 0xE2, 0xFF, 0x00, 0x00, 0x00, 0xA3, null, null, null, null, 0xD1, 0xE0, 0x0F, 0x93, 0xC3, 0x33, 0xC0, 0x8A, 0xC3, 0xA3, null, null, null, null, 0x68, 0xFF, 0x00, 0x00, 0x00, 0xE8, null, null, null, null, 0x6A, 0x00, 0xE8, null, null, null, null, 0xA3, null, null, null, null, 0xBB, null, null, null, null, 0xC7, 0x03, 0x44, 0x00, 0x00, 0x00 }))
|
||||
return "ActiveMark -> Trymedia Systems Inc. (Unconfirmed - Please report to us on Github)";
|
||||
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
else if (pex.EntryPointData.StartsWith(new byte?[] { 0x20, 0x2D, 0x2D, 0x4D, 0x50, 0x52, 0x4D, 0x4D, 0x47, 0x56, 0x41, 0x2D, 0x2D, 0x00, 0x75, 0x73, 0x65, 0x72, 0x33, 0x32, 0x2E, 0x64, 0x6C, 0x6C, 0x00, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x6F, 0x78, 0x41, 0x00, 0x54, 0x68, 0x69, 0x73, 0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x6E, 0x20, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67 }))
|
||||
return "ActiveMARK 5.x -> Trymedia Systems Inc. (h) (Unconfirmed - Please report to us on Github)";
|
||||
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
else if (pex.EntryPointData.StartsWith(new byte?[] { 0x20, 0x2D, 0x2D, 0x4D, 0x50, 0x52, 0x4D, 0x4D, 0x47, 0x56, 0x41, 0x2D, 0x2D, 0x00, 0x75, 0x73, 0x65, 0x72, 0x33, 0x32, 0x2E, 0x64, 0x6C, 0x6C, 0x00, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x6F, 0x78, 0x41, 0x00, 0x54, 0x68, 0x69, 0x73, 0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x6E, 0x20, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x65, 0x72, 0x20, 0x69, 0x6E, 0x20, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x2E, 0x0D, 0x0A, 0x50, 0x6C, 0x65, 0x61, 0x73, 0x65, 0x20, 0x75, 0x6E, 0x6C, 0x6F, 0x61, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x65, 0x72, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2E, 0x00, 0x57, 0x61, 0x72, 0x6E, 0x69, 0x6E, 0x67 }))
|
||||
return "ActiveMARK 5.x -> Trymedia Systems,Inc. (Unconfirmed - Please report to us on Github)";
|
||||
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
else if (pex.EntryPointData.StartsWith(new byte?[] { 0x79, 0x11, 0x7F, 0xAB, 0x9A, 0x4A, 0x83, 0xB5, 0xC9, 0x6B, 0x1A, 0x48, 0xF9, 0x27, 0xB4, 0x25 }))
|
||||
return "ActiveMARK[TM] (Unconfirmed - Please report to us on Github)";
|
||||
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
else if (pex.EntryPointData.StartsWith(new byte?[] { 0x79, 0x07, 0x0F, 0xB7, 0x07, 0x47, 0x50, 0x47, 0xB9, 0x57, 0x48, 0xF2, 0xAE, 0x55, 0xFF, 0x96, 0x84, null, 0x00, 0x00, 0x09, 0xC0, 0x74, 0x07, 0x89, 0x03, 0x83, 0xC3, 0x04, 0xEB, 0xD8, 0xFF, 0x96, 0x88, null, 0x00, 0x00, 0x61, 0xE9, null, null, null, 0xFF }))
|
||||
return "ActiveMARK[TM] R5.31.1140 -> Trymedia (Unconfirmed - Please report to us on Github)";
|
||||
|
||||
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
|
||||
else if (pex.EntryPointData.StartsWith(new byte?[] { 0xBE, 0x48, 0x01, 0x40, 0x00, 0xAD, 0x8B, 0xF8, 0x95, 0xA5, 0x33, 0xC0, 0x33, 0xC9, 0xAB, 0x48, 0xAB, 0xF7, 0xD8, 0xB1, 0x04, 0xF3, 0xAB, 0xC1, 0xE0, 0x0A, 0xB5, 0x1C, 0xF3, 0xAB, 0xAD, 0x50, 0x97, 0x51, 0xAD, 0x87, 0xF5, 0x58, 0x8D, 0x54, 0x86, 0x5C, 0xFF, 0xD5, 0x72, 0x5A, 0x2C, 0x03, 0x73, 0x02, 0xB0, 0x00, 0x3C, 0x07, 0x72, 0x02, 0x2C, 0x03, 0x50, 0x0F, 0xB6, 0x5F, 0xFF, 0xC1, 0xE3, 0x03, 0xB3, 0x00, 0x8D, 0x1C, 0x5B, 0x8D, 0x9C, 0x9E, 0x0C, 0x10, 0x00, 0x00, 0xB0, 0x01, 0x67, 0xE3, 0x29, 0x8B, 0xD7, 0x2B, 0x56, 0x0C, 0x8A, 0x2A, 0x33, 0xD2, 0x84, 0xE9, 0x0F, 0x95, 0xC6, 0x52, 0xFE, 0xC6, 0x8A, 0xD0, 0x8D, 0x14, 0x93, 0xFF, 0xD5, 0x5A, 0x9F, 0x12, 0xC0, 0xD0, 0xE9, 0x74, 0x0E, 0x9E, 0x1A, 0xF2, 0x74, 0xE4, 0xB4, 0x00, 0x33, 0xC9, 0xB5, 0x01, 0xFF, 0x55, 0xCC, 0x33, 0xC9, 0xE9, 0xDF, 0x00, 0x00, 0x00, 0x8B, 0x5E, 0x0C, 0x83, 0xC2, 0x30, 0xFF, 0xD5, 0x73, 0x50, 0x83, 0xC2, 0x30, 0xFF, 0xD5, 0x72, 0x1B, 0x83, 0xC2, 0x30, 0xFF, 0xD5, 0x72, 0x2B, 0x3C, 0x07, 0xB0, 0x09, 0x72, 0x02, 0xB0, 0x0B, 0x50, 0x8B, 0xC7, 0x2B, 0x46, 0x0C, 0xB1, 0x80, 0x8A, 0x00, 0xEB, 0xCF, 0x83, 0xC2, 0x60, 0xFF, 0xD5, 0x87, 0x5E, 0x10, 0x73, 0x0D, 0x83, 0xC2, 0x30, 0xFF, 0xD5, 0x87, 0x5E, 0x14, 0x73, 0x03, 0x87, 0x5E, 0x18, 0x3C, 0x07, 0xB0, 0x08, 0x72, 0x02, 0xB0, 0x0B, 0x50, 0x53, 0x8D, 0x96, 0x7C, 0x07, 0x00, 0x00, 0xFF, 0x55, 0xD0, 0x5B, 0x91, 0xEB, 0x77, 0x3C, 0x07, 0xB0, 0x07, 0x72, 0x02, 0xB0, 0x0A, 0x50, 0x87, 0x5E, 0x10, 0x87, 0x5E, 0x14, 0x89, 0x5E, 0x18, 0x8D, 0x96, 0xC4, 0x0B, 0x00, 0x00, 0xFF, 0x55, 0xD0, 0x50, 0x48 }))
|
||||
return "ActiveMARK 5.x -> Trymedia Systems,Inc. (h) (Unconfirmed - Please report to us on Github)";
|
||||
}
|
||||
|
||||
// Get the .data section strings, if they exist
|
||||
var strs = pex.GetLastSectionStrings(".data");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("MPRMMGVA"))
|
||||
&& strs.Any(s => s.Contains("This application cannot run with an active debugger in memory.")))
|
||||
{
|
||||
return "ActiveMARK 6.x";
|
||||
}
|
||||
}
|
||||
|
||||
// Get "REGISTRY, AMINTERNETPROTOCOL" resource items
|
||||
var resources = pex.FindResourceByNamedType("REGISTRY, AMINTERNETPROTOCOL");
|
||||
if (resources.Any())
|
||||
{
|
||||
bool match = resources
|
||||
.Select(r => r == null ? string.Empty : Encoding.ASCII.GetString(r))
|
||||
.Any(r => r.Contains("ActiveMARK"));
|
||||
if (match)
|
||||
return "ActiveMARK";
|
||||
}
|
||||
|
||||
// Get the overlay data, if it exists
|
||||
if (pex.OverlayStrings != null)
|
||||
{
|
||||
if (pex.OverlayStrings.Any(s => s.Contains("TMSAMVOH")))
|
||||
return "ActiveMARK";
|
||||
}
|
||||
|
||||
// Get the last .bss section strings, if they exist
|
||||
strs = pex.GetLastSectionStrings(".bss");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("TMSAMVOF")))
|
||||
return "ActiveMARK";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
114
BinaryObjectScanner/Protection/AegiSoft.cs
Normal file
114
BinaryObjectScanner/Protection/AegiSoft.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// 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(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.IsNullOrEmpty(match))
|
||||
return match;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET20 || NET35
|
||||
public Queue<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(new FilePathMatch("AscLM.cpl"), "AegiSoft License Manager"),
|
||||
new(new FilePathMatch("AscLM.vxd"), "AegiSoft License Manager"),
|
||||
new(new FilePathMatch("AscLMd.vxd"), "AegiSoft License Manager"),
|
||||
|
||||
// There are a few other files present, but the file names on their own may be too overmatching. Due to the small sample size, it's not sure if these files are always present together.
|
||||
// These files are "Asc001.dll", "Asc002.dll", "Asc003.dll", "Asc005.dll", and "Asc006.exe" (Found in Redump entry 73521/IA item "Nova_HoyleCasino99USA").
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Found in Redump entry 73521/IA item "Nova_HoyleCasino99USA".
|
||||
new(new FilePathMatch("AscLM.cpl"), "AegiSoft License Manager"),
|
||||
new(new FilePathMatch("AscLM.vxd"), "AegiSoft License Manager"),
|
||||
new(new FilePathMatch("AscLMd.vxd"), "AegiSoft License Manager"),
|
||||
|
||||
// There are a few other files present, but the file names on their own may be too overmatching. Due to the small sample size, it's not sure if these files are always present together.
|
||||
// These files are "Asc001.dll", "Asc002.dll", "Asc003.dll", "Asc005.dll", and "Asc006.exe" (Found in Redump entry 73521/IA item "Nova_HoyleCasino99USA").
|
||||
|
||||
// The "DATA.TAG" file in the "AgeiSoft" folder is an INI that includes:
|
||||
// Company=AegiSoft Corporation
|
||||
// Application=AegiSoft License Manager
|
||||
// Version=2.1
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
46
BinaryObjectScanner/Protection/AlphaDVD.cs
Normal file
46
BinaryObjectScanner/Protection/AlphaDVD.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
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 NET20 || NET35
|
||||
public Queue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new(new FilePathMatch("PlayDVD.exe"), "Alpha-DVD (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new(new FilePathMatch("PlayDVD.exe"), "Alpha-DVD (Unconfirmed - Please report to us on Github"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
87
BinaryObjectScanner/Protection/AlphaROM.cs
Normal file
87
BinaryObjectScanner/Protection/AlphaROM.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// TODO: Add support for detecting Alpha-ROM found in older games made with the RealLive engine.
|
||||
// TODO: Add version detection for Alpha-ROM.
|
||||
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("\\SETTEC")))
|
||||
return "Alpha-ROM";
|
||||
|
||||
if (strs.Any(s => s.Contains("SETTEC0000")))
|
||||
return "Alpha-ROM";
|
||||
}
|
||||
|
||||
// Get the .rdata section strings, if they exist
|
||||
strs = pex.GetFirstSectionStrings(".rdata");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("This Game is Japan Only")))
|
||||
return "Alpha-ROM";
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
61
BinaryObjectScanner/Protection/Bitpool.cs
Normal file
61
BinaryObjectScanner/Protection/Bitpool.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
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 NET20 || NET35
|
||||
public Queue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new(new FilePathMatch("bitpool.rsc"), "Bitpool"),
|
||||
new(new FilePathMatch("CD.IDX"), "Bitpool"),
|
||||
|
||||
// Completely empty file present on multiple discs with Bitpool (Redump entries 52626 and 50229).
|
||||
new(new FilePathMatch("LEADOUT.OFS"), "Bitpool"),
|
||||
|
||||
// A set of 4 identically sized (within the same game, not between games), corrupted/padded files present in several games (Redump entries 31782 and 35476).
|
||||
// Both examples with only having the first letter uppercase and as the whole file name being uppercase have been seen.
|
||||
new(new List<PathMatch>
|
||||
{
|
||||
new FilePathMatch("Crc_a"),
|
||||
new FilePathMatch("Crc_b"),
|
||||
new FilePathMatch("Crc_c"),
|
||||
new FilePathMatch("Crc_d"),
|
||||
}, "Bitpool"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new(new FilePathMatch("bitpool.rsc"), "Bitpool"),
|
||||
new(new FilePathMatch("CD.IDX"), "Bitpool"),
|
||||
|
||||
// Completely empty file present on multiple discs with Bitpool (Redump entries 52626 and 50229).
|
||||
new(new FilePathMatch("LEADOUT.OFS"), "Bitpool"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
166
BinaryObjectScanner/Protection/ByteShield.cs
Normal file
166
BinaryObjectScanner/Protection/ByteShield.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
var name = pex.FileDescription;
|
||||
if (name?.Equals("ByteShield Client") == true)
|
||||
return $"ByteShield Activation Client {pex.GetInternalVersion()}";
|
||||
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
name = pex.InternalName;
|
||||
if (name?.Equals("ByteShield") == true)
|
||||
return $"ByteShield Activation Client {pex.GetInternalVersion()}";
|
||||
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
name = pex.OriginalFilename;
|
||||
if (name?.Equals("ByteShield.EXE") == true)
|
||||
return $"ByteShield Activation Client {pex.GetInternalVersion()}";
|
||||
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
name = pex.ProductName;
|
||||
if (name?.Equals("ByteShield Client") == true)
|
||||
return $"ByteShield Activation Client {pex.GetInternalVersion()}";
|
||||
|
||||
// Found in "ByteShield.dll" in Redump entry 6236
|
||||
name = pex.Model.ExportTable?.ExportDirectoryTable?.Name;
|
||||
if (name?.Equals("ByteShield Client") == true)
|
||||
return "ByteShield Component Module";
|
||||
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
var stMatch = pex.FindStringTableByEntry("ByteShield");
|
||||
if (stMatch.Any())
|
||||
return $"ByteShield Activation Client {pex.GetInternalVersion()}";
|
||||
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
var dbMatch = pex.FindDialogByTitle("About ByteShield");
|
||||
if (dbMatch.Any())
|
||||
return "ByteShield";
|
||||
|
||||
// TODO: See if the version number is anywhere else
|
||||
// TODO: Parse the version number out of the dialog box item
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
dbMatch = pex.FindDialogBoxByItemTitle("ByteShield Version 1.0");
|
||||
if (dbMatch.Any())
|
||||
return "ByteShield";
|
||||
|
||||
// Get the .data/DATA section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
|
||||
if (strs != null)
|
||||
{
|
||||
// Found in "LineRider2.exe" in Redump entry 6236
|
||||
if (strs.Any(s => s?.Contains("ByteShield") == true))
|
||||
return "ByteShield";
|
||||
}
|
||||
|
||||
// Get the .rdata section strings, if they exist
|
||||
strs = pex.GetFirstSectionStrings(".rdata");
|
||||
if (strs != null)
|
||||
{
|
||||
// Found in "ByteShield.dll" in Redump entry 6236
|
||||
if (strs.Any(s => s?.Contains("Byte|Shield") == true))
|
||||
return "ByteShield Component Module";
|
||||
|
||||
// Found in "ByteShield.dll" in Redump entry 6236
|
||||
else if (strs.Any(s => s?.Contains("Byteshield0") == true))
|
||||
return "ByteShield Component Module";
|
||||
|
||||
// Found in "ByteShield.dll" in Redump entry 6236
|
||||
else if (strs.Any(s => s?.Contains("ByteShieldLoader") == true))
|
||||
return "ByteShield Component Module";
|
||||
}
|
||||
|
||||
// Get the .ret section strings, if they exist
|
||||
strs = pex.GetFirstSectionStrings(".ret");
|
||||
if (strs != null)
|
||||
{
|
||||
// TODO: Figure out if this specifically indicates if the file is encrypted
|
||||
// Found in "LineRider2.bbz" in Redump entry 6236
|
||||
if (strs.Any(s => s?.Contains("ByteShield") == true))
|
||||
return "ByteShield";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET20 || NET35
|
||||
public Queue<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(new FilePathMatch("Byteshield.dll"), "ByteShield Component Module"),
|
||||
new(new FilePathMatch("Byteshield.ini"), "ByteShield"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
// TODO: Investigate reference to "bbz650.tmp" in "Byteshield.dll" (Redump entry 6236)
|
||||
// Files with the ".bbz" extension are associated with ByteShield, but the extenstion is known to be used in other places as well.
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new(new FilePathMatch("Byteshield.dll"), "ByteShield Component Module"),
|
||||
new(new FilePathMatch("Byteshield.ini"), "ByteShield"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
BinaryObjectScanner/Protection/CDCheck.cs
Normal file
33
BinaryObjectScanner/Protection/CDCheck.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class CDCheck : IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var name = pex.Comments;
|
||||
if (name?.Contains("CDCheck utlity for Microsoft Game Studios") == true)
|
||||
return "Microsoft Game Studios CD Check";
|
||||
|
||||
// To broad to be of use
|
||||
//name = pex.InternalName;
|
||||
//if (name?.Contains("CDCheck") == true)
|
||||
// return "Microsoft Game Studios CD Check";
|
||||
|
||||
// To broad to be of use
|
||||
//name = pex.OriginalFilename;
|
||||
//if (name?.Contains("CDCheck.exe") == true)
|
||||
// return "Microsoft Game Studios CD Check";
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
250
BinaryObjectScanner/Protection/CDDVDCops.cs
Normal file
250
BinaryObjectScanner/Protection/CDDVDCops.cs
Normal file
@@ -0,0 +1,250 @@
|
||||
using System;
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
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/>
|
||||
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
{
|
||||
// TODO: Obtain a sample to find where this string is in a typical executable
|
||||
if (includeDebug)
|
||||
{
|
||||
var contentMatchSets = new List<ContentMatchSet>
|
||||
{
|
||||
// TODO: Remove from here once it's confirmed that no PE executables contain this string
|
||||
// CD-Cops, ver.
|
||||
new(new byte?[]
|
||||
{
|
||||
0x43, 0x44, 0x2D, 0x43, 0x6F, 0x70, 0x73, 0x2C,
|
||||
0x20, 0x20, 0x76, 0x65, 0x72, 0x2E, 0x20
|
||||
}, GetVersion, "CD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
|
||||
// // DVD-Cops, ver.
|
||||
new(new byte?[]
|
||||
{
|
||||
0x44, 0x56, 0x44, 0x2D, 0x43, 0x6F, 0x70, 0x73,
|
||||
0x2C, 0x20, 0x20, 0x76, 0x65, 0x72, 0x2E, 0x20
|
||||
}, GetVersion, "DVD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchSets, includeDebug);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
|
||||
{
|
||||
// TODO: Don't read entire file
|
||||
var data = nex.ReadArbitraryRange();
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
// TODO: Figure out what NE section this lives in
|
||||
var neMatchSets = new List<ContentMatchSet>
|
||||
{
|
||||
// CD-Cops, ver.
|
||||
// Found in "h3blade.exe" in Redump entry 85077.
|
||||
new(new byte?[]
|
||||
{
|
||||
0x43, 0x44, 0x2D, 0x43, 0x6F, 0x70, 0x73, 0x2C,
|
||||
0x20, 0x20, 0x76, 0x65, 0x72, 0x2E, 0x20
|
||||
}, GetVersion, "CD-Cops"),
|
||||
};
|
||||
|
||||
var match = MatchUtil.GetFirstMatch(file, data, neMatchSets, includeDebug);
|
||||
if (!string.IsNullOrEmpty(match))
|
||||
return match;
|
||||
|
||||
// Check the imported-name table
|
||||
// Found in "h3blade.exe" in Redump entry 85077.
|
||||
bool importedNameTableEntries = nex.Model.ImportedNameTable?
|
||||
.Select(kvp => kvp.Value)
|
||||
.Select(inte => inte?.NameString == null ? string.Empty : Encoding.ASCII.GetString(inte.NameString))
|
||||
.Any(s => s.Contains("CDCOPS")) ?? false;
|
||||
if (importedNameTableEntries)
|
||||
return "CD-Cops";
|
||||
|
||||
// Check the nonresident-name table
|
||||
// Found in "CDCOPS.DLL" in Redump entry 85077.
|
||||
bool nonresidentNameTableEntries = nex.Model.NonResidentNameTable?
|
||||
.Select(nrnte => nrnte?.NameString == null ? string.Empty : Encoding.ASCII.GetString(nrnte.NameString))
|
||||
.Any(s => s.Contains("CDcops assembly-language DLL")) ?? false;
|
||||
if (nonresidentNameTableEntries)
|
||||
return "CD-Cops";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the stub executable data, if it exists
|
||||
if (pex.StubExecutableData != null)
|
||||
{
|
||||
var matchers = new List<ContentMatchSet>
|
||||
{
|
||||
// WEBCOPS
|
||||
// Found in "HyperBowl.C_S" in https://web.archive.org/web/20120616074941/http://icm.games.tucows.com/files2/HyperDemo-109a.exe.
|
||||
new(new byte?[]
|
||||
{
|
||||
0x57, 0x45, 0x42, 0x43, 0x4F, 0x50, 0x53
|
||||
}, "WEB-Cops")
|
||||
};
|
||||
|
||||
var match = MatchUtil.GetFirstMatch(file, pex.StubExecutableData, matchers, includeDebug);
|
||||
if (!string.IsNullOrEmpty(match))
|
||||
return match;
|
||||
}
|
||||
|
||||
// Get the .grand section, if it exists
|
||||
// Found in "AGENTHUG.QZ_" in Redump entry 84517 and "h3blade.QZ_" in Redump entry 85077.
|
||||
bool grandSection = pex.ContainsSection(".grand", exact: true);
|
||||
if (grandSection)
|
||||
return "CD/DVD/WEB-Cops";
|
||||
|
||||
// Get the UNICops section, if it exists
|
||||
// Found in "FGP.exe" in IA item "flaklypa-grand-prix-dvd"/Redump entry 108169.
|
||||
bool UNICopsSection = pex.ContainsSection("UNICops", exact: true);
|
||||
if (UNICopsSection)
|
||||
return "UNI-Cops";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET20 || NET35
|
||||
public Queue<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(new PathMatch("CDCOPS.DLL", useEndsWith: true), "CD-Cops"),
|
||||
new(new PathMatch(".W_X", matchExact: true, useEndsWith: true), "CD/DVD-Cops"),
|
||||
new(new PathMatch(".QZ_", matchExact: true, useEndsWith: true), "CD/DVD-Cops"),
|
||||
|
||||
new(new PathMatch(".GZ_", matchExact: true, useEndsWith: true), "CD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
new(new PathMatch(".Qz", matchExact: true, useEndsWith: true), "CD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// A 400+ MB file called "WASTE.DAT" that is fully 00 padded can be found in IA item "Faculty_Edition_People_Problems_and_Power_by_Joseph_Unekis_Textbytes".
|
||||
// Presumably used to increase the amount of data written to the disc to allow DPM checking to be used for the protection. It's unknown if this file is used on any other protected discs.
|
||||
|
||||
// Found in Redump entry 84517.
|
||||
new(new PathMatch("CDCOPS.DLL", useEndsWith: true), "CD-Cops"),
|
||||
new(new PathMatch(".W_X", matchExact: true, useEndsWith: true), "CD/DVD-Cops"),
|
||||
new(new PathMatch(".QZ_", matchExact: true, useEndsWith: true), "CD/DVD-Cops"),
|
||||
|
||||
new(new PathMatch(".GZ_", matchExact: true, useEndsWith: true), "CD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
new(new PathMatch(".Qz", matchExact: true, useEndsWith: true), "CD-Cops (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
|
||||
public static string? GetVersion(string file, byte[]? fileContent, List<int> positions)
|
||||
{
|
||||
// If we have no content
|
||||
if (fileContent == null)
|
||||
return null;
|
||||
|
||||
#if NET20 || NET35 || NET40
|
||||
byte[] versionBytes = new byte[4];
|
||||
Array.Copy(fileContent, positions[0] + 15, versionBytes, 0, 4);
|
||||
char[] version = versionBytes.Select(b => (char)b).ToArray();
|
||||
#else
|
||||
char[] version = new ArraySegment<byte>(fileContent, positions[0] + 15, 4).Select(b => (char)b).ToArray();
|
||||
#endif
|
||||
if (version[0] == 0x00)
|
||||
return string.Empty;
|
||||
|
||||
return new string(version);
|
||||
}
|
||||
}
|
||||
}
|
||||
87
BinaryObjectScanner/Protection/CDGuard.cs
Normal file
87
BinaryObjectScanner/Protection/CDGuard.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// TODO: Investigate the numerous ".guard" sections present in "Randevu.exe" in Redump entry 97142.
|
||||
|
||||
// Get the export directory table
|
||||
if (pex.Model.ExportTable?.ExportDirectoryTable != null)
|
||||
{
|
||||
// Found in "cdguard.dll" in Redump entry 97142 and IA item "pahgeby-he3hakomkou".
|
||||
bool match = pex.Model.ExportTable.ExportDirectoryTable.Name?.Equals("cdguard.dll", StringComparison.OrdinalIgnoreCase) == true;
|
||||
if (match)
|
||||
return "CD-Guard Copy Protection System";
|
||||
}
|
||||
|
||||
// Get the import directory table
|
||||
if (pex.Model.ImportTable?.ImportDirectoryTable != null)
|
||||
{
|
||||
// Found in "Randevu.exe" in Redump entry 97142.
|
||||
bool match = pex.Model.ImportTable.ImportDirectoryTable.Any(idte => idte?.Name != null && idte.Name.Equals("cdguard.dll", StringComparison.OrdinalIgnoreCase));
|
||||
if (match)
|
||||
return "CD-Guard Copy Protection System";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET20 || NET35
|
||||
public Queue<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(new FilePathMatch("cdguard.dll"), "CD-Guard Copy Protection System"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Found in Redump entry 97142.
|
||||
new(new FilePathMatch("cdguard.dll"), "CD-Guard Copy Protection System"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
BinaryObjectScanner/Protection/CDKey.cs
Normal file
24
BinaryObjectScanner/Protection/CDKey.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class CDKey : IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
var name = pex.InternalName;
|
||||
if (name?.Equals("CDKey", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return "CD-Key / Serial";
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
101
BinaryObjectScanner/Protection/CDLock.cs
Normal file
101
BinaryObjectScanner/Protection/CDLock.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the .data/DATA section, 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(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.IsNullOrEmpty(match))
|
||||
return match;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET20 || NET35
|
||||
public Queue<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(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, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// TODO: Determine if there's any consistency in the naming of the additional AFP files.
|
||||
|
||||
// Found in every confirmed sample of CD-Lock, generally (but not always) appears to include markers relating to the additional AFP files present (Redump entries 24287 and 31615).
|
||||
new(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
69
BinaryObjectScanner/Protection/CDProtector.cs
Normal file
69
BinaryObjectScanner/Protection/CDProtector.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
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 NET20 || NET35
|
||||
public Queue<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(new FilePathMatch("_cdp16.dat"), "CD-Protector"),
|
||||
new(new FilePathMatch("_cdp16.dll"), "CD-Protector"),
|
||||
new(new FilePathMatch("_cdp32.dat"), "CD-Protector"),
|
||||
new(new FilePathMatch("_cdp32.dll"), "CD-Protector"),
|
||||
|
||||
// This is the "Phantom Trax" file generated by CD-Protector, intended to be burned to a protected CD as an audio track.
|
||||
new(new FilePathMatch("Track#1 - Track#2 Cd-Protector.wav"), "CD-Protector"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// These are the main files used by CD-Protector, which should all be present in every protected disc.
|
||||
// "_cdp16.dll" and "_cdp32.dll" are actually renamed WAV files.
|
||||
// "_cdp32.dat" is actually an archive that contains the original executable.
|
||||
// Another EXE is created, with the name of the original executable. I'm not sure what this executable does, but it appears to be compressed with NeoLite.
|
||||
// TODO: Invesitage if this EXE itself can be detected in any way.
|
||||
new(new FilePathMatch("_cdp16.dat"), "CD-Protector"),
|
||||
new(new FilePathMatch("_cdp16.dll"), "CD-Protector"),
|
||||
new(new FilePathMatch("_cdp32.dat"), "CD-Protector"),
|
||||
new(new FilePathMatch("_cdp32.dll"), "CD-Protector"),
|
||||
|
||||
// This is the "Phantom Trax" file generated by CD-Protector, intended to be burned to a protected CD as an audio track.
|
||||
new(new FilePathMatch("Track#1 - Track#2 Cd-Protector.wav"), "CD-Protector"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
BinaryObjectScanner/Protection/CDSHiELDSE.cs
Normal file
38
BinaryObjectScanner/Protection/CDSHiELDSE.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class CDSHiELDSE : IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// TODO: Indicates Hypertech Crack Proof as well?
|
||||
//// Get the import directory table
|
||||
//if (pex.Model.ImportTable?.ImportDirectoryTable != null)
|
||||
//{
|
||||
// bool match = pex.Model.ImportTable.ImportDirectoryTable.Any(idte => idte.Name == "KeRnEl32.dLl");
|
||||
// if (match)
|
||||
// return "CDSHiELD SE";
|
||||
//}
|
||||
|
||||
// Get the code/CODE section strings, if they exist
|
||||
var strs = pex.GetFirstSectionStrings("code") ?? pex.GetFirstSectionStrings("CODE");
|
||||
if (strs != null)
|
||||
{
|
||||
if (strs.Any(s => s.Contains("~0017.tmp")))
|
||||
return "CDSHiELD SE";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
47
BinaryObjectScanner/Protection/CDX.cs
Normal file
47
BinaryObjectScanner/Protection/CDX.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
using System.Collections.Generic;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
// Nothing is currently known about this DRM. One program may possibly have included it, as it has been listed as including these files in the installion directory (https://www.advanceduninstaller.com/Visit-Gallery-2-90896afd3151ed9660dddc23b892863f-application.htm).
|
||||
// Unfortunately, this program and developer are so obscure, I'm not able to find any relevant further information on them whatsoever.
|
||||
// The only source of valuable information currently known is a forum post about a user attempting to crack this DRM (https://forum.p30world.com/showthread.php?t=413264).
|
||||
// Every attachment, upload, or photo from the forum thread are offline and unarchived.
|
||||
public class CDX : IPathCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
#if NET20 || NET35
|
||||
public Queue<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(new FilePathMatch("CHKCDX16.DLL"), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
new(new FilePathMatch("CHKCDX32.DLL"), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
new(new FilePathMatch("CHKCDXNT.DLL"), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new(new FilePathMatch("CHKCDX16.DLL"), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
new(new FilePathMatch("CHKCDX32.DLL"), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
new(new FilePathMatch("CHKCDXNT.DLL"), "CD-X (Unconfirmed - Please report to us on Github)"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
BinaryObjectScanner/Protection/CactusDataShield.cs
Normal file
33
BinaryObjectScanner/Protection/CactusDataShield.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
public class CactusDataShield : IContentCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
|
||||
{
|
||||
// TODO: Limit these checks to Mac binaries
|
||||
// TODO: Obtain a sample to find where this string is in a typical executable
|
||||
if (includeDebug)
|
||||
{
|
||||
var contentMatchSets = new List<ContentMatchSet>
|
||||
{
|
||||
// CDSPlayer
|
||||
new(new byte?[] { 0x43, 0x44, 0x53, 0x50, 0x6C, 0x61, 0x79, 0x65, 0x72 }, "Cactus Data Shield 200"),
|
||||
|
||||
// yucca.cds
|
||||
new(new byte?[] { 0x79, 0x75, 0x63, 0x63, 0x61, 0x2E, 0x63, 0x64, 0x73 }, "Cactus Data Shield 200"),
|
||||
};
|
||||
|
||||
if (contentMatchSets != null && contentMatchSets.Any())
|
||||
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchSets, includeDebug);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
87
BinaryObjectScanner/Protection/CenegaProtectDVD.cs
Normal file
87
BinaryObjectScanner/Protection/CenegaProtectDVD.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Get the 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 NET20 || NET35
|
||||
public Queue<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(new FilePathMatch("cenega.dll"), "Cenega ProtectDVD"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Seems likely to be present in most, if not all discs protected with Cenega ProtectDVD, but unable to confirm due to only having a small sample size.
|
||||
// Found in Redump entry 31422 and IA item "speed-pack".
|
||||
new(new FilePathMatch("cenega.dll"), "Cenega ProtectDVD"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
92
BinaryObjectScanner/Protection/ChosenBytesCodeLock.cs
Normal file
92
BinaryObjectScanner/Protection/ChosenBytesCodeLock.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
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 BinaryObjectScanner 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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Found in "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 NET20 || NET35
|
||||
public Queue<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(new FilePathMatch("Code-Lock.chm"), "ChosenBytes Code-Lock"),
|
||||
new(new FilePathMatch("Code-Lock.DEP"), "ChosenBytes Code-Lock"),
|
||||
new(new FilePathMatch("Code-Lock.ocx"), "ChosenBytes Code-Lock"),
|
||||
new(new FilePathMatch("Code-Lock Wizard.exe"), "ChosenBytes Code-Lock"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
// Found in the installation directory of Code-Lock version 2.35.
|
||||
new(new FilePathMatch("Code-Lock.chm"), "ChosenBytes Code-Lock"),
|
||||
new(new FilePathMatch("Code-Lock.DEP"), "ChosenBytes Code-Lock"),
|
||||
new(new FilePathMatch("Code-Lock.ocx"), "ChosenBytes Code-Lock"),
|
||||
new(new FilePathMatch("Code-Lock Wizard.exe"), "ChosenBytes Code-Lock"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
103
BinaryObjectScanner/Protection/CopyKiller.cs
Normal file
103
BinaryObjectScanner/Protection/CopyKiller.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
using System.Collections.Generic;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// CopyKiller was a program made by WebStylerZone that allowed users to copy-protect their burned discs.
|
||||
/// It worked by having users copy files with byte patterns that would create weak sectors to their discs to burn, and relied on drives with buggy firmwares to create bad burns of the discs.
|
||||
/// This would result in discs having intentional bad sectors, making them harder to copy. There was also an optional autorun available that would check for the original CopyKiller files on the disc.
|
||||
/// <see href="https://github.com/TheRogueArchivist/DRML/blob/main/entries/CopyKiller/CopyKiller.md"/>
|
||||
/// TODO: Add support for the developer's EXE obfuscator, "EXEShield Deluxe". Most, if not all, EXEShield protected files are currently detected as "EXE Stealth" by BOS.
|
||||
/// Samples include CopyKiller (Versions 3.64 & 3.99a) and SafeDiscScanner (Version 0.16) (https://archive.org/details/safediscscanner-0.16-webstylerzone-from-unofficial-source).
|
||||
/// </summary>
|
||||
public class CopyKiller : IPathCheck, IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// TODO: Figure out how to differentiate between V3.99 and V3.99a.
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// TODO: Figure out why this check doesn't work.
|
||||
// Found in "autorun.exe" in CopyKiller V3.64, V3.99, and V3.99a.
|
||||
var name = pex.ProductName;
|
||||
if (name?.StartsWith("CopyKiller", StringComparison.OrdinalIgnoreCase) == true)
|
||||
return "CopyKiller V3.64+";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET20 || NET35
|
||||
public Queue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#else
|
||||
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string>? files)
|
||||
#endif
|
||||
{
|
||||
// Previous versions of BOS noted to look at ".PFF" files as possible indicators of CopyKiller, but those files seem unrelated.
|
||||
// TODO: Figure out why this doesn't work.
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new(new FilePathMatch("CopyKillerV3"), "CopyKiller V3.62-3.64"),
|
||||
new(new FilePathMatch("CopyKillerV4"), "CopyKiller V3.99-3.99a"),
|
||||
|
||||
new(new List<PathMatch>
|
||||
{
|
||||
new FilePathMatch("ACK3900.ckt"),
|
||||
new FilePathMatch("ACK3999.ckt"),
|
||||
new FilePathMatch("CK100.wzc"),
|
||||
new FilePathMatch("CK2500.ck"),
|
||||
new FilePathMatch("CK3600.tcwz"),
|
||||
new FilePathMatch("Engine.wzc"),
|
||||
new FilePathMatch("P261XP.tcck"),
|
||||
new FilePathMatch("WZ200.rwzc"),
|
||||
new FilePathMatch("XCK3900.ck2"),
|
||||
}, "CopyKiller V3.99+"),
|
||||
|
||||
new(new List<PathMatch>
|
||||
{
|
||||
new FilePathMatch("ACK3900.ckt"),
|
||||
new FilePathMatch("CK100.wzc"),
|
||||
new FilePathMatch("CK2500.ck"),
|
||||
new FilePathMatch("CK3600.tcwz"),
|
||||
new FilePathMatch("Engine.wzc"),
|
||||
new FilePathMatch("P261XP.tcck"),
|
||||
new FilePathMatch("WZ200.rwzc"),
|
||||
new FilePathMatch("XCK3900.ck2"),
|
||||
}, "CopyKiller V3.64+"),
|
||||
|
||||
new(new List<PathMatch>
|
||||
{
|
||||
new FilePathMatch("CK100.wzc"),
|
||||
new FilePathMatch("Engine.wzc"),
|
||||
}, "CopyKiller V3.62+"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetAllMatches(files, matchers, any: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? CheckFilePath(string path)
|
||||
{
|
||||
// Previous versions of BOS noted to look at ".PFF" files as possible indicators of CopyKiller, but those files seem unrelated.
|
||||
// TODO: Figure out why this doesn't work.
|
||||
var matchers = new List<PathMatchSet>
|
||||
{
|
||||
new(new FilePathMatch("CopyKillerV3"), "CopyKiller V3.62-3.64"),
|
||||
new(new FilePathMatch("CopyKillerV4"), "CopyKiller V3.99-3.99a"),
|
||||
};
|
||||
|
||||
return MatchUtil.GetFirstMatch(path, matchers, any: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
BinaryObjectScanner/Protection/CopyLok.cs
Normal file
43
BinaryObjectScanner/Protection/CopyLok.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
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 BinaryObjectScanner 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/>
|
||||
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex.Model.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// If there are 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user