Compare commits

...

357 Commits
2.7.0 ... 3.1.7

Author SHA1 Message Date
Matt Nadareski
3936a15ef7 Bump version 2024-04-24 17:08:16 -04:00
Matt Nadareski
f6dbb349c4 Update packages 2024-04-24 17:06:51 -04:00
Matt Nadareski
3c69e02cfc Update SabreTools.Serialization 2024-04-24 16:29:58 -04:00
Matt Nadareski
531e634e62 Version-gate a using statement 2024-04-24 14:43:28 -04:00
Matt Nadareski
f9c0c42b26 One Nuget packing error 2024-04-24 12:13:56 -04:00
Matt Nadareski
83aebbbfbd Maybe the last one? 2024-04-24 12:11:05 -04:00
Matt Nadareski
3847e5e9dc Even more warnings 2024-04-24 11:55:27 -04:00
Matt Nadareski
a72bb7e332 Yet more, plus an XML comment 2024-04-24 11:45:20 -04:00
Matt Nadareski
839791f467 Add a couple more for external 2024-04-24 11:43:09 -04:00
Matt Nadareski
469356e8c1 Suppress some "necessary" warnings 2024-04-24 11:35:09 -04:00
Matt Nadareski
66e8eb985c Bump version 2024-04-24 11:23:33 -04:00
Matt Nadareski
90223e6c94 Handle some warnings and messages 2024-04-24 11:16:03 -04:00
Matt Nadareski
2f2cf76d7b Update SabreTools.Printing 2024-04-24 11:15:51 -04:00
Matt Nadareski
558fee2200 Clean up using statements 2024-04-24 11:03:09 -04:00
Matt Nadareski
a82abc05ec Update packages 2024-04-24 11:01:10 -04:00
Matt Nadareski
74df37597a Slight cleanup to Scanner 2024-04-22 11:34:04 -04:00
Matt Nadareski
1581023c01 Update WiseUnpacker 2024-04-18 13:04:49 -04:00
Matt Nadareski
c0d1260656 Update UnshieldSharp 2024-04-18 12:58:32 -04:00
Matt Nadareski
969d103c2c Update packages 2024-04-18 12:48:44 -04:00
TheRogueArchivist
e5e3f3e3ef Add check for StarForce driver removal tool (#301) 2024-04-17 19:30:57 -04:00
Matt Nadareski
c1ee399262 Usings cleanup 2024-04-17 13:46:38 -04:00
Matt Nadareski
74ee9932a7 Update WrapperFactory a bit 2024-04-17 13:44:33 -04:00
Matt Nadareski
e70f8d7220 Reduce unncessary printing code 2024-04-17 13:41:00 -04:00
Matt Nadareski
ceba351372 Update packages 2024-04-17 13:38:14 -04:00
Matt Nadareski
ad4082c531 Forgot the name for playlist 2024-04-17 12:16:57 -04:00
Matt Nadareski
115ea02822 Update libraries 2024-04-17 12:12:01 -04:00
TheRogueArchivist
f876a4e4a6 Add RealArcade detection (#300) 2024-04-16 19:30:48 -04:00
TheRogueArchivist
be114f60d3 Fix WinZip SFX folders not being scanned (#299)
* Fix WinZip SFX folders not being scanned

Use PKZIP extraction to fix WinZip SFX extraction not extracting folders.

* Remove unneeded null check

* Add checks for incomplete zip entries
2024-04-15 00:18:00 -04:00
Matt Nadareski
b2594f8148 Update WiseUnpacker 2024-04-11 12:33:00 -04:00
TheRogueArchivist
f58ada3dde Fix Steam overmatch (#298) 2024-04-11 12:23:46 -04:00
TheRogueArchivist
bc4f07970d Minor Rainbow Sentinel improvements (#295) 2024-04-07 19:15:30 -04:00
Matt Nadareski
25d6822283 Bump version 2024-04-05 15:42:43 -04:00
Silent
3b22262c21 Update SecuROM v7 detection to correctly handle a partly stripped header (#297) 2024-04-04 15:00:09 -04:00
Matt Nadareski
314fc1e3fc Update SabreTools.Printing 2024-04-04 13:48:00 -04:00
Matt Nadareski
5742749dec Update packages 2024-04-04 12:12:52 -04:00
Silent
c55fffeb7b Fix a crash when a sharing violation occurs during --info (#296)
Prints an exception the same way GetInternalProtections
does.
2024-04-03 12:52:12 -07:00
TheRogueArchivist
e469dc38bf Fix Roxxe false positive (#294) 2024-04-02 21:57:23 -07:00
Matt Nadareski
553703c30e Bump version 2024-04-02 17:03:49 -04:00
Matt Nadareski
3fd093f9b4 Update packages 2024-04-02 16:37:02 -04:00
TheRogueArchivist
4946d4e7ff Add Roxxe detection (#293)
* Add Roxxe detection

* Fix Roxxe PR review comments
2024-04-01 09:14:13 -07:00
Matt Nadareski
491fc0f71c Bump version 2024-03-27 12:01:40 -04:00
Matt Nadareski
fe6627f1ba Update to WiseUnpacker 1.3.1 2024-03-25 22:43:34 -04:00
Matt Nadareski
edffa3c7cc Default to using net8.0 target for launch 2024-03-25 22:32:29 -04:00
Matt Nadareski
a66d62bfbc Remove version gate from UnshieldSharp in Test 2024-03-25 21:56:08 -04:00
Matt Nadareski
9321b8f221 Update to UnshieldSharp 1.7.3 2024-03-25 21:53:33 -04:00
Matt Nadareski
cd0863ac56 Ensure zlib inflateEnd is called 2024-03-25 21:34:38 -04:00
Matt Nadareski
24a73e8bfd Update README 2024-03-25 20:08:51 -04:00
Matt Nadareski
46eaa7db1e Fix errant formatting issues 2024-03-25 19:58:37 -04:00
TheRogueArchivist
0eab7fd555 Significantly update Rainbow Sentinel detections and notes (#290) 2024-03-25 16:57:29 -07:00
Matt Nadareski
dba476d8bb Create new extractable interfaces for each executable type 2024-03-25 19:55:16 -04:00
Matt Nadareski
b10b4d6658 Seek to beginning for Embedded Executable 2024-03-25 17:14:10 -04:00
Matt Nadareski
2959fdbe9a Fix zlib with init 2024-03-25 17:10:24 -04:00
Matt Nadareski
9c0d100c2a Set zlib totals (nw) 2024-03-25 17:06:17 -04:00
Matt Nadareski
03ca0faf2e Start fixing zlib implementation 2024-03-25 16:58:11 -04:00
Matt Nadareski
cbaf004e25 Seek to beginning of stream for CExe 2024-03-25 16:37:53 -04:00
Matt Nadareski
bbe4fb610c Correct assumption about directory separators 2024-03-25 16:16:55 -04:00
Matt Nadareski
650115f722 Replace SharpZipLib with ST.Compression 2024-03-25 15:00:42 -04:00
Matt Nadareski
1afcbe3182 Update packages 2024-03-25 14:41:20 -04:00
TheRogueArchivist
8aa90dbc49 Add FlexLM detection (#289) 2024-03-24 19:08:09 -07:00
Matt Nadareski
0781524669 Bump version 2024-03-16 11:16:00 -04:00
Matt Nadareski
3b3cb7a862 Case-insensitive hash comparisons 2024-03-15 21:22:48 -04:00
Matt Nadareski
810d20d95c Bump version 2024-03-14 13:20:30 -04:00
Matt Nadareski
de578511bf Fix type retrieval for all build types 2024-03-13 23:26:16 -04:00
Matt Nadareski
f1ec025950 Fix nulability warning 2024-03-12 16:54:49 -04:00
Matt Nadareski
0c58ecc548 Update packages 2024-03-12 16:53:12 -04:00
Matt Nadareski
d2a73a153b Unroll Linq statements to fix exceptions (fixes #287) 2024-03-10 22:27:18 -04:00
TheRogueArchivist
eae2e3366b Remove DBB Skeleton (#286)
DBB is now documented in DRML.
2024-03-06 08:15:06 -08:00
Matt Nadareski
afb04c99c0 Update packages 2024-03-06 11:08:22 -05:00
Matt Nadareski
1d3bd2f8b1 Use SabreTools.Hashing 2024-03-04 21:27:36 -05:00
TheRogueArchivist
3f52c24713 Move CopyKiller comments to DRML (#285) 2024-02-28 07:36:23 -08:00
Matt Nadareski
ae1417a343 Remove unnecessary commit setting 2024-02-27 17:17:04 -05:00
Matt Nadareski
871a3e6366 Add PR check workflow 2024-02-27 11:21:01 -05:00
Matt Nadareski
3457b807cb Remove GHA on pull request builds 2024-02-27 11:17:41 -05:00
Matt Nadareski
027f295d21 Remove unnecessary newline 2024-02-26 12:46:26 -05:00
Matt Nadareski
63e6d1e285 Add badges to README 2024-02-26 12:43:49 -05:00
Matt Nadareski
2193095f70 Change nupkg location 2024-02-26 12:42:55 -05:00
Matt Nadareski
074694298f Typo typo 2024-02-26 12:37:43 -05:00
Matt Nadareski
ce4d32b053 Forgot to add recursive here 2024-02-26 12:37:11 -05:00
Matt Nadareski
a25af3940c Add recursive checkout 2024-02-26 12:36:47 -05:00
Matt Nadareski
9d1a2db45a Migrate to GitHub Actions 2024-02-26 12:29:12 -05:00
Matt Nadareski
5cdf269a3e Bump version 2024-02-26 12:14:30 -05:00
TheRogueArchivist
b9d90ec35d Remove GetVersionFromSHA1Hash from SafeDisc (#284)
* Remove GetVersionFromSHA1Hash from SafeDisc.
* Minor SafeDisc comment cleanup.
2024-02-23 10:58:58 -08:00
Matt Nadareski
5fc1d3254d Remove outdated comments in build scripts 2024-02-21 19:24:33 -05:00
Matt Nadareski
d61bae8e61 Don't use the auto git hash 2024-02-21 00:04:16 -05:00
Matt Nadareski
f8f53869ae Fix build scripts, again 2024-02-20 23:22:10 -05:00
Matt Nadareski
f5146a6e35 osx-arm64 is such a pain 2024-02-20 22:48:12 -05:00
Matt Nadareski
faf96b9375 Wrong array 2024-02-20 22:10:51 -05:00
Matt Nadareski
2228e344f6 Or, not And 2024-02-20 21:53:52 -05:00
Matt Nadareski
9955bdcab1 Not all DLLs, oops 2024-02-20 21:47:59 -05:00
Matt Nadareski
4586d49a3f Use DLL filtering in publish scripts 2024-02-20 21:45:18 -05:00
Matt Nadareski
1f4e24452a Add non-DLL lists, not hooked up 2024-02-20 21:33:14 -05:00
Matt Nadareski
090bac4d59 Remove unbuildable runtime 2024-02-20 21:12:21 -05:00
Matt Nadareski
59bedf5fce Fix DLL bundling 2024-02-20 21:07:29 -05:00
Matt Nadareski
1bbc541957 Limit packing to just BOS library 2024-02-20 21:04:39 -05:00
Matt Nadareski
1bb0107ceb Remove net6.0 from AppVeyor, add win-x86 2024-02-20 21:03:28 -05:00
Matt Nadareski
10dad356cd Expand default and extended publish targets 2024-02-20 20:59:28 -05:00
Matt Nadareski
22b6971e51 Tabs lose this battle 2024-02-20 20:54:28 -05:00
Matt Nadareski
3203b56ef6 Update publish scripts 2024-02-20 20:51:59 -05:00
Matt Nadareski
d6db84152f Limit to MS-CAB to x86 until IntPtr issue resolved 2024-02-20 20:14:38 -05:00
Matt Nadareski
b7afad5a4a Enable MS-CAB extraction on at least x86 2024-02-20 19:59:43 -05:00
Matt Nadareski
9d6c53f631 Update build after submodule update 2024-02-20 18:45:37 -05:00
Matt Nadareski
aa7b02dfc3 Add libmspack4n and LessIO as submodlues 2024-02-20 18:44:51 -05:00
TheRogueArchivist
379ffaf61a Add more empty file checks for SafeDisc (#283) 2024-02-20 06:47:20 -08:00
TheRogueArchivist
1bdfccddbc Fix Hexalock false positive (#281) 2024-02-08 10:09:11 -08:00
Matt Nadareski
c83cdd590c Update libraries 2024-02-06 10:48:59 -05:00
Matt Nadareski
f4770374a7 Update copyright date 2024-02-06 10:44:36 -05:00
TheRogueArchivist
72880e93bc Add new Denuvo Anti-Cheat detections (#279) 2024-02-01 20:27:49 -08:00
TheRogueArchivist
6c9cd72948 Add CD-X notes (#278)
* Add CD-X notes

* Add additional note
2024-01-26 06:40:09 -08:00
TheRogueArchivist
2e71ef4635 Update CopyKiller detection and notes (WIP) (#277)
* Update CopyKiller detection and notes

* Cleanup Copykiller
2024-01-25 21:19:16 -08:00
TheRogueArchivist
04cd1098ea Improve CrypKey detection (#275)
* Improve CrypKey detection

* Add CrypKey file detections.
* Add new CrypKey executable detections.

* Fix CrypKey version parsing

* Address PR reviews

* Check for both "code" and "CODE" sections
2023-12-28 20:35:57 -08:00
TheRogueArchivist
e76ce64568 Update known versions for the first SafeDisc splash-screen (#274) 2023-12-25 17:31:09 -08:00
Matt Nadareski
8fe84abef3 Use more lenient file reading 2023-12-13 15:52:03 -05:00
TheRogueArchivist
1b1fa53547 Add small note about ProtectDISC using CSS (#272) 2023-12-01 21:28:21 -08:00
TheRogueArchivist
5019407f35 Add additional SafeDisc for Mac detections and notes (#271)
* Add additional SafeDisc for Mac detections and notes

* Slightly update notes

* Minor additions

* Update earliest known SafeDisc splash-screen version
2023-12-01 17:42:03 -08:00
Matt Nadareski
83ba19eccb Fix ancient .NET dictionary extensions 2023-11-29 13:13:27 -05:00
Matt Nadareski
936bf38521 Slight cleanup 2023-11-25 22:25:44 -05:00
TheRogueArchivist
f54b0d2bbb Add SafeDisc Splash-Screen detection and notes (#269)
* Add initial SafeDisc splash-screen scanning and notes

So far only SafeDisc 1 has been added.

* Further update SafeDisc splash screen notes

This gets part of the way through SafeDisc 2 at least

* Update SafeDisc splash-screen notes and detection up through SafeDisc 2

* Update splash-screen notes through SafeDisc 3

* Starting adding SafeDisc 4 splash-screen notes

* Finish adding SafeDisc 4 splash-screen support

* Update SafeDisc splash-screen notes

* oops

* oops again
2023-11-25 19:21:59 -08:00
Matt Nadareski
0e32abc76c Bump version 2023-11-22 13:28:56 -05:00
Matt Nadareski
94cb06a3bd Handle some messages 2023-11-22 13:28:13 -05:00
Matt Nadareski
907aea443e Support .NET Framework 2.0 2023-11-22 12:22:01 -05:00
Matt Nadareski
385922723c Upate packages 2023-11-22 10:48:08 -05:00
Matt Nadareski
3061c2f009 Reenable .NET Framework 4.0 2023-11-21 10:59:29 -05:00
Matt Nadareski
5c0ccbde35 Temporarily remove .NET Framework 4.0 2023-11-21 10:19:15 -05:00
Matt Nadareski
7b998de2ca Handle more C# 12 syntax 2023-11-21 10:17:25 -05:00
Matt Nadareski
b0d49f52a5 Bump version 2023-11-20 12:16:53 -05:00
Matt Nadareski
6f9bcc2111 Fix multiple invocation bug
This bug arose when all of the libraries were consoliated into the same library for better packaging. Each set of classes was being instantiated 3 times as a result.
2023-11-18 20:55:32 -05:00
Matt Nadareski
0fb0ecd28a Update ST libraries for bugfixes 2023-11-15 12:54:32 -05:00
Matt Nadareski
6194d88aec Correct the excludes 2023-11-15 11:26:00 -05:00
Matt Nadareski
b02c3121fe Use official package for IProgress 2023-11-15 00:24:47 -05:00
Matt Nadareski
580db0cb65 Minor tweaks to reduce warnings 2023-11-14 23:48:35 -05:00
Matt Nadareski
6bcdc0e3c6 Bump version 2023-11-14 16:16:08 -05:00
Matt Nadareski
9b4fd91717 Update build scripts 2023-11-14 16:13:49 -05:00
Matt Nadareski
9421249b8e Support ancient .NET 2023-11-14 16:10:10 -05:00
Matt Nadareski
e823cbaee5 Expand supported RIDs 2023-11-08 22:51:47 -05:00
Matt Nadareski
c34618554b Update project comment 2023-11-08 12:08:22 -05:00
Matt Nadareski
6ab7b4a004 Enable latest language version 2023-11-08 11:59:50 -05:00
Matt Nadareski
5b6bf3b73e Rename to BinaryObjectScanner 2023-11-08 11:37:27 -05:00
Matt Nadareski
2a30a13f5f Omit submodule code for modern .NET 2023-10-27 00:58:03 -04:00
Matt Nadareski
3c05a112ca Remove DLLs from modern .NET builds 2023-10-27 00:55:31 -04:00
Matt Nadareski
66af2d83b8 Bump version 2023-10-26 00:35:18 -04:00
Matt Nadareski
fc3be76657 Figure out last couple of nullability issues 2023-10-26 00:27:38 -04:00
Matt Nadareski
259a91dd77 Ensure that the nupkg has the right name 2023-10-26 00:18:48 -04:00
Matt Nadareski
e957b29bae Remove unncessary/broken appveyor zips 2023-10-26 00:10:18 -04:00
Matt Nadareski
33060d9787 Sync appveyor script further to build release 2023-10-26 00:08:22 -04:00
Matt Nadareski
71ca79a456 Update SharpCompress and SharpZipLib 2023-10-26 00:00:34 -04:00
Matt Nadareski
fc744d241a Normalize archive naming with MPF 2023-10-25 23:56:20 -04:00
Matt Nadareski
78b5b3dbc1 Add publish scripts for easier distribution 2023-10-25 23:51:07 -04:00
Matt Nadareski
cb3846261a Fix more nullability locations 2023-10-25 23:33:51 -04:00
Matt Nadareski
9a3dcf70de Slight reordering of XMID/XeMID 2023-10-25 23:16:51 -04:00
Matt Nadareski
670c78302f Add printing for IRD, XMID, XeMID 2023-10-25 23:03:40 -04:00
Matt Nadareski
f4cb97b3bf Bump all ST library versions 2023-10-25 23:03:20 -04:00
TheRogueArchivist
be6d44ed04 Add WEB-Cops detection to CD/DVD-Cops (#268) 2023-10-04 17:55:10 -07:00
TheRogueArchivist
cfc9092479 Add DigiGuard detection (#267)
* Add DigiGuard detection

* Use FilePathMatch instead of PathMatch
2023-10-01 19:05:04 -07:00
TheRogueArchivist
d674ae5b1f Add new CD/DVD-Cops detections (#266) 2023-10-01 11:59:20 -07:00
Matt Nadareski
864972e575 Update packages to fix printing issues 2023-09-29 11:48:42 -04:00
Matt Nadareski
7d97850cb0 Fix some more nullability warnings 2023-09-26 15:31:43 -04:00
Matt Nadareski
3e33f098a6 Fix solution file 2023-09-26 15:25:30 -04:00
Matt Nadareski
adf9ce5e2a Matching to 1.1.1, OpenMcdf to 2.3.0 2023-09-18 15:53:24 -04:00
Matt Nadareski
0c5dff71e9 More nullability fixes 2023-09-18 14:58:33 -04:00
Matt Nadareski
715f773672 Fix DLL inclusion issues 2023-09-18 14:15:51 -04:00
Matt Nadareski
a54b89d380 Make the leap to BinaryObjectScanner 2023-09-18 13:56:07 -04:00
Matt Nadareski
2085c306ab Move stormlibsharp to main library 2023-09-18 12:52:42 -04:00
Matt Nadareski
98b99da0bc Remove stormlibsharp from Compression 2023-09-18 12:15:24 -04:00
Matt Nadareski
e00238b24e Migrate to compression library, mostly 2023-09-18 12:09:54 -04:00
Matt Nadareski
aef2b756c9 Add warning in Extractor 2023-09-18 11:35:52 -04:00
Matt Nadareski
f1f1e20a3e Remove reference to incomplete compressions 2023-09-18 11:33:25 -04:00
Matt Nadareski
62a2fdeaa6 Remove incomplete compressions 2023-09-18 11:24:12 -04:00
Matt Nadareski
37c1852058 Remove unnecessary package references 2023-09-18 01:24:44 -04:00
Matt Nadareski
99c4a08d83 Remove two incomplete compressions 2023-09-18 01:14:01 -04:00
Matt Nadareski
9d4bc6bfab Move psxt001z to its own library 2023-09-18 00:59:37 -04:00
Matt Nadareski
71fd5af48e Move some classes to new library 2023-09-18 00:33:24 -04:00
Matt Nadareski
995521b789 Move Utilities to new library 2023-09-18 00:13:23 -04:00
Matt Nadareski
5031985883 Shift references around to be more accurate 2023-09-18 00:06:07 -04:00
Matt Nadareski
5759090291 Move FileType to new library 2023-09-17 23:58:42 -04:00
Matt Nadareski
008c1c89fb Move Protection to new library 2023-09-17 23:53:14 -04:00
Matt Nadareski
624eb40315 Move Packer to new library 2023-09-17 23:40:29 -04:00
Matt Nadareski
aaa12ae817 Move GameEngine to new library 2023-09-17 23:35:59 -04:00
Matt Nadareski
d37c90878c Move Interfaces to new library 2023-09-17 23:32:29 -04:00
Matt Nadareski
668a631c11 Create new base library 2023-09-17 23:28:27 -04:00
Matt Nadareski
f06c0f4553 Simplify some directives 2023-09-17 23:11:32 -04:00
Matt Nadareski
a0b13a6e6f Fix templated nullability issues 2023-09-17 22:37:01 -04:00
Matt Nadareski
1bd9f3fd88 Open the nullability floodgates 2023-09-17 00:20:33 -04:00
Matt Nadareski
93ba88a35f Enable nullable context and move WrapperFactory 2023-09-17 00:18:04 -04:00
Matt Nadareski
c86b1251a3 Extraction can go in FileType 2023-09-16 23:09:07 -04:00
Matt Nadareski
6f6954b270 Printing is only used by Test 2023-09-16 22:33:43 -04:00
Matt Nadareski
1d4ed425f1 Remove unnecessary dependency 2023-09-16 22:24:00 -04:00
Matt Nadareski
ba657e28ad Migrate to matching library 2023-09-16 22:08:18 -04:00
Matt Nadareski
7575353597 Add nullability to matching library, fix warnings 2023-09-16 21:25:50 -04:00
Matt Nadareski
10d3c09cfa Migrate to printing library 2023-09-16 16:28:41 -04:00
Matt Nadareski
5001c4a881 Fix build from previous updates 2023-09-16 02:04:47 -04:00
Matt Nadareski
7ecd0d1893 Fix using statements in interfaces 2023-09-16 01:00:47 -04:00
Matt Nadareski
ae802d5d75 Update serialization library 2023-09-16 00:58:24 -04:00
Matt Nadareski
3b4266246d Update serialization library 2023-09-16 00:50:48 -04:00
Matt Nadareski
a801e720b2 Use wrappers from Serialization (nw) 2023-09-16 00:44:22 -04:00
Matt Nadareski
a52d45f7c2 Use exposed model directly in more places 2023-09-15 22:21:05 -04:00
Matt Nadareski
57eaa1f04c Make wrapper model visible 2023-09-15 14:15:28 -04:00
Matt Nadareski
6cc2cc5be2 Port XZP to new printing 2023-09-15 13:55:56 -04:00
Matt Nadareski
ebcdc08a77 Port WAD to new printing 2023-09-15 12:01:56 -04:00
Matt Nadareski
7aebdf56fc Port VPK to new printing 2023-09-15 11:50:40 -04:00
Matt Nadareski
6de36eb71c Port VBSP to new printing 2023-09-15 11:37:04 -04:00
Matt Nadareski
5ebd392c5b Port SGA to new printing 2023-09-15 11:25:21 -04:00
Matt Nadareski
5fc2029725 Port Quantum to new printing 2023-09-15 02:48:19 -04:00
Matt Nadareski
45e4a01fc1 Port PE to new printing 2023-09-15 02:37:45 -04:00
Matt Nadareski
c4ea7891ea Port PlayJ to new printing 2023-09-15 01:29:29 -04:00
Matt Nadareski
de871fb8c1 Port PFF to new printing 2023-09-15 01:03:48 -04:00
Matt Nadareski
c322eebb98 Port PAK to new printing 2023-09-15 00:54:40 -04:00
Matt Nadareski
ea6b0f1ca3 Port Nitro to new printing 2023-09-15 00:47:44 -04:00
Matt Nadareski
a35a9a4ab6 Add byte array to encoding extension 2023-09-15 00:23:03 -04:00
Matt Nadareski
9a93c7b15d Port NE to new printing 2023-09-15 00:21:12 -04:00
Matt Nadareski
43eed75635 Port NCF to new printing 2023-09-14 23:52:06 -04:00
Matt Nadareski
74ea6e6002 Port N3DS to new printing 2023-09-14 23:36:29 -04:00
Matt Nadareski
91e2157622 Retrofit existing printers to use extensions 2023-09-14 23:25:39 -04:00
Matt Nadareski
87961e5451 Add StringBuilder extensions to be more consistent 2023-09-14 21:21:08 -04:00
Matt Nadareski
4ff203f393 Port MZ to new printing 2023-09-13 22:46:46 -04:00
Matt Nadareski
410b2bef2b Fix one nullability issue 2023-09-13 22:40:00 -04:00
Matt Nadareski
a9792fdff1 Port MS-CAB to new printing 2023-09-13 22:38:01 -04:00
Matt Nadareski
92e36527fd Port LE/LX to new printing 2023-09-13 22:03:11 -04:00
Matt Nadareski
aa1d7d475c Fix build from oversight 2023-09-13 21:28:09 -04:00
Matt Nadareski
91185c4fe1 Port IS-CAB to new printing 2023-09-13 21:21:19 -04:00
Matt Nadareski
c527b1911f Port GCF to new printing 2023-09-13 13:52:54 -04:00
Matt Nadareski
d1501b2e3e Remove .NET 8 entirely (thanks AppVeyor) 2023-09-13 13:17:08 -04:00
Matt Nadareski
03ac117844 Disable .NET 8 for Test 2023-09-13 13:14:11 -04:00
Matt Nadareski
515be8b025 Port CIA to new printing 2023-09-13 13:12:31 -04:00
Matt Nadareski
c3479450f5 Disable AppVeyor restore until .NET 8 support 2023-09-13 12:52:57 -04:00
Matt Nadareski
611aa3229c Port CFB to new printing 2023-09-13 12:49:46 -04:00
Matt Nadareski
5a865f3d08 Fix one nullability issue 2023-09-13 12:35:08 -04:00
Matt Nadareski
9d8d5c23c8 Clean up project files a little 2023-09-13 11:21:06 -04:00
Matt Nadareski
3fed1a3282 Allow building with .NET 8 2023-09-13 10:48:21 -04:00
Matt Nadareski
f47387c3a4 Port BSP to new printing 2023-09-13 10:41:24 -04:00
Matt Nadareski
133272acb7 Flatten some of the printing code 2023-09-13 10:32:02 -04:00
Matt Nadareski
c49ae98df5 Port BFPK to new printing 2023-09-13 01:37:13 -04:00
Matt Nadareski
a1672a9bc0 Port BD+ to new printing 2023-09-13 01:29:50 -04:00
Matt Nadareski
ffc2e23b2f Let's try a new library for this instead 2023-09-13 01:24:42 -04:00
Matt Nadareski
c481e73418 Take it one step further (test) 2023-09-13 01:15:57 -04:00
Matt Nadareski
06b3124b62 Attempt to make most printing static (test) 2023-09-13 01:14:25 -04:00
Matt Nadareski
d7d81665a0 Fix miscellaneous nullability warnings 2023-09-13 00:29:21 -04:00
Matt Nadareski
24c542c22f Fix "converting null literal" warnings 2023-09-13 00:16:27 -04:00
Matt Nadareski
ec616fcdac Fix "dereference" warnings 2023-09-13 00:08:11 -04:00
Matt Nadareski
24e9455733 Fix "possible null reference" warnings 2023-09-12 17:12:23 -04:00
Matt Nadareski
58aaf46a0e Merge branch 'master' of https://github.com/mnadareski/BurnOutSharp 2023-09-12 12:35:41 -04:00
Matt Nadareski
54e92fe9c8 Fix "nullability of reference types" warnings 2023-09-12 12:35:19 -04:00
Matt Nadareski
4bb83c5d86 Don't allow model to be nullable 2023-09-11 23:38:51 -04:00
Matt Nadareski
2f9280460e Make wrappers more type-defined 2023-09-11 23:25:09 -04:00
Matt Nadareski
30e8e79cf7 Fix PE references 2023-09-11 21:08:08 -04:00
Matt Nadareski
7bb0d4f39a Migrate to ASN1 package 2023-09-11 20:59:11 -04:00
TheRogueArchivist
046814b7c4 Add new EasyAntiCheat file detections (#265)
* Add new EasyAntiCheat file detections

* Add additional EasyAntiCheat file detections
2023-09-11 07:42:54 -07:00
Matt Nadareski
cf00348d46 Update Serialization to 1.1.1 2023-09-11 01:21:38 -04:00
Matt Nadareski
8466edf80f Migrate to Serialization package 2023-09-10 23:51:38 -04:00
Matt Nadareski
ccdf539ed4 Merge branch 'master' of https://github.com/mnadareski/BurnOutSharp 2023-09-10 23:26:54 -04:00
Matt Nadareski
aeee6e9cda Fix build from package update 2023-09-10 23:26:32 -04:00
TheRogueArchivist
2af21cb245 Add DRML link to engine32 (#264) 2023-09-10 20:21:27 -07:00
Matt Nadareski
96fb5a2f93 Update Models to 1.1.1 2023-09-10 21:56:14 -04:00
Matt Nadareski
1eaefb16ba Use IO package for array and stream extensions 2023-09-08 16:33:06 -04:00
Matt Nadareski
1c972a29a7 Migrate INI reading to Nuget 2023-09-08 12:19:31 -04:00
Matt Nadareski
aa33c083fe Fix package reference for Models 2023-09-08 10:36:45 -04:00
Matt Nadareski
a9454e96ed Migrate to Nuget package for Models 2023-09-04 23:44:45 -04:00
Matt Nadareski
fb6fa85cd3 Merge branch 'master' of https://github.com/mnadareski/BurnOutSharp 2023-09-04 22:58:25 -04:00
TheRogueArchivist
3c12bdc212 Add Engine32 detection (#263)
* Add Engine32 detection

* Address PR comment
2023-08-28 12:09:25 -07:00
Matt Nadareski
5eeee760f7 Remove unnecessary param summaries 2023-08-26 22:53:35 -04:00
Matt Nadareski
cfe889d5b3 Add and use FilePathMatch (fixes #262) 2023-08-26 22:51:55 -04:00
TheRogueArchivist
3045c41eda Fix Uplay false positive (#261)
Fix a false positive that resulted in "yuPlay.exe" being detected as Uplay.
2023-08-26 19:02:26 -07:00
TheRogueArchivist
d194ef9dd8 Add new SafeCast version (#260) 2023-07-19 10:30:48 -07:00
Matt Nadareski
aeb379f307 Bump version to 2.8 2023-07-14 11:37:17 -04:00
TheRogueArchivist
cae0edfb49 Add DRML links to Denuvo AC and PlayJ (#259) 2023-07-14 08:35:09 -07:00
TheRogueArchivist
e97558db1f Add new HexaLock check (#254) 2023-05-30 17:56:19 -07:00
Matt Nadareski
06ecc1a571 Temporarily disable MS-CAB extraction
This only affects the code for normal protection scans. This does not affect trying to run extract separately, mainly for the purposes of testing.
2023-05-30 13:43:35 -04:00
TheRogueArchivist
adcf9ee27e Add hash check for RipGuard (#252)
* Add hash check for RipGuard

* Make sure the file exists before trying to hash it

* It's the safety dance
2023-05-14 12:45:30 -07:00
TheRogueArchivist
a8c759be55 Add basic RipGuard detection (#251)
* Add basic RipGuard detection and notes.
2023-05-04 05:24:33 -07:00
Matt Nadareski
ecf97de439 Use larger output buffer for MSZIP 2023-04-28 15:14:28 -04:00
TheRogueArchivist
f134de6609 Minor SecDrv cleanup (#249)
Update comments and improve output.
2023-04-22 18:18:09 -07:00
TheRogueArchivist
d91ce3100b Overhaul Macrovision and related protections (#248)
* Improve several SafeCast checks

* Add new SafeCast versions.

* Add SafeCast NE checks.

* Move stxt* checks to Macrovision, and add SafeCast specific check.

* Confirm SafeCast file and add relevant executable checks.

* Add empty file detection for some SafeDisc drivers.

* Various Macrovision improvements

* Move and improve stxt* section checks.

* Add proper Redump entry number for Puyo Puyo Fever.

* Add directory separator to some SafeDisc checks to avoid false positives.

* Add SafeWrap skeleton with notes.

* Minor Macrovision fixes

* Fix stxt* section checks.

* Add product identification for one CDS-300 version.

* Further Macrovision improvements

* Add product detection via version for SafeDisc Lite and SafeCast ESD.

* Add improved SafeDisc LT/Lite checks.

* Add various notes about SafeCast/SafeDisc.

* Add new secdrv version detection.

* Herald the upcoming secdrv cataclysm.

* Move SecDrv checks into Macrovision

* Move SecDrv checks into Macrovision, as it's known to be present on multiple Macrovision products.

* Improve SafeCast checks

* Add new SafeCast executable and path checks.

* Change a C-Dilla output.

* ah sweet, man-made horrors beyond my comprehension

* Split new and old "BoG" string checks to make finding an expunged version easier.

* Confirm unconfirmed SafeCast version, add new unconfirmed version.

* Remove now unneeded result list cleaning.

* Improve SafeCast notes.

* Add new SafeCast file description and file name checks.

* Remove "GetSafeDisc320to4xVersion" and improve CLCD16 version report.

* Address PR comments

* Address further PR comments
2023-04-21 19:38:18 -07:00
Matt Nadareski
20de40014d Fix version info reading 2023-03-27 10:46:05 -04:00
TheRogueArchivist
a7f9a99d10 Overhaul SVKP (#246)
* Begin SVKP Overhaul

* Add several entry point checks for SVKP, as well as a section check.

* Add SVKP notes.

* Add unconfirmed message in various ActiveMARK checks.

* Add file detections for SVKP

* Add file detections for SVKP.
* Cleanup notes.
* Update README.
2023-03-24 07:35:37 -07:00
TheRogueArchivist
4bdfbabaab Add new SafeDisc DrvMgt detection (#247)
* Add new SafeDisc DrvMgt detection.
2023-03-24 06:03:56 -07:00
TheRogueArchivist
5465c57d96 Add NeoLite detection (#245)
* Add NeoLite detection.
* Update README.
2023-03-22 11:26:25 -07:00
TheRogueArchivist
5082c8f705 Add RenderWare detection (#244)
* Add RenderWare detection.
* Remove unneeded note from SafeDisc.
* Update README.
2023-03-22 10:20:22 -07:00
Matt Nadareski
509fff4684 Reset state.ll for dynamic 2023-03-21 14:14:17 -04:00
Matt Nadareski
9d2c14f809 Fix MSZIP signature check (nw) 2023-03-21 13:39:25 -04:00
Matt Nadareski
b023549b87 Add note for IExtractable 2023-03-21 13:14:42 -04:00
Matt Nadareski
0983073538 Add game engine detection skeleton 2023-03-21 10:42:14 -04:00
TheRogueArchivist
815acf2ce0 Add NEAC Protect detection (#241)
* Add NEAC Protect detection

* Add NEAC Protect detection.
* Update README.

* Address PR comments
2023-03-16 12:38:30 -07:00
Matt Nadareski
6b8596466b Add unused MS-DOS executable check interface 2023-03-15 16:12:12 -04:00
Matt Nadareski
1e5bb7df64 Simplify packer handling code 2023-03-15 15:58:19 -04:00
Matt Nadareski
e920bfc69c Executable is not inherently extractable 2023-03-15 15:53:24 -04:00
Matt Nadareski
a85c6f4028 Rearrange Executable-related things 2023-03-15 15:51:38 -04:00
Matt Nadareski
a75bc15f29 Update IPathCheck helper a bit 2023-03-15 14:21:02 -04:00
Matt Nadareski
659aa30fb3 Update names, access permissions 2023-03-15 14:16:20 -04:00
Matt Nadareski
54a11916d2 Cleanup namespaces 2023-03-15 13:16:21 -04:00
Matt Nadareski
47f423d092 Implement IPathCheck helper 2023-03-15 12:55:08 -04:00
Matt Nadareski
5c5e68e31d Gut Executable for now 2023-03-15 11:37:20 -04:00
Matt Nadareski
9e917e2bb9 Create new Scanner helper classes 2023-03-15 11:06:29 -04:00
Matt Nadareski
4bd4d2f395 Remove errant using statement 2023-03-13 22:58:37 -04:00
Matt Nadareski
6406248840 Make extractable packers safer 2023-03-13 22:53:57 -04:00
Matt Nadareski
3d8134bbd3 Map out Executable extraction skeleton 2023-03-13 22:36:28 -04:00
Matt Nadareski
15310a6c47 Change namespaces but don't move yet 2023-03-13 22:22:52 -04:00
Matt Nadareski
450a8cd5bd Migrate wrapper methods to proper factory 2023-03-13 22:07:28 -04:00
Matt Nadareski
088f99942f Move file type helpers to Utilities 2023-03-13 22:03:10 -04:00
Matt Nadareski
3e97bd8d2d Move SupportedFileType to correct namespace 2023-03-13 21:54:40 -04:00
Matt Nadareski
866051c975 Fix MPQ namespace 2023-03-13 21:49:25 -04:00
Matt Nadareski
0be437f3cf Add skeleton to Scanner helpers 2023-03-13 21:47:40 -04:00
Matt Nadareski
11f3dec65d Add more notes about extraction 2023-03-13 21:45:17 -04:00
Matt Nadareski
912d151164 Add skeleton implementions in Executable 2023-03-13 21:12:11 -04:00
Matt Nadareski
863678f850 Add extractable protections to caches 2023-03-13 21:05:18 -04:00
Matt Nadareski
b82a6a8c5d Simplify some Executable code
Hopefully, simplifying this code will open the door to getting rid of `IScannable` in some way
2023-03-13 20:49:59 -04:00
Matt Nadareski
ab2daf2a80 Fix helper in Executable 2023-03-13 20:39:50 -04:00
Matt Nadareski
f734e3a58a Move MPQ to proper library, fix stormlibsharp 2023-03-13 16:41:53 -04:00
Matt Nadareski
212981fda9 Move stormlibsharp to Compression 2023-03-13 16:31:34 -04:00
Matt Nadareski
c64e138ba7 Figure out how to convert Textfile 2023-03-13 16:17:21 -04:00
Matt Nadareski
c43353d126 Move most detectables to new interface 2023-03-13 16:06:45 -04:00
Matt Nadareski
d439ba9592 Add unused IDetectable interface 2023-03-13 15:34:26 -04:00
Matt Nadareski
b7f06f0b59 Move extractable file types to new library 2023-03-10 13:48:24 -05:00
Matt Nadareski
60d666f8be Add skeleton file type library 2023-03-10 11:55:19 -05:00
Matt Nadareski
4ff4c2afef Move helper methods used in a single place 2023-03-10 11:41:08 -05:00
Matt Nadareski
655b8385f9 Rename tool class 2023-03-10 11:20:35 -05:00
Matt Nadareski
439c141c2c Move packer scans to their own library 2023-03-09 23:52:58 -05:00
Matt Nadareski
95755b930d Add packer library skeleton 2023-03-09 23:31:45 -05:00
Matt Nadareski
9cf54c1f2d Create dummy class for protection library 2023-03-09 23:26:18 -05:00
Matt Nadareski
e118418a23 Move protection scans to their own library
This change also removes a couple of things from `BurnOutSharp.Tools.Utilities` that are no longer needed there. Linear executables are included in the scanning classes. Update the guides accordingly.
2023-03-09 23:19:27 -05:00
Matt Nadareski
a3567d6eb2 Add protection library skeleton 2023-03-09 20:50:59 -05:00
Matt Nadareski
a359143bc7 Include debug in IExtractable 2023-03-09 17:16:39 -05:00
Matt Nadareski
77b7a24d85 Add TODO in Executable 2023-03-09 16:12:21 -05:00
Matt Nadareski
338b2593d5 Update guides 2023-03-09 16:08:56 -05:00
Matt Nadareski
ded5e27355 Migrate packers to extractable only 2023-03-09 16:02:51 -05:00
Matt Nadareski
ee1b005d96 Add call to packer extraction 2023-03-09 15:50:14 -05:00
Matt Nadareski
cd2673d1ba Implement unused packer extractions 2023-03-09 15:46:48 -05:00
Matt Nadareski
41d77085ae Add IExtractable skeletons to packers 2023-03-09 15:33:21 -05:00
Matt Nadareski
67059b2e43 Update guides 2023-03-09 15:17:35 -05:00
Matt Nadareski
f6157ef79b Implementations act the same 2023-03-09 15:07:35 -05:00
Matt Nadareski
6e5d517e82 Replace parts of Scan with Extract 2023-03-09 14:50:24 -05:00
Matt Nadareski
1dca51988a Implement unused extractions 2023-03-09 14:39:26 -05:00
Matt Nadareski
3c064bad55 Add skeleton IExtractable implementations 2023-03-09 14:04:31 -05:00
Matt Nadareski
d60a3ce05a Add extractable interface (unused) 2023-03-09 13:48:51 -05:00
Matt Nadareski
f68438ff8c Remove some errant temp directories 2023-03-09 13:45:42 -05:00
Matt Nadareski
d351f1d08e Create separate Options object 2023-03-09 13:26:20 -05:00
Matt Nadareski
70bdb8f37d Move most interfaces to separate library 2023-03-09 11:52:28 -05:00
TheRogueArchivist
3cd713e078 Add SafeDisc entry point check (#240)
* Add SafeDisc entry point check

* Add SafeDisc entry point check

* Address PR comments
2023-03-08 18:46:14 -08:00
Matt Nadareski
454655de5a Missed a couple in Wrappers 2023-03-08 17:53:26 -05:00
Matt Nadareski
268ed1a6a6 Clean up namespaces in Builders 2023-03-08 17:51:17 -05:00
Matt Nadareski
a42ce601b8 Simplify namespaces in Wrappers 2023-03-08 17:49:14 -05:00
Matt Nadareski
756b37ef6c Minor update for clarity 2023-03-08 17:16:29 -05:00
TheRogueArchivist
ab7e708e02 fuck macrovision (#239)
* Make SafeDisc stxt* section checks more accurate.
* Add unknown SafeDisc section notes.
2023-03-08 14:08:36 -08:00
TheRogueArchivist
326d916c0b Separate unconfirmed Macrovision checks (#238)
* Separate unconfirmed Macrovision checks

* Separate unconfirmed Macrovision checks.

* Separate unconfirmed SafeDisc version

* Separate unconfirmed SafeDisc version.
2023-03-08 13:21:34 -08:00
Matt Nadareski
db09bd931b Macrovision-adjacent additions (TheRogueArchivist) 2023-03-08 15:21:21 -05:00
TheRogueArchivist
d1e9eb90f1 Add DRML link to phenoProtect (#235)
* Add DRML link to phenoProtect.
* Fix case of the "phenoProtect" class name.
2023-03-08 11:59:57 -08:00
Matt Nadareski
20a5c4c78d Use new Macrovision helper method 2023-03-08 09:50:34 -05:00
Matt Nadareski
499f9888b1 Create helper for Macrovision version mapping 2023-03-08 09:40:19 -05:00
Matt Nadareski
7bb3364b43 Trim not TrimEnd 2023-03-08 08:47:46 -05:00
Matt Nadareski
473cbc5694 BOS.* -> BOS.* 2023-03-07 16:59:14 -05:00
Matt Nadareski
e32b24c9f6 BOS.ASN1 -> BOS.ASN1 2023-03-07 12:42:39 -05:00
Matt Nadareski
777fdc14c8 BOS.Utilities -> BOS.Utilities 2023-03-07 12:04:48 -05:00
Matt Nadareski
ffbb01c25c Migrate CDS path checks 2023-03-07 11:31:55 -05:00
Matt Nadareski
47380c2c1c Start migrating CDS, fix Macrovision directory check 2023-03-07 11:28:02 -05:00
Matt Nadareski
51e9121a6b Update generic Macrovision check 2023-03-07 11:22:45 -05:00
Matt Nadareski
94f51d518d Move SafeDisc-specific checks 2023-03-07 11:19:18 -05:00
Matt Nadareski
8fdc17b239 Missed an access modifier 2023-03-07 11:12:31 -05:00
Matt Nadareski
42bb29185f Macrovision cleanup 2023-03-07 11:10:56 -05:00
Matt Nadareski
05ae0f4e80 Add .NET 7 support 2023-03-06 09:34:51 -05:00
760 changed files with 15241 additions and 95772 deletions

43
.github/workflows/build_nupkg.yml vendored Normal file
View 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
View 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
View 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

10
.gitmodules vendored
View File

@@ -1,3 +1,9 @@
[submodule "BurnOutSharp/External/stormlibsharp"]
path = BurnOutSharp/External/stormlibsharp
[submodule "BinaryObjectScanner/_EXTERNAL/stormlibsharp"]
path = BinaryObjectScanner/_EXTERNAL/stormlibsharp
url = https://github.com/robpaveza/stormlibsharp.git
[submodule "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

2
.vscode/launch.json vendored
View File

@@ -10,7 +10,7 @@
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Test/bin/Debug/net6.0/Test.dll",
"program": "${workspaceFolder}/Test/bin/Debug/net8.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
View File

@@ -0,0 +1,3 @@
{
"dotnet.defaultSolution": "BinaryObjectScanner.sln"
}

6
.vscode/tasks.json vendored
View File

@@ -7,7 +7,7 @@
"type": "process",
"args": [
"build",
"${workspaceFolder}/BurnOutSharp.sln",
"${workspaceFolder}/BinaryObjectScanner.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
@@ -19,7 +19,7 @@
"type": "process",
"args": [
"publish",
"${workspaceFolder}/BurnOutSharp.sln",
"${workspaceFolder}/BinaryObjectScanner.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
@@ -32,7 +32,7 @@
"args": [
"watch",
"run",
"${workspaceFolder}/BurnOutSharp.sln",
"${workspaceFolder}/BinaryObjectScanner.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],

42
BinaryObjectScanner.sln Normal file
View File

@@ -0,0 +1,42 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32407.343
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "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
{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
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0B343DD2-8852-47B0-9647-DFCFBEDF933C}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,96 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>3.1.7</Version>
<!-- Mostly added due to external libraries -->
<WarningsNotAsErrors>CS0162;CS0612;CS8600;CS8601;CS8602;CS8603;CS8604;CS8605;CS8618;CS8625;CS8634;CS8765;IL3000;NU5100</WarningsNotAsErrors>
<!-- Package Properties -->
<Authors>Matt Nadareski</Authors>
<Description>Protection scanning library</Description>
<Copyright>Copyright (c)2018-2024 Matt Nadareski</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<RepositoryUrl>https://github.com/SabreTools/BinaryObjectScanner</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>protection copy-protection scanning packer</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<!-- Set a build flag for Windows specifically -->
<PropertyGroup Condition="'$(RuntimeIdentifier)'=='win-x86'">
<DefineConstants>$(DefineConstants);WIN</DefineConstants>
</PropertyGroup>
<!-- Exclude certain parts of external modules for .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" />
</ItemGroup>
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
<PackageReference Include="SharpCompress" Version="0.37.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`net4`)) AND !$(TargetFramework.StartsWith(`net40`))">
<PackageReference Include="System.Memory" Version="4.5.5" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.Compression" Version="0.5.0" />
<PackageReference Include="SabreTools.Hashing" Version="1.2.0" />
<PackageReference Include="SabreTools.IO" Version="1.4.0" />
<PackageReference Include="SabreTools.Matching" Version="1.3.1" />
<PackageReference Include="SabreTools.Models" Version="1.4.5" />
<PackageReference Include="SabreTools.Serialization" Version="1.5.6" />
<PackageReference Include="UnshieldSharp" Version="1.8.1" />
<PackageReference Include="WiseUnpacker" Version="1.4.1" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,72 @@
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.RealArcadeInstaller: return new FileType.RealArcadeInstaller();
case SupportedFileType.RealArcadeMezzanine: return new FileType.RealArcadeMezzanine();
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;
}
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}
}

View 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();
}
}
}

View 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
}
}
}

View File

@@ -1,37 +1,38 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Text;
using BurnOutSharp.Interfaces;
using BinaryObjectScanner.Interfaces;
#if NET40_OR_GREATER || NETCOREAPP
using OpenMcdf;
using static BurnOutSharp.Utilities.Dictionary;
#endif
namespace BurnOutSharp.FileType
namespace BinaryObjectScanner.FileType
{
/// <summary>
/// Compound File Binary
/// </summary>
public class CFB : IScannable
public class CFB : IExtractable
{
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Scan(scanner, fs, file);
}
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Extract(fs, file, includeDebug);
}
// TODO: Add stream opening support
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
public string? Extract(Stream? stream, string file, bool includeDebug)
{
// If the MSI file itself fails
#if NET20 || NET35
// Not supported for .NET Framework 2.0 or .NET Framework 3.5 due to library support
return null;
#else
try
{
// Create a temp output directory
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempPath);
@@ -39,65 +40,59 @@ namespace BurnOutSharp.FileType
{
msi.RootStorage.VisitEntries((e) =>
{
if (!e.IsStream)
return;
var str = msi.RootStorage.GetStream(e.Name);
if (str == null)
return;
byte[] strData = str.GetData();
if (strData == null)
return;
string decoded = DecodeStreamName(e.Name).TrimEnd('\0');
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())
try
{
decoded = decoded.Replace(c, '_');
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);
}
}
string filename = Path.Combine(tempPath, decoded);
using (Stream fs = File.OpenWrite(filename))
catch (Exception ex)
{
fs.Write(strData, 0, strData.Length);
if (includeDebug) Console.WriteLine(ex);
}
}, recursive: true);
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
}
// Remove temporary path references
StripFromKeys(protections, tempPath);
return protections;
return tempPath;
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
if (includeDebug) Console.WriteLine(ex);
return null;
}
return null;
#endif
}
/// <remarks>Adapted from LibMSI</remarks>
public static string DecodeStreamName(string input)
public static string? DecodeStreamName(string input)
{
if (input == null)
return null;

View File

@@ -0,0 +1,602 @@
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;
#if NET40_OR_GREATER || NETCOREAPP
using System.Threading.Tasks;
#endif
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
}
}

View 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;
}
}
}

View File

@@ -0,0 +1,75 @@
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 the entry is a directory
if (entry.IsDirectory)
continue;
// If the entry has an invalid key
if (entry.Key == null)
continue;
string tempFile = Path.Combine(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
}
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.IO;
using BinaryObjectScanner.Interfaces;
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)
{
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 (var cfile in archive.Files)
{
try
{
string tempFile = Path.Combine(tempPath, cfile.Key);
var directoryName = Path.GetDirectoryName(tempFile);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
(byte[]? fileContents, string? error) = archive.Extract(cfile.Key);
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;
}
}
}
}

View File

@@ -0,0 +1,101 @@
using System;
using System.IO;
using System.Text.RegularExpressions;
using BinaryObjectScanner.Interfaces;
using UnshieldSharp.Cabinet;
namespace BinaryObjectScanner.FileType
{
/// <summary>
/// InstallShield cabinet file
/// </summary>
public class InstallShieldCAB : IExtractable
{
/// <inheritdoc/>
public 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)
{
// 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?.HeaderList == null)
return null;
for (int i = 0; i < cabfile.HeaderList.FileCount; i++)
{
try
{
// Check if the file is valid first
if (!cabfile.HeaderList.FileIsValid(i))
continue;
string tempFile;
try
{
string? filename = cabfile.HeaderList.GetFileName(i);
tempFile = Path.Combine(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;
}
}
}
}

View 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;
}
}
}

View File

@@ -1,57 +1,54 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using BurnOutSharp.Interfaces;
#if NET48
using BinaryObjectScanner.Interfaces;
#if ((NETFRAMEWORK && !NET20 && !NET35 && !NET40) || NETCOREAPP) && WIN
using StormLibSharp;
#endif
using static BurnOutSharp.Utilities.Dictionary;
namespace BurnOutSharp.FileType
namespace BinaryObjectScanner.FileType
{
/// <summary>
/// MoPaQ game data archive
/// </summary>
public class MPQ : IScannable
public class MPQ : IExtractable
{
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
public string? Extract(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Scan(scanner, fs, file);
}
using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Extract(fs, file, includeDebug);
}
// TODO: Add stream opening support
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
public string? Extract(Stream? stream, string file, bool includeDebug)
{
#if NET6_0_OR_GREATER
// Not supported for .NET 6.0 due to Windows DLL requirements
#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
// If the MPQ file itself fails
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();
}
@@ -62,7 +59,6 @@ namespace BurnOutSharp.FileType
// Loop over each entry
foreach (string sub in listfileLines)
{
// If an individual entry fails
try
{
string tempFile = Path.Combine(tempPath, sub);
@@ -71,35 +67,18 @@ namespace BurnOutSharp.FileType
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(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 (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
}
// Remove temporary path references
StripFromKeys(protections, tempPath);
return protections;
return tempPath;
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
if (includeDebug) Console.WriteLine(ex);
return null;
}
return null;
#endif
}
}

View 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
}
}
}

View 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;
}
}
}
}

View 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;
}
}
}

View 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;
}
}
}
}

View File

@@ -0,0 +1,82 @@
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 the entry is a directory
if (entry.IsDirectory)
continue;
// If the entry has an invalid key
if (entry.Key == null)
continue;
// If the entry is partial due to an incomplete multi-part archive, skip it
if (!entry.IsComplete)
continue;
string tempFile = Path.Combine(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
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View File

@@ -0,0 +1,75 @@
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 the entry is a directory
if (entry.IsDirectory)
continue;
// If the entry has an invalid key
if (entry.Key == null)
continue;
string tempFile = Path.Combine(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
}
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
namespace BinaryObjectScanner.FileType
{
/// <summary>
/// RealArcade Installer. Known to use the ".rgs" file extension.
///
/// TODO: Add further parsing, game ID and name should be possible to parse.
/// </summary>
public class RealArcadeInstaller : IDetectable
{
/// <inheritdoc/>
public string? Detect(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Detect(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Detect(Stream stream, string file, bool includeDebug)
{
try
{
byte[] magic = new byte[16];
stream.Read(magic, 0, 16);
// RASGI2.0
// Found in the ".rgs" files in IA item "Nova_RealArcadeCD_USA".
if (magic.StartsWith(new byte?[] { 0x52, 0x41, 0x53, 0x47, 0x49, 0x32, 0x2E, 0x30 }))
return "RealArcade Installer";
}
catch (Exception ex)
{
if (includeDebug) Console.WriteLine(ex);
}
return null;
}
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.IO;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
namespace BinaryObjectScanner.FileType
{
/// <summary>
/// RealArcade Mezzanine files, which contain metadata. Known to use the ".mez" file extension.
///
/// TODO: Add further parsing, game ID should be possible to parse.
/// </summary>
public class RealArcadeMezzanine : IDetectable
{
/// <inheritdoc/>
public string? Detect(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
return Detect(fs, file, includeDebug);
}
}
/// <inheritdoc/>
public string? Detect(Stream stream, string file, bool includeDebug)
{
try
{
byte[] magic = new byte[16];
stream.Read(magic, 0, 16);
// XZip2.0
// Found in the ".mez" files in IA item "Nova_RealArcadeCD_USA".
if (magic.StartsWith(new byte?[] { 0x58, 0x5A, 0x69, 0x70, 0x32, 0x2E, 0x30 }))
return "RealArcade Mezzanine";
}
catch (Exception ex)
{
if (includeDebug) Console.WriteLine(ex);
}
return null;
}
}
}

View 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;
}
}
}

View File

@@ -0,0 +1,280 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Compression.zlib;
namespace BinaryObjectScanner.FileType
{
/// <summary>
/// SGA game archive
/// </summary>
public class SGA : IExtractable
{
/// <inheritdoc/>
public 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
{
// Inflate the data into the buffer
var zstream = new ZLib.z_stream_s();
data = new byte[outputFileSize];
unsafe
{
fixed (byte* payloadPtr = compressedData)
fixed (byte* dataPtr = data)
{
zstream.next_in = payloadPtr;
zstream.avail_in = (uint)compressedData.Length;
zstream.total_in = (uint)compressedData.Length;
zstream.next_out = dataPtr;
zstream.avail_out = (uint)data.Length;
zstream.total_out = 0;
ZLib.inflateInit_(zstream, ZLib.zlibVersion(), compressedData.Length);
int zret = ZLib.inflate(zstream, 1);
ZLib.inflateEnd(zstream);
}
}
}
// If we have an invalid output directory
if (string.IsNullOrEmpty(outputDirectory))
return false;
// Create the full output path
filename = Path.Combine(outputDirectory, filename);
// Ensure the output directory is created
var directoryName = Path.GetDirectoryName(filename);
if (directoryName != null)
Directory.CreateDirectory(directoryName);
// Try to write the data
try
{
// Open the output file for writing
using Stream fs = File.OpenWrite(filename);
fs.Write(data, 0, data.Length);
}
catch
{
return false;
}
return false;
}
}
}

View File

@@ -0,0 +1,75 @@
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 (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 (SevenZipArchive sevenZipFile = SevenZipArchive.Open(stream))
{
foreach (var entry in sevenZipFile.Entries)
{
try
{
// If the entry is a directory
if (entry.IsDirectory)
continue;
// If the entry has an invalid key
if (entry.Key == null)
continue;
string tempFile = Path.Combine(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
}
}
}

View File

@@ -0,0 +1,75 @@
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 the entry is a directory
if (entry.IsDirectory)
continue;
// If the entry has an invalid key
if (entry.Key == null)
continue;
string tempFile = Path.Combine(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
}
}
}

View File

@@ -0,0 +1,168 @@
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 "\disc4\cad\sdcc_200.zip\DISK1\_USER1.HDR\Language_Independent_Intel_32_Files\SNTNLUSB.INF" in "CICA 32 For Windows CD-ROM (Walnut Creek) (October 1999) (Disc 4).iso" in IA item "CICA_32_For_Windows_CD-ROM_Walnut_Creek_October_1999".
if (fileContent.Contains("SNTNLUSB.SvcDesc=\"Rainbow Security Device\""))
protections.Add("Rainbow Sentinel USB Driver");
if (fileContent.Contains("SntUsb95.SvcDesc=\"Rainbow Security Device\""))
protections.Add("Rainbow Sentinel USB Driver");
// Found in "OEMSETUP.INF" in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]".
if (fileContent.Contains("Sentinel Driver Disk"))
protections.Add("Rainbow Sentinel");
// 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]);
}
}
}

View 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;
}
}
}

View File

@@ -0,0 +1,187 @@
using System;
using System.IO;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.IO.Extensions;
namespace BinaryObjectScanner.FileType
{
/// <summary>
/// Valve Package File
/// </summary>
public class VPK : IExtractable
{
/// <inheritdoc/>
public 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;
}
}
}

View 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;
}
}
}

View 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
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View File

@@ -0,0 +1,478 @@
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 SabreTools.Serialization.Wrappers;
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(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar))?.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">IExtractable 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 IExtractableMSDOSExecutable implementation
/// </summary>
/// <param name="impl">IExtractableMSDOSExecutable class representing the file type</param>
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
/// <param name="mz">MSDOS 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(IExtractableMSDOSExecutable impl, string fileName, MSDOS mz, Scanner scanner)
#else
public static ConcurrentDictionary<string, ConcurrentQueue<string>>? HandleExtractable(IExtractableMSDOSExecutable impl, string fileName, MSDOS mz, Scanner scanner)
#endif
{
// If the extractable file itself fails
try
{
// Extract and get the output path
var tempPath = impl.Extract(fileName, mz, 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 IExtractableLinearExecutable implementation
/// </summary>
/// <param name="impl">IExtractableLinearExecutable class representing the file type</param>
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
/// <param name="lex">LinearExecutable 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(IExtractableLinearExecutable impl, string fileName, LinearExecutable lex, Scanner scanner)
#else
public static ConcurrentDictionary<string, ConcurrentQueue<string>>? HandleExtractable(IExtractableLinearExecutable impl, string fileName, LinearExecutable lex, Scanner scanner)
#endif
{
// If the extractable file itself fails
try
{
// Extract and get the output path
var tempPath = impl.Extract(fileName, lex, 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 IExtractableNewExecutable implementation
/// </summary>
/// <param name="impl">IExtractableNewExecutable class representing the file type</param>
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
/// <param name="nex">NewExecutable 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(IExtractableNewExecutable impl, string fileName, NewExecutable nex, Scanner scanner)
#else
public static ConcurrentDictionary<string, ConcurrentQueue<string>>? HandleExtractable(IExtractableNewExecutable impl, string fileName, NewExecutable nex, Scanner scanner)
#endif
{
// If the extractable file itself fails
try
{
// Extract and get the output path
var tempPath = impl.Extract(fileName, nex, 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 IExtractablePortableExecutable implementation
/// </summary>
/// <param name="impl">IExtractablePortableExecutable class representing the file type</param>
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
/// <param name="pex">PortableExecutable 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(IExtractablePortableExecutable impl, string fileName, PortableExecutable pex, Scanner scanner)
#else
public static ConcurrentDictionary<string, ConcurrentQueue<string>>? HandleExtractable(IExtractablePortableExecutable impl, string fileName, PortableExecutable pex, Scanner scanner)
#endif
{
// If the extractable file itself fails
try
{
// Extract and get the output path
var tempPath = impl.Extract(fileName, pex, 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
}
}

View File

@@ -1,4 +1,4 @@
namespace BurnOutSharp.Interfaces
namespace BinaryObjectScanner.Interfaces
{
/// <summary>
/// Check a generic file for protection
@@ -12,6 +12,6 @@
/// <param name="fileContent">Byte array representing the file contents</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>String containing any protections found in the file</returns>
string CheckContents(string file, byte[] fileContent, bool includeDebug);
string? CheckContents(string file, byte[] fileContent, bool includeDebug);
}
}

View 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);
}
}

View 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);
}
}

View File

@@ -0,0 +1,19 @@
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Interfaces
{
/// <summary>
/// Mark a LinearExecutable type as being able to be extracted
/// </summary>
public interface IExtractableLinearExecutable
{
/// <summary>
/// Extract a LinearExecutable to a temporary path, if possible
/// </summary>
/// <param name="file">Path to the input file</param>
/// <param name="lex">LinearExecutable representing the read-in file</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Path to extracted files, null on error</returns>
string? Extract(string file, LinearExecutable lex, bool includeDebug);
}
}

View File

@@ -0,0 +1,19 @@
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Interfaces
{
/// <summary>
/// Mark a MSDOS type as being able to be extracted
/// </summary>
public interface IExtractableMSDOSExecutable
{
/// <summary>
/// Extract a MSDOS to a temporary path, if possible
/// </summary>
/// <param name="file">Path to the input file</param>
/// <param name="mz">MSDOS representing the read-in file</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Path to extracted files, null on error</returns>
string? Extract(string file, MSDOS mz, bool includeDebug);
}
}

View File

@@ -0,0 +1,19 @@
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Interfaces
{
/// <summary>
/// Mark a NewExecutable type as being able to be extracted
/// </summary>
public interface IExtractableNewExecutable
{
/// <summary>
/// Extract a NewExecutable to a temporary path, if possible
/// </summary>
/// <param name="file">Path to the input file</param>
/// <param name="nex">NewExecutable representing the read-in file</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Path to extracted files, null on error</returns>
string? Extract(string file, NewExecutable nex, bool includeDebug);
}
}

View File

@@ -0,0 +1,19 @@
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Interfaces
{
/// <summary>
/// Mark a PortableExecutable type as being able to be extracted
/// </summary>
public interface IExtractablePortableExecutable
{
/// <summary>
/// Extract a PortableExecutable to a temporary path, if possible
/// </summary>
/// <param name="file">Path to the input file</param>
/// <param name="pex">PortableExecutable representing the read-in file</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>Path to extracted files, null on error</returns>
string? Extract(string file, PortableExecutable pex, bool includeDebug);
}
}

View File

@@ -1,6 +1,6 @@
using BurnOutSharp.Wrappers;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.Interfaces
namespace BinaryObjectScanner.Interfaces
{
/// <summary>
/// Check a Linear Executable (LE) for protection
@@ -14,6 +14,6 @@ namespace BurnOutSharp.Interfaces
/// <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 CheckNewExecutable(string file, LinearExecutable lex, bool includeDebug);
string? CheckLinearExecutable(string file, LinearExecutable lex, bool includeDebug);
}
}

View 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);
}
}

View File

@@ -1,6 +1,6 @@
using BurnOutSharp.Wrappers;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.Interfaces
namespace BinaryObjectScanner.Interfaces
{
/// <summary>
/// Check a New Executable (NE) for protection
@@ -14,6 +14,6 @@ namespace BurnOutSharp.Interfaces
/// <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);
string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug);
}
}

View File

@@ -1,7 +1,9 @@
using System.Collections.Concurrent;
#if NET40_OR_GREATER || NETCOREAPP
using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
namespace BurnOutSharp.Interfaces
namespace BinaryObjectScanner.Interfaces
{
/// <summary>
/// Check a file or directory path for protection
@@ -17,13 +19,17 @@ namespace BurnOutSharp.Interfaces
/// <param name="path">Path to check for protection indicators</param>
/// <param name="files">Enumerable of strings representing files in a directory</param>
/// <remarks>This can do some limited content checking as well, but it's suggested to use a content check instead, if possible</remarks>
ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files);
#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);
string? CheckFilePath(string path);
}
}

View File

@@ -1,6 +1,6 @@
using BurnOutSharp.Wrappers;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.Interfaces
namespace BinaryObjectScanner.Interfaces
{
/// <summary>
/// Check a Portable Executable (PE) for protection
@@ -14,6 +14,6 @@ namespace BurnOutSharp.Interfaces
/// <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);
string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug);
}
}

View 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; }
}
}

View File

@@ -1,21 +1,19 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Text;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.PackerType
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
public class ASPack : IPortableExecutableCheck, IScannable
public class ASPack : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
@@ -29,21 +27,21 @@ namespace BurnOutSharp.PackerType
// if (pex.EntryPointRaw != null)
// {
// var matchers = GenerateMatchers();
// string match = MatchUtil.GetFirstMatch(file, pex.EntryPointRaw, matchers, includeDebug);
// if (!string.IsNullOrWhiteSpace(match))
// var match = MatchUtil.GetFirstMatch(file, pex.EntryPointRaw, matchers, includeDebug);
// if (!string.IsNullOrEmpty(match))
// return match;
// }
// Get the .adata* section, if it exists
var adataSection = pex.GetFirstSection(".adata", exact: false);
if (adataSection != null)
if (adataSection?.Name != null)
{
var adataSectionRaw = pex.GetFirstSectionData(Encoding.UTF8.GetString(adataSection.Name));
if (adataSectionRaw != null)
{
var matchers = GenerateMatchers();
string match = MatchUtil.GetFirstMatch(file, adataSectionRaw, matchers, includeDebug);
if (!string.IsNullOrWhiteSpace(match))
var match = MatchUtil.GetFirstMatch(file, adataSectionRaw, matchers, includeDebug);
if (!string.IsNullOrEmpty(match))
return match;
}
}
@@ -52,19 +50,7 @@ namespace BurnOutSharp.PackerType
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}
@@ -79,7 +65,7 @@ namespace BurnOutSharp.PackerType
{
#region No Wildcards (Long)
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0x92, 0x1A, 0x44, 0x00, 0xB8, 0x8C, 0x1A,
@@ -88,7 +74,7 @@ namespace BurnOutSharp.PackerType
0x80, 0xBD, 0xC4, 0x1D, 0x44,
}, "ASPack 1.00b -> Solodovnikov Alexey"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0xD2, 0x2A, 0x44, 0x00, 0xB8, 0xCC, 0x2A,
@@ -102,7 +88,7 @@ namespace BurnOutSharp.PackerType
0x44, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
}, "ASPack 1.01b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0xD2, 0x2A, 0x44, 0x00, 0xB8, 0xCC, 0x2A,
@@ -111,7 +97,7 @@ namespace BurnOutSharp.PackerType
0x80, 0xBD, 0x9C, 0x2E, 0x44
}, "ASPack 1.01b -> Solodovnikov Alexey"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0x96, 0x78, 0x43, 0x00, 0xB8, 0x90, 0x78,
@@ -125,7 +111,7 @@ namespace BurnOutSharp.PackerType
0x43, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
}, "ASPack 1.02b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0x96, 0x78, 0x43, 0x00, 0xB8, 0x90, 0x78,
@@ -135,7 +121,7 @@ namespace BurnOutSharp.PackerType
0x15, 0xFE, 0x85, 0x74, 0x7C, 0x43
}, "ASPack 1.02b -> Solodovnikov Alexey"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0x8A, 0x1C, 0x40, 0x00, 0xB9, 0x9E, 0x00,
@@ -143,14 +129,14 @@ namespace BurnOutSharp.PackerType
0x8B, 0xF7, 0x33
}, "ASPack 1.02b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0x96, 0x78, 0x43, 0x00, 0xB8, 0x90, 0x78,
0x43, 0x00, 0x03, 0xC5
}, "ASPack 1.02b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0xAE, 0x98, 0x43, 0x00, 0xB8, 0xA8, 0x98,
@@ -159,7 +145,7 @@ namespace BurnOutSharp.PackerType
0x80, 0xBD, 0x0E, 0x9D, 0x43
}, "ASPack 1.03b -> Solodovnikov Alexey"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0xCE, 0x3A, 0x44, 0x00, 0xB8, 0xC8, 0x3A,
@@ -168,7 +154,7 @@ namespace BurnOutSharp.PackerType
0x80, 0xBD, 0xAC, 0x3E, 0x44
}, "ASPack 1.05b -> Solodovnikov Alexey"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0xEA, 0xA8, 0x43, 0x00, 0xB8, 0xE4, 0xA8,
@@ -182,7 +168,7 @@ namespace BurnOutSharp.PackerType
0x43, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
}, "ASPack 1.06.01b (DLL)"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0xEA, 0xA8, 0x43, 0x00, 0xB8, 0xE4, 0xA8,
@@ -192,7 +178,7 @@ namespace BurnOutSharp.PackerType
0x15, 0xFE, 0x85, 0x6E, 0xAD, 0x43
}, "ASPack 1.06.01b -> Solodovnikov Alexey"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0x3E, 0xD9, 0x43, 0x00, 0xB8, 0x38, 0xD9,
@@ -206,7 +192,7 @@ namespace BurnOutSharp.PackerType
0x43, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
}, "ASPack 1.07b (DLL)"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xEB, 0x03, 0x5D, 0xFF, 0xE5, 0xE8, 0xF8,
0xFF, 0xFF, 0xFF, 0x81, 0xED, 0x1B, 0x6A, 0x44,
@@ -214,7 +200,7 @@ namespace BurnOutSharp.PackerType
0x2B, 0x9D, 0x2A
}, "ASPack 1.08"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0x0A, 0x4A, 0x44, 0x00, 0xBB, 0x04, 0x4A,
@@ -228,7 +214,7 @@ namespace BurnOutSharp.PackerType
0x53, 0x50, 0xFF, 0x95, 0x90, 0x51, 0x44, 0x00
}, "ASPack 1.08.03"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0x0A, 0x4A, 0x44, 0x00, 0xBB, 0x04, 0x4A,
@@ -237,20 +223,20 @@ namespace BurnOutSharp.PackerType
0x00, 0x89, 0x9D, 0xBB, 0x4E
}, "ASPack 1.08.03"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0x0A, 0x4A, 0x44, 0x00, 0xBB, 0x04, 0x4A,
0x44, 0x00, 0x03, 0xDD
}, "ASPack 1.08.03"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x72, 0x05, 0x00, 0x00, 0xEB, 0x33,
0x87, 0xDB, 0x90, 0x00
}, "ASPack 2.00.01"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9, 0xEB,
0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01, 0x00,
@@ -258,34 +244,34 @@ namespace BurnOutSharp.PackerType
0xFF, 0x03, 0xDD, 0x81, 0xEB
}, "ASPack 2.1"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x02, 0x00, 0x00, 0x00, 0xEB, 0x09,
0x5D, 0x55, 0x81, 0xED, 0x39, 0x39, 0x44, 0x00,
0xC3, 0xE9, 0x3D, 0x04, 0x00, 0x00
}, "ASPack 2.11b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x02, 0x00, 0x00, 0x00, 0xEB, 0x09,
0x5D, 0x55
}, "ASPack 2.11b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x02, 0x00, 0x00, 0x00, 0xEB, 0x09,
0x5D, 0x55, 0x81, 0xED, 0x39, 0x39, 0x44, 0x00,
0xC3, 0xE9, 0x59, 0x04, 0x00, 0x00
}, "ASPack 2.11c"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x02, 0x00, 0x00, 0x00, 0xCD, 0x20,
0xE8, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x2B, 0xC9,
0x58, 0x74, 0x02
}, "ASPack 2.11d"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9, 0xEB,
0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01
@@ -295,7 +281,7 @@ namespace BurnOutSharp.PackerType
#region Wildcards (Long)
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
0xED, 0x3E, 0xD9, 0x43, null, 0xB8, 0x38, null,
@@ -309,7 +295,7 @@ namespace BurnOutSharp.PackerType
0x43, null, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
}, "ASPack 1.00b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
0xED, 0xD2, 0x2A, 0x44, null, 0xB8, 0xCC, 0x2A,
@@ -318,7 +304,7 @@ namespace BurnOutSharp.PackerType
0x80, 0xBD, 0x9C, 0x2E, 0x44
}, "ASPack 1.01b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
0xED, 0xCE, 0x3A, 0x44, null, 0xB8, 0xC8, 0x3A,
@@ -327,7 +313,7 @@ namespace BurnOutSharp.PackerType
0x80, 0xBD, 0xAC, 0x3E, 0x44
}, "ASPack 1.01b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, 0x3E, 0xD9, 0x43, 0x00, 0xB8, 0x38, null,
@@ -341,13 +327,13 @@ namespace BurnOutSharp.PackerType
0x43, 0x00, 0x89, 0x44, 0x24, 0x1C, 0x61, 0xFF
}, "ASPack 1.02a -> Solodovnikov Alexey"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
0xED, 0x06, null, null, null, 0x64, 0xA0, 0x23
}, "ASPack 1.02a"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
0xED, 0x96, 0x78, 0x43, null, 0xB8, 0x90, 0x78,
@@ -356,7 +342,7 @@ namespace BurnOutSharp.PackerType
0x80, 0xBD, 0x74, 0x7C, 0x43
}, "ASPack 1.02b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
0xED, 0xAE, 0x98, 0x43, null, 0xB8, 0xA8, 0x98,
@@ -365,7 +351,7 @@ namespace BurnOutSharp.PackerType
0x80, 0xBD, 0x0E, 0x9D, 0x43
}, "ASPack 1.03b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
0xED, null, null, null, null, 0xE8, 0x0D, null,
@@ -373,7 +359,7 @@ namespace BurnOutSharp.PackerType
null, null, null, null, null, null, null, 0x58
}, "ASPack 1.03b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81,
0xED, null, null, null, 0x00, 0xB8, null, null,
@@ -382,7 +368,7 @@ namespace BurnOutSharp.PackerType
0x80, 0xBD, 0x08, 0x9D, null, 0x00, 0x00
}, "ASPack 1.04b -> Solodovnikov Alexey"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
0xED, null, null, null, null, 0xB8, null, null,
@@ -391,7 +377,7 @@ namespace BurnOutSharp.PackerType
0x80, 0xBD, 0x08, 0x9D
}, "ASPack 1.04b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
0xED, null, null, null, null, 0xB8, null, null,
@@ -400,7 +386,7 @@ namespace BurnOutSharp.PackerType
0x80, 0xBD, 0x01, 0xDE
}, "ASPack 1.04b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
0xED, 0xEA, 0xA8, 0x43, null, 0xB8, 0xE4, 0xA8,
@@ -409,28 +395,28 @@ namespace BurnOutSharp.PackerType
0x80, 0xBD, 0x6E, 0xAD, 0x43
}, "ASPack 1.06.1b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x90, 0x61, 0xBE, null, null, null, null, 0x8D,
0xBE, null, null, null, null, 0x57, 0x83, 0xCD,
0xFF
}, "ASPack 1.06.1b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, null,
null, null, null, null, null, 0xB8, null, null,
null, null, 0x03, 0xC5
}, "ASPack 1.07b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, null, null, null, null, 0x5D, 0x81,
0xED, null, null, null, null, 0x60, 0xE8, 0x2B,
0x03, 0x00, 0x00
}, "ASPack 1.07b"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xEB, 0x0A, 0x5D, 0xEB, 0x02, 0xFF, 0x25,
0x45, 0xFF, 0xE5, 0xE8, 0xE9, 0xE8, 0xF1, 0xFF,
@@ -439,7 +425,7 @@ namespace BurnOutSharp.PackerType
0x2B, 0x9D
}, "ASPack 1.08.01"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xEB, 0x0A, 0x5D, 0xEB, 0x02, 0xFF, 0x25,
0x45, 0xFF, 0xE5, 0xE8, 0xE9, 0xE8, 0xF1, 0xFF,
@@ -448,7 +434,7 @@ namespace BurnOutSharp.PackerType
0x2B, 0x9D
}, "ASPack 1.08.01"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xEB, 0x0A, 0x5D, 0xEB, 0x02, 0xFF, 0x25,
0x45, 0xFF, 0xE5, 0xE8, 0xE9, 0xE8, 0xF1, 0xFF,
@@ -457,7 +443,7 @@ namespace BurnOutSharp.PackerType
0x2B, 0x9D, 0x72
}, "ASPack 1.08.02"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, null,
null, null, null, null, null, 0xBB, null, null,
@@ -466,14 +452,14 @@ namespace BurnOutSharp.PackerType
0x00, 0x89, 0x9D, 0xBB, 0x4E
}, "ASPack 1.08.03"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, null,
null, null, null, null, null, 0xBB, null, null,
null, null, 0x03, 0xDD
}, "ASPack 1.08.03"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x55, 0x57, 0x51, 0x53, 0xE8, null, null, null,
null, 0x5D, 0x8B, 0xC5, 0x81, 0xED, null, null,
@@ -482,27 +468,27 @@ namespace BurnOutSharp.PackerType
null, 0x0F, 0xB6
}, "ASPack 1.08.03"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE9, null, null, null, null, 0xEF, 0x40,
0x03, 0xA7, 0x07, 0x8F, 0x07, 0x1C, 0x37, 0x5D,
0x43, 0xA7, 0x04, 0xB9, 0x2C, 0x3A
}, "ASPack 1.08.x"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x02, 0x00, 0x00, 0x00, 0xEB, 0x09,
0x5D, 0x55, 0x81, 0xED, 0x39, 0x39, 0x44, 0x00,
0xC3, 0xE9, null, 0x04, 0x00, 0x00
}, "ASPack 2.11.x -> Alexey Solodovnikov"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
null, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9, 0xEB,
0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01
}, "ASPack 2.12 (without Poly) -> Solodovnikov Alexey"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
null, 0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9,
0xEB, 0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01,
@@ -519,7 +505,7 @@ namespace BurnOutSharp.PackerType
0xFF, 0x95, 0x48, 0x0F
}, "ASPack 2.12b -> Solodovnikov Alexey"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9, 0xEB,
0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01, 0x00,
@@ -568,7 +554,7 @@ namespace BurnOutSharp.PackerType
0x08
}, "ASPack 2.2 -> Alexey Solodovnikov & StarForce * 2009408"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
null, 0x60, 0xE8, 0x03, 0x00, 0x00, 0x00, 0xE9,
0xEB, 0x04, 0x5D, 0x45, 0x55, 0xC3, 0xE8, 0x01,
@@ -581,7 +567,7 @@ namespace BurnOutSharp.PackerType
#region 2.xx (Long)
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0xA8, 0x03, 0x00, 0x00, 0x61, 0x75, 0x08, 0xB8,
0x01, 0x00, 0x00, 0x00, 0xC2, 0x0C, 0x00, 0x68,
@@ -590,7 +576,7 @@ namespace BurnOutSharp.PackerType
0x00, 0x51, 0x50, 0xFF, 0x95
}, "ASPack 2.xx"),
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0xA8, 0x03, null, null, 0x61, 0x75, 0x08, 0xB8,
0x01, null, null, null, 0xC2, 0x0C, null, 0x68,
@@ -603,55 +589,55 @@ namespace BurnOutSharp.PackerType
#region Short
new ContentMatchSet(new byte?[] { 0x75, 0x00, 0xE9 }, "ASPack 1.05b"),
new(new byte?[] { 0x75, 0x00, 0xE9 }, "ASPack 1.05b"),
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
new(new byte?[] { 0x90, 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
new ContentMatchSet(new byte?[] { 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
new(new byte?[] { 0x90, 0x75, 0x00, 0xE9 }, "ASPack 1.06.1b"),
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
new(new byte?[] { 0x90, 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
new ContentMatchSet(new byte?[] { 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
new(new byte?[] { 0x90, 0x75, null, 0xE9 }, "ASPack 1.07b"),
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08"),
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08"),
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
new(new byte?[] { 0x90, 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
new ContentMatchSet(new byte?[] { 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
new(new byte?[] { 0x90, 0x75, 0x01, 0xFF, 0xE9 }, "ASPack 1.08"),
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
new(new byte?[] { 0x90, 0x90, 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
new(new byte?[] { 0x90, 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
new ContentMatchSet(new byte?[] { 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
new(new byte?[] { 0x90, 0x75, null, 0x90, 0xE9 }, "ASPack 1.08.01"),
new ContentMatchSet(new byte?[] { 0x90, 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08.02"),
new(new byte?[] { 0x90, 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08.02"),
new ContentMatchSet(new byte?[] { 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08.02"),
new(new byte?[] { 0x90, 0x75, 0x01, 0x90, 0xE9 }, "ASPack 1.08.02"),
new ContentMatchSet(new byte?[] { 0x60, 0xE8, 0x41, 0x06, 0x00, 0x00, 0xEB, 0x41 }, "ASPack 1.08.04"),
new(new byte?[] { 0x60, 0xE8, 0x41, 0x06, 0x00, 0x00, 0xEB, 0x41 }, "ASPack 1.08.04"),
new ContentMatchSet(new byte?[] { 0x60, 0xE8, null, null, null, null, 0xEB }, "ASPack 1.08.04"),
new(new byte?[] { 0x60, 0xE8, null, null, null, null, 0xEB }, "ASPack 1.08.04"),
new ContentMatchSet(new byte?[] { 0x60, 0xE8, 0x70, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.00"),
new(new byte?[] { 0x60, 0xE8, 0x70, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.00"),
new ContentMatchSet(new byte?[] { 0x60, 0xE8, 0x48, 0x11, 0x00, 0x00, 0xC3, 0x83 }, "ASPack 2.00.00"),
new(new byte?[] { 0x60, 0xE8, 0x48, 0x11, 0x00, 0x00, 0xC3, 0x83 }, "ASPack 2.00.00"),
new ContentMatchSet(new byte?[] { 0x60, 0xE8, 0x72, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.01"),
new(new byte?[] { 0x60, 0xE8, 0x72, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.01"),
new ContentMatchSet(new byte?[] { 0x60, 0xE8, null, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.x -> Alexey Solodovnikov"),
new(new byte?[] { 0x60, 0xE8, null, 0x05, 0x00, 0x00, 0xEB, 0x4C }, "ASPack 2.00.x -> Alexey Solodovnikov"),
new ContentMatchSet(new byte?[] { 0x60, 0xE9, 0x3D, 0x04, 0x00, 0x00 }, "ASPack 2.11"),
new(new byte?[] { 0x60, 0xE9, 0x3D, 0x04, 0x00, 0x00 }, "ASPack 2.11"),
new ContentMatchSet(new byte?[] { 0x60, 0xE8, 0xF9, 0x11, 0x00, 0x00, 0xC3, 0x83 }, "ASPack 2.11"),
new(new byte?[] { 0x60, 0xE8, 0xF9, 0x11, 0x00, 0x00, 0xC3, 0x83 }, "ASPack 2.11"),
new ContentMatchSet(new byte?[] { 0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81, 0xED }, "ASPack 1.02b/1.08.03"),
new(new byte?[] { 0x60, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x81, 0xED }, "ASPack 1.02b/1.08.03"),
#endregion
};

View File

@@ -0,0 +1,36 @@
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// TODO: Verify that all versions are detected
public class AdvancedInstaller : IExtractablePortableExecutable, 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, PortableExecutable pex, bool includeDebug)
{
return null;
}
}
}

View File

@@ -0,0 +1,49 @@
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 : IExtractablePortableExecutable, 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, PortableExecutable pex, bool includeDebug)
{
return null;
}
}
}

View File

@@ -1,26 +1,24 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.PackerType
namespace BinaryObjectScanner.Packer
{
// Created by IndigoRose (creators of Setup Factory), primarily to be used to create autorun menus for various media.
// Official website: https://www.autoplay.org/
// TODO: Add extraction
public class AutoPlayMediaStudio : IPortableExecutableCheck, IScannable
public class AutoPlayMediaStudio : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
// Known to detect versions 5.0.0.3 - 8.1.0.0
string name = pex.ProductName;
var name = pex.ProductName;
if (name?.StartsWith("AutoPlay Media Studio", StringComparison.OrdinalIgnoreCase) == true)
return $"AutoPlay Media Studio {GetVersion(pex)}";
@@ -35,34 +33,22 @@ namespace BurnOutSharp.PackerType
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}
private string GetVersion(PortableExecutable pex)
{
// Check the product version explicitly
string version = pex.ProductVersion;
var version = pex.ProductVersion;
if (!string.IsNullOrEmpty(version))
return version;
return version!;
// Check the internal versions
version = Tools.Utilities.GetInternalVersion(pex);
version = pex.GetInternalVersion();
if (!string.IsNullOrEmpty(version))
return version;
return version!;
return "(Unknown Version)";
}

View File

@@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Compression.zlib;
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 : IExtractablePortableExecutable, 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, PortableExecutable pex, bool includeDebug)
{
try
{
// Get the first resource of type 99 with index 2
var payload = pex.FindResourceByNamedType("99, 2").FirstOrDefault();
if (payload == null || payload.Length == 0)
return 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
var zstream = new ZLib.z_stream_s();
data = new byte[payload.Length * 4];
unsafe
{
fixed (byte* payloadPtr = payload)
fixed (byte* dataPtr = data)
{
zstream.next_in = payloadPtr;
zstream.avail_in = (uint)payload.Length;
zstream.total_in = (uint)payload.Length;
zstream.next_out = dataPtr;
zstream.avail_out = (uint)data.Length;
zstream.total_out = 0;
ZLib.inflateInit_(zstream, ZLib.zlibVersion(), payload.Length);
int zret = ZLib.inflate(zstream, 1);
ZLib.inflateEnd(zstream);
}
}
// Trim the buffer to the proper size
uint read = zstream.total_out;
#if NET462_OR_GREATER || NETCOREAPP
data = new ReadOnlySpan<byte>(data, 0, (int)read).ToArray();
#else
var temp = new byte[read];
Array.Copy(data, 0, temp, 0, read);
data = temp;
#endif
}
catch
{
// Reset the data
data = null;
}
}
// Otherwise, LZ is used
else
{
try
{
data = SabreTools.Compression.LZ.Decompressor.Decompress(payload);
}
catch
{
// Reset the data
data = null;
}
}
// If we have no data
if (data == null)
return 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;
}
}
}
}

View File

@@ -1,20 +1,18 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
using BurnOutSharp.Wrappers;
using System.Collections.Generic;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.PackerType
namespace BinaryObjectScanner.Packer
{
// TODO: Figure out how to more granularly determine versions like PiD
// TODO: Detect 3.15 and up (maybe looking for `Metamorphism`)
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class EXEStealth : IContentCheck, IPortableExecutableCheck, IScannable
public class EXEStealth : IContentCheck, IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includeDebug)
public string? CheckContents(string file, byte[] fileContent, bool includeDebug)
{
// TODO: Obtain a sample to find where this string is in a typical executable
if (includeDebug)
@@ -22,7 +20,7 @@ namespace BurnOutSharp.PackerType
var contentMatchSets = new List<ContentMatchSet>
{
// ??[[__[[_ + (char)0x00 + {{ + (char)0x0 + (char)0x00 + {{ + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x0 + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x00 + ?;??;??
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x3F, 0x3F, 0x5B, 0x5B, 0x5F, 0x5F, 0x5B, 0x5B,
0x5F, 0x00, 0x7B, 0x7B, 0x00, 0x00, 0x7B, 0x7B,
@@ -39,10 +37,10 @@ namespace BurnOutSharp.PackerType
}
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
@@ -76,19 +74,7 @@ namespace BurnOutSharp.PackerType
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -0,0 +1,82 @@
using System;
using System.IO;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
/// <summary>
/// Though not technically a packer, this detection is for any executables that include
/// others in their resources in some uncompressed manner to be used at runtime.
/// </summary>
public class EmbeddedExecutable : IExtractablePortableExecutable, 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, PortableExecutable pex, bool includeDebug)
{
try
{
// If there are no resources
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);
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;
}
}
}
}

View File

@@ -0,0 +1,39 @@
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class GenteeInstaller : IExtractablePortableExecutable, 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, PortableExecutable pex, bool includeDebug)
{
return null;
}
}
}

View File

@@ -1,30 +1,28 @@
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Wrappers;
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.PackerType
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 : IPortableExecutableCheck, IScannable
public class HyperTechCrackProof : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
// 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.ImportTable?.ImportDirectoryTable?.Any(idte => idte.Name == "KeRnEl32.dLl") ?? false);
bool importTableMatch = (pex.Model.ImportTable?.ImportDirectoryTable?.Any(idte => idte?.Name == "KeRnEl32.dLl") ?? false);
if (peCSection && importTableMatch)
return "HyperTech CrackProof";
@@ -33,19 +31,7 @@ namespace BurnOutSharp.PackerType
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -1,29 +1,23 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.PackerType
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction - https://github.com/dscharrer/InnoExtract
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class InnoSetup : INewExecutableCheck, IPortableExecutableCheck, IScannable
public class InnoSetup : IExtractablePortableExecutable, INewExecutableCheck, IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
{
// Check we have a valid executable
if (nex == null)
return null;
// Check for "Inno" in the reserved words
if (nex.Stub_Reserved2[4] == 0x6E49 && nex.Stub_Reserved2[5] == 0x6F6E)
if (nex.Model.Stub?.Header?.Reserved2?[4] == 0x6E49 && nex.Model.Stub?.Header?.Reserved2?[5] == 0x6F6E)
{
string version = GetOldVersion(file, nex);
if (!string.IsNullOrWhiteSpace(version))
if (!string.IsNullOrEmpty(version))
return $"Inno Setup {version}";
return "Inno Setup (Unknown Version)";
@@ -33,18 +27,18 @@ namespace BurnOutSharp.PackerType
}
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
// Get the .data/DATA section strings, if they exist
List<string> strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
if (strs != null)
{
string str = strs.FirstOrDefault(s => s.StartsWith("Inno Setup Setup Data"));
var str = strs.FirstOrDefault(s => s.StartsWith("Inno Setup Setup Data"));
if (str != null)
{
return str.Replace("Inno Setup Setup Data", "Inno Setup")
@@ -59,19 +53,7 @@ namespace BurnOutSharp.PackerType
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}
@@ -90,7 +72,7 @@ namespace BurnOutSharp.PackerType
var matchers = new List<ContentMatchSet>
{
// "rDlPtS02" + (char)0x87 + "eVx"
new ContentMatchSet(new byte?[] { 0x72, 0x44, 0x6C, 0x50, 0x74, 0x53, 0x30, 0x32, 0x87, 0x65, 0x56, 0x78 }, "1.2.16 or earlier"),
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";

View File

@@ -1,24 +1,22 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.PackerType
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 : IPortableExecutableCheck, IScannable
public class InstallAnywhere : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
string name = pex.FileDescription;
var name= pex.FileDescription;
if (name?.StartsWith("InstallAnywhere Self Extractor", StringComparison.OrdinalIgnoreCase) == true)
return $"InstallAnywhere {GetVersion(pex)}";
@@ -30,19 +28,7 @@ namespace BurnOutSharp.PackerType
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}
@@ -50,9 +36,9 @@ namespace BurnOutSharp.PackerType
private string GetVersion(PortableExecutable pex)
{
// Check the internal versions
string version = Tools.Utilities.GetInternalVersion(pex);
var version = pex.GetInternalVersion();
if (!string.IsNullOrEmpty(version))
return version;
return version!;
return "(Unknown Version)";
}

View File

@@ -0,0 +1,37 @@
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction - https://github.com/Bioruebe/UniExtract2
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class InstallerVISE : IExtractablePortableExecutable, 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, PortableExecutable pex, bool includeDebug)
{
return null;
}
}
}

View File

@@ -0,0 +1,41 @@
using System;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction, seems to primarily use MSZip compression.
public class IntelInstallationFramework : IExtractablePortableExecutable, 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, PortableExecutable pex, bool includeDebug)
{
return null;
}
}
}

View File

@@ -1,26 +1,23 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.PackerType
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 : IPortableExecutableCheck, IScannable
public class MicrosoftCABSFX : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
string name = pex.InternalName;
var name= pex.InternalName;
if (name?.Equals("Wextract", StringComparison.OrdinalIgnoreCase) == true)
return $"Microsoft CAB SFX {GetVersion(pex)}";
@@ -29,7 +26,7 @@ namespace BurnOutSharp.PackerType
return $"Microsoft CAB SFX {GetVersion(pex)}";
// Get the .data/DATA section strings, if they exist
List<string> strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
if (strs != null)
{
if (strs.Any(s => s.Contains("wextract_cleanup")))
@@ -50,28 +47,16 @@ namespace BurnOutSharp.PackerType
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}
private string GetVersion(PortableExecutable pex)
{
// Check the internal versions
string version = Tools.Utilities.GetInternalVersion(pex);
if (!string.IsNullOrWhiteSpace(version))
var version = pex.GetInternalVersion();
if (!string.IsNullOrEmpty(version))
return $"v{version}";
return string.Empty;

View File

@@ -0,0 +1,39 @@
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
public class NSIS : IExtractablePortableExecutable, 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, PortableExecutable pex, bool includeDebug)
{
return null;
}
}
}

View File

@@ -0,0 +1,45 @@
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 : IExtractablePortableExecutable, 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, PortableExecutable pex, bool includeDebug)
{
return null;
}
}
}

View File

@@ -1,24 +1,22 @@
using System.Collections.Concurrent;
using System.IO;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.PackerType
namespace BinaryObjectScanner.Packer
{
// TODO: Better version detection - https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
// TODO: Add extraction
public class PECompact : IPortableExecutableCheck, IScannable
public class PECompact : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
// 0x4F434550 is "PECO"
if (pex.PointerToSymbolTable == 0x4F434550)
if (pex.Model.COFFFileHeader?.PointerToSymbolTable == 0x4F434550)
return "PE Compact v1.x";
// TODO: Get more granular version detection. PiD is somehow able to detect version ranges based
@@ -41,21 +39,9 @@ namespace BurnOutSharp.PackerType
return null;
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -0,0 +1,32 @@
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class PEtite : IExtractablePortableExecutable, 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, PortableExecutable pex, bool includeDebug)
{
return null;
}
}
}

View File

@@ -1,26 +1,24 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.PackerType
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction, which is possible but the only tools available that can
// do this seem to be Universal Extractor 2 and InstallExplorer (https://totalcmd.net/plugring/InstallExplorer.html)
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class SetupFactory : IPortableExecutableCheck, IScannable
public class SetupFactory : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
// Known to detect versions 7.0.5.1 - 9.1.0.0
string name = pex.LegalCopyright;
var name = pex.LegalCopyright;
if (name?.StartsWith("Setup Engine", StringComparison.OrdinalIgnoreCase) == true)
return $"Setup Factory {GetVersion(pex)}";
@@ -40,34 +38,22 @@ namespace BurnOutSharp.PackerType
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}
private string GetVersion(PortableExecutable pex)
{
// Check the product version explicitly
string version = pex.ProductVersion;
var version = pex.ProductVersion;
if (!string.IsNullOrEmpty(version))
return version;
return version!;
// Check the internal versions
version = Tools.Utilities.GetInternalVersion(pex);
version = pex.GetInternalVersion();
if (!string.IsNullOrEmpty(version))
return version;
return version!;
return "(Unknown Version)";
}

View File

@@ -1,19 +1,17 @@
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.PackerType
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
public class SevenZipSFX : IPortableExecutableCheck, IScannable
public class SevenZipSFX : IExtractablePortableExecutable, IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
@@ -47,19 +45,7 @@ namespace BurnOutSharp.PackerType
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -0,0 +1,33 @@
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class Shrinker : IExtractablePortableExecutable, 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, PortableExecutable pex, bool includeDebug)
{
return null;
}
}
}

View File

@@ -1,35 +1,32 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.PackerType
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class UPX : IPortableExecutableCheck, IScannable
public class UPX : IExtractablePortableExecutable, 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)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
// Check header padding strings
if (pex.HeaderPaddingStrings?.Any() == true)
{
string match = pex.HeaderPaddingStrings.FirstOrDefault(s => s.Contains("UPX!"));
var match = pex.HeaderPaddingStrings.FirstOrDefault(s => s.Contains("UPX!"));
//if (match != null)
// return "UPX";
@@ -44,7 +41,7 @@ namespace BurnOutSharp.PackerType
}
match = pex.HeaderPaddingStrings.FirstOrDefault(s => _upxVersionMatch.IsMatch(s));
if (pex.HeaderPaddingStrings.Any(s => s == "UPX!" && match != null))
if (match != null && pex.HeaderPaddingStrings.Any(s => s == "UPX!"))
{
var regexMatch = _upxVersionMatch.Match(match);
if (regexMatch.Success)
@@ -52,7 +49,7 @@ namespace BurnOutSharp.PackerType
else
return "UPX (Unknown Version)";
}
else if (pex.HeaderPaddingStrings.Any(s => s == "NOS " && match != null))
else if (match != null && pex.HeaderPaddingStrings.Any(s => s == "NOS "))
{
var regexMatch = _upxVersionMatch.Match(match);
if (regexMatch.Success)
@@ -66,19 +63,7 @@ namespace BurnOutSharp.PackerType
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
{
return null;
}

View File

@@ -0,0 +1,84 @@
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 : IExtractablePortableExecutable, 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, PortableExecutable pex, 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 the entry is a directory
if (entry.IsDirectory)
continue;
// If the entry has an invalid key
if (entry.Key == null)
continue;
string tempFile = Path.Combine(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
}
}
}

View File

@@ -0,0 +1,813 @@
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 : IExtractableNewExecutable, IExtractablePortableExecutable, 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, NewExecutable nex, bool includeDebug)
=> Extract(file, includeDebug);
/// <inheritdoc/>
public string? Extract(string file, PortableExecutable pex, bool includeDebug)
=> Extract(file, includeDebug);
/// <summary>
/// Handle common extraction between executable types
/// </summary>
/// <inheritdoc/>
public static string? Extract(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 (ZipArchive zipFile = ZipArchive.Open(file))
{
foreach (var entry in zipFile.Entries)
{
try
{
// If the entry is a directory
if (entry.IsDirectory)
continue;
// If the entry has an invalid key
if (entry.Key == null)
continue;
// If we have a partial entry due to an incomplete multi-part archive, skip it
if (!entry.IsComplete)
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
}
/// <summary>
/// Get the version from the NE header value combinations
/// </summary>
/// TODO: Reduce the checks to only the ones that differ between versions
/// TODO: Research to see if the versions are embedded elsewhere in these files
private static string? GetNEHeaderVersion(NewExecutable nex)
{
#region 2.0 Variants
// 2.0 (MS-DOS/16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x0086
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
&& nex.Model.Header?.InitialStackAlloc == 0x4000
&& nex.Model.Header?.InitialCSIPSetting == 0x00012BE6
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000044B8
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "2.0 (MS-DOS/16-bit)";
// 2.0 (16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x0086
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
&& nex.Model.Header?.InitialStackAlloc == 0x4000
&& nex.Model.Header?.InitialCSIPSetting == 0x00013174
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000198
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "2.0 (16-bit)";
// Compact 2.0 (16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x0080
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
&& nex.Model.Header?.InitialStackAlloc == 0x4000
&& nex.Model.Header?.InitialCSIPSetting == 0x000124A0
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0003
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006A
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000192
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "Compact 2.0 (16-bit)";
// Software Installation 2.0 (16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x00CD
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
&& nex.Model.Header?.InitialStackAlloc == 0x4000
&& nex.Model.Header?.InitialCSIPSetting == 0x000136FA
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0005
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0097
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x00A3
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00AD
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001DF
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "Software Installation 2.0 (16-bit)";
#endregion
#region 2.1 RC2 Variants
// 2.1 RC2 (MS-DOS/16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x0086
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
&& nex.Model.Header?.InitialStackAlloc == 0x4000
&& nex.Model.Header?.InitialCSIPSetting == 0x00013386
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000043C8
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "2.1 RC2 (MS-DOS/16-bit)";
// 2.1 RC2 (16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x00BE
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
&& nex.Model.Header?.InitialStackAlloc == 0x4000
&& nex.Model.Header?.InitialCSIPSetting == 0x00013E56
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "2.1 RC2 (16-bit)";
// Compact 2.1 RC2 (16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x0080
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
&& nex.Model.Header?.InitialStackAlloc == 0x4000
&& nex.Model.Header?.InitialCSIPSetting == 0x00012B84
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0003
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006A
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000192
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "Compact 2.1 RC2 (16-bit)";
// Software Installation 2.1 RC2 (16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x00BE
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
&& nex.Model.Header?.InitialStackAlloc == 0x4000
&& nex.Model.Header?.InitialCSIPSetting == 0x000143AC
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "Software Installation 2.1 RC2 (16-bit)";
#endregion
#region 2.1 Variants
// 2.1 (MS-DOS/16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x0086
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
&& nex.Model.Header?.InitialStackAlloc == 0x3A00
&& nex.Model.Header?.InitialCSIPSetting == 0x00013396
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000043C8
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "2.1 (MS-DOS/16-bit)";
// 2.1 (16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x00BE
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
&& nex.Model.Header?.InitialStackAlloc == 0x3A00
&& nex.Model.Header?.InitialCSIPSetting == 0x00013E7E
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "2.1 (16-bit)";
// Compact 2.1 (16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x0080
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
&& nex.Model.Header?.InitialStackAlloc == 0x3A00
&& nex.Model.Header?.InitialCSIPSetting == 0x00012B90
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0003
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006A
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000192
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "Compact 2.1 (16-bit)";
// Software Installation 2.1 (16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x00BE
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
&& nex.Model.Header?.InitialStackAlloc == 0x3A00
&& nex.Model.Header?.InitialCSIPSetting == 0x00014408
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "Software Installation 2.1 (16-bit)";
#endregion
#region Misc. Variants
// Personal Edition (16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x0086
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.ProtectedModeOnly
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
&& nex.Model.Header?.InitialStackAlloc == 0x4000
&& nex.Model.Header?.InitialCSIPSetting == 0x0001317C
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0058
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x0064
&& nex.Model.Header?.ImportedNamesTableOffset == 0x006C
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x00000198
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "Personal Edition (16-bit)";
// Personal Edition 32-bit (16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x00BE
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x2000
&& nex.Model.Header?.InitialStackAlloc == 0x3C00
&& nex.Model.Header?.InitialCSIPSetting == 0x00013E7C
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0004
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A4
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D0
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "Personal Edition 32-bit (16-bit)";
// Personal Edition 32-bit Build 1260/1285 (16-bit)
if (nex.Model.Header?.LinkerVersion == 0x11
&& nex.Model.Header?.LinkerRevision == 0x20
&& nex.Model.Header?.EntryTableOffset == 0x00C6
&& nex.Model.Header?.EntryTableSize == 0x0002
&& nex.Model.Header?.CrcChecksum == 0x00000000
&& nex.Model.Header?.FlagWord == (SabreTools.Models.NewExecutable.HeaderFlag.MULTIPLEDATA
| SabreTools.Models.NewExecutable.HeaderFlag.FullScreen
| SabreTools.Models.NewExecutable.HeaderFlag.WindowsPMCompatible)
&& nex.Model.Header?.AutomaticDataSegmentNumber == 0x0003
&& nex.Model.Header?.InitialHeapAlloc == 0x43DC
&& nex.Model.Header?.InitialStackAlloc == 0x2708
&& nex.Model.Header?.InitialCSIPSetting == 0x00014ADC
&& nex.Model.Header?.InitialSSSPSetting == 0x00030000
&& nex.Model.Header?.FileSegmentCount == 0x0003
&& nex.Model.Header?.ModuleReferenceTableSize == 0x0005
&& nex.Model.Header?.NonResidentNameTableSize == 0x004B
&& nex.Model.Header?.SegmentTableOffset == 0x0040
&& nex.Model.Header?.ResourceTableOffset == 0x0058
&& nex.Model.Header?.ResidentNameTableOffset == 0x0090
&& nex.Model.Header?.ModuleReferenceTableOffset == 0x009C
&& nex.Model.Header?.ImportedNamesTableOffset == 0x00A6
&& nex.Model.Header?.NonResidentNamesTableOffset == 0x000001D8
&& nex.Model.Header?.MovableEntriesCount == 0x0000
&& nex.Model.Header?.SegmentAlignmentShiftCount == 0x0001
&& nex.Model.Header?.ResourceEntriesCount == 0x0000
&& nex.Model.Header?.TargetOperatingSystem == SabreTools.Models.NewExecutable.OperatingSystem.WINDOWS
&& nex.Model.Header?.AdditionalFlags == 0x00
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.ReturnThunkOffset == 0x0000
&& nex.Model.Header?.MinCodeSwapAreaSize == 0x0000
&& nex.Model.Header?.WindowsSDKRevision == 0x00
&& nex.Model.Header?.WindowsSDKVersion == 0x03)
return "Personal Edition 32-bit Build 1260/1285 (16-bit)";
#endregion
return null;
}
/// <summary>
/// Get the version from the PE export directory table value combinations
/// </summary>
/// TODO: Research to see if the versions are embedded elsewhere in these files
private static string? GetPEExportDirectoryVersion(PortableExecutable pex)
{
string sfxFileName = pex.Model.ExportTable?.ExportDirectoryTable?.Name ?? string.Empty;
uint sfxTimeDateStamp = pex.Model.ExportTable?.ExportDirectoryTable?.TimeDateStamp ?? uint.MaxValue;
string assemblyVersion = pex.AssemblyVersion ?? "Unknown Version";
// Standard
if (sfxFileName == "VW95SE.SFX" || sfxFileName == "ST32E.SFX"
|| sfxFileName == "WZIPSE32.exe" || sfxFileName == "SI32LPG.SFX"
|| sfxFileName == "ST32E.WZE")
{
return sfxTimeDateStamp switch
{
842636344 => "2.0 (32-bit)",
865370756 => "2.1 RC2 (32-bit)",
869059925 => "2.1 (32-bit)",
979049321 => "2.2.4003",
1149714685 => "3.0.7158",
1185211734 => "3.1.7556",
1185211920 => "3.1.7556",
1235490556 => "4.0.8421",
1235490757 => "4.0.8421",
1235490687 => "4.0.8421",// 3.1.8421.0, SI32LPG?
1257193383 => "4.0.8672",// 3.1.8672.0
1257193543 => "4.0.8672",
1470410848 => "4.0.12218",// 4.0.1221.0
_ => $"{assemblyVersion} (32-bit)",
};
}
// Personal Edition
if (sfxFileName == "VW95LE.SFX" || sfxFileName == "PE32E.SFX"
|| sfxFileName == "wzsepe32.exe" || sfxFileName == "SI32PE.SFX"
|| sfxFileName == "SI32LPE.SFX")
{
return sfxTimeDateStamp switch
{
845061601 => "Personal Edition (32-bit)",// TODO: Find version
868303343 => "Personal Edition (32-bit)",// TODO: Find version
868304170 => "Personal Edition (32-bit)",// TODO: Find version
906039079 => "Personal Edition 2.2.1260 (32-bit)",
906040543 => "Personal Edition 2.2.1260 (32-bit)",
908628435 => "Personal Edition 2.2.1285 (32-bit)",
908628785 => "Personal Edition 2.2.1285 (32-bit)",
956165981 => "Personal Edition 2.2.3063",
956166038 => "Personal Edition 2.2.3063",
1006353695 => "Personal Edition 2.2.4325",
1006353714 => "Personal Edition 2.2.4325",// 8.1.0.0
1076515698 => "Personal Edition 2.2.6028",
1076515784 => "Personal Edition 2.2.6028",// 9.0.6028.0
1092688561 => "Personal Edition 2.2.6224",
1092688645 => "Personal Edition 2.2.6224",// 9.0.6224.0
1125074095 => "Personal Edition 2.2.6604",
1125074162 => "Personal Edition 2.2.6604",// 10.0.6604.0
1130153399 => "Personal Edition 2.2.6663",
1130153428 => "Personal Edition 2.2.6663",// 10.0.6663.0
1149714176 => "Personal Edition 3.0.7158",
1163137967 => "Personal Edition 3.0.7305",
1163137994 => "Personal Edition 3.0.7313",// 11.0.7313.0
1176345383 => "Personal Edition 3.0.7452",
1176345423 => "Personal Edition 3.1.7466",// 11.1.7466.0
1184106698 => "Personal Edition 3.1.7556",
1207280880 => "Personal Edition 4.0.8060",// 2.3.7382.0
1207280892 => "Personal Edition 4.0.8094",// 11.2.8094.0
1220904506 => "Personal Edition 4.0.8213",// 2.3.7382.0
1220904518 => "Personal Edition 4.0.8252",// 12.0.8252.0
1235490648 => "Personal Edition 4.0.8421",// 3.1.8421.0
1242049399 => "Personal Edition 4.0.8497",// 12.1.8497.0
1257193469 => "Personal Edition 4.0.8672",// 3.1.8672.0, SI32LPE?
_ => $"Personal Edition {assemblyVersion} (32-bit)",
};
}
// Software Installation
else if (sfxFileName == "VW95SRE.SFX" || sfxFileName == "SI32E.SFX"
|| sfxFileName == "SI32E.WZE")
{
return sfxTimeDateStamp switch
{
842636381 => "Software Installation 2.0 (32-bit)",
865370800 => "Software Installation 2.1 RC2 (32-bit)",
869059963 => "Software Installation 2.1 (32-bit)",
893107697 => "Software Installation 2.2.1110 (32-bit)",
952007369 => "Software Installation 2.2.3063",
1006352634 => "Software Installation 2.2.4325",// +Personal Edition?
979049345 => "Software Installation 2.2.4403",
1026227373 => "Software Installation 2.2.5196",// +Personal Edition?
1090582390 => "Software Installation 2.2.6202",// +Personal Edition?
1149714757 => "Software Installation 3.0.7158",
1154357628 => "Software Installation 3.0.7212",
1175234637 => "Software Installation 3.0.7454",
1185211802 => "Software Installation 3.1.7556",
1470410906 => "Software Installation 4.0.12218",// 4.0.1221.0
_ => $"Software Installation {assemblyVersion} (32-bit)",
};
}
return sfxFileName switch
{
// Standard
"VW95SE.SFX" => "Unknown Version (32-bit)",// TODO: Find starting version
"ST32E.SFX" => "Unknown Version (32-bit)",// TODO: Find starting version
"WZIPSE32.exe" => "Unknown Version (32-bit)",// TODO: Find starting version
"SI32LPG.SFX" => "Unknown Version (32-bit)",// TODO: Find starting version
"ST32E.WZE" => "Unknown Version (32-bit)",// TODO: Find starting version
// Personal Edition
"VW95LE.SFX" => "Unknown Version before Personal Edition Build 1285 (32-bit)",
"PE32E.SFX" => "Unknown Version after Personal Edition Build 1285 (32-bit)",
"wzsepe32.exe" => "Unknown Version Personal Edition (32-bit)",// TODO: Find starting version
"SI32PE.SFX" => "Unknown Version Personal Edition (32-bit)",// TODO: Find starting version
"SI32LPE.SFX" => "Unknown Version Personal Edition (32-bit)",// TODO: Find starting version
// Software Installation
"VW95SRE.SFX" => "Unknown Version before Software Installation 2.1 (32-bit)",
"SI32E.SFX" => "Unknown Version after Software Installation 2.1 (32-bit)",
"SI32E.WZE" => "Unknown Version Software Installation (32-bit)",// TODO: Find starting version
_ => null,
};
}
}
}

View File

@@ -1,27 +1,22 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
using BurnOutSharp.Utilities;
using BurnOutSharp.Wrappers;
using static BurnOutSharp.Utilities.Dictionary;
using Wise = WiseUnpacker.WiseUnpacker;
using BinaryObjectScanner.Interfaces;
using SabreTools.IO.Extensions;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
using WiseUnpacker;
using WiseUnpacker.EWISE;
namespace BurnOutSharp.PackerType
namespace BinaryObjectScanner.Packer
{
// https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt
public class WiseInstaller : INewExecutableCheck, IPortableExecutableCheck, IScannable
public class WiseInstaller : IExtractableNewExecutable, IExtractablePortableExecutable, INewExecutableCheck, IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug)
{
/// Check we have a valid executable
if (nex == null)
return null;
// If we match a known header
if (MatchesNEVersion(nex) != null)
return "Wise Installation Wizard Module";
@@ -36,20 +31,20 @@ namespace BurnOutSharp.PackerType
var neMatchSets = new List<ContentMatchSet>
{
// WiseInst
new ContentMatchSet(new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x49, 0x6E, 0x73, 0x74 }, "Wise Installation Wizard Module"),
new(new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x49, 0x6E, 0x73, 0x74 }, "Wise Installation Wizard Module"),
// WiseMain
new ContentMatchSet(new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x4D, 0x61, 0x69, 0x6E }, "Wise Installation Wizard Module"),
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)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
@@ -60,7 +55,7 @@ namespace BurnOutSharp.PackerType
// TODO: Investigate STUB32.EXE in export directory table
// Get the .data/DATA section strings, if they exist
List<string> strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
if (strs != null)
{
if (strs.Any(s => s.Contains("WiseMain")))
@@ -79,41 +74,144 @@ namespace BurnOutSharp.PackerType
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
public string? Extract(string file, NewExecutable nex, bool includeDebug)
{
if (!File.Exists(file))
return null;
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempPath);
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the installer file itself fails
try
{
// Try to parse as a New Executable
NewExecutable nex = NewExecutable.Create(stream);
if (nex != null)
return ScanNewExecutable(scanner, nex, file);
// TODO: Try to find where the file data lives and how to get it
if (!Extractor.ExtractTo(file, tempPath))
{
try
{
Directory.Delete(tempPath, true);
}
catch (Exception ex)
{
if (includeDebug) Console.WriteLine(ex);
}
// Try to parse as a Portable Executable
PortableExecutable pex = PortableExecutable.Create(stream);
if (pex != null)
return ScanPortableExecutable(scanner, pex, file);
return null;
return null;
}
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
if (includeDebug) Console.WriteLine(ex);
return null;
}
return null;
return tempPath;
}
/// <inheritdoc/>
public string? Extract(string file, PortableExecutable pex, 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 = (int)(dataStart + format.ArchiveEnd);
int archiveEndLoaded = overlayData.ReadInt32(ref overlayOffset);
if (archiveEndLoaded != 0)
format.ArchiveEnd = archiveEndLoaded;
}
// Skip to the start of the archive
overlayOffset = (int)(dataStart + format.ArchiveStart);
// Skip over the initialization text, if we expect it
if (format.InitText)
{
int initTextLength = overlayData.ReadByte(ref overlayOffset);
_ = overlayData.ReadBytes(ref overlayOffset, initTextLength);
}
// Cache the current offset in the overlay as the "start of data"
int offsetReal = overlayOffset;
// If the first entry is PKZIP, we assume it's an embedded zipfile
var magic = overlayData.ReadBytes(ref overlayOffset, 4); overlayOffset -= 4;
bool pkzip = magic?.StartsWith(new byte?[] { (byte)'P', (byte)'K' }) ?? false;
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
{
if (!Extractor.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>
@@ -121,10 +219,10 @@ namespace BurnOutSharp.PackerType
/// </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)
private FormatProperty? MatchesNEVersion(NewExecutable nex)
{
// TODO: Offset is _not_ the EXE header address, rather where the data starts. Fix this.
switch (nex.Stub_NewExeHeaderAddr)
switch (nex.Model.Stub?.Header?.NewExeHeaderAddr)
{
case 0x84b0:
return new FormatProperty { Dll = false, ArchiveStart = 0x11, ArchiveEnd = -1, InitText = false, FilenamePosition = 0x04, NoCrc = true };
@@ -178,7 +276,7 @@ namespace BurnOutSharp.PackerType
/// </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)
private FormatProperty? GetPEFormat(PortableExecutable pex)
{
if (pex.OverlayAddress == 0x6e00
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3cf4
@@ -217,240 +315,5 @@ namespace BurnOutSharp.PackerType
return null;
}
/// <summary>
/// Attempt to extract Wise data from a New Executable
/// </summary>
/// <param name="scanner">Scanner object for state tracking</param>
/// <param name="nex">New executable to check</param>
/// <param name="file">Path to the input file</param>
/// <returns>True if it matches a known version, false otherwise</returns>
private ConcurrentDictionary<string, ConcurrentQueue<string>> ScanNewExecutable(Scanner scanner, NewExecutable nex, string file)
{
// If the installer file itself fails
try
{
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempPath);
// TODO: Try to find where the file data lives and how to get it
Wise unpacker = new Wise();
unpacker.ExtractTo(file, tempPath);
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
}
// Remove temporary path references
StripFromKeys(protections, tempPath);
return protections;
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
}
return null;
}
/// <summary>
/// Attempt to extract Wise data from a Portable Executable
/// </summary>
/// <param name="scanner">Scanner object for state tracking</param>
/// <param name="pex">Portable executable to check</param>
/// <param name="file">Path to the input file</param>
/// <returns>True if it matches a known version, false otherwise</returns>
private ConcurrentDictionary<string, ConcurrentQueue<string>> ScanPortableExecutable(Scanner scanner, PortableExecutable pex, string file)
{
// If the installer file itself fails
try
{
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempPath);
// 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;
byte[] 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
byte[] magic = overlayData.ReadBytes(ref overlayOffset, 4); overlayOffset -= 4;
bool pkzip = magic.StartsWith(new byte?[] { (byte)'P', (byte)'K' });
// If we have PKZIP
if (pkzip)
{
try
{
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);
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
}
// Remove temporary path references
StripFromKeys(protections, tempPath);
return protections;
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
return null;
}
}
// If we have DEFLATE -- TODO: Port implementation here or use DeflateStream
else
{
Wise unpacker = new Wise();
unpacker.ExtractTo(file, tempPath);
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch (Exception ex)
{
if (scanner.IncludeDebug) Console.WriteLine(ex);
}
// Remove temporary path references
StripFromKeys(protections, tempPath);
return protections;
}
}
catch (Exception ex)
{
if (scanner.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; }
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Linq;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.Packer
{
// TODO: Add extraction
public class dotFuscator : IExtractablePortableExecutable, 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, PortableExecutable pex, bool includeDebug)
{
return null;
}
}
}

View 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

View File

@@ -1,17 +1,17 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.ProtectionType
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)
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)
@@ -19,7 +19,7 @@ namespace BurnOutSharp.ProtectionType
var contentMatchSets = new List<ContentMatchSet>
{
// " " + (char)0xC2 + (char)0x16 + (char)0x00 + (char)0xA8 + (char)0xC1 + (char)0x16 + (char)0x00 + (char)0xB8 + (char)0xC1 + (char)0x16 + (char)0x00 + (char)0x86 + (char)0xC8 + (char)0x16 + (char)0x00 + (char)0x9A + (char)0xC1 + (char)0x16 + (char)0x00 + (char)0x10 + (char)0xC2 + (char)0x16 + (char)0x00
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x20, 0xC2, 0x16, 0x00, 0xA8, 0xC1, 0x16, 0x00,
0xB8, 0xC1, 0x16, 0x00, 0x86, 0xC8, 0x16, 0x00,
@@ -34,10 +34,10 @@ namespace BurnOutSharp.ProtectionType
}
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
@@ -50,35 +50,35 @@ namespace BurnOutSharp.ProtectionType
// 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.";
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.";
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)";
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.";
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]";
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";
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)";
return "ActiveMARK 5.x -> Trymedia Systems,Inc. (h) (Unconfirmed - Please report to us on Github)";
}
// Get the .data section strings, if they exist
List<string> strs = pex.GetLastSectionStrings(".data");
var strs = pex.GetLastSectionStrings(".data");
if (strs != null)
{
if (strs.Any(s => s.Contains("MPRMMGVA"))
@@ -92,8 +92,8 @@ namespace BurnOutSharp.ProtectionType
var resources = pex.FindResourceByNamedType("REGISTRY, AMINTERNETPROTOCOL");
if (resources.Any())
{
bool match = resources.Where(r => r != null)
.Select(r => Encoding.ASCII.GetString(r))
bool match = resources
.Select(r => r == null ? string.Empty : Encoding.ASCII.GetString(r))
.Any(r => r.Contains("ActiveMARK"));
if (match)
return "ActiveMARK";

View File

@@ -1,11 +1,13 @@
using System.Collections.Concurrent;
#if NET40_OR_GREATER || NETCOREAPP
using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using System.Linq;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.ProtectionType
namespace BinaryObjectScanner.Protection
{
/// <summary>
/// AegiSoft License Manager was made AegiSoft, which was later bought by Real Networks, the makes of RealArcade (https://www.crunchbase.com/organization/aegisoft).
@@ -20,10 +22,10 @@ namespace BurnOutSharp.ProtectionType
public class AegiSoft : IPathCheck, IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
@@ -51,15 +53,15 @@ namespace BurnOutSharp.ProtectionType
{
// Found in "Asc001.dll", "Asc002.dll", "Asc003.dll", "Asc005.dll", "Asc006.exe", and "AscLM.cpl" (Redump entry 73521/IA item "Nova_HoyleCasino99USA").
// ÿÿÿÿ\\.\ASCLM
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0xFF, 0xFF, 0xFF, 0xFF, 0x5C, 0x5C, 0x2E, 0x5C,
0x41, 0x53, 0x43, 0x4C, 0x4D
}, "AegiSoft License Manager"),
};
string match = MatchUtil.GetFirstMatch(file, dataSectionRaw, matchers, includeDebug);
if (!string.IsNullOrWhiteSpace(match))
var match = MatchUtil.GetFirstMatch(file, dataSectionRaw, matchers, includeDebug);
if (!string.IsNullOrEmpty(match))
return match;
}
@@ -67,14 +69,18 @@ namespace BurnOutSharp.ProtectionType
}
/// <inheritdoc/>
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
#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 PathMatchSet(new PathMatch("AscLM.cpl", useEndsWith: true), "AegiSoft License Manager"),
new PathMatchSet(new PathMatch("AscLM.vxd", useEndsWith: true), "AegiSoft License Manager"),
new PathMatchSet(new PathMatch("AscLMd.vxd", useEndsWith: true), "AegiSoft License Manager"),
new(new FilePathMatch("AscLM.cpl"), "AegiSoft License Manager"),
new(new FilePathMatch("AscLM.vxd"), "AegiSoft License Manager"),
new(new FilePathMatch("AscLMd.vxd"), "AegiSoft License Manager"),
// There are a few other files present, but the file names on their own may be too overmatching. Due to the small sample size, it's not sure if these files are always present together.
// These files are "Asc001.dll", "Asc002.dll", "Asc003.dll", "Asc005.dll", and "Asc006.exe" (Found in Redump entry 73521/IA item "Nova_HoyleCasino99USA").
@@ -84,14 +90,14 @@ namespace BurnOutSharp.ProtectionType
}
/// <inheritdoc/>
public string CheckFilePath(string path)
public string? CheckFilePath(string path)
{
var matchers = new List<PathMatchSet>
{
// Found in Redump entry 73521/IA item "Nova_HoyleCasino99USA".
new PathMatchSet(new PathMatch("AscLM.cpl", useEndsWith: true), "AegiSoft License Manager"),
new PathMatchSet(new PathMatch("AscLM.vxd", useEndsWith: true), "AegiSoft License Manager"),
new PathMatchSet(new PathMatch("AscLMd.vxd", useEndsWith: true), "AegiSoft License Manager"),
new(new FilePathMatch("AscLM.cpl"), "AegiSoft License Manager"),
new(new FilePathMatch("AscLM.vxd"), "AegiSoft License Manager"),
new(new FilePathMatch("AscLMd.vxd"), "AegiSoft License Manager"),
// There are a few other files present, but the file names on their own may be too overmatching. Due to the small sample size, it's not sure if these files are always present together.
// These files are "Asc001.dll", "Asc002.dll", "Asc003.dll", "Asc005.dll", and "Asc006.exe" (Found in Redump entry 73521/IA item "Nova_HoyleCasino99USA").

View File

@@ -1,4 +1,4 @@
namespace BurnOutSharp.ProtectionType
namespace BinaryObjectScanner.Protection
{
public class AlphaAudio
{

View File

@@ -1,9 +1,11 @@
using System.Collections.Concurrent;
#if NET40_OR_GREATER || NETCOREAPP
using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
namespace BurnOutSharp.ProtectionType
namespace BinaryObjectScanner.Protection
{
/// <summary>
/// Alpha-DVD is a DVD-Video copy protection created by SETTEC.
@@ -16,22 +18,26 @@ namespace BurnOutSharp.ProtectionType
public class AlphaDVD : IPathCheck
{
/// <inheritdoc/>
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
#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 PathMatchSet(new PathMatch("PlayDVD.exe", useEndsWith: true), "Alpha-DVD (Unconfirmed - Please report to us on Github)"),
new(new FilePathMatch("PlayDVD.exe"), "Alpha-DVD (Unconfirmed - Please report to us on Github)"),
};
return MatchUtil.GetAllMatches(files, matchers, any: true);
}
/// <inheritdoc/>
public string CheckFilePath(string path)
public string? CheckFilePath(string path)
{
var matchers = new List<PathMatchSet>
{
new PathMatchSet(new PathMatch("PlayDVD.exe", useEndsWith: true), "Alpha-DVD (Unconfirmed - Please report to us on Github"),
new(new FilePathMatch("PlayDVD.exe"), "Alpha-DVD (Unconfirmed - Please report to us on Github"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);

View File

@@ -1,10 +1,9 @@
using System.Collections.Generic;
using System.Linq;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.ProtectionType
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.
@@ -45,18 +44,18 @@ namespace BurnOutSharp.ProtectionType
public class AlphaROM : IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
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?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
// Get the .data/DATA section strings, if they exist
List<string> strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
if (strs != null)
{
if (strs.Any(s => s.Contains("\\SETTEC")))

View File

@@ -1,10 +1,11 @@
using System.Collections.Concurrent;
#if NET40_OR_GREATER || NETCOREAPP
using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using System.IO;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
namespace BurnOutSharp.ProtectionType
namespace BinaryObjectScanner.Protection
{
/// <summary>
/// Bitpool is a copy protection found most commonly in German releases.
@@ -14,24 +15,28 @@ namespace BurnOutSharp.ProtectionType
public class Bitpool : IPathCheck
{
/// <inheritdoc/>
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
#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 PathMatchSet(new PathMatch("bitpool.rsc", useEndsWith: true), "Bitpool"),
new PathMatchSet(new PathMatch($"{Path.DirectorySeparatorChar}CD.IDX", useEndsWith: true), "Bitpool"),
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 PathMatchSet(new PathMatch("LEADOUT.OFS", useEndsWith: true), "Bitpool"),
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 PathMatchSet(new List<PathMatch>
new(new List<PathMatch>
{
new PathMatch($"{Path.DirectorySeparatorChar}Crc_a", useEndsWith: true),
new PathMatch($"{Path.DirectorySeparatorChar}Crc_b", useEndsWith: true),
new PathMatch($"{Path.DirectorySeparatorChar}Crc_c", useEndsWith: true),
new PathMatch($"{Path.DirectorySeparatorChar}Crc_d", useEndsWith: true),
new FilePathMatch("Crc_a"),
new FilePathMatch("Crc_b"),
new FilePathMatch("Crc_c"),
new FilePathMatch("Crc_d"),
}, "Bitpool"),
};
@@ -39,15 +44,15 @@ namespace BurnOutSharp.ProtectionType
}
/// <inheritdoc/>
public string CheckFilePath(string path)
public string? CheckFilePath(string path)
{
var matchers = new List<PathMatchSet>
{
new PathMatchSet(new PathMatch("bitpool.rsc", useEndsWith: true), "Bitpool"),
new PathMatchSet(new PathMatch($"{Path.DirectorySeparatorChar}CD.IDX", useEndsWith: true), "Bitpool"),
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 PathMatchSet(new PathMatch("LEADOUT.OFS", useEndsWith: true), "Bitpool"),
new(new FilePathMatch("LEADOUT.OFS"), "Bitpool"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);

View File

@@ -1,11 +1,13 @@
using System.Collections.Concurrent;
#if NET40_OR_GREATER || NETCOREAPP
using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using System.Linq;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.ProtectionType
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).
@@ -41,42 +43,42 @@ namespace BurnOutSharp.ProtectionType
public class ByteShield : IPortableExecutableCheck, IPathCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
// Found in "LineRider2.exe" in Redump entry 6236
string name = pex.FileDescription;
var name = pex.FileDescription;
if (name?.Equals("ByteShield Client") == true)
return $"ByteShield Activation Client {Tools.Utilities.GetInternalVersion(pex)}";
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 {Tools.Utilities.GetInternalVersion(pex)}";
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 {Tools.Utilities.GetInternalVersion(pex)}";
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 {Tools.Utilities.GetInternalVersion(pex)}";
return $"ByteShield Activation Client {pex.GetInternalVersion()}";
// Found in "ByteShield.dll" in Redump entry 6236
name = pex.ExportTable?.ExportDirectoryTable?.Name;
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 {Tools.Utilities.GetInternalVersion(pex)}";
return $"ByteShield Activation Client {pex.GetInternalVersion()}";
// Found in "LineRider2.exe" in Redump entry 6236
var dbMatch = pex.FindDialogByTitle("About ByteShield");
@@ -91,7 +93,7 @@ namespace BurnOutSharp.ProtectionType
return "ByteShield";
// Get the .data/DATA section strings, if they exist
List<string> strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
var strs = pex.GetFirstSectionStrings(".data") ?? pex.GetFirstSectionStrings("DATA");
if (strs != null)
{
// Found in "LineRider2.exe" in Redump entry 6236
@@ -128,30 +130,34 @@ namespace BurnOutSharp.ProtectionType
return null;
}
/// <inheritdoc/>
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
#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 PathMatchSet(new PathMatch("Byteshield.dll", useEndsWith: true), "ByteShield Component Module"),
new PathMatchSet(new PathMatch("Byteshield.ini", useEndsWith: true), "ByteShield"),
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)
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 PathMatchSet(new PathMatch("Byteshield.dll", useEndsWith: true), "ByteShield Component Module"),
new PathMatchSet(new PathMatch("Byteshield.ini", useEndsWith: true), "ByteShield"),
new(new FilePathMatch("Byteshield.dll"), "ByteShield Component Module"),
new(new FilePathMatch("Byteshield.ini"), "ByteShield"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);

View File

@@ -1,19 +1,19 @@
using BurnOutSharp.Interfaces;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.ProtectionType
namespace BinaryObjectScanner.Protection
{
public class CDCheck : IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
string name = pex.Comments;
var name = pex.Comments;
if (name?.Contains("CDCheck utlity for Microsoft Game Studios") == true)
return "Microsoft Game Studios CD Check";

View 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);
}
}
}

View File

@@ -1,12 +1,14 @@
using System;
#if NET40_OR_GREATER || NETCOREAPP
using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using System.Linq;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.ProtectionType
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.
@@ -24,29 +26,29 @@ namespace BurnOutSharp.ProtectionType
public class CDGuard : IPathCheck, IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
// TODO: Investigate the numerous ".guard" sections present in "Randevu.exe" in Redump entry 97142.
// Get the export directory table
if (pex.ExportTable?.ExportDirectoryTable != null)
if (pex.Model.ExportTable?.ExportDirectoryTable != null)
{
// Found in "cdguard.dll" in Redump entry 97142 and IA item "pahgeby-he3hakomkou".
bool match = pex.ExportTable.ExportDirectoryTable.Name?.Equals("cdguard.dll", StringComparison.OrdinalIgnoreCase) == true;
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.ImportTable?.ImportDirectoryTable != null)
if (pex.Model.ImportTable?.ImportDirectoryTable != null)
{
// Found in "Randevu.exe" in Redump entry 97142.
bool match = pex.ImportTable.ImportDirectoryTable.Any(idte => idte.Name?.Equals("cdguard.dll", StringComparison.OrdinalIgnoreCase) == true);
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";
}
@@ -55,24 +57,28 @@ namespace BurnOutSharp.ProtectionType
}
/// <inheritdoc/>
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
#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 PathMatchSet(new PathMatch("cdguard.dll", useEndsWith: true), "CD-Guard Copy Protection System"),
new(new FilePathMatch("cdguard.dll"), "CD-Guard Copy Protection System"),
};
return MatchUtil.GetAllMatches(files, matchers, any: true);
}
/// <inheritdoc/>
public string CheckFilePath(string path)
public string? CheckFilePath(string path)
{
var matchers = new List<PathMatchSet>
{
// Found in Redump entry 97142.
new PathMatchSet(new PathMatch("cdguard.dll", useEndsWith: true), "CD-Guard Copy Protection System"),
new(new FilePathMatch("cdguard.dll"), "CD-Guard Copy Protection System"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);

View File

@@ -1,20 +1,20 @@
using System;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.ProtectionType
namespace BinaryObjectScanner.Protection
{
public class CDKey : IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
string name = pex.InternalName;
var name = pex.InternalName;
if (name?.Equals("CDKey", StringComparison.OrdinalIgnoreCase) == true)
return "CD-Key / Serial";

View File

@@ -1,10 +1,12 @@
using System.Collections.Concurrent;
#if NET40_OR_GREATER || NETCOREAPP
using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.ProtectionType
namespace BinaryObjectScanner.Protection
{
/// <summary>
/// CD-Lock (https://www.cdmediaworld.com/hardware/cdrom/cd_protections_dummy_files.shtml) is a copy protection most commonly used in Europe and active from 1998-2001 that seems to have a resemblance to Bitpool in a few ways.
@@ -27,10 +29,10 @@ namespace BurnOutSharp.ProtectionType
public class CDLock : IPathCheck, IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
@@ -43,7 +45,7 @@ namespace BurnOutSharp.ProtectionType
// Found in game executables protected with CD-Lock (Redump entries 24287 and 31615).
// TODO: Check for possible false postives (Redump entry 97942).
// 2 + (char)0xF2 + (char)0x02 + (char)0x82 + (char)0xC3 + (char)0xBC + (char)0x0B + $ + (char)0x99 + (char)0xAD + 'C + (char)0xE4 + (char)0x9D + st + (char)0x99 + (char)0xFA + 2$ + (char)0x9D + )4 + (char)0xFF + t
new ContentMatchSet(new byte?[]
new(new byte?[]
{
0x32, 0xF2, 0x02, 0x82, 0xC3, 0xBC, 0x0B, 0x24,
0x99, 0xAD, 0x27, 0x43, 0xE4, 0x9D, 0x73, 0x74,
@@ -52,8 +54,8 @@ namespace BurnOutSharp.ProtectionType
}, "CD-Lock"),
};
string match = MatchUtil.GetFirstMatch(file, dataSectionRaw, matchers, includeDebug);
if (!string.IsNullOrWhiteSpace(match))
var match = MatchUtil.GetFirstMatch(file, dataSectionRaw, matchers, includeDebug);
if (!string.IsNullOrEmpty(match))
return match;
}
@@ -61,14 +63,18 @@ namespace BurnOutSharp.ProtectionType
}
/// <inheritdoc/>
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
#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 PathMatchSet(new PathMatch("CONFIG.AFP", useEndsWith: true), "CD-Lock"),
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).
};
@@ -77,14 +83,14 @@ namespace BurnOutSharp.ProtectionType
}
/// <inheritdoc/>
public string CheckFilePath(string path)
public string? CheckFilePath(string path)
{
var matchers = new List<PathMatchSet>
{
// TODO: Determine if there's any consistency in the naming of the additional AFP files.
// Found in every confirmed sample of CD-Lock, generally (but not always) appears to include markers relating to the additional AFP files present (Redump entries 24287 and 31615).
new PathMatchSet(new PathMatch("CONFIG.AFP", useEndsWith: true), "CD-Lock"),
new(new 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).
};

View File

@@ -1,9 +1,11 @@
using System.Collections.Concurrent;
#if NET40_OR_GREATER || NETCOREAPP
using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
using BinaryObjectScanner.Interfaces;
using SabreTools.Matching;
namespace BurnOutSharp.ProtectionType
namespace BinaryObjectScanner.Protection
{
/// <summary>
/// CD-Protector is a form of DRM that allows users to create their own copy protected discs.
@@ -17,7 +19,11 @@ namespace BurnOutSharp.ProtectionType
public class CDProtector : IPathCheck
{
/// <inheritdoc/>
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
#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>
{
@@ -26,20 +32,20 @@ namespace BurnOutSharp.ProtectionType
// "_cdp32.dat" is actually an archive that contains the original executable.
// Another EXE is created, with the name of the original executable. I'm not sure what this executable does, but it appears to be compressed with NeoLite.
// TODO: Invesitage if this EXE itself can be detected in any way.
new PathMatchSet(new PathMatch("_cdp16.dat", useEndsWith: true), "CD-Protector"),
new PathMatchSet(new PathMatch("_cdp16.dll", useEndsWith: true), "CD-Protector"),
new PathMatchSet(new PathMatch("_cdp32.dat", useEndsWith: true), "CD-Protector"),
new PathMatchSet(new PathMatch("_cdp32.dll", useEndsWith: true), "CD-Protector"),
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 PathMatchSet(new PathMatch("Track#1 - Track#2 Cd-Protector.wav", useEndsWith: true), "CD-Protector"),
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)
public string? CheckFilePath(string path)
{
var matchers = new List<PathMatchSet>
{
@@ -48,13 +54,13 @@ namespace BurnOutSharp.ProtectionType
// "_cdp32.dat" is actually an archive that contains the original executable.
// Another EXE is created, with the name of the original executable. I'm not sure what this executable does, but it appears to be compressed with NeoLite.
// TODO: Invesitage if this EXE itself can be detected in any way.
new PathMatchSet(new PathMatch("_cdp16.dat", useEndsWith: true), "CD-Protector"),
new PathMatchSet(new PathMatch("_cdp16.dll", useEndsWith: true), "CD-Protector"),
new PathMatchSet(new PathMatch("_cdp32.dat", useEndsWith: true), "CD-Protector"),
new PathMatchSet(new PathMatch("_cdp32.dll", useEndsWith: true), "CD-Protector"),
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 PathMatchSet(new PathMatch("Track#1 - Track#2 Cd-Protector.wav", useEndsWith: true), "CD-Protector"),
new(new FilePathMatch("Track#1 - Track#2 Cd-Protector.wav"), "CD-Protector"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);

View File

@@ -1,31 +1,31 @@
using System.Collections.Generic;
using System.Linq;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Wrappers;
using BinaryObjectScanner.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BurnOutSharp.ProtectionType
namespace BinaryObjectScanner.Protection
{
public class CDSHiELDSE : IPortableExecutableCheck
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
{
// Get the sections from the executable, if possible
var sections = pex?.SectionTable;
var sections = pex.Model.SectionTable;
if (sections == null)
return null;
// TODO: Indicates Hypertech Crack Proof as well?
//// Get the import directory table
//if (pex.ImportTable?.ImportDirectoryTable != null)
//if (pex.Model.ImportTable?.ImportDirectoryTable != null)
//{
// bool match = pex.ImportTable.ImportDirectoryTable.Any(idte => idte.Name == "KeRnEl32.dLl");
// 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
List<string> strs = pex.GetFirstSectionStrings("code") ?? pex.GetFirstSectionStrings("CODE");
var strs = pex.GetFirstSectionStrings("code") ?? pex.GetFirstSectionStrings("CODE");
if (strs != null)
{
if (strs.Any(s => s.Contains("~0017.tmp")))

Some files were not shown because too many files have changed in this diff Show More