Compare commits

..

210 Commits
1.4.1 ... 1.6.0

Author SHA1 Message Date
Matt Nadareski
6740011c11 Bump version to 1.6.0 2021-04-09 09:32:12 -07:00
SilasLaspada
732078f24d Improve Steam detection (#35)
* Improve Steam detection

* Alphabetize the order of detections
2021-04-03 21:25:22 -07:00
Matt Nadareski
5218aaaeb1 Remove all uses of Pause in Valve 2021-04-03 20:49:53 -07:00
Matt Nadareski
6b9df94613 Remove unused library from README 2021-04-02 16:02:39 -07:00
Matt Nadareski
b1ac88fc20 Use submodule for WixToolset.Dtf 2021-04-02 16:01:23 -07:00
Matt Nadareski
ca83019a58 Move wix mirror to backup 2021-04-02 15:51:46 -07:00
Matt Nadareski
d68272a5bb Use submodule for stormlibsharp
This also gets CascLibSharp for free. Maybe keep that in mind
2021-04-02 15:48:57 -07:00
Matt Nadareski
6ab0fd5e3b Move StormLibSharp mirror to backup 2021-04-02 15:38:59 -07:00
Matt Nadareski
a0034b8fa8 Use submodule for hllib 2021-04-02 15:34:47 -07:00
Matt Nadareski
a884737242 Move HLLib mirror to backup 2021-04-02 15:19:00 -07:00
Matt Nadareski
c7b3776386 Use submodule for libmspack4n 2021-04-02 15:17:26 -07:00
Matt Nadareski
b387c77179 Move libmspack4n mirror to backup 2021-04-02 15:09:56 -07:00
Matt Nadareski
f6b58223de Replace LessIO code mirror with submodule 2021-04-02 15:08:05 -07:00
Matt Nadareski
8960ad3b16 Backup current LessIO code mirror 2021-04-02 14:54:46 -07:00
Matt Nadareski
28e95f9eb7 Slight SolidShield cleanup 2021-04-01 15:00:22 -07:00
Matt Nadareski
0bf5065cbc Add future work note 2021-04-01 14:53:25 -07:00
Matt Nadareski
579c9c0f84 Fix missing SafeDisc version 2021-04-01 11:38:32 -07:00
Matt Nadareski
b2e8b66eae Fix SmartE overmatching 2021-04-01 11:20:13 -07:00
Matt Nadareski
80e71f43de Try to make structs more marshalable 2021-04-01 11:09:20 -07:00
Matt Nadareski
e0497e6fee Migrate SafeDisc to modern path checking 2021-04-01 10:38:50 -07:00
Matt Nadareski
18a78f44c0 Make EVORE a bit safer 2021-04-01 10:36:03 -07:00
Matt Nadareski
f9e7fd5725 Fix marshalling issue 2021-04-01 10:34:53 -07:00
Matt Nadareski
e9c1a170ad Move generic CD key to own file 2021-03-31 19:07:00 -07:00
SilasLaspada
9ce84c75dd Comment out an overmatching definition (#34) 2021-03-31 18:58:34 -07:00
Matt Nadareski
0bd5339b78 Update to UnshieldSharp 1.5.0 2021-03-29 14:35:06 -07:00
Matt Nadareski
e06b1987b9 Update to WiseUnpacker 1.0.2 2021-03-29 14:17:42 -07:00
Matt Nadareski
c8b271ac76 Port over executable header code from Wise 2021-03-25 15:53:45 -07:00
Matt Nadareski
1672c73a57 Fix PECompact scanning
Thanks Silas for noticing the regression
2021-03-25 15:25:15 -07:00
Matt Nadareski
32f6e0e8fc Further making test executable a bit nicer 2021-03-24 20:50:58 -07:00
Matt Nadareski
5c21de5a0f Make the test executable a bit nicer 2021-03-23 16:55:19 -07:00
Matt Nadareski
9f40a8c4c0 Perform some post-removal cleanup 2021-03-23 16:43:23 -07:00
Matt Nadareski
c179f29e2e Remove .NET Framework 4.7.2, update SharpCompress 2021-03-23 16:37:21 -07:00
Matt Nadareski
f9d6fce3bd Reduce boilerplate for directory checks 2021-03-23 13:35:12 -07:00
Matt Nadareski
aa83896963 Final batch of first pass for path check conversions 2021-03-23 10:36:14 -07:00
Matt Nadareski
7d13b8c9db Optimize checking with better caching 2021-03-23 10:04:09 -07:00
Matt Nadareski
921292e077 Static list of content matchers
This also includes some more path matcher conversions that I couldn't reasonably split out
2021-03-23 09:52:09 -07:00
Matt Nadareski
c3e7f0b99f Return empty not null 2021-03-23 08:47:36 -07:00
Matt Nadareski
b9cc5e9ada Second batch of path check conversions 2021-03-22 23:02:01 -07:00
Matt Nadareski
76d76b2bf2 Convert a few more path checks 2021-03-22 22:23:55 -07:00
Matt Nadareski
532e912a2d Accidental comment issue 2021-03-22 22:12:25 -07:00
Matt Nadareski
8ada667dfe Be consistent with var naming 2021-03-22 22:11:01 -07:00
Matt Nadareski
28a4f7ce82 File path should only get first match 2021-03-22 22:09:35 -07:00
Matt Nadareski
3a66183d0e Convert AACS to use new matching 2021-03-22 22:07:14 -07:00
Matt Nadareski
7f91346878 Fix assumptions for path matching 2021-03-22 22:06:55 -07:00
Matt Nadareski
2af0dc4a8c Don't include PDBs 2021-03-22 21:38:49 -07:00
Matt Nadareski
5240f2eb70 Simplify util method naming 2021-03-22 21:32:58 -07:00
Matt Nadareski
f25800510b MatchSet is abstract 2021-03-22 21:30:30 -07:00
Matt Nadareski
6400c954ef Split matchers more cleanly, comment better 2021-03-22 21:25:14 -07:00
Matt Nadareski
e43423d2c9 Fix misleading version results 2021-03-22 16:25:40 -07:00
Matt Nadareski
bc613a0413 Fix build 2021-03-22 11:44:16 -07:00
Matt Nadareski
e47a52dbe0 Use framework in even more content protections 2021-03-22 11:43:51 -07:00
Matt Nadareski
da165345b6 Use framework for more content protections 2021-03-22 11:13:14 -07:00
Matt Nadareski
8ea54328ef Use framework for WZ-SFX v2 checks 2021-03-22 10:22:56 -07:00
Matt Nadareski
a4aeebee68 Split content and path version checks 2021-03-22 10:04:33 -07:00
Matt Nadareski
95a61c3b28 Add path matching to util 2021-03-22 09:55:29 -07:00
Matt Nadareski
9aadf5e948 Add invariance 2021-03-22 01:26:33 -07:00
Matt Nadareski
129ac1bb78 More granular on path search 2021-03-22 01:18:49 -07:00
Matt Nadareski
e4278c55b7 Add path matching matcher 2021-03-22 01:11:59 -07:00
Matt Nadareski
7aca58a6c9 Better split matching code, fix UPX name 2021-03-22 00:41:18 -07:00
Matt Nadareski
ea022de022 Fix a couple things:
- Fix PECompact 2 version string
- Fix UPX (NOS Variant) over-matching
2021-03-21 23:14:37 -07:00
Matt Nadareski
bb4f16d91f Use content matching helper, part 6 2021-03-21 22:45:06 -07:00
Matt Nadareski
f1c165845f Merge branch 'master' of https://github.com/mnadareski/BurnOutSharp 2021-03-21 22:37:25 -07:00
Matt Nadareski
15ae2441c3 Use content matching helper, part 5 2021-03-21 22:37:16 -07:00
SilasLaspada
6b8f8957de Add Advanced Installer Detection (#32)
* Add Advanced Installer Detection

* Updated README

* Address comments
2021-03-21 22:36:01 -07:00
Matt Nadareski
cf9bd99f3d Use content matching helper, part 4 2021-03-21 22:19:38 -07:00
SilasLaspada
557114d92d Add CExe detection (#30)
* Add CExe detection

* Optimize check
2021-03-21 21:39:01 -07:00
Matt Nadareski
d01826ffa4 Use content matching helper, part 3 2021-03-21 15:34:19 -07:00
Matt Nadareski
7e3ef544f0 Use content matching helper, part 2 2021-03-21 15:24:23 -07:00
Matt Nadareski
ab07eb96ce Use content matching helper, part 1 2021-03-21 14:30:37 -07:00
Matt Nadareski
7c53eaab13 Merge branch 'master' of https://github.com/mnadareski/BurnOutSharp 2021-03-21 14:04:19 -07:00
SilasLaspada
fad7d87282 Add WinRAR SFX detection and extraction (#31) 2021-03-21 14:03:47 -07:00
Matt Nadareski
578519169a Add helper method for simple content matches 2021-03-20 22:37:56 -07:00
Matt Nadareski
fe106d23ec Fix BD+ in reverse 2021-03-20 22:28:57 -07:00
Matt Nadareski
e1669f031f BD+ using BitConverter 2021-03-20 22:00:38 -07:00
SilasLaspada
9bff6d5fe1 Improve version detection (#29)
* Improve version detection

* Address comments

* Address comments

Co-authored-by: Matt Nadareski <mnadareski@outlook.com>
2021-03-20 21:29:19 -07:00
Matt Nadareski
544aaed9da Modify array finding, part 2 2021-03-20 20:47:56 -07:00
Matt Nadareski
b6b7a5e7aa Clean up terminology 2021-03-20 19:23:59 -07:00
Matt Nadareski
cdc4d509ee Modify array finding, part 1 2021-03-20 19:00:22 -07:00
Matt Nadareski
07882f7632 Create and use manifesr version utility 2021-03-20 17:34:31 -07:00
Matt Nadareski
deb3c01362 Merge branch 'master' of https://github.com/mnadareski/BurnOutSharp 2021-03-20 16:26:19 -07:00
SilasLaspada
a51b16aed2 Add Setup Factory detection (#28)
* Add Setup Factory detection

* Improve version detection

* Address comments
2021-03-20 16:18:17 -07:00
Matt Nadareski
0e7d98158e Split out generic online registration 2021-03-20 13:29:46 -07:00
Matt Nadareski
56d8518ee4 Comment out over-matching EA check 2021-03-19 17:31:53 -07:00
Matt Nadareski
8897fd8650 Make SafeDisc more like Tages path check 2021-03-19 15:56:07 -07:00
Matt Nadareski
6f811e43d0 Split method in Scanner too 2021-03-19 15:48:53 -07:00
Matt Nadareski
a2888e3371 Split IPathCheck method 2021-03-19 15:41:49 -07:00
SilasLaspada
30d3312d87 Added BD+ version detection (#26)
* Added BD+ version detection

* Address Comments

* Address comment

* Remove unnecessary assignment
2021-03-18 10:50:45 -07:00
SilasLaspada
854a257fd6 Implement AACS version detection (#25)
* Implement AACS version detection

* Address comments

* Address more comments

* Address comment
2021-03-17 22:27:37 -07:00
Matt Nadareski
28344c6e3f Add WinZip SFX to readme 2021-03-14 14:39:31 -07:00
SilasLaspada
11a82addd8 Implement BD+ detection (#24) 2021-03-14 14:23:24 -07:00
Matt Nadareski
152f6c7051 Reduce size of 32-bit checks 2021-03-14 14:16:54 -07:00
Matt Nadareski
bedbceafa7 Use NE headers for all 16-bit versions 2021-03-14 13:55:26 -07:00
Matt Nadareski
2f19bf7ceb SFX cleanup and add missing check 2021-03-14 00:19:10 -08:00
SilasLaspada
a39ae9facf Add support for WinZip SFX archives (#23)
* Add inital check for WinZip SFX archives

Every version of WinZip SFX has the string "WinZip Self-Extractor" in it,

* Add basic version detection

Versions 3+ and 2.x are identified radically differently, so make separate methods for them.

* Implement version 3+ detection

Should be very thorough detection, detects every 3+ file I have accurately.

* Cleanup code

General clanup

* Improve version 3+ detection

Use an XML string to determine the version.

* Harden against false positives

* Implement basic extraction

* Partial 2.X version detection

Very crude but effective 2.X detection for 2.0 versions

* Add version detection for 2.1 RC2 variants

* Add 2.1 version detection

* Add 2.2 version detection

Aside from clean-ups, this is the final functional addition

* Address comments
2021-03-13 20:18:03 -08:00
Matt Nadareski
56b234bc96 A little cleanup 2021-03-02 15:21:14 -08:00
Matt Nadareski
633fe23b80 Reflection
This change eliminates the need to explicitly list out every single protection in the same way now that we have interfaces that we can rely on.
2021-03-02 15:10:52 -08:00
Matt Nadareski
2867ce2e9a Add more EA CDKey checks (fixes #21) 2021-03-02 13:48:10 -08:00
Matt Nadareski
ac0e5e95a9 Add note to RPT scan 2021-03-02 13:34:15 -08:00
Matt Nadareski
e3bed19e79 Import WixToolset code as external 2021-03-02 13:09:15 -08:00
Matt Nadareski
73aae8118f Wrap in libmspack4n and LessIO as external code 2021-03-02 12:14:14 -08:00
Matt Nadareski
b3671a430e Swap order of params for IPathCheck 2021-02-26 11:02:10 -08:00
Matt Nadareski
54465ff4e7 Move both installers to packers 2021-02-26 09:34:07 -08:00
Matt Nadareski
52eef84374 Make InnoSetup like WiseInstaller 2021-02-26 09:32:41 -08:00
Matt Nadareski
f4310206e9 Add IScannable interface 2021-02-26 09:26:23 -08:00
Matt Nadareski
7cfa9649e4 Add IContentCheck interface 2021-02-26 01:26:49 -08:00
Matt Nadareski
c6eaafebbe Add IPathCheck interface 2021-02-26 00:32:09 -08:00
Matt Nadareski
df1e14b6c9 Rename NOS variant of UPX 2021-02-25 13:38:13 -08:00
Matt Nadareski
ad2d854969 Add versioned NOS check, fix naming 2021-02-25 11:27:08 -08:00
Matt Nadareski
61202a87fb Add UPX detection for odd cases 2021-02-25 11:13:57 -08:00
Matt Nadareski
9ebbeaed0f Make EA CDKey checks more robust 2021-02-23 13:16:25 -08:00
Matt Nadareski
aebc139d52 I lied, keep it separate 2021-02-20 22:13:48 -08:00
Matt Nadareski
0e82eea891 Origin is an EA protection 2021-02-20 22:06:18 -08:00
Matt Nadareski
7ec76acf2f Remove Cucko until more investigation 2021-02-20 13:16:52 -08:00
Matt Nadareski
21f17791ff No... that's not right 2021-02-19 21:26:49 -08:00
Matt Nadareski
fff5f2610a Add Cucko notes 2021-02-19 10:14:02 -08:00
Matt Nadareski
d574fb5e44 Fix link to MPF in README 2021-01-25 09:53:18 -08:00
Matt Nadareski
d8aacbcc5d Treat Wise internally a bit strangely 2021-01-25 09:51:16 -08:00
Matt Nadareski
6467ef97d5 Only scan Wise internals if scanning archives 2021-01-24 21:23:05 -08:00
Matt Nadareski
5ec4872b36 Fix framework string 2021-01-22 11:25:24 -08:00
Matt Nadareski
30bfff833f Bump version to 1.5.1 2021-01-22 11:10:19 -08:00
Matt Nadareski
e37d5a80ab Add .NET 5.0 target framework 2021-01-21 13:17:59 -08:00
Matt Nadareski
df1081507d Thrown exceptions get logged to file in Test 2021-01-21 13:17:34 -08:00
Matt Nadareski
a140e1c444 Fix MPQ extraction (fixes #16) 2021-01-21 11:54:05 -08:00
Matt Nadareski
f9a990b27b Rename this to be more accurate 2020-11-12 22:47:33 -08:00
Matt Nadareski
b841c7aa94 More comments in EVORE for later 2020-11-12 22:40:50 -08:00
Matt Nadareski
554520ae1f Less EVORE code to fail 2020-11-12 22:33:18 -08:00
Matt Nadareski
3155b3fe41 Move EVORE things around 2020-11-12 22:10:45 -08:00
Matt Nadareski
e52cfd244a Little more reorganization 2020-11-12 22:00:50 -08:00
Matt Nadareski
c52b22fb4e Split things out for executables 2020-11-12 21:58:06 -08:00
Matt Nadareski
b3f72bbbe1 Add EVORE note, VSCode stuff 2020-11-12 21:35:43 -08:00
Matt Nadareski
9ebcfdba53 Bump version to 1.5.0 2020-11-03 21:15:36 -08:00
Matt Nadareski
15f020cb06 Fix textfile content scan 2020-11-03 14:57:23 -08:00
Matt Nadareski
4b387f86c1 Hook up MediaMax to scan 2020-11-03 14:47:15 -08:00
Matt Nadareski
1a3a73a86d Add MediaMax CD-3 content detections 2020-11-03 14:41:05 -08:00
Matt Nadareski
31eff196e6 Forgot to fix path in Wise 2020-11-02 15:18:03 -08:00
Matt Nadareski
828d3403f1 Upgrade UnshieldSharp, re-enable ISCab for Core 2020-11-01 23:13:35 -08:00
Matt Nadareski
28d6d06033 Unshield uses zlib.net which isn't .NET Core... 2020-11-01 22:13:46 -08:00
Matt Nadareski
0a2477e1b3 Remove console writes, make LibCrypt read better 2020-11-01 21:53:42 -08:00
Matt Nadareski
01451d7009 A bit of cleanup 2020-11-01 16:01:45 -08:00
Matt Nadareski
cff9582bf5 Comment out one of the 3PLock checks 2020-11-01 14:30:32 -08:00
Matt Nadareski
428d839700 Remove outdated TODOs 2020-11-01 14:18:01 -08:00
Matt Nadareski
50db0044b0 Fix MSCab corner case of trailing periods 2020-11-01 14:06:46 -08:00
SilasLaspada
68b1ec7b3f Fix name of INTENIUM protection (#15)
Change ITENIUM to INTENIUM
2020-11-01 10:45:30 -08:00
Matt Nadareski
fd524e1d5c List archive formats in README 2020-10-31 23:57:02 -07:00
Matt Nadareski
ef581c3f36 Add note for MS-CAB 2020-10-31 23:29:27 -07:00
Matt Nadareski
64e67b8daa Gate non-core compatible includes 2020-10-31 23:17:03 -07:00
Matt Nadareski
8624350b82 Why am I doing this? 2020-10-31 22:44:26 -07:00
Matt Nadareski
e3b32fd974 Clear empty keys as you go 2020-10-31 21:20:16 -07:00
Matt Nadareski
6f789d2454 Fix output of in-archive files 2020-10-31 14:57:47 -07:00
Matt Nadareski
d365dd1164 More consistent naming 2020-10-31 14:48:25 -07:00
Matt Nadareski
aa3afd676b Add option for including packers in scan 2020-10-31 14:46:08 -07:00
Matt Nadareski
69a04ff825 Hook up ITENIUM 2020-10-31 14:43:27 -07:00
Matt Nadareski
938e1f94bb Fix CDCheck message (non-false-positive in XCP) 2020-10-31 14:19:41 -07:00
Matt Nadareski
3eda785a5a Skip files with no protection on output 2020-10-31 14:16:51 -07:00
Matt Nadareski
6b895fa7c8 Fix invalid UPX packing versions 2020-10-31 14:15:33 -07:00
Matt Nadareski
45e10a84ae Strip temp paths, add archive name as prefix 2020-10-31 14:14:35 -07:00
Matt Nadareski
81f0400790 Pass-thru scanner, better return types 2020-10-31 14:00:31 -07:00
Matt Nadareski
df90583f73 Scanner shouldn't include path intrinsically 2020-10-31 13:20:54 -07:00
Matt Nadareski
5b980e138a Add scanner to all archive signatures (nw) 2020-10-31 00:06:41 -07:00
Matt Nadareski
0dd71d72ca Add new, but unused, Scanner class 2020-10-30 23:56:27 -07:00
Matt Nadareski
a7364eab67 Comments and a better guard 2020-10-30 21:43:40 -07:00
Matt Nadareski
99013e3448 Separate out archive and non-archive in code 2020-10-30 21:14:07 -07:00
Matt Nadareski
7f5e93db95 Fix 3PLock scanning finally 2020-10-30 17:02:58 -07:00
Matt Nadareski
4aa9662ebe Update README 2020-10-30 09:16:16 -07:00
Matt Nadareski
68dc6c3139 Region-ize the executable scans 2020-10-30 09:11:17 -07:00
Matt Nadareski
8b99577c66 New namespace for packers 2020-10-30 09:09:16 -07:00
Matt Nadareski
20ea1b4427 Fix formatting 2020-10-30 09:02:48 -07:00
SilasLaspada
cf0c6126b9 Add support for detecting NSIS (#14) 2020-10-30 08:56:34 -07:00
Matt Nadareski
49eef3f45a Update README with changes 2020-10-29 11:07:00 -07:00
Matt Nadareski
23e852bbdb Add first ITENIUM check, not hooked up 2020-10-29 11:01:52 -07:00
Matt Nadareski
f182dccbf2 21 -> 321, add a couple protection notes 2020-10-29 10:05:56 -07:00
Matt Nadareski
2d2cff4d0e Add MSI extraction and scanning 2020-10-28 22:51:33 -07:00
Matt Nadareski
c0621a83cb Enable 21Studios scan 2020-10-28 22:51:14 -07:00
Matt Nadareski
3c5da9f7ec Add 21Studios activator detection 2020-10-28 22:49:55 -07:00
Matt Nadareski
a7b5288277 One more EA CDKey detection 2020-10-28 21:03:45 -07:00
Matt Nadareski
fee980e048 CD Check has a valid case again 2020-10-28 16:33:20 -07:00
Matt Nadareski
21eac43e9f Remove unused vars 2020-10-28 13:31:15 -07:00
Matt Nadareski
b55698004c Make it blindingly obvious the files are from an archive 2020-10-28 13:25:58 -07:00
Matt Nadareski
40bdb9b2d9 Use 'i+1' during progress 2020-10-28 13:21:42 -07:00
Matt Nadareski
dfabb1a244 Strip out temp paths from results and progress 2020-10-28 13:17:26 -07:00
Matt Nadareski
cd5f9561bf Pre-scan progress indicator 2020-10-28 13:06:45 -07:00
Matt Nadareski
d8d6fac67e Fix issue with libmspack4n, again 2020-10-28 12:34:33 -07:00
Matt Nadareski
965c32482d Remove note 2020-10-28 12:24:04 -07:00
Matt Nadareski
c8c9d4ac64 Scan whole path for Wise 2020-10-28 12:23:25 -07:00
Matt Nadareski
5f5abf8a14 Scan whole path for Valve 2020-10-28 12:22:29 -07:00
Matt Nadareski
406791b938 Scan whole path for TAP 2020-10-28 12:21:59 -07:00
Matt Nadareski
eb2e6e7029 Scan whole path for 7zip 2020-10-28 12:21:44 -07:00
Matt Nadareski
9f0b41c6aa Scan whole path for RAR 2020-10-28 12:21:26 -07:00
Matt Nadareski
e2b72d8a5b Scan whole folder for PKZIP 2020-10-28 12:20:55 -07:00
Matt Nadareski
9802840309 Scan whole folder for MPQ 2020-10-28 12:20:36 -07:00
Matt Nadareski
4a6e2fd62d Scan whole folder for MSCab 2020-10-28 12:20:07 -07:00
Matt Nadareski
79c706e35a Scan whole folder for ISCab 2020-10-28 12:19:10 -07:00
Matt Nadareski
9be19d6925 Scan whole folder for BFPK 2020-10-28 12:17:10 -07:00
Matt Nadareski
0cfb9907d0 Make archives use full scan, not just content 2020-10-28 12:05:48 -07:00
Matt Nadareski
c18e9b3538 Combine EA protection checks, add/fix reg checks 2020-10-28 11:13:26 -07:00
Matt Nadareski
3754c3a65b Ensure mspack.dll exists for libmspack4n 2020-10-28 10:49:24 -07:00
Matt Nadareski
513a64df4c Rename Cucko and cleanup misc 2020-10-28 10:42:54 -07:00
Matt Nadareski
d18a51c7fa Add SolidShield wrapper check 2020-10-28 10:23:57 -07:00
Matt Nadareski
b43433b9ed Add more EA protection checks 2020-10-28 10:10:43 -07:00
Matt Nadareski
d553395f3f Reorder CDS path checks, change one to fit code 2020-10-27 17:13:35 -07:00
Matt Nadareski
28dbe8542b Add CDS content checks, fix XCP over-detection 2020-10-27 17:01:33 -07:00
Matt Nadareski
912e605dc8 Clarify SafeDisc version identifiers 2020-10-27 14:47:02 -07:00
Matt Nadareski
039982d02d Cleanup on Cactus for future dev work 2020-10-27 14:37:14 -07:00
Matt Nadareski
1a40c6cbb2 SafeDisc... one OR greater 2020-10-27 13:35:57 -07:00
Matt Nadareski
35921e3cac Be smarter about SecuROM strings 2020-10-26 23:30:35 -07:00
Matt Nadareski
a42040d644 Add UPX checking 2020-10-26 23:30:06 -07:00
Matt Nadareski
e1fb1c7bcf Minor renaming 2020-10-26 22:22:46 -07:00
Matt Nadareski
095de1441d Add XCP content checks (thanks to Silas) 2020-10-26 21:08:39 -07:00
Matt Nadareski
43cbafc0f5 Add better detection for XCP2 2020-10-26 20:34:47 -07:00
Matt Nadareski
9143e4c02c Disable CD Check in source due to false positives 2020-10-09 23:12:11 -07:00
169 changed files with 9631 additions and 7744 deletions

15
.gitmodules vendored Normal file
View File

@@ -0,0 +1,15 @@
[submodule "BurnOutSharp/External/LessIO"]
path = BurnOutSharp/External/LessIO
url = https://github.com/activescott/LessIO.git
[submodule "BurnOutSharp/External/libmspack4n"]
path = BurnOutSharp/External/libmspack4n
url = https://github.com/activescott/libmspack4n.git
[submodule "BurnOutSharp/External/hllib"]
path = BurnOutSharp/External/hllib
url = https://github.com/RavuAlHemio/hllib.git
[submodule "BurnOutSharp/External/stormlibsharp"]
path = BurnOutSharp/External/stormlibsharp
url = https://github.com/robpaveza/stormlibsharp.git
[submodule "BurnOutSharp/External/WixToolset"]
path = BurnOutSharp/External/WixToolset
url = https://github.com/wixtoolset/Dtf.git

27
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,27 @@
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Test/bin/Debug/netcoreapp3.1/Test.dll",
"args": [],
"cwd": "${workspaceFolder}/Test",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}

42
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,42 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/Test/Test.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/Test/Test.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"${workspaceFolder}/Test/Test.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}

View File

@@ -1,18 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net472;net48;netcoreapp3.1</TargetFrameworks>
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<Title>BurnOutSharp</Title>
<AssemblyName>BurnOutSharp</AssemblyName>
<Description>Port of BurnOut to C#, with additions</Description>
<Authors>Matt Nadareski;Gernot Knippen</Authors>
<Product>BurnOutSharp</Product>
<Copyright>Copyright (c)2005-2010 Gernot Knippen, Copyright (c)2018-2020 Matt Nadareski</Copyright>
<Copyright>Copyright (c)2005-2010 Gernot Knippen, Copyright (c)2018-2021 Matt Nadareski</Copyright>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<RepositoryUrl>https://github.com/mnadareski/BurnOutSharp</RepositoryUrl>
<Version>1.4.1</Version>
<AssemblyVersion>1.4.1</AssemblyVersion>
<FileVersion>1.04.1</FileVersion>
<Version>1.6.0</Version>
<AssemblyVersion>1.6.0</AssemblyVersion>
<FileVersion>1.6.0</FileVersion>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
</PropertyGroup>
@@ -22,14 +23,32 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LessIO" Version="0.6.16" />
<PackageReference Include="libmspack4n" Version="0.9.10" />
<PackageReference Include="SharpCompress" Version="0.26.0" />
<PackageReference Include="UnshieldSharp" Version="1.4.2.3" />
<PackageReference Include="WiseUnpacker" Version="1.0.1" />
<PackageReference Include="zlib.net" Version="1.0.4" />
<PackageReference Include="SharpCompress" Version="0.28.1" />
<PackageReference Include="UnshieldSharp" Version="1.5.0" />
<PackageReference Include="WiseUnpacker" Version="1.0.2" />
</ItemGroup>
<!-- These are needed for dealing with submodules -->
<PropertyGroup>
<DefaultItemExcludes>
$(DefaultItemExcludes);
**\AssemblyInfo.cs;
External\hllib\HLExtract\**\*;
External\hllib\HLExtract.Net\Program.cs;
External\hllib\HLLib\**\*;
External\LessIO\src\LessIO.Tests\**\*;
External\libmspack4n\lib\**\*;
External\libmspack4n\libmspack4ntest\**\*;
External\stormlibsharp\lib\**;
External\stormlibsharp\TestConsole\**;
External\WixToolset\src\Samples\**;
External\WixToolset\src\Tools\**;
External\WixToolset\src\WixToolset.Dtf.MSBuild\**;
External\WixToolset\src\WixToolset.Dtf.Resources\**;
External\WixToolset\src\WixToolsetTests.*\**
</DefaultItemExcludes>
</PropertyGroup>
<ItemGroup>
<None Include="LICENSE.txt" Pack="true" PackagePath="$(PackageLicenseFile)" />
</ItemGroup>
@@ -40,12 +59,6 @@
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
</None>
<None Include="*.pdb" Pack="true">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
</None>
</ItemGroup>
</ItemGroup>
</Project>

BIN
BurnOutSharp/CascLib.dll Normal file

Binary file not shown.

View File

@@ -19,45 +19,34 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using BurnOutSharp.ExecutableType.Microsoft;
namespace BurnOutSharp
{
internal static class EVORE
{
private struct Section
/// <summary>
/// Checks if the file contents that represent a PE is a DLL or an EXE
/// </summary>
/// <param name="fileContent">File contents to check</param>
/// <returns>True if the file is an EXE, false if it's a DLL</returns>
internal static bool IsEXE(byte[] fileContent)
{
public uint iVirtualSize;
public uint iVirtualOffset;
public uint iRawOffset;
int PEHeaderOffset = BitConverter.ToInt32(fileContent, 60);
short Characteristics = BitConverter.ToInt16(fileContent, PEHeaderOffset + 22);
// Check if file is dll
return (Characteristics & 0x2000) != 0x2000;
}
private const int WaitSeconds = 20;
private static Process StartSafe(string file)
{
if (file == null || !File.Exists(file))
return null;
Process startingprocess = new Process();
startingprocess.StartInfo.FileName = file;
startingprocess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
startingprocess.StartInfo.CreateNoWindow = true;
startingprocess.StartInfo.ErrorDialog = false;
try
{
startingprocess.Start();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
return startingprocess;
}
private static string MakeTempFile(byte[] fileContent, string sExtension = ".exe")
/// <summary>
/// Writes the file contents to a temporary file, if possible
/// </summary>
/// <param name="fileContent">File contents to write</param>
/// <param name="sExtension">Optional extension for the temproary file, defaults to ".exe"</param>
/// <returns>Name of the new temporary file, null on error</returns>
internal static string MakeTempFile(byte[] fileContent, string sExtension = ".exe")
{
string filei = Guid.NewGuid().ToString();
string tempPath = Path.Combine(Path.GetTempPath(), "tmp", $"{filei}{sExtension}");
@@ -82,21 +71,15 @@ namespace BurnOutSharp
return null;
}
private static bool IsEXE(byte[] fileContent)
/// <summary>
/// Copies all required DLLs for a given executable
/// </summary>
/// <param name="file">Temporary file path</param>
/// <param name="fileContent">File contents to read</param>
/// <returns>Paths for all of the copied DLLs, null on error</returns>
internal static string[] CopyDependentDlls(string file, byte[] fileContent)
{
int PEHeaderOffset = BitConverter.ToInt32(fileContent, 60);
short Characteristics = BitConverter.ToInt16(fileContent, PEHeaderOffset + 22);
// Check if file is dll
if ((Characteristics & 0x2000) == 0x2000)
return false;
else
return true;
}
private static string[] CopyDependentDlls(string file, byte[] fileContent)
{
Section[] sections = ReadSections(fileContent);
var sections = ReadSections(fileContent);
long lastPosition;
string[] saDependentDLLs = null;
@@ -152,295 +135,111 @@ namespace BurnOutSharp
return saDependentDLLs;
}
private static Section[] ReadSections(byte[] fileContent)
/// <summary>
/// Attempt to run an executable
/// </summary>
/// <param name="file">Executable to attempt to run</param>
/// <returns>Process representing the running executable, null on error</returns>
internal static Process StartSafe(string file)
{
if (file == null || !File.Exists(file))
return null;
Process startingprocess = new Process();
startingprocess.StartInfo.FileName = file;
startingprocess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
startingprocess.StartInfo.CreateNoWindow = true;
startingprocess.StartInfo.ErrorDialog = false;
try
{
startingprocess.Start();
}
catch
{
return null;
}
return startingprocess;
}
private static IMAGE_SECTION_HEADER[] ReadSections(byte[] fileContent)
{
if (fileContent == null)
return null;
uint PEHeaderOffset = BitConverter.ToUInt32(fileContent, 60);
ushort NumberOfSections = BitConverter.ToUInt16(fileContent, (int)PEHeaderOffset + 6);
Section[] sections = new Section[NumberOfSections];
var sections = new IMAGE_SECTION_HEADER[NumberOfSections];
int index = (int)PEHeaderOffset + 120 + 16 * 8;
for (int i = 0; i < NumberOfSections; i++)
{
index += 8;
uint ivs = BitConverter.ToUInt32(fileContent, index);
index += 4;
uint ivo = BitConverter.ToUInt32(fileContent, index);
index += 4;
index += 4;
uint iro = BitConverter.ToUInt32(fileContent, index);
index += 4;
index += 16;
sections[i] = new Section()
{
iVirtualSize = ivs,
iVirtualOffset = ivo,
iRawOffset = iro,
};
sections[i] = ReadSection(fileContent, index);
}
return sections;
}
private static uint RVA2Offset(uint RVA, Section[] sections)
private static IMAGE_SECTION_HEADER ReadSection(byte[] fileContent, int ptr)
{
int i = 0;
while (i != sections.Length)
try
{
if (sections[i].iVirtualOffset <= RVA && sections[i].iVirtualOffset + sections[i].iVirtualSize > RVA)
return RVA - sections[i].iVirtualOffset + sections[i].iRawOffset;
i++;
// Get the size of a section header for later
int sectionSize = Marshal.SizeOf<IMAGE_SECTION_HEADER>();
// If the contents are null or the wrong size, we can't read a section
if (fileContent == null || fileContent.Length < sectionSize)
return null;
// Create a new section and try our best to read one
IMAGE_SECTION_HEADER section = null;
IntPtr tempPtr = IntPtr.Zero;
try
{
// Get the pointer to where the section will go
tempPtr = Marshal.AllocHGlobal(sectionSize);
// If we couldn't get the space, just return null
if (tempPtr == IntPtr.Zero)
return null;
// Copy from the array to the new space
Marshal.Copy(fileContent, ptr, tempPtr, sectionSize);
// Get the new section and return
section = Marshal.PtrToStructure<IMAGE_SECTION_HEADER>(tempPtr);
}
catch
{
// We don't care what the error was
return null;
}
finally
{
if (tempPtr != IntPtr.Zero)
Marshal.FreeHGlobal(tempPtr);
}
return section;
}
catch
{
return null;
}
}
private static uint RVA2Offset(uint RVA, IMAGE_SECTION_HEADER[] sections)
{
for (int i = 0; i < sections.Length; i++)
{
if (sections[i] == null)
continue;
var section = sections[i];
if (section.VirtualAddress <= RVA && section.VirtualAddress + section.PhysicalAddress > RVA)
return RVA - section.VirtualAddress + section.PointerToRawData;
}
return 0;
}
#region "EVORE version-search-functions"
public static string SearchProtectDiscVersion(string file, byte[] fileContent)
{
string version = "";
DateTime timestart;
if (!IsEXE(fileContent))
return "";
string tempexe = MakeTempFile(fileContent);
string[] DependentDlls = CopyDependentDlls(file, fileContent);
try
{
File.Delete(Path.Combine(Path.GetTempPath(), "a*.tmp"));
}
catch { }
try
{
File.Delete(Path.Combine(Path.GetTempPath(), "PCD*.sys"));
}
catch { }
if (Directory.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ProtectDisc")))
{
try
{
File.Delete(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ProtectDisc", "p*.dll"));
}
catch { }
}
Process exe = StartSafe(tempexe);
if (exe == null)
return "";
Process[] processes = new Process[0];
timestart = DateTime.Now;
do
{
exe.Refresh();
string[] files = null;
//check for ProtectDisc 8.2-x
if (Directory.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ProtectDisc")))
{
files = Directory.GetFiles(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ProtectDisc"), "p*.dll");
}
if (files != null)
{
if (files.Length > 0)
{
FileVersionInfo fvinfo = FileVersionInfo.GetVersionInfo(files[0]);
if (fvinfo.FileVersion != "")
{
version = fvinfo.FileVersion.Replace(" ", "").Replace(",", ".");
//ProtectDisc 9 uses a ProtectDisc-Core dll version 8.0.x
if (version.StartsWith("8.0"))
version = "";
fvinfo = null;
break;
}
}
}
//check for ProtectDisc 7.1-8.1
files = Directory.GetFiles(Path.GetTempPath(), "a*.tmp");
if (files.Length > 0)
{
FileVersionInfo fvinfo = FileVersionInfo.GetVersionInfo(files[0]);
if (fvinfo.FileVersion != "")
{
version = fvinfo.FileVersion.Replace(" ", "").Replace(",", ".");
fvinfo = null;
break;
}
}
if (exe.HasExited)
break;
processes = Process.GetProcessesByName(exe.ProcessName);
if (processes.Length == 2)
{
processes[0].Refresh();
processes[1].Refresh();
if (processes[1].WorkingSet64 > exe.WorkingSet64)
exe = processes[1];
else if (processes[0].WorkingSet64 > exe.WorkingSet64) //else if (processes[0].Modules.Count > exe.Modules.Count)
exe = processes[0];
}
} while (processes.Length > 0 && DateTime.Now.Subtract(timestart).TotalSeconds < WaitSeconds);
Thread.Sleep(500);
if (!exe.HasExited)
{
processes = Process.GetProcessesByName(exe.ProcessName);
if (processes.Length == 2)
{
try
{
processes[0].Kill();
}
catch { }
processes[0].Close();
try
{
processes[1].Kill();
}
catch { }
}
else
{
exe.Refresh();
try
{
exe.Kill();
}
catch { }
}
}
exe.Close();
Thread.Sleep(500);
if (Directory.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ProtectDisc")))
{
try
{
File.Delete(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ProtectDisc", "p*.dll"));
}
catch { }
}
try
{
File.Delete(Path.Combine(Path.GetTempPath(), "a*.tmp"));
}
catch { }
try
{
File.Delete(Path.Combine(Path.GetTempPath(), "PCD*.sys"));
}
catch { }
File.Delete(tempexe);
if (DependentDlls != null)
{
for (int i = 0; i < DependentDlls.Length; i++)
{
try
{
File.Delete(DependentDlls[i]);
}
catch (Exception ex)
{
Console.WriteLine("!error while deleting file " + DependentDlls[i] + "; " + ex.Message);
}
}
}
return version;
}
public static string SearchSafeDiscVersion(string file, byte[] fileContent)
{
Process exe = new Process();
string version = "";
DateTime timestart;
if (!IsEXE(fileContent))
return "";
string tempexe = MakeTempFile(fileContent);
string[] DependentDlls = CopyDependentDlls(file, fileContent);
try
{
Directory.Delete(Path.Combine(Path.GetTempPath(), "~e*"), true);
}
catch { }
try
{
File.Delete(Path.Combine(Path.GetTempPath(), "~e*"));
}
catch { }
exe = StartSafe(tempexe);
if (exe == null)
return "";
timestart = DateTime.Now;
do
{
if (Directory.GetDirectories(Path.GetTempPath(), "~e*").Length > 0)
{
string[] files = Directory.GetFiles(Directory.GetDirectories(Path.GetTempPath(), "~e*")[0], "~de*.tmp");
if (files.Length > 0)
{
StreamReader sr;
try
{
sr = new StreamReader(files[0], Encoding.Default);
string FileContent = sr.ReadToEnd();
sr.Close();
int position = FileContent.IndexOf("%ld.%ld.%ld, %ld, %s,") - 1;
if (position > -1)
version = FileContent.Substring(position + 28, 12);
break;
}
catch { }
}
}
} while (!exe.HasExited && DateTime.Now.Subtract(timestart).TotalSeconds < WaitSeconds);
if (!exe.HasExited)
exe.Kill();
exe.Close();
try
{
Directory.Delete(Path.Combine(Path.GetTempPath(), "~e*"), true);
}
catch { }
try
{
File.Delete(Path.Combine(Path.GetTempPath(), "~e*"));
File.Delete(tempexe);
}
catch { }
if (DependentDlls != null)
{
for (int i = 0; i < DependentDlls.Length; i--)
{
try
{
File.Delete(DependentDlls[i]);
}
catch (Exception ex)
{
Console.WriteLine("!error while deleting file " + DependentDlls[i] + "; " + ex.Message);
}
}
}
return version;
}
#endregion
}
}

View File

@@ -0,0 +1,99 @@
namespace BurnOutSharp.ExecutableType.Microsoft
{
/// <summary>
/// All constant values needed for file header reading
/// </summary>
internal static class Constants
{
public const ushort IMAGE_DOS_SIGNATURE = 0x5A4D; // MZ
public const ushort IMAGE_OS2_SIGNATURE = 0x454E; // NE
public const ushort IMAGE_OS2_SIGNATURE_LE = 0x454C; // LE
public const uint IMAGE_NT_SIGNATURE = 0x00004550; // PE00
#region IMAGE_DOS_HEADER
public const ushort ENEWEXE = 0x40; // Value of E_LFARLC for new .EXEs
public const ushort ENEWHDR = 0x003C; // Offset in old hdr. of ptr. to new
public const ushort ERESWDS = 0x0010; // No. of reserved words (OLD)
public const ushort ERES1WDS = 0x0004; // No. of reserved words in e_res
public const ushort ERES2WDS = 0x000A; // No. of reserved words in e_res2
public const ushort ECP = 0x0004; // Offset in struct of E_CP
public const ushort ECBLP = 0x0002; // Offset in struct of E_CBLP
public const ushort EMINALLOC = 0x000A; // Offset in struct of E_MINALLOC
#endregion
#region IMAGE_OS2_HEADER
public const ushort NERESWORDS = 3; // 6 bytes reserved
public const ushort NECRC = 8; //Offset into new header of NE_CRC
#endregion
#region NewSeg
public const ushort NSALIGN = 9; // Segment data aligned on 512 byte boundaries
public const ushort NSLOADED = 0x0004; // ns_sector field contains memory addr
#endregion
#region RsrcNameInfo
public const ushort RSORDID = 0x8000; /* if high bit of ID set then integer id */
/* otherwise ID is offset of string from
the beginning of the resource table */
/* Ideally these are the same as the */
/* corresponding segment flags */
public const ushort RNMOVE = 0x0010; /* Moveable resource */
public const ushort RNPURE = 0x0020; /* Pure (read-only) resource */
public const ushort RNPRELOAD = 0x0040; /* Preloaded resource */
public const ushort RNDISCARD = 0xF000; /* Discard priority level for resource */
#endregion
#region IMAGE_OPTIONAL_HEADER
public const ushort IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
/* Directory Entries */
/* Export Directory */
public const byte IMAGE_DIRECTORY_ENTRY_EXPORT = 0;
/* Import Directory */
public const byte IMAGE_DIRECTORY_ENTRY_IMPORT = 1;
/* Resource Directory */
public const byte IMAGE_DIRECTORY_ENTRY_RESOURCE = 2;
/* Exception Directory */
public const byte IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3;
/* Security Directory */
public const byte IMAGE_DIRECTORY_ENTRY_SECURITY = 4;
/* Base Relocation Table */
public const byte IMAGE_DIRECTORY_ENTRY_BASERELOC = 5;
/* Debug Directory */
public const byte IMAGE_DIRECTORY_ENTRY_DEBUG = 6;
/* Description String */
public const byte IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7;
/* Machine Value (MIPS GP) */
public const byte IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8;
/* TLS Directory */
public const byte IMAGE_DIRECTORY_ENTRY_TLS = 9;
/* Load Configuration Directory */
public const byte IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10;
#endregion
#region IMAGE_SECTION_HEADER
public const int IMAGE_SIZEOF_SHORT_NAME = 8;
#endregion
#region IMAGE_RESOURCE_DATA_ENTRY
public const uint IMAGE_RESOURCE_DATA_IS_DIRECTORY = 0x80000000;
public const uint IMAGE_RESOURCE_NAME_IS_STRING = 0x80000000;
#endregion
}
}

View File

@@ -0,0 +1,384 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System;
namespace BurnOutSharp.ExecutableType.Microsoft
{
internal enum ExecutableType
{
Unknown,
NE,
PE,
}
/// <summary>
/// Format of NE_FLAGS(x):
///
/// p Not-a-process
/// x Unused
/// e Errors in image
/// x Unused
/// b Bound as family app
/// ttt Application type
/// f Floating-point instructions
/// 3 386 instructions
/// 2 286 instructions
/// 0 8086 instructions
/// P Protected mode only
/// p Per-process library initialization
/// i Instance data
/// s Solo data
/// </summary>
[Flags]
internal enum NeFlags : ushort
{
/// <summary>
/// Not a process
/// </summary>
NENOTP = 0x8000,
/// <summary>
/// Errors in image
/// </summary>
NEIERR = 0x2000,
/// <summary>
/// Bound as family app
/// </summary>
NEBOUND = 0x0800,
/// <summary>
/// Application type mask
/// </summary>
NEAPPTYP = 0x0700,
/// <summary>
/// Not compatible with P.M. Windowing
/// </summary>
NENOTWINCOMPAT = 0x0100,
/// <summary>
/// Compatible with P.M. Windowing
/// </summary>
NEWINCOMPAT = 0x0200,
/// <summary>
/// Uses P.M. Windowing API
/// </summary>
NEWINAPI = 0x0300,
/// <summary>
/// Floating-point instructions
/// </summary>
NEFLTP = 0x0080,
/// <summary>
/// 386 instructions
/// </summary>
NEI386 = 0x0040,
/// <summary>
/// 286 instructions
/// </summary>
NEI286 = 0x0020,
/// <summary>
/// 8086 instructions
/// </summary>
NEI086 = 0x0010,
/// <summary>
/// Runs in protected mode only
/// </summary>
NEPROT = 0x0008,
/// <summary>
/// Per-Process Library Initialization
/// </summary>
NEPPLI = 0x0004,
/// <summary>
/// Instance data
/// </summary>
NEINST = 0x0002,
/// <summary>
/// Solo data
/// </summary>
NESOLO = 0x0001,
}
/// <summary>
/// Format of NR_FLAGS(x):
///
/// xxxxx Unused
/// a Additive fixup
/// rr Reference type
/// </summary>
[Flags]
internal enum NrFlags : byte
{
/// <summary>
/// Additive fixup
/// </summary>
NRADD = 0x04,
/// <summary>
/// Reference type mask
/// </summary>
NRRTYP = 0x03,
/// <summary>
/// Internal reference
/// </summary>
NRRINT = 0x00,
/// <summary>
/// Import by ordinal
/// </summary>
NRRORD = 0x01,
/// <summary>
/// Import by name
/// </summary>
NRRNAM = 0x02,
/// <summary>
/// Operating system fixup
/// </summary>
NRROSF = 0x03,
}
/// <summary>
/// Format of NR_STYPE(x):
///
/// xxxxx Unused
/// sss Source type
////
/// </summary>
[Flags]
internal enum NrStype : byte
{
/// <summary>
/// Source type mask
/// </summary>
NRSTYP = 0x0f,
/// <summary>
/// lo byte
/// </summary>
NRSBYT = 0x00,
/// <summary>
/// 16-bit segment
/// </summary>
NRSSEG = 0x02,
/// <summary>
/// 32-bit pointer
/// </summary>
NRSPTR = 0x03,
/// <summary>
/// 16-bit offset
/// </summary>
NRSOFF = 0x05,
/// <summary>
/// 48-bit pointer
/// </summary>
NRSPTR48 = 0x0B,
/// <summary>
/// 32-bit offset
/// </summary>
NRSOFF32 = 0x0D,
}
/// <summary>
/// Format of NS_FLAGS(x)
///
/// x Unused
/// h Huge segment
/// c 32-bit code segment
/// d Discardable segment
/// DD I/O privilege level (286 DPL bits)
/// c Conforming segment
/// r Segment has relocations
/// e Execute/read only
/// p Preload segment
/// P Pure segment
/// m Movable segment
/// i Iterated segment
/// ttt Segment type
/// </summary>
[Flags]
internal enum NsFlags : ushort
{
/// <summary>
/// Segment type mask
/// </summary>
NSTYPE = 0x0007,
/// <summary>
/// Code segment
/// </summary>
NSCODE = 0x0000,
/// <summary>
/// Data segment
/// </summary>
NSDATA = 0x0001,
/// <summary>
/// Iterated segment flag
/// </summary>
NSITER = 0x0008,
/// <summary>
/// Movable segment flag
/// </summary>
NSMOVE = 0x0010,
/// <summary>
/// Shared segment flag
/// </summary>
NSSHARED = 0x0020,
/// <summary>
/// For compatibility
/// </summary>
NSPURE = 0x0020,
/// <summary>
/// Preload segment flag
/// </summary>
NSPRELOAD = 0x0040,
/// <summary>
/// Execute-only (code segment), or read-only (data segment)
/// </summary>
NSEXRD = 0x0080,
/// <summary>
/// Segment has relocations
/// </summary>
NSRELOC = 0x0100,
/// <summary>
/// Conforming segment
/// </summary>
NSCONFORM = 0x0200,
/// <summary>
/// I/O privilege level (286 DPL bits)
/// </summary>
NSDPL = 0x0C00,
/// <summary>
/// Left shift count for SEGDPL field
/// </summary>
SHIFTDPL = 10,
/// <summary>
/// Segment is discardable
/// </summary>
NSDISCARD = 0x1000,
/// <summary>
/// 32-bit code segment
/// </summary>
NS32BIT = 0x2000,
/// <summary>
/// Huge memory segment, length of segment and minimum allocation size are in segment sector units
/// </summary>
NSHUGE = 0x4000,
}
/// <summary>
/// Predefined Resource Types
/// </summary>
internal enum ResourceTypes : ushort
{
RT_CURSOR = 1,
RT_BITMAP = 2,
RT_ICON = 3,
RT_MENU = 4,
RT_DIALOG = 5,
RT_STRING = 6,
RT_FONTDIR = 7,
RT_FONT = 8,
RT_ACCELERATOR = 9,
RT_RCDATA = 10,
RT_MESSAGELIST = 11, // RT_MESSAGETABLE
RT_GROUP_CURSOR = 12,
RT_RESERVED_1 = 13, // Undefined
RT_GROUP_ICON = 14,
RT_RESERVED_2 = 15, // Undefined
RT_VERSION = 16,
RT_DLGINCLUDE = 17,
RT_PLUGPLAY = 19,
RT_VXD = 20,
RT_ANICURSOR = 21,
RT_NEWRESOURCE = 0x2000,
RT_NEWBITMAP = (RT_BITMAP |RT_NEWRESOURCE),
RT_NEWMENU = (RT_MENU |RT_NEWRESOURCE),
RT_NEWDIALOG = (RT_DIALOG |RT_NEWRESOURCE),
RT_ERROR = 0x7fff,
}
[Flags]
internal enum SectionCharacteristics : uint
{
CodeSection = 0x00000020,
InitializedDataSection = 0x00000040,
UninitializedDataSection = 0x00000080,
SectionCannotBeCached = 0x04000000,
SectionIsNotPageable = 0x08000000,
SectionIsShared = 0x10000000,
ExecutableSection = 0x20000000,
ReadableSection = 0x40000000,
WritableSection = 0x80000000,
}
[Flags]
internal enum TargetOperatingSystems : byte
{
/// <summary>
/// Unknown (any "new-format" OS)
/// </summary>
NE_UNKNOWN = 0x0,
/// <summary>
/// Microsoft/IBM OS/2 (default)
/// </summary>
NE_OS2 = 0x1,
/// <summary>
/// Microsoft Windows
/// </summary>
NE_WINDOWS = 0x2,
/// <summary>
/// Microsoft MS-DOS 4.x
/// </summary>
NE_DOS4 = 0x3,
/// <summary>
/// Windows 386
/// </summary>
NE_WIN386 = 0x4,
}
}

View File

@@ -0,0 +1,34 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
[StructLayout(LayoutKind.Sequential)]
internal class IMAGE_DATA_DIRECTORY
{
public uint VirtualAddress;
public uint Size;
public static IMAGE_DATA_DIRECTORY Deserialize(Stream stream)
{
var idd = new IMAGE_DATA_DIRECTORY();
idd.VirtualAddress = stream.ReadUInt32();
idd.Size = stream.ReadUInt32();
return idd;
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
/// <summary>
/// DOS 1, 2, 3 .EXE header
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal class IMAGE_DOS_HEADER
{
public ushort Magic; // 00 Magic number
public ushort LastPageBytes; // 02 Bytes on last page of file
public ushort Pages; // 04 Pages in file
public ushort Relocations; // 06 Relocations
public ushort HeaderParagraphSize; // 08 Size of header in paragraphs
public ushort MinimumExtraParagraphs; // 0A Minimum extra paragraphs needed
public ushort MaximumExtraParagraphs; // 0C Maximum extra paragraphs needed
public ushort InitialSSValue; // 0E Initial (relative) SS value
public ushort InitialSPValue; // 10 Initial SP value
public ushort Checksum; // 12 Checksum
public ushort InitialIPValue; // 14 Initial IP value
public ushort InitialCSValue; // 16 Initial (relative) CS value
public ushort RelocationTableAddr; // 18 File address of relocation table
public ushort OverlayNumber; // 1A Overlay number
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.ERES1WDS)]
public ushort[] Reserved1; // 1C Reserved words
public ushort OEMIdentifier; // 24 OEM identifier (for e_oeminfo)
public ushort OEMInformation; // 26 OEM information; e_oemid specific
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.ERES2WDS)]
public ushort[] Reserved2; // 28 Reserved words
public int NewExeHeaderAddr; // 3C File address of new exe header
public static IMAGE_DOS_HEADER Deserialize(Stream stream)
{
IMAGE_DOS_HEADER idh = new IMAGE_DOS_HEADER();
idh.Magic = stream.ReadUInt16();
idh.LastPageBytes = stream.ReadUInt16();
idh.Pages = stream.ReadUInt16();
idh.Relocations = stream.ReadUInt16();
idh.HeaderParagraphSize = stream.ReadUInt16();
idh.MinimumExtraParagraphs = stream.ReadUInt16();
idh.MaximumExtraParagraphs = stream.ReadUInt16();
idh.InitialSSValue = stream.ReadUInt16();
idh.InitialSPValue = stream.ReadUInt16();
idh.Checksum = stream.ReadUInt16();
idh.InitialIPValue = stream.ReadUInt16();
idh.InitialCSValue = stream.ReadUInt16();
idh.RelocationTableAddr = stream.ReadUInt16();
idh.OverlayNumber = stream.ReadUInt16();
idh.Reserved1 = new ushort[Constants.ERES1WDS];
for (int i = 0; i < Constants.ERES1WDS; i++)
{
idh.Reserved1[i] = stream.ReadUInt16();
}
idh.OEMIdentifier = stream.ReadUInt16();
idh.OEMInformation = stream.ReadUInt16();
idh.Reserved2 = new ushort[Constants.ERES2WDS];
for (int i = 0; i < Constants.ERES2WDS; i++)
{
idh.Reserved2[i] = stream.ReadUInt16();
}
idh.NewExeHeaderAddr = stream.ReadInt32();
return idh;
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
[StructLayout(LayoutKind.Sequential)]
internal class IMAGE_FILE_HEADER
{
public ushort Machine;
public ushort NumberOfSections;
public uint TimeDateStamp;
public uint PointerToSymbolTable;
public uint NumberOfSymbols;
public ushort SizeOfOptionalHeader;
public ushort Characteristics;
public static IMAGE_FILE_HEADER Deserialize(Stream stream)
{
var ifh = new IMAGE_FILE_HEADER();
ifh.Machine = stream.ReadUInt16();
ifh.NumberOfSections = stream.ReadUInt16();
ifh.TimeDateStamp = stream.ReadUInt32();
ifh.PointerToSymbolTable = stream.ReadUInt32();
ifh.NumberOfSymbols = stream.ReadUInt32();
ifh.SizeOfOptionalHeader = stream.ReadUInt16();
ifh.Characteristics = stream.ReadUInt16();
return ifh;
}
}
}

View File

@@ -0,0 +1,103 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
[StructLayout(LayoutKind.Sequential)]
internal class IMAGE_OPTIONAL_HEADER
{
// Standard fields
public ushort Magic;
public byte MajorLinkerVersion;
public byte MinorLinkerVersion;
public uint SizeOfCode;
public uint SizeOfInitializedData;
public uint SizeOfUninitializedData;
public uint AddressOfEntryPoint;
public uint BaseOfCode;
public uint BaseOfData;
// NT additional fields.
public uint ImageBase;
public uint SectionAlignment;
public uint FileAlignment;
public ushort MajorOperatingSystemVersion;
public ushort MinorOperatingSystemVersion;
public ushort MajorImageVersion;
public ushort MinorImageVersion;
public ushort MajorSubsystemVersion;
public ushort MinorSubsystemVersion;
public uint Reserved1;
public uint SizeOfImage;
public uint SizeOfHeaders;
public uint CheckSum;
public ushort Subsystem;
public ushort DllCharacteristics;
public uint SizeOfStackReserve;
public uint SizeOfStackCommit;
public uint SizeOfHeapReserve;
public uint SizeOfHeapCommit;
public uint LoaderFlags;
public uint NumberOfRvaAndSizes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES)]
public IMAGE_DATA_DIRECTORY[] DataDirectory;
public static IMAGE_OPTIONAL_HEADER Deserialize(Stream stream)
{
var ioh = new IMAGE_OPTIONAL_HEADER();
ioh.Magic = stream.ReadUInt16();
ioh.MajorLinkerVersion = stream.ReadByteValue();
ioh.MinorLinkerVersion = stream.ReadByteValue();
ioh.SizeOfCode = stream.ReadUInt32();
ioh.SizeOfInitializedData = stream.ReadUInt32();
ioh.SizeOfUninitializedData = stream.ReadUInt32();
ioh.AddressOfEntryPoint = stream.ReadUInt32();
ioh.BaseOfCode = stream.ReadUInt32();
ioh.BaseOfData = stream.ReadUInt32();
ioh.ImageBase = stream.ReadUInt32();
ioh.SectionAlignment = stream.ReadUInt32();
ioh.FileAlignment = stream.ReadUInt32();
ioh.MajorOperatingSystemVersion = stream.ReadUInt16();
ioh.MinorOperatingSystemVersion = stream.ReadUInt16();
ioh.MajorImageVersion = stream.ReadUInt16();
ioh.MinorImageVersion = stream.ReadUInt16();
ioh.MajorSubsystemVersion = stream.ReadUInt16();
ioh.MinorSubsystemVersion = stream.ReadUInt16();
ioh.Reserved1 = stream.ReadUInt32();
ioh.SizeOfImage = stream.ReadUInt32();
ioh.SizeOfHeaders = stream.ReadUInt32();
ioh.CheckSum = stream.ReadUInt32();
ioh.Subsystem = stream.ReadUInt16();
ioh.DllCharacteristics = stream.ReadUInt16();
ioh.SizeOfStackReserve = stream.ReadUInt32();
ioh.SizeOfStackCommit = stream.ReadUInt32();
ioh.SizeOfHeapReserve = stream.ReadUInt32();
ioh.SizeOfHeapCommit = stream.ReadUInt32();
ioh.LoaderFlags = stream.ReadUInt32();
ioh.NumberOfRvaAndSizes = stream.ReadUInt32();
ioh.DataDirectory = new IMAGE_DATA_DIRECTORY[Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
for (int i = 0; i < Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++)
{
ioh.DataDirectory[i] = IMAGE_DATA_DIRECTORY.Deserialize(stream);
}
return ioh;
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
/// <summary>
/// New .EXE header
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal class IMAGE_OS2_HEADER
{
public ushort Magic; // 00 Magic number NE_MAGIC
public byte LinkerVersion; // 02 Linker Version number
public byte LinkerRevision; // 03 Linker Revision number
public ushort EntryTableOffset; // 04 Offset of Entry Table
public ushort EntryTableSize; // 06 Number of bytes in Entry Table
public uint CrcChecksum; // 08 Checksum of whole file
public ushort Flags; // 0C Flag word
public ushort Autodata; // 0E Automatic data segment number
public ushort InitialHeapAlloc; // 10 Initial heap allocation
public ushort InitialStackAlloc; // 12 Initial stack allocation
public uint InitialCSIPSetting; // 14 Initial CS:IP setting
public uint InitialSSSPSetting; // 18 Initial SS:SP setting
public ushort FileSegmentCount; // 1C Count of file segments
public ushort ModuleReferenceTableSize; // 1E Entries in Module Reference Table
public ushort NonResidentNameTableSize; // 20 Size of non-resident name table
public ushort SegmentTableOffset; // 22 Offset of Segment Table
public ushort ResourceTableOffset; // 24 Offset of Resource Table
public ushort ResidentNameTableOffset; // 26 Offset of resident name table
public ushort ModuleReferenceTableOffset; // 28 Offset of Module Reference Table
public ushort ImportedNamesTableOffset; // 2A Offset of Imported Names Table
public uint NonResidentNamesTableOffset; // 2C Offset of Non-resident Names Table
public ushort MovableEntriesCount; // 30 Count of movable entries
public ushort SegmentAlignmentShiftCount; // 32 Segment alignment shift count
public ushort ResourceEntriesCount; // 34 Count of resource entries
public byte TargetOperatingSystem; // 36 Target operating system
public byte AdditionalFlags; // 37 Additional flags
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.NERESWORDS)]
public ushort[] Reserved; // 38 3 reserved words
public byte WindowsSDKRevision; // 3E Windows SDK revison number
public byte WindowsSDKVersion; // 3F Windows SDK version number
public static IMAGE_OS2_HEADER Deserialize(Stream stream)
{
var ioh = new IMAGE_OS2_HEADER();
ioh.Magic = stream.ReadUInt16();
ioh.LinkerVersion = stream.ReadByteValue();
ioh.LinkerRevision = stream.ReadByteValue();
ioh.EntryTableOffset = stream.ReadUInt16();
ioh.EntryTableSize = stream.ReadUInt16();
ioh.CrcChecksum = stream.ReadUInt32();
ioh.Flags = stream.ReadUInt16();
ioh.Autodata = stream.ReadUInt16();
ioh.InitialHeapAlloc = stream.ReadUInt16();
ioh.InitialStackAlloc = stream.ReadUInt16();
ioh.InitialCSIPSetting = stream.ReadUInt32();
ioh.InitialSSSPSetting = stream.ReadUInt32();
ioh.FileSegmentCount = stream.ReadUInt16();
ioh.ModuleReferenceTableSize = stream.ReadUInt16();
ioh.NonResidentNameTableSize = stream.ReadUInt16();
ioh.SegmentTableOffset = stream.ReadUInt16();
ioh.ResourceTableOffset = stream.ReadUInt16();
ioh.ResidentNameTableOffset = stream.ReadUInt16();
ioh.ModuleReferenceTableOffset = stream.ReadUInt16();
ioh.ImportedNamesTableOffset = stream.ReadUInt16();
ioh.NonResidentNamesTableOffset = stream.ReadUInt32();
ioh.MovableEntriesCount = stream.ReadUInt16();
ioh.SegmentAlignmentShiftCount = stream.ReadUInt16();
ioh.ResourceEntriesCount = stream.ReadUInt16();
ioh.TargetOperatingSystem = stream.ReadByteValue();
ioh.AdditionalFlags = stream.ReadByteValue();
ioh.Reserved = new ushort[Constants.NERESWORDS];
for (int i = 0; i < Constants.NERESWORDS; i++)
{
ioh.Reserved[i] = stream.ReadUInt16();
}
ioh.WindowsSDKRevision = stream.ReadByteValue();
ioh.WindowsSDKVersion = stream.ReadByteValue();
return ioh;
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
[StructLayout(LayoutKind.Sequential)]
internal class IMAGE_RESOURCE_DATA_ENTRY
{
public uint OffsetToData;
public uint Size;
public uint CodePage;
public uint Reserved;
public static IMAGE_RESOURCE_DATA_ENTRY Deserialize(Stream stream)
{
var irde = new IMAGE_RESOURCE_DATA_ENTRY();
irde.OffsetToData = stream.ReadUInt32();
irde.Size = stream.ReadUInt32();
irde.CodePage = stream.ReadUInt32();
irde.Reserved = stream.ReadUInt32();
return irde;
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
[StructLayout(LayoutKind.Sequential)]
internal class IMAGE_RESOURCE_DIRECTORY
{
public uint Characteristics;
public uint TimeDateStamp;
public ushort MajorVersion;
public ushort MinorVersion;
public ushort NumberOfNamedEntries;
public ushort NumberOfIdEntries;
public static IMAGE_RESOURCE_DIRECTORY Deserialize(Stream stream)
{
var ird = new IMAGE_RESOURCE_DIRECTORY();
ird.Characteristics = stream.ReadUInt32();
ird.TimeDateStamp = stream.ReadUInt32();
ird.MajorVersion = stream.ReadUInt16();
ird.MinorVersion = stream.ReadUInt16();
ird.NumberOfNamedEntries = stream.ReadUInt16();
ird.NumberOfIdEntries = stream.ReadUInt16();
return ird;
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
[StructLayout(LayoutKind.Sequential)]
internal class IMAGE_RESOURCE_DIRECTORY_ENTRY
{
public uint Name;
public uint OffsetToData;
public static IMAGE_RESOURCE_DIRECTORY_ENTRY Deserialize(Stream stream)
{
var irde = new IMAGE_RESOURCE_DIRECTORY_ENTRY();
irde.Name = stream.ReadUInt32();
irde.OffsetToData = stream.ReadUInt32();
return irde;
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
[StructLayout(LayoutKind.Sequential)]
internal class IMAGE_RESOURCE_DIR_STRING_U
{
public ushort Length;
public char[] NameString;
public static IMAGE_RESOURCE_DIR_STRING_U Deserialize(Stream stream)
{
var irdsu = new IMAGE_RESOURCE_DIR_STRING_U();
irdsu.Length = stream.ReadUInt16();
irdsu.NameString = stream.ReadChars(irdsu.Length);
return irdsu;
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
[StructLayout(LayoutKind.Sequential)]
internal class IMAGE_SECTION_HEADER
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.IMAGE_SIZEOF_SHORT_NAME)]
public byte[] Name;
// Misc
public uint PhysicalAddress;
public uint VirtualSize;
public uint VirtualAddress;
public uint SizeOfRawData;
public uint PointerToRawData;
public uint PointerToRelocations;
public uint PointerToLinenumbers;
public ushort NumberOfRelocations;
public ushort NumberOfLinenumbers;
public SectionCharacteristics Characteristics;
public static IMAGE_SECTION_HEADER Deserialize(Stream stream)
{
var ish = new IMAGE_SECTION_HEADER();
ish.Name = stream.ReadBytes(Constants.IMAGE_SIZEOF_SHORT_NAME);
// Misc
ish.PhysicalAddress = stream.ReadUInt32();
ish.VirtualSize = ish.PhysicalAddress;
ish.VirtualAddress = stream.ReadUInt32();
ish.SizeOfRawData = stream.ReadUInt32();
ish.PointerToRawData = stream.ReadUInt32();
ish.PointerToRelocations = stream.ReadUInt32();
ish.PointerToLinenumbers = stream.ReadUInt32();
ish.NumberOfRelocations = stream.ReadUInt16();
ish.NumberOfLinenumbers = stream.ReadUInt16();
ish.Characteristics = (SectionCharacteristics)stream.ReadUInt32();
return ish;
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
[StructLayout(LayoutKind.Sequential)]
internal class NAMEINFO
{
public ushort Offset;
public ushort Length;
public ushort Flags;
public ushort ID;
public ushort Handle;
public ushort Usage;
public static NAMEINFO Deserialize(Stream stream)
{
var ni = new NAMEINFO();
ni.Offset = stream.ReadUInt16();
ni.Length = stream.ReadUInt16();
ni.Flags = stream.ReadUInt16();
ni.ID = stream.ReadUInt16();
ni.Handle = stream.ReadUInt16();
ni.Usage = stream.ReadUInt16();
return ni;
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
/// <summary>
/// Relocation item
/// </summary>
/// TODO: Fix this because Marshal will not work since it's not a direct read
[StructLayout(LayoutKind.Sequential)]
internal class NewRlc
{
public char SourceType; // Source type
public char Flags; // Flag byte
public ushort SourceOffset; // Source offset
// nr_intref - Internal Reference
public char TargetSegmentNumber; // Target segment number
public char Reserved1; // Reserved
public ushort TargetEntryTableOffset; // Target Entry Table offset
// nr_import - Import
public ushort ModuleReferenceTableIndex; // Index into Module Reference Table
public ushort ProcedureOffset; // Procedure ordinal or name offset
// nr_osfix - Operating system fixup
public ushort OperatingSystemFixupType; // OSFIXUP type
public ushort Reserved2; // Reserved
public static NewRlc Deserialize(Stream stream)
{
var nr = new NewRlc();
nr.SourceType = stream.ReadChar();
nr.Flags = stream.ReadChar();
nr.SourceOffset = stream.ReadUInt16();
// nr_intref
nr.TargetSegmentNumber = stream.ReadChar();
nr.Reserved1 = stream.ReadChar();
nr.TargetEntryTableOffset = stream.ReadUInt16();
// nr_import
nr.ModuleReferenceTableIndex = BitConverter.ToUInt16(new byte[] { (byte)nr.SourceType, (byte)nr.Flags }, 0);
nr.ProcedureOffset = nr.TargetEntryTableOffset;
// nr_osfix
nr.OperatingSystemFixupType = nr.ModuleReferenceTableIndex;
nr.Reserved2 = nr.ProcedureOffset;
return nr;
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
/// <summary>
/// Relocation info
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal class NewRlcInfo
{
/// <summary>
/// Number of relocation items that follow
/// </summary>
public ushort RelocationItemCount;
public static NewRlcInfo Deserialize(Stream stream)
{
var nri = new NewRlcInfo();
nri.RelocationItemCount = stream.ReadUInt16();
return nri;
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
/// <summary>
/// Resource table
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal class NewRsrc
{
/// <summary>
/// Alignment shift count for resources
/// </summary>
public ushort AlignmentShiftCount;
public RsrcTypeInfo TypeInfo;
public static NewRsrc Deserialize(Stream stream)
{
var nr = new NewRsrc();
nr.AlignmentShiftCount = stream.ReadUInt16();
nr.TypeInfo = RsrcTypeInfo.Deserialize(stream);
return nr;
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
/// <summary>
/// New .EXE segment table entry
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal class NewSeg
{
/// <summary>
/// File sector of start of segment
/// </summary>
public ushort StartFileSector;
/// <summary>
/// Number of bytes in file
/// </summary>
public ushort BytesInFile;
/// <summary>
/// Attribute flags
/// </summary>
public ushort Flags;
/// <summary>
/// Minimum allocation in bytes
/// </summary>
public ushort MinimumAllocation;
public static NewSeg Deserialize(Stream stream)
{
var ns = new NewSeg();
ns.StartFileSector = stream.ReadUInt16();
ns.BytesInFile = stream.ReadUInt16();
ns.Flags = stream.ReadUInt16();
ns.MinimumAllocation = stream.ReadUInt16();
return ns;
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
/// <summary>
/// Segment data
/// </summary>
/// TODO: Fix this because Marshal will not work since it's not a direct read
[StructLayout(LayoutKind.Sequential)]
internal class NewSegdata
{
#region ns_iter
/// <summary>
/// Number of iterations
/// </summary>
public ushort Iterations;
/// <summary>
/// Number of bytes
/// </summary>
public ushort TotalBytes;
/// <summary>
/// Iterated data bytes
/// </summary>
public char IteratedDataBytes;
#endregion
#region ns_noiter
/// <summary>
/// Data bytes
/// </summary>
public char DataBytes;
#endregion
public static NewSegdata Deserialize(Stream stream)
{
var nsd = new NewSegdata();
nsd.Iterations = stream.ReadUInt16();
nsd.TotalBytes = stream.ReadUInt16();
nsd.IteratedDataBytes = stream.ReadChar();
nsd.DataBytes = (char)BitConverter.GetBytes(nsd.Iterations)[0];
return nsd;
}
}
}

View File

@@ -0,0 +1,41 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
[StructLayout(LayoutKind.Sequential)]
internal class ResourceTable
{
public ushort rscAlignShift;
public TYPEINFO TypeInfo;
public ushort rscEndTypes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0)]
public sbyte[][] rscResourceNames;
public byte rscEndNames;
public static ResourceTable Deserialize(Stream stream)
{
var rt = new ResourceTable();
rt.rscAlignShift = stream.ReadUInt16();
rt.TypeInfo = TYPEINFO.Deserialize(stream);
rt.rscEndTypes = stream.ReadUInt16();
rt.rscResourceNames = null; // TODO: Figure out size
rt.rscEndNames = stream.ReadByteValue();
return rt;
}
}
}

View File

@@ -0,0 +1,75 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
/// <summary>
/// Resource name information block
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal class RsrcNameInfo
{
/*
* The following two fields must be shifted left by the value of
* the rs_align field to compute their actual value. This allows
* resources to be larger than 64k, but they do not need to be
* aligned on 512 byte boundaries, the way segments are.
*/
/// <summary>
/// File offset to resource data
/// </summary>
public ushort Offset;
/// <summary>
/// Length of resource data
/// </summary>
public ushort Length;
/// <summary>
/// Resource flags
/// </summary>
public ushort Flags;
/// <summary>
/// Resource name id
/// </summary>
public ushort NameID;
/// <summary>
/// If loaded, then global handle
/// </summary>
public ushort Handle;
/// <summary>
/// Initially zero. Number of times the handle for this resource has been given out
/// </summary>
public ushort UsageCount;
public static RsrcNameInfo Deserialize(Stream stream)
{
var rni = new RsrcNameInfo();
rni.Offset = stream.ReadUInt16();
rni.Length = stream.ReadUInt16();
rni.Flags = stream.ReadUInt16();
rni.NameID = stream.ReadUInt16();
rni.Handle = stream.ReadUInt16();
rni.UsageCount = stream.ReadUInt16();
return rni;
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
/// <summary>
/// Resource type or name string
/// </summary>
/// TODO: Fix this because SizeConst = 0 is not valid
[StructLayout(LayoutKind.Sequential)]
internal class RsrcString
{
/// <summary>
/// Number of bytes in string
/// </summary>
public byte Length;
/// <summary>
/// Next of string
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0)]
public char[] Text;
public static RsrcString Deserialize(Stream stream)
{
var rs = new RsrcString();
rs.Length = stream.ReadByteValue();
rs.Text = stream.ReadChars(rs.Length);
return rs;
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
/// <summary>
/// Resource type information block
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal class RsrcTypeInfo
{
public ushort ID;
public ushort rt_nres;
public uint rt_proc;
public static RsrcTypeInfo Deserialize(Stream stream)
{
var rti = new RsrcTypeInfo();
rti.ID = stream.ReadUInt16();
rti.rt_nres = stream.ReadUInt16();
rti.rt_proc = stream.ReadUInt32();
return rti;
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* NEWEXE.H (C) Copyright Microsoft Corp 1984-1987
*
* Data structure definitions for the OS/2 & Windows
* executable file format.
*
* Modified by IVS on 24-Jan-1991 for Resource DeCompiler
* (C) Copyright IVS 1991
*
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
using System.IO;
using System.Runtime.InteropServices;
namespace BurnOutSharp.ExecutableType.Microsoft
{
[StructLayout(LayoutKind.Sequential)]
internal class TYPEINFO
{
public ushort TypeID;
public ushort ResourceCount;
public uint Reserved;
public NAMEINFO NameInfo;
public static TYPEINFO Deserialize(Stream stream)
{
var ti = new TYPEINFO();
ti.TypeID = stream.ReadUInt16();
ti.ResourceCount = stream.ReadUInt16();
ti.Reserved = stream.ReadUInt32();
ti.NameInfo = NAMEINFO.Deserialize(stream);
return ti;
}
}
}

109
BurnOutSharp/Extensions.cs Normal file
View File

@@ -0,0 +1,109 @@
using System;
using System.IO;
using System.Text;
namespace BurnOutSharp
{
internal static class Ebuffertensions
{
/// <summary>
/// Read a byte from the stream
/// </summary>
public static byte ReadByteValue(this Stream stream)
{
byte[] buffer = new byte[1];
stream.Read(buffer, 0, 1);
return buffer[0];
}
/// <summary>
/// Read a byte array from the stream
/// </summary>
public static byte[] ReadBytes(this Stream stream, int count)
{
byte[] buffer = new byte[count];
stream.Read(buffer, 0, count);
return buffer;
}
/// <summary>
/// Read a character from the stream
/// </summary>
public static char ReadChar(this Stream stream)
{
byte[] buffer = new byte[1];
stream.Read(buffer, 0, 1);
return (char)buffer[0];
}
/// <summary>
/// Read a character array from the stream
/// </summary>
public static char[] ReadChars(this Stream stream, int count)
{
byte[] buffer = new byte[count];
stream.Read(buffer, 0, count);
return Encoding.Default.GetString(buffer).ToCharArray();
}
/// <summary>
/// Read a short from the stream
/// </summary>
public static short ReadInt16(this Stream stream)
{
byte[] buffer = new byte[2];
stream.Read(buffer, 0, 2);
return BitConverter.ToInt16(buffer, 0);
}
/// <summary>
/// Read a ushort from the stream
/// </summary>
public static ushort ReadUInt16(this Stream stream)
{
byte[] buffer = new byte[2];
stream.Read(buffer, 0, 2);
return BitConverter.ToUInt16(buffer, 0);
}
/// <summary>
/// Read an int from the stream
/// </summary>
public static int ReadInt32(this Stream stream)
{
byte[] buffer = new byte[4];
stream.Read(buffer, 0, 4);
return BitConverter.ToInt32(buffer, 0);
}
/// <summary>
/// Read a uint from the stream
/// </summary>
public static uint ReadUInt32(this Stream stream)
{
byte[] buffer = new byte[4];
stream.Read(buffer, 0, 4);
return BitConverter.ToUInt32(buffer, 0);
}
/// <summary>
/// Read a long from the stream
/// </summary>
public static long ReadInt64(this Stream stream)
{
byte[] buffer = new byte[8];
stream.Read(buffer, 0, 8);
return BitConverter.ToInt64(buffer, 0);
}
/// <summary>
/// Read a ulong from the stream
/// </summary>
public static ulong ReadUInt64(this Stream stream)
{
byte[] buffer = new byte[8];
stream.Read(buffer, 0, 8);
return BitConverter.ToUInt64(buffer, 0);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1
BurnOutSharp/External/LessIO vendored Submodule

View File

@@ -1,414 +0,0 @@
using StormLibSharp.Native;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace StormLibSharp
{
public class MpqArchive : IDisposable
{
private MpqArchiveSafeHandle _handle;
private List<MpqFileStream> _openFiles = new List<MpqFileStream>();
private FileAccess _accessType;
private List<MpqArchiveCompactingEventHandler> _compactCallbacks = new List<MpqArchiveCompactingEventHandler>();
private SFILE_COMPACT_CALLBACK _compactCallback;
#region Constructors / Factories
public MpqArchive(string filePath, FileAccess accessType)
{
_accessType = accessType;
SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsFile;
if (accessType == FileAccess.Read)
flags |= SFileOpenArchiveFlags.AccessReadOnly;
else
flags |= SFileOpenArchiveFlags.AccessReadWriteShare;
// constant 2 = SFILE_OPEN_HARD_DISK_FILE
if (!NativeMethods.SFileOpenArchive(filePath, 2, flags, out _handle))
throw new Win32Exception(); // Implicitly calls GetLastError
}
public MpqArchive(MemoryMappedFile file, FileAccess accessType)
{
_accessType = accessType;
string fileName = Win32Methods.GetFileNameOfMemoryMappedFile(file);
if (fileName == null)
throw new ArgumentException("Could not retrieve the name of the file to initialize.");
SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsMemoryMapped;
if (accessType == FileAccess.Read)
flags |= SFileOpenArchiveFlags.AccessReadOnly;
else
flags |= SFileOpenArchiveFlags.AccessReadWriteShare;
// constant 2 = SFILE_OPEN_HARD_DISK_FILE
if (!NativeMethods.SFileOpenArchive(fileName, 2, flags, out _handle))
throw new Win32Exception(); // Implicitly calls GetLastError
}
private MpqArchive(string filePath, MpqArchiveVersion version, MpqFileStreamAttributes listfileAttributes, MpqFileStreamAttributes attributesFileAttributes, int maxFileCount)
{
if (maxFileCount < 0)
throw new ArgumentException("maxFileCount");
SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsFile | SFileOpenArchiveFlags.AccessReadWriteShare;
flags |= (SFileOpenArchiveFlags)version;
//SFILE_CREATE_MPQ create = new SFILE_CREATE_MPQ()
//{
// cbSize = unchecked((uint)Marshal.SizeOf(typeof(SFILE_CREATE_MPQ))),
// dwMaxFileCount = unchecked((uint)maxFileCount),
// dwMpqVersion = (uint)version,
// dwFileFlags1 = (uint)listfileAttributes,
// dwFileFlags2 = (uint)attributesFileAttributes,
// dwStreamFlags = (uint)flags,
//};
//if (!NativeMethods.SFileCreateArchive2(filePath, ref create, out _handle))
// throw new Win32Exception();
if (!NativeMethods.SFileCreateArchive(filePath, (uint)flags, int.MaxValue, out _handle))
throw new Win32Exception();
}
public static MpqArchive CreateNew(string mpqPath, MpqArchiveVersion version)
{
return CreateNew(mpqPath, version, MpqFileStreamAttributes.None, MpqFileStreamAttributes.None, int.MaxValue);
}
public static MpqArchive CreateNew(string mpqPath, MpqArchiveVersion version, MpqFileStreamAttributes listfileAttributes,
MpqFileStreamAttributes attributesFileAttributes, int maxFileCount)
{
return new MpqArchive(mpqPath, version, listfileAttributes, attributesFileAttributes, maxFileCount);
}
#endregion
#region Properties
// TODO: Move to common location.
// This is a global setting, not per-archive setting.
//public int Locale
//{
// get
// {
// throw new NotImplementedException();
// }
// set
// {
// throw new NotImplementedException();
// }
//}
public long MaxFileCount
{
get
{
VerifyHandle();
return NativeMethods.SFileGetMaxFileCount(_handle);
}
set
{
if (value < 0 || value > uint.MaxValue)
throw new ArgumentException("value");
VerifyHandle();
if (!NativeMethods.SFileSetMaxFileCount(_handle, unchecked((uint)value)))
throw new Win32Exception();
}
}
private void VerifyHandle()
{
if (_handle == null || _handle.IsInvalid)
throw new ObjectDisposedException("MpqArchive");
}
public bool IsPatchedArchive
{
get
{
VerifyHandle();
return NativeMethods.SFileIsPatchedArchive(_handle);
}
}
#endregion
public void Flush()
{
VerifyHandle();
if (!NativeMethods.SFileFlushArchive(_handle))
throw new Win32Exception();
}
public int AddListFile(string listfileContents)
{
VerifyHandle();
return NativeMethods.SFileAddListFile(_handle, listfileContents);
}
public void AddFileFromDisk(string filePath, string archiveName)
{
VerifyHandle();
if (!NativeMethods.SFileAddFile(_handle, filePath, archiveName, 0))
throw new Win32Exception();
}
public void Compact(string listfile)
{
VerifyHandle();
if (!NativeMethods.SFileCompactArchive(_handle, listfile, false))
throw new Win32Exception();
}
private void _OnCompact(IntPtr pvUserData, uint dwWorkType, ulong bytesProcessed, ulong totalBytes)
{
MpqArchiveCompactingEventArgs args = new MpqArchiveCompactingEventArgs(dwWorkType, bytesProcessed, totalBytes);
OnCompacting(args);
}
protected virtual void OnCompacting(MpqArchiveCompactingEventArgs e)
{
foreach (var cb in _compactCallbacks)
{
cb(this, e);
}
}
public event MpqArchiveCompactingEventHandler Compacting
{
add
{
VerifyHandle();
_compactCallback = _OnCompact;
if (!NativeMethods.SFileSetCompactCallback(_handle, _compactCallback, IntPtr.Zero))
throw new Win32Exception();
_compactCallbacks.Add(value);
}
remove
{
_compactCallbacks.Remove(value);
VerifyHandle();
if (_compactCallbacks.Count == 0)
{
if (!NativeMethods.SFileSetCompactCallback(_handle, null, IntPtr.Zero))
{
// Don't do anything here. Remove shouldn't fail hard.
}
}
}
}
// TODO: Determine if SFileGetAttributes/SFileSetAttributes/SFileUpdateFileAttributes deserves a projection.
// It's unclear - these seem to affect the (attributes) file but I can't figure out exactly what that means.
public void AddPatchArchive(string patchPath)
{
VerifyHandle();
if (!NativeMethods.SFileOpenPatchArchive(_handle, patchPath, null, 0))
throw new Win32Exception();
}
public void AddPatchArchives(IEnumerable<string> patchPaths)
{
if (patchPaths == null)
throw new ArgumentNullException("patchPaths");
VerifyHandle();
foreach (string path in patchPaths)
{
// Don't sublet to AddPatchArchive to avoid having to repeatedly call VerifyHandle()
if (!NativeMethods.SFileOpenPatchArchive(_handle, path, null, 0))
throw new Win32Exception();
}
}
public bool HasFile(string fileToFind)
{
VerifyHandle();
return NativeMethods.SFileHasFile(_handle, fileToFind);
}
public MpqFileStream OpenFile(string fileName)
{
VerifyHandle();
MpqFileSafeHandle fileHandle;
if (!NativeMethods.SFileOpenFileEx(_handle, fileName, 0, out fileHandle))
throw new Win32Exception();
MpqFileStream fs = new MpqFileStream(fileHandle, _accessType, this);
_openFiles.Add(fs);
return fs;
}
public void ExtractFile(string fileToExtract, string destinationPath)
{
VerifyHandle();
if (!NativeMethods.SFileExtractFile(_handle, fileToExtract, destinationPath, 0))
throw new Win32Exception();
}
public MpqFileVerificationResults VerifyFile(string fileToVerify)
{
VerifyHandle();
return (MpqFileVerificationResults)NativeMethods.SFileVerifyFile(_handle, fileToVerify, 0);
}
// TODO: Consider SFileVerifyRawData
public MpqArchiveVerificationResult VerifyArchive()
{
VerifyHandle();
return (MpqArchiveVerificationResult)NativeMethods.SFileVerifyArchive(_handle);
}
#region IDisposable implementation
public void Dispose()
{
Dispose(true);
}
~MpqArchive()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// Release owned files first.
if (_openFiles != null)
{
foreach (var file in _openFiles)
{
file.Dispose();
}
_openFiles.Clear();
_openFiles = null;
}
// Release
if (_handle != null && !_handle.IsInvalid)
{
_handle.Close();
_handle = null;
}
}
}
internal void RemoveOwnedFile(MpqFileStream file)
{
_openFiles.Remove(file);
}
#endregion
}
public enum MpqArchiveVersion
{
Version1 = 0,
Version2 = 0x01000000,
Version3 = 0x02000000,
Version4 = 0x03000000,
}
[Flags]
public enum MpqFileStreamAttributes
{
None = 0x0,
}
[Flags]
public enum MpqFileVerificationResults
{
/// <summary>
/// There were no errors with the file.
/// </summary>
Verified = 0,
/// <summary>
/// Failed to open the file
/// </summary>
Error = 0x1,
/// <summary>
/// Failed to read all data from the file
/// </summary>
ReadError = 0x2,
/// <summary>
/// File has sector CRC
/// </summary>
HasSectorCrc = 0x4,
/// <summary>
/// Sector CRC check failed
/// </summary>
SectorCrcError = 0x8,
/// <summary>
/// File has CRC32
/// </summary>
HasChecksum = 0x10,
/// <summary>
/// CRC32 check failed
/// </summary>
ChecksumError = 0x20,
/// <summary>
/// File has data MD5
/// </summary>
HasMd5 = 0x40,
/// <summary>
/// MD5 check failed
/// </summary>
Md5Error = 0x80,
/// <summary>
/// File has raw data MD5
/// </summary>
HasRawMd5 = 0x100,
/// <summary>
/// Raw MD5 check failed
/// </summary>
RawMd5Error = 0x200,
}
public enum MpqArchiveVerificationResult
{
/// <summary>
/// There is no signature in the MPQ
/// </summary>
NoSignature = 0,
/// <summary>
/// There was an error during verifying signature (like no memory)
/// </summary>
VerificationFailed = 1,
/// <summary>
/// There is a weak signature and sign check passed
/// </summary>
WeakSignatureVerified = 2,
/// <summary>
/// There is a weak signature but sign check failed
/// </summary>
WeakSignatureFailed = 3,
/// <summary>
/// There is a strong signature and sign check passed
/// </summary>
StrongSignatureVerified = 4,
/// <summary>
/// There is a strong signature but sign check failed
/// </summary>
StrongSignatureFailed = 5,
}
}

View File

@@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace StormLibSharp
{
public delegate void MpqArchiveCompactingEventHandler(MpqArchive sender, MpqArchiveCompactingEventArgs e);
public class MpqArchiveCompactingEventArgs : EventArgs
{
internal MpqArchiveCompactingEventArgs(uint dwWorkType, ulong processed, ulong total)
{
unchecked
{
WorkType = (MpqCompactingWorkType)dwWorkType;
BytesProcessed = (long)processed;
TotalBytes = (long)total;
}
}
public MpqCompactingWorkType WorkType
{
get;
private set;
}
public long BytesProcessed
{
get;
private set;
}
public long TotalBytes
{
get;
private set;
}
}
public enum MpqCompactingWorkType
{
CheckingFiles = 1,
CheckingHashTable = 2,
CopyingNonMpqData = 3,
CompactingFiles = 4,
ClosingArchive = 5,
}
}

View File

@@ -1,185 +0,0 @@
using StormLibSharp.Native;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
namespace StormLibSharp
{
public class MpqFileStream : Stream
{
private MpqFileSafeHandle _handle;
private FileAccess _accessType;
private MpqArchive _owner;
internal MpqFileStream(MpqFileSafeHandle handle, FileAccess accessType, MpqArchive owner)
{
_handle = handle;
_accessType = accessType;
_owner = owner;
}
private void VerifyHandle()
{
if (_handle == null || _handle.IsInvalid || _handle.IsClosed)
throw new ObjectDisposedException("MpqFileStream");
}
public override bool CanRead
{
get { VerifyHandle(); return true; }
}
public override bool CanSeek
{
get { VerifyHandle(); return true; }
}
public override bool CanWrite
{
get { VerifyHandle(); return _accessType != FileAccess.Read; }
}
public override void Flush()
{
VerifyHandle();
_owner.Flush();
}
public override long Length
{
get
{
VerifyHandle();
uint high = 0;
uint low = NativeMethods.SFileGetFileSize(_handle, ref high);
ulong val = (high << 32) | low;
return unchecked((long)val);
}
}
public override long Position
{
get
{
VerifyHandle();
return NativeMethods.SFileGetFilePointer(_handle);
}
set
{
Seek(value, SeekOrigin.Begin);
}
}
public override unsafe int Read(byte[] buffer, int offset, int count)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset > buffer.Length || (offset + count) > buffer.Length)
throw new ArgumentException();
if (count < 0)
throw new ArgumentOutOfRangeException("count");
VerifyHandle();
bool success;
uint read;
fixed (byte* pb = &buffer[offset])
{
NativeOverlapped overlapped = default(NativeOverlapped);
success = NativeMethods.SFileReadFile(_handle, new IntPtr(pb), unchecked((uint)count), out read, ref overlapped);
}
if (!success)
{
int lastError = Win32Methods.GetLastError();
if (lastError != 38) // EOF
throw new Win32Exception(lastError);
}
return unchecked((int)read);
}
public override long Seek(long offset, SeekOrigin origin)
{
VerifyHandle();
uint low, high;
low = unchecked((uint)(offset & 0xffffffffu));
high = unchecked((uint)(offset >> 32));
return NativeMethods.SFileSetFilePointer(_handle, low, ref high, (uint)origin);
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override unsafe void Write(byte[] buffer, int offset, int count)
{
VerifyHandle();
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset > buffer.Length || (offset + count) > buffer.Length)
throw new ArgumentException();
if (count < 0)
throw new ArgumentOutOfRangeException("count");
VerifyHandle();
bool success;
fixed (byte* pb = &buffer[offset])
{
success = NativeMethods.SFileWriteFile(_handle, new IntPtr(pb), unchecked((uint)count), 0u);
}
if (!success)
throw new Win32Exception();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
if (_handle != null && !_handle.IsInvalid)
{
_handle.Close();
_handle = null;
}
if (_owner != null)
{
_owner.RemoveOwnedFile(this);
_owner = null;
}
}
}
// TODO: Seems like the right place for SFileGetFileInfo, but will need to determine
// what value add these features have except for sophisticated debugging purposes
// (like in Ladis' MPQ Editor app).
public int ChecksumCrc32
{
get
{
throw new NotImplementedException();
}
}
public byte[] GetMd5Hash()
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace StormLibSharp.Native
{
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
internal delegate void SFILE_DOWNLOAD_CALLBACK(IntPtr pvUserData, ulong byteOffset, uint dwTotalBytes);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
internal delegate void SFILE_COMPACT_CALLBACK(IntPtr pvUserData, uint dwWorkType, ulong bytesProcessed, ulong totalBytes);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
internal delegate void SFILE_ADDFILE_CALLBACK(IntPtr pvUserData, uint dwBytesWritte, uint dwTotalBytes, bool bFinalCall);
}

View File

@@ -1,26 +0,0 @@
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace StormLibSharp.Native
{
internal sealed class MpqArchiveSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public MpqArchiveSafeHandle(IntPtr handle)
: base(true)
{
this.SetHandle(handle);
}
public MpqArchiveSafeHandle()
: base(true) { }
protected override bool ReleaseHandle()
{
return NativeMethods.SFileCloseArchive(this.handle);
}
}
}

View File

@@ -1,27 +0,0 @@
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace StormLibSharp.Native
{
internal sealed class MpqFileSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public MpqFileSafeHandle(IntPtr handle)
: base(true)
{
this.SetHandle(handle);
}
public MpqFileSafeHandle()
: base(true)
{
}
protected override bool ReleaseHandle()
{
return NativeMethods.SFileCloseFile(this.handle);
}
}
}

View File

@@ -1,497 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace StormLibSharp.Native
{
internal static class NativeMethods
{
private const string STORMLIB = "stormlib.dll";
#region Functions for manipulation with StormLib global flags
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern uint SFileGetLocale();
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern uint SFileSetLocale(uint lcNewLocale);
#endregion
#region Functions for archive manipulation
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileOpenArchive(
[MarshalAs(UnmanagedType.LPTStr)] string szMpqName,
uint dwPriority,
SFileOpenArchiveFlags dwFlags,
out MpqArchiveSafeHandle phMpq
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileCreateArchive(
[MarshalAs(UnmanagedType.LPTStr)] string szMpqName,
uint dwCreateFlags,
uint dwMaxFileCount,
out MpqArchiveSafeHandle phMpq
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileCreateArchive2(
[MarshalAs(UnmanagedType.LPTStr)] string szMpqName,
ref SFILE_CREATE_MPQ pCreateInfo,
out MpqArchiveSafeHandle phMpq
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileSetDownloadCallback(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.FunctionPtr)] SFILE_DOWNLOAD_CALLBACK pfnCallback,
IntPtr pvUserData
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileFlushArchive(MpqArchiveSafeHandle hMpq);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileCloseArchive(IntPtr hMpq);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileCloseArchive(MpqArchiveSafeHandle hMpq);
#endregion
#region Adds another listfile into MPQ.
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern int SFileAddListFile(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPStr)] string szListFile
);
#endregion
#region Archive compacting
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileSetCompactCallback(
MpqArchiveSafeHandle hMpq,
SFILE_COMPACT_CALLBACK compactCB,
IntPtr pvUserData
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileCompactArchive(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPStr)] string szListFile,
bool bReserved
);
#endregion
#region Maximum file count
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern uint SFileGetMaxFileCount(MpqArchiveSafeHandle hMpq);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileSetMaxFileCount(MpqArchiveSafeHandle hMpq, uint dwMaxFileCount);
#endregion
#region Changing (attributes) file
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern uint SFileGetAttributes(MpqArchiveSafeHandle hMpq);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileSetAttributes(MpqArchiveSafeHandle hMpq, uint dwFlags);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileUpdateFileAttributes(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPStr)] string szFileName
);
#endregion
#region Functions for manipulation with patch archives
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileOpenPatchArchive(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPTStr)] string szPatchMpqName,
[MarshalAs(UnmanagedType.LPStr)] string szPatchPathPrefix,
uint dwFlags
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileIsPatchedArchive(MpqArchiveSafeHandle hMpq);
#endregion
#region Functions for file manipulation
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileHasFile(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPStr)] string szFileName
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileOpenFileEx(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPStr)] string szFileName,
uint dwSearchScope,
out MpqFileSafeHandle phFile
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern uint SFileGetFileSize(MpqFileSafeHandle hFile, ref uint pdwFileSizeHigh);
public static unsafe uint SFileGetFilePointer(
MpqFileSafeHandle hFile
)
{
if (hFile.IsInvalid || hFile.IsClosed)
throw new InvalidOperationException();
IntPtr handle = hFile.DangerousGetHandle();
_TMPQFileHeader* header = (_TMPQFileHeader*)handle.ToPointer();
return header->dwFilePos;
}
//public static unsafe uint SFileGetFileSize(
// MpqFileSafeHandle hFile
// )
//{
// if (hFile.IsInvalid || hFile.IsClosed)
// throw new InvalidOperationException();
// IntPtr handle = hFile.DangerousGetHandle();
// _TMPQFileHeader* header = (_TMPQFileHeader*)handle.ToPointer();
// return header->pFileEntry->dwFileSize;
//}
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern uint SFileSetFilePointer(
MpqFileSafeHandle hFile,
uint lFilePos,
ref uint plFilePosHigh,
uint dwMoveMethod
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileReadFile(
MpqFileSafeHandle hFile,
IntPtr lpBuffer,
uint dwToRead,
out uint pdwRead,
ref System.Threading.NativeOverlapped lpOverlapped
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileCloseFile(IntPtr hFile);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileCloseFile(MpqFileSafeHandle hFile);
#region Retrieving info about a file in the archive
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileGetFileInfo(
IntPtr hMpqOrFile,
SFileInfoClass InfoClass,
IntPtr pvFileInfo,
uint cbFileInfoSize,
out uint pcbLengthNeeded
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileGetFileInfo(
MpqArchiveSafeHandle hMpqOrFile,
SFileInfoClass InfoClass,
IntPtr pvFileInfo,
uint cbFileInfoSize,
out uint pcbLengthNeeded
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileGetFileInfo(
MpqFileSafeHandle hMpqOrFile,
SFileInfoClass InfoClass,
IntPtr pvFileInfo,
uint cbFileInfoSize,
out uint pcbLengthNeeded
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileGetFileName(
MpqFileSafeHandle hFile,
[MarshalAs(UnmanagedType.LPStr)] out string szFileName
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileFreeFileInfo(
IntPtr pvFileInfo,
SFileInfoClass infoClass
);
#endregion
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileExtractFile(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPStr)] string szToExtract,
[MarshalAs(UnmanagedType.LPTStr)] string szExtracted,
uint dwSearchScope
);
#endregion
#region Functions for file and archive verification
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileGetFileChecksums(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPStr)] string szFileName,
out uint pdwCrc32,
IntPtr pMD5
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern uint SFileVerifyFile(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPStr)] string szFileName,
uint dwFlags
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern int SFileVerifyRawData(
MpqArchiveSafeHandle hMpq,
uint dwWhatToVerify,
[MarshalAs(UnmanagedType.LPStr)] string szFileName
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern uint SFileVerifyArchive(MpqArchiveSafeHandle hMpq);
#endregion
#region Functions for file searching
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern IntPtr SFileFindFirstFile(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPStr)] string szMask,
out _SFILE_FIND_DATA lpFindFileData,
[MarshalAs(UnmanagedType.LPStr)] string szListFile
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileFindNextFile(
IntPtr hFind,
[In, Out] ref _SFILE_FIND_DATA lpFindFileData
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileFindClose(IntPtr hFind);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern IntPtr SListFileFindFirstFile(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPStr)] string szListFile,
[MarshalAs(UnmanagedType.LPStr)] string szMask,
[In, Out] ref _SFILE_FIND_DATA lpFindFileData
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SListFileFindNextFile(
IntPtr hFind,
[In, Out] ref _SFILE_FIND_DATA lpFindFileData
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SListFileFindClose(IntPtr hFind);
#endregion
#region Locale support
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern int SFileEnumLocales(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPStr)] string szFileName,
IntPtr plcLocales,
ref uint pdwMaxLocales,
uint dwSearchScope
);
#endregion
#region Support for adding files to the MPQ
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileCreateFile(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPStr)] string szArchiveName,
ulong fileTime,
uint dwFileSize,
uint lcLocale,
uint dwFlags,
out IntPtr phFile
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileWriteFile(
MpqFileSafeHandle hFile,
IntPtr pvData,
uint dwSize,
uint dwCompression
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileFinishFile(MpqFileSafeHandle hFile);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileAddFileEx(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPTStr)] string szFileName,
[MarshalAs(UnmanagedType.LPStr)] string szArchivedName,
uint dwFlags,
uint dwCompression,
uint dwCompressionNext
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileAddFile(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPTStr)] string szFileName,
[MarshalAs(UnmanagedType.LPStr)] string szArchivedName,
uint dwFlags
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileAddWave(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPTStr)] string szFileName,
[MarshalAs(UnmanagedType.LPStr)] string szArchivedName,
uint dwFlags,
uint dwQuality
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileRemoveFile(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPStr)] string szFileName,
uint dwSearchScope
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileRenameFile(
MpqArchiveSafeHandle hMpq,
[MarshalAs(UnmanagedType.LPStr)] string szOldFileName,
[MarshalAs(UnmanagedType.LPStr)] string szNewFileName
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileSetFileLocale(
MpqFileSafeHandle hFile,
uint lcNewLocale
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileSetDataCompression(uint DataCompression);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern bool SFileSetAddFileCallback(
MpqArchiveSafeHandle hMpq,
SFILE_ADDFILE_CALLBACK AddFileCB,
IntPtr pvUserData
);
#endregion
#region Compression and decompression
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern int SCompImplode(
IntPtr pvOutBuffer,
ref int pcbOutBuffer,
IntPtr pvInBuffer,
int cbInBuffer
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern int SCompExplode(
IntPtr pvOutBuffer,
ref int pcbOutBuffer,
IntPtr pvInBuffer,
int cbInBuffer
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern int SCompCompress(
IntPtr pvOutBuffer,
ref int pcbOutBuffer,
IntPtr pvInBuffer,
int cbInBuffer,
uint uCompressionMask,
int nCmpType,
int nCmpLevel
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern int SCompDecompress(
IntPtr pvOutBuffer,
ref int pcbOutBuffer,
IntPtr pvInBuffer,
int cbInBuffer
);
[DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)]
public static extern int SCompDecompress2(
IntPtr pvOutBuffer,
ref int pcbOutBuffer,
IntPtr pvInBuffer,
int cbInBuffer
);
#endregion
}
#pragma warning disable 0169,0649
internal struct SFILE_CREATE_MPQ
{
public uint cbSize;
public uint dwMpqVersion;
private IntPtr pvUserData;
private uint cbUserData;
public uint dwStreamFlags;
public uint dwFileFlags1;
public uint dwFileFlags2;
public uint dwAttrFlags;
public uint dwSectorSize;
public uint dwRawChunkSize;
public uint dwMaxFileCount;
}
internal unsafe struct _SFILE_FIND_DATA
{
public fixed char cFileName[260]; // Full name of the found file
public IntPtr szPlainName; // Plain name of the found file
public uint dwHashIndex; // Hash table index for the file
public uint dwBlockIndex; // Block table index for the file
public uint dwFileSize; // File size in bytes
public uint dwFileFlags; // MPQ file flags
public uint dwCompSize; // Compressed file size
public uint dwFileTimeLo; // Low 32-bits of the file time (0 if not present)
public uint dwFileTimeHi; // High 32-bits of the file time (0 if not present)
public uint lcLocale; // Locale version
}
internal unsafe struct _TFileEntry
{
public ulong FileNameHash;
public ulong ByteOffset;
public ulong FileTime;
public uint dwHashIndex;
public uint dwFileSize;
public uint dwCmpSize;
public uint dwFlags;
public ushort lcLocale;
public ushort wPlatform;
public uint dwCrc32;
public fixed byte md5[16];
public IntPtr szFileName;
}
// Provides enough of _TMPQFile to get to the file size and current position.
internal unsafe struct _TMPQFileHeader
{
public IntPtr pStream;
public IntPtr ha;
public _TFileEntry* pFileEntry;
public uint dwFileKey;
public uint dwFilePos;
}
#pragma warning restore 0169,0649
}

View File

@@ -1,70 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace StormLibSharp.Native
{
internal enum SFileInfoClass
{
// Info classes for archives
SFileMpqFileName, // Name of the archive file (TCHAR [])
SFileMpqStreamBitmap, // Array of bits, each bit means availability of one block (BYTE [])
SFileMpqUserDataOffset, // Offset of the user data header (ULONGLONG)
SFileMpqUserDataHeader, // Raw (unfixed) user data header (TMPQUserData)
SFileMpqUserData, // MPQ USer data, without the header (BYTE [])
SFileMpqHeaderOffset, // Offset of the MPQ header (ULONGLONG)
SFileMpqHeaderSize, // Fixed size of the MPQ header
SFileMpqHeader, // Raw (unfixed) archive header (TMPQHeader)
SFileMpqHetTableOffset, // Offset of the HET table, relative to MPQ header (ULONGLONG)
SFileMpqHetTableSize, // Compressed size of the HET table (ULONGLONG)
SFileMpqHetHeader, // HET table header (TMPQHetHeader)
SFileMpqHetTable, // HET table as pointer. Must be freed using SFileFreeFileInfo
SFileMpqBetTableOffset, // Offset of the BET table, relative to MPQ header (ULONGLONG)
SFileMpqBetTableSize, // Compressed size of the BET table (ULONGLONG)
SFileMpqBetHeader, // BET table header, followed by the flags (TMPQBetHeader + DWORD[])
SFileMpqBetTable, // BET table as pointer. Must be freed using SFileFreeFileInfo
SFileMpqHashTableOffset, // Hash table offset, relative to MPQ header (ULONGLONG)
SFileMpqHashTableSize64, // Compressed size of the hash table (ULONGLONG)
SFileMpqHashTableSize, // Size of the hash table, in entries (DWORD)
SFileMpqHashTable, // Raw (unfixed) hash table (TMPQBlock [])
SFileMpqBlockTableOffset, // Block table offset, relative to MPQ header (ULONGLONG)
SFileMpqBlockTableSize64, // Compressed size of the block table (ULONGLONG)
SFileMpqBlockTableSize, // Size of the block table, in entries (DWORD)
SFileMpqBlockTable, // Raw (unfixed) block table (TMPQBlock [])
SFileMpqHiBlockTableOffset, // Hi-block table offset, relative to MPQ header (ULONGLONG)
SFileMpqHiBlockTableSize64, // Compressed size of the hi-block table (ULONGLONG)
SFileMpqHiBlockTable, // The hi-block table (USHORT [])
SFileMpqSignatures, // Signatures present in the MPQ (DWORD)
SFileMpqStrongSignatureOffset, // Byte offset of the strong signature, relative to begin of the file (ULONGLONG)
SFileMpqStrongSignatureSize, // Size of the strong signature (DWORD)
SFileMpqStrongSignature, // The strong signature (BYTE [])
SFileMpqArchiveSize64, // Archive size from the header (ULONGLONG)
SFileMpqArchiveSize, // Archive size from the header (DWORD)
SFileMpqMaxFileCount, // Max number of files in the archive (DWORD)
SFileMpqFileTableSize, // Number of entries in the file table (DWORD)
SFileMpqSectorSize, // Sector size (DWORD)
SFileMpqNumberOfFiles, // Number of files (DWORD)
SFileMpqRawChunkSize, // Size of the raw data chunk for MD5
SFileMpqStreamFlags, // Stream flags (DWORD)
SFileMpqIsReadOnly, // Nonzero if the MPQ is read only (DWORD)
// Info classes for files
SFileInfoPatchChain, // Chain of patches where the file is (TCHAR [])
SFileInfoFileEntry, // The file entry for the file (TFileEntry)
SFileInfoHashEntry, // Hash table entry for the file (TMPQHash)
SFileInfoHashIndex, // Index of the hash table entry (DWORD)
SFileInfoNameHash1, // The first name hash in the hash table (DWORD)
SFileInfoNameHash2, // The second name hash in the hash table (DWORD)
SFileInfoNameHash3, // 64-bit file name hash for the HET/BET tables (ULONGLONG)
SFileInfoLocale, // File locale (DWORD)
SFileInfoFileIndex, // Block index (DWORD)
SFileInfoByteOffset, // File position in the archive (ULONGLONG)
SFileInfoFileTime, // File time (ULONGLONG)
SFileInfoFileSize, // Size of the file (DWORD)
SFileInfoCompressedSize, // Compressed file size (DWORD)
SFileInfoFlags, // File flags from (DWORD)
SFileInfoEncryptionKey, // File encryption key
SFileInfoEncryptionKeyRaw, // Unfixed value of the file key
}
}

View File

@@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace StormLibSharp.Native
{
[Flags]
internal enum SFileOpenArchiveFlags : uint
{
None = 0,
TypeIsFile = None,
TypeIsMemoryMapped = 1,
TypeIsHttp = 2,
AccessReadOnly = 0x100,
AccessReadWriteShare = 0x200,
AccessUseBitmap = 0x400,
DontOpenListfile = 0x10000,
DontOpenAttributes = 0x20000,
DontSearchHeader = 0x40000,
ForceVersion1 = 0x80000,
CheckSectorCRC = 0x100000,
}
}

View File

@@ -1,60 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace StormLibSharp.Native
{
internal static class Win32Methods
{
[DllImport("kernel32", ExactSpelling = false, SetLastError = true)]
public static extern uint GetMappedFileName(
IntPtr hProcess,
IntPtr fileHandle,
IntPtr lpFilename,
uint nSize
);
[DllImport("kernel32", ExactSpelling = false, SetLastError = true)]
public static extern uint GetFinalPathNameByHandle(
IntPtr hFile,
IntPtr lpszFilePath,
uint cchFilePath,
uint dwFlags
);
[DllImport("kernel32", SetLastError = false, ExactSpelling = false)]
public static extern int GetLastError();
public static string GetFileNameOfMemoryMappedFile(MemoryMappedFile file)
{
const uint size = 522;
IntPtr path = Marshal.AllocCoTaskMem(unchecked((int)size)); // MAX_PATH + 1 char
string result = null;
try
{
// constant 0x2 = VOLUME_NAME_NT
uint test = GetFinalPathNameByHandle(file.SafeMemoryMappedFileHandle.DangerousGetHandle(), path, size, 0x2);
if (test != 0)
throw new Win32Exception();
result = Marshal.PtrToStringAuto(path);
}
catch
{
uint test = GetMappedFileName(Process.GetCurrentProcess().Handle, file.SafeMemoryMappedFileHandle.DangerousGetHandle(), path, size);
if (test != 0)
throw new Win32Exception();
result = Marshal.PtrToStringAuto(path);
}
return result;
}
}
}

1
BurnOutSharp/External/hllib vendored Submodule

View File

@@ -1,146 +1,128 @@
using System;
using System.IO;
using System.Linq;
namespace BurnOutSharp.External.psxt001z
{
/// <summary>
/// LibCrypt detection code
/// Originally written by Dremora: https://github.com/Dremora/psxt001z
/// Ported and changed by darksabre76
/// </summary>
public class LibCrypt
/// <summary>
/// LibCrypt detection code
/// Originally written by Dremora: https://github.com/Dremora/psxt001z
/// Ported and changed by darksabre76
/// </summary>
public class LibCrypt
{
public static bool CheckSubfile(string subFilePath)
{
// Check the file exists first
if (!File.Exists(subFilePath))
{
Console.WriteLine($"{subFilePath} could not be found");
return false;
}
// Check the file exists first
if (!File.Exists(subFilePath))
return false;
// Check the extension is a subfile
string ext = Path.GetExtension(subFilePath).TrimStart('.').ToLowerInvariant();
if (ext != "sub")
{
Console.WriteLine($"{ext}: unknown file extension");
return false;
}
// Check the extension is a subfile
string ext = Path.GetExtension(subFilePath).TrimStart('.').ToLowerInvariant();
if (ext != "sub")
return false;
// Open and check the subfile for LibCrypt
try
{
using (FileStream subfile = File.OpenRead(subFilePath))
{
return CheckSubfile(subfile);
}
}
catch
{
Console.WriteLine($"Error processing {subFilePath}");
return false;
}
}
// Open and check the subfile for LibCrypt
try
{
using (FileStream subfile = File.OpenRead(subFilePath))
{
return CheckSubfile(subfile);
}
}
catch
{
return false;
}
}
public static bool CheckSubfile(Stream subfile)
{
// Check the length is valid for subfiles
long size = subfile.Length;
if (size % 96 != 0)
{
Console.WriteLine($"Wrong size");
return false;
}
public static bool CheckSubfile(Stream subfile)
{
// Check the length is valid for subfiles
long size = subfile.Length;
if (size % 96 != 0)
return false;
// Persistent values
byte[] buffer = new byte[16];
byte[] sub = new byte[16];
int tpos = 0;
int modifiedSectors = 0;
// Persistent values
byte[] buffer = new byte[16];
byte[] sub = new byte[16];
int tpos = 0;
int modifiedSectors = 0;
// Check each sector for modifications
for (uint sector = 150; sector < ((size / 96) + 150); sector++)
{
subfile.Seek(12, SeekOrigin.Current);
if (subfile.Read(buffer, 0, 12) == 0)
return modifiedSectors != 0;
// Check each sector for modifications
for (uint sector = 150; sector < ((size / 96) + 150); sector++)
{
subfile.Seek(12, SeekOrigin.Current);
if (subfile.Read(buffer, 0, 12) == 0)
return modifiedSectors != 0;
subfile.Seek(72, SeekOrigin.Current);
subfile.Seek(72, SeekOrigin.Current);
// New track
if ((btoi(buffer[1]) == (btoi(sub[1]) + 1)) && (buffer[2] == 0 || buffer[2] == 1))
{
Array.Copy(buffer, sub, 6);
tpos = ((btoi((byte)(buffer[3] * 60)) + btoi(buffer[4])) * 75) + btoi(buffer[5]);
}
// New track
if ((btoi(buffer[1]) == (btoi(sub[1]) + 1)) && (buffer[2] == 0 || buffer[2] == 1))
{
Array.Copy(buffer, sub, 6);
tpos = ((btoi((byte)(buffer[3] * 60)) + btoi(buffer[4])) * 75) + btoi(buffer[5]);
}
// New index
else if (btoi(buffer[2]) == (btoi(sub[2]) + 1) && buffer[1] == sub[1])
{
Array.Copy(buffer, 2, sub, 2, 4);
tpos = ((btoi((byte)(buffer[3] * 60)) + btoi(buffer[4])) * 75) + btoi(buffer[5]);
}
// New index
else if (btoi(buffer[2]) == (btoi(sub[2]) + 1) && buffer[1] == sub[1])
{
Array.Copy(buffer, 2, sub, 2, 4);
tpos = ((btoi((byte)(buffer[3] * 60)) + btoi(buffer[4])) * 75) + btoi(buffer[5]);
}
// MSF1 [3-5]
else
{
if (sub[2] == 0)
tpos--;
else
tpos++;
// MSF1 [3-5]
else
{
if (sub[2] == 0)
tpos--;
else
tpos++;
sub[3] = itob((byte)(tpos / 60 / 75));
sub[4] = itob((byte)((tpos / 75) % 60));
sub[5] = itob((byte)(tpos % 75));
}
sub[3] = itob((byte)(tpos / 60 / 75));
sub[4] = itob((byte)((tpos / 75) % 60));
sub[5] = itob((byte)(tpos % 75));
}
// MSF2 [7-9]
sub[7] = itob((byte)(sector / 60 / 75));
sub[8] = itob((byte)((sector / 75) % 60));
sub[9] = itob((byte)(sector % 75));
// MSF2 [7-9]
sub[7] = itob((byte)(sector / 60 / 75));
sub[8] = itob((byte)((sector / 75) % 60));
sub[9] = itob((byte)(sector % 75));
// CRC-16 [10-11]
ushort crc = CRC16.Calculate(sub, 0, 10);
byte[] crcBytes = BitConverter.GetBytes(crc);
sub[10] = crcBytes[0];
sub[11] = crcBytes[1];
// CRC-16 [10-11]
ushort crc = CRC16.Calculate(sub, 0, 10);
byte[] crcBytes = BitConverter.GetBytes(crc);
sub[10] = crcBytes[0];
sub[11] = crcBytes[1];
// TODO: This *was* a memcmp, but that's harder to do. Fix this for C# later
if (buffer[10] != sub[10] && buffer[11] != sub[11] && (buffer[3] != sub[3] || buffer[4] != sub[4] || buffer[5] != sub[5] || buffer[7] != sub[7] || buffer[8] != sub[8] || buffer[9] != sub[9]))
{
if (buffer[3] != sub[3] || buffer[4] != sub[4] || buffer[5] != sub[5] || buffer[7] != sub[7] || buffer[8] != sub[8] || buffer[9] != sub[9] || buffer[10] != sub[10] || buffer[11] != sub[11])
{
Console.Write($"MSF: {sub[7]:x}:{sub[8]:x}:{sub[9]:x} Q-Data: {buffer[0]:x}{buffer[1]:x}{buffer[2]:x} {buffer[3]:x}:{buffer[4]:x}:{buffer[5]:x} {buffer[6]:x} {buffer[7]:x}:{buffer[8]:x}:{buffer[9]:x} {buffer[10]:x}{buffer[11]:x} xor {crc ^ ((buffer[10] << 8) + buffer[11]):x} % {CRC16.Calculate(buffer, 0, 10) ^ ((buffer[10] << 8) + buffer[11]):x}");
//Console.Write($"\nMSF: {sub[7]:x}:{sub[8]:x}:{sub[9]:x} Q-Data: {sub[0]:x}{sub[1]:x}{sub[2]:x} {sub[3]:x}:{sub[4]:x}:{sub[5]:x} {sub[6]:x} {sub[7]:x}:{sub[8]:x}:{sub[9]:x} {sub[10]:x}{sub[11]:x}");
if (buffer[3] != sub[3] && buffer[7] != sub[7] && buffer[4] == sub[4] && buffer[8] == sub[8] && buffer[5] == sub[5] && buffer[9] == sub[9])
Console.Write($" P1 xor {buffer[3] ^ sub[3]:x} {buffer[7] ^ sub[7]:x}");
else if (buffer[3] == sub[3] && buffer[7] == sub[7] && buffer[4] != sub[4] && buffer[8] != sub[8] && buffer[5] == sub[5] && buffer[9] == sub[9])
Console.Write($" P2 xor {buffer[4] ^ sub[4]:x} {buffer[8] ^ sub[8]:x}");
else if (buffer[3] == sub[3] && buffer[7] == sub[7] && buffer[4] == sub[4] && buffer[8] == sub[8] && buffer[5] != sub[5] && buffer[9] != sub[9])
Console.Write($" P3 xor {buffer[5] ^ sub[5]:x} {buffer[9] ^ sub[9]:x}");
else
Console.Write(" ?");
// If any byte (except position 6) is different, it's a modified sector
for (int i = 0; i < 12; i++)
{
if (i == 6)
continue;
Console.Write("\n");
modifiedSectors++;
}
}
}
if (buffer[i] != sub[i])
{
modifiedSectors++;
break;
}
}
}
Console.WriteLine($"Number of modified sectors: {modifiedSectors}");
return modifiedSectors != 0;
}
return modifiedSectors != 0;
}
private static byte btoi(byte b)
{
/* BCD to u_char */
return (byte)((b) / 16 * 10 + (b) % 16);
}
private static byte btoi(byte b)
{
/* BCD to u_char */
return (byte)((b) / 16 * 10 + (b) % 16);
}
private static byte itob(byte i)
{
/* u_char to BCD */
return (byte)((i) / 10 * 16 + (i) % 10);
}
}
private static byte itob(byte i)
{
/* u_char to BCD */
return (byte)((i) / 10 * 16 + (i) % 10);
}
}
}

View File

@@ -7,20 +7,32 @@ using SharpCompress.Compressors.Deflate;
namespace BurnOutSharp.FileType
{
internal class BFPK
internal class BFPK : IScannable
{
public static bool ShouldScan(byte[] magic)
/// <inheritdoc/>
public bool ShouldScan(byte[] magic)
{
if (magic.StartsWith(new byte[] { 0x42, 0x46, 0x50, 0x4b }))
if (magic.StartsWith(new byte?[] { 0x42, 0x46, 0x50, 0x4b }))
return true;
return false;
}
public static List<string> Scan(Stream stream, bool includePosition = false)
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
List<string> protections = new List<string>();
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the BFPK file itself fails
try
{
@@ -87,36 +99,31 @@ namespace BurnOutSharp.FileType
}
}
}
string protection = ProtectionFind.ScanContent(tempFile, includePosition);
// If tempfile cleanup fails
try
{
File.Delete(tempFile);
}
catch { }
if (!string.IsNullOrEmpty(protection))
protections.Add(tempFile);
}
catch { }
br.BaseStream.Seek(current, SeekOrigin.Begin);
}
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
catch { }
return protections;
return null;
}
}
}

View File

@@ -6,21 +6,33 @@ using SharpCompress.Compressors.BZip2;
namespace BurnOutSharp.FileType
{
internal class BZip2
internal class BZip2 : IScannable
{
public static bool ShouldScan(byte[] magic)
/// <inheritdoc/>
public bool ShouldScan(byte[] magic)
{
if (magic.StartsWith(new byte[] { 0x42, 0x52, 0x68 }))
if (magic.StartsWith(new byte?[] { 0x42, 0x52, 0x68 }))
return true;
return false;
}
public static List<string> Scan(Stream stream, bool includePosition = false)
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
List<string> protections = new List<string>();
if (!File.Exists(file))
return null;
// If the 7-zip file itself fails
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the BZip2 file itself fails
try
{
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
@@ -31,37 +43,33 @@ namespace BurnOutSharp.FileType
// If an individual entry fails
try
{
string tempfile = Path.Combine(tempPath, Guid.NewGuid().ToString());
using (FileStream fs = File.OpenWrite(tempfile))
string tempFile = Path.Combine(tempPath, Guid.NewGuid().ToString());
using (FileStream fs = File.OpenWrite(tempFile))
{
bz2File.CopyTo(fs);
}
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
// If tempfile cleanup fails
try
{
File.Delete(tempfile);
}
catch { }
if (!string.IsNullOrEmpty(protection))
protections.Add($"\r\n{protection}");
}
catch { }
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
catch { }
return protections;
return null;
}
}
}

View File

@@ -1,46 +1,67 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using BurnOutSharp.ProtectionType;
namespace BurnOutSharp.FileType
{
internal class Executable
internal class Executable : IScannable
{
public static bool ShouldScan(byte[] magic)
/// <summary>
/// Cache for all IContentCheck types
/// </summary>
private static readonly IEnumerable<IContentCheck> contentCheckClasses = InitContentCheckClasses();
/// <inheritdoc/>
public bool ShouldScan(byte[] magic)
{
// DOS MZ executable file format (and descendants)
if (magic.StartsWith(new byte[] { 0x4d, 0x5a }))
if (magic.StartsWith(new byte?[] { 0x4d, 0x5a }))
return true;
// Executable and Linkable Format
if (magic.StartsWith(new byte[] { 0x7f, 0x45, 0x4c, 0x46 }))
if (magic.StartsWith(new byte?[] { 0x7f, 0x45, 0x4c, 0x46 }))
return true;
// Mach-O binary (32-bit)
if (magic.StartsWith(new byte[] { 0xfe, 0xed, 0xfa, 0xce }))
if (magic.StartsWith(new byte?[] { 0xfe, 0xed, 0xfa, 0xce }))
return true;
// Mach-O binary (32-bit, reverse byte ordering scheme)
if (magic.StartsWith(new byte[] { 0xce, 0xfa, 0xed, 0xfe }))
if (magic.StartsWith(new byte?[] { 0xce, 0xfa, 0xed, 0xfe }))
return true;
// Mach-O binary (64-bit)
if (magic.StartsWith(new byte[] { 0xfe, 0xed, 0xfa, 0xcf }))
if (magic.StartsWith(new byte?[] { 0xfe, 0xed, 0xfa, 0xcf }))
return true;
// Mach-O binary (64-bit, reverse byte ordering scheme)
if (magic.StartsWith(new byte[] { 0xcf, 0xfa, 0xed, 0xfe }))
if (magic.StartsWith(new byte?[] { 0xcf, 0xfa, 0xed, 0xfe }))
return true;
// Prefrred Executable File Format
if (magic.StartsWith(new byte[] { 0x4a, 0x6f, 0x79, 0x21, 0x70, 0x65, 0x66, 0x66 }))
if (magic.StartsWith(new byte?[] { 0x4a, 0x6f, 0x79, 0x21, 0x70, 0x65, 0x66, 0x66 }))
return true;
return false;
}
public static List<string> Scan(Stream stream, string file = null, bool includePosition = false)
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// Load the current file content
byte[] fileContent = null;
@@ -54,201 +75,44 @@ namespace BurnOutSharp.FileType
stream.Seek(0, SeekOrigin.Begin);
// Files can be protected in multiple ways
List<string> protections = new List<string>();
List<string> subProtections = new List<string>();
string protection;
var protections = new Dictionary<string, List<string>>();
// 3PLock
protection = ThreePLock.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Iterate through all content checks
foreach (var contentCheckClass in contentCheckClasses)
{
string protection = contentCheckClass.CheckContents(file, fileContent, scanner.IncludePosition);
// ActiveMARK
protection = ActiveMARK.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// If we have a valid content check based on settings
if (!contentCheckClass.GetType().Namespace.ToLowerInvariant().Contains("packertype") || scanner.ScanPackers)
{
if (!string.IsNullOrWhiteSpace(protection))
Utilities.AppendToDictionary(protections, file, protection);
}
// Alpha-ROM
protection = AlphaROM.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Armadillo
protection = Armadillo.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// CD-Cops
protection = CDCops.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// CD-Lock
protection = CDLock.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// CDSHiELD SE
protection = CDSHiELDSE.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// CD Check
protection = CDCheck.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Cenega ProtectDVD
protection = CengaProtectDVD.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Code Lock
protection = CodeLock.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// CopyKiller
protection = CopyKiller.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Cucko (EA Custom)
protection = Cucko.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// dotFuscator
protection = dotFuscator.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// DVD-Cops
protection = DVDCops.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// EA CdKey Registration Module
protection = EACdKey.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// EXE Stealth
protection = EXEStealth.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Games for Windows - Live
protection = GFWL.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Impulse Reactor
protection = ImpulseReactor.CheckContents(file, fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Inno Setup
protection = InnoSetup.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// JoWooD X-Prot
protection = JoWooDXProt.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Key-Lock (Dongle)
protection = KeyLock.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// LaserLock
protection = LaserLock.CheckContents(file, fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// PE Compact
protection = PECompact.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// ProtectDisc
protection = ProtectDisc.CheckContents(file, fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Ring PROTECH
protection = RingPROTECH.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// SafeDisc / SafeCast
protection = SafeDisc.CheckContents(file, fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// SafeLock
protection = SafeLock.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// SecuROM
protection = SecuROM.CheckContents(file, fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// SmartE
protection = SmartE.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// SolidShield
protection = SolidShield.CheckContents(file, fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// StarForce
protection = StarForce.CheckContents(file, fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// SVK Protector
protection = SVKProtector.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Sysiphus / Sysiphus DVD
protection = Sysiphus.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// TAGES
protection = Tages.CheckContents(file, fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// VOB ProtectCD/DVD
protection = VOBProtectCDDVD.CheckContents(file, fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Wise Installer
subProtections = WiseInstaller.CheckContents(file, fileContent, includePosition);
if (subProtections != null && subProtections.Count > 0)
protections.AddRange(subProtections);
// WTM CD Protect
protection = WTMCDProtect.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Xtreme-Protector
protection = XtremeProtector.CheckContents(fileContent, includePosition);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// If we have an IScannable implementation
if (contentCheckClass is IScannable)
{
IScannable scannable = contentCheckClass as IScannable;
if (file != null && !string.IsNullOrEmpty(protection))
{
var subProtections = scannable.Scan(scanner, null, file);
Utilities.PrependToKeys(subProtections, file);
Utilities.AppendToDictionary(protections, subProtections);
}
}
}
return protections;
}
/// <summary>
/// Initialize all IContentCheck implementations
/// </summary>
private static IEnumerable<IContentCheck> InitContentCheckClasses()
{
return Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.IsClass && t.GetInterface(nameof(IContentCheck)) != null)
.Select(t => Activator.CreateInstance(t) as IContentCheck);
}
}
}

View File

@@ -6,20 +6,32 @@ using SharpCompress.Archives.GZip;
namespace BurnOutSharp.FileType
{
internal class GZIP
internal class GZIP : IScannable
{
public static bool ShouldScan(byte[] magic)
/// <inheritdoc/>
public bool ShouldScan(byte[] magic)
{
if (magic.StartsWith(new byte[] { 0x1f, 0x8b }))
if (magic.StartsWith(new byte?[] { 0x1f, 0x8b }))
return true;
return false;
}
public static List<string> Scan(Stream stream, bool includePosition = false)
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
List<string> protections = new List<string>();
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the gzip file itself fails
try
{
@@ -37,34 +49,31 @@ namespace BurnOutSharp.FileType
if (entry.IsDirectory)
continue;
string tempfile = Path.Combine(tempPath, entry.Key);
entry.WriteToFile(tempfile);
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
// If tempfile cleanup fails
try
{
File.Delete(tempfile);
}
catch { }
if (!string.IsNullOrEmpty(protection))
protections.Add($"\r\n{entry.Key} - {protection}");
string tempFile = Path.Combine(tempPath, entry.Key);
entry.WriteToFile(tempFile);
}
catch { }
}
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
catch { }
return protections;
return null;
}
}
}

View File

@@ -0,0 +1,296 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace BurnOutSharp.FileType
{
public class IniFile : IDictionary<string, string>
{
private Dictionary<string, string> _keyValuePairs = new Dictionary<string, string>();
public string this[string key]
{
get
{
if (_keyValuePairs == null)
_keyValuePairs = new Dictionary<string, string>();
key = key.ToLowerInvariant();
if (_keyValuePairs.ContainsKey(key))
return _keyValuePairs[key];
return null;
}
set
{
if (_keyValuePairs == null)
_keyValuePairs = new Dictionary<string, string>();
key = key.ToLowerInvariant();
_keyValuePairs[key] = value;
}
}
/// <summary>
/// Create an empty INI file
/// </summary>
public IniFile()
{
}
/// <summary>
/// Populate an INI file from path
/// </summary>
public IniFile(string path)
{
this.Parse(path);
}
/// <summary>
/// Populate an INI file from stream
/// </summary>
public IniFile(Stream stream)
{
this.Parse(stream);
}
/// <summary>
/// Add or update a key and value to the INI file
/// </summary>
public void AddOrUpdate(string key, string value)
{
_keyValuePairs[key.ToLowerInvariant()] = value;
}
/// <summary>
/// Remove a key from the INI file
/// </summary>
public void Remove(string key)
{
_keyValuePairs.Remove(key.ToLowerInvariant());
}
/// <summary>
/// Read an INI file based on the path
/// </summary>
public bool Parse(string path)
{
// If we don't have a file, we can't read it
if (!File.Exists(path))
return false;
using (var fileStream = File.OpenRead(path))
{
return Parse(fileStream);
}
}
/// <summary>
/// Read an INI file from a stream
/// </summary>
public bool Parse(Stream stream)
{
// If the stream is invalid or unreadable, we can't process it
if (stream == null || !stream.CanRead || stream.Position >= stream.Length - 1)
return false;
// Keys are case-insensitive by default
try
{
using (StreamReader sr = new StreamReader(stream))
{
string section = string.Empty;
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
// Comments start with ';'
if (line.StartsWith(";"))
{
// No-op, we don't process comments
}
// Section titles are surrounded by square brackets
else if (line.StartsWith("["))
{
section = line.TrimStart('[').TrimEnd(']');
}
// Valid INI lines are in the format key=value
else if (line.Contains("="))
{
// Split the line by '=' for key-value pairs
string[] data = line.Split('=');
// If the value field contains an '=', we need to put them back in
string key = data[0].Trim();
string value = string.Join("=", data.Skip(1)).Trim();
// Section names are prepended to the key with a '.' separating
if (!string.IsNullOrEmpty(section))
key = $"{section}.{key}";
// Set or overwrite keys in the returned dictionary
_keyValuePairs[key.ToLowerInvariant()] = value;
}
// All other lines are ignored
}
}
}
catch
{
// We don't care what the error was, just catch and return
return false;
}
return true;
}
/// <summary>
/// Write an INI file to a path
/// </summary>
public bool Write(string path)
{
// If we don't have a valid dictionary with values, we can't write out
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
return false;
using (var fileStream = File.OpenWrite(path))
{
return Write(fileStream);
}
}
/// <summary>
/// Write an INI file to a stream
/// </summary>
public bool Write(Stream stream)
{
// If we don't have a valid dictionary with values, we can't write out
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
return false;
// If the stream is invalid or unwritable, we can't output to it
if (stream == null || !stream.CanWrite || stream.Position >= stream.Length - 1)
return false;
try
{
using (StreamWriter sw = new StreamWriter(stream))
{
// Order the dictionary by keys to link sections together
var orderedKeyValuePairs = _keyValuePairs.OrderBy(kvp => kvp.Key);
string section = string.Empty;
foreach (var keyValuePair in orderedKeyValuePairs)
{
// Extract the key and value
string key = keyValuePair.Key;
string value = keyValuePair.Value;
// We assume '.' is a section name separator
if (key.Contains('.'))
{
// Split the key by '.'
string[] data = keyValuePair.Key.Split('.');
// If the key contains an '.', we need to put them back in
string newSection = data[0].Trim();
key = string.Join(".", data.Skip(1)).Trim();
// If we have a new section, write it out
if (!string.Equals(newSection, section, StringComparison.OrdinalIgnoreCase))
{
sw.WriteLine($"[{newSection}]");
section = newSection;
}
}
// Now write out the key and value in a standardized way
sw.WriteLine($"{key}={value}");
}
}
}
catch
{
// We don't care what the error was, just catch and return
return false;
}
return true;
}
#region IDictionary Impelementations
public ICollection<string> Keys => ((IDictionary<string, string>)_keyValuePairs).Keys;
public ICollection<string> Values => ((IDictionary<string, string>)_keyValuePairs).Values;
public int Count => ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Count;
public bool IsReadOnly => ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).IsReadOnly;
public void Add(string key, string value)
{
((IDictionary<string, string>)_keyValuePairs).Add(key.ToLowerInvariant(), value);
}
bool IDictionary<string, string>.Remove(string key)
{
return ((IDictionary<string, string>)_keyValuePairs).Remove(key.ToLowerInvariant());
}
public bool TryGetValue(string key, out string value)
{
return ((IDictionary<string, string>)_keyValuePairs).TryGetValue(key.ToLowerInvariant(), out value);
}
public void Add(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Add(newItem);
}
public void Clear()
{
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Clear();
}
public bool Contains(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
return ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Contains(newItem);
}
public bool ContainsKey(string key)
{
return _keyValuePairs.ContainsKey(key.ToLowerInvariant());
}
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
{
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).CopyTo(array, arrayIndex);
}
public bool Remove(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
return ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Remove(newItem);
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return ((IEnumerable<KeyValuePair<string, string>>)_keyValuePairs).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_keyValuePairs).GetEnumerator();
}
#endregion
}
}

View File

@@ -6,21 +6,33 @@ using UnshieldSharp;
namespace BurnOutSharp.FileType
{
internal class InstallShieldCAB
internal class InstallShieldCAB : IScannable
{
public static bool ShouldScan(byte[] magic)
/// <inheritdoc/>
public bool ShouldScan(byte[] magic)
{
if (magic.StartsWith(new byte[] { 0x49, 0x53, 0x63 }))
if (magic.StartsWith(new byte?[] { 0x49, 0x53, 0x63 }))
return true;
return false;
}
// TODO: Add stream opening support
public static List<string> Scan(string file, bool includePosition = false)
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
List<string> protections = new List<string>();
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
// TODO: Add stream opening support
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// Get the name of the first cabinet file or header
string directory = Path.GetDirectoryName(file);
string noExtension = Path.GetFileNameWithoutExtension(file);
@@ -48,35 +60,30 @@ namespace BurnOutSharp.FileType
try
{
string tempFile = Path.Combine(tempPath, cabfile.FileName(i));
if (cabfile.FileSave(i, tempFile))
{
string protection = ProtectionFind.ScanContent(tempFile, includePosition);
// If tempfile cleanup fails
try
{
File.Delete(tempFile);
}
catch { }
if (!string.IsNullOrEmpty(protection))
protections.Add($"\r\n{cabfile.FileName(i)} - {protection}");
}
cabfile.FileSave(i, tempFile);
}
catch { }
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
catch { }
}
return protections;
return null;
}
}
}

View File

@@ -5,21 +5,33 @@ using StormLibSharp;
namespace BurnOutSharp.FileType
{
internal class MPQ
internal class MPQ : IScannable
{
public static bool ShouldScan(byte[] magic)
/// <inheritdoc/>
public bool ShouldScan(byte[] magic)
{
if (magic.StartsWith(new byte[] { 0x4d, 0x50, 0x51, 0x1a }))
if (magic.StartsWith(new byte?[] { 0x4d, 0x50, 0x51, 0x1a }))
return true;
return false;
}
// TODO: Add stream opening support
public static List<string> Scan(string file, bool includePosition = false)
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
List<string> protections = new List<string>();
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
// TODO: Add stream opening support
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the mpq file itself fails
try
{
@@ -28,51 +40,55 @@ namespace BurnOutSharp.FileType
using (MpqArchive mpqArchive = new MpqArchive(file, FileAccess.Read))
{
// Try to open the listfile
string listfile = null;
MpqFileStream listStream = mpqArchive.OpenFile("(listfile)");
bool canRead = listStream.CanRead;
// If we can't read the listfile, we just return
if (!listStream.CanRead)
return null;
// Read the listfile in for processing
using (StreamReader sr = new StreamReader(listStream))
{
listfile = sr.ReadToEnd();
Console.WriteLine(listfile);
}
string sub = string.Empty;
while ((sub = listfile) != null)
// Split the listfile by newlines
string[] listfileLines = listfile.Replace("\r\n", "\n").Split('\n');
// Loop over each entry
foreach (string sub in listfileLines)
{
// If an individual entry fails
try
{
string tempfile = Path.Combine(tempPath, sub);
Directory.CreateDirectory(Path.GetDirectoryName(tempfile));
mpqArchive.ExtractFile(sub, tempfile);
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
// If tempfile cleanup fails
try
{
File.Delete(tempfile);
}
catch { }
if (!string.IsNullOrEmpty(protection))
protections.Add($"\r\n{sub} - {protection}");
string tempFile = Path.Combine(tempPath, sub);
Directory.CreateDirectory(Path.GetDirectoryName(tempFile));
mpqArchive.ExtractFile(sub, tempFile);
}
catch { }
}
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
catch { }
return protections;
return null;
}
}
}

View File

@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.IO;
using WixToolset.Dtf.WindowsInstaller;
namespace BurnOutSharp.FileType
{
internal class MSI : IScannable
{
/// <inheritdoc/>
public bool ShouldScan(byte[] magic)
{
if (magic.StartsWith(new byte?[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }))
return true;
return false;
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
// TODO: Add stream opening support
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the MSI file itself fails
try
{
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempPath);
using (Database msidb = new Database(file, DatabaseOpenMode.ReadOnly))
{
msidb.ExportAll(tempPath);
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
catch { }
return null;
}
}
}

View File

@@ -5,21 +5,34 @@ using LibMSPackN;
namespace BurnOutSharp.FileType
{
internal class MicrosoftCAB
// Specification available at http://download.microsoft.com/download/5/0/1/501ED102-E53F-4CE0-AA6B-B0F93629DDC6/Exchange/%5BMS-CAB%5D.pdf
internal class MicrosoftCAB : IScannable
{
public static bool ShouldScan(byte[] magic)
/// <inheritdoc/>
public bool ShouldScan(byte[] magic)
{
if (magic.StartsWith(new byte[] { 0x4d, 0x53, 0x43, 0x46 }))
if (magic.StartsWith(new byte?[] { 0x4d, 0x53, 0x43, 0x46 }))
return true;
return false;
}
// TODO: Add stream opening support
public static List<string> Scan(string file, bool includePosition = false)
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
List<string> protections = new List<string>();
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
// TODO: Add stream opening support
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the cab file itself fails
try
{
@@ -33,34 +46,32 @@ namespace BurnOutSharp.FileType
// If an individual entry fails
try
{
string tempfile = Path.Combine(tempPath, sub.Filename);
sub.ExtractTo(tempfile);
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
// If tempfile cleanup fails
try
{
File.Delete(tempfile);
}
catch { }
if (!string.IsNullOrEmpty(protection))
protections.Add($"\r\n{sub.Filename} - {protection}");
// The trim here is for some very odd and stubborn files
string tempFile = Path.Combine(tempPath, sub.Filename.TrimEnd('.'));
sub.ExtractTo(tempFile);
}
catch { }
}
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
catch { }
return protections;
return null;
}
}
}

View File

@@ -6,29 +6,41 @@ using SharpCompress.Archives.Zip;
namespace BurnOutSharp.FileType
{
internal class PKZIP
internal class PKZIP : IScannable
{
public static bool ShouldScan(byte[] magic)
/// <inheritdoc/>
public bool ShouldScan(byte[] magic)
{
// PKZIP
if (magic.StartsWith(new byte[] { 0x50, 0x4b, 0x03, 0x04 }))
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x03, 0x04 }))
return true;
// PKZIP (Empty Archive)
if (magic.StartsWith(new byte[] { 0x50, 0x4b, 0x05, 0x06 }))
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x05, 0x06 }))
return true;
// PKZIP (Spanned Archive)
if (magic.StartsWith(new byte[] { 0x50, 0x4b, 0x07, 0x08 }))
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x07, 0x08 }))
return true;
return false;
}
public static List<string> Scan(Stream stream, bool includePosition = false)
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
List<string> protections = new List<string>();
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the zip file itself fails
try
{
@@ -46,34 +58,31 @@ namespace BurnOutSharp.FileType
if (entry.IsDirectory)
continue;
string tempfile = Path.Combine(tempPath, entry.Key);
entry.WriteToFile(tempfile);
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
// If tempfile cleanup fails
try
{
File.Delete(tempfile);
}
catch { }
if (!string.IsNullOrEmpty(protection))
protections.Add($"\r\n{entry.Key} - {protection}");
string tempFile = Path.Combine(tempPath, entry.Key);
entry.WriteToFile(tempFile);
}
catch { }
}
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
catch { }
return protections;
return null;
}
}
}

View File

@@ -6,25 +6,37 @@ using SharpCompress.Archives.Rar;
namespace BurnOutSharp.FileType
{
internal class RAR
internal class RAR : IScannable
{
public static bool ShouldScan(byte[] magic)
/// <inheritdoc/>
public bool ShouldScan(byte[] magic)
{
// RAR archive version 1.50 onwards
if (magic.StartsWith(new byte[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 }))
if (magic.StartsWith(new byte?[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 }))
return true;
// RAR archive version 5.0 onwards
if (magic.StartsWith(new byte[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 }))
if (magic.StartsWith(new byte?[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 }))
return true;
return false;
}
public static List<string> Scan(Stream stream, bool includePosition = false)
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
List<string> protections = new List<string>();
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the rar file itself fails
try
{
@@ -42,34 +54,31 @@ namespace BurnOutSharp.FileType
if (entry.IsDirectory)
continue;
string tempfile = Path.Combine(tempPath, entry.Key);
entry.WriteToFile(tempfile);
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
// If tempfile cleanup fails
try
{
File.Delete(tempfile);
}
catch { }
if (!string.IsNullOrEmpty(protection))
protections.Add($"\r\n{entry.Key} - {protection}");
string tempFile = Path.Combine(tempPath, entry.Key);
entry.WriteToFile(tempFile);
}
catch { }
}
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
catch { }
return protections;
return null;
}
}
}

View File

@@ -6,20 +6,32 @@ using SharpCompress.Archives.SevenZip;
namespace BurnOutSharp.FileType
{
internal class SevenZip
internal class SevenZip : IScannable
{
public static bool ShouldScan(byte[] magic)
/// <inheritdoc/>
public bool ShouldScan(byte[] magic)
{
if (magic.StartsWith(new byte[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c }))
if (magic.StartsWith(new byte?[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c }))
return true;
return false;
}
public static List<string> Scan(Stream stream, bool includePosition = false)
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
List<string> protections = new List<string>();
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the 7-zip file itself fails
try
{
@@ -37,34 +49,31 @@ namespace BurnOutSharp.FileType
if (entry.IsDirectory)
continue;
string tempfile = Path.Combine(tempPath, entry.Key);
entry.WriteToFile(tempfile);
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
// If tempfile cleanup fails
try
{
File.Delete(tempfile);
}
catch { }
if (!string.IsNullOrEmpty(protection))
protections.Add($"\r\n{entry.Key} - {protection}");
string tempFile = Path.Combine(tempPath, entry.Key);
entry.WriteToFile(tempFile);
}
catch { }
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
}
catch { }
return protections;
return null;
}
}
}

View File

@@ -6,23 +6,35 @@ using SharpCompress.Archives.Tar;
namespace BurnOutSharp.FileType
{
internal class TapeArchive
internal class TapeArchive : IScannable
{
public static bool ShouldScan(byte[] magic)
/// <inheritdoc/>
public bool ShouldScan(byte[] magic)
{
if (magic.StartsWith(new byte[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30 }))
if (magic.StartsWith(new byte?[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30 }))
return true;
if (magic.StartsWith(new byte[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00 }))
if (magic.StartsWith(new byte?[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00 }))
return true;
return false;
}
public static List<string> Scan(Stream stream, bool includePosition = false)
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
List<string> protections = new List<string>();
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the tar file itself fails
try
{
@@ -40,34 +52,31 @@ namespace BurnOutSharp.FileType
if (entry.IsDirectory)
continue;
string tempfile = Path.Combine(tempPath, entry.Key);
entry.WriteToFile(tempfile);
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
// If tempfile cleanup fails
try
{
File.Delete(tempfile);
}
catch { }
if (!string.IsNullOrEmpty(protection))
protections.Add($"\r\n{entry.Key} - {protection}");
string tempFile = Path.Combine(tempPath, entry.Key);
entry.WriteToFile(tempFile);
}
catch { }
}
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
catch { }
return protections;
return null;
}
}
}

View File

@@ -5,52 +5,81 @@ using System.Text;
namespace BurnOutSharp.FileType
{
internal class Textfile
internal class Textfile : IScannable
{
public static bool ShouldScan(byte[] magic, string extension)
/// <inheritdoc/>
public bool ShouldScan(byte[] magic)
{
return ShouldScan(magic, null);
}
/// <summary>
/// Determine if a file signature or extension matches one of the expected values
/// </summary>
/// <param name="magic">Byte array representing the file header</param>
/// <param name="extension">Extension for the file being checked</param>
/// <returns>True if the signature is valid, false otherwise</returns>
public bool ShouldScan(byte[] magic, string extension)
{
// Rich Text File
if (magic.StartsWith(new byte[] { 0x7b, 0x5c, 0x72, 0x74, 0x66, 0x31 }))
if (magic.StartsWith(new byte?[] { 0x7b, 0x5c, 0x72, 0x74, 0x66, 0x31 }))
return true;
// HTML
if (magic.StartsWith(new byte[] { 0x3c, 0x68, 0x74, 0x6d, 0x6c }))
if (magic.StartsWith(new byte?[] { 0x3c, 0x68, 0x74, 0x6d, 0x6c }))
return true;
// HTML and XML
if (magic.StartsWith(new byte[] { 0x3c, 0x21, 0x44, 0x4f, 0x43, 0x54, 0x59, 0x50, 0x45 }))
if (magic.StartsWith(new byte?[] { 0x3c, 0x21, 0x44, 0x4f, 0x43, 0x54, 0x59, 0x50, 0x45 }))
return true;
// Microsoft Office File (old)
if (magic.StartsWith(new byte[] { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }))
if (magic.StartsWith(new byte?[] { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }))
return true;
// Generic textfile (no header)
if (string.Equals(extension, "txt", StringComparison.OrdinalIgnoreCase))
if (string.Equals(extension?.TrimStart('.'), "txt", StringComparison.OrdinalIgnoreCase))
return true;
return false;
}
public static List<string> Scan(Stream stream, bool includePosition = false)
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
List<string> protections = new List<string>();
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// Files can be protected in multiple ways
var protections = new Dictionary<string, List<string>>();
try
{
// Load the current file content
string fileContent = null;
using (StreamReader sr = new StreamReader(stream, Encoding.Default, false, 1024 * 1024, true))
using (var sr = new StreamReader(stream, Encoding.Default, true, 1024 * 1024, true))
{
fileContent = sr.ReadToEnd();
}
// CD-Key
if (fileContent.Contains("a valid serial number is required")
|| fileContent.Contains("serial number is located"))
{
protections.Add("CD-Key / Serial");
}
if (fileContent.Contains("a valid serial number is required"))
Utilities.AppendToDictionary(protections, file, "CD-Key / Serial");
else if (fileContent.Contains("serial number is located"))
Utilities.AppendToDictionary(protections, file, "CD-Key / Serial");
// MediaMax
if (fileContent.Contains("MediaMax technology"))
Utilities.AppendToDictionary(protections, file, "MediaMax CD-3");
}
catch
{

File diff suppressed because it is too large Load Diff

View File

@@ -5,21 +5,33 @@ using SharpCompress.Compressors.Xz;
namespace BurnOutSharp.FileType
{
internal class XZ
internal class XZ : IScannable
{
public static bool ShouldScan(byte[] magic)
/// <inheritdoc/>
public bool ShouldScan(byte[] magic)
{
if (magic.StartsWith(new byte[] { 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00 }))
if (magic.StartsWith(new byte?[] { 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00 }))
return true;
return false;
}
public static List<string> Scan(Stream stream, bool includePosition = false)
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
List<string> protections = new List<string>();
if (!File.Exists(file))
return null;
// If the 7-zip file itself fails
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the xz file itself fails
try
{
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
@@ -30,37 +42,33 @@ namespace BurnOutSharp.FileType
// If an individual entry fails
try
{
string tempfile = Path.Combine(tempPath, Guid.NewGuid().ToString());
using (FileStream fs = File.OpenWrite(tempfile))
string tempFile = Path.Combine(tempPath, Guid.NewGuid().ToString());
using (FileStream fs = File.OpenWrite(tempFile))
{
xzFile.CopyTo(fs);
}
string protection = ProtectionFind.ScanContent(tempfile, includePosition);
// If tempfile cleanup fails
try
{
File.Delete(tempfile);
}
catch { }
if (!string.IsNullOrEmpty(protection))
protections.Add($"\r\n{protection}");
}
catch { }
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
catch { }
return protections;
return null;
}
}
}

View File

@@ -0,0 +1,15 @@
namespace BurnOutSharp
{
internal interface IContentCheck
{
/// <summary>
/// Check a path for protections based on file contents
/// </summary>
/// <param name="file">File to check for protection indicators</param>
/// <param name="fileContent">Byte array representing the file contents</param>
/// <param name="includePosition">True to include positional data, false otherwise</param>
/// <returns>String containing any protections found in the file</returns>
/// TODO: This should be replaced with a "GenerateMatchers" that produces a list of matchers to be run instead
string CheckContents(string file, byte[] fileContent, bool includePosition);
}
}

View File

@@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace BurnOutSharp
{
internal interface IPathCheck
{
/// <summary>
/// Check a file path for protections based on path name
/// </summary>
/// <param name="path">Path to check for protection indicators</param>
/// <param name="files">Enumerable of strings representing files in a directory</param>
/// <remarks>This can do some limited content checking as well, but it's suggested to use IContentCheck instead, if possible</remarks>
List<string> CheckDirectoryPath(string path, IEnumerable<string> files);
/// <summary>
/// Check a file path for protections based on path name
/// </summary>
/// <param name="path">Path to check for protection indicators</param>
/// <remarks>This can do some limited content checking as well, but it's suggested to use IContentCheck instead, if possible</remarks>
string CheckFilePath(string path);
}
}

View File

@@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.IO;
namespace BurnOutSharp
{
internal interface IScannable
{
/// <summary>
/// Determine if a file signature matches one of the expected values
/// </summary>
/// <param name="magic">Byte array representing the file header</param>
/// <returns>True if the signature is valid, false otherwise</returns>
bool ShouldScan(byte[] magic);
/// <summary>
/// Scan a file for all internal protections
/// </summary>
/// <param name="scanner">Scanner object for state tracking</param>
/// <param name="file">Path to the input file</param>
/// <returns>Dictionary mapping paths to protection lists</returns>
/// <remarks>Ideally, this should just point to the other scan implementation</remarks>
Dictionary<string, List<string>> Scan(Scanner scanner, string file);
/// <summary>
/// Scan a stream for all internal protections
/// </summary>
/// <param name="scanner">Scanner object for state tracking</param>
/// <param name="stream">Stream representing the input file</param>
/// <param name="file">Path to the input file</param>
/// <returns>Dictionary mapping paths to protection lists</returns>
Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string filename);
}
}

View File

@@ -0,0 +1,99 @@
namespace BurnOutSharp.Matching
{
/// <summary>
/// Content matching criteria
/// </summary>
internal class ContentMatch : IMatch<byte?[]>
{
/// <summary>
/// Content to match
/// </summary>
public byte?[] Needle { get; set; }
/// <summary>
/// Starting index for matching
/// </summary>
public int Start { get; set; }
/// <summary>
/// Ending index for matching
/// </summary>
public int End { get; set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="needle">Byte array representing the search</param>
/// <param name="start">Optional starting index</param>
/// <param name="end">Optional ending index</param>
public ContentMatch(byte?[] needle, int start = -1, int end = -1)
{
Needle = needle;
Start = start;
End = end;
}
#region Matching
/// <summary>
/// Get if this match can be found in a stack
/// </summary>
/// <param name="stack">Array to search for the given content</param>
/// <param name="reverse">True to search from the end of the array, false from the start</param>
/// <returns>Tuple of success and found position</returns>
public (bool success, int position) Match(byte[] stack, bool reverse = false)
{
// If either array is null or empty, we can't do anything
if (stack == null || stack.Length == 0 || Needle == null || Needle.Length == 0)
return (false, -1);
// If the needle array is larger than the stack array, it can't be contained within
if (Needle.Length > stack.Length)
return (false, -1);
// If start or end are not set properly, set them to defaults
if (Start < 0)
Start = 0;
if (End < 0)
End = stack.Length - Needle.Length;
for (int i = reverse ? End : Start; reverse ? i > Start : i < End; i += reverse ? -1 : 1)
{
if (EqualAt(stack, i))
return (true, i);
}
return (false, -1);
}
/// <summary>
/// Get if a stack at a certain index is equal to a needle
/// </summary>
/// <param name="stack">Array to search for the given content</param>
/// <param name="index">Starting index to check equality</param>
/// <returns>True if the needle matches the stack at a given index</returns>
private bool EqualAt(byte[] stack, int index)
{
// If the index is invalid, we can't do anything
if (index < 0)
return false;
// If we're too close to the end of the stack, return false
if (Needle.Length >= stack.Length - index)
return false;
for (int i = 0; i < Needle.Length; i++)
{
// A null value is a wildcard
if (Needle[i] == null)
continue;
else if (stack[i + index] != Needle[i])
return false;
}
return true;
}
#endregion
}
}

View File

@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace BurnOutSharp.Matching
{
/// <summary>
/// A set of content matches that work together
/// </summary>
internal class ContentMatchSet : MatchSet<ContentMatch, byte?[]>
{
/// <summary>
/// Function to get a content version
/// </summary>
/// <remarks>
/// A content version method takes the file path, the file contents,
/// and a list of found positions and returns a single string. That
/// string is either a version string, in which case it will be appended
/// to the protection name, or `null`, in which case it will cause
/// the protection to be omitted.
/// </remarks>
public Func<string, byte[], List<int>, string> GetVersion { get; set; }
#region Constructors
public ContentMatchSet(byte?[] needle, string protectionName)
: this(new List<byte?[]> { needle }, null, protectionName) { }
public ContentMatchSet(List<byte?[]> needles, string protectionName)
: this(needles, null, protectionName) { }
public ContentMatchSet(byte?[] needle, Func<string, byte[], List<int>, string> getVersion, string protectionName)
: this(new List<byte?[]> { needle }, getVersion, protectionName) { }
public ContentMatchSet(List<byte?[]> needles, Func<string, byte[], List<int>, string> getVersion, string protectionName)
: this(needles.Select(n => new ContentMatch(n)).ToList(), getVersion, protectionName) { }
public ContentMatchSet(ContentMatch needle, string protectionName)
: this(new List<ContentMatch>() { needle }, null, protectionName) { }
public ContentMatchSet(List<ContentMatch> needles, string protectionName)
: this(needles, null, protectionName) { }
public ContentMatchSet(ContentMatch needle, Func<string, byte[], List<int>, string> getVersion, string protectionName)
: this(new List<ContentMatch>() { needle }, getVersion, protectionName) { }
public ContentMatchSet(List<ContentMatch> needles, Func<string, byte[], List<int>, string> getVersion, string protectionName)
{
Matchers = needles;
GetVersion = getVersion;
ProtectionName = protectionName;
}
#endregion
#region Matching
/// <summary>
/// Determine whether all content matches pass
/// </summary>
/// <param name="fileContent">Byte array representing the file contents</param>
/// <returns>Tuple of passing status and matching positions</returns>
public (bool, List<int>) MatchesAll(byte[] fileContent)
{
// If no content matches are defined, we fail out
if (Matchers == null || !Matchers.Any())
return (false, new List<int>());
// Initialize the position list
List<int> positions = new List<int>();
// Loop through all content matches and make sure all pass
foreach (var contentMatch in Matchers)
{
(bool match, int position) = contentMatch.Match(fileContent);
if (!match)
return (false, new List<int>());
else
positions.Add(position);
}
return (true, positions);
}
/// <summary>
/// Determine whether any content matches pass
/// </summary>
/// <param name="fileContent">Byte array representing the file contents</param>
/// <returns>Tuple of passing status and first matching position</returns>
public (bool, int) MatchesAny(byte[] fileContent)
{
// If no content matches are defined, we fail out
if (Matchers == null || !Matchers.Any())
return (false, -1);
// Loop through all content matches and make sure all pass
foreach (var contentMatch in Matchers)
{
(bool match, int position) = contentMatch.Match(fileContent);
if (match)
return (true, position);
}
return (false, -1);
}
#endregion
}
}

View File

@@ -0,0 +1,7 @@
namespace BurnOutSharp.Matching
{
internal interface IMatch<T>
{
T Needle { get; set; }
}
}

View File

@@ -0,0 +1,20 @@
using System.Collections.Generic;
namespace BurnOutSharp.Matching
{
/// <summary>
/// Wrapper for a single set of matching criteria
/// </summary>
internal abstract class MatchSet<T, U> where T : IMatch<U>
{
/// <summary>
/// Set of all matchers
/// </summary>
public IEnumerable<T> Matchers { get; set; }
/// <summary>
/// Name of the protection to show
/// </summary>
public string ProtectionName { get; set; }
}
}

View File

@@ -0,0 +1,237 @@
using System.Collections.Generic;
using System.Linq;
namespace BurnOutSharp.Matching
{
/// <summary>
/// Helper class for matching
/// </summary>
internal static class MatchUtil
{
#region Content Matching
/// <summary>
/// Get all content matches for a given list of matchers
/// </summary>
/// <param name="file">File to check for matches</param>
/// <param name="fileContent">Byte array representing the file contents</param>
/// <param name="matchers">Enumerable of ContentMatchSets to be run on the file</param>
/// <param name="includePosition">True to include positional data, false otherwise</param>
/// <returns>List of strings representing the matched protections, null or empty otherwise</returns>
public static List<string> GetAllMatches(
string file,
byte[] fileContent,
IEnumerable<ContentMatchSet> matchers,
bool includePosition = false)
{
return FindAllMatches(file, fileContent, matchers, includePosition, false);
}
/// <summary>
/// Get first content match for a given list of matchers
/// </summary>
/// <param name="file">File to check for matches</param>
/// <param name="fileContent">Byte array representing the file contents</param>
/// <param name="matchers">Enumerable of ContentMatchSets to be run on the file</param>
/// <param name="includePosition">True to include positional data, false otherwise</param>
/// <returns>String representing the matched protection, null otherwise</returns>
public static string GetFirstMatch(
string file,
byte[] fileContent,
IEnumerable<ContentMatchSet> matchers,
bool includePosition = false)
{
var contentMatches = FindAllMatches(file, fileContent, matchers, includePosition, true);
if (contentMatches == null || !contentMatches.Any())
return null;
return contentMatches.First();
}
/// <summary>
/// Get the required set of content matches on a per Matcher basis
/// </summary>
/// <param name="file">File to check for matches</param>
/// <param name="fileContent">Byte array representing the file contents</param>
/// <param name="matchers">Enumerable of ContentMatchSets to be run on the file</param>
/// <param name="includePosition">True to include positional data, false otherwise</param>
/// <param name="stopAfterFirst">True to stop after the first match, false otherwise</param>
/// <returns>List of strings representing the matched protections, null or empty otherwise</returns>
private static List<string> FindAllMatches(
string file,
byte[] fileContent,
IEnumerable<ContentMatchSet> matchers,
bool includePosition,
bool stopAfterFirst)
{
// If there's no mappings, we can't match
if (matchers == null || !matchers.Any())
return null;
// Initialize the list of matched protections
List<string> matchedProtections = new List<string>();
// Loop through and try everything otherwise
foreach (var matcher in matchers)
{
// Determine if the matcher passes
(bool passes, List<int> positions) = matcher.MatchesAll(fileContent);
if (!passes)
continue;
// Format the list of all positions found
string positionsString = string.Join(", ", positions);
// If we there is no version method, just return the protection name
if (matcher.GetVersion == null)
{
matchedProtections.Add((matcher.ProtectionName ?? "Unknown Protection") + (includePosition ? $" (Index {positionsString})" : string.Empty));
}
// Otherwise, invoke the version method
else
{
// A null version returned means the check didn't pass at the version step
string version = matcher.GetVersion(file, fileContent, positions);
if (version == null)
continue;
matchedProtections.Add($"{matcher.ProtectionName ?? "Unknown Protection"} {version}".TrimEnd() + (includePosition ? $" (Index {positionsString})" : string.Empty));
}
// If we're stopping after the first protection, bail out here
if (stopAfterFirst)
return matchedProtections;
}
return matchedProtections;
}
#endregion
#region Path Matching
/// <summary>
/// Get all path matches for a given list of matchers
/// </summary>
/// <param name="file">File path to check for matches</param>
/// <param name="matchers">Enumerable of PathMatchSets to be run on the file</param>
/// <param name="any">True if any path match is a success, false if all have to match</param>
/// <returns>List of strings representing the matched protections, null or empty otherwise</returns>
public static List<string> GetAllMatches(string file, IEnumerable<PathMatchSet> matchers, bool any = false)
{
return FindAllMatches(new List<string> { file }, matchers, any, false);
}
// <summary>
/// Get all path matches for a given list of matchers
/// </summary>
/// <param name="files">File paths to check for matches</param>
/// <param name="matchers">Enumerable of PathMatchSets to be run on the file</param>
/// <param name="any">True if any path match is a success, false if all have to match</param>
/// <returns>List of strings representing the matched protections, null or empty otherwise</returns>
public static List<string> GetAllMatches(IEnumerable<string> files, IEnumerable<PathMatchSet> matchers, bool any = false)
{
return FindAllMatches(files, matchers, any, false);
}
/// <summary>
/// Get first path match for a given list of matchers
/// </summary>
/// <param name="file">File path to check for matches</param>
/// <param name="matchers">Enumerable of PathMatchSets to be run on the file</param>
/// <param name="any">True if any path match is a success, false if all have to match</param>
/// <returns>String representing the matched protection, null otherwise</returns>
public static string GetFirstMatch(string file, IEnumerable<PathMatchSet> matchers, bool any = false)
{
var contentMatches = FindAllMatches(new List<string> { file }, matchers, any, true);
if (contentMatches == null || !contentMatches.Any())
return null;
return contentMatches.First();
}
/// <summary>
/// Get first path match for a given list of matchers
/// </summary>
/// <param name="files">File paths to check for matches</param>
/// <param name="matchers">Enumerable of PathMatchSets to be run on the file</param>
/// <param name="any">True if any path match is a success, false if all have to match</param>
/// <returns>String representing the matched protection, null otherwise</returns>
public static string GetFirstMatch(IEnumerable<string> files, IEnumerable<PathMatchSet> matchers, bool any = false)
{
var contentMatches = FindAllMatches(files, matchers, any, true);
if (contentMatches == null || !contentMatches.Any())
return null;
return contentMatches.First();
}
/// <summary>
/// Get the required set of path matches on a per Matcher basis
/// </summary>
/// <param name="files">File paths to check for matches</param>
/// <param name="matchers">Enumerable of PathMatchSets to be run on the file</param>
/// <param name="any">True if any path match is a success, false if all have to match</param>
/// <param name="stopAfterFirst">True to stop after the first match, false otherwise</param>
/// <returns>List of strings representing the matched protections, null or empty otherwise</returns>
private static List<string> FindAllMatches(IEnumerable<string> files, IEnumerable<PathMatchSet> matchers, bool any, bool stopAfterFirst)
{
// If there's no mappings, we can't match
if (matchers == null || !matchers.Any())
return new List<string>();
// Initialize the list of matched protections
List<string> matchedProtections = new List<string>();
// Loop through and try everything otherwise
foreach (var matcher in matchers)
{
// Determine if the matcher passes
bool passes;
string firstMatchedString;
if (any)
{
(bool anyPasses, string matchedString) = matcher.MatchesAny(files);
passes = anyPasses;
firstMatchedString = matchedString;
}
else
{
(bool allPasses, List<string> matchedStrings) = matcher.MatchesAll(files);
passes = allPasses;
firstMatchedString = matchedStrings.FirstOrDefault();
}
// If we don't have a pass, just continue
if (!passes)
continue;
// If we there is no version method, just return the protection name
if (matcher.GetVersion == null)
{
matchedProtections.Add(matcher.ProtectionName ?? "Unknown Protection");
}
// Otherwise, invoke the version method
else
{
// A null version returned means the check didn't pass at the version step
string version = matcher.GetVersion(firstMatchedString, files);
if (version == null)
continue;
matchedProtections.Add($"{matcher.ProtectionName ?? "Unknown Protection"} {version}".TrimEnd());
}
// If we're stopping after the first protection, bail out here
if (stopAfterFirst)
return matchedProtections;
}
return matchedProtections;
}
#endregion
}
}

View File

@@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Linq;
namespace BurnOutSharp.Matching
{
/// <summary>
/// Path matching criteria
/// </summary>
internal class PathMatch : IMatch<string>
{
/// <summary>
/// String to match
/// </summary>
public string Needle { get; set; }
/// <summary>
/// Match exact casing instead of invariant
/// </summary>
public bool MatchExact { get; set; }
/// <summary>
/// Match that values end with the needle and not just contains
/// </summary>
public bool UseEndsWith { get; set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="needle">String representing the search</param>
/// <param name="matchExact">True to match exact casing, false otherwise</param>
/// <param name="useEndsWith">True to match the end only, false for all contents</param>
public PathMatch(string needle, bool matchExact = false, bool useEndsWith = false)
{
Needle = needle;
MatchExact = matchExact;
UseEndsWith = useEndsWith;
}
#region Matching
/// <summary>
/// Get if this match can be found in a stack
/// </summary>
/// <param name="stack">List of strings to search for the given content</param>
/// <returns>Tuple of success and matched item</returns>
public (bool, string) Match(IEnumerable<string> stack)
{
// If either array is null or empty, we can't do anything
if (stack == null || !stack.Any() || Needle == null || Needle.Length == 0)
return (false, null);
// Preprocess the needle, if necessary
string procNeedle = MatchExact ? Needle : Needle.ToLowerInvariant();
foreach (string stackItem in stack)
{
// Preprocess the stack item, if necessary
string procStackItem = MatchExact ? stackItem : stackItem.ToLowerInvariant();
if (UseEndsWith && procStackItem.EndsWith(procNeedle))
return (true, stackItem);
else if (!UseEndsWith && procStackItem.Contains(procNeedle))
return (true, stackItem);
}
return (false, null);
}
#endregion
}
}

View File

@@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace BurnOutSharp.Matching
{
/// <summary>
/// A set of path matches that work together
/// </summary>
internal class PathMatchSet : MatchSet<PathMatch, string>
{
/// <summary>
/// Function to get a path version for this Matcher
/// </summary>
/// <remarks>
/// A path version method takes the matched path and an enumerable of files
/// and returns a single string. That string is either a version string,
/// in which case it will be appended to the protection name, or `null`,
/// in which case it will cause the protection to be omitted.
/// </remarks>
public Func<string, IEnumerable<string>, string> GetVersion { get; set; }
#region Constructors
public PathMatchSet(string needle, string protectionName)
: this(new List<string> { needle }, null, protectionName) { }
public PathMatchSet(List<string> needles, string protectionName)
: this(needles, null, protectionName) { }
public PathMatchSet(string needle, Func<string, IEnumerable<string>, string> getVersion, string protectionName)
: this(new List<string> { needle }, getVersion, protectionName) { }
public PathMatchSet(List<string> needles, Func<string, IEnumerable<string>, string> getVersion, string protectionName)
: this(needles.Select(n => new PathMatch(n)).ToList(), getVersion, protectionName) { }
public PathMatchSet(PathMatch needle, string protectionName)
: this(new List<PathMatch>() { needle }, null, protectionName) { }
public PathMatchSet(List<PathMatch> needles, string protectionName)
: this(needles, null, protectionName) { }
public PathMatchSet(PathMatch needle, Func<string, IEnumerable<string>, string> getVersion, string protectionName)
: this(new List<PathMatch>() { needle }, getVersion, protectionName) { }
public PathMatchSet(List<PathMatch> needles, Func<string, IEnumerable<string>, string> getVersion, string protectionName)
{
Matchers = needles;
GetVersion = getVersion;
ProtectionName = protectionName;
}
#endregion
#region Matching
/// <summary>
/// Determine whether all path matches pass
/// </summary>
/// <param name="stack">List of strings to try to match</param>
/// <returns>Tuple of passing status and matching values</returns>
public (bool, List<string>) MatchesAll(IEnumerable<string> stack)
{
// If no path matches are defined, we fail out
if (Matchers == null || !Matchers.Any())
return (false, new List<string>());
// Initialize the value list
List<string> values = new List<string>();
// Loop through all path matches and make sure all pass
foreach (var pathMatch in Matchers)
{
(bool match, string value) = pathMatch.Match(stack);
if (!match)
return (false, new List<string>());
else
values.Add(value);
}
return (true, values);
}
/// <summary>
/// Determine whether any path matches pass
/// </summary>
/// <param name="stack">List of strings to try to match</param>
/// <returns>Tuple of passing status and first matching value</returns>
public (bool, string) MatchesAny(IEnumerable<string> stack)
{
// If no path matches are defined, we fail out
if (Matchers == null || !Matchers.Any())
return (false, null);
// Loop through all path matches and make sure all pass
foreach (var pathMatch in Matchers)
{
(bool match, string value) = pathMatch.Match(stack);
if (match)
return (true, value);
}
return (false, null);
}
#endregion
}
}

View File

@@ -0,0 +1,31 @@
using System.Collections.Generic;
using BurnOutSharp.Matching;
namespace BurnOutSharp.PackerType
{
// TODO: Add extraction and verify that all versions are detected
public class AdvancedInstaller : IContentCheck
{
/// <summary>
/// Set of all ContentMatchSets for this protection
/// </summary>
private static readonly List<ContentMatchSet> contentMatchers = new List<ContentMatchSet>
{
// Software\Caphyon\Advanced Installer
new ContentMatchSet(new byte?[]
{
0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
0x5C, 0x43, 0x61, 0x70, 0x68, 0x79, 0x6F, 0x6E,
0x5C, 0x41, 0x64, 0x76, 0x61, 0x6E, 0x63, 0x65,
0x64, 0x20, 0x49, 0x6E, 0x73, 0x74, 0x61, 0x6C,
0x6C, 0x65, 0x72
}, "Caphyon Advanced Installer"),
};
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchers, includePosition);
}
}
}

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
using BurnOutSharp.Matching;
namespace BurnOutSharp.PackerType
{
public class Armadillo : IContentCheck
{
/// <summary>
/// Set of all ContentMatchSets for this protection
/// </summary>
private static readonly List<ContentMatchSet> contentMatchers = new List<ContentMatchSet>
{
// .nicode + (char)0x00
new ContentMatchSet(new byte?[] { 0x2E, 0x6E, 0x69, 0x63, 0x6F, 0x64, 0x65, 0x00 }, "Armadillo"),
// ARMDEBUG
new ContentMatchSet(new byte?[] { 0x41, 0x52, 0x4D, 0x44, 0x45, 0x42, 0x55, 0x47 }, "Armadillo"),
};
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchers, includePosition);
}
}
}

View File

@@ -0,0 +1,57 @@
using System.Collections.Generic;
using System.IO;
using BurnOutSharp.Matching;
namespace BurnOutSharp.PackerType
{
// The official website for CExe also includes the source code (which does have to be retrieved by the Wayback Machine)
// http://www.scottlu.com/Content/CExe.html
public class CExe : IContentCheck, IScannable
{
/// <summary>
/// Set of all ContentMatchSets for this protection
/// </summary>
private static readonly List<ContentMatchSet> contentMatchers = new List<ContentMatchSet>
{
// %Wo<57>a6.<2E>a6.<2E>a6.<2E>a6.<2E>{6.<2E>.).<2E>f6.<2E><>).<2E>`6.<2E><>0.<2E>`6.<2E>
new ContentMatchSet(
new ContentMatch(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
}, end: 200), "CExe"),
};
/// <inheritdoc/>
public bool ShouldScan(byte[] magic) => true;
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchers, includePosition);
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
// TODO: Add extraction if viable
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
return null;
}
}
}

View File

@@ -0,0 +1,30 @@
using System.Collections.Generic;
using BurnOutSharp.Matching;
namespace BurnOutSharp.PackerType
{
public class EXEStealth : IContentCheck
{
/// <summary>
/// Set of all ContentMatchSets for this protection
/// </summary>
private static readonly List<ContentMatchSet> contentMatchers = new List<ContentMatchSet>
{
// ??[[__[[_ + (char)0x00 + {{ + (char)0x0 + (char)0x00 + {{ + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x0 + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x00 + ?;??;??
new ContentMatchSet(new byte?[]
{
0x3F, 0x3F, 0x5B, 0x5B, 0x5F, 0x5F, 0x5B, 0x5B,
0x5F, 0x00, 0x7B, 0x7B, 0x00, 0x00, 0x7B, 0x7B,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x20, 0x3F, 0x3B, 0x3F, 0x3F, 0x3B, 0x3F,
0x3F
}, "EXE Stealth"),
};
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchers, includePosition);
}
}
}

View File

@@ -1,23 +1,55 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BurnOutSharp.Matching;
namespace BurnOutSharp.ProtectionType
namespace BurnOutSharp.PackerType
{
public class InnoSetup
public class InnoSetup : IContentCheck, IScannable
{
/// <summary>
/// Set of all ContentMatchSets for this protection
/// </summary>
private static readonly List<ContentMatchSet> contentMatchers = new List<ContentMatchSet>
{
// Inno
new ContentMatchSet(
new ContentMatch(new byte?[] { 0x49, 0x6E, 0x6E, 0x6F }, start: 0x30, end: 0x31),
GetVersion,
"Inno Setup"),
};
/// <inheritdoc/>
public bool ShouldScan(byte[] magic) => true;
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchers, includePosition);
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
// TOOO: Add Inno Setup extraction
// https://github.com/dscharrer/InnoExtract
public static string CheckContents(byte[] fileContent, bool includePosition = false)
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// "Inno"
byte[] check = new byte[] { 0x49, 0x6E, 0x6E, 0x6F };
if (fileContent.Contains(check, out int position) && position == 0x30)
return $"Inno Setup {GetVersion(fileContent)}" + (includePosition ? $" (Index {position})" : string.Empty);
return null;
}
private static string GetVersion(byte[] fileContent)
public static string GetVersion(string file, byte[] fileContent, List<int> positions)
{
byte[] signature = new ArraySegment<byte>(fileContent, 0x30, 12).ToArray();

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BurnOutSharp.Matching;
namespace BurnOutSharp.PackerType
{
public class NSIS : IContentCheck
{
/// <summary>
/// Set of all ContentMatchSets for this protection
/// </summary>
private static readonly List<ContentMatchSet> contentMatchers = new List<ContentMatchSet>
{
// Nullsoft Install System
new ContentMatchSet(new byte?[]
{
0x4e, 0x75, 0x6c, 0x6c, 0x73, 0x6f, 0x66, 0x74,
0x20, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c,
0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d
}, GetVersion, "NSIS"),
// NullsoftInst
new ContentMatchSet(new byte?[]
{
0x4e, 0x75, 0x6c, 0x6c, 0x73, 0x6f, 0x66, 0x74,
0x49, 0x6e, 0x73, 0x74
}, "NSIS"),
};
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchers, includePosition);
}
public static string GetVersion(string file, byte[] fileContent, List<int> positions)
{
try
{
int index = positions[0];
index += 24;
if (fileContent[index] != 'v')
return "(Unknown Version)";
var versionBytes = new ReadOnlySpan<byte>(fileContent, index, 16).ToArray();
var onlyVersion = versionBytes.TakeWhile(b => b != '<').ToArray();
return Encoding.ASCII.GetString(onlyVersion);
}
catch
{
return "(Unknown Version)";
}
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using BurnOutSharp.Matching;
namespace BurnOutSharp.PackerType
{
// TODO: Add extraction and better version detection
public class PECompact : IContentCheck
{
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
// Another possible version string for version 1 is "PECO" (50 45 43 4F)
var matchers = new List<ContentMatchSet>
{
// pec1
new ContentMatchSet(new ContentMatch(new byte?[] { 0x70, 0x65, 0x63, 0x31 }, end: 2048), "PE Compact 1"),
// PEC2
new ContentMatchSet(new ContentMatch(new byte?[] { 0x50, 0x45, 0x43, 0x32 }, end: 2048), GetVersion, "PE Compact 2"),
// PECompact2
new ContentMatchSet(new byte?[]
{
0x50, 0x45, 0x43, 0x6F, 0x6D, 0x70, 0x61, 0x63,
0x74, 0x32
}, "PE Compact 2"),
};
return MatchUtil.GetFirstMatch(file, fileContent, matchers, includePosition);
}
public static string GetVersion(string file, byte[] fileContent, List<int> positions)
{
return $"v{BitConverter.ToInt16(fileContent, positions[0] + 4)}";
}
}
}

View File

@@ -0,0 +1,10 @@
namespace BurnOutSharp.PackerType
{
public class Petite
{
/*
* Possible strings for PEtite Win32 Executable Compressor (Unknown how to get version)
* - petite - 40 70 65 74 69 74 65 (Made with Version 2.4, Compression Level 1-9)
*/
}
}

View File

@@ -0,0 +1,88 @@
using System.Collections.Generic;
using System.IO;
using BurnOutSharp.Matching;
namespace BurnOutSharp.PackerType
{
public class SetupFactory : IContentCheck, IScannable
{
/// <summary>
/// Set of all ContentMatchSets for this protection
/// </summary>
private static readonly List<ContentMatchSet> contentMatchers = new List<ContentMatchSet>
{
// S.e.t.u.p. .F.a.c.t.o.r.y.
new ContentMatchSet(new byte?[]
{
0x53, 0x00, 0x65, 0x00, 0x74, 0x00, 0x75, 0x00,
0x70, 0x00, 0x20, 0x00, 0x46, 0x00, 0x61, 0x00,
0x63, 0x00, 0x74, 0x00, 0x6F, 0x00, 0x72, 0x00,
0x79, 0x00
}, GetVersion, "Setup Factory"),
// Longer version of the check that can be used if false positves become an issue:
// S.e.t.u.p. .F.a.c.t.o.r.y. .i.s. .a. .t.r.a.d.e.m.a.r.k. .o.f. .I.n.d.i.g.o. .R.o.s.e. .C.o.r.p.o.r.a.t.i.o.n.
// new ContentMatchSet(new byte?[]
// {
// 0x53, 0x00, 0x65, 0x00, 0x74, 0x00, 0x75, 0x00,
// 0x70, 0x00, 0x20, 0x00, 0x46, 0x00, 0x61, 0x00,
// 0x63, 0x00, 0x74, 0x00, 0x6F, 0x00, 0x72, 0x00,
// 0x79, 0x00, 0x20, 0x00, 0x69, 0x00, 0x73, 0x00,
// 0x20, 0x00, 0x61, 0x00, 0x20, 0x00, 0x74, 0x00,
// 0x72, 0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00,
// 0x6D, 0x00, 0x61, 0x00, 0x72, 0x00, 0x6B, 0x00,
// 0x20, 0x00, 0x6F, 0x00, 0x66, 0x00, 0x20, 0x00,
// 0x49, 0x00, 0x6E, 0x00, 0x64, 0x00, 0x69, 0x00,
// 0x67, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x52, 0x00,
// 0x6F, 0x00, 0x73, 0x00, 0x65, 0x00, 0x20, 0x00,
// 0x43, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x70, 0x00,
// 0x6F, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00,
// 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00
// }, GetVersion, "Setup Factory"),
};
/// <inheritdoc/>
public bool ShouldScan(byte[] magic) => true;
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchers, includePosition);
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
// TODO: Add extraction, 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)
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
return null;
}
public static string GetVersion(string file, byte[] fileContent, List<int> positions)
{
// Check the manifest version first
string version = Utilities.GetManifestVersion(fileContent);
if (!string.IsNullOrEmpty(version))
return version;
// Then check the file version
version = Utilities.GetFileVersion(file);
if (!string.IsNullOrEmpty(version))
return version;
return "(Unknown Version)";
}
}
}

View File

@@ -0,0 +1,69 @@
using System.Collections.Generic;
using System.Text;
using BurnOutSharp.Matching;
namespace BurnOutSharp.PackerType
{
public class UPX : IContentCheck
{
/// <summary>
/// Set of all ContentMatchSets for this protection
/// </summary>
private static readonly List<ContentMatchSet> contentMatchers = new List<ContentMatchSet>
{
// UPX!
new ContentMatchSet(new byte?[] { 0x55, 0x50, 0x58, 0x21 }, GetVersion, "UPX"),
// NOS
new ContentMatchSet(new byte?[] { 0x4E, 0x4F, 0x53, 0x20 }, GetVersion, "UPX (NOS Variant)"),
new ContentMatchSet(
new List<byte?[]>
{
// UPX0
new byte?[] { 0x55, 0x50, 0x58, 0x30 },
// UPX1
new byte?[] { 0x55, 0x50, 0x58, 0x31 },
},
"UPX (Unknown Version)"
),
new ContentMatchSet(
new List<byte?[]>
{
// NOS0
new byte?[] { 0x4E, 0x4F, 0x53, 0x30 },
// NOS1
new byte?[] { 0x4E, 0x4F, 0x53, 0x31 },
},
"UPX (NOS Variant) (Unknown Version)"
),
};
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchers, includePosition);
}
public static string GetVersion(string file, byte[] fileContent, List<int> positions)
{
try
{
int index = positions[0];
index -= 5;
string versionString = Encoding.ASCII.GetString(fileContent, index, 4);
if (!char.IsNumber(versionString[0]))
return "(Unknown Version)";
return versionString;
}
catch
{
return "(Unknown Version)";
}
}
}
}

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.IO;
using BurnOutSharp.Matching;
using SharpCompress.Archives;
using SharpCompress.Archives.Rar;
namespace BurnOutSharp.PackerType
{
public class WinRARSFX : IContentCheck, IScannable
{
/// <summary>
/// Set of all ContentMatchSets for this protection
/// </summary>
private static readonly List<ContentMatchSet> contentMatchers = new List<ContentMatchSet>
{
// Software\WinRAR SFX
new ContentMatchSet(new byte?[]
{
0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
0x5C, 0x57, 0x69, 0x6E, 0x52, 0x41, 0x52, 0x20,
0x53, 0x46, 0x58
}, "WinRAR SFX"),
};
/// <inheritdoc/>
public bool ShouldScan(byte[] magic) => true;
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchers, includePosition);
}
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the rar file itself fails
try
{
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempPath);
// 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 SharpCompress.Readers.ReaderOptions() {LookForHeader = true}))
{
foreach (var entry in zipFile.Entries)
{
// If an individual entry fails
try
{
// If we have a directory, skip it
if (entry.IsDirectory)
continue;
string tempFile = Path.Combine(tempPath, entry.Key);
entry.WriteToFile(tempFile);
}
catch { }
}
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
catch { }
return null;
}
}
}

View File

@@ -0,0 +1,391 @@
using System;
using System.Collections.Generic;
using System.IO;
using BurnOutSharp.Matching;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
namespace BurnOutSharp.PackerType
{
public class WinZipSFX : IContentCheck, IScannable
{
/// <inheritdoc/>
public bool ShouldScan(byte[] magic) => true;
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
var matchers = new List<ContentMatchSet>
{
// WinZip Self-Extractor
new ContentMatchSet(new byte?[]
{
0x57, 0x69, 0x6E, 0x5A, 0x69, 0x70, 0x20, 0x53,
0x65, 0x6C, 0x66, 0x2D, 0x45, 0x78, 0x74, 0x72,
0x61, 0x63, 0x74, 0x6F, 0x72
}, GetVersion, "WinZip SFX"),
// _winzip_
new ContentMatchSet(new byte?[] { 0x5F, 0x77, 0x69, 0x6E, 0x7A, 0x69, 0x70, 0x5F }, GetVersion, "WinZip SFX"),
};
return MatchUtil.GetFirstMatch(file, fileContent, matchers, includePosition);
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the zip file itself fails
try
{
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempPath);
// Should be using stream instead of file, but stream fails to extract anything. My guess is that the executable portion of the archive is causing stream to fail, but not file.
using (ZipArchive zipFile = ZipArchive.Open(file))
{
foreach (var entry in zipFile.Entries)
{
// If an individual entry fails
try
{
// If we have a directory, skip it
if (entry.IsDirectory)
continue;
string tempFile = Path.Combine(tempPath, entry.Key);
entry.WriteToFile(tempFile);
}
catch { }
}
}
// Collect and format all found protections
var protections = scanner.GetProtections(tempPath);
// If temp directory cleanup fails
try
{
Directory.Delete(tempPath, true);
}
catch { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
catch { }
return null;
}
public static string GetVersion(string file, byte[] fileContent, List<int> positions)
{
// Check the manifest version first
string version = Utilities.GetManifestVersion(fileContent);
if (!string.IsNullOrEmpty(version))
return GetV3PlusVersion(version);
// Assume an earlier version
return GetV2Version(file, fileContent);
}
private static string GetV2Version(string file, byte[] fileContent)
{
var matchers = new List<ContentMatchSet>
{
#region 16-bit NE Header Checks
new ContentMatchSet(new byte?[]
{
0x4E, 0x45, 0x11, 0x20, 0x86, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x03, 0x03, 0x00,
0x00, 0x20, 0x00, 0x40, 0xE6, 0x2B, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00,
0x4B, 0x00, 0x40, 0x00, 0x58, 0x00, 0x58, 0x00,
0x64, 0x00, 0x6C, 0x00, 0xB8, 0x44, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
}, "2.0 (MS-DOS/16-bit)"),
new ContentMatchSet(new byte?[]
{
0x4E, 0x45, 0x11, 0x20, 0x86, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x03, 0x03, 0x00,
0x00, 0x20, 0x00, 0x40, 0x74, 0x31, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00,
0x4B, 0x00, 0x40, 0x00, 0x58, 0x00, 0x58, 0x00,
0x64, 0x00, 0x6C, 0x00, 0x98, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
}, "2.0 (16-bit)"),
new ContentMatchSet(new byte?[]
{
0x4E, 0x45, 0x11, 0x20, 0x80, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x03, 0x03, 0x00,
0x00, 0x20, 0x00, 0x40, 0xA0, 0x24, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
0x4B, 0x00, 0x40, 0x00, 0x58, 0x00, 0x58, 0x00,
0x64, 0x00, 0x6A, 0x00, 0x92, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
}, "Compact 2.0 (16-bit)"),
new ContentMatchSet(new byte?[]
{
0x4E, 0x45, 0x11, 0x20, 0xCD, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x03, 0x00,
0x00, 0x20, 0x00, 0x40, 0xFA, 0x36, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x05, 0x00,
0x4B, 0x00, 0x40, 0x00, 0x58, 0x00, 0x97, 0x00,
0xA3, 0x00, 0xAD, 0x00, 0xDF, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
}, "Software Installation 2.0 (16-bit)"),
new ContentMatchSet(new byte?[]
{
0x4E, 0x45, 0x11, 0x20, 0x86, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x03, 0x03, 0x00,
0x00, 0x20, 0x00, 0x40, 0x86, 0x33, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00,
0x4B, 0x00, 0x40, 0x00, 0x58, 0x00, 0x58, 0x00,
0x64, 0x00, 0x6C, 0x00, 0xC8, 0x43, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
}, "2.1 RC2 (MS-DOS/16-bit)"),
new ContentMatchSet(new byte?[]
{
0x4E, 0x45, 0x11, 0x20, 0xBE, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x03, 0x00,
0x00, 0x20, 0x00, 0x40, 0x56, 0x3E, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00,
0x4B, 0x00, 0x40, 0x00, 0x58, 0x00, 0x90, 0x00,
0x9C, 0x00, 0xA4, 0x00, 0xD0, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
}, "2.1 RC2 (16-bit)"),
new ContentMatchSet(new byte?[]
{
0x4E, 0x45, 0x11, 0x20, 0x80, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x03, 0x03, 0x00,
0x00, 0x20, 0x00, 0x40, 0x84, 0x2B, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
0x4B, 0x00, 0x40, 0x00, 0x58, 0x00, 0x58, 0x00,
0x64, 0x00, 0x6A, 0x00, 0x92, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
}, "Compact 2.1 RC2 (16-bit)"),
new ContentMatchSet(new byte?[]
{
0x4E, 0x45, 0x11, 0x20, 0xBE, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x03, 0x00,
0x00, 0x20, 0x00, 0x40, 0xAC, 0x43, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00,
0x4B, 0x00, 0x40, 0x00, 0x58, 0x00, 0x90, 0x00,
0x9C, 0x00, 0xA4, 0x00, 0xD0, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
}, "Software Installation 2.1 RC2 (16-bit)"),
new ContentMatchSet(new byte?[]
{
0x4E, 0x45, 0x11, 0x20, 0x86, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x03, 0x03, 0x00,
0x00, 0x20, 0x00, 0x3A, 0x96, 0x33, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00,
0x4B, 0x00, 0x40, 0x00, 0x58, 0x00, 0x58, 0x00,
0x64, 0x00, 0x6C, 0x00, 0xC8, 0x43, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
}, "2.1 (MS-DOS/16-bit)"),
new ContentMatchSet(new byte?[]
{
0x4E, 0x45, 0x11, 0x20, 0xBE, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x03, 0x00,
0x00, 0x20, 0x00, 0x3A, 0x7E, 0x3E, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00,
0x4B, 0x00, 0x40, 0x00, 0x58, 0x00, 0x90, 0x00,
0x9C, 0x00, 0xA4, 0x00, 0xD0, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
}, "2.1 (16-bit)"),
new ContentMatchSet(new byte?[]
{
0x4E, 0x45, 0x11, 0x20, 0x80, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0A, 0x03, 0x03, 0x00,
0x00, 0x20, 0x00, 0x3A, 0x90, 0x2B, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,
0x4B, 0x00, 0x40, 0x00, 0x58, 0x00, 0x58, 0x00,
0x64, 0x00, 0x6A, 0x00, 0x92, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
}, "Compact 2.1 (16-bit)"),
new ContentMatchSet(new byte?[]
{
0x4E, 0x45, 0x11, 0x20, 0xBE, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x03, 0x00,
0x00, 0x20, 0x00, 0x3A, 0x08, 0x44, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00,
0x4B, 0x00, 0x40, 0x00, 0x58, 0x00, 0x90, 0x00,
0x9C, 0x00, 0xA4, 0x00, 0xD0, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
}, "Software Installation 2.1 (16-bit)"),
#endregion
#region 32-bit SFX Header Checks
// .............8<EFBFBD>92....<2E>P..............<2E>P..<2E>P..<2E>P..VW95SE.SFX
new ContentMatchSet(new byte?[]
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9C, 0x39,
0x32, 0x00, 0x00, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x88, 0x50, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x56, 0x57, 0x39, 0x35, 0x53, 0x45, 0x2E,
0x53, 0x46, 0x58,
}, "2.0 (32-bit)"),
// .............]<5D>92....<2E>P..............<2E>P..<2E>P..<2E>P..VW95SRE.SFX
new ContentMatchSet(new byte?[]
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x9C, 0x39,
0x32, 0x00, 0x00, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x88, 0x50, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x56, 0x57, 0x39, 0x35, 0x53, 0x52, 0x45,
0x2E, 0x53, 0x46, 0x58,
}, "Software Installation 2.0 (32-bit)"),
// .............<2E><><EFBFBD>3....<2E>P..............<2E>P..<2E>P..<2E>P..VW95SE.SFX
new ContentMatchSet(new byte?[]
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x82, 0x94,
0x33, 0x00, 0x00, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x88, 0x50, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x56, 0x57, 0x39, 0x35, 0x53, 0x45, 0x2E,
0x53, 0x46, 0x58,
}, "2.1 RC2 (32-bit)"),
// .............<2E><><EFBFBD>3....<2E>P..............<2E>P..<2E>P..<2E>P..VW95SRE.SFX
new ContentMatchSet(new byte?[]
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x82, 0x94,
0x33, 0x00, 0x00, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x88, 0x50, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x56, 0x57, 0x39, 0x35, 0x53, 0x52, 0x45,
0x2E, 0x53, 0x46, 0x58,
}, "Software Installation 2.1 RC2 (32-bit)"),
// .............U<><55>3....<2E>P..............<2E>P..<2E>P..<2E>P..VW95SE.SFX
new ContentMatchSet(new byte?[]
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xCD, 0xCC,
0x33, 0x00, 0x00, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x88, 0x50, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x56, 0x57, 0x39, 0x35, 0x53, 0x45, 0x2E,
0x53, 0x46, 0x58,
}, "2.1 (32-bit)"),
// .............{<7B><>3....<2E>P..............<2E>P..<2E>P..<2E>P..VW95SRE.SFX
new ContentMatchSet(new byte?[]
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0xCD, 0xCC,
0x33, 0x00, 0x00, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x88, 0x50, 0x00, 0x00, 0x88, 0x50, 0x00,
0x00, 0x56, 0x57, 0x39, 0x35, 0x53, 0x52, 0x45,
0x2E, 0x53, 0x46, 0x58,
}, "Software Installation 2.1 (32-bit)"),
#endregion
#region 32-bit PE Header Checks
new ContentMatchSet(new byte?[]
{
0x50, 0x45, 0x00, 0x00, 0x4C, 0x01, 0x05, 0x00,
0x69, 0x1B, 0x5B, 0x3A, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x0F, 0x01,
0x0B, 0x01, 0x05, 0x0A, 0x00, 0x4A, 0x00, 0x00,
0x00, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xD8, 0x39, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
}, "2.2.4003"),
new ContentMatchSet(new byte?[]
{
0x50, 0x45, 0x00, 0x00, 0x4C, 0x01, 0x05, 0x00,
0x81, 0x1B, 0x5B, 0x3A, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x0F, 0x01,
0x0B, 0x01, 0x05, 0x0A, 0x00, 0x56, 0x00, 0x00,
0x00, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x8F, 0x3F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
}, "Software Installation 2.2.4003"),
#endregion
};
string match = MatchUtil.GetFirstMatch(file, fileContent, matchers, false);
return match ?? "Unknown 2.x";
}
private static string GetV3PlusVersion(string version)
{
// Some version strings don't exactly match the public version number
switch (version)
{
case "3.0.7158.0":
return "3.0.7158";
case "3.1.7556.0":
return "3.1.7556";
case "3.1.8421.0":
return "4.0.8421";
case "3.1.8672.0":
return "4.0.8672";
case "4.0.1221.0":
return "4.0.12218";
default:
return $"(Unknown - {version})";
}
}
}
}

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.IO;
using BurnOutSharp.Matching;
using Wise = WiseUnpacker.WiseUnpacker;
namespace BurnOutSharp.PackerType
{
public class WiseInstaller : IContentCheck, IScannable
{
/// <inheritdoc/>
public bool ShouldScan(byte[] magic) => true;
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
var matchers = new List<ContentMatchSet>
{
// WiseMain
new ContentMatchSet(new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x4D, 0x61, 0x69, 0x6E }, "Wise Installation Wizard Module"),
};
return MatchUtil.GetFirstMatch(file, fileContent, matchers, includePosition);
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, string file)
{
if (!File.Exists(file))
return null;
using (var fs = File.OpenRead(file))
{
return Scan(scanner, fs, file);
}
}
/// <inheritdoc/>
public Dictionary<string, List<string>> Scan(Scanner scanner, Stream stream, string file)
{
// If the installer file itself fails
try
{
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempPath);
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 { }
// Remove temporary path references
Utilities.StripFromKeys(protections, tempPath);
return protections;
}
catch { }
return null;
}
}
}

View File

@@ -0,0 +1,28 @@
using System.Collections.Generic;
using BurnOutSharp.Matching;
namespace BurnOutSharp.PackerType
{
public class dotFuscator : IContentCheck
{
/// <summary>
/// Set of all ContentMatchSets for this protection
/// </summary>
private static readonly List<ContentMatchSet> contentMatchers = new List<ContentMatchSet>
{
// DotfuscatorAttribute
new ContentMatchSet(new byte?[]
{
0x44, 0x6F, 0x74, 0x66, 0x75, 0x73, 0x63, 0x61,
0x74, 0x6F, 0x72, 0x41, 0x74, 0x74, 0x72, 0x69,
0x62, 0x75, 0x74, 0x65
}, "dotFuscator"),
};
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchers, includePosition);
}
}
}

View File

@@ -1,509 +0,0 @@
//this file is part of BurnOut
//Copyright (C)2005-2010 Gernot Knippen
//Ported code with augments Copyright (C)2018 Matt Nadareski
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//GNU General Public License for more details.
//
//You can get a copy of the GNU General Public License
//by writing to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using BurnOutSharp.FileType;
using BurnOutSharp.ProtectionType;
namespace BurnOutSharp
{
public static class ProtectionFind
{
/// <summary>
/// Scan a path to find any known copy protection(s)
/// </summary>
/// <param name="path">Path to scan for protection(s)</param>
/// <param name="includePosition">True to include scanned copy protection position, false otherwise (default)</param>
/// <param name="progress">Optional progress indicator that will return a float in the range from 0 to 1</param>
/// <returns>Dictionary of filename to protection mappings, if possible</returns>
public static Dictionary<string, string> Scan(string path, bool includePosition = false, IProgress<FileProtection> progress = null)
{
var protections = new Dictionary<string, string>();
// Checkpoint
progress?.Report(new FileProtection(null, 0, null));
// If we have a file
if (File.Exists(path))
{
// Try using just the file first to get protection info
string fileProtection = ScanPath(path, false);
if (!string.IsNullOrWhiteSpace(fileProtection))
protections[path] = fileProtection;
// Now check to see if the file contains any additional information
string contentProtection = ScanContent(path, includePosition)?.Replace("" + (char)0x00, "");
if (!string.IsNullOrWhiteSpace(contentProtection))
{
if (protections.ContainsKey(path))
protections[path] += $", {contentProtection}";
else
protections[path] = contentProtection;
}
// Checkpoint
progress?.Report(new FileProtection(path, 1, contentProtection));
}
// If we have a directory
else if (Directory.Exists(path))
{
// Get the lists of files to be used
var files = Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories);
// Try using just the path first to get protection info
string pathProtection = ScanPath(path, true);
if (!string.IsNullOrWhiteSpace(pathProtection))
protections[path] = pathProtection;
// Loop through all files and scan their contents
for (int i = 0; i < files.Count(); i++)
{
// Get the current file
string file = files.ElementAt(i);
// Try using just the file first to get protection info
string fileProtection = ScanPath(file, false);
if (!string.IsNullOrWhiteSpace(fileProtection))
protections[file] = fileProtection;
// Now check to see if the file contains any additional information
string contentProtection = ScanContent(file, includePosition)?.Replace("" + (char)0x00, "");
if (!string.IsNullOrWhiteSpace(contentProtection))
{
if (protections.ContainsKey(file))
protections[file] += $", {contentProtection}";
else
protections[file] = contentProtection;
}
// Checkpoint
progress?.Report(new FileProtection(file, i / (float)files.Count(), contentProtection));
}
}
// If we have an empty list, we need to take care of that
if (protections.Count(p => !string.IsNullOrWhiteSpace(p.Value)) == 0)
{
protections = new Dictionary<string, string>();
}
return protections;
}
/// <summary>
/// Scan a path for indications of copy protection
/// </summary>
/// <param name="path"></param>
/// <param name="isDirectory"></param>
/// <returns></returns>
public static string ScanPath(string path, bool isDirectory)
{
List<string> protections = new List<string>();
string protection;
// If we have a directory, get the files in the directory for searching
IEnumerable<string> files = null;
if (isDirectory)
files = Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories);
// AACS
protection = AACS.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Alpha-DVD
protection = AlphaDVD.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Bitpool
protection = Bitpool.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// ByteShield
protection = ByteShield.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Cactus Data Shield
protection = CactusDataShield.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// CD-Cops
protection = CDCops.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// CD-Lock
protection = CDLock.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// CD-Protector
protection = CDProtector.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// CD-X
protection = CDX.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
/*
// CopyKiller
protection = CopyKiller.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
*/
// DiscGuard
protection = DiscGuard.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// DVD Crypt
protection = DVDCrypt.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// DVD-Movie-PROTECT
protection = DVDMoviePROTECT.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// FreeLock
protection = FreeLock.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Games for Windows - Live
protection = GFWL.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Hexalock AutoLock
protection = HexalockAutoLock.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Impulse Reactor
protection = ImpulseReactor.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// IndyVCD
protection = IndyVCD.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Key2Audio XS
protection = Key2AudioXS.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// LaserLock
protection = LaserLock.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// MediaCloQ
protection = MediaCloQ.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// MediaMax CD3
protection = MediaMaxCD3.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Origin
protection = Origin.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Protect DVD-Video
protection = ProtectDVDVideo.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// SafeCast
protection = SafeCast.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// SafeDisc
protection = SafeDisc.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// SafeDisc Lite
protection = SafeDiscLite.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// SafeLock
protection = SafeLock.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// SecuROM
protection = SecuROM.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// SmartE
protection = SmartE.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// SoftLock
protection = SoftLock.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// SolidShield
protection = SolidShield.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// StarForce
protection = StarForce.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Steam
protection = Steam.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// TAGES
protection = Tages.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// TZCopyProtector
protection = TZCopyProtector.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Uplay
protection = Uplay.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// VOB ProtectCD/DVD
protection = VOBProtectCDDVD.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Winlock
protection = Winlock.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// WTM CD Protect
protection = WTMCDProtect.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// XCP
protection = XCP.CheckPath(path, files, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Zzxzz
protection = Zzxzz.CheckPath(path, isDirectory);
if (!string.IsNullOrWhiteSpace(protection))
protections.Add(protection);
// Now combine any found protections, or null if empty
if (protections.Count() == 0)
return null;
else
return string.Join(", ", protections);
}
/// <summary>
/// Scan an individual file for copy protection
/// </summary>
/// <param name="file">File path for scanning</param>
/// <param name="includePosition">True to include scanned copy protection position, false otherwise (default)</param>
public static string ScanContent(string file, bool includePosition = false)
{
using (FileStream fs = File.OpenRead(file))
{
return ScanContent(fs, file, includePosition);
}
}
/// <summary>
/// Scan an individual stream for copy protection
/// </summary>
/// <param name="stream">Generic stream to scan</param>
/// <param name="file">File path to be used for name checks (optional)</param>
/// <param name="includePosition">True to include scanned copy protection position, false otherwise (default)</param>
public static string ScanContent(Stream stream, string file = null, bool includePosition = false)
{
// Get the extension for certain checks
string extension = Path.GetExtension(file).ToLower().TrimStart('.');
// Assume the first part of the stream is the start of a file
byte[] magic = new byte[16];
try
{
stream.Read(magic, 0, 16);
stream.Seek(-16, SeekOrigin.Current);
}
catch
{
// We don't care what the issue was, we can't read or seek the file
return null;
}
// Files can be protected in multiple ways
List<string> protections = new List<string>();
// 7-Zip archive
if (SevenZip.ShouldScan(magic))
protections.AddRange(SevenZip.Scan(stream, includePosition));
// BFPK archive
if (BFPK.ShouldScan(magic))
protections.AddRange(BFPK.Scan(stream, includePosition));
// BZip2
if (BZip2.ShouldScan(magic))
protections.AddRange(BZip2.Scan(stream, includePosition));
// Executable
if (Executable.ShouldScan(magic))
protections.AddRange(Executable.Scan(stream, file, includePosition));
// GZIP
if (GZIP.ShouldScan(magic))
protections.AddRange(GZIP.Scan(stream, includePosition));
// InstallShield Cabinet
if (file != null && InstallShieldCAB.ShouldScan(magic))
protections.AddRange(InstallShieldCAB.Scan(file, includePosition));
// Microsoft Cabinet
if (file != null && MicrosoftCAB.ShouldScan(magic))
protections.AddRange(MicrosoftCAB.Scan(file, includePosition));
// MPQ archive
if (file != null && MPQ.ShouldScan(magic))
protections.AddRange(MPQ.Scan(file, includePosition));
// PKZIP archive (and derivatives)
if (PKZIP.ShouldScan(magic))
protections.AddRange(PKZIP.Scan(stream, includePosition));
// RAR archive
if (RAR.ShouldScan(magic))
protections.AddRange(RAR.Scan(stream, includePosition));
// Tape Archive
if (TapeArchive.ShouldScan(magic))
protections.AddRange(TapeArchive.Scan(stream, includePosition));
// Text-based files
if (Textfile.ShouldScan(magic, extension))
protections.AddRange(Textfile.Scan(stream, includePosition));
// Valve archive formats
if (file != null && Valve.ShouldScan(magic))
protections.AddRange(Valve.Scan(file, includePosition));
// XZ
if (XZ.ShouldScan(magic))
protections.AddRange(XZ.Scan(stream, includePosition));
// Return blank if nothing found, or comma-separated list of protections
if (protections.Count() == 0)
return string.Empty;
else
return string.Join(", ", protections);
}
/// <summary>
/// Scan a disc sector by sector for protection
/// </summary>
/// <remarks>
/// https://stackoverflow.com/questions/8819188/c-sharp-classes-to-undelete-files/8820157#8820157
/// TODO: Finish implementation
/// </remarks>
private static string ScanSectors(char driveLetter, int sectorsize)
{
string fsName = Utilities.GetFileSystemName(driveLetter);
// Gets a handle to the physical disk
IntPtr hDisk = Utilities.CreateFile($"\\\\.\\{driveLetter}:",
FileAccess.Read,
FileShare.ReadWrite,
IntPtr.Zero,
FileMode.Open,
0,
IntPtr.Zero);
// If we have a good pointer
if (hDisk.ToInt32() != -1)
{
// Setup vars
byte[] buffer = new byte[sectorsize];
IntPtr pt = IntPtr.Zero;
NativeOverlapped no = new NativeOverlapped();
// Set initial offset
Utilities.SetFilePointerEx(
hDisk,
0,
ref pt,
Utilities.FileBegin);
// Read a whole sector
while (true)
{
buffer = new byte[sectorsize];
Utilities.ReadFileEx(
hDisk,
buffer,
(uint)sectorsize,
ref no,
null);
Utilities.SetFilePointerEx(
hDisk,
sectorsize,
ref pt,
Utilities.FileCurrent);
}
}
Utilities.CloseHandle(hDisk);
return null;
}
}
}

View File

@@ -1,12 +1,12 @@
namespace BurnOutSharp
{
public class FileProtection
public class ProtectionProgress
{
public string Filename { get; private set; }
public float Percentage { get; private set; }
public string Protection { get; private set; }
public FileProtection(string filename, float percentage, string protection)
public ProtectionProgress(string filename, float percentage, string protection)
{
this.Filename = filename;
this.Percentage = percentage;

View File

@@ -1,33 +1,58 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BurnOutSharp.Matching;
namespace BurnOutSharp.ProtectionType
{
public class AACS
public class AACS : IPathCheck
{
public static string CheckPath(string path, IEnumerable<string> files, bool isDirectory)
/// <inheritdoc/>
public List<string> CheckDirectoryPath(string path, IEnumerable<string> files)
{
if (isDirectory)
var matchers = new List<PathMatchSet>
{
if (files.Any(f => f.Contains(Path.Combine("aacs", "VTKF000.AACS")))
&& files.Any(f => f.Contains(Path.Combine("AACS", "CPSUnit00001.cci"))))
{
return "AACS";
}
}
else
{
string filename = Path.GetFileName(path);
if (filename.Equals("VTKF000.AACS", StringComparison.OrdinalIgnoreCase)
|| filename.Equals("CPSUnit00001.cci", StringComparison.OrdinalIgnoreCase))
{
return "AACS";
}
}
// BD-ROM
new PathMatchSet(Path.Combine("AACS", "MKB_RO.inf"), GetVersion, "AACS"),
return null;
// HD-DVD
new PathMatchSet(Path.Combine("AACS", "MKBROM.AACS"), GetVersion, "AACS"),
};
return MatchUtil.GetAllMatches(files, matchers, any: true);
}
/// <inheritdoc/>
public string CheckFilePath(string path)
{
var matchers = new List<PathMatchSet>
{
// BD-ROM
new PathMatchSet(new PathMatch("MKB_RO.inf", useEndsWith: true), GetVersion, "AACS"),
// HD-DVD
new PathMatchSet(new PathMatch("MKBROM.AACS", useEndsWith: true), GetVersion, "AACS"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);
}
public static string GetVersion(string firstMatchedString, IEnumerable<string> files)
{
if (!File.Exists(firstMatchedString))
return "(Unknown Version)";
try
{
using (var fs = File.OpenRead(firstMatchedString))
{
fs.Seek(0xB, SeekOrigin.Begin);
return fs.ReadByte().ToString();
}
}
catch
{
return "(Unknown Version)";
}
}
}
}

View File

@@ -1,20 +1,31 @@
namespace BurnOutSharp.ProtectionType
using System.Collections.Generic;
using BurnOutSharp.Matching;
namespace BurnOutSharp.ProtectionType
{
public class ActiveMARK
public class ActiveMARK : IContentCheck
{
public static string CheckContents(byte[] fileContent, bool includePosition = false)
/// <summary>
/// Set of all ContentMatchSets for this protection
/// </summary>
private static readonly List<ContentMatchSet> contentMatchers = new List<ContentMatchSet>
{
// "TMSAMVOF"
byte[] check = new byte[] { 0x54, 0x4D, 0x53, 0x41, 0x4D, 0x56, 0x4F, 0x46 };
if (fileContent.Contains(check, out int position))
return "ActiveMARK" + (includePosition ? $" (Index {position})" : string.Empty);
// TMSAMVOF
new ContentMatchSet(new byte?[] { 0x54, 0x4D, 0x53, 0x41, 0x4D, 0x56, 0x4F, 0x46 }, "ActiveMARK"),
// " " + (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)0x0 + (char)0x9A + (char)0xC1 + (char)0x16 + (char)0x00 + (char)0x10 + (char)0xC2 + (char)0x16 + (char)0x00
check = new byte[] { 0x20, 0xC2, 0x16, 0x00, 0xA8, 0xC1, 0x16, 0x00, 0xB8, 0xC1, 0x16, 0x00, 0x86, 0xC8, 0x16, 0x0, 0x9A, 0xC1, 0x16, 0x00, 0x10, 0xC2, 0x16, 0x00 };
if (fileContent.Contains(check, out position))
return "ActiveMARK 5" + (includePosition ? $" (Index {position})" : string.Empty);
// " " + (char)0xC2 + (char)0x16 + (char)0x00 + (char)0xA8 + (char)0xC1 + (char)0x16 + (char)0x00 + (char)0xB8 + (char)0xC1 + (char)0x16 + (char)0x00 + (char)0x86 + (char)0xC8 + (char)0x16 + (char)0x00 + (char)0x9A + (char)0xC1 + (char)0x16 + (char)0x00 + (char)0x10 + (char)0xC2 + (char)0x16 + (char)0x00
new ContentMatchSet(new byte?[]
{
0x20, 0xC2, 0x16, 0x00, 0xA8, 0xC1, 0x16, 0x00,
0xB8, 0xC1, 0x16, 0x00, 0x86, 0xC8, 0x16, 0x00,
0x9A, 0xC1, 0x16, 0x00, 0x10, 0xC2, 0x16, 0x00
}, "ActiveMARK 5"),
};
return null;
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchers, includePosition);
}
}
}

View File

@@ -1,26 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using BurnOutSharp.Matching;
namespace BurnOutSharp.ProtectionType
{
public class AlphaDVD
public class AlphaDVD : IPathCheck
{
public static string CheckPath(string path, IEnumerable<string> files, bool isDirectory)
/// <inheritdoc/>
public List<string> CheckDirectoryPath(string path, IEnumerable<string> files)
{
if (isDirectory)
var matchers = new List<PathMatchSet>
{
if (files.Any(f => Path.GetFileName(f).Equals("PlayDVD.exe", StringComparison.OrdinalIgnoreCase)))
return "Alpha-DVD";
}
else
{
if (Path.GetFileName(path).Equals("PlayDVD.exe", StringComparison.OrdinalIgnoreCase))
return "Alpha-DVD";
}
new PathMatchSet(new PathMatch("PlayDVD.exe", useEndsWith: true), "Alpha-DVD"),
};
return null;
return MatchUtil.GetAllMatches(files, matchers, any: true);
}
/// <inheritdoc/>
public string CheckFilePath(string path)
{
var matchers = new List<PathMatchSet>
{
new PathMatchSet(new PathMatch("PlayDVD.exe", useEndsWith: true), "Alpha-DVD"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);
}
}
}

View File

@@ -1,15 +1,23 @@
namespace BurnOutSharp.ProtectionType
{
public class AlphaROM
{
public static string CheckContents(byte[] fileContent, bool includePosition = false)
{
// "SETTEC"
byte[] check = new byte[] { 0x53, 0x45, 0x54, 0x54, 0x45, 0x43 };
if (fileContent.Contains(check, out int position))
return "Alpha-ROM" + (includePosition ? $" (Index {position})" : string.Empty);
using System.Collections.Generic;
using BurnOutSharp.Matching;
return null;
namespace BurnOutSharp.ProtectionType
{
public class AlphaROM : IContentCheck
{
/// <summary>
/// Set of all ContentMatchSets for this protection
/// </summary>
private static readonly List<ContentMatchSet> contentMatchers = new List<ContentMatchSet>
{
// SETTEC
new ContentMatchSet(new byte?[] { 0x53, 0x45, 0x54, 0x54, 0x45, 0x43 }, "Alpha-ROM"),
};
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchers, includePosition);
}
}
}

View File

@@ -1,20 +0,0 @@
namespace BurnOutSharp.ProtectionType
{
public class Armadillo
{
public static string CheckContents(byte[] fileContent, bool includePosition = false)
{
// ".nicode" + (char)0x00
byte[] check = new byte[] { 0x2E, 0x6E, 0x69, 0x63, 0x6F, 0x64, 0x65, 0x00 };
if (fileContent.Contains(check, out int position))
return $"Armadillo" + (includePosition ? $" (Index {position})" : string.Empty);
// "ARMDEBUG"
check = new byte[] { 0x41, 0x52, 0x4D, 0x44, 0x45, 0x42, 0x55, 0x47 };
if (fileContent.Contains(check, out position))
return $"Armadillo" + (includePosition ? $" (Index {position})" : string.Empty);
return null;
}
}
}

View File

@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BurnOutSharp.Matching;
namespace BurnOutSharp.ProtectionType
{
public class BDPlus : IPathCheck
{
/// <inheritdoc/>
public List<string> CheckDirectoryPath(string path, IEnumerable<string> files)
{
var matchers = new List<PathMatchSet>
{
new PathMatchSet(new List<string>
{
Path.Combine("BDSVM", "00000.svm"),
Path.Combine("BDSVM", "BACKUP", "00000.svm"),
}, GetVersion, "BD+"),
};
return MatchUtil.GetAllMatches(files, matchers, any: true);
}
/// <inheritdoc/>
public string CheckFilePath(string path)
{
var matchers = new List<PathMatchSet>
{
new PathMatchSet(new PathMatch("00000.svm", useEndsWith: true), GetVersion, "BD+"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);
}
/// <remarks>
/// Version detection logic from libbdplus was used to implement this
/// </remarks>
public static string GetVersion(string firstMatchedString, IEnumerable<string> files)
{
if (!File.Exists(firstMatchedString))
return "(Unknown Version)";
try
{
using (var fs = File.OpenRead(firstMatchedString))
{
fs.Seek(0x0D, SeekOrigin.Begin);
byte[] date = new byte[4];
fs.Read(date, 0, 4); //yymd
short year = BitConverter.ToInt16(date.Reverse().ToArray(), 2);
// Do some rudimentary date checking
if (year < 2006 || year > 2100 || date[2] < 1 || date[2] > 12 || date[3] < 1 || date[3] > 31)
return "(Invalid Version)";
return $"{year:0000}/{date[2]:00}/{date[3]:00}";
}
}
catch
{
return "(Unknown Version)";
}
}
}
}

View File

@@ -1,26 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using BurnOutSharp.Matching;
namespace BurnOutSharp.ProtectionType
{
public class Bitpool
public class Bitpool : IPathCheck
{
public static string CheckPath(string path, IEnumerable<string> files, bool isDirectory)
/// <inheritdoc/>
public List<string> CheckDirectoryPath(string path, IEnumerable<string> files)
{
if (isDirectory)
var matchers = new List<PathMatchSet>
{
if (files.Any(f => Path.GetFileName(f).Equals("bitpool.rsc", StringComparison.OrdinalIgnoreCase)))
return "Bitpool";
}
else
{
if (Path.GetFileName(path).Equals("bitpool.rsc", StringComparison.OrdinalIgnoreCase))
return "Bitpool";
}
new PathMatchSet(new PathMatch("bitpool.rsc", useEndsWith: true), "Bitpool"),
};
return null;
return MatchUtil.GetAllMatches(files, matchers, any: true);
}
/// <inheritdoc/>
public string CheckFilePath(string path)
{
var matchers = new List<PathMatchSet>
{
new PathMatchSet(new PathMatch("bitpool.rsc", useEndsWith: true), "Bitpool"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);
}
}
}

View File

@@ -1,32 +1,32 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using BurnOutSharp.Matching;
namespace BurnOutSharp.ProtectionType
{
public class ByteShield
public class ByteShield : IPathCheck
{
public static string CheckPath(string path, IEnumerable<string> files, bool isDirectory)
/// <inheritdoc/>
public List<string> CheckDirectoryPath(string path, IEnumerable<string> files)
{
if (isDirectory)
var matchers = new List<PathMatchSet>
{
if (files.Any(f => Path.GetFileName(f).Equals("Byteshield.dll", StringComparison.OrdinalIgnoreCase))
|| files.Any(f => Path.GetExtension(f).Trim('.').Equals("bbz", StringComparison.OrdinalIgnoreCase)))
{
return "ByteShield";
}
}
else
{
if (Path.GetFileName(path).Equals("Byteshield.dll", StringComparison.OrdinalIgnoreCase)
|| Path.GetExtension(path).Trim('.').Equals("bbz", StringComparison.OrdinalIgnoreCase))
{
return "ByteShield";
}
}
new PathMatchSet(new PathMatch("Byteshield.dll", useEndsWith: true), "ByteShield"),
new PathMatchSet(new PathMatch(".bbz", useEndsWith: true), "ByteShield"),
};
return null;
return MatchUtil.GetAllMatches(files, matchers, any: true);
}
/// <inheritdoc/>
public string CheckFilePath(string path)
{
var matchers = new List<PathMatchSet>
{
new PathMatchSet(new PathMatch("Byteshield.dll", useEndsWith: true), "ByteShield"),
new PathMatchSet(new PathMatch(".bbz", useEndsWith: true), "ByteShield"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);
}
}
}

View File

@@ -1,20 +1,54 @@
namespace BurnOutSharp.ProtectionType
using System.Collections.Generic;
using BurnOutSharp.Matching;
namespace BurnOutSharp.ProtectionType
{
public class CDCheck
public class CDCheck : IContentCheck
{
public static string CheckContents(byte[] fileContent, bool includePosition = false)
/// <summary>
/// Set of all ContentMatchSets for this protection
/// </summary>
private static readonly List<ContentMatchSet> contentMatchers = new List<ContentMatchSet>
{
// "GetDriveType"
byte[] check = new byte[] { 0x47, 0x65, 0x74, 0x44, 0x72, 0x69, 0x76, 0x65, 0x54, 0x79, 0x70, 0x65 };
if (fileContent.Contains(check, out int position))
return "CD Check" + (includePosition ? $" (Index {position})" : string.Empty);
// MGS CDCheck
new ContentMatchSet(new byte?[]
{
0x4D, 0x47, 0x53, 0x20, 0x43, 0x44, 0x43, 0x68,
0x65, 0x63, 0x6B
}, "Microsoft Game Studios CD Check"),
// "GetVolumeInformation"
check = new byte[] { 0x47, 0x65, 0x74, 0x56, 0x6F, 0x6C, 0x75, 0x6D, 0x65, 0x49, 0x6E, 0x66, 0x6F, 0x72, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E };
if (fileContent.Contains(check, out position))
return "CD Check" + (includePosition ? $" (Index {position})" : string.Empty);
// CDCheck
new ContentMatchSet(new byte?[] { 0x43, 0x44, 0x43, 0x68, 0x65, 0x63, 0x6B }, "Executable-Based CD Check"),
};
return null;
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchers, includePosition);
}
// These content checks are too broad to be useful
private static string CheckContentsBroad(string file, byte[] fileContent, bool includePosition = false)
{
var matchers = new List<ContentMatchSet>
{
// GetDriveType
new ContentMatchSet(new byte?[]
{
0x47, 0x65, 0x74, 0x44, 0x72, 0x69, 0x76, 0x65,
0x54, 0x79, 0x70, 0x65
}, "Executable-Based CD Check"),
// GetVolumeInformation
new ContentMatchSet(new byte?[]
{
0x47, 0x65, 0x74, 0x56, 0x6F, 0x6C, 0x75, 0x6D,
0x65, 0x49, 0x6E, 0x66, 0x6F, 0x72, 0x6D, 0x61,
0x74, 0x69, 0x6F, 0x6E
}, "Executable-Based CD Check"),
};
return MatchUtil.GetFirstMatch(file, fileContent, matchers, includePosition);
}
}
}

View File

@@ -1,60 +1,70 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BurnOutSharp.Matching;
namespace BurnOutSharp.ProtectionType
{
public class CDCops
public class CDCops : IContentCheck, IPathCheck
{
public static string CheckContents(byte[] fileContent, bool includePosition = false)
/// <summary>
/// Set of all ContentMatchSets for this protection
/// </summary>
private static readonly List<ContentMatchSet> contentMatchers = new List<ContentMatchSet>
{
// "CD-Cops, ver. "
byte[] check = new byte[] { 0x43, 0x44, 0x2D, 0x43, 0x6F, 0x70, 0x73, 0x2C, 0x20, 0x20, 0x76, 0x65, 0x72, 0x2E, 0x20 };
if (fileContent.Contains(check, out int position))
return $"CD-Cops {GetVersion(fileContent, position)}" + (includePosition ? $" (Index {position})" : string.Empty);
// CD-Cops, ver.
new ContentMatchSet(new byte?[]
{
0x43, 0x44, 0x2D, 0x43, 0x6F, 0x70, 0x73, 0x2C,
0x20, 0x20, 0x76, 0x65, 0x72, 0x2E, 0x20
}, GetVersion, "CD-Cops"),
// ".grand" + (char)0x00
check = new byte[] { 0x2E, 0x67, 0x72, 0x61, 0x6E, 0x64, 0x00};
if (fileContent.Contains(check, out position))
return "CD-Cops" + (includePosition ? $" (Index {position})" : string.Empty);
// .grand + (char)0x00
new ContentMatchSet(new byte?[] { 0x2E, 0x67, 0x72, 0x61, 0x6E, 0x64, 0x00 }, "CD-Cops"),
};
return null;
/// <inheritdoc/>
public string CheckContents(string file, byte[] fileContent, bool includePosition = false)
{
return MatchUtil.GetFirstMatch(file, fileContent, contentMatchers, includePosition);
}
public static string CheckPath(string path, IEnumerable<string> files, bool isDirectory)
/// <inheritdoc/>
public List<string> CheckDirectoryPath(string path, IEnumerable<string> files)
{
if (isDirectory)
// TODO: Original had "CDCOPS.DLL" required and all the rest in a combined OR
var matchers = new List<PathMatchSet>
{
if (files.Any(f => Path.GetFileName(f).Equals("CDCOPS.DLL", StringComparison.OrdinalIgnoreCase))
&& (files.Any(f => Path.GetExtension(f).Trim('.').Equals("GZ_", StringComparison.OrdinalIgnoreCase))
|| files.Any(f => Path.GetExtension(f).Trim('.').Equals("W_X", StringComparison.OrdinalIgnoreCase))
|| files.Any(f => Path.GetExtension(f).Trim('.').Equals("Qz", StringComparison.OrdinalIgnoreCase))
|| files.Any(f => Path.GetExtension(f).Trim('.').Equals("QZ_", StringComparison.OrdinalIgnoreCase))))
{
return "CD-Cops";
}
}
else
{
if (Path.GetFileName(path).Equals("CDCOPS.DLL", StringComparison.OrdinalIgnoreCase)
|| Path.GetExtension(path).Trim('.').Equals("GZ_", StringComparison.OrdinalIgnoreCase)
|| Path.GetExtension(path).Trim('.').Equals("W_X", StringComparison.OrdinalIgnoreCase)
|| Path.GetExtension(path).Trim('.').Equals("Qz", StringComparison.OrdinalIgnoreCase)
|| Path.GetExtension(path).Trim('.').Equals("QZ_", StringComparison.OrdinalIgnoreCase))
{
return "CD-Cops";
}
}
new PathMatchSet(new PathMatch("CDCOPS.DLL", useEndsWith: true), "CD-Cops"),
new PathMatchSet(new PathMatch(".GZ_", useEndsWith: true), "CD-Cops"),
new PathMatchSet(new PathMatch(".W_X", useEndsWith: true), "CD-Cops"),
new PathMatchSet(new PathMatch(".Qz", useEndsWith: true), "CD-Cops"),
new PathMatchSet(new PathMatch(".QZ_", useEndsWith: true), "CD-Cops"),
};
return null;
return MatchUtil.GetAllMatches(files, matchers, any: true);
}
private static string GetVersion(byte[] fileContent, int position)
/// <inheritdoc/>
public string CheckFilePath(string path)
{
char[] version = new ArraySegment<byte>(fileContent, position + 15, 4).Select(b => (char)b).ToArray();
var matchers = new List<PathMatchSet>
{
new PathMatchSet(new PathMatch("CDCOPS.DLL", useEndsWith: true), "CD-Cops"),
new PathMatchSet(new PathMatch(".GZ_", useEndsWith: true), "CD-Cops"),
new PathMatchSet(new PathMatch(".W_X", useEndsWith: true), "CD-Cops"),
new PathMatchSet(new PathMatch(".Qz", useEndsWith: true), "CD-Cops"),
new PathMatchSet(new PathMatch(".QZ_", useEndsWith: true), "CD-Cops"),
};
return MatchUtil.GetFirstMatch(path, matchers, any: true);
}
public static string GetVersion(string file, byte[] fileContent, List<int> positions)
{
char[] version = new ArraySegment<byte>(fileContent, positions[0] + 15, 4).Select(b => (char)b).ToArray();
if (version[0] == 0x00)
return "";
return string.Empty;
return new string(version);
}

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