Compare commits

...

383 Commits
2.3.2 ... 2.5.0

Author SHA1 Message Date
Matt Nadareski
229e645c8e Bump version 2022-12-12 13:35:36 -08:00
Matt Nadareski
b4606a8b84 Merge pull request #179 from TheRogueArchivist/TTR-Subchannel-Notes
Add notes about various TTR protections
2022-12-12 00:11:20 -08:00
SilasLaspada
d11782a87b Add notes about various TTR protections
* Add notes about DiscGuard's use of subchannels.

* Add reference to DiscAudit and MusicGuard.
2022-12-12 00:13:22 -07:00
Matt Nadareski
4faaf3d251 Add notes to README 2022-12-11 15:42:39 -08:00
Matt Nadareski
89dea30211 Update everything to support .NET 6.0 correctly 2022-12-11 15:32:09 -08:00
Matt Nadareski
7563634f53 Add coding guide 2022-12-11 14:55:29 -08:00
Matt Nadareski
d74dcc1d26 Proofreading 2022-12-11 01:28:17 -08:00
Matt Nadareski
cd75fd0329 Add developer guide 2022-12-11 01:09:49 -08:00
Matt Nadareski
0390ee3363 Don't print section table on debug now 2022-12-10 22:42:03 -08:00
Matt Nadareski
64f52698c3 Add extension to convert byte array 2022-12-10 22:36:02 -08:00
Matt Nadareski
8bd4a87f0b Add SecuROM AddD overlay data models 2022-12-10 22:26:42 -08:00
Matt Nadareski
4ad7c60443 Add overlay string finding 2022-12-10 20:10:25 -08:00
Matt Nadareski
d1a6c9be00 Add header padding strings 2022-12-10 17:31:41 -08:00
Matt Nadareski
3c3fd1be50 Add more guards to PE builder 2022-12-10 14:21:37 -08:00
Matt Nadareski
92c5745ac8 Remove obsolete PE helper method 2022-12-10 14:11:25 -08:00
Matt Nadareski
792de5cda2 Fix Macrovision and SafeCast checks 2022-12-10 14:11:10 -08:00
Matt Nadareski
bfe2381896 Slight tweaks and addtions to AegiSoft 2022-12-09 22:48:23 -08:00
Matt Nadareski
ded5ecb3ed Fix broken COFF string table reading 2022-12-09 22:37:35 -08:00
Matt Nadareski
7895b40a77 Convert LabelGate to string finding 2022-12-09 22:36:21 -08:00
Matt Nadareski
3f2319093f Partially convert SolidShield to string finding 2022-12-09 21:54:21 -08:00
Matt Nadareski
3806b383c9 Convert SmartE to string finding 2022-12-09 21:40:25 -08:00
Matt Nadareski
84a65e998f Partially convert ActiveMARK to string finding 2022-12-09 21:37:33 -08:00
Matt Nadareski
88049affb5 Add note to Macrovision 2022-12-09 21:36:12 -08:00
Matt Nadareski
5fd59b9eac Convert WTM to string finding 2022-12-09 21:34:00 -08:00
Matt Nadareski
8288cc7f96 Convert Wise Installer to string finding 2022-12-09 21:30:37 -08:00
Matt Nadareski
063b643597 Convert Themida to string finding 2022-12-09 21:28:21 -08:00
Matt Nadareski
ff4214877e Convert Sysiphus to string finding 2022-12-09 21:25:19 -08:00
Matt Nadareski
847fa7d2ad Partially convert SecuROM to string finding 2022-12-09 21:22:55 -08:00
Matt Nadareski
886284b42f Convert Rainbow Sentinel to string finding 2022-12-09 20:56:27 -08:00
Matt Nadareski
46776b461b Partially convert ProtectDISC to string finding 2022-12-09 20:53:25 -08:00
Matt Nadareski
e0150d7bb5 Convert NSIS to string finding 2022-12-09 15:18:17 -08:00
Matt Nadareski
ad16260c53 Convert Microsoft CAB SFX to string finding 2022-12-09 15:13:46 -08:00
Matt Nadareski
3cfe9138a5 Convert MediaMax CD-3 to string finding 2022-12-09 15:10:23 -08:00
Matt Nadareski
9e43babe0c Convert SafeCast to string finding 2022-12-09 15:06:17 -08:00
Matt Nadareski
4efbf54edf Convert C-Dilla to string finding 2022-12-09 15:04:14 -08:00
Matt Nadareski
37121a1fd2 Convert Installer VISE to string finding 2022-12-09 15:01:23 -08:00
Matt Nadareski
8a77a8a009 Simplify PE section helper methods 2022-12-09 14:59:31 -08:00
Matt Nadareski
1aef137cb2 Convert Inno Setup to string finding 2022-12-09 14:58:12 -08:00
Matt Nadareski
86707b28c0 Convert Impulse Reactor to string finding 2022-12-09 14:36:43 -08:00
Matt Nadareski
5e42bae77f Convert Gentee Installer to string finding 2022-12-09 14:22:59 -08:00
Matt Nadareski
9396804379 Convert EA to string finding 2022-12-09 14:21:18 -08:00
Matt Nadareski
5613cf9aae Convert dotFuscator to string finding 2022-12-09 14:17:01 -08:00
Matt Nadareski
ff44c717a2 Convert Code-Lock to string finding 2022-12-09 13:55:28 -08:00
Matt Nadareski
42faeb1402 Convert CDSHiELD SE to string finding 2022-12-09 13:41:09 -08:00
Matt Nadareski
7c243ac6ff Convert CDS to string finding 2022-12-09 13:09:27 -08:00
Matt Nadareski
85c6353cba Convert Armadillo to string finding 2022-12-09 12:53:19 -08:00
Matt Nadareski
c7d049efe2 Convert Alpha-ROM to string finding 2022-12-09 12:49:22 -08:00
Matt Nadareski
97aabdca33 Convert Advanced Installer to string finding 2022-12-09 12:35:58 -08:00
Matt Nadareski
9562a0eaf7 Convert XCP to string finding 2022-12-09 12:33:12 -08:00
Matt Nadareski
04bffd4889 Add string finding to wrapper base 2022-12-09 12:32:44 -08:00
Matt Nadareski
a9c71ced47 Add skelton for section string finding 2022-12-09 11:53:58 -08:00
Matt Nadareski
a03bf60ca5 Tweak LaserLok check to pre-screen 2022-12-09 11:21:27 -08:00
Matt Nadareski
02ee94f732 Reduce WMDS checks 2022-12-09 11:18:07 -08:00
Matt Nadareski
0a5ffd247c Slightly update JoWooD 2022-12-09 11:11:18 -08:00
Matt Nadareski
3f4f6a2d07 Update C-Dilla checks 2022-12-09 10:37:09 -08:00
Matt Nadareski
0d22f78b10 Remove standalone ExecutableTest 2022-12-08 23:40:39 -08:00
Matt Nadareski
ca1e3e8e63 Port ExecutableTest to Test 2022-12-08 23:38:28 -08:00
Matt Nadareski
debb7cde2d Start preparing ExecutableTest 2022-12-08 23:31:48 -08:00
Matt Nadareski
40c35a5d50 Update Nuget packages 2022-12-08 23:01:28 -08:00
Matt Nadareski
971f769031 Update project files 2022-12-08 22:53:07 -08:00
Matt Nadareski
a1a3adfafa Move psxt001z to own library 2022-12-08 22:50:42 -08:00
Matt Nadareski
912b49ecc4 Cleanup ProtectionProgress 2022-12-08 22:37:57 -08:00
Matt Nadareski
dcccd6e313 Remove System.Diagnostics dependency 2022-12-08 22:33:02 -08:00
Matt Nadareski
005529f959 Add stream support to Matching 2022-12-08 22:27:17 -08:00
Matt Nadareski
3d5904c997 Move Matching to own library 2022-12-08 22:07:14 -08:00
Matt Nadareski
dcc8915dd2 FileTypes -> SupportedFileType 2022-12-08 21:46:22 -08:00
Matt Nadareski
4a589603e4 Clarify the IScannable description 2022-12-08 21:43:39 -08:00
Matt Nadareski
4f16b9d9e8 Fix misnamed paramter in IScannable 2022-12-08 21:42:24 -08:00
Matt Nadareski
ce7ecc78cc Remove now-useless ShouldScan, part 2 2022-12-08 21:41:33 -08:00
Matt Nadareski
9c8a677f13 Remove now-useless ShouldScan 2022-12-08 21:37:11 -08:00
Matt Nadareski
39f2dd88aa Make it easier to support new file types 2022-12-08 21:32:52 -08:00
Matt Nadareski
3b4929a368 Invert if in ProtectDisc check for clarity 2022-12-08 17:19:42 -08:00
Matt Nadareski
f290b8462d Fix getting last section for SmartE 2022-12-08 17:18:09 -08:00
Matt Nadareski
5ce4f3be56 Clean up awkard SolidShield check 2022-12-08 17:16:47 -08:00
Matt Nadareski
a5158f04db Add ActiveMARK resource check 2022-12-08 17:16:12 -08:00
Matt Nadareski
2aa9f088a4 Add WTM legal trademarks check 2022-12-08 17:06:36 -08:00
Matt Nadareski
bb37c3f94c Add missing commented out print 2022-12-08 17:06:05 -08:00
Matt Nadareski
92b3f14d7b Add SolidShield import directory table checks 2022-12-08 16:53:43 -08:00
Matt Nadareski
2dcd30e346 Make unknown resource printing better 2022-12-08 16:40:04 -08:00
Matt Nadareski
c4ed59dc46 Replace 2 SecuROM PA checks 2022-12-08 16:38:56 -08:00
Matt Nadareski
81141fb2fa Fix Impulse Reactor 2022-12-08 16:08:58 -08:00
Matt Nadareski
58c6f00a6c Add another embedded resource check 2022-12-08 16:01:53 -08:00
Matt Nadareski
c1f4e42219 Convert one MediaMax CD-3 check 2022-12-08 16:01:30 -08:00
Matt Nadareski
02b16843e5 Add note to LaserLok 2022-12-08 15:46:01 -08:00
Matt Nadareski
86bfcc15f9 Add note to JoWooD 2022-12-08 15:37:11 -08:00
Matt Nadareski
4fab4e71f7 Convert one Impulse Reactor check 2022-12-08 15:24:53 -08:00
Matt Nadareski
b230462860 Update GFWL with new framework 2022-12-08 15:16:43 -08:00
Matt Nadareski
5f49b56c3d Add a couple more named version fields 2022-12-08 14:59:15 -08:00
Matt Nadareski
51482dd225 Rename Code-Lock file 2022-12-08 14:55:07 -08:00
Matt Nadareski
c1cb1a41a9 Add note to CDSHiELD SE 2022-12-08 14:53:59 -08:00
Matt Nadareski
b496c79b34 Remove previously added note 2022-12-08 14:49:39 -08:00
Matt Nadareski
fcad7db5ab Add note to CD-Lock 2022-12-08 14:44:15 -08:00
Matt Nadareski
e1bd26f712 Add undocumented version info keys 2022-12-08 14:40:30 -08:00
Matt Nadareski
0518184786 Make CD-Check simpler 2022-12-08 14:36:43 -08:00
Matt Nadareski
576e234216 Remove last explicit .rsrc section read 2022-12-08 14:22:32 -08:00
Matt Nadareski
3c750dac86 Clean up most of the StarForce rsrc checks 2022-12-08 14:01:12 -08:00
Matt Nadareski
714cee586d Make printing methods for all PE resources 2022-12-08 14:00:29 -08:00
Matt Nadareski
cf9cc30d5e Handle casting exceptions 2022-12-08 13:13:16 -08:00
Matt Nadareski
713b1c83e1 Fix yet more string data reading 2022-12-08 10:38:46 -08:00
Matt Nadareski
ddf289d747 Force reading name table as ASCII 2022-12-08 10:17:26 -08:00
Matt Nadareski
ae1edf0f21 Sync resource building in byte and stream paths 2022-12-08 10:13:22 -08:00
Matt Nadareski
38665ddbf3 Add note to Wise installer 2022-12-08 00:07:11 -08:00
Matt Nadareski
2af0f166eb Update WinRAR SFX with new framework 2022-12-07 23:56:38 -08:00
Matt Nadareski
aeaeff28d3 Better alignment in PE resources 2022-12-07 23:56:08 -08:00
Matt Nadareski
28948a0511 Add back truncation 2022-12-07 23:09:18 -08:00
Matt Nadareski
52d190e339 Rewrite WinZip SFX PE checks 2022-12-07 22:52:49 -08:00
Matt Nadareski
d47707c433 Add some CD-Cops notes 2022-12-06 22:31:46 -08:00
Matt Nadareski
11bc46ae86 Update WZ-SFX NE checks a little 2022-12-06 21:04:52 -08:00
Matt Nadareski
d229b23ea6 Fix CD-Cops table checks 2022-12-06 21:04:08 -08:00
Matt Nadareski
41dc7f7b14 Add notes for NE Inno detection 2022-12-05 20:44:34 -08:00
Matt Nadareski
dbd8b14cd2 Convert arbitray data read for NE WZ-SFX 2022-12-05 19:57:57 -08:00
Matt Nadareski
3af8adb067 Add unversioned CD-Cops NE checks 2022-12-05 17:05:41 -08:00
Matt Nadareski
5baa470d54 Use header padding data for UPX 2022-12-05 16:50:18 -08:00
Matt Nadareski
1f5d5215f7 Clean up SafeDisc checks, add header padding check 2022-12-05 15:58:44 -08:00
Matt Nadareski
24c77ecd07 Add header padding data for searching 2022-12-05 15:56:37 -08:00
Matt Nadareski
2264fc0172 Fix IIF product name check 2022-12-05 15:46:56 -08:00
Matt Nadareski
00028ac59b Add notes to EXE Stealth 2022-12-05 14:11:52 -08:00
Matt Nadareski
ad5735a559 Update printing for import tables 2022-12-05 13:57:07 -08:00
Matt Nadareski
9411f5044a Fix import table parsing 2022-12-05 13:52:15 -08:00
Matt Nadareski
7293d55239 Fix PlayJ description check 2022-12-05 12:57:54 -08:00
Matt Nadareski
31dc347df4 Merge pull request #178 from mnadareski/new-exe-framework
Use new executable framework for protection checks
2022-12-05 11:30:54 -08:00
Matt Nadareski
d72d0d4dc2 Clean up printing methods 2022-12-05 11:01:22 -08:00
Matt Nadareski
7181dc9d5b Fix formatting/tagging 2022-12-05 10:47:17 -08:00
Matt Nadareski
95fa8681fe Make SmartE checks even simpler 2022-12-05 10:21:15 -08:00
Matt Nadareski
3a63755d96 Simplify section printing 2022-12-05 10:06:37 -08:00
Matt Nadareski
5390970054 Fix segment validity checks 2022-12-05 10:04:37 -08:00
Matt Nadareski
2b43f2b261 Add notes to SmartE 2022-12-04 23:21:18 -08:00
Matt Nadareski
4330cd1aac Fix WTM code/CODE section 2022-12-04 23:20:22 -08:00
Matt Nadareski
fc9dd8a34d Add base relocation table to passthrough 2022-12-04 23:17:34 -08:00
Matt Nadareski
c64abc15c9 Move .rsrc StarForce checks to new file 2022-12-04 23:00:30 -08:00
Matt Nadareski
fca12c639c Update CDS PlayJ check 2022-12-04 22:44:59 -08:00
Matt Nadareski
a1522aabd6 Add relocation real address 2022-12-04 22:37:59 -08:00
Matt Nadareski
9be4b339f8 Add PE base relocation table parsing and printing 2022-12-04 22:32:41 -08:00
Matt Nadareski
ce1c74aec3 Update DiscGuard 2022-12-04 21:11:55 -08:00
Matt Nadareski
e824428e0f String tables are always Unicode 2022-12-04 13:38:04 -08:00
Matt Nadareski
2fd4a8a9b1 Fix launch.json 2022-12-04 00:41:56 -08:00
Matt Nadareski
82de7e8b8e Off by one 2022-12-03 23:58:20 -08:00
Matt Nadareski
26831b4732 Both, both is good 2022-12-03 23:48:48 -08:00
Matt Nadareski
a6862925ca Use endOffset instead of EOF 2022-12-03 23:46:19 -08:00
Matt Nadareski
1e2ce169af Fix unaligned end-of-file certificates 2022-12-03 23:41:42 -08:00
Matt Nadareski
dda9b3551a Cast certificate length more safely 2022-12-03 23:32:11 -08:00
Matt Nadareski
b1760d3541 Fix version printing 2022-12-03 23:30:32 -08:00
Matt Nadareski
2c1e087bc6 Include negative numbers 2022-12-03 23:23:55 -08:00
Matt Nadareski
90d5bd52a2 Check more EA resources 2022-12-03 23:17:29 -08:00
Matt Nadareski
26db75853b Fix malformed PE certificates 2022-12-03 23:09:45 -08:00
Matt Nadareski
fe5a674518 Add more safety around resource finding 2022-12-03 22:55:03 -08:00
Matt Nadareski
2fe56cd6af Register encoding provider in scanner 2022-12-03 22:47:57 -08:00
Matt Nadareski
f26e82d2bc Fix some PE resource caching issues 2022-12-03 22:47:32 -08:00
Matt Nadareski
65892f067a Merge remote-tracking branch 'origin' into new-exe-framework 2022-12-03 22:29:12 -08:00
Matt Nadareski
768717d7b3 Remove old executable framework 2022-12-03 22:28:03 -08:00
Matt Nadareski
f78b3daf8b Attempt to use new executable framework 2022-12-03 22:17:48 -08:00
Matt Nadareski
8a6f481118 Fix indexed section data read 2022-12-03 22:13:17 -08:00
Matt Nadareski
f420434fd3 Handle overlay data better 2022-12-03 21:59:21 -08:00
Matt Nadareski
8e73d7970f Fix PE data locking, add offset read helper 2022-12-03 21:37:32 -08:00
Matt Nadareski
9699af93bc Add temporary helper method for NE 2022-12-03 21:37:05 -08:00
Matt Nadareski
44ca0a94b7 Better PE wrapper section handling 2022-12-03 20:56:06 -08:00
Matt Nadareski
b3bf008e31 Fix NE table printing 2022-12-03 14:44:18 -08:00
Matt Nadareski
ce5e2982d2 Fix resource entry handling 2022-12-03 14:22:54 -08:00
Matt Nadareski
7d2edd315c Add stub data to cache, resource/section finding 2022-12-03 13:17:29 -08:00
Matt Nadareski
8ae0452873 Fix reading 0-length string data 2022-12-03 13:08:10 -08:00
Matt Nadareski
e0efc0d9ab Cache PE overlay data 2022-12-02 22:44:55 -08:00
Matt Nadareski
3ce3b7ca2b Cache PE resource, version, manifest 2022-12-02 22:24:22 -08:00
Matt Nadareski
6997608b63 Split printing into methods, add notes 2022-12-02 21:20:52 -08:00
Matt Nadareski
cee7f12974 Add printing regions 2022-12-02 20:09:55 -08:00
Matt Nadareski
5b4a8d5775 Ceeate wrapper base class, PE raw reads 2022-12-02 20:05:20 -08:00
Matt Nadareski
a59bedec5d Add data source to all wrappers, add note 2022-12-02 17:19:59 -08:00
Matt Nadareski
3c5d670924 Add printing to wrappers, remove from test exe 2022-12-02 16:39:49 -08:00
Matt Nadareski
7bab251915 Add PE passthrough properties 2022-12-02 16:16:12 -08:00
Matt Nadareski
57e47eee5d Add LE passthrough properties 2022-12-02 15:58:06 -08:00
Matt Nadareski
4f09c57755 Add NE passthrough properties 2022-12-02 15:44:33 -08:00
Matt Nadareski
7c1edab6ca Add DOS stub passthrough for LE/NE/PE 2022-12-02 15:35:10 -08:00
Matt Nadareski
f24004c949 Add proof-of-concept MS-DOS wrapper 2022-12-02 15:29:10 -08:00
Matt Nadareski
c4bf3931e2 Add skeletons for all wrappers 2022-12-02 15:20:44 -08:00
Matt Nadareski
fe13562f3e Add notes about ByteShield (TheRogueArchivist) 2022-12-02 15:06:31 -08:00
Matt Nadareski
64334d72ea Improve SolidShield detection
- Add new SolidShield executable and file checks.
- Fix false positives in file name checks due to not using a directory separator in the check.
- Add a few notes and reorganize slightly.
2022-12-02 15:02:45 -08:00
Matt Nadareski
a915980187 Improve SafeDisc detection
- Add support for detecting 4.60.000's drvmgt.
- Add version checks for Diag.exe to remove one case of "SafeCast/SafeDisc" ambiguity.
2022-12-02 14:59:26 -08:00
Matt Nadareski
af882fa588 Properly differentiate between Code-Lock and CopyLok (TheRogueArchivist) 2022-12-02 14:56:08 -08:00
Matt Nadareski
7fcaa16835 Add first Themida check (TheRogueArchivist) 2022-12-02 14:52:28 -08:00
Matt Nadareski
4d640f3cf2 Add Wrapper skeleton project 2022-12-02 14:44:06 -08:00
Matt Nadareski
25d495b1d0 ASN.1 OID parsing (nw) 2022-12-02 14:18:15 -08:00
Matt Nadareski
7fd936c4a8 Handle empty resource name strings 2022-12-02 14:15:50 -08:00
Matt Nadareski
fe753fc4fd Try to fix null resource types issue 2022-12-02 14:15:44 -08:00
Matt Nadareski
0a4763fcc1 Fix PE dialog item printing 2022-12-02 14:15:37 -08:00
Matt Nadareski
e281faf664 Add first attempt at PE certificate parsing 2022-11-12 21:56:24 -08:00
Matt Nadareski
dcb291c1c6 Disable printing raw PE cert data 2022-11-11 16:23:25 -08:00
Matt Nadareski
ecd1c93bb9 Add PE message resource printing, fix parsing 2022-11-11 16:20:17 -08:00
Matt Nadareski
eeb555a6ce Add PE message resource data to parser 2022-11-11 15:52:05 -08:00
Matt Nadareski
27d53abd10 Add PE message resource models 2022-11-11 15:41:37 -08:00
Matt Nadareski
91eef55173 Fix tiny formatting issue 2022-11-11 15:33:41 -08:00
Matt Nadareski
f9e1518da6 Add PE menu resource reading and writing 2022-11-11 15:31:00 -08:00
Matt Nadareski
5b974260cc Add PE extended dialog templates 2022-11-11 14:22:53 -08:00
Matt Nadareski
554374b710 Add PE dialog template extended 2022-11-11 14:12:03 -08:00
Matt Nadareski
475669ac1b Add PE standard dialog parsing and writing 2022-11-11 13:56:23 -08:00
Matt Nadareski
623d1e6a40 Invert PE "if" logic where possible 2022-11-11 10:08:15 -08:00
Matt Nadareski
08fa4a997f Fix PE resource data entry printing 2022-11-11 09:58:50 -08:00
Matt Nadareski
4e21cf8494 Make PE RVA checks simpler 2022-11-11 09:58:19 -08:00
Matt Nadareski
2ebbda6852 Don't trust PE sections 2022-11-10 23:38:59 -08:00
Matt Nadareski
010a6d6e42 Safeguard all PE virtual address uses 2022-11-10 23:06:21 -08:00
Matt Nadareski
3b1481879a Fix PE debug table parsing 2022-11-10 22:39:10 -08:00
Matt Nadareski
3ddcc3884b Fix PE printing typo 2022-11-10 22:23:36 -08:00
Matt Nadareski
260ab1ec89 Safeguard PE invalid virtual addresses 2022-11-10 22:19:58 -08:00
Matt Nadareski
69803a999f Fix PE virtual address for section-aligned RVAs 2022-11-10 22:09:58 -08:00
Matt Nadareski
d4a75ed871 Reorganize PE notes for delay-load 2022-11-10 21:41:42 -08:00
Matt Nadareski
7394f14218 Add missing PE notes for sections 2022-11-10 21:38:52 -08:00
Matt Nadareski
23cd7b9ebd Add note for PE .drectve section 2022-11-10 21:32:01 -08:00
Matt Nadareski
477cfee78e Add PE debug section printing 2022-11-10 21:29:17 -08:00
Matt Nadareski
750cecfdaf Add PE partial debug table parsing 2022-11-10 21:24:28 -08:00
TheRogueArchivist
32a28fba32 Add more checks for Rainbow Sentinel (#171)
* Add more checks for Rainbow Sentinel.

* Make comments more consistent.

* Add more notes on versions/
2022-11-10 16:23:10 -08:00
Matt Nadareski
fe926cbf9a Rewrite PE accelerator table extension 2022-11-10 13:09:23 -08:00
Matt Nadareski
d18e65ca6c Add PE debug directory skeleton, notes 2022-11-10 12:57:41 -08:00
Matt Nadareski
ec67ca605c Fix PE virtual directory size issues 2022-11-10 12:57:19 -08:00
Matt Nadareski
9cb3c963a1 Add PE .sxdata section notes 2022-11-10 12:42:34 -08:00
Matt Nadareski
8a4caf82bb Add PE .coremeta section note 2022-11-10 12:40:18 -08:00
Matt Nadareski
7a5941cfa9 Add PE import table printing 2022-11-10 12:16:48 -08:00
Matt Nadareski
690c49ae1f Fix PE import table parsing 2022-11-10 11:58:46 -08:00
Matt Nadareski
c77c095893 Add initial PE import table parsing 2022-11-10 11:31:06 -08:00
Matt Nadareski
98ddc65fa2 Add PE import table to model 2022-11-10 10:10:12 -08:00
Matt Nadareski
41a7c71b7d Fix PE bitmasks 2022-11-10 00:06:29 -08:00
Matt Nadareski
cb1d3d1db4 Add PE export table to printing 2022-11-09 23:27:06 -08:00
Matt Nadareski
5ba2a31d7d Add PE export table to builder 2022-11-09 23:06:52 -08:00
Matt Nadareski
0768a93bcb Fix Stream ReadString extension 2022-11-09 23:04:07 -08:00
Matt Nadareski
e690c6d0ff Add PE .edata components (not hooked up)
This also does a pretty major cleanup of TODOs
2022-11-09 22:23:40 -08:00
Matt Nadareski
0c6bf406c1 Fix PE delay-load directory 2022-11-09 22:02:38 -08:00
Matt Nadareski
95b5f12226 Add PE grouped sections note 2022-11-09 21:55:15 -08:00
Matt Nadareski
5b4b622834 Add PE delay-load directory to printing 2022-11-09 21:50:36 -08:00
Matt Nadareski
b908b77a34 Add PE delay-load directory table to builder 2022-11-09 21:47:39 -08:00
Matt Nadareski
dbba310385 Add console print to PE string table parsing 2022-11-09 21:32:36 -08:00
Matt Nadareski
0a0ca9ba93 Be slightly safer on PE string table parsing 2022-11-09 21:31:40 -08:00
Matt Nadareski
8aa574a7c4 Add PE COFF string table printing 2022-11-09 21:28:00 -08:00
Matt Nadareski
37ac8c038f Add PE COFF string table to builder 2022-11-09 21:22:29 -08:00
Matt Nadareski
9b6456a80f Register encoding provider for ExecutableTest 2022-11-09 21:09:37 -08:00
Matt Nadareski
f6ffd314b1 Add PE attribute certificate table printing 2022-11-09 21:08:33 -08:00
Matt Nadareski
b569c6a6dd Add PE certificate attribute table to builder 2022-11-09 21:02:02 -08:00
Matt Nadareski
c84f416973 Add better TODO 2022-11-09 20:44:14 -08:00
Matt Nadareski
6ebc476d2b Remove partially completed TODO 2022-11-09 20:43:47 -08:00
Matt Nadareski
98c340d94d Add unused PE font group parser 2022-11-09 20:39:20 -08:00
Matt Nadareski
78d80918aa Remove incorrect console statement 2022-11-09 20:38:37 -08:00
Matt Nadareski
e8d7d6b4e7 Print nonstandard PE manifest items 2022-11-09 20:07:03 -08:00
Matt Nadareski
53341b0dc0 Add remaining unused PE manifest types 2022-11-09 20:04:06 -08:00
Matt Nadareski
f64c7d81ad Add full PE assembly manifest printing 2022-11-09 19:59:39 -08:00
Matt Nadareski
197de59089 Add PE assembly manifest deserialization 2022-11-09 19:09:30 -08:00
Matt Nadareski
13eb37cc46 Add full PE file info printing 2022-11-09 16:17:40 -08:00
Matt Nadareski
c21c0ff411 Add PE version info parsing 2022-11-09 15:53:40 -08:00
Matt Nadareski
72f6af7019 Add PE version resource models 2022-11-09 15:17:53 -08:00
Matt Nadareski
6b14321505 Clean up PE string table parsing 2022-11-09 14:27:32 -08:00
Matt Nadareski
4fcb719613 Start adding PE cursor and icon resources 2022-11-09 14:19:23 -08:00
Matt Nadareski
50915d9100 Clean up PE accelerator table 2022-11-09 14:19:14 -08:00
Matt Nadareski
834792bc2d Print NE string table resources 2022-11-09 14:18:59 -08:00
Matt Nadareski
04b225711f Better add TODOs to PE resource printing 2022-11-09 13:29:30 -08:00
Matt Nadareski
eee4a75353 Remove PE resource header writing
This was incorrectly assuming all resources had this header. This is not correct, only a few do. Another debug statement to print out as Unicode characters helped solve this.
2022-11-09 13:22:07 -08:00
Matt Nadareski
15d0df1a12 Add PE resource tree printing (incomplete) 2022-11-09 13:17:14 -08:00
Matt Nadareski
5c3e8c35c4 Add PE accelerator table 2022-11-09 12:05:30 -08:00
Matt Nadareski
ac514fce30 Add PE resource header 2022-11-09 11:58:35 -08:00
Matt Nadareski
f7343ea305 Update PE resource type enum 2022-11-09 11:35:31 -08:00
Matt Nadareski
1435421c3c Add PE accelerator table resource 2022-11-09 11:26:21 -08:00
Matt Nadareski
735c0fe367 Add PE resource table parsing (incomplete) 2022-11-09 11:11:30 -08:00
Matt Nadareski
af99cfa6f9 Add PE virtual address extension 2022-11-08 22:05:30 -08:00
Matt Nadareski
525ff009b6 Fix COFF symbol table entries 2022-11-08 21:43:20 -08:00
Matt Nadareski
ee46167320 Add PE COFF symbol table parsing, printing 2022-11-08 21:36:46 -08:00
Matt Nadareski
6fc03403b4 Add PE section table parsing, writing 2022-11-08 16:31:30 -08:00
Matt Nadareski
760c481d39 Fix PE section header 2022-11-08 16:31:15 -08:00
Matt Nadareski
2c4906534b Enable PE headers writing 2022-11-08 15:11:18 -08:00
Matt Nadareski
2ed79f3f9c Safer PE optional header builder; start printing PE 2022-11-08 15:02:31 -08:00
Matt Nadareski
7e9be878c4 Add PE optional header parsing to builder 2022-11-08 14:27:41 -08:00
Matt Nadareski
0a5ae3b090 Add PE COFF file header parsing to builder 2022-11-08 14:12:05 -08:00
Matt Nadareski
70b8c1f9b7 NE nonresident table absolute, not relative 2022-11-08 14:07:02 -08:00
Matt Nadareski
b067b671db Add PE signature parsing to builder 2022-11-08 14:04:02 -08:00
Matt Nadareski
268ccac7e1 Add NE nonresident name table printing 2022-11-08 13:41:51 -08:00
Matt Nadareski
5e487bc4ba Add NE entry table printing 2022-11-08 13:41:37 -08:00
Matt Nadareski
ffeda7d60b Print NE imported-name table 2022-11-08 13:35:21 -08:00
Matt Nadareski
2779d1a489 Add NE module reference table printing 2022-11-08 13:30:12 -08:00
Matt Nadareski
1752815654 Print NE resident name table 2022-11-08 13:27:12 -08:00
Matt Nadareski
429ca400e5 Fix all NE table bounded offsets 2022-11-08 13:25:31 -08:00
Matt Nadareski
984853bd66 Add NE resource name/type string output 2022-11-08 13:16:24 -08:00
Matt Nadareski
ff2549d4b7 Fix NE resource table parsing 2022-11-08 13:16:00 -08:00
Matt Nadareski
d33bb6b4bb Add first part of NE printing 2022-11-08 13:07:48 -08:00
Matt Nadareski
a3c0eca063 Address some NE parsing issues 2022-11-08 11:52:24 -08:00
Matt Nadareski
10de4ac78e Create executable info test program 2022-11-08 11:35:39 -08:00
Matt Nadareski
895394ebb9 Fix MS-DOS header parsing 2022-11-08 11:30:23 -08:00
Matt Nadareski
349f8d936a Add NE nonresident table to builder 2022-11-08 10:36:16 -08:00
Matt Nadareski
bbc65391a1 Add NE entry table to builder 2022-11-08 10:09:23 -08:00
Matt Nadareski
751c248657 Update NE entry table bundle 2022-11-08 09:59:15 -08:00
Matt Nadareski
9052cd3cdd Add NE imported-name table to builder 2022-11-07 14:13:23 -08:00
Matt Nadareski
2736527fc1 NE imported name table needs indexes 2022-11-07 13:22:59 -08:00
Matt Nadareski
0c7001acf6 Add NE module-reference table parsing
This also fixes a critical flaw in the resident-name table parsing since there is no defined size, just the point where the next table begins.
2022-11-07 10:42:48 -08:00
Matt Nadareski
9e9be5bf09 Add NE resident-name table parsing to builder 2022-11-07 10:13:10 -08:00
Matt Nadareski
008e1ad27b Finalize NE resource table building 2022-11-07 09:38:34 -08:00
Matt Nadareski
3e5ae14a54 Implment first half of NE resource table builder 2022-11-07 09:18:20 -08:00
Matt Nadareski
0c28833b14 Fix offsets for NE tables in builder 2022-11-06 23:58:53 -08:00
Matt Nadareski
75ef95c6bf Fix missing LE/LX/PE stub setting in builders 2022-11-06 23:52:52 -08:00
Matt Nadareski
b906f3c654 Add NE resource table skeleton to builder 2022-11-06 23:49:46 -08:00
Matt Nadareski
4b274a454b Add extensions for NE resource entries 2022-11-06 23:47:54 -08:00
Matt Nadareski
6554005742 Return incomplete NE from builder 2022-11-06 21:48:53 -08:00
Matt Nadareski
9f04022afc Add regions for easier code navigation 2022-11-06 21:48:19 -08:00
Matt Nadareski
fbab512975 Add NE segment table parsing to builder 2022-11-06 21:45:37 -08:00
Matt Nadareski
c9b3a67c8b Merge branch 'master' of https://github.com/mnadareski/BurnOutSharp 2022-11-06 21:39:02 -08:00
Matt Nadareski
3169cd6591 Let private methods be simpler 2022-11-06 21:38:42 -08:00
TheRogueArchivist
b116e487d3 Overhaul Freelock (#170)
* Overhaul Freelock

* Overhaul Freelock, including notes and new checks, along with confirming the existing checks.

* Add text-based checks for Freelock.

* Update README.

* Fix whitespace and re-add return

* Fix whitespace and re-add return
2022-11-06 21:30:59 -08:00
Matt Nadareski
aa57044bb8 Add skeleton for NE segment table parsing 2022-11-06 21:30:19 -08:00
TheRogueArchivist
fdd578dad9 Confirm existing WinLock checks (#169)
* Confirm existing WinLock checks

* Confirm existing WinLock checks.

* Add WinLock notes.

* Update README.

* Rename Winlock.cs to WinLock.cs
2022-11-06 21:22:58 -08:00
Matt Nadareski
2801520546 Add NE header parsing tp builder 2022-11-06 21:19:48 -08:00
TheRogueArchivist
caaf983b3d Overhaul TZCopyProtection (#168)
* Add notes and research relating to TZCopyProtection.

* Fix name (was previously "TZCopyProtector").

* Add new file check for "ZakMcCrack.Ghost".

* Update README.
2022-11-06 21:05:58 -08:00
TheRogueArchivist
aaba13530c Confirm existing CD-Protector checks (#167)
* Confirm existing CD-Protector checks, and add one new one.

* Add CD-Protector notes.

* Update README.
2022-11-06 21:04:03 -08:00
TheRogueArchivist
e05ec3bcee Initial addition of Rainbow Sentinel (#166)
* Initial addition of Rainbow Sentinel

* Basic detection based off of one sample, no specific research/notes.

* Update README.

* Add additional sample sources for Rainbow Sentinel

* Add additional sample sources for Rainbow Sentinel, with no new functionality.

* Add Rainbow Sentinel text checks

* Add Rainbow Sentinel text checks.
2022-11-06 21:03:23 -08:00
Matt Nadareski
703a132a61 Add missing MS-DOS stub to builders 2022-11-06 00:01:24 -07:00
Matt Nadareski
e55226e685 Add header skeletons for NE/LE/LX/PE builders 2022-11-05 23:59:38 -07:00
Matt Nadareski
9a4e6de5f9 Add MS-DOS stub to NE/LE/LX/PE builders 2022-11-05 23:52:16 -07:00
Matt Nadareski
a4e55a328c Add boilerplate for NE, LE, PE builders 2022-11-05 23:47:50 -07:00
Matt Nadareski
2705685f07 Add placeholder LE interface (fixes #165) 2022-11-05 23:42:55 -07:00
Matt Nadareski
b7fb17a79f Add skeletons for other executable types 2022-11-05 23:36:15 -07:00
Matt Nadareski
ffeb73ab7c Add proof-of-concept MS-DOS builder 2022-11-05 23:29:04 -07:00
Matt Nadareski
427dec56e4 Add COFF archive note 2022-11-05 22:46:50 -07:00
Matt Nadareski
94ce87d953 Add PE resource classes 2022-11-05 22:45:18 -07:00
Matt Nadareski
0dc4f0f11a Add PE load configuration directory; fix naming 2022-11-05 22:34:33 -07:00
Matt Nadareski
a1d7e65ffb Add PE TLS directory 2022-11-05 22:11:41 -07:00
Matt Nadareski
61702d9c2a Add PE base relocation blocks 2022-11-05 22:03:17 -07:00
Matt Nadareski
5b08bef53f Add "missing" pieces list, so far 2022-11-05 21:54:36 -07:00
Matt Nadareski
53a6588054 Add PE hint name table entries 2022-11-05 21:49:34 -07:00
Matt Nadareski
9855c0c13e Add PE import directory table entries 2022-11-05 21:41:33 -07:00
Matt Nadareski
c5d005bdeb Add PE export address table entries 2022-11-05 21:37:54 -07:00
Matt Nadareski
1eb844c75b Add PE export directory table 2022-11-05 21:34:30 -07:00
Matt Nadareski
7e177f3cbf Add PE debug directory 2022-11-05 21:29:36 -07:00
Matt Nadareski
eb91cfbda1 Add PE delay load directory 2022-11-05 21:12:41 -07:00
Matt Nadareski
54082c1fce Add PE attribute certificate table 2022-11-05 21:02:30 -07:00
Matt Nadareski
b5caf6dacf Add PE COFF string table 2022-11-05 15:40:48 -07:00
Matt Nadareski
f4d1ce5388 Add PE CLR token definition symbol 2022-11-05 15:37:25 -07:00
Matt Nadareski
7d7ec69dc1 Add PE auxiliary symbol record formats 2022-11-05 15:34:14 -07:00
Matt Nadareski
7208288c00 Add PE section numbers 2022-11-05 15:16:38 -07:00
Matt Nadareski
aff3745859 Add PE COFF symbol table 2022-11-05 00:17:26 -07:00
Matt Nadareski
e103ddd216 Add PE COFF line numbers to section headers 2022-11-05 00:08:00 -07:00
Matt Nadareski
41a4965775 Add PE COFF relocations to section headers 2022-11-05 00:04:17 -07:00
Matt Nadareski
49a06f513b Add PE section table 2022-11-04 23:56:56 -07:00
Matt Nadareski
1308f3684b Add PE data directories 2022-11-04 23:50:54 -07:00
Matt Nadareski
c51eccac38 Add PE optional header 2022-11-04 23:41:31 -07:00
Matt Nadareski
09157767bf Add PE COFF file header 2022-11-04 23:25:02 -07:00
Matt Nadareski
32cc2c708a Add PE enums 2022-11-04 23:19:28 -07:00
Matt Nadareski
7f2de233fc Add PE skeleton, change MZ stubs 2022-11-04 21:05:03 -07:00
Matt Nadareski
7cb150606c Add LE/LX fix-up record table 2022-11-04 21:00:02 -07:00
Matt Nadareski
87cac010eb Fill out most of FixupRecordTableEntry 2022-11-04 17:23:21 -07:00
Matt Nadareski
03926754e7 Add LE/LX FRT source offset/count 2022-11-04 16:59:29 -07:00
Matt Nadareski
65efda1a7a Add LE/LX entry table 2022-11-04 16:51:04 -07:00
Matt Nadareski
5941d4ca16 Add skeleton for FixupRecordTableEntry 2022-11-04 16:27:01 -07:00
Matt Nadareski
e77101af89 Add LE/LX fixup record enums 2022-11-04 16:19:42 -07:00
Matt Nadareski
e766be6af9 Add LE/LX Fix-up page table 2022-11-04 16:09:20 -07:00
Matt Nadareski
95d1658324 Add LE/LX debug information 2022-11-04 16:04:01 -07:00
Matt Nadareski
9b24550738 Add LE/LX verify record directive table 2022-11-04 15:56:08 -07:00
Matt Nadareski
7947568019 Add LE/LX entry table bundle type enum 2022-11-04 15:46:24 -07:00
Matt Nadareski
399ee98923 Add LE/LX import procedure name table 2022-11-04 15:36:38 -07:00
Matt Nadareski
7b3b4a2ec5 Add LE/LX imported module name table 2022-11-04 15:31:59 -07:00
Matt Nadareski
09177da620 Add LE/LX per-page checksum table 2022-11-04 15:29:04 -07:00
Matt Nadareski
8392cfb2fa Add LE/LX module format directives table 2022-11-04 15:24:15 -07:00
Matt Nadareski
01face7315 Add LE/LX resident name tables 2022-11-04 15:00:11 -07:00
Matt Nadareski
5b6f4d65bf Add LE/LX resource table 2022-11-04 14:51:57 -07:00
Matt Nadareski
cd6f8f3db3 Add LE/LX object page table 2022-11-04 13:48:30 -07:00
Matt Nadareski
a9b07ddf1d Add more thorough LE/LX notes 2022-11-04 13:42:58 -07:00
Matt Nadareski
f3710c575b Add LE object table 2022-11-04 12:59:50 -07:00
Matt Nadareski
1f5ab45a1e Add LE information block 2022-11-04 12:55:43 -07:00
Matt Nadareski
58d453db11 Update csproj 2022-11-04 10:35:26 -07:00
Matt Nadareski
bd426b763c Add skeleton for LE work 2022-11-04 10:34:49 -07:00
Matt Nadareski
2e42efa71f Add NE per segment data 2022-11-04 10:25:48 -07:00
Matt Nadareski
58181bd723 Add NE entry table 2022-11-04 10:09:09 -07:00
Matt Nadareski
dcef3115b8 Add NE resource table 2022-11-04 09:56:06 -07:00
Matt Nadareski
4bdc5dc90f Add simple NE tables 2022-11-04 09:40:29 -07:00
TheRogueArchivist
e33d6b3a0a Add support for C-Dilla protections (#164)
* Fuck C-Dilla

* Add initial detection of C-Dilla LMS/CD-Secure.

* Add a few code comments for Macrovision.

* Update README.

* Reorganize C-Dilla NE Checks

* Reorganize C-Dilla NE Checks.
* Add NE skeleton for C-Dilla and other Macrovision protections.
* Add more detections for CD-Secure 1.

* Let Macrovision return multiple protections

* Let Macrovision return multiple protections.
* Add new C-Dilla and SafeCast checks.

* why is C-Dilla so confusing

* Add additional checks for C-Dilla and SafeCast.

* Add skeleton for NE checks for SafeCast.

* Address PR comments
2022-11-04 09:31:20 -07:00
Matt Nadareski
d8ddaccf07 Add OS/2 flags 2022-11-03 23:51:04 -07:00
Matt Nadareski
ca55ea16f0 Fill in missing NE header flags 2022-11-03 23:49:05 -07:00
Matt Nadareski
cb42330e22 Add missing operating systems to enum 2022-11-03 23:42:19 -07:00
Matt Nadareski
4e55cf0baa Add NE segment table 2022-11-03 23:38:51 -07:00
Matt Nadareski
abec45c492 Add skeleton of NE to Models project 2022-11-03 23:32:35 -07:00
Matt Nadareski
610e25b98a Add empty folders for future executable formats 2022-11-03 23:11:26 -07:00
Matt Nadareski
ad11e63338 Add template BurnOutSharp.Models project 2022-11-03 22:57:51 -07:00
TheRogueArchivist
440eb72ae4 The End Is Never The End Is Never The End Is (#163)
* Add notes about C-Dilla.
* Add related notes for other Macrovision/C-Dilla products.
2022-11-02 11:41:34 -07:00
TheRogueArchivist
c4553de302 Add Denuvo Anti-Cheat detection (#162)
* Add support for detecting Denuvo Anti-Cheat

* Update README
2022-10-28 23:10:08 -07:00
TheRogueArchivist
32904b75e4 Begin porting protections made by Macrovision to Macrovision sub-protections (#161)
* Fuck Macrovision

* Port SafeCast/SafeDisc checks to Macrovision sub-protections.

* Move generic checks into the main Macrovision checks.

* Add basic detection for FLEXnet.

* Add C-Dilla notes.

* Add TODO's for porting CactusDataShield.

* Address PR comments
2022-10-27 16:40:16 -07:00
Matt Nadareski
35a4771f89 Split out obvious SafeCast from SafeDisc (nw) 2022-10-26 21:10:33 -07:00
Matt Nadareski
bbb2e9391e Add SafeDisc as Macrovision example (nw) 2022-10-26 21:03:33 -07:00
Matt Nadareski
9dc186f455 Merge branch 'master' of https://github.com/mnadareski/BurnOutSharp 2022-10-26 20:38:39 -07:00
Matt Nadareski
82b9c03a7e Add skeleton for Macrovision sub-protections 2022-10-26 20:38:12 -07:00
TheRogueArchivist
f4f9ba9efa Add "SafeDisc.exe" checks (#160)
* Add checks for "SafeDisc.exe".
* Add note about previously unknown SafeDisc version.
* Add note about Drvmgt file with known sample but no known version.
* Add various SafeDisc/SafeCast resources.
* Add resource for CDS.
2022-10-25 10:31:17 -07:00
TheRogueArchivist
495864e8e0 Add summary for Denuvo (#159)
Add summary and various notes for Denuvo.
2022-10-23 20:46:58 -07:00
Matt Nadareski
ac917df519 Bump version to 2.3.4 2022-10-17 13:18:55 -07:00
Matt Nadareski
3b12fef948 Fix build, fix Nuget 2022-10-17 12:26:57 -07:00
Matt Nadareski
a74b769aef Bump version to 2.3.3 2022-10-17 10:26:21 -07:00
317 changed files with 46027 additions and 9939 deletions

17
.vscode/launch.json vendored
View File

@@ -5,12 +5,25 @@
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"name": ".NET Core Launch (Test)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Test/bin/Debug/netcoreapp3.1/Test.dll",
"program": "${workspaceFolder}/Test/bin/Debug/net6.0/Test.dll",
"args": [],
"cwd": "${workspaceFolder}/Test",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Launch (ExecutableTest)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/ExecutableTest/bin/Debug/net6.0/ExecutableTest.dll",
"args": [],
"cwd": "${workspaceFolder}/Test",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console

6
.vscode/tasks.json vendored
View File

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

View File

@@ -0,0 +1,336 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
namespace BurnOutSharp.Builder
{
/// <summary>
/// ASN.1 type indicators
/// </summary>
[Flags]
public enum ASN1Type : byte
{
#region Modifiers
V_ASN1_UNIVERSAL = 0x00,
V_ASN1_PRIMITIVE_TAG = 0x1F,
V_ASN1_CONSTRUCTED = 0x20,
V_ASN1_APPLICATION = 0x40,
V_ASN1_CONTEXT_SPECIFIC = 0x80,
V_ASN1_PRIVATE = 0xC0,
#endregion
#region Types
V_ASN1_EOC = 0x00,
V_ASN1_BOOLEAN = 0x01,
V_ASN1_INTEGER = 0x02,
V_ASN1_BIT_STRING = 0x03,
V_ASN1_OCTET_STRING = 0x04,
V_ASN1_NULL = 0x05,
V_ASN1_OBJECT = 0x06,
V_ASN1_OBJECT_DESCRIPTOR = 0x07,
V_ASN1_EXTERNAL = 0x08,
V_ASN1_REAL = 0x09,
V_ASN1_ENUMERATED = 0x0A,
V_ASN1_UTF8STRING = 0x0C,
V_ASN1_SEQUENCE = 0x10,
V_ASN1_SET = 0x11,
V_ASN1_NUMERICSTRING = 0x12,
V_ASN1_PRINTABLESTRING = 0x13,
V_ASN1_T61STRING = 0x14,
V_ASN1_TELETEXSTRING = 0x14,
V_ASN1_VIDEOTEXSTRING = 0x15,
V_ASN1_IA5STRING = 0x16,
V_ASN1_UTCTIME = 0x17,
V_ASN1_GENERALIZEDTIME = 0x18,
V_ASN1_GRAPHICSTRING = 0x19,
V_ASN1_ISO64STRING = 0x1A,
V_ASN1_VISIBLESTRING = 0x1A,
V_ASN1_GENERALSTRING = 0x1B,
V_ASN1_UNIVERSALSTRING = 0x1C,
V_ASN1_BMPSTRING = 0x1E,
#endregion
}
/// <summary>
/// ASN.1 Parser
/// </summary>
public class AbstractSyntaxNotationOne
{
/// <summary>
/// Parse a byte array into a DER-encoded ASN.1 structure
/// </summary>
/// <param name="data">Byte array representing the data</param>
/// <param name="pointer">Current pointer into the data</param>
/// <returns></returns>
public static List<ASN1TypeLengthValue> Parse(byte[] data, int pointer)
{
// Create the output list to return
var topLevelValues = new List<ASN1TypeLengthValue>();
// Loop through the data and return all top-level values
while (pointer < data.Length)
{
var topLevelValue = new ASN1TypeLengthValue(data, ref pointer);
topLevelValues.Add(topLevelValue);
}
return topLevelValues;
}
}
/// <summary>
/// ASN.1 type/length/value class that all types are based on
/// </summary>
public class ASN1TypeLengthValue
{
/// <summary>
/// The ASN.1 type
/// </summary>
public ASN1Type Type { get; private set; }
/// <summary>
/// Length of the value
/// </summary>
public ulong Length { get; private set; }
/// <summary>
/// Generic value associated with <see cref="Type"/>
/// </summary>
public object Value { get; private set; }
/// <summary>
/// Read from the source data array at an index
/// </summary>
/// <param name="data">Byte array representing data to read</param>
/// <param name="index">Index within the array to read at</param>
public ASN1TypeLengthValue(byte[] data, ref int index)
{
// Get the type and modifiers
this.Type = (ASN1Type)data[index++];
// If we have an end indicator, we just return
if (this.Type == ASN1Type.V_ASN1_EOC)
return;
// Get the length of the value
this.Length = ReadLength(data, ref index);
// Read the value
if (this.Type.HasFlag(ASN1Type.V_ASN1_CONSTRUCTED))
{
var valueList = new List<ASN1TypeLengthValue>();
int currentIndex = index;
while (index < currentIndex + (int)this.Length)
{
valueList.Add(new ASN1TypeLengthValue(data, ref index));
}
this.Value = valueList.ToArray();
}
else
{
// TODO: Get more granular based on type
this.Value = data.ReadBytes(ref index, (int)this.Length);
}
}
/// <summary>
/// Format the TLV as a string
/// </summary>
/// <param name="paddingLevel">Padding level of the item when formatting</param>
/// <returns>String representing the TLV, if possible</returns>
public string Format(int paddingLevel = 0)
{
// Create the left-padding string
string padding = new string(' ', paddingLevel);
// If we have an invalid item
if (this.Type == 0)
return $"{padding}UNKNOWN TYPE";
// Create the string builder
StringBuilder formatBuilder = new StringBuilder();
// Append the type
formatBuilder.Append($"{padding}Type: {this.Type}");
if (this.Type == ASN1Type.V_ASN1_EOC)
return formatBuilder.ToString();
// Append the length
formatBuilder.Append($", Length: {this.Length}");
if (this.Length == 0)
return formatBuilder.ToString();
// If we have a constructed type
if (this.Type.HasFlag(ASN1Type.V_ASN1_CONSTRUCTED))
{
var valueAsObjectArray = this.Value as ASN1TypeLengthValue[];
if (valueAsObjectArray == null)
{
formatBuilder.Append(", Value: [INVALID DATA TYPE]");
return formatBuilder.ToString();
}
formatBuilder.Append(", Value:\n");
for (int i = 0; i < valueAsObjectArray.Length; i++)
{
var child = valueAsObjectArray[i];
string childString = child.Format(paddingLevel + 1);
formatBuilder.Append($"{childString}\n");
}
return formatBuilder.ToString().TrimEnd('\n');
}
// Get the value as a byte array
byte[] valueAsByteArray = this.Value as byte[];
if (valueAsByteArray == null)
{
formatBuilder.Append(", Value: [INVALID DATA TYPE]");
return formatBuilder.ToString();
}
// If we have a primitive type
switch (this.Type)
{
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-boolean"/>
case ASN1Type.V_ASN1_BOOLEAN:
if (this.Length > 1 || valueAsByteArray.Length > 1)
formatBuilder.Append($" [Expected length of 1]");
bool booleanValue = valueAsByteArray[0] == 0x00 ? false : true;
formatBuilder.Append($", Value: {booleanValue}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-integer"/>
case ASN1Type.V_ASN1_INTEGER:
Array.Reverse(valueAsByteArray);
BigInteger integerValue = new BigInteger(valueAsByteArray);
formatBuilder.Append($", Value: {integerValue}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-bit-string"/>
case ASN1Type.V_ASN1_BIT_STRING:
// TODO: Read into a BitArray and print that out instead?
int unusedBits = valueAsByteArray[0];
formatBuilder.Append($", Value with {unusedBits} unused bits: {BitConverter.ToString(valueAsByteArray.Skip(1).ToArray()).Replace('-', ' ')}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-octet-string"/>
case ASN1Type.V_ASN1_OCTET_STRING:
formatBuilder.Append($", Value: {BitConverter.ToString(valueAsByteArray).Replace('-', ' ')}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-object-identifier"/>
/// <see cref="http://snmpsharpnet.com/index.php/2009/03/02/ber-encoding-and-decoding-oid-values/"/>
case ASN1Type.V_ASN1_OBJECT:
// Derive array of values
ulong[] objectNodes = ObjectIdentifier.ParseDERIntoArray(valueAsByteArray, this.Length);
// Append the dot and modified OID-IRI notations
string dotNotationString = ObjectIdentifier.ParseOIDToDotNotation(objectNodes);
string oidIriString = ObjectIdentifier.ParseOIDToOIDIRINotation(objectNodes);
formatBuilder.Append($", Value: {dotNotationString} ({oidIriString})");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-utf8string"/>
case ASN1Type.V_ASN1_UTF8STRING:
formatBuilder.Append($", Value: {Encoding.UTF8.GetString(valueAsByteArray)}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-printablestring"/>
case ASN1Type.V_ASN1_PRINTABLESTRING:
formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}");
break;
//case ASN1Type.V_ASN1_T61STRING:
case ASN1Type.V_ASN1_TELETEXSTRING:
formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-ia5string"/>
case ASN1Type.V_ASN1_IA5STRING:
formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}");
break;
case ASN1Type.V_ASN1_UTCTIME:
string utctimeString = Encoding.ASCII.GetString(valueAsByteArray);
if (DateTime.TryParse(utctimeString, out DateTime utctimeDateTime))
formatBuilder.Append($", Value: {utctimeDateTime}");
else
formatBuilder.Append($", Value: {utctimeString}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-bmpstring"/>
case ASN1Type.V_ASN1_BMPSTRING:
formatBuilder.Append($", Value: {Encoding.Unicode.GetString(valueAsByteArray)}");
break;
default:
formatBuilder.Append($", Value (Unknown Format): {BitConverter.ToString(this.Value as byte[]).Replace('-', ' ')}");
break;
}
// Return the formatted string
return formatBuilder.ToString();
}
/// <summary>
/// Reads the length field for a type
/// </summary>
/// <param name="data">Byte array representing data to read</param>
/// <param name="index">Index within the array to read at</param>
/// <returns>The length value read from the array</returns>
private static ulong ReadLength(byte[] data, ref int index)
{
// If we have invalid data, throw an exception
if (data == null || index < 0 && index >= data.Length)
throw new ArgumentException();
// Read the first byte, assuming it's the length
byte length = data[index++];
// If the bit 7 is not set, then use the value as it is
if ((length & 0x80) == 0)
return length;
// Otherwise, use the value as the number of remaining bytes to read
int bytesToRead = length & ~0x80;
byte[] bytesRead = data.ReadBytes(ref index, bytesToRead);
// TODO: Write extensions to read big-endian
// Reverse the bytes to be in big-endian order
Array.Reverse(bytesRead);
switch (bytesRead.Length)
{
case 1:
return bytesRead[0];
case 2:
return BitConverter.ToUInt16(bytesRead, 0);
case 3:
Array.Resize(ref bytesRead, 4);
goto case 4;
case 4:
return BitConverter.ToUInt32(bytesRead, 0);
case 5:
case 6:
case 7:
Array.Resize(ref bytesRead, 8);
goto case 8;
case 8:
return BitConverter.ToUInt64(bytesRead, 0);
default:
throw new InvalidOperationException();
}
}
}
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
<Title>BurnOutSharp.Builder</Title>
<AssemblyName>BurnOutSharp.Builder</AssemblyName>
<Authors>Matt Nadareski</Authors>
<Product>BurnOutSharp</Product>
<Copyright>Copyright (c)2022 Matt Nadareski</Copyright>
<RepositoryUrl>https://github.com/mnadareski/BurnOutSharp</RepositoryUrl>
<Version>2.5</Version>
<AssemblyVersion>2.5</AssemblyVersion>
<FileVersion>2.5</FileVersion>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
</PropertyGroup>
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\BurnOutSharp.Models\BurnOutSharp.Models.csproj" />
</ItemGroup>
</Project>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,107 @@
using System.IO;
using BurnOutSharp.Models.LinearExecutable;
namespace BurnOutSharp.Builder
{
// TODO: Make Stream Data rely on Byte Data
public static class LinearExecutable
{
#region Byte Data
/// <summary>
/// Parse a byte array into a Linear Executable
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled executable on success, null on error</returns>
public static Executable ParseExecutable(byte[] data, int offset)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Cache the current offset
int initialOffset = offset;
// Create a new executable to fill
var executable = new Executable();
// Parse the MS-DOS stub
var stub = MSDOS.ParseExecutable(data, offset);
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
return null;
// Set the MS-DOS stub
executable.Stub = stub;
// TODO: Implement LE/LX parsing
return null;
}
/// <summary>
/// Parse a byte array into a Linear Executable information block
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled information block on success, null on error</returns>
private static InformationBlock ParseInformationBlock(byte[] data, int offset)
{
// TODO: Implement LE/LX information block parsing
return null;
}
#endregion
#region Stream Data
/// <summary>
/// Parse a Stream into a Linear Executable
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled executable on success, null on error</returns>
public static Executable ParseExecutable(Stream data)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new executable to fill
var executable = new Executable();
// Parse the MS-DOS stub
var stub = MSDOS.ParseExecutable(data);
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
return null;
// Set the MS-DOS stub
executable.Stub = stub;
// TODO: Implement LE/LX parsing
return null;
}
/// <summary>
/// Parse a Stream into a Linear Executable information block
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled executable header on success, null on error</returns>
private static InformationBlock ParseInformationBlock(Stream data)
{
// TODO: Implement LE/LX information block parsing
return null;
}
#endregion
}
}

View File

@@ -0,0 +1,295 @@
using System.IO;
using BurnOutSharp.Models.MSDOS;
namespace BurnOutSharp.Builder
{
// TODO: Make Stream Data rely on Byte Data
public static class MSDOS
{
#region Byte Data
/// <summary>
/// Parse a byte array into an MS-DOS executable
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled executable on success, null on error</returns>
public static Executable ParseExecutable(byte[] data, int offset)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Cache the current offset
int initialOffset = offset;
// Create a new executable to fill
var executable = new Executable();
#region Executable Header
// Try to parse the executable header
var executableHeader = ParseExecutableHeader(data, offset);
if (executableHeader == null)
return null;
// Set the executable header
executable.Header = executableHeader;
#endregion
#region Relocation Table
// If the offset for the relocation table doesn't exist
int tableAddress = initialOffset + executableHeader.RelocationTableAddr;
if (tableAddress >= data.Length)
return executable;
// Try to parse the relocation table
var relocationTable = ParseRelocationTable(data, tableAddress, executableHeader.RelocationItems);
if (relocationTable == null)
return null;
// Set the relocation table
executable.RelocationTable = relocationTable;
#endregion
// Return the executable
return executable;
}
/// <summary>
/// Parse a byte array into an MS-DOS executable header
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled executable header on success, null on error</returns>
private static ExecutableHeader ParseExecutableHeader(byte[] data, int offset)
{
// TODO: Use marshalling here instead of building
var header = new ExecutableHeader();
#region Standard Fields
header.Magic = new byte[2];
for (int i = 0; i < header.Magic.Length; i++)
{
header.Magic[i] = data.ReadByte(ref offset);
}
if (header.Magic[0] != 'M' || header.Magic[1] != 'Z')
return null;
header.LastPageBytes = data.ReadUInt16(ref offset);
header.Pages = data.ReadUInt16(ref offset);
header.RelocationItems = data.ReadUInt16(ref offset);
header.HeaderParagraphSize = data.ReadUInt16(ref offset);
header.MinimumExtraParagraphs = data.ReadUInt16(ref offset);
header.MaximumExtraParagraphs = data.ReadUInt16(ref offset);
header.InitialSSValue = data.ReadUInt16(ref offset);
header.InitialSPValue = data.ReadUInt16(ref offset);
header.Checksum = data.ReadUInt16(ref offset);
header.InitialIPValue = data.ReadUInt16(ref offset);
header.InitialCSValue = data.ReadUInt16(ref offset);
header.RelocationTableAddr = data.ReadUInt16(ref offset);
header.OverlayNumber = data.ReadUInt16(ref offset);
#endregion
// If we don't have enough data for PE extensions
if (offset >= data.Length || data.Length - offset < 36)
return header;
#region PE Extensions
header.Reserved1 = new ushort[4];
for (int i = 0; i < header.Reserved1.Length; i++)
{
header.Reserved1[i] = data.ReadUInt16(ref offset);
}
header.OEMIdentifier = data.ReadUInt16(ref offset);
header.OEMInformation = data.ReadUInt16(ref offset);
header.Reserved2 = new ushort[10];
for (int i = 0; i < header.Reserved2.Length; i++)
{
header.Reserved2[i] = data.ReadUInt16(ref offset);
}
header.NewExeHeaderAddr = data.ReadUInt32(ref offset);
#endregion
return header;
}
/// <summary>
/// Parse a byte array into a relocation table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="count">Number of relocation table entries to read</param>
/// <returns>Filled relocation table on success, null on error</returns>
private static RelocationEntry[] ParseRelocationTable(byte[] data, int offset, int count)
{
// TODO: Use marshalling here instead of building
var relocationTable = new RelocationEntry[count];
for (int i = 0; i < count; i++)
{
var entry = new RelocationEntry();
entry.Offset = data.ReadUInt16(ref offset);
entry.Segment = data.ReadUInt16(ref offset);
relocationTable[i] = entry;
}
return relocationTable;
}
#endregion
#region Stream Data
/// <summary>
/// Parse a Stream into an MS-DOS executable
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled executable on success, null on error</returns>
public static Executable ParseExecutable(Stream data)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new executable to fill
var executable = new Executable();
#region Executable Header
// Try to parse the executable header
var executableHeader = ParseExecutableHeader(data);
if (executableHeader == null)
return null;
// Set the executable header
executable.Header = executableHeader;
#endregion
#region Relocation Table
// If the offset for the relocation table doesn't exist
int tableAddress = initialOffset + executableHeader.RelocationTableAddr;
if (tableAddress >= data.Length)
return executable;
// Try to parse the relocation table
data.Seek(tableAddress, SeekOrigin.Begin);
var relocationTable = ParseRelocationTable(data, executableHeader.RelocationItems);
if (relocationTable == null)
return null;
// Set the relocation table
executable.RelocationTable = relocationTable;
#endregion
// Return the executable
return executable;
}
/// <summary>
/// Parse a Stream into an MS-DOS executable header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled executable header on success, null on error</returns>
private static ExecutableHeader ParseExecutableHeader(Stream data)
{
// TODO: Use marshalling here instead of building
var header = new ExecutableHeader();
#region Standard Fields
header.Magic = new byte[2];
for (int i = 0; i < header.Magic.Length; i++)
{
header.Magic[i] = data.ReadByteValue();
}
if (header.Magic[0] != 'M' || header.Magic[1] != 'Z')
return null;
header.LastPageBytes = data.ReadUInt16();
header.Pages = data.ReadUInt16();
header.RelocationItems = data.ReadUInt16();
header.HeaderParagraphSize = data.ReadUInt16();
header.MinimumExtraParagraphs = data.ReadUInt16();
header.MaximumExtraParagraphs = data.ReadUInt16();
header.InitialSSValue = data.ReadUInt16();
header.InitialSPValue = data.ReadUInt16();
header.Checksum = data.ReadUInt16();
header.InitialIPValue = data.ReadUInt16();
header.InitialCSValue = data.ReadUInt16();
header.RelocationTableAddr = data.ReadUInt16();
header.OverlayNumber = data.ReadUInt16();
#endregion
// If we don't have enough data for PE extensions
if (data.Position >= data.Length || data.Length - data.Position < 36)
return header;
#region PE Extensions
header.Reserved1 = new ushort[4];
for (int i = 0; i < header.Reserved1.Length; i++)
{
header.Reserved1[i] = data.ReadUInt16();
}
header.OEMIdentifier = data.ReadUInt16();
header.OEMInformation = data.ReadUInt16();
header.Reserved2 = new ushort[10];
for (int i = 0; i < header.Reserved2.Length; i++)
{
header.Reserved2[i] = data.ReadUInt16();
}
header.NewExeHeaderAddr = data.ReadUInt32();
#endregion
return header;
}
/// <summary>
/// Parse a Stream into a relocation table
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="count">Number of relocation table entries to read</param>
/// <returns>Filled relocation table on success, null on error</returns>
private static RelocationEntry[] ParseRelocationTable(Stream data, int count)
{
// TODO: Use marshalling here instead of building
var relocationTable = new RelocationEntry[count];
for (int i = 0; i < count; i++)
{
var entry = new RelocationEntry();
entry.Offset = data.ReadUInt16();
entry.Segment = data.ReadUInt16();
relocationTable[i] = entry;
}
return relocationTable;
}
#endregion
}
}

View File

@@ -0,0 +1,958 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BurnOutSharp.Models.NewExecutable;
namespace BurnOutSharp.Builder
{
// TODO: Make Stream Data rely on Byte Data
public static class NewExecutable
{
#region Byte Data
/// <summary>
/// Parse a byte array into a New Executable
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled executable on success, null on error</returns>
public static Executable ParseExecutable(byte[] data, int offset)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Cache the current offset
int initialOffset = offset;
// Create a new executable to fill
var executable = new Executable();
#region MS-DOS Stub
// Parse the MS-DOS stub
var stub = MSDOS.ParseExecutable(data, offset);
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
return null;
// Set the MS-DOS stub
executable.Stub = stub;
#endregion
#region Executable Header
// Try to parse the executable header
offset = (int)(initialOffset + stub.Header.NewExeHeaderAddr);
var executableHeader = ParseExecutableHeader(data, offset);
if (executableHeader == null)
return null;
// Set the executable header
executable.Header = executableHeader;
#endregion
#region Segment Table
// If the offset for the segment table doesn't exist
int tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.SegmentTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the segment table
var segmentTable = ParseSegmentTable(data, tableAddress, executableHeader.FileSegmentCount);
if (segmentTable == null)
return null;
// Set the segment table
executable.SegmentTable = segmentTable;
#endregion
#region Resource Table
// If the offset for the segment table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.SegmentTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the resource table
var resourceTable = ParseResourceTable(data, tableAddress, executableHeader.ResourceEntriesCount);
if (resourceTable == null)
return null;
// Set the resource table
executable.ResourceTable = resourceTable;
#endregion
#region Resident-Name Table
// If the offset for the resident-name table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.ResidentNameTableOffset;
int endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.ModuleReferenceTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the resident-name table
var residentNameTable = ParseResidentNameTable(data, tableAddress, endOffset);
if (residentNameTable == null)
return null;
// Set the resident-name table
executable.ResidentNameTable = residentNameTable;
#endregion
#region Module-Reference Table
// If the offset for the module-reference table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.ModuleReferenceTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the module-reference table
var moduleReferenceTable = ParseModuleReferenceTable(data, tableAddress, executableHeader.ModuleReferenceTableSize);
if (moduleReferenceTable == null)
return null;
// Set the module-reference table
executable.ModuleReferenceTable = moduleReferenceTable;
#endregion
#region Imported-Name Table
// If the offset for the imported-name table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.ImportedNamesTableOffset;
endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.EntryTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the imported-name table
var importedNameTable = ParseImportedNameTable(data, tableAddress, endOffset);
if (importedNameTable == null)
return null;
// Set the imported-name table
executable.ImportedNameTable = importedNameTable;
#endregion
#region Entry Table
// If the offset for the entry table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.EntryTableOffset;
endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.EntryTableOffset
+ executableHeader.EntryTableSize;
if (tableAddress >= data.Length)
return executable;
// Try to parse the entry table
var entryTable = ParseEntryTable(data, tableAddress, endOffset);
if (entryTable == null)
return null;
// Set the entry table
executable.EntryTable = entryTable;
#endregion
#region Nonresident-Name Table
// If the offset for the nonresident-name table doesn't exist
tableAddress = initialOffset
+ (int)executableHeader.NonResidentNamesTableOffset;
endOffset = initialOffset
+ (int)executableHeader.NonResidentNamesTableOffset
+ executableHeader.NonResidentNameTableSize;
if (tableAddress >= data.Length)
return executable;
// Try to parse the nonresident-name table
var nonResidentNameTable = ParseNonResidentNameTable(data, tableAddress, endOffset);
if (nonResidentNameTable == null)
return null;
// Set the nonresident-name table
executable.NonResidentNameTable = nonResidentNameTable;
#endregion
return executable;
}
/// <summary>
/// Parse a byte array into a New Executable header
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled executable header on success, null on error</returns>
private static ExecutableHeader ParseExecutableHeader(byte[] data, int offset)
{
// TODO: Use marshalling here instead of building
var header = new ExecutableHeader();
header.Magic = new byte[2];
for (int i = 0; i < header.Magic.Length; i++)
{
header.Magic[i] = data.ReadByte(ref offset);
}
if (header.Magic[0] != 'N' || header.Magic[1] != 'E')
return null;
header.LinkerVersion = data.ReadByte(ref offset);
header.LinkerRevision = data.ReadByte(ref offset);
header.EntryTableOffset = data.ReadUInt16(ref offset);
header.EntryTableSize = data.ReadUInt16(ref offset);
header.CrcChecksum = data.ReadUInt32(ref offset);
header.FlagWord = (HeaderFlag)data.ReadUInt16(ref offset);
header.AutomaticDataSegmentNumber = data.ReadUInt16(ref offset);
header.InitialHeapAlloc = data.ReadUInt16(ref offset);
header.InitialStackAlloc = data.ReadUInt16(ref offset);
header.InitialCSIPSetting = data.ReadUInt32(ref offset);
header.InitialSSSPSetting = data.ReadUInt32(ref offset);
header.FileSegmentCount = data.ReadUInt16(ref offset);
header.ModuleReferenceTableSize = data.ReadUInt16(ref offset);
header.NonResidentNameTableSize = data.ReadUInt16(ref offset);
header.SegmentTableOffset = data.ReadUInt16(ref offset);
header.ResourceTableOffset = data.ReadUInt16(ref offset);
header.ResidentNameTableOffset = data.ReadUInt16(ref offset);
header.ModuleReferenceTableOffset = data.ReadUInt16(ref offset);
header.ImportedNamesTableOffset = data.ReadUInt16(ref offset);
header.NonResidentNamesTableOffset = data.ReadUInt32(ref offset);
header.MovableEntriesCount = data.ReadUInt16(ref offset);
header.SegmentAlignmentShiftCount = data.ReadUInt16(ref offset);
header.ResourceEntriesCount = data.ReadUInt16(ref offset);
header.TargetOperatingSystem = (OperatingSystem)data.ReadByte(ref offset);
header.AdditionalFlags = (OS2Flag)data.ReadByte(ref offset);
header.ReturnThunkOffset = data.ReadUInt16(ref offset);
header.SegmentReferenceThunkOffset = data.ReadUInt16(ref offset);
header.MinCodeSwapAreaSize = data.ReadUInt16(ref offset);
header.WindowsSDKRevision = data.ReadByte(ref offset);
header.WindowsSDKVersion = data.ReadByte(ref offset);
return header;
}
/// <summary>
/// Parse a byte array into a segment table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="count">Number of segment table entries to read</param>
/// <returns>Filled segment table on success, null on error</returns>
private static SegmentTableEntry[] ParseSegmentTable(byte[] data, int offset, int count)
{
// TODO: Use marshalling here instead of building
var segmentTable = new SegmentTableEntry[count];
for (int i = 0; i < count; i++)
{
var entry = new SegmentTableEntry();
entry.Offset = data.ReadUInt16(ref offset);
entry.Length = data.ReadUInt16(ref offset);
entry.FlagWord = (SegmentTableEntryFlag)data.ReadUInt16(ref offset);
entry.MinimumAllocationSize = data.ReadUInt16(ref offset);
segmentTable[i] = entry;
}
return segmentTable;
}
/// <summary>
/// Parse a byte array into a resource table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="count">Number of resource table entries to read</param>
/// <returns>Filled resource table on success, null on error</returns>
private static ResourceTable ParseResourceTable(byte[] data, int offset, int count)
{
int initialOffset = offset;
// TODO: Use marshalling here instead of building
var resourceTable = new ResourceTable();
resourceTable.AlignmentShiftCount = data.ReadUInt16(ref offset);
resourceTable.ResourceTypes = new ResourceTypeInformationEntry[count];
for (int i = 0; i < resourceTable.ResourceTypes.Length; i++)
{
var entry = new ResourceTypeInformationEntry();
entry.TypeID = data.ReadUInt16(ref offset);
entry.ResourceCount = data.ReadUInt16(ref offset);
entry.Reserved = data.ReadUInt32(ref offset);
entry.Resources = new ResourceTypeResourceEntry[entry.ResourceCount];
for (int j = 0; j < entry.ResourceCount; j++)
{
// TODO: Should we read and store the resource data?
var resource = new ResourceTypeResourceEntry();
resource.Offset = data.ReadUInt16(ref offset);
resource.Length = data.ReadUInt16(ref offset);
resource.FlagWord = (ResourceTypeResourceFlag)data.ReadUInt16(ref offset);
resource.ResourceID = data.ReadUInt16(ref offset);
resource.Reserved = data.ReadUInt32(ref offset);
entry.Resources[j] = resource;
}
resourceTable.ResourceTypes[i] = entry;
}
// Get the full list of unique string offsets
var stringOffsets = resourceTable.ResourceTypes
.Where(rt => rt.IsIntegerType() == false)
.Select(rt => rt.TypeID)
.Union(resourceTable.ResourceTypes
.SelectMany(rt => rt.Resources)
.Where(r => r.IsIntegerType() == false)
.Select(r => r.ResourceID))
.Distinct()
.OrderBy(o => o)
.ToList();
// Populate the type and name string dictionary
resourceTable.TypeAndNameStrings = new Dictionary<ushort, ResourceTypeAndNameString>();
for (int i = 0; i < stringOffsets.Count; i++)
{
int stringOffset = stringOffsets[i] + initialOffset;
var str = new ResourceTypeAndNameString();
str.Length = data.ReadByte(ref stringOffset);
str.Text = data.ReadBytes(ref stringOffset, str.Length);
resourceTable.TypeAndNameStrings[stringOffsets[i]] = str;
}
return resourceTable;
}
/// <summary>
/// Parse a byte array into a resident-name table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="endOffset">First address not part of the resident-name table</param>
/// <returns>Filled resident-name table on success, null on error</returns>
private static ResidentNameTableEntry[] ParseResidentNameTable(byte[] data, int offset, int endOffset)
{
// TODO: Use marshalling here instead of building
var residentNameTable = new List<ResidentNameTableEntry>();
while (offset < endOffset)
{
var entry = new ResidentNameTableEntry();
entry.Length = data.ReadByte(ref offset);
entry.NameString = data.ReadBytes(ref offset, entry.Length);
entry.OrdinalNumber = data.ReadUInt16(ref offset);
residentNameTable.Add(entry);
}
return residentNameTable.ToArray();
}
/// <summary>
/// Parse a byte array into a module-reference table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="count">Number of module-reference table entries to read</param>
/// <returns>Filled module-reference table on success, null on error</returns>
private static ModuleReferenceTableEntry[] ParseModuleReferenceTable(byte[] data, int offset, int count)
{
// TODO: Use marshalling here instead of building
var moduleReferenceTable = new ModuleReferenceTableEntry[count];
for (int i = 0; i < count; i++)
{
var entry = new ModuleReferenceTableEntry();
entry.Offset = data.ReadUInt16(ref offset);
moduleReferenceTable[i] = entry;
}
return moduleReferenceTable;
}
/// <summary>
/// Parse a byte array into an imported-name table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="endOffset">First address not part of the imported-name table</param>
/// <returns>Filled imported-name table on success, null on error</returns>
private static Dictionary<ushort, ImportedNameTableEntry> ParseImportedNameTable(byte[] data, int offset, int endOffset)
{
// TODO: Use marshalling here instead of building
var importedNameTable = new Dictionary<ushort, ImportedNameTableEntry>();
while (offset < endOffset)
{
ushort currentOffset = (ushort)offset;
var entry = new ImportedNameTableEntry();
entry.Length = data.ReadByte(ref offset);
entry.NameString = data.ReadBytes(ref offset, entry.Length);
importedNameTable[currentOffset] = entry;
}
return importedNameTable;
}
/// <summary>
/// Parse a byte array into an entry table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="endOffset">First address not part of the entry table</param>
/// <returns>Filled entry table on success, null on error</returns>
private static EntryTableBundle[] ParseEntryTable(byte[] data, int offset, int endOffset)
{
// TODO: Use marshalling here instead of building
var entryTable = new List<EntryTableBundle>();
while (offset < endOffset)
{
var entry = new EntryTableBundle();
entry.EntryCount = data.ReadByte(ref offset);
entry.SegmentIndicator = data.ReadByte(ref offset);
switch (entry.GetEntryType())
{
case SegmentEntryType.Unused:
break;
case SegmentEntryType.FixedSegment:
entry.FixedFlagWord = (FixedSegmentEntryFlag)data.ReadByte(ref offset);
entry.FixedOffset = data.ReadUInt16(ref offset);
break;
case SegmentEntryType.MoveableSegment:
entry.MoveableFlagWord = (MoveableSegmentEntryFlag)data.ReadByte(ref offset);
entry.MoveableReserved = data.ReadUInt16(ref offset);
entry.MoveableSegmentNumber = data.ReadByte(ref offset);
entry.MoveableOffset = data.ReadUInt16(ref offset);
break;
}
entryTable.Add(entry);
}
return entryTable.ToArray();
}
/// <summary>
/// Parse a byte array into a nonresident-name table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="endOffset">First address not part of the nonresident-name table</param>
/// <returns>Filled nonresident-name table on success, null on error</returns>
private static NonResidentNameTableEntry[] ParseNonResidentNameTable(byte[] data, int offset, int endOffset)
{
// TODO: Use marshalling here instead of building
var residentNameTable = new List<NonResidentNameTableEntry>();
while (offset < endOffset)
{
var entry = new NonResidentNameTableEntry();
entry.Length = data.ReadByte(ref offset);
entry.NameString = data.ReadBytes(ref offset, entry.Length);
entry.OrdinalNumber = data.ReadUInt16(ref offset);
residentNameTable.Add(entry);
}
return residentNameTable.ToArray();
}
#endregion
#region Stream Data
/// <summary>
/// Parse a Stream into a New Executable
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled executable on success, null on error</returns>
public static Executable ParseExecutable(Stream data)
{
// If the data is invalid
if (data == null)
return null;
// If the offset is out of bounds
if (data.Position < 0 || data.Position >= data.Length)
return null;
// Cache the current offset
int initialOffset = (int)data.Position;
// Create a new executable to fill
var executable = new Executable();
#region MS-DOS Stub
// Parse the MS-DOS stub
var stub = MSDOS.ParseExecutable(data);
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
return null;
// Set the MS-DOS stub
executable.Stub = stub;
#endregion
#region Executable Header
// Try to parse the executable header
data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin);
var executableHeader = ParseExecutableHeader(data);
if (executableHeader == null)
return null;
// Set the executable header
executable.Header = executableHeader;
#endregion
#region Segment Table
// If the offset for the segment table doesn't exist
int tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.SegmentTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the segment table
data.Seek(tableAddress, SeekOrigin.Begin);
var segmentTable = ParseSegmentTable(data, executableHeader.FileSegmentCount);
if (segmentTable == null)
return null;
// Set the segment table
executable.SegmentTable = segmentTable;
#endregion
#region Resource Table
// If the offset for the segment table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.SegmentTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the resource table
data.Seek(tableAddress, SeekOrigin.Begin);
var resourceTable = ParseResourceTable(data, executableHeader.ResourceEntriesCount);
if (resourceTable == null)
return null;
// Set the resource table
executable.ResourceTable = resourceTable;
#endregion
#region Resident-Name Table
// If the offset for the resident-name table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.ResidentNameTableOffset;
int endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.ModuleReferenceTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the resident-name table
data.Seek(tableAddress, SeekOrigin.Begin);
var residentNameTable = ParseResidentNameTable(data, endOffset);
if (residentNameTable == null)
return null;
// Set the resident-name table
executable.ResidentNameTable = residentNameTable;
#endregion
#region Module-Reference Table
// If the offset for the module-reference table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.ModuleReferenceTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the module-reference table
data.Seek(tableAddress, SeekOrigin.Begin);
var moduleReferenceTable = ParseModuleReferenceTable(data, executableHeader.ModuleReferenceTableSize);
if (moduleReferenceTable == null)
return null;
// Set the module-reference table
executable.ModuleReferenceTable = moduleReferenceTable;
#endregion
#region Imported-Name Table
// If the offset for the imported-name table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.ImportedNamesTableOffset;
endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.EntryTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the imported-name table
data.Seek(tableAddress, SeekOrigin.Begin);
var importedNameTable = ParseImportedNameTable(data, endOffset);
if (importedNameTable == null)
return null;
// Set the imported-name table
executable.ImportedNameTable = importedNameTable;
#endregion
#region Entry Table
// If the offset for the imported-name table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.EntryTableOffset;
endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.EntryTableOffset
+ executableHeader.EntryTableSize;
if (tableAddress >= data.Length)
return executable;
// Try to parse the imported-name table
data.Seek(tableAddress, SeekOrigin.Begin);
var entryTable = ParseEntryTable(data, endOffset);
if (entryTable == null)
return null;
// Set the entry table
executable.EntryTable = entryTable;
#endregion
#region Nonresident-Name Table
// If the offset for the nonresident-name table doesn't exist
tableAddress = initialOffset
+ (int)executableHeader.NonResidentNamesTableOffset;
endOffset = initialOffset
+ (int)executableHeader.NonResidentNamesTableOffset
+ executableHeader.NonResidentNameTableSize;
if (tableAddress >= data.Length)
return executable;
// Try to parse the nonresident-name table
data.Seek(tableAddress, SeekOrigin.Begin);
var nonResidentNameTable = ParseNonResidentNameTable(data, endOffset);
if (nonResidentNameTable == null)
return null;
// Set the nonresident-name table
executable.NonResidentNameTable = nonResidentNameTable;
#endregion
return executable;
}
/// <summary>
/// Parse a Stream into a New Executable header
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled executable header on success, null on error</returns>
private static ExecutableHeader ParseExecutableHeader(Stream data)
{
// TODO: Use marshalling here instead of building
var header = new ExecutableHeader();
header.Magic = new byte[2];
for (int i = 0; i < header.Magic.Length; i++)
{
header.Magic[i] = data.ReadByteValue();
}
if (header.Magic[0] != 'N' || header.Magic[1] != 'E')
return null;
header.LinkerVersion = data.ReadByteValue();
header.LinkerRevision = data.ReadByteValue();
header.EntryTableOffset = data.ReadUInt16();
header.EntryTableSize = data.ReadUInt16();
header.CrcChecksum = data.ReadUInt32();
header.FlagWord = (HeaderFlag)data.ReadUInt16();
header.AutomaticDataSegmentNumber = data.ReadUInt16();
header.InitialHeapAlloc = data.ReadUInt16();
header.InitialStackAlloc = data.ReadUInt16();
header.InitialCSIPSetting = data.ReadUInt32();
header.InitialSSSPSetting = data.ReadUInt32();
header.FileSegmentCount = data.ReadUInt16();
header.ModuleReferenceTableSize = data.ReadUInt16();
header.NonResidentNameTableSize = data.ReadUInt16();
header.SegmentTableOffset = data.ReadUInt16();
header.ResourceTableOffset = data.ReadUInt16();
header.ResidentNameTableOffset = data.ReadUInt16();
header.ModuleReferenceTableOffset = data.ReadUInt16();
header.ImportedNamesTableOffset = data.ReadUInt16();
header.NonResidentNamesTableOffset = data.ReadUInt32();
header.MovableEntriesCount = data.ReadUInt16();
header.SegmentAlignmentShiftCount = data.ReadUInt16();
header.ResourceEntriesCount = data.ReadUInt16();
header.TargetOperatingSystem = (OperatingSystem)data.ReadByteValue();
header.AdditionalFlags = (OS2Flag)data.ReadByteValue();
header.ReturnThunkOffset = data.ReadUInt16();
header.SegmentReferenceThunkOffset = data.ReadUInt16();
header.MinCodeSwapAreaSize = data.ReadUInt16();
header.WindowsSDKRevision = data.ReadByteValue();
header.WindowsSDKVersion = data.ReadByteValue();
return header;
}
/// <summary>
/// Parse a Stream into a segment table
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="count">Number of segment table entries to read</param>
/// <returns>Filled segment table on success, null on error</returns>
private static SegmentTableEntry[] ParseSegmentTable(Stream data, int count)
{
// TODO: Use marshalling here instead of building
var segmentTable = new SegmentTableEntry[count];
for (int i = 0; i < count; i++)
{
var entry = new SegmentTableEntry();
entry.Offset = data.ReadUInt16();
entry.Length = data.ReadUInt16();
entry.FlagWord = (SegmentTableEntryFlag)data.ReadUInt16();
entry.MinimumAllocationSize = data.ReadUInt16();
segmentTable[i] = entry;
}
return segmentTable;
}
/// <summary>
/// Parse a Stream into a resource table
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="count">Number of resource table entries to read</param>
/// <returns>Filled resource table on success, null on error</returns>
private static ResourceTable ParseResourceTable(Stream data, int count)
{
long initialOffset = data.Position;
// TODO: Use marshalling here instead of building
var resourceTable = new ResourceTable();
resourceTable.AlignmentShiftCount = data.ReadUInt16();
resourceTable.ResourceTypes = new ResourceTypeInformationEntry[count];
for (int i = 0; i < resourceTable.ResourceTypes.Length; i++)
{
var entry = new ResourceTypeInformationEntry();
entry.TypeID = data.ReadUInt16();
entry.ResourceCount = data.ReadUInt16();
entry.Reserved = data.ReadUInt32();
entry.Resources = new ResourceTypeResourceEntry[entry.ResourceCount];
for (int j = 0; j < entry.ResourceCount; j++)
{
// TODO: Should we read and store the resource data?
var resource = new ResourceTypeResourceEntry();
resource.Offset = data.ReadUInt16();
resource.Length = data.ReadUInt16();
resource.FlagWord = (ResourceTypeResourceFlag)data.ReadUInt16();
resource.ResourceID = data.ReadUInt16();
resource.Reserved = data.ReadUInt32();
entry.Resources[j] = resource;
}
resourceTable.ResourceTypes[i] = entry;
}
// Get the full list of unique string offsets
var stringOffsets = resourceTable.ResourceTypes
.Where(rt => rt.IsIntegerType() == false)
.Select(rt => rt.TypeID)
.Union(resourceTable.ResourceTypes
.SelectMany(rt => rt.Resources)
.Where(r => r.IsIntegerType() == false)
.Select(r => r.ResourceID))
.Distinct()
.OrderBy(o => o)
.ToList();
// Populate the type and name string dictionary
resourceTable.TypeAndNameStrings = new Dictionary<ushort, ResourceTypeAndNameString>();
for (int i = 0; i < stringOffsets.Count; i++)
{
int stringOffset = (int)(stringOffsets[i] + initialOffset);
data.Seek(stringOffset, SeekOrigin.Begin);
var str = new ResourceTypeAndNameString();
str.Length = data.ReadByteValue();
str.Text = data.ReadBytes(str.Length);
resourceTable.TypeAndNameStrings[stringOffsets[i]] = str;
}
return resourceTable;
}
/// <summary>
/// Parse a Stream into a resident-name table
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="endOffset">First address not part of the resident-name table</param>
/// <returns>Filled resident-name table on success, null on error</returns>
private static ResidentNameTableEntry[] ParseResidentNameTable(Stream data, int endOffset)
{
// TODO: Use marshalling here instead of building
var residentNameTable = new List<ResidentNameTableEntry>();
while (data.Position < endOffset)
{
var entry = new ResidentNameTableEntry();
entry.Length = data.ReadByteValue();
entry.NameString = data.ReadBytes(entry.Length);
entry.OrdinalNumber = data.ReadUInt16();
residentNameTable.Add(entry);
}
return residentNameTable.ToArray();
}
/// <summary>
/// Parse a Stream into a module-reference table
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="count">Number of module-reference table entries to read</param>
/// <returns>Filled module-reference table on success, null on error</returns>
private static ModuleReferenceTableEntry[] ParseModuleReferenceTable(Stream data, int count)
{
// TODO: Use marshalling here instead of building
var moduleReferenceTable = new ModuleReferenceTableEntry[count];
for (int i = 0; i < count; i++)
{
var entry = new ModuleReferenceTableEntry();
entry.Offset = data.ReadUInt16();
moduleReferenceTable[i] = entry;
}
return moduleReferenceTable;
}
/// <summary>
/// Parse a Stream into an imported-name table
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="endOffset">First address not part of the imported-name table</param>
/// <returns>Filled imported-name table on success, null on error</returns>
private static Dictionary<ushort, ImportedNameTableEntry> ParseImportedNameTable(Stream data, int endOffset)
{
// TODO: Use marshalling here instead of building
var importedNameTable = new Dictionary<ushort, ImportedNameTableEntry>();
while (data.Position < endOffset)
{
ushort currentOffset = (ushort)data.Position;
var entry = new ImportedNameTableEntry();
entry.Length = data.ReadByteValue();
entry.NameString = data.ReadBytes(entry.Length);
importedNameTable[currentOffset] = entry;
}
return importedNameTable;
}
/// <summary>
/// Parse a Stream into an entry table
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="endOffset">First address not part of the entry table</param>
/// <returns>Filled entry table on success, null on error</returns>
private static EntryTableBundle[] ParseEntryTable(Stream data, int endOffset)
{
// TODO: Use marshalling here instead of building
var entryTable = new List<EntryTableBundle>();
while (data.Position < endOffset)
{
var entry = new EntryTableBundle();
entry.EntryCount = data.ReadByteValue();
entry.SegmentIndicator = data.ReadByteValue();
switch (entry.GetEntryType())
{
case SegmentEntryType.Unused:
break;
case SegmentEntryType.FixedSegment:
entry.FixedFlagWord = (FixedSegmentEntryFlag)data.ReadByteValue();
entry.FixedOffset = data.ReadUInt16();
break;
case SegmentEntryType.MoveableSegment:
entry.MoveableFlagWord = (MoveableSegmentEntryFlag)data.ReadByteValue();
entry.MoveableReserved = data.ReadUInt16();
entry.MoveableSegmentNumber = data.ReadByteValue();
entry.MoveableOffset = data.ReadUInt16();
break;
}
entryTable.Add(entry);
}
return entryTable.ToArray();
}
/// <summary>
/// Parse a Stream into a nonresident-name table
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="endOffset">First address not part of the nonresident-name table</param>
/// <returns>Filled nonresident-name table on success, null on error</returns>
private static NonResidentNameTableEntry[] ParseNonResidentNameTable(Stream data, int endOffset)
{
// TODO: Use marshalling here instead of building
var residentNameTable = new List<NonResidentNameTableEntry>();
while (data.Position < endOffset)
{
var entry = new NonResidentNameTableEntry();
entry.Length = data.ReadByteValue();
entry.NameString = data.ReadBytes(entry.Length);
entry.OrdinalNumber = data.ReadUInt16();
residentNameTable.Add(entry);
}
return residentNameTable.ToArray();
}
#endregion
}
}

View File

@@ -0,0 +1,25 @@
namespace BurnOutSharp.Builder
{
#pragma warning disable IDE0011
/// <summary>
/// Methods related to Object Identifiers (OID) and ASN.1 notation
/// </summary>
public static partial class ObjectIdentifier
{
/// <summary>
/// Parse an OID in separated-value notation into ASN.1 notation
/// </summary>
/// <param name="values">List of values to check against</param>
/// <param name="index">Current index into the list</param>
/// <returns>ASN.1 formatted string, if possible</returns>
/// <remarks>
public static string ParseOIDToASN1Notation(ulong[] values, ref int index)
{
// TODO: Once the modified OID-IRI formatting is done, make an ASN.1 notation version
return null;
}
}
#pragma warning restore IDE0011
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,870 @@
using System.Linq;
using System.Text;
namespace BurnOutSharp.Builder
{
#pragma warning disable IDE0011
/// <summary>
/// Methods related to Object Identifiers (OID) and OID-IRI formatting
/// </summary>
public static partial class ObjectIdentifier
{
/// <summary>
/// Parse an OID in separated-value notation into OID-IRI notation
/// </summary>
/// <param name="values">List of values to check against</param>
/// <param name="index">Current index into the list</param>
/// <returns>OID-IRI formatted string, if possible</returns>
/// <see href="http://www.oid-info.com/index.htm"/>
public static string ParseOIDToOIDIRINotation(ulong[] values)
{
// If we have an invalid set of values, we can't do anything
if (values == null || values.Length == 0)
return null;
// Set the initial index
int index = 0;
// Get a string builder for the path
var nameBuilder = new StringBuilder();
// Try to parse the standard value
string standard = ParseOIDToOIDIRINotation(values, ref index);
if (standard == null)
return null;
// Add the standard value to the output
nameBuilder.Append(standard);
// If we have no more items
if (index == values.Length)
return nameBuilder.ToString();
// Add trailing items as just values
nameBuilder.Append("/");
nameBuilder.Append(string.Join("/", values.Skip(index)));
// Create and return the string
return nameBuilder.ToString();
}
/// <summary>
/// Parse an OID in separated-value notation into OID-IRI notation
/// </summary>
/// <param name="values">List of values to check against</param>
/// <param name="index">Current index into the list</param>
/// <returns>OID-IRI formatted string, if possible</returns>
/// <see href="http://www.oid-info.com/index.htm"/>
private static string ParseOIDToOIDIRINotation(ulong[] values, ref int index)
{
// If we have an invalid set of values, we can't do anything
if (values == null || values.Length == 0)
return null;
// If we have an invalid index, we can't do anything
if (index < 0 || index >= values.Length)
return null;
#region Start
switch (values[index++])
{
case 0: goto oid_0;
case 1: goto oid_1;
case 2: goto oid_2;
default: return $"/{values[index - 1]}";
}
#endregion
// itu-t, ccitt, itu-r
#region 0.*
oid_0:
if (index == values.Length) return "/ITU-T";
switch (values[index++])
{
case 0: goto oid_0_0;
case 2: return "/ITU-T/Administration";
case 3: return "/ITU-T/Network-Operator";
case 4: return "/ITU-T/Identified-Organization";
case 5: return "/ITU-R/R-Recommendation";
case 9: return "/ITU-T/Data";
default: return $"/ITU-T/{values[index - 1]}";
};
// recommendation
#region 0.0.*
oid_0_0:
if (index == values.Length) return "/ITU-T/Recommendation";
switch (values[index++])
{
case 1: return "/ITU-T/Recommendation/A";
case 2: return "/ITU-T/Recommendation/B";
case 3: return "/ITU-T/Recommendation/C";
case 4: return "/ITU-T/Recommendation/D";
case 5: return "/ITU-T/Recommendation/E";
case 6: return "/ITU-T/Recommendation/F";
case 7: return "/ITU-T/Recommendation/G";
case 8: return "/ITU-T/Recommendation/H";
case 9: return "/ITU-T/Recommendation/I";
case 10: return "/ITU-T/Recommendation/J";
case 11: return "/ITU-T/Recommendation/K";
case 12: return "/ITU-T/Recommendation/L";
case 13: return "/ITU-T/Recommendation/M";
case 14: return "/ITU-T/Recommendation/N";
case 15: return "/ITU-T/Recommendation/O";
case 16: return "/ITU-T/Recommendation/P";
case 17: return "/ITU-T/Recommendation/Q";
case 18: return "/ITU-T/Recommendation/R";
case 19: return "/ITU-T/Recommendation/S";
case 20: return "/ITU-T/Recommendation/T";
case 21: return "/ITU-T/Recommendation/U";
case 22: return "/ITU-T/Recommendation/V";
case 24: return "/ITU-T/Recommendation/X";
case 25: return "/ITU-T/Recommendation/Y";
case 26: return "/ITU-T/Recommendation/Z";
default: return $"/ITU-T/Recommendation/{values[index - 1]}";
}
#endregion
#endregion
// iso
#region 1.*
oid_1:
if (index == values.Length) return "/ISO";
switch (values[index++])
{
case 0: return "/ISO/Standard";
case 1: return "/ISO/Registration-Authority";
case 2: goto oid_1_2;
case 3: return "/ISO/Identified-Organization";
default: return $"/ISO/{values[index - 1]}";
}
// member-body
#region 1.2.*
oid_1_2:
if (index == values.Length) return "/ISO/Member-Body";
switch (values[index++])
{
case 36: return "/ISO/Member-Body/AU";
case 40: return "/ISO/Member-Body/AT";
case 56: return "/ISO/Member-Body/BE";
case 124: return "/ISO/Member-Body/CA";
case 156: return "/ISO/Member-Body/CN";
case 203: return "/ISO/Member-Body/CZ";
case 208: return "/ISO/Member-Body/DK";
case 246: return "/ISO/Member-Body/FI";
case 250: return "/ISO/Member-Body/FR";
case 276: return "/ISO/Member-Body/DE";
case 300: return "/ISO/Member-Body/GR";
case 344: return "/ISO/Member-Body/HK";
case 372: return "/ISO/Member-Body/IE";
case 392: return "/ISO/Member-Body/JP";
case 398: return "/ISO/Member-Body/KZ";
case 410: return "/ISO/Member-Body/KR";
case 498: return "/ISO/Member-Body/MD";
case 528: return "/ISO/Member-Body/NL";
case 566: return "/ISO/Member-Body/NG";
case 578: return "/ISO/Member-Body/NO";
case 616: return "/ISO/Member-Body/PL";
case 643: return "/ISO/Member-Body/RU";
case 702: return "/ISO/Member-Body/SG";
case 752: return "/ISO/Member-Body/SE";
case 804: return "/ISO/Member-Body/UA";
case 826: return "/ISO/Member-Body/GB";
case 840: return "/ISO/Member-Body/US";
default: return $"/ISO/Member-Body/{values[index - 1]}";
}
#endregion
#endregion
// joint-iso-itu-t, joint-iso-ccitt
#region 2.*
oid_2:
if (index == values.Length) return "/Joint-ISO-ITU-T";
switch (values[index++])
{
case 1: return "/ASN.1";
case 16: goto oid_2_16;
case 17: return "/Joint-ISO-ITU-T/Registration-Procedures";
case 23: return "/Joint-ISO-ITU-T/International-Organizations";
case 25: goto oid_2_25;
case 27: return "/Tag-Based";
case 28: return "/Joint-ISO-ITU-T/ITS";
case 41: return "/BIP";
case 42: goto oid_2_42;
case 48: goto oid_2_48;
case 49: goto oid_2_49;
case 50: return "/OIDResolutionSystem";
case 51: return "/GS1";
case 52: return "/Joint-ISO-ITU-T/UAV";
case 999: return "/Joint-ISO-ITU-T/Example";
default: return $"/Joint-ISO-ITU-T/{values[index - 1]}";
}
// country
#region 2.16.*
oid_2_16:
if (index == values.Length) return "/Country";
switch (values[index++])
{
case 4: return "/Country/AF";
case 8: return "/Country/AL";
case 12: return "/Country/DZ";
case 20: return "/Country/AD";
case 24: return "/Country/AO";
case 28: return "/Country/AG";
case 31: return "/Country/AZ";
case 32: return "/Country/AR";
case 36: return "/Country/AU";
case 40: return "/Country/AT";
case 44: return "/Country/BS";
case 48: return "/Country/BH";
case 50: return "/Country/BD";
case 51: return "/Country/AM";
case 52: return "/Country/BB";
case 56: return "/Country/BE";
case 60: return "/Country/BM";
case 64: return "/Country/BT";
case 68: return "/Country/BO";
case 70: return "/Country/BA";
case 72: return "/Country/BW";
case 76: return "/Country/BR";
case 84: return "/Country/BZ";
case 90: return "/Country/SB";
case 96: return "/Country/BN";
case 100: return "/Country/BG";
case 104: return "/Country/MM";
case 108: return "/Country/BI";
case 112: return "/Country/BY";
case 116: return "/Country/KH";
case 120: return "/Country/CM";
case 124: return "/Country/CA";
case 132: return "/Country/CV";
case 140: return "/Country/CF";
case 144: return "/Country/LK";
case 148: return "/Country/TD";
case 152: return "/Country/CL";
case 156: return "/Country/CN";
case 158: return "/Country/TW";
case 170: return "/Country/CO";
case 174: return "/Country/KM";
case 178: return "/Country/CG";
case 180: return "/Country/CD";
case 188: return "/Country/CR";
case 191: return "/Country/HR";
case 192: return "/Country/CU";
case 196: return "/Country/CY";
case 203: return "/Country/CZ";
case 204: return "/Country/BJ";
case 208: return "/Country/DK";
case 212: return "/Country/DM";
case 214: return "/Country/DO";
case 218: return "/Country/EC";
case 222: return "/Country/SV";
case 226: return "/Country/GQ";
case 231: return "/Country/ET";
case 232: return "/Country/ER";
case 233: return "/Country/EE";
case 242: return "/Country/FJ";
case 246: return "/Country/FI";
case 250: return "/Country/FR";
case 262: return "/Country/DJ";
case 266: return "/Country/GA";
case 268: return "/Country/GE";
case 270: return "/Country/GM";
case 275: return "/Country/PS";
case 276: return "/Country/DE";
case 288: return "/Country/GH";
case 296: return "/Country/KI";
case 300: return "/Country/GR";
case 308: return "/Country/GD";
case 320: return "/Country/GT";
case 324: return "/Country/GN";
case 328: return "/Country/GY";
case 332: return "/Country/HT";
case 336: return "/Country/VA";
case 340: return "/Country/HN";
case 344: return "/Country/HK";
case 348: return "/Country/HU";
case 352: return "/Country/IS";
case 356: return "/Country/IN";
case 360: return "/Country/ID";
case 364: return "/Country/IR";
case 368: return "/Country/IQ";
case 372: return "/Country/IE";
case 376: return "/Country/IL";
case 380: return "/Country/IT";
case 384: return "/Country/CI";
case 388: return "/Country/JM";
case 392: return "/Country/JP";
case 398: return "/Country/KZ";
case 400: return "/Country/JO";
case 404: return "/Country/KE";
case 408: return "/Country/KP";
case 410: return "/Country/KR";
case 414: return "/Country/KW";
case 417: return "/Country/KG";
case 418: return "/Country/LA";
case 422: return "/Country/LB";
case 426: return "/Country/LS";
case 428: return "/Country/LV";
case 430: return "/Country/LR";
case 434: return "/Country/LY";
case 438: return "/Country/LI";
case 440: return "/Country/LT";
case 442: return "/Country/LU";
case 450: return "/Country/MG";
case 454: return "/Country/MW";
case 458: return "/Country/MY";
case 462: return "/Country/MV";
case 466: return "/Country/ML";
case 470: return "/Country/MT";
case 478: return "/Country/MR";
case 480: return "/Country/MU";
case 484: return "/Country/MX";
case 492: return "/Country/MC";
case 496: return "/Country/MN";
case 498: return "/Country/MD";
case 499: return "/Country/ME";
case 504: return "/Country/MA";
case 508: return "/Country/MZ";
case 512: return "/Country/OM";
case 516: return "/Country/NA";
case 520: return "/Country/NR";
case 524: return "/Country/NP";
case 528: return "/Country/NL";
case 530: return "/Country/AN";
case 548: return "/Country/VU";
case 554: return "/Country/NZ";
case 558: return "/Country/NI";
case 562: return "/Country/NE";
case 566: return "/Country/NG";
case 578: return "/Country/NO";
case 583: return "/Country/FM";
case 584: return "/Country/MH";
case 585: return "/Country/PW";
case 586: return "/Country/PK";
case 591: return "/Country/PA";
case 598: return "/Country/PG";
case 600: return "/Country/PY";
case 604: return "/Country/PE";
case 608: return "/Country/PH";
case 616: return "/Country/PL";
case 620: return "/Country/PT";
case 624: return "/Country/GW";
case 626: return "/Country/TL";
case 634: return "/Country/QA";
case 642: return "/Country/RO";
case 643: return "/Country/RU";
case 646: return "/Country/RW";
case 659: return "/Country/KN";
case 662: return "/Country/LC";
case 670: return "/Country/VC";
case 674: return "/Country/SM";
case 678: return "/Country/ST";
case 682: return "/Country/SA";
case 686: return "/Country/SN";
case 688: return "/Country/RS";
case 690: return "/Country/SC";
case 694: return "/Country/SL";
case 702: return "/Country/SG";
case 703: return "/Country/SK";
case 704: return "/Country/VN";
case 705: return "/Country/SI";
case 706: return "/Country/SO";
case 710: return "/Country/ZA";
case 716: return "/Country/ZW";
case 724: return "/Country/ES";
case 728: return "/Country/SS";
case 729: return "/Country/SD";
case 740: return "/Country/SR";
case 748: return "/Country/SZ";
case 752: return "/Country/SE";
case 756: return "/Country/CH";
case 760: return "/Country/SY";
case 762: return "/Country/TJ";
case 764: return "/Country/TH";
case 768: return "/Country/TG";
case 776: return "/Country/TO";
case 780: return "/Country/TT";
case 784: return "/Country/AE";
case 788: return "/Country/TN";
case 792: return "/Country/TR";
case 795: return "/Country/TM";
case 798: return "/Country/TV";
case 800: return "/Country/UG";
case 804: return "/Country/UA";
case 807: return "/Country/MK";
case 818: return "/Country/EG";
case 826: return "/Country/GB";
case 834: return "/Country/TZ";
case 840: return "/Country/US";
case 854: return "/Country/BF";
case 858: return "/Country/UY";
case 860: return "/Country/UZ";
case 862: return "/Country/VE";
case 882: return "/Country/WS";
case 887: return "/Country/YE";
case 894: return "/Country/ZM";
default: return $"/Country/{values[index - 1]}";
}
#endregion
// uuid [TODO: Requires 128-bit values]
#region 2.25.*
oid_2_25:
if (index == values.Length) return "/Joint-ISO-ITU-T/UUID";
switch (values[index++])
{
case 0: return "/Joint-ISO-ITU-T/UUID/00000000-0000-0000-0000-000000000000";
//case 288786655511405443130567505384701230: return "/Joint-ISO-ITU-T/UUID/00379e48-0a2b-1085-b288-0002a5d5fd2e";
//case 987895962269883002155146617097157934: return "/Joint-ISO-ITU-T/UUID/00be4308-0c89-1085-8ea0-0002a5d5fd2e";
//case 1858228783942312576083372383319475483: return "/Joint-ISO-ITU-T/UUID/0165e1c0-a655-11e0-95b8-0002a5d5c51b";
//case 2474299330026746002885628159579243803: return "/Joint-ISO-ITU-T/UUID/01dc8860-25fb-11da-82b2-0002a5d5c51b";
//case 3263645701162998421821186056373271854: return "/Joint-ISO-ITU-T/UUID/02748e28-08c4-1085-b21d-0002a5d5fd2e";
//case 3325839809379844461264382260940242222: return "/Joint-ISO-ITU-T/UUID/02808890-0ad8-1085-9bdf-0002a5d5fd2e";
// TODO: Left off at http://www.oid-info.com/get/2.25.3664154270495270126161055518190585115
default: return $"/Joint-ISO-ITU-T/UUID/{values[index - 1]}";
}
#endregion
// telebiometrics
#region 2.42.*
oid_2_42:
if (index == values.Length) return "/Telebiometrics";
switch (values[index++])
{
case 0: goto oid_2_42_0;
case 1: goto oid_2_42_1;
case 2: goto oid_2_42_2;
case 3: goto oid_2_42_3;
default: return $"/Telebiometrics/{values[index - 1]}";
}
// modules
#region 2.42.0.*
oid_2_42_0:
if (index == values.Length) return "/Telebiometrics/Modules";
switch (values[index++])
{
case 0: goto oid_2_42_0_0;
default: return $"/Telebiometrics/Modules/{values[index - 1]}";
}
// main
#region 2.42.0.0.*
oid_2_42_0_0:
if (index == values.Length) return "/Telebiometrics/Modules/Main_Module";
switch (values[index++])
{
case 1: return "/Telebiometrics/Modules/Main_Module/Version1";
default: return $"/Telebiometrics/Modules/Main_Module/{values[index - 1]}";
}
#endregion
#endregion
// tmm
#region 2.42.1.*
oid_2_42_1:
if (index == values.Length) return "/Telebiometrics/TMM";
switch (values[index++])
{
case 0: goto oid_2_42_1_0;
case 1: goto oid_2_42_1_1;
case 2: goto oid_2_42_1_2;
case 3: goto oid_2_42_1_3;
case 4: return "/Telebiometrics/TMM/Practitioners";
default: return $"/Telebiometrics/TMM/{values[index - 1]}";
}
// modules
#region 2.42.1.0.*
oid_2_42_1_0:
if (index == values.Length) return "/Telebiometrics/TMM/Modules";
switch (values[index++])
{
case 0: goto oid_2_42_1_0_0;
default: return $"/Telebiometrics/TMM/Modules/{values[index - 1]}";
}
// main
#region 2.42.1.0.0.*
oid_2_42_1_0_0:
if (index == values.Length) return "/Telebiometrics/TMM/Modules/Main";
switch (values[index++])
{
case 0: return "/Telebiometrics/TMM/Modules/Main/First_Version";
default: return $"/Telebiometrics/TMM/Modules/Main/{values[index - 1]}";
}
#endregion
#endregion
// measures, metric
#region 2.42.1.1.*
oid_2_42_1_1:
if (index == values.Length) return "/Telebiometrics/TMM/Measures";
switch (values[index++])
{
case 1: goto oid_2_42_1_1_1;
case 2: return "/Telebiometrics/TMM/Measures/Units";
case 3: return "/Telebiometrics/TMM/Measures/Symbols";
case 4: return "/Telebiometrics/TMM/Measures/Conditions";
case 5: goto oid_2_42_1_1_5;
default: return $"/Telebiometrics/TMM/Measures/{values[index - 1]}";
}
// quantities
#region 2.42.1.1.1.*
oid_2_42_1_1_1:
if (index == values.Length) return "/Telebiometrics/TMM/Measures/Quantities";
switch (values[index++])
{
case 1: return "/Telebiometrics/TMM/Measures/Quantities/Physics";
case 2: return "/Telebiometrics/TMM/Measures/Quantities/Chemistry";
case 3: return "/Telebiometrics/TMM/Measures/Quantities/Biology";
case 4: return "/Telebiometrics/TMM/Measures/Quantities/Culturology";
case 5: return "/Telebiometrics/TMM/Measures/Quantities/Psychology";
default: return $"/Telebiometrics/TMM/Measures/Quantities/{values[index - 1]}";
}
#endregion
// methods
#region 2.42.1.1.5.*
oid_2_42_1_1_5:
if (index == values.Length) return "/Telebiometrics/TMM/Measures/Methods";
switch (values[index++])
{
case 1: return "/Telebiometrics/TMM/Measures/Methods/Physics";
case 2: return "/Telebiometrics/TMM/Measures/Methods/Chemistry";
case 3: return "/Telebiometrics/TMM/Measures/Methods/Biology";
case 4: return "/Telebiometrics/TMM/Measures/Methods/Culturology";
case 5: return "/Telebiometrics/TMM/Measures/Methods/Psychology";
default: return $"/Telebiometrics/TMM/Measures/Methods/{values[index - 1]}";
}
#endregion
#endregion
// fields-of-study, scientific
#region 2.42.1.2.*
oid_2_42_1_2:
if (index == values.Length) return "/Telebiometrics/TMM/Fields_of_Study";
switch (values[index++])
{
case 1: return "/Telebiometrics/TMM/Fields_of_Study/Physics";
case 2: return "/Telebiometrics/TMM/Fields_of_Study/Chemistry";
case 3: return "/Telebiometrics/TMM/Fields_of_Study/Biology";
case 4: return "/Telebiometrics/TMM/Fields_of_Study/Culturology";
case 5: return "/Telebiometrics/TMM/Fields_of_Study/Psychology";
default: return $"/Telebiometrics/TMM/Fields_of_Study/{values[index - 1]}";
}
#endregion
// modalities, sensory
#region 2.42.1.3.*
oid_2_42_1_3:
if (index == values.Length) return "/Telebiometrics/TMM/Modalities";
switch (values[index++])
{
case 1: return "/Telebiometrics/TMM/Modalities/Tango";
case 2: return "/Telebiometrics/TMM/Modalities/Video";
case 3: return "/Telebiometrics/TMM/Modalities/Audio";
case 4: return "/Telebiometrics/TMM/Modalities/Chemo";
case 5: return "/Telebiometrics/TMM/Modalities/Radio";
case 6: return "/Telebiometrics/TMM/Modalities/Calor";
case 7: return "/Telebiometrics/TMM/Modalities/Electro";
default: return $"/Telebiometrics/TMM/Modalities/{values[index - 1]}";
}
#endregion
#endregion
// human-physiology
#region 2.42.2.*
oid_2_42_2:
if (index == values.Length) return "/Telebiometrics/Human_Physiology";
switch (values[index++])
{
case 0: goto oid_2_42_2_0;
case 1: goto oid_2_42_2_1;
case 2: return "/Telebiometrics/Human_Physiology/Symbol_Combinations";
default: return $"/Telebiometrics/Human_Physiology/{values[index - 1]}";
}
// modules
#region 2.42.2.0.*
oid_2_42_2_0:
if (index == values.Length) return "/Telebiometrics/Human_Physiology/Modules";
switch (values[index++])
{
case 0: goto oid_2_42_2_0_0;
default: return $"/Telebiometrics/Human_Physiology/Modules/{values[index - 1]}";
}
// main
#region 2.42.2.0.0.*
oid_2_42_2_0_0:
if (index == values.Length) return "/Telebiometrics/Human_Physiology/Modules/Main_Module";
switch (values[index++])
{
case 0: return "/Telebiometrics/Human_Physiology/Modules/Main_Module/First_Version";
default: return $"/Telebiometrics/Human_Physiology/Modules/Main_Module/{values[index - 1]}";
}
#endregion
#endregion
// symbols
#region 2.42.2.1.*
oid_2_42_2_1:
if (index == values.Length) return "/Telebiometrics/Human_Physiology/Symbols";
switch (values[index++])
{
case 1: return "/Telebiometrics/Human_Physiology/Symbols/Tango_in";
case 2: return "/Telebiometrics/Human_Physiology/Symbols/Video_in";
case 3: return "/Telebiometrics/Human_Physiology/Symbols/Audio_in";
case 4: return "/Telebiometrics/Human_Physiology/Symbols/Chemo_in";
case 5: return "/Telebiometrics/Human_Physiology/Symbols/Radio_in";
case 6: return "/Telebiometrics/Human_Physiology/Symbols/Calor_in";
case 7: return "/Telebiometrics/Human_Physiology/Symbols/Tango_out";
case 8: return "/Telebiometrics/Human_Physiology/Symbols/Video_out";
case 9: return "/Telebiometrics/Human_Physiology/Symbols/Audio_out";
case 10: return "/Telebiometrics/Human_Physiology/Symbols/Chemo_out";
case 11: return "/Telebiometrics/Human_Physiology/Symbols/Radio_out";
case 12: return "/Telebiometrics/Human_Physiology/Symbols/Calor_out";
case 13: return "/Telebiometrics/Human_Physiology/Symbols/Safe";
case 14: return "/Telebiometrics/Human_Physiology/Symbols/Threshold";
default: return $"/Telebiometrics/Human_Physiology/Symbols/{values[index - 1]}";
}
#endregion
#endregion
// obj-cat, telehealth, e-health-protocol, th
#region 2.42.3.*
oid_2_42_3:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol";
switch (values[index++])
{
case 0: goto oid_2_42_3_0;
case 1: return "/Telebiometrics/E_Health_Protocol/[Patient schemes]";
case 2: return "/Telebiometrics/E_Health_Protocol/[Medical staff schemes]";
case 3: return "/Telebiometrics/E_Health_Protocol/[Observer schemes]";
case 4: return "/Telebiometrics/E_Health_Protocol/[Pharmaceutical schemes]";
case 5: return "/Telebiometrics/E_Health_Protocol/[Laboratory schemes]";
case 6: return "/Telebiometrics/E_Health_Protocol/[Drug manufacturer schemes]";
case 7: return "/Telebiometrics/E_Health_Protocol/[Medical device schemes]";
case 8: return "/Telebiometrics/E_Health_Protocol/[Medical software schemes]";
case 9: return "/Telebiometrics/E_Health_Protocol/[Medical insurance schemes]";
case 10: return "/Telebiometrics/E_Health_Protocol/[Medical record schemes]";
default: return $"/Telebiometrics/E_Health_Protocol/{values[index - 1]}";
}
// obj-cat, telehealth, e-health-protocol, th
#region 2.42.3.0.*
oid_2_42_3_0:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol/Modules";
switch (values[index++])
{
case 0: goto oid_2_42_3_0_0;
case 1: goto oid_2_42_3_0_1;
case 2: goto oid_2_42_3_0_2;
case 3: goto oid_2_42_3_0_3;
case 4: goto oid_2_42_3_0_4;
case 5: goto oid_2_42_3_0_5;
default: return $"/Telebiometrics/E_Health_Protocol/Modules/{values[index - 1]}";
}
// identification
#region 2.42.3.0.0.*
oid_2_42_3_0_0:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol/Modules/Identification";
switch (values[index++])
{
case 1: return "/Telebiometrics/E_Health_Protocol/Modules/Identification/Version1";
default: return $"/Telebiometrics/E_Health_Protocol/Modules/Identification/{values[index - 1]}";
}
#endregion
// set-up
#region 2.42.3.0.1.*
oid_2_42_3_0_1:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol/Modules/Setup";
switch (values[index++])
{
case 1: return "/Telebiometrics/E_Health_Protocol/Modules/Setup/Version1";
default: return $"/Telebiometrics/E_Health_Protocol/Modules/Setup/{values[index - 1]}";
}
#endregion
// send-and-ack
#region 2.42.3.0.2.*
oid_2_42_3_0_2:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol/Modules/Send-and-ack";
switch (values[index++])
{
case 1: return "/Telebiometrics/E_Health_Protocol/Modules/Send-and-ack/Version1";
default: return $"/Telebiometrics/E_Health_Protocol/Modules/Send-and-ack/{values[index - 1]}";
}
#endregion
// command-response
#region 2.42.3.0.3.*
oid_2_42_3_0_3:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol/Modules/Command-response";
switch (values[index++])
{
case 1: return "/Telebiometrics/E_Health_Protocol/Modules/Command-response/Version1";
default: return $"/Telebiometrics/E_Health_Protocol/Modules/Command-response/{values[index - 1]}";
}
#endregion
// quantity-and-units
#region 2.42.3.0.4.*
oid_2_42_3_0_4:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol/Modules/Quantities_And_Units";
switch (values[index++])
{
case 1: return "/Telebiometrics/E_Health_Protocol/Modules/Quantities_And_Units/Version1";
default: return $"/Telebiometrics/E_Health_Protocol/Modules/Quantities_And_Units/{values[index - 1]}";
}
#endregion
// examples
#region 2.42.3.0.5.*
oid_2_42_3_0_5:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol/Modules/Examples";
switch (values[index++])
{
case 0: return "/Telebiometrics/E_Health_Protocol/Modules/Examples/Command_Response";
case 1: return "/Telebiometrics/E_Health_Protocol/Modules/Examples/Data_Message";
default: return $"/Telebiometrics/E_Health_Protocol/Modules/Examples/{values[index - 1]}";
}
#endregion
#endregion
#endregion
#endregion
// cybersecurity
#region 2.48.*
oid_2_48:
if (index == values.Length) return "/Cybersecurity";
switch (values[index++])
{
case 1: return "/Cybersecurity/Country";
case 2: return "/Cybersecurity/International-Org";
default: return $"/Cybersecurity/{values[index - 1]}";
}
#endregion
// alerting
#region 2.49.*
oid_2_49:
if (index == values.Length) return "/Alerting";
switch (values[index++])
{
case 0: return "/Alerting/WMO";
default: return $"/Alerting/{values[index - 1]}";
}
#endregion
#endregion
}
}
#pragma warning restore IDE0011
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
namespace BurnOutSharp.Builder
{
/// <summary>
/// Methods related to Object Identifiers (OID)
/// </summary>
public static partial class ObjectIdentifier
{
// TODO: ulong[] isn't going to work. If we can use .NET 7, we can use UInt128
// We might want to look into storing all values as GUID? I don't remember if
// you can do value comparisions between an integral value and a GUID, though.
/// <summary>
/// Parse an OID in DER-encoded byte notation into a list of values
/// </summary>
/// <param name="data">Byte array representing the data to read</param>
/// <param name="length">Total length of the data according to the DER TLV</param>
/// <returns>Array of values representing the OID</returns>
public static ulong[] ParseDERIntoArray(byte[] data, ulong length)
{
// The first byte contains nodes 1 and 2
int firstNode = Math.DivRem(data[0], 40, out int secondNode);
// Create a list for all nodes
List<ulong> nodes = new List<ulong> { (ulong)firstNode, (ulong)secondNode };
// All other nodes are encoded uniquely
int offset = 1;
while (offset < (long)length)
{
// If bit 7 is not set
if ((data[offset] & 0x80) == 0)
{
nodes.Add(data[offset]);
offset++;
continue;
}
// Otherwise, read the encoded value in a loop
ulong dotValue = 0;
bool doneProcessing = false;
do
{
// Shift the current encoded value
dotValue <<= 7;
// If we have a leading zero byte, we're at the end
if ((data[offset] & 0x80) == 0)
doneProcessing = true;
// Clear the top byte
unchecked { data[offset] &= (byte)~0x80; }
// Add the new value to the result
dotValue |= data[offset];
// Increment the offset
offset++;
} while (offset < data.Length && !doneProcessing);
// Add the parsed value to the output
nodes.Add(dotValue);
}
return nodes.ToArray();
}
}
}

View File

@@ -0,0 +1,26 @@
namespace BurnOutSharp.Builder
{
#pragma warning disable IDE0011
/// <summary>
/// Methods related to Object Identifiers (OID) and dot notation
/// </summary>
public static partial class ObjectIdentifier
{
/// <summary>
/// Parse an OID in separated-value notation into dot notation
/// </summary>
/// <param name="values">List of values to check against</param>
/// <returns>List of values representing the dot notation</returns>
public static string ParseOIDToDotNotation(ulong[] values)
{
// If we have an invalid set of values, we can't do anything
if (values == null || values.Length == 0)
return null;
return string.Join(".", values);
}
}
#pragma warning restore IDE0011
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
<Title>BurnOutSharp.Matching</Title>
<AssemblyName>BurnOutSharp.Matching</AssemblyName>
<Authors>Matt Nadareski</Authors>
<Product>BurnOutSharp</Product>
<Copyright>Copyright (c)2018-2022 Matt Nadareski</Copyright>
<RepositoryUrl>https://github.com/mnadareski/BurnOutSharp</RepositoryUrl>
<Version>2.5</Version>
<AssemblyVersion>2.5</AssemblyVersion>
<FileVersion>2.5</FileVersion>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
</PropertyGroup>
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>

View File

@@ -1,3 +1,5 @@
using System.IO;
namespace BurnOutSharp.Matching
{
/// <summary>
@@ -33,7 +35,7 @@ namespace BurnOutSharp.Matching
End = end;
}
#region Matching
#region Array Matching
/// <summary>
/// Get if this match can be found in a stack
@@ -91,6 +93,7 @@ namespace BurnOutSharp.Matching
if (Needle.Length > stack.Length - index)
return false;
// Loop through and check the value
for (int i = 0; i < Needle.Length; i++)
{
// A null value is a wildcard
@@ -102,7 +105,96 @@ namespace BurnOutSharp.Matching
return true;
}
#endregion
#region Stream Matching
/// <summary>
/// Get if this match can be found in a stack
/// </summary>
/// <param name="stack">Stream 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(Stream 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);
// Set the default start and end values
int start = Start;
int end = End;
// If start or end are not set properly, set them to defaults
if (start < 0)
start = 0;
if (end < 0)
end = (int)(stack.Length - Needle.Length);
for (int i = reverse ? end : start; reverse ? i > start : i < end; i += reverse ? -1 : 1)
{
// If we somehow have an invalid end and we haven't matched, return
if (i > stack.Length)
return (false, -1);
// Check to see if the values are equal
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">Stream 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(Stream 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;
// Save the current position and move to the index
long currentPosition = stack.Position;
stack.Seek(index, SeekOrigin.Begin);
// Set the return value
bool matched = true;
// Loop through and check the value
for (int i = 0; i < Needle.Length; i++)
{
byte stackValue = (byte)stack.ReadByte();
// A null value is a wildcard
if (Needle[i] == null)
{
continue;
}
else if (stackValue != Needle[i])
{
matched = false;
break;
}
}
// Reset the position and return the value
stack.Seek(currentPosition, SeekOrigin.Begin);
return matched;
}
#endregion
}
}

View File

@@ -0,0 +1,199 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace BurnOutSharp.Matching
{
/// <summary>
/// A set of content matches that work together
/// </summary>
public 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> GetArrayVersion { get; set; }
/// <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, Stream, List<int>, string> GetStreamVersion { get; set; }
#region Generic Constructors
public ContentMatchSet(byte?[] needle, string protectionName)
: this(new List<byte?[]> { needle }, getArrayVersion: null, protectionName) { }
public ContentMatchSet(List<byte?[]> needles, string protectionName)
: this(needles, getArrayVersion: null, protectionName) { }
public ContentMatchSet(ContentMatch needle, string protectionName)
: this(new List<ContentMatch>() { needle }, getArrayVersion: null, protectionName) { }
public ContentMatchSet(List<ContentMatch> needles, string protectionName)
: this(needles, getArrayVersion: null, protectionName) { }
#endregion
#region Array Constructors
public ContentMatchSet(byte?[] needle, Func<string, byte[], List<int>, string> getArrayVersion, string protectionName)
: this(new List<byte?[]> { needle }, getArrayVersion, protectionName) { }
public ContentMatchSet(List<byte?[]> needles, Func<string, byte[], List<int>, string> getArrayVersion, string protectionName)
: this(needles.Select(n => new ContentMatch(n)).ToList(), getArrayVersion, protectionName) { }
public ContentMatchSet(ContentMatch needle, Func<string, byte[], List<int>, string> getArrayVersion, string protectionName)
: this(new List<ContentMatch>() { needle }, getArrayVersion, protectionName) { }
public ContentMatchSet(List<ContentMatch> needles, Func<string, byte[], List<int>, string> getArrayVersion, string protectionName)
{
Matchers = needles;
GetArrayVersion = getArrayVersion;
ProtectionName = protectionName;
}
#endregion
#region Stream Constructors
public ContentMatchSet(byte?[] needle, Func<string, Stream, List<int>, string> getStreamVersion, string protectionName)
: this(new List<byte?[]> { needle }, getStreamVersion, protectionName) { }
public ContentMatchSet(List<byte?[]> needles, Func<string, Stream, List<int>, string> getStreamVersion, string protectionName)
: this(needles.Select(n => new ContentMatch(n)).ToList(), getStreamVersion, protectionName) { }
public ContentMatchSet(ContentMatch needle, Func<string, Stream, List<int>, string> getStreamVersion, string protectionName)
: this(new List<ContentMatch>() { needle }, getStreamVersion, protectionName) { }
public ContentMatchSet(List<ContentMatch> needles, Func<string, Stream, List<int>, string> getStreamVersion, string protectionName)
{
Matchers = needles;
GetStreamVersion = getStreamVersion;
ProtectionName = protectionName;
}
#endregion
#region Array Matching
/// <summary>
/// Determine whether all content matches pass
/// </summary>
/// <param name="stack">Array to search</param>
/// <returns>Tuple of passing status and matching positions</returns>
public (bool, List<int>) MatchesAll(byte[] stack)
{
// 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(stack);
if (!match)
return (false, new List<int>());
else
positions.Add(position);
}
return (true, positions);
}
/// <summary>
/// Determine whether any content matches pass
/// </summary>
/// <param name="stack">Array to search</param>
/// <returns>Tuple of passing status and first matching position</returns>
public (bool, int) MatchesAny(byte[] stack)
{
// 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(stack);
if (match)
return (true, position);
}
return (false, -1);
}
#endregion
#region Stream Matching
/// <summary>
/// Determine whether all content matches pass
/// </summary>
/// <param name="stack">Stream to search</param>
/// <returns>Tuple of passing status and matching positions</returns>
public (bool, List<int>) MatchesAll(Stream stack)
{
// 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(stack);
if (!match)
return (false, new List<int>());
else
positions.Add(position);
}
return (true, positions);
}
/// <summary>
/// Determine whether any content matches pass
/// </summary>
/// <param name="stack">Stream to search</param>
/// <returns>Tuple of passing status and first matching position</returns>
public (bool, int) MatchesAny(Stream stack)
{
// 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(stack);
if (match)
return (true, position);
}
return (false, -1);
}
#endregion
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace BurnOutSharp.Matching
@@ -7,42 +8,42 @@ namespace BurnOutSharp.Matching
/// <summary>
/// Helper class for matching
/// </summary>
internal static class MatchUtil
public static class MatchUtil
{
#region Content Matching
#region Array 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="stack">Array to search</param>
/// <param name="matchers">Enumerable of ContentMatchSets to be run on the file</param>
/// <param name="includeDebug">True to include positional data, false otherwise</param>
/// <returns>List of strings representing the matched protections, null or empty otherwise</returns>
public static ConcurrentQueue<string> GetAllMatches(
string file,
byte[] fileContent,
byte[] stack,
IEnumerable<ContentMatchSet> matchers,
bool includeDebug = false)
{
return FindAllMatches(file, fileContent, matchers, includeDebug, false);
return FindAllMatches(file, stack, matchers, includeDebug, 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="stack">Array to search</param>
/// <param name="matchers">Enumerable of ContentMatchSets to be run on the file</param>
/// <param name="includeDebug">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,
byte[] stack,
IEnumerable<ContentMatchSet> matchers,
bool includeDebug = false)
{
var contentMatches = FindAllMatches(file, fileContent, matchers, includeDebug, true);
var contentMatches = FindAllMatches(file, stack, matchers, includeDebug, true);
if (contentMatches == null || !contentMatches.Any())
return null;
@@ -53,14 +54,14 @@ namespace BurnOutSharp.Matching
/// 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="stack">Array to search</param>
/// <param name="matchers">Enumerable of ContentMatchSets to be run on the file</param>
/// <param name="includeDebug">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 ConcurrentQueue<string> FindAllMatches(
string file,
byte[] fileContent,
byte[] stack,
IEnumerable<ContentMatchSet> matchers,
bool includeDebug,
bool stopAfterFirst)
@@ -76,7 +77,7 @@ namespace BurnOutSharp.Matching
foreach (var matcher in matchers)
{
// Determine if the matcher passes
(bool passes, List<int> positions) = matcher.MatchesAll(fileContent);
(bool passes, List<int> positions) = matcher.MatchesAll(stack);
if (!passes)
continue;
@@ -84,7 +85,7 @@ namespace BurnOutSharp.Matching
string positionsString = string.Join(", ", positions);
// If we there is no version method, just return the protection name
if (matcher.GetVersion == null)
if (matcher.GetArrayVersion == null)
{
matchedProtections.Enqueue((matcher.ProtectionName ?? "Unknown Protection") + (includeDebug ? $" (Index {positionsString})" : string.Empty));
}
@@ -93,7 +94,108 @@ namespace BurnOutSharp.Matching
else
{
// A null version returned means the check didn't pass at the version step
string version = matcher.GetVersion(file, fileContent, positions);
string version = matcher.GetArrayVersion(file, stack, positions);
if (version == null)
continue;
matchedProtections.Enqueue($"{matcher.ProtectionName ?? "Unknown Protection"} {version}".TrimEnd() + (includeDebug ? $" (Index {positionsString})" : string.Empty));
}
// If we're stopping after the first protection, bail out here
if (stopAfterFirst)
return matchedProtections;
}
return matchedProtections;
}
#endregion
#region Stream 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="stack">Stream to search</param>
/// <param name="matchers">Enumerable of ContentMatchSets to be run on the file</param>
/// <param name="includeDebug">True to include positional data, false otherwise</param>
/// <returns>List of strings representing the matched protections, null or empty otherwise</returns>
public static ConcurrentQueue<string> GetAllMatches(
string file,
Stream stack,
IEnumerable<ContentMatchSet> matchers,
bool includeDebug = false)
{
return FindAllMatches(file, stack, matchers, includeDebug, false);
}
/// <summary>
/// Get first content match for a given list of matchers
/// </summary>
/// <param name="file">File to check for matches</param>
/// <param name="stack">Stream to search</param>
/// <param name="matchers">Enumerable of ContentMatchSets to be run on the file</param>
/// <param name="includeDebug">True to include positional data, false otherwise</param>
/// <returns>String representing the matched protection, null otherwise</returns>
public static string GetFirstMatch(
string file,
Stream stack,
IEnumerable<ContentMatchSet> matchers,
bool includeDebug = false)
{
var contentMatches = FindAllMatches(file, stack, matchers, includeDebug, 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="stack">Stream to search</param>
/// <param name="matchers">Enumerable of ContentMatchSets to be run on the file</param>
/// <param name="includeDebug">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 ConcurrentQueue<string> FindAllMatches(
string file,
Stream stack,
IEnumerable<ContentMatchSet> matchers,
bool includeDebug,
bool stopAfterFirst)
{
// If there's no mappings, we can't match
if (matchers == null || !matchers.Any())
return null;
// Initialize the queue of matched protections
var matchedProtections = new ConcurrentQueue<string>();
// Loop through and try everything otherwise
foreach (var matcher in matchers)
{
// Determine if the matcher passes
(bool passes, List<int> positions) = matcher.MatchesAll(stack);
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.GetStreamVersion == null)
{
matchedProtections.Enqueue((matcher.ProtectionName ?? "Unknown Protection") + (includeDebug ? $" (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.GetStreamVersion(file, stack, positions);
if (version == null)
continue;

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
<Title>BurnOutSharp.Models</Title>
<AssemblyName>BurnOutSharp.Models</AssemblyName>
<Authors>Matt Nadareski</Authors>
<Product>BurnOutSharp</Product>
<Copyright>Copyright (c)2022 Matt Nadareski</Copyright>
<RepositoryUrl>https://github.com/mnadareski/BurnOutSharp</RepositoryUrl>
<Version>2.5</Version>
<AssemblyVersion>2.5</AssemblyVersion>
<FileVersion>2.5</FileVersion>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
</PropertyGroup>
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,36 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The debug information is defined by the debugger and is not controlled by
/// the linear EXE format or linker. The only data defined by the linear EXE
/// format relative to the debug information is it's offset in the EXE file and
/// length in bytes as defined in the linear EXE header.
///
/// To support multiple debuggers the first word of the debug information is a
/// type field which determines the format of the debug information.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public class DebugInformation
{
/// <summary>
/// The signature consists of a string of three (3) ASCII characters: "NB0"
/// </summary>
public byte[] Signature;
/// <summary>
/// This defines the type of debugger data that exists in the remainder of the
/// debug information.
/// </summary>
public DebugFormatType FormatType;
// DEBUGGER DATA = Debugger specific data.
// The format of the debugger data is defined by the debugger that is being used.
// The values defined for the type field are not enforced by the system. It is
// the responsibility of the linker or debugging tools to follow the convention
// for the type field that is defined here.
}
}

View File

@@ -0,0 +1,234 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The entry table contains object and offset information that is used to resolve
/// fixup references to the entry points within this module. Not all entry points
/// in the entry table will be exported, some entry points will only be used
/// within the module. An ordinal number is used to index into the entry table.
/// The entry table entries are numbered starting from one.
///
/// The list of entries are compressed into 'bundles', where possible. The entries
/// within each bundle are all the same size. A bundle starts with a count field
/// which indicates the number of entries in the bundle. The count is followed by
/// a type field which identifies the bundle format. This provides both a means
/// for saving space as well as a mechanism for extending the bundle types.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Explicit)]
public class EntryTableEntry
{
/// <summary>
/// Number of entries.
/// </summary>
/// <remarks>
/// This is the number of entries in this bundle.
///
/// A zero value for the number of entries identifies the end of the
/// entry table. There is no further bundle information when the number
/// of entries is zero. In other words the entry table is terminated by
/// a single zero byte.
///
/// For <see cref="BundleType.UnusedEntry"/>, this is the number of unused
/// entries to skip.
/// For <see cref="BundleType.SixteenBitEntry"/>, this is the number of 16-bit
/// entries in this bundle. The flags and offset value are repeated this
/// number of times.
/// For <see cref="BundleType.TwoEightySixCallGateEntry"/>, this is the number
/// of 286 call gate entries in this bundle. The flags, callgate, and offset
/// value are repeated this number of times.
/// For <see cref="BundleType.ThirtyTwoBitEntry"/>, this is the number
/// of 32-bit entries in this bundle. The flags and offset value are repeated
/// this number of times.
/// For <see cref="BundleType.ForwarderEntry"/>, this field is reserved for future use.
/// </remarks>
[FieldOffset(0)] public byte Entries;
/// <summary>
/// This defines the bundle type which determines the contents of the BUNDLE INFO.
/// </summary>
[FieldOffset(1)] public BundleType BundleType;
#region 16-bit Entry
/// <summary>
/// Object number.
/// </summary>
/// <remarks>
/// This is the object number for the entries in this bundle.
/// </remarks>
[FieldOffset(2)] public ushort SixteenBitObjectNumber;
/// <summary>
/// Entry flags.
/// </summary>
/// <remarks>
/// These are the flags for this entry point.
/// </remarks>
[FieldOffset(4)] public EntryFlags SixteenBitEntryFlags;
/// <summary>
/// Offset in object.
/// </summary>
/// <remarks>
/// This is the offset in the object for the entry point defined at this ordinal number.
/// </remarks>
[FieldOffset(5)] public ushort SixteenBitOffset;
#endregion
#region 286 Call Gate Entry
/// <summary>
/// Object number.
/// </summary>
/// <remarks>
/// This is the object number for the entries in this bundle.
/// </remarks>
[FieldOffset(2)] public ushort TwoEightySixObjectNumber;
/// <summary>
/// Entry flags.
/// </summary>
/// <remarks>
/// These are the flags for this entry point.
/// </remarks>
[FieldOffset(4)] public EntryFlags TwoEightySixEntryFlags;
/// <summary>
/// Offset in object.
/// </summary>
/// <remarks>
/// This is the offset in the object for the entry point defined at this ordinal number.
/// </remarks>
[FieldOffset(5)] public ushort TwoEightySixOffset;
/// <summary>
/// Callgate selector.
/// </summary>
/// <remarks>
/// The callgate selector is a reserved field used by the loader to store a call
/// gate selector value for references to ring 2 entry points. When a ring 3
/// reference to a ring 2 entry point is made, the callgate selector with a zero
/// offset is place in the relocation fixup address. The segment number and offset
/// in segment is placed in the LDT callgate.
/// </remarks>
[FieldOffset(7)] public ushort TwoEightySixCallgate;
#endregion
#region 32-bit Entry
/// <summary>
/// Object number.
/// </summary>
/// <remarks>
/// This is the object number for the entries in this bundle.
/// </remarks>
[FieldOffset(2)] public ushort ThirtyTwoBitObjectNumber;
/// <summary>
/// Entry flags.
/// </summary>
/// <remarks>
/// These are the flags for this entry point.
/// </remarks>
[FieldOffset(4)] public EntryFlags ThirtyTwoBitEntryFlags;
/// <summary>
/// Offset in object.
/// </summary>
/// <remarks>
/// This is the offset in the object for the entry point defined at this ordinal number.
/// </remarks>
[FieldOffset(5)] public uint ThirtyTwoBitOffset;
#endregion
#region Forwarder Entry
/// <summary>
/// 0
/// </summary>
/// <remarks>
/// This field is reserved for future use.
/// </remarks>
[FieldOffset(2)] public ushort ForwarderReserved;
/// <summary>
/// Forwarder flags.
/// </summary>
/// <remarks>
/// These are the flags for this entry point.
/// </remarks>
[FieldOffset(4)] public ForwarderFlags ForwarderFlags;
/// <summary>
/// Module Ordinal Number
/// </summary>
/// <remarks>
/// This is the index into the Import Module Name Table for this forwarder.
/// </remarks>
[FieldOffset(5)] public ushort ForwarderModuleOrdinalNumber;
/// <summary>
/// Procedure Name Offset
/// </summary>
/// <remarks>
/// If the FLAGS field indicates import by ordinal, then this field is the
/// ordinal number into the Entry Table of the target module, otherwise this
/// field is the offset into the Procedure Names Table of the target module.
///
/// A Forwarder entry (type = 4) is an entry point whose value is an imported
/// reference. When a load time fixup occurs whose target is a forwarder, the
/// loader obtains the address imported by the forwarder and uses that imported
/// address to resolve the fixup.
///
/// A forwarder may refer to an entry point in another module which is itself a
/// forwarder, so there can be a chain of forwarders. The loader will traverse
/// the chain until it finds a non-forwarded entry point which terminates the
/// chain, and use this to resolve the original fixup. Circular chains are
/// detected by the loader and result in a load time error. A maximum of 1024
/// forwarders is allowed in a chain; more than this results in a load time error.
///
/// Forwarders are useful for merging and recombining API calls into different
/// sets of libraries, while maintaining compatibility with applications. For
/// example, if one wanted to combine MONCALLS, MOUCALLS, and VIOCALLS into a
/// single libraries, one could provide entry points for the three libraries
/// that are forwarders pointing to the common implementation.
/// </remarks>
[FieldOffset(7)] public uint ProcedureNameOffset;
/// <summary>
/// Import Ordinal Number
/// </summary>
/// <remarks>
/// If the FLAGS field indicates import by ordinal, then this field is the
/// ordinal number into the Entry Table of the target module, otherwise this
/// field is the offset into the Procedure Names Table of the target module.
///
/// A Forwarder entry (type = 4) is an entry point whose value is an imported
/// reference. When a load time fixup occurs whose target is a forwarder, the
/// loader obtains the address imported by the forwarder and uses that imported
/// address to resolve the fixup.
///
/// A forwarder may refer to an entry point in another module which is itself a
/// forwarder, so there can be a chain of forwarders. The loader will traverse
/// the chain until it finds a non-forwarded entry point which terminates the
/// chain, and use this to resolve the original fixup. Circular chains are
/// detected by the loader and result in a load time error. A maximum of 1024
/// forwarders is allowed in a chain; more than this results in a load time error.
///
/// Forwarders are useful for merging and recombining API calls into different
/// sets of libraries, while maintaining compatibility with applications. For
/// example, if one wanted to combine MONCALLS, MOUCALLS, and VIOCALLS into a
/// single libraries, one could provide entry points for the three libraries
/// that are forwarders pointing to the common implementation.
/// </remarks>
[FieldOffset(7)] public uint ImportOrdinalNumber;
#endregion
}
}

View File

@@ -0,0 +1,662 @@
using System;
namespace BurnOutSharp.Models.LinearExecutable
{
[Flags]
public enum BundleType : byte
{
/// <summary>
/// Unused Entry.
/// </summary>
UnusedEntry = 0x00,
/// <summary>
/// 16-bit Entry.
/// </summary>
SixteenBitEntry = 0x01,
/// <summary>
/// 286 Call Gate Entry.
/// </summary>
TwoEightySixCallGateEntry = 0x02,
/// <summary>
/// 32-bit Entry.
/// </summary>
ThirtyTwoBitEntry = 0x03,
/// <summary>
/// Forwarder Entry.
/// </summary>
ForwarderEntry = 0x04,
/// <summary>
/// Parameter Typing Information Present.
/// </summary>
/// <remarks>
/// This bit signifies that additional information is contained in the
/// linear EXE module and will be used in the future for parameter type checking.
/// </remarks>
ParameterTypingInformationPresent = 0x80,
}
public enum ByteOrder : byte
{
/// <summary>
/// little-endian
/// </summary>
LE = 0x00,
/// <summary>
/// big-endian
/// </summary>
/// <remarks>non-zero</remarks>
BE = 0x01,
}
public enum CPUType : ushort
{
/// <summary>
/// Intel 80286 or upwardly compatible
/// </summary>
Intel80286 = 0x01,
/// <summary>
/// Intel 80386 or upwardly compatible
/// </summary>
Intel80386 = 0x02,
/// <summary>
/// Intel 80486 or upwardly compatible
/// </summary>
Intel80486 = 0x03,
/// <summary>
/// Intel 80586 or upwardly compatible
/// </summary>
Intel80586 = 0x04,
/// <summary>
/// Intel i860 (N10) or compatible
/// </summary>
Inteli860 = 0x20,
/// <summary>
/// Intel "N11" or compatible
/// </summary>
IntelN11 = 0x21,
/// <summary>
/// MIPS Mark I (R2000, R3000) or compatible
/// </summary>
MIPSMarkI = 0x40,
/// <summary>
/// MIPS Mark II ( R6000 ) or compatible
/// </summary>
MIPSMarkII = 0x41,
/// <summary>
/// MIPS Mark III ( R4000 ) or compatible
/// </summary>
MIPSMarkIII = 0x42,
}
public enum DebugFormatType : byte
{
/// <summary>
/// 32-bit CodeView debugger format.
/// </summary>
CodeView32Bit = 0x00,
/// <summary>
/// AIX debugger format.
/// </summary>
AIXDebugger = 0x01,
/// <summary>
/// 16-bit CodeView debugger format.
/// </summary>
CodeView16Bit = 0x02,
/// <summary>
/// 32-bit OS/2 PM debugger (IBM) format.
/// </summary>
OS2PM32Bit = 0x04,
}
public enum DirectiveNumber : ushort
{
/// <summary>
/// Resident Flag Mask.
/// </summary>
/// <remarks>
/// Directive numbers with this bit set indicate that the directive data
/// is in the resident area and will be kept resident in memory when the
/// module is loaded.
/// </remarks>
ResidentFlagMask = 0x8000,
/// <summary>
/// Verify Record Directive. (Verify record is a resident table.)
/// </summary>
VerifyRecordDirective = 0x8001,
/// <summary>
/// Language Information Directive. (This is a non-resident table.)
/// </summary>
LanguageInformationDirective = 0x0002,
/// <summary>
/// Co-Processor Required Support Table.
/// </summary>
CoProcessorRequiredSupportTable = 0x0003,
/// <summary>
/// Thread State Initialization Directive.
/// </summary>
ThreadStateInitializationDirective = 0x0004,
// Additional directives can be added as needed in the future, as long as
// they do not overlap previously defined directive numbers.
}
[Flags]
public enum EntryFlags : byte
{
/// <summary>
/// Exported entry flag.
/// </summary>
ExportedEntry = 0x01,
/// <summary>
/// Parameter word count mask.
/// </summary>
ParameterWordCountMask = 0xF8,
}
[Flags]
public enum FixupRecordSourceType : byte
{
/// <summary>
/// Source mask.
/// </summary>
SourceMask = 0x0F,
/// <summary>
/// Byte fixup (8-bits).
/// </summary>
ByteFixup = 0x00,
/// <summary>
/// (undefined).
/// </summary>
Undefined1 = 0x01,
/// <summary>
/// 16-bit Selector fixup (16-bits).
/// </summary>
SixteenBitSelectorFixup = 0x02,
/// <summary>
/// 16:16 Pointer fixup (32-bits).
/// </summary>
SixteenSixteenPointerFixup = 0x03,
/// <summary>
/// (undefined).
/// </summary>
Undefined4 = 0x04,
/// <summary>
/// 16-bit Offset fixup (16-bits).
/// </summary>
SixteenBitOffsetFixup = 0x05,
/// <summary>
/// 16:32 Pointer fixup (48-bits).
/// </summary>
SixteenThirtyTwoPointerFixup = 0x06,
/// <summary>
/// 32-bit Offset fixup (32-bits).
/// </summary>
ThirtyTwoBitOffsetFixup = 0x07,
/// <summary>
/// 32-bit Self-relative offset fixup (32-bits).
/// </summary>
ThirtyTwoBitSelfRelativeOffsetFixup = 0x08,
/// <summary>
/// Fixup to Alias Flag.
/// </summary>
/// <remarks>
/// When the 'Fixup to Alias' Flag is set, the source fixup refers to
/// the 16:16 alias for the object. This is only valid for source types
/// of 2, 3, and 6. For fixups such as this, the linker and loader will
/// be required to perform additional checks such as ensuring that the
/// target offset for this fixup is less than 64K.
/// </remarks>
FixupToAliasFlag = 0x10,
/// <summary>
/// Source List Flag.
/// </summary>
/// <remarks>
/// When the 'Source List' Flag is set, the SRCOFF field is compressed
/// to a byte and contains the number of source offsets, and a list of source
/// offsets follows the end of fixup record (after the optional additive value).
/// </remarks>
SourceListFlag = 0x20,
}
[Flags]
public enum FixupRecordTargetFlags : byte
{
/// <summary>
/// Fixup target type mask.
/// </summary>
FixupTargetTypeMask = 0x03,
/// <summary>
/// Internal reference.
/// </summary>
InternalReference = 0x00,
/// <summary>
/// Imported reference by ordinal.
/// </summary>
ImportedReferenceByOrdinal = 0x01,
/// <summary>
/// Imported reference by name.
/// </summary>
ImportedReferenceByName = 0x02,
/// <summary>
/// Internal reference via entry table.
/// </summary>
InternalReferenceViaEntryTable = 0x03,
/// <summary>
/// Additive Fixup Flag.
/// </summary>
/// <remarks>
/// When set, an additive value trails the fixup record (before the optional
/// source offset list).
/// </remarks>
AdditiveFixupFlag = 0x04,
/// <summary>
/// Reserved. Must be zero.
/// </summary>
Reserved = 0x08,
/// <summary>
/// 32-bit Target Offset Flag.
/// </summary>
/// <remarks>
/// When set, the target offset is 32-bits, otherwise it is 16-bits.
/// </remarks>
ThirtyTwoBitTargetOffsetFlag = 0x10,
/// <summary>
/// 32-bit Additive Fixup Flag.
/// </summary>
/// When set, the additive value is 32-bits, otherwise it is 16-bits.
/// </remarks>
ThirtyTwoBitAdditiveFixupFlag = 0x20,
/// <summary>
/// 16-bit Object Number/Module Ordinal Flag.
/// </summary>
/// <remarks>
/// When set, the object number or module ordinal number is 16-bits,
/// otherwise it is 8-bits.
/// </remarks>
SixteenBitObjectNumberModuleOrdinalFlag = 0x40,
/// <summary>
/// 8-bit Ordinal Flag.
/// </summary>
/// <remarks>
/// When set, the ordinal number is 8-bits, otherwise it is 16-bits.
/// </remarks>
EightBitOrdinalFlag = 0x80,
}
[Flags]
public enum ForwarderFlags : byte
{
/// <summary>
/// Import by ordinal.
/// </summary>
ImportByOrdinal = 0x01,
/// <summary>
/// Reserved for future use; should be zero.
/// </summary>
Reserved = 0xF7,
}
[Flags]
public enum ModuleFlags : uint
{
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved0 = 0x00000001,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved1 = 0x00000002,
/// <summary>
/// Per-Process Library Initialization.
/// </summary>
/// <remarks>
/// The setting of this bit requires the EIP Object # and EIP fields
/// to have valid values. If the EIP Object # and EIP fields are
/// valid and this bit is NOT set, then Global Library Initialization
/// is assumed. Setting this bit for an EXE file is invalid.
/// </remarks>
Initialization = 0x00000004,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved3 = 0x00000008,
/// <summary>
/// Internal fixups for the module have been applied.
/// </summary>
/// <remarks>
/// The setting of this bit in a Linear Executable Module indicates that
/// each object of the module has a preferred load address specified in
/// the Object Table Reloc Base Addr. If the module's objects can not be
/// loaded at these preferred addresses, then the relocation records that
/// have been retained in the file data will be applied.
/// </remarks>
InternalFixupsApplied = 0x00000010,
/// <summary>
/// External fixups for the module have been applied.
/// </summary>
ExternalFixupsApplied = 0x00000020,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved6 = 0x00000040,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved7 = 0x00000080,
/// <summary>
/// Incompatible with PM windowing.
/// </summary>
IncompatibleWithPMWindowing = 0x00000100,
/// <summary>
/// Incompatible with PM windowing.
/// </summary>
CompatibleWithPMWindowing = 0x00000200,
/// <summary>
/// Uses PM windowing API.
/// </summary>
UsesPMWindowing = 0x00000300,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved10 = 0x00000400,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved11 = 0x00000800,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved12 = 0x00001000,
/// <summary>
/// Module is not loadable.
/// </summary>
/// <remarks>
/// When the 'Module is not loadable' flag is set, it indicates that
/// either errors were detected at link time or that the module is
/// being incrementally linked and therefore can't be loaded.
/// </remarks>
ModuleNotLoadable = 0x00002000,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved14 = 0x00004000,
/// <summary>
/// Module type mask.
/// </summary>
ModuleTypeMask = 0x00038000,
/// <summary>
/// Program module.
/// </summary>
/// <remarks>
/// A module can not contain dynamic links to other modules that have
/// the 'program module' type.
/// </remarks>
ProgramModule = 0x00000000,
/// <summary>
/// Library module.
/// </summary>
LibraryModule = 0x00008000,
/// <summary>
/// Protected Memory Library module.
/// </summary>
ProtectedMemoryLibraryModule = 0x00018000,
/// <summary>
/// Physical Device Driver module.
/// </summary>
PhysicalDeviceDriverModule = 0x00020000,
/// <summary>
/// Virtual Device Driver module.
/// </summary>
VirtualDeviceDriverModule = 0x00028000,
/// <summary>
/// Per-process Library Termination.
/// </summary>
/// <remarks>
/// The setting of this bit requires the EIP Object # and EIP fields
/// to have valid values. If the EIP Object # and EIP fields are
/// valid and this bit is NOT set, then Global Library Termination
/// is assumed. Setting this bit for an EXE file is invalid.
/// </remarks>
PerProcessLibraryTermination = 0x40000000,
}
[Flags]
public enum ObjectFlags : ushort
{
/// <summary>
/// Readable Object.
/// </summary>
ReadableObject = 0x0001,
/// <summary>
/// Writable Object.
/// </summary>
WritableObject = 0x0002,
/// <summary>
/// Executable Object.
/// </summary>
ExecutableObject = 0x0004,
// The readable, writable and executable flags provide support for all possible
// protections. In systems where all of these protections are not supported,
// the loader will be responsible for making the appropriate protection match
// for the system.
/// <summary>
/// Resource Object.
/// </summary>
ResourceObject = 0x0008,
/// <summary>
/// Discardable Object.
/// </summary>
DiscardableObject = 0x0010,
/// <summary>
/// Object is Shared.
/// </summary>
Shared = 0x0020,
/// <summary>
/// Object has Preload Pages.
/// </summary>
HasPreloadPages = 0x0040,
/// <summary>
/// Object has Invalid Pages.
/// </summary>
HasInvalidPages = 0x0080,
/// <summary>
/// Object has Zero Filled Pages.
/// </summary>
HasZeroFilledPages = 0x0100,
/// <summary>
/// Object is Resident (valid for VDDs, PDDs only).
/// </summary>
Resident = 0x0200,
/// <summary>
/// Object is Resident & Contiguous (VDDs, PDDs only).
/// </summary>
ResidentAndContiguous = 0x0300,
/// <summary>
/// Object is Resident & 'long-lockable' (VDDs, PDDs only).
/// </summary>
ResidentAndLongLockable = 0x0400,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved = 0x0800,
/// <summary>
/// 16:16 Alias Required (80x86 Specific).
/// </summary>
AliasRequired = 0x1000,
/// <summary>
/// Big/Default Bit Setting (80x86 Specific).
/// </summary>
BitSetting = 0x2000,
// The 'big/default' bit, for data segments, controls the setting of the
// Big bit in the segment descriptor. (The Big bit, or B-bit, determines
// whether ESP or SP is used as the stack pointer.) For code segments,
// this bit controls the setting of the Default bit in the segment
// descriptor. (The Default bit, or D-bit, determines whether the default
// word size is 32-bits or 16-bits. It also affects the interpretation of
// the instruction stream.)
/// <summary>
/// Object is conforming for code (80x86 Specific).
/// </summary>
Conforming = 0x4000,
/// <summary>
/// Object I/O privilege level (80x86 Specific). Only used for 16:16 Alias Objects.
/// </summary>
PrivilegeLevel = 0x8000,
}
[Flags]
public enum ObjectPageFlags : ushort
{
/// <summary>
/// Legal Physical Page in the module (Offset from Preload Page Section).
/// </summary>
LegalPhysicalPage = 0x0000,
/// <summary>
/// Iterated Data Page (Offset from Iterated Data Pages Section).
/// </summary>
IteratedDataPage = 0x0001,
/// <summary>
/// Invalid Page (zero).
/// </summary>
InvalidPage = 0x0002,
/// <summary>
/// Zero Filled Page (zero).
/// </summary>
ZeroFilledPage = 0x0003,
/// <summary>
/// Range of Pages.
/// </summary>
RangeOfPages = 0x0004,
}
public enum OperatingSystem : ushort
{
/// <summary>
/// Unknown (any "new-format" OS)
/// </summary>
Unknown = 0x00,
/// <summary>
/// OS/2 (default)
/// </summary>
OS2 = 0x01,
/// <summary>
/// Windows
/// </summary>
Windows = 0x02,
/// <summary>
/// DOS 4.x
/// </summary>
DOS4x = 0x03,
/// <summary>
/// Windows 386
/// </summary>
Windows386 = 0x04,
}
public enum WordOrder : byte
{
/// <summary>
/// little-endian
/// </summary>
LE = 0x00,
/// <summary>
/// big-endian
/// </summary>
/// <remarks>non-zero</remarks>
BE = 0x01,
}
}

View File

@@ -0,0 +1,101 @@
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The `LINEAR` executable-file header contains information that the loader requires for
/// segmented executable files. This information includes the linker version number, data
/// specified by linker, data specified by resource compiler, tables of segment data, tables
/// of resource data, and so on. The following illustrations shows the LE file header:
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
public class Executable
{
/// <summary>
/// MS-DOS executable stub
/// </summary>
public MSDOS.Executable Stub { get; set; }
/// <summary>
/// Information block
/// </summary>
public InformationBlock InformationBlock { get; set; }
/// <summary>
/// Object table
/// </summary>
public ObjectTableEntry[] ObjectTable { get; set; }
/// <summary>
/// Object page table
/// </summary>
public ObjectPageTableEntry[] ObjectPageTable { get; set; }
// TODO: Object iterate data map table [Does this exist?]
/// <summary>
/// Resource table
/// </summary>
public ResourceTableEntry[] ResourceTable { get; set; }
/// <summary>
/// Resident Name table
/// </summary>
public ResidentNameTableEntry[] ResidentNameTable { get; set; }
/// <summary>
/// Entry table
/// </summary>
public EntryTableEntry[] EntryTable { get; set; }
/// <summary>
/// Module format directives table (optional)
/// </summary>
public ModuleFormatDirectivesTableEntry[] ModuleFormatDirectivesTable { get; set; }
/// <summary>
/// Verify record directive table (optional)
/// </summary>
public VerifyRecordDirectiveTableEntry[] VerifyRecordDirectiveTable { get; set; }
/// <summary>
/// Per-Page checksum table
/// </summary>
public PerPageChecksumTableEntry[] PerPageChecksumTable { get; set; }
/// <summary>
/// Fix-up page table
/// </summary>
public FixupPageTableEntry[] FixupPageTable { get; set; }
/// <summary>
/// Fix-up record table
/// </summary>
public FixupRecordTableEntry[] FixupRecordTable { get; set; }
/// <summary>
/// Import module name table
/// </summary>
public ImportModuleNameTableEntry[] ImportModuleNameTable { get; set; }
/// <summary>
/// Import procedure name table
/// </summary>
public ImportModuleProcedureNameTableEntry[] ImportModuleProcedureNameTable { get; set; }
// TODO: Preload Pages
// TODO: Demand Load Pages
// TODO: Iterated Pages
/// <summary>
/// Non-Resident Name table
/// </summary>
public NonResidentNameTableEntry[] NonResidentNameTable { get; set; }
// TODO: Non-resident directives data (Undefined)
/// <summary>
/// Debug information
/// </summary>
public DebugInformation DebugInformation { get; set; }
}
}

View File

@@ -0,0 +1,36 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The Fixup Page Table provides a simple mapping of a logical page number
/// to an offset into the Fixup Record Table for that page.
///
/// This table is parallel to the Object Page Table, except that there is
/// one additional entry in this table to indicate the end of the Fixup
/// Record Table.
///
/// The fixup records are kept in order by logical page in the fixup record
/// table. This allows the end of each page's fixup records is defined by the
/// offset for the next logical page's fixup records. This last entry provides
/// support of this mechanism for the last page in the fixup page table.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public class FixupPageTableEntry
{
/// <summary>
/// Offset for fixup record for this page. (1 to n)
/// Offset to the end of the fixup records. (n + 1)
/// </summary>
/// <remarks>
/// This field specifies the offset, from the beginning of the fixup record
/// table, to the first fixup record for this page. (1 to n)
///
/// This field specifies the offset following the last fixup record in the
/// fixup record table. This is the last entry in the fixup page table. (n + 1)
/// </remarks>
public uint Offset;
}
}

View File

@@ -0,0 +1,363 @@
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The Fixup Record Table contains entries for all fixups in the linear EXE module.
/// The fixup records for a logical page are grouped together and kept in sorted
/// order by logical page number. The fixups for each page are further sorted such
/// that all external fixups and internal selector/pointer fixups come before
/// internal non-selector/non-pointer fixups. This allows the loader to ignore
/// internal fixups if the loader is able to load all objects at the addresses
/// specified in the object table.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
public class FixupRecordTableEntry
{
/// <summary>
/// Source type.
/// </summary>
/// <remarks>
/// The source type specifies the size and type of the fixup to be performed
/// on the fixup source.
/// </remarks>
public FixupRecordSourceType SourceType;
/// <summary>
/// Target Flags.
/// </summary>
/// <remarks>
/// The target flags specify how the target information is interpreted.
/// </remarks>
public FixupRecordTargetFlags TargetFlags;
#region Source List Flag
#region Set
/// <summary>
/// Source offset.
/// </summary>
/// <remarks>
/// This field contains either an offset or a count depending on the Source
/// List Flag. If the Source List Flag is set, a list of source offsets
/// follows the additive field and this field contains the count of the
/// entries in the source offset list. Otherwise, this is the single source
/// offset for the fixup. Source offsets are relative to the beginning of
/// the page where the fixup is to be made.
///
/// Note that for fixups that cross page boundaries, a separate fixup record
/// is specified for each page. An offset is still used for the 2nd page but
/// it now becomes a negative offset since the fixup originated on the
/// preceding page. (For example, if only the last one byte of a 32-bit
/// address is on the page to be fixed up, then the offset would have a value
/// of -3.)
/// </remarks>
public ushort SourceOffset;
#endregion
#region Unset
/// <summary>
/// Source offset list count.
/// </summary>
/// <remarks>
/// This field contains either an offset or a count depending on the Source
/// List Flag. If the Source List Flag is set, a list of source offsets
/// follows the additive field and this field contains the count of the
/// entries in the source offset list. Otherwise, this is the single source
/// offset for the fixup. Source offsets are relative to the beginning of
/// the page where the fixup is to be made.
///
/// Note that for fixups that cross page boundaries, a separate fixup record
/// is specified for each page. An offset is still used for the 2nd page but
/// it now becomes a negative offset since the fixup originated on the
/// preceding page. (For example, if only the last one byte of a 32-bit
/// address is on the page to be fixed up, then the offset would have a value
/// of -3.)
/// </remarks>
public byte SourceOffsetListCount;
#endregion
#endregion
#region OBJECT / TRGOFF
#region 16-bit Object Number/Module Ordinal Flag
#region Set
/// <summary>
/// Target object number.
/// </summary>
/// <remarks>
/// This field is an index into the current module's Object Table to specify
/// the target Object. It is a Byte value when the '16-bit Object Number/Module
/// Ordinal Flag' bit in the target flags field is clear and a Word value when
/// the bit is set.
/// </remarks>
public ushort TargetObjectNumberWORD;
#endregion
#region Unset
/// <summary>
/// Target object number.
/// </summary>
/// <remarks>
/// This field is an index into the current module's Object Table to specify
/// the target Object. It is a Byte value when the '16-bit Object Number/Module
/// Ordinal Flag' bit in the target flags field is clear and a Word value when
/// the bit is set.
/// </remarks>
public byte TargetObjectNumberByte;
#endregion
#endregion
#region 32-bit Target Offset Flag
#region Set
/// <summary>
/// Target offset.
/// </summary>
/// <remarks>
/// This field is an offset into the specified target Object. It is not
/// present when the Source Type specifies a 16-bit Selector fixup. It
/// is a Word value when the '32-bit Target Offset Flag' bit in the target
/// flags field is clear and a Dword value when the bit is set.
/// </remarks>
public uint TargetOffsetDWORD;
#endregion
#region Unset
/// <summary>
/// Target offset.
/// </summary>
/// <remarks>
/// This field is an offset into the specified target Object. It is not
/// present when the Source Type specifies a 16-bit Selector fixup. It
/// is a Word value when the '32-bit Target Offset Flag' bit in the target
/// flags field is clear and a Dword value when the bit is set.
/// </remarks>
public ushort TargetOffsetWORD;
#endregion
#endregion
#endregion
#region 16-bit Object Number/Module Ordinal Flag [Incompatible with OBJECT / TRGOFF]
#region Set
/// <summary>
/// Ordinal index into the Import Module Name Table.
/// </summary>
/// <remarks>
/// This value is an ordered index in to the Import Module Name Table for
/// the module containing the procedure entry point. It is a Byte value
/// when the '16-bit Object Number/Module Ordinal' Flag bit in the target
/// flags field is clear and a Word value when the bit is set. The loader
/// creates a table of pointers with each pointer in the table corresponds
/// to the modules named in the Import Module Name Table. This value is used
/// by the loader to index into this table created by the loader to locate
/// the referenced module.
/// </remarks>
public ushort OrdinalIndexImportModuleNameTableWORD;
#endregion
#region Unset
/// <summary>
/// Ordinal index into the Import Module Name Table.
/// </summary>
/// <remarks>
/// This value is an ordered index in to the Import Module Name Table for
/// the module containing the procedure entry point. It is a Byte value
/// when the '16-bit Object Number/Module Ordinal' Flag bit in the target
/// flags field is clear and a Word value when the bit is set. The loader
/// creates a table of pointers with each pointer in the table corresponds
/// to the modules named in the Import Module Name Table. This value is used
/// by the loader to index into this table created by the loader to locate
/// the referenced module.
/// </remarks>
public byte OrdinalIndexImportModuleNameTableByte;
#endregion
#endregion
#region MOD ORD# / PROCEDURE NAME OFFSET / ADDITIVE
#region 32-bit Target Offset Flag
#region Set
/// <summary>
/// Offset into the Import Procedure Name Table.
/// </summary>
/// <remarks>
/// This field is an offset into the Import Procedure Name Table. It is
/// a Word value when the '32-bit Target Offset Flag' bit in the target
/// flags field is clear and a Dword value when the bit is set.
/// </remarks>
public uint OffsetImportProcedureNameTableDWORD;
#endregion
#region Unset
/// <summary>
/// Offset into the Import Procedure Name Table.
/// </summary>
/// <remarks>
/// This field is an offset into the Import Procedure Name Table. It is
/// a Word value when the '32-bit Target Offset Flag' bit in the target
/// flags field is clear and a Dword value when the bit is set.
/// </remarks>
public ushort OffsetImportProcedureNameTableWORD;
#endregion
#endregion
#endregion
#region MOD ORD# / IMPORT ORD / ADDITIVE
#region 8-bit Ordinal Flag
#region Set
/// <summary>
/// Imported ordinal number.
/// </summary>
/// <remarks>
/// This is the imported procedure's ordinal number. It is a Byte value when the
/// '8-bit Ordinal' bit in the target flags field is set. Otherwise it is a Word value
/// when the '32-bit Target Offset Flag' bit in the target flags field is clear and a
/// Dword value when the bit is set.
/// </remarks>
public byte ImportedOrdinalNumberByte;
#endregion
#region Unset
#region 32-bit Target Offset Flag
#region Set
/// <summary>
/// Imported ordinal number.
/// </summary>
/// <remarks>
/// This is the imported procedure's ordinal number. It is a Byte value when the
/// '8-bit Ordinal' bit in the target flags field is set. Otherwise it is a Word value
/// when the '32-bit Target Offset Flag' bit in the target flags field is clear and a
/// Dword value when the bit is set.
/// </remarks>
public uint ImportedOrdinalNumberDWORD;
#endregion
#region Unset
/// <summary>
/// Imported ordinal number.
/// </summary>
/// <remarks>
/// This is the imported procedure's ordinal number. It is a Byte value when the
/// '8-bit Ordinal' bit in the target flags field is set. Otherwise it is a Word value
/// when the '32-bit Target Offset Flag' bit in the target flags field is clear and a
/// Dword value when the bit is set.
/// </remarks>
public uint ImportedOrdinalNumberWORD;
#endregion
#endregion
#endregion
#endregion
#endregion
#region Additive Fixup Flag [Incompatible with OBJECT / TRGOFF]
#region Set
#region 32-bit Additive Fixup Flag
#region Set
/// <summary>
/// Additive fixup value.
/// </summary>
/// <remarks>
/// This field exists in the fixup record only when the 'Additive Fixup Flag'
/// bit in the target flags field is set. When the 'Additive Fixup Flag' is
/// clear the fixup record does not contain this field and is immediately
/// followed by the next fixup record (or by the source offset list for this
/// fixup record).
///
/// This value is added to the address derived from the target entry point.
/// This field is a Word value when the '32-bit Additive Flag' bit in the
/// target flags field is clear and a Dword value when the bit is set.
/// </remarks>
public uint AdditiveFixupValueDWORD;
#endregion
#region Unset
/// <summary>
/// Additive fixup value.
/// </summary>
/// <remarks>
/// This field exists in the fixup record only when the 'Additive Fixup Flag'
/// bit in the target flags field is set. When the 'Additive Fixup Flag' is
/// clear the fixup record does not contain this field and is immediately
/// followed by the next fixup record (or by the source offset list for this
/// fixup record).
///
/// This value is added to the address derived from the target entry point.
/// This field is a Word value when the '32-bit Additive Flag' bit in the
/// target flags field is clear and a Dword value when the bit is set.
/// </remarks>
public ushort AdditiveFixupValueWORD;
#endregion
#endregion
#endregion
#endregion
#region SCROFFn
/// <summary>
/// Source offset list.
/// </summary>
/// <remarks>
/// This list is present if the Source List Flag is set in the Target Flags field.
/// The number of entries in the source offset list is defined in the SRCOFF/CNT
/// field. The source offsets are relative to the beginning of the page where the
/// fixups are to be made.
/// </remarks>
public ushort[] SourceOffsetList;
#endregion
}
}

View File

@@ -0,0 +1,41 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The import module name table defines the module name strings imported through
/// dynamic link references. These strings are referenced through the imported
/// relocation fixups.
///
/// To determine the length of the import module name table subtract the import
/// module name table offset from the import procedure name table offset. These
/// values are located in the linear EXE header. The end of the import module
/// name table is not terminated by a special character, it is followed directly
/// by the import procedure name table.
///
/// The strings are CASE SENSITIVE and NOT NULL TERMINATED.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public class ImportModuleNameTableEntry
{
/// <summary>
/// String Length.
/// </summary>
/// <remarks>
/// This defines the length of the string in bytes. The length of each
/// ascii name string is limited to 127 characters.
/// </remarks>
public byte Length;
/// <summary>
/// ASCII String.
/// </summary>
/// <remarks>
/// This is a variable length string with it's length defined in bytes by
/// the LEN field. The string is case sensitive and is not null terminated.
/// </remarks>
public byte[] Name;
}
}

View File

@@ -0,0 +1,49 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The import procedure name table defines the procedure name strings imported
/// by this module through dynamic link references. These strings are referenced
/// through the imported relocation fixups.
///
/// To determine the length of the import procedure name table add the fixup
/// section size to the fixup page table offset, this computes the offset to
/// the end of the fixup section, then subtract the import procedure name table
/// offset. These values are located in the linear EXE header. The import
/// procedure name table is followed by the data pages section. Since the data
/// pages section is aligned on a 'page size' boundary, padded space may exist
/// between the last import name string and the first page in the data pages
/// section. If this padded space exists it will be zero filled.
///
/// The strings are CASE SENSITIVE and NOT NULL TERMINATED.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public class ImportModuleProcedureNameTableEntry
{
/// <summary>
/// String Length.
/// </summary>
/// <remarks>
/// This defines the length of the string in bytes. The length of each
/// ascii name string is limited to 127 characters.
///
/// The high bit in the LEN field (bit 7) is defined as an Overload bit.
/// This bit signifies that additional information is contained in the
/// linear EXE module and will be used in the future for parameter type
/// checking.
/// </remarks>
public byte Length;
/// <summary>
/// ASCII String.
/// </summary>
/// <remarks>
/// This is a variable length string with it's length defined in bytes by
/// the LEN field. The string is case sensitive and is not null terminated.
/// </remarks>
public byte[] Name;
}
}

View File

@@ -0,0 +1,426 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The `information block` in the LE header contains the linker version number,
/// length of various tables that further describe the executable file, the
/// offsets from the beginning of the header to the beginning of these tables,
/// the heap and stack sizes, and so on. The following list summarizes the
/// contents of the header `information block` (the locations are relative to
/// the beginning of the block):
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public class InformationBlock
{
/// <summary>
/// Specifies the signature word
/// 'LE' (4Ch 45H)
/// 'LX' (4Ch 58H)
/// </summary>
/// <remarks>
/// The signature word is used by the loader to identify the EXE
/// file as a valid 32-bit Linear Executable Module Format.
/// </remarks>
public char[] Signature;
/// <summary>
/// Byte Ordering.
/// </summary>
/// <remarks>
/// This byte specifies the byte ordering for the linear EXE format.
/// </remarks>
public ByteOrder ByteOrder;
/// <summary>
/// Word Ordering.
/// </summary>
/// <remarks>
/// This byte specifies the Word ordering for the linear EXE format.
/// </remarks>
public WordOrder WordOrder;
/// <summary>
/// Linear EXE Format Level.
/// </summary>
/// <remarks>
/// The Linear EXE Format Level is set to 0 for the initial version of the
/// 32-bit linear EXE format. Each incompatible change to the linear EXE
/// format must increment this value. This allows the system to recognized
/// future EXE file versions so that an appropriate error message may be
/// displayed if an attempt is made to load them.
/// </remarks>
public uint ExecutableFormatLevel;
/// <summary>
/// Module CPU Type.
/// </summary>
/// <remarks>
/// This field specifies the type of CPU required by this module to run.
/// </remarks>
public CPUType CPUType;
/// <summary>
/// Module OS Type.
/// </summary>
/// <remarks>
/// This field specifies the type of Operating system required to run this module.
/// </remarks>
public OperatingSystem ModuleOS;
/// <summary>
/// Module version
/// </summary>
/// <remarks>
/// This is useful for differentiating between revisions of dynamic linked modules.
/// This value is specified at link time by the user.
/// </remarks>
public uint ModuleVersion;
/// <summary>
/// Module type flags
/// </summary>
public ModuleFlags ModuleTypeFlags;
/// <summary>
/// Number of pages in module.
/// </summary>
/// <remarks>
/// This field specifies the number of pages physically contained in this module.
/// In other words, pages containing either enumerated or iterated data, or
/// zero-fill pages that have relocations, not invalid or zero-fill pages implied
/// by the Virtual Size in the Object Table being larger than the number of pages
/// actually in the linear EXE file. These pages are contained in the 'preload
/// pages', 'demand load pages' and 'iterated data pages' sections of the linear
/// EXE module. This is used to determine the size of the page information tables
/// in the linear EXE module.
/// </remarks>
public uint ModuleNumberPages;
/// <summary>
/// The Object number to which the Entry Address is relative.
/// </summary>
/// <remarks>
/// This specifies the object to which the Entry Address is relative. This must be
/// a nonzero value for a program module to be correctly loaded. A zero value for
/// a library module indicates that no library entry routine exists. If this value
/// is zero, then both the Per-process Library Initialization bit and the Per-process
/// Library Termination bit must be clear in the module flags, or else the loader
/// will fail to load the module. Further, if the Per-process Library Termination bit
/// is set, then the object to which this field refers must be a 32-bit object (i.e.,
/// the Big/Default bit must be set in the object flags; see below).
/// </remarks>
public uint InitialObjectCS;
/// <summary>
/// Entry Address of module.
/// </summary>
/// <remarks>
/// The Entry Address is the starting address for program modules and the library
/// initialization and Library termination address for library modules.
/// </remarks>
public uint InitialEIP;
/// <summary>
/// The Object number to which the ESP is relative.
/// </summary>
/// <remarks>
/// This specifies the object to which the starting ESP is relative. This must be a
/// nonzero value for a program module to be correctly loaded. This field is ignored
/// for a library module.
/// </remarks>
public uint InitialObjectSS;
/// <summary>
/// Starting stack address of module.
/// </summary>
/// <remarks>
/// The ESP defines the starting stack pointer address for program modules. A zero
/// value in this field indicates that the stack pointer is to be initialized to the
/// highest address/offset in the object. This field is ignored for a library module.
/// </remarks>
public uint InitialESP;
/// <summary>
/// The size of one page for this system.
/// </summary>
/// <remarks>
/// This field specifies the page size used by the linear EXE format and the system.
/// For the initial version of this linear EXE format the page size is 4Kbytes.
/// (The 4K page size is specified by a value of 4096 in this field.)
/// </remarks>
public uint MemoryPageSize;
/// <summary>
/// The shift left bits for page offsets.
/// </summary>
/// <remarks>
/// This field gives the number of bit positions to shift left when interpreting
/// the Object Page Table entries' page offset field. This determines the alignment
/// of the page information in the file. For example, a value of 4 in this field
/// would align all pages in the Data Pages and Iterated Pages sections on 16 byte
/// (paragraph) boundaries. A Page Offset Shift of 9 would align all pages on a
/// 512 byte (disk sector) basis. The default value for this field is 12 (decimal),
/// which give a 4096 byte alignment. All other offsets are byte aligned.
/// </remarks>
public uint BytesOnLastPage;
/// <summary>
/// Total size of the fixup information in bytes.
/// </summary>
/// <remarks>
/// This includes the following 4 tables:
/// - Fixup Page Table
/// - Fixup Record Table
/// - Import Module name Table
/// - Import Procedure Name Table
/// </remarks>
public uint FixupSectionSize;
/// <summary>
/// Checksum for fixup information.
/// </summary>
/// <remarks>
/// This is a cryptographic checksum covering all of the fixup information. The
/// checksum for the fixup information is kept separate because the fixup data is
/// not always loaded into main memory with the 'loader section'. If the checksum
/// feature is not implemented, then the linker will set these fields to zero.
/// </remarks>
public uint FixupSectionChecksum;
/// <summary>
/// Size of memory resident tables.
/// </summary>
/// <remarks>
/// This is the total size in bytes of the tables required to be memory resident
/// for the module, while the module is in use. This total size includes all
/// tables from the Object Table down to and including the Per-Page Checksum Table.
/// </remarks>
public uint LoaderSectionSize;
/// <summary>
/// Checksum for loader section.
/// </summary>
/// <remarks>
/// This is a cryptographic checksum covering all of the loader section information.
/// If the checksum feature is not implemented, then the linker will set these fields
/// to zero.
/// </remarks>
public uint LoaderSectionChecksum;
/// <summary>
/// Object Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ObjectTableOffset;
/// <summary>
/// Object Table Count.
/// </summary>
/// <remarks>
/// This defines the number of entries in Object Table.
/// </remarks>
public uint ObjectTableCount;
/// <summary>
/// Object Page Table offset
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ObjectPageMapOffset;
/// <summary>
/// Object Iterated Pages offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ObjectIterateDataMapOffset;
/// <summary>
/// Resource Table offset
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ResourceTableOffset;
/// <summary>
/// Number of entries in Resource Table.
/// </summary>
public uint ResourceTableCount;
/// <summary>
/// Resident Name Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ResidentNamesTableOffset;
/// <summary>
/// Entry Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint EntryTableOffset;
/// <summary>
/// Module Format Directives Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ModuleDirectivesTableOffset;
/// <summary>
/// Number of Module Format Directives in the Table.
/// </summary>
/// <remarks>
/// This field specifies the number of entries in the
/// Module Format Directives Table.
/// </remarks>
public uint ModuleDirectivesCount;
/// <summary>
/// Fixup Page Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint FixupPageTableOffset;
/// <summary>
/// Fixup Record Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint FixupRecordTableOffset;
/// <summary>
/// Import Module Name Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ImportedModulesNameTableOffset;
/// <summary>
/// The number of entries in the Import Module Name Table.
/// </summary>
public uint ImportedModulesCount;
/// <summary>
/// Import Procedure Name Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ImportProcedureNameTableOffset;
/// <summary>
/// Per-page Checksum Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint PerPageChecksumTableOffset;
/// <summary>
/// Data Pages Offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the EXE file.
/// </remarks>
public uint DataPagesOffset;
/// <summary>
/// Number of Preload pages for this module.
/// </summary>
/// <remarks>
/// Note that OS/2 2.0 does not respect the preload of pages as specified
/// in the executable file for performance reasons.
/// </remarks>
public uint PreloadPageCount;
/// <summary>
/// Non-Resident Name Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the EXE file.
/// </remarks>
public uint NonResidentNamesTableOffset;
/// <summary>
/// Number of bytes in the Non-resident name table.
/// </summary>
public uint NonResidentNamesTableLength;
/// <summary>
/// Non-Resident Name Table Checksum.
/// </summary>
/// <remarks>
/// This is a cryptographic checksum of the Non-Resident Name Table.
/// </remarks>
public uint NonResidentNamesTableChecksum;
/// <summary>
/// The Auto Data Segment Object number.
/// </summary>
/// <remarks>
/// This is the object number for the Auto Data Segment used by 16-bit modules.
/// This field is supported for 16-bit compatibility only and is not used by
/// 32-bit modules.
/// </remarks>
public uint AutomaticDataObject;
/// <summary>
/// Debug Information offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint DebugInformationOffset;
/// <summary>
/// Debug Information length.
/// </summary>
/// <remarks>
/// The length of the debug information in bytes.
/// </remarks>
public uint DebugInformationLength;
/// <summary>
/// Instance pages in preload section.
/// </summary>
/// <remarks>
/// The number of instance data pages found in the preload section.
/// </remarks>
public uint PreloadInstancePagesNumber;
/// <summary>
/// Instance pages in demand section.
/// </summary>
/// <remarks>
/// The number of instance data pages found in the demand section.
/// </remarks>
public uint DemandInstancePagesNumber;
/// <summary>
/// Heap size added to the Auto DS Object.
/// </summary>
/// <remarks>
/// The heap size is the number of bytes added to the Auto Data Segment
/// by the loader. This field is supported for 16-bit compatibility only and
/// is not used by 32-bit modules.
/// </remarks>
public uint ExtraHeapAllocation;
}
}

View File

@@ -0,0 +1,48 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The Module Format Directives Table is an optional table that allows additional
/// options to be specified. It also allows for the extension of the linear EXE
/// format by allowing additional tables of information to be added to the linear
/// EXE module without affecting the format of the linear EXE header. Likewise,
/// module format directives provide a place in the linear EXE module for
/// 'temporary tables' of information, such as incremental linking information
/// and statistic information gathered on the module. When there are no module
/// format directives for a linear EXE module, the fields in the linear EXE header
/// referencing the module format directives table are zero.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public class ModuleFormatDirectivesTableEntry
{
/// <summary>
/// Directive number.
/// </summary>
/// <remarks>
/// The directive number specifies the type of directive defined. This can be
/// used to determine the format of the information in the directive data.
/// </remarks>
public DirectiveNumber DirectiveNumber;
/// <summary>
/// Directive data length.
/// </summary>
/// <remarks>
/// This specifies the length in byte of the directive data for this directive number.
/// </remarks>
public ushort DirectiveDataLength;
/// <summary>
/// Directive data offset.
/// </summary>
/// <remarks>
/// This is the offset to the directive data for this directive number. It is relative
/// to beginning of linear EXE header for a resident table, and relative to the
/// beginning of the EXE file for non-resident tables.
/// </remarks>
public uint DirectiveDataOffset;
}
}

View File

@@ -0,0 +1,64 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The resident and non-resident name tables define the ASCII names and ordinal
/// numbers for exported entries in the module. In addition the first entry in
/// the resident name table contains the module name. These tables are used to
/// translate a procedure name string into an ordinal number by searching for a
/// matching name string. The ordinal number is used to locate the entry point
/// information in the entry table.
///
/// The resident name table is kept resident in system memory while the module is
/// loaded.It is intended to contain the exported entry point names that are
/// frequently dynamically linked to by name.Non-resident names are not kept in
/// memory and are read from the EXE file when a dynamic link reference is made.
/// Exported entry point names that are infrequently dynamically linked to by name
/// or are commonly referenced by ordinal number should be placed in the
/// non-resident name table.The trade off made for references by name is performance
/// vs memory usage.
///
/// Import references by name require these tables to be searched to obtain the entry
/// point ordinal number.Import references by ordinal number provide the fastest
/// lookup since the search of these tables is not required.
///
/// The strings are CASE SENSITIVE and are NOT NULL TERMINATED.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public class NonResidentNameTableEntry
{
/// <summary>
/// String Length.
/// </summary>
/// <remarks>
/// This defines the length of the string in bytes. A zero length indicates there are
/// no more entries in table. The length of each ascii name string is limited to 127
/// characters.
///
/// The high bit in the LEN field (bit 7) is defined as an Overload bit. This bit
/// signifies that additional information is contained in the linear EXE module and
/// will be used in the future for parameter type checking.
/// </remarks>
public byte Length;
/// <summary>
/// ASCII String.
/// </summary>
/// <remarks>
/// This is a variable length string with it's length defined in bytes by the LEN field.
/// The string is case case sensitive and is not null terminated.
/// </remarks>
public byte[] Name;
/// <summary>
/// Ordinal number.
/// </summary>
/// <remarks>
/// The ordinal number in an ordered index into the entry table for this entry point.
/// </remarks>
public ushort OrdinalNumber;
}
}

View File

@@ -0,0 +1,60 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The Object page table provides information about a logical page in an object.
/// A logical page may be an enumerated page, a pseudo page or an iterated page.
/// The structure of the object page table in conjunction with the structure of
/// the object table allows for efficient access of a page when a page fault occurs,
/// while still allowing the physical page data to be located in the preload page,
/// demand load page or iterated data page sections in the linear EXE module. The
/// logical page entries in the Object Page Table are numbered starting from one.
/// The Object Page Table is parallel to the Fixup Page Table as they are both
/// indexed by the logical page number.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public class ObjectPageTableEntry
{
/// <summary>
/// Offset to the page data in the EXE file.
/// </summary>
/// <remarks>
/// This field, when bit shifted left by the PAGE OFFSET SHIFT from the module
/// header, specifies the offset from the beginning of the Preload Page section
/// of the physical page data in the EXE file that corresponds to this logical
/// page entry. The page data may reside in the Preload Pages, Demand Load Pages
/// or the Iterated Data Pages sections.
///
/// If the FLAGS field specifies that this is a zero-Filled page then the PAGE
/// DATA OFFSET field will contain a 0.
///
/// If the logical page is specified as an iterated data page, as indicated by
/// the FLAGS field, then this field specifies the offset into the Iterated Data
/// Pages section.
///
/// The logical page number (Object Page Table index), is used to index the Fixup
/// Page Table to find any fixups associated with the logical page.
/// </remarks>
public uint PageDataOffset;
/// <summary>
/// Number of bytes of data for this page.
/// </summary>
/// <remarks>
/// This field specifies the actual number of bytes that represent the page in the
/// file. If the PAGE SIZE field from the module header is greater than the value
/// of this field and the FLAGS field indicates a Legal Physical Page, the remaining
/// bytes are to be filled with zeros. If the FLAGS field indicates an Iterated Data
/// Page, the iterated data records will completely fill out the remainder.
/// </remarks>
public ushort DataSize;
/// <summary>
/// Attributes specifying characteristics of this logical page.
/// </summary>
public ObjectPageFlags Flags;
}
}

View File

@@ -0,0 +1,82 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The object table contains information that describes each segment in
/// an executable file. This information includes segment length, segment
/// type, and segment-relocation data. The following list summarizes the
/// values found in in the segment table (the locations are relative to
/// the beginning of each entry):
/// </summary>
/// <remarks>
/// Entries in the Object Table are numbered starting from one.
/// </remarks>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public class ObjectTableEntry
{
/// <summary>
/// Virtual memory size.
/// </summary>
/// <remarks>
/// This is the size of the object that will be allocated when the object
/// is loaded. The object's virtual size (rounded up to the page size value)
/// must be greater than or equal to the total size of the pages in the EXE
/// file for the object. This memory size must also be large enough to
/// contain all of the iterated data and uninitialized data in the EXE file.
/// </remarks>
public uint VirtualSegmentSize;
/// <summary>
/// Relocation Base Address.
/// </summary>
/// <remarks>
/// The relocation base address the object is currently relocated to. If the
/// internal relocation fixups for the module have been removed, this is the
/// address the object will be allocated at by the loader.
/// </remarks>
public uint RelocationBaseAddress;
/// <summary>
/// Flag bits for the object.
/// </summary>
public ObjectFlags ObjectFlags;
/// <summary>
/// Object Page Table Index.
/// </summary>
/// <remarks>
/// This specifies the number of the first object page table entry for this object.
/// The object page table specifies where in the EXE file a page can be found for
/// a given object and specifies per-page attributes.
///
/// The object table entries are ordered by logical page in the object table. In
/// other words the object table entries are sorted based on the object page table
/// index value.
/// </remarks>
public uint PageTableIndex;
/// <summary>
/// # of object page table entries for this object.
/// </summary>
/// <remarks>
/// Any logical pages at the end of an object that do not have an entry in the object
/// page table associated with them are handled as zero filled or invalid pages by
/// the loader.
///
/// When the last logical pages of an object are not specified with an object page
/// table entry, they are treated as either zero filled pages or invalid pages based
/// on the last entry in the object page table for that object. If the last entry
/// was neither a zero filled or invalid page, then the additional pages are treated
/// as zero filled pages.
/// </remarks>
public uint PageTableEntries;
/// <summary>
/// Reserved for future use. Must be set to zero.
/// </summary>
public uint Reserved;
}
}

View File

@@ -0,0 +1,24 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The Per-Page Checksum table provides space for a cryptographic checksum for
/// each physical page in the EXE file.
///
/// The checksum table is arranged such that the first entry in the table corresponds
/// to the first logical page of code/data in the EXE file (usually a preload page)
/// and the last entry corresponds to the last logical page in the EXE file (usually
/// a iterated data page).
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public class PerPageChecksumTableEntry
{
/// <summary>
/// Cryptographic checksum.
/// </summary>
public uint Checksum;
}
}

View File

@@ -0,0 +1,64 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The resident and non-resident name tables define the ASCII names and ordinal
/// numbers for exported entries in the module. In addition the first entry in
/// the resident name table contains the module name. These tables are used to
/// translate a procedure name string into an ordinal number by searching for a
/// matching name string. The ordinal number is used to locate the entry point
/// information in the entry table.
///
/// The resident name table is kept resident in system memory while the module is
/// loaded.It is intended to contain the exported entry point names that are
/// frequently dynamically linked to by name.Non-resident names are not kept in
/// memory and are read from the EXE file when a dynamic link reference is made.
/// Exported entry point names that are infrequently dynamically linked to by name
/// or are commonly referenced by ordinal number should be placed in the
/// non-resident name table.The trade off made for references by name is performance
/// vs memory usage.
///
/// Import references by name require these tables to be searched to obtain the entry
/// point ordinal number.Import references by ordinal number provide the fastest
/// lookup since the search of these tables is not required.
///
/// The strings are CASE SENSITIVE and are NOT NULL TERMINATED.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public class ResidentNameTableEntry
{
/// <summary>
/// String Length.
/// </summary>
/// <remarks>
/// This defines the length of the string in bytes. A zero length indicates there are
/// no more entries in table. The length of each ascii name string is limited to 127
/// characters.
///
/// The high bit in the LEN field (bit 7) is defined as an Overload bit. This bit
/// signifies that additional information is contained in the linear EXE module and
/// will be used in the future for parameter type checking.
/// </remarks>
public byte Length;
/// <summary>
/// ASCII String.
/// </summary>
/// <remarks>
/// This is a variable length string with it's length defined in bytes by the LEN field.
/// The string is case case sensitive and is not null terminated.
/// </remarks>
public byte[] Name;
/// <summary>
/// Ordinal number.
/// </summary>
/// <remarks>
/// The ordinal number in an ordered index into the entry table for this entry point.
/// </remarks>
public ushort OrdinalNumber;
}
}

View File

@@ -0,0 +1,52 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The resource table is an array of resource table entries. Each resource table
/// entry contains a type ID and name ID. These entries are used to locate resource
/// objects contained in the Object table. The number of entries in the resource
/// table is defined by the Resource Table Count located in the linear EXE header.
/// More than one resource may be contained within a single object. Resource table
/// entries are in a sorted order, (ascending, by Resource Name ID within the
/// Resource Type ID). This allows the DosGetResource API function to use a binary
/// search when looking up a resource in a 32-bit module instead of the linear search
/// being used in the current 16-bit module.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public class ResourceTableEntry
{
/// <summary>
/// Resource type ID.
/// </summary>
/// <remarks>
/// The type of resources are:
/// - BTMP = Bitmap
/// - EMSG = Error message string
/// - FONT = Fonts
/// </remarks>
public ushort TypeID;
/// <summary>
/// An ID used as a name for the resource when referred to.
/// </summary>
public ushort NameID;
/// <summary>
/// The number of bytes the resource consists of.
/// </summary>
public uint ResourceSize;
/// <summary>
/// The number of the object which contains the resource.
/// </summary>
public ushort ObjectNumber;
/// <summary>
/// The offset within the specified object where the resource begins.
/// </summary>
public uint Offset;
}
}

View File

@@ -0,0 +1,85 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.LinearExecutable
{
/// <summary>
/// The Verify Record Directive Table is an optional table. It maintains a record
/// of the pages in the EXE file that have been fixed up and written back to the
/// original linear EXE module, along with the module dependencies used to perform
/// these fixups. This table provides an efficient means for verifying the virtual
/// addresses required for the fixed up pages when the module is loaded.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public class VerifyRecordDirectiveTableEntry
{
/// <summary>
/// Number of module dependencies.
/// </summary>
/// <remarks>
/// This field specifies how many entries there are in the verify record
/// directive table. This is equal to the number of modules referenced by
/// this module.
/// </remarks>
public ushort EntryCount;
/// <summary>
/// Ordinal index into the Import Module Name Table.
/// </summary>
/// <remarks>
/// This value is an ordered index in to the Import Module Name Table for
/// the referenced module.
/// </remarks>
public ushort OrdinalIndex;
/// <summary>
/// Module Version.
/// </summary>
/// <remarks>
/// This is the version of the referenced module that the fixups were
/// originally performed.This is used to insure the same version of the
/// referenced module is loaded that was fixed up in this module and
/// therefore the fixups are still correct. This requires the version
/// number in a module to be incremented anytime the entry point offsets
/// change.
/// </remarks>
public ushort Version;
/// <summary>
/// Module # of Object Entries.
/// </summary>
/// <remarks>
/// This field is used to identify the number of object verify entries
/// that follow for the referenced module.
/// </remarks>
public ushort ObjectEntriesCount;
/// <summary>
/// Object # in Module.
/// </summary>
/// <remarks>
/// This field specifies the object number in the referenced module that
/// is being verified.
/// </remarks>
public ushort ObjectNumberInModule;
/// <summary>
/// Object load base address.
/// </summary>
/// <remarks>
/// This is the address that the object was loaded at when the fixups were
/// performed.
/// </remarks>
public ushort ObjectLoadBaseAddress;
/// <summary>
/// Object virtual address size.
/// </summary>
/// <remarks>
/// This field specifies the total amount of virtual memory required for
/// this object.
/// </remarks>
public ushort ObjectVirtualAddressSize;
}
}

View File

@@ -0,0 +1,29 @@
namespace BurnOutSharp.Models.MSDOS
{
/// <summary>
/// The MS-DOS EXE format, also known as MZ after its signature (the initials of Microsoft engineer Mark Zbykowski),
/// was introduced with MS-DOS 2.0 (version 1.0 only sported the simple COM format). It is designed as a relocatable
/// executable running under real mode. As such, only DOS and Windows 9x can use this format natively, but there are
/// several free DOS emulators (e.g., DOSBox) that support it and that run under various operating systems (e.g.,
/// Linux, Amiga, Windows NT, etc.). Although they can exist on their own, MZ executables are embedded in all NE, LE,
/// and PE executables, usually as stubs so that when they are ran under DOS, they display a warning.
/// </summary>
/// <see href="https://wiki.osdev.org/MZ"/>
public class Executable
{
/// <summary>
/// MS-DOS executable header
/// </summary>
public ExecutableHeader Header { get; set; }
/// <summary>
/// After loading the executable into memory, the program loader goes through
/// every entry in relocation table. For each relocation entry, the loader
/// adds the start segment address into word value pointed to by the
/// segment:offset pair. So, for example, a relocation entry 0001:001A will
/// make the loader add start segment address to the value at offset
/// 1*0x10+0x1A=0x2A within the program data.
/// </summary>
public RelocationEntry[] RelocationTable { get; set; }
}
}

View File

@@ -0,0 +1,130 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.MSDOS
{
/// <summary>
/// MZ executables only consists of 2 structures: the header and the relocation table.
/// The header, which is followed by the program image, looks like this.
/// </summary>
/// <see href="https://wiki.osdev.org/MZ"/>
/// <see href="http://www.pinvoke.net/default.aspx/Structures.IMAGE_DOS_HEADER"/>
[StructLayout(LayoutKind.Sequential)]
public class ExecutableHeader
{
#region Standard Fields
/// <summary>
/// 0x5A4D (ASCII for 'M' and 'Z')
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] Magic;
/// <summary>
/// Number of bytes in the last page.
/// </summary>
public ushort LastPageBytes;
/// <summary>
/// Number of whole/partial pages.
/// </summary>
/// <remarks>A page (or block) is 512 bytes long.</remarks>
public ushort Pages;
/// <summary>
/// Number of entries in the relocation table.
/// </summary>
public ushort RelocationItems;
/// <summary>
/// The number of paragraphs taken up by the header. It can be any value, as the loader
/// just uses it to find where the actual executable data starts. It may be larger than
/// what the "standard" fields take up, and you may use it if you want to include your
/// own header metadata, or put the relocation table there, or use it for any other purpose. [08]
/// </summary>
/// <remarks>A paragraph is 16 bytes in size</remarks>
public ushort HeaderParagraphSize;
/// <summary>
/// The number of paragraphs required by the program, excluding the PSP and program image.
/// If no free block is big enough, the loading stops.
/// </summary>
/// <remarks>A paragraph is 16 bytes in size</remarks>
public ushort MinimumExtraParagraphs;
/// <summary>
/// The number of paragraphs requested by the program.
/// If no free block is big enough, the biggest one possible is allocated.
/// </summary>
/// <remarks>A paragraph is 16 bytes in size</remarks>
public ushort MaximumExtraParagraphs;
/// <summary>
/// Relocatable segment address for SS.
/// </summary>
public ushort InitialSSValue;
/// <summary>
/// Initial value for SP.
/// </summary>
public ushort InitialSPValue;
/// <summary>
/// When added to the sum of all other words in the file, the result should be zero.
/// </summary>
public ushort Checksum;
/// <summary>
/// Initial value for IP. [14]
/// </summary>
public ushort InitialIPValue;
/// <summary>
/// Relocatable segment address for CS.
/// </summary>
public ushort InitialCSValue;
/// <summary>
/// The (absolute) offset to the relocation table.
/// </summary>
public ushort RelocationTableAddr;
/// <summary>
/// Value used for overlay management.
/// If zero, this is the main executable.
/// </summary>
public ushort OverlayNumber;
#endregion
#region PE Extensions
/// <summary>
/// Reserved words
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public ushort[] Reserved1;
/// <summary>
/// Defined by name but no other information is given; typically zeroes
/// </summary>
public ushort OEMIdentifier;
/// <summary>
/// Defined by name but no other information is given; typically zeroes
/// </summary>
public ushort OEMInformation;
/// <summary>
/// Reserved words
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public ushort[] Reserved2;
/// <summary>
/// Starting address of the PE header
/// </summary>
public uint NewExeHeaderAddr;
#endregion
}
}

View File

@@ -0,0 +1,22 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.MSDOS
{
/// <summary>
/// Each pointer in the relocation table looks as such
/// </summary>
/// <see href="https://wiki.osdev.org/MZ"/>
[StructLayout(LayoutKind.Sequential)]
public class RelocationEntry
{
/// <summary>
/// Offset of the relocation within provided segment.
/// </summary>
public ushort Offset;
/// <summary>
/// Segment of the relocation, relative to the load segment address.
/// </summary>
public ushort Segment;
}
}

View File

@@ -0,0 +1,84 @@
namespace BurnOutSharp.Models.NewExecutable
{
/// <summary>
/// The entry table follows the imported-name table. This table contains
/// bundles of entry-point definitions. Bundling is done to save space in
/// the entry table. The entry table is accessed by an ordinal value.
/// Ordinal number one is defined to index the first entry in the entry
/// table. To find an entry point, the bundles are scanned searching for a
/// specific entry point using an ordinal number. The ordinal number is
/// adjusted as each bundle is checked. When the bundle that contains the
/// entry point is found, the ordinal number is multiplied by the size of
/// the bundle's entries to index the proper entry.
/// </summary>
/// <remarks>
/// The linker forms bundles in the most dense manner it can, under the
/// restriction that it cannot reorder entry points to improve bundling.
/// The reason for this restriction is that other .EXE files may refer to
/// entry points within this bundle by their ordinal number. The following
/// describes the format of the entry table bundles.
/// </remarks>
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
public class EntryTableBundle
{
/// <summary>
/// Number of entries in this bundle. All records in one bundle
/// are either moveable or refer to the same fixed segment. A zero
/// value in this field indicates the end of the entry table.
/// </summary>
public byte EntryCount;
/// <summary>
/// Segment indicator for this bundle. This defines the type of
/// entry table entry data within the bundle. There are three
/// types of entries that are defined.
/// - 000h = Unused entries. There is no entry data in an unused
/// bundle. The next bundle follows this field. This is
/// used by the linker to skip ordinal numbers.
/// - 001h-0FEh = Segment number for fixed segment entries. A fixed
/// segment entry is 3 bytes long.
/// - 0FFH = Moveable segment entries. The entry data contains the
/// segment number for the entry points. A moveable segment
/// entry is 6 bytes long.
/// </summary>
public byte SegmentIndicator;
#region Fixed Segment Entry
/// <summary>
/// Flag word.
/// </summary>
public FixedSegmentEntryFlag FixedFlagWord;
/// <summary>
/// Offset within segment to entry point.
/// </summary>
public ushort FixedOffset;
#endregion
#region Moveable Segment Entry
/// <summary>
/// Flag word.
/// </summary>
public MoveableSegmentEntryFlag MoveableFlagWord;
/// <summary>
/// INT 3FH.
/// </summary>
public ushort MoveableReserved;
/// <summary>
/// Segment number.
/// </summary>
public byte MoveableSegmentNumber;
/// <summary>
/// Offset within segment to entry point.
/// </summary>
public ushort MoveableOffset;
#endregion
}
}

View File

@@ -0,0 +1,324 @@
using System;
namespace BurnOutSharp.Models.NewExecutable
{
[Flags]
public enum FixedSegmentEntryFlag : byte
{
/// <summary>
/// Set if the entry is exported.
/// </summary>
Exported = 0x01,
/// <summary>
/// Set if the entry uses a global (shared) data segments.
/// </summary>
/// <remarks>
/// The first assembly-language instruction in the
/// entry point prologue must be "MOV AX,data
/// segment number". This may be set only for
/// SINGLEDATA library modules.
/// </remarks>
Global = 0x02,
}
[Flags]
public enum HeaderFlag : ushort
{
#region Program Flags
NOAUTODATA = 0x0000,
/// <summary>
/// Shared automatic data segment
/// </summary>
SINGLEDATA = 0x0001,
/// <summary>
/// Instanced automatic data segment
/// </summary>
MULTIPLEDATA = 0x0002,
/// <summary>
/// Global initialization
/// </summary>
GlobalInitialization = 0x0004,
/// <summary>
/// Protected mode only
/// </summary>
ProtectedModeOnly = 0x0008,
/// <summary>
/// 8086 instructions
/// </summary>
Instructions8086 = 0x0010,
/// <summary>
/// 80286 instructions
/// </summary>
Instructions80286 = 0x0020,
/// <summary>
/// 80386 instructions
/// </summary>
Instructions80386 = 0x0040,
/// <summary>
/// 80x87 instructions
/// </summary>
Instructions80x87 = 0x0080,
#endregion
#region Application Flags
/// <summary>
/// Full screen (not aware of Windows/P.M. API)
/// </summary>
FullScreen = 0x0100,
/// <summary>
/// Compatible with Windows/P.M. API
/// </summary>
WindowsPMCompatible = 0x0200,
/// <summary>
/// Uses Windows/P.M. API
/// </summary>
WindowsPM = 0x0400,
/// <summary>
/// OS/2 family application
/// </summary>
OS2FamilyApplication = 0x0800,
/// <summary>
/// Unknown (Reserved?)
/// </summary>
UnknownReserved = 0x1000,
/// <summary>
/// Errors detected at link time, module will not load
/// </summary>
ErrorsDetectedAtLinkTime = 0x2000,
/// <summary>
/// Unknown (non-conforming program)
/// </summary>
UnknownNonConforming = 0x4000,
/// <summary>
/// Library module.
/// The SS:SP information is invalid, CS:IP points
/// to an initialization procedure that is called
/// with AX equal to the module handle. This
/// initialization procedure must perform a far
/// return to the caller, with AX not equal to
/// zero to indicate success, or AX equal to zero
/// to indicate failure to initialize. DS is set
/// to the library's data segment if the
/// SINGLEDATA flag is set. Otherwise, DS is set
/// to the caller's data segment.
/// </summary>
/// <remarks>
/// A program or DLL can only contain dynamic
/// links to executable files that have this
/// library module flag set. One program cannot
/// dynamic-link to another program.
/// </remarks>
LibraryModule = 0x8000,
#endregion
}
[Flags]
public enum MoveableSegmentEntryFlag : byte
{
/// <summary>
/// Set if the entry is exported.
/// </summary>
Exported = 0x01,
/// <summary>
/// Set if the entry uses a global (shared) data segments.
/// </summary>
Global = 0x02,
}
public enum OperatingSystem : byte
{
OS2 = 0x01,
WINDOWS = 0x02,
EU_MSDOS4 = 0x03,
WINDOWS_386 = 0x04,
BOSS = 0x05,
}
[Flags]
public enum OS2Flag : byte
{
/// <summary>
/// Long filename support
/// </summary>
LongFilenameSupport = 0x01,
/// <summary>
/// 2.x protected mode
/// </summary>
ProtectedMode = 0x02,
/// <summary>
/// 2.x proportional fonts
/// </summary>
ProportionalFonts = 0x04,
/// <summary>
/// Executable has gangload area
/// </summary>
HasGangload = 0x08,
/// <summary>
/// Unknown
/// </summary>
Unknown = 0xF0,
}
public enum OSFixupType : ushort
{
/// <summary>
/// FIARQQ, FJARQQ
/// </summary>
FIARQQ = 0x0001,
/// <summary>
/// FISRQQ, FJSRQQ
/// </summary>
FISRQQ = 0x0002,
/// <summary>
/// FICRQQ, FJCRQQ
/// </summary>
FICRQQ = 0x0003,
FIERQQ = 0x0004,
FIDRQQ = 0x0005,
FIWRQQ = 0x0006,
}
[Flags]
public enum RelocationRecordFlag : byte
{
TARGET_MASK = 0x03,
INTERNALREF = 0x00,
IMPORTORDINAL = 0x01,
IMPORTNAME = 0x02,
OSFIXUP = 0x03,
ADDITIVE = 0x04,
}
public enum RelocationRecordSourceType : byte
{
SOURCE_MASK = 0x0F,
LOBYTE = 0x00,
SEGMENT = 0x02,
/// <summary>
/// 32-bit pointer
/// </summary>
FAR_ADDR = 0x03,
/// <summary>
/// 16-bit offset
/// </summary>
OFFSET = 0x05,
}
[Flags]
public enum ResourceTypeResourceFlag : ushort
{
/// <summary>
/// Resource is not fixed.
/// </summary>
MOVEABLE = 0x0010,
/// <summary>
/// Resource can be shared.
/// </summary>
PURE = 0x0020,
/// <summary>
/// Resource is preloaded.
/// </summary>
PRELOAD = 0x0040,
}
public enum SegmentEntryType
{
/// <summary>
/// 000h - There is no entry data in an unused bundle. The next bundle
/// follows this field. This is used by the linker to skip ordinal numbers.
/// </summary>
Unused,
/// <summary>
/// 001h-0FEh - Segment number for fixed segment entries. A fixed segment
/// entry is 3 bytes long.
/// </summary>
FixedSegment,
/// <summary>
/// 0FFH - Moveable segment entries. The entry data contains the segment
/// number for the entry points. A moveable segment entry is 6 bytes long.
/// </summary>
MoveableSegment,
}
[Flags]
public enum SegmentTableEntryFlag : ushort
{
/// <summary>
/// Segment-type field.
/// </summary>
TYPE_MASK = 0x0007,
/// <summary>
/// Code-segment type.
/// </summary>
CODE = 0x0000,
/// <summary>
/// Data-segment type.
/// </summary>
DATA = 0x0001,
/// <summary>
/// Segment is not fixed.
/// </summary>
MOVEABLE = 0x0010,
/// <summary>
/// Segment will be preloaded; read-only if this is a data segment.
/// </summary>
PRELOAD = 0x0040,
/// <summary>
/// Set if segment has relocation records.
/// </summary>
RELOCINFO = 0x0100,
/// <summary>
/// Discard priority.
/// </summary>
DISCARD = 0xF000,
}
}

View File

@@ -0,0 +1,61 @@
using System.Collections.Generic;
namespace BurnOutSharp.Models.NewExecutable
{
/// <summary>
/// The segmented EXE header contains general information about the EXE
/// file and contains information on the location and size of the other
/// sections. The Windows loader copies this section, along with other
/// data, into the module table in the system data. The module table is
/// internal data used by the loader to manage the loaded executable
/// modules in the system and to support dynamic linking.
/// </summary>
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
public class Executable
{
/// <summary>
/// MS-DOS executable stub
/// </summary>
public MSDOS.Executable Stub { get; set; }
/// <summary>
/// New Executable header
/// </summary>
public ExecutableHeader Header { get; set; }
/// <summary>
/// Segment table
/// </summary>
public SegmentTableEntry[] SegmentTable { get; set; }
/// <summary>
/// Resource table
/// </summary>
public ResourceTable ResourceTable { get; set; }
/// <summary>
/// Resident-Name table
/// </summary>
public ResidentNameTableEntry[] ResidentNameTable { get; set; }
/// <summary>
/// Module-Reference table
/// </summary>
public ModuleReferenceTableEntry[] ModuleReferenceTable { get; set; }
/// <summary>
/// Imported-Name table
/// </summary>
public Dictionary<ushort, ImportedNameTableEntry> ImportedNameTable { get; set; }
/// <summary>
/// Entry table
/// </summary>
public EntryTableBundle[] EntryTable { get; set; }
/// <summary>
/// Nonresident-Name table
/// </summary>
public NonResidentNameTableEntry[] NonResidentNameTable { get; set; }
}
}

View File

@@ -0,0 +1,198 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <summary>
/// The NE header is a relatively large structure with multiple characteristics.
/// Because of the age of the format some items are unclear in meaning.
/// </summary>
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
/// <see href="https://github.com/libyal/libexe/blob/main/documentation/Executable%20(EXE)%20file%20format.asciidoc#24-ne-extended-header"/>
[StructLayout(LayoutKind.Sequential)]
public class ExecutableHeader
{
/// <summary>
/// Signature word.
/// "N" is low-order byte.
/// "E" is high-order byte.
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] Magic;
/// <summary>
/// Version number of the linker.
/// </summary>
public byte LinkerVersion;
/// <summary>
/// Revision number of the linker.
/// </summary>
public byte LinkerRevision;
/// <summary>
/// Entry Table file offset, relative to the beginning of the segmented EXE header.
/// </summary>
public ushort EntryTableOffset;
/// <summary>
/// Number of bytes in the entry table.
/// </summary>
public ushort EntryTableSize;
/// <summary>
/// 32-bit CRC of entire contents of file.
/// </summary>
/// <remarks>These words are taken as 00 during the calculation.</remarks>
public uint CrcChecksum;
/// <summary>
/// Flag word
/// </summary>
public HeaderFlag FlagWord;
/// <summary>
/// Segment number of automatic data segment.
/// This value is set to zero if SINGLEDATA and
/// MULTIPLEDATA flag bits are clear, NOAUTODATA is
/// indicated in the flags word.
/// </summary>
/// <remarks>
/// A Segment number is an index into the module's segment
/// table. The first entry in the segment table is segment
/// number 1.
/// </remarks>
public ushort AutomaticDataSegmentNumber;
/// <summary>
/// Initial size, in bytes, of dynamic heap added to the
/// data segment. This value is zero if no initial local
/// heap is allocated.
/// </summary>
public ushort InitialHeapAlloc;
/// <summary>
/// Initial size, in bytes, of stack added to the data
/// segment. This value is zero to indicate no initial
/// stack allocation, or when SS is not equal to DS.
/// </summary>
public ushort InitialStackAlloc;
/// <summary>
/// Segment number:offset of CS:IP.
/// </summary>
public uint InitialCSIPSetting;
/// <summary>
/// Segment number:offset of SS:SP.
/// </summary>
/// <remarks>
/// If SS equals the automatic data segment and SP equals
/// zero, the stack pointer is set to the top of the
/// automatic data segment just below the additional heap
/// area.
/// </remarks>
public uint InitialSSSPSetting;
/// <summary>
/// Number of entries in the Segment Table.
/// </summary>
public ushort FileSegmentCount;
/// <summary>
/// Number of entries in the Module Reference Table.
/// </summary>
public ushort ModuleReferenceTableSize;
/// <summary>
/// Number of bytes in the Non-Resident Name Table.
/// </summary>
public ushort NonResidentNameTableSize;
/// <summary>
/// Segment Table file offset, relative to the beginning
/// of the segmented EXE header.
/// </summary>
public ushort SegmentTableOffset;
/// <summary>
/// Resource Table file offset, relative to the beginning
/// of the segmented EXE header.
/// </summary>
public ushort ResourceTableOffset;
/// <summary>
/// Resident Name Table file offset, relative to the
/// beginning of the segmented EXE header.
/// </summary>
public ushort ResidentNameTableOffset;
/// <summary>
/// Module Reference Table file offset, relative to the
/// beginning of the segmented EXE header.
/// </summary>
public ushort ModuleReferenceTableOffset;
/// <summary>
/// Imported Names Table file offset, relative to the
/// beginning of the segmented EXE header.
/// </summary>
public ushort ImportedNamesTableOffset;
/// <summary>
/// Non-Resident Name Table offset, relative to the
/// beginning of the file.
/// </summary>
public uint NonResidentNamesTableOffset;
/// <summary>
/// Number of movable entries in the Entry Table.
/// </summary>
public ushort MovableEntriesCount;
/// <summary>
/// Logical sector alignment shift count, log(base 2) of
/// the segment sector size (default 9).
/// </summary>
public ushort SegmentAlignmentShiftCount;
/// <summary>
/// Number of resource entries.
/// </summary>
public ushort ResourceEntriesCount;
/// <summary>
/// Executable type, used by loader.
/// </summary>
public OperatingSystem TargetOperatingSystem;
/// <summary>
/// Other OS/2 flags
/// </summary>
public OS2Flag AdditionalFlags;
/// <summary>
/// Offset to return thunks or start of gangload area
/// </summary>
public ushort ReturnThunkOffset;
/// <summary>
/// Offset to segment reference thunks or size of gangload area
/// </summary>
public ushort SegmentReferenceThunkOffset;
/// <summary>
/// Minimum code swap area size
/// </summary>
public ushort MinCodeSwapAreaSize;
/// <summary>
/// Windows SDK revison number
/// </summary>
public byte WindowsSDKRevision;
/// <summary>
/// Windows SDK version number
/// </summary>
public byte WindowsSDKVersion;
}
}

View File

@@ -0,0 +1,19 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public class ImportNameRelocationRecord
{
/// <summary>
/// Index into module reference table for the imported module.
/// </summary>
public ushort Index;
/// <summary>
/// Offset within Imported Names Table to procedure name string.
/// </summary>
public ushort Offset;
}
}

View File

@@ -0,0 +1,19 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public class ImportOrdinalRelocationRecord
{
/// <summary>
/// Index into module reference table for the imported module.
/// </summary>
public ushort Index;
/// <summary>
/// Procedure ordinal number.
/// </summary>
public ushort Ordinal;
}
}

View File

@@ -0,0 +1,28 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <summary>
/// The imported-name table follows the module-reference table. This table
/// contains the names of modules and procedures that are imported by the
/// executable file. Each entry is composed of a 1-byte field that
/// contains the length of the string, followed by any number of
/// characters. The strings are not null-terminated and are case
/// sensitive.
/// </summary>
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public class ImportedNameTableEntry
{
/// <summary>
/// Length of the name string that follows. A zero value indicates
/// the end of the name table.
/// </summary>
public byte Length;
/// <summary>
/// ASCII text of the name string.
/// </summary>
public byte[] NameString;
}
}

View File

@@ -0,0 +1,26 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public class InternalRefRelocationRecord
{
/// <summary>
/// Segment number for a fixed segment, or 0FFh for a
/// movable segment.
/// </summary>
public byte SegmentNumber;
/// <summary>
/// 0
/// </summary>
public byte Reserved;
/// <summary>
/// Offset into segment if fixed segment, or ordinal
/// number index into Entry Table if movable segment.
/// </summary>
public ushort Offset;
}
}

View File

@@ -0,0 +1,19 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <summary>
/// The module-reference table follows the resident-name table. Each entry
/// contains an offset for the module-name string within the imported-
/// names table; each entry is 2 bytes long.
/// </summary>
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public class ModuleReferenceTableEntry
{
/// <summary>
/// Offset within Imported Names Table to referenced module name string.
/// </summary>
public ushort Offset;
}
}

View File

@@ -0,0 +1,34 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <summary>
/// The nonresident-name table follows the entry table, and contains a
/// module description and nonresident exported procedure name strings.
/// The first string in this table is a module description. These name
/// strings are case-sensitive and are not null-terminated. The name
/// strings follow the same format as those defined in the resident name
/// table.
/// </summary>
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public class NonResidentNameTableEntry
{
/// <summary>
/// Length of the name string that follows. A zero value indicates
/// the end of the name table.
/// </summary>
public byte Length;
/// <summary>
/// ASCII text of the name string.
/// </summary>
public byte[] NameString;
/// <summary>
/// Ordinal number (index into entry table). This value is ignored
/// for the module name.
/// </summary>
public ushort OrdinalNumber;
}
}

View File

@@ -0,0 +1,20 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public class OSFixupRelocationRecord
{
/// <summary>
/// Operating system fixup type.
/// Floating-point fixups.
/// </summary>
public OSFixupType FixupType;
/// <summary>
/// 0
/// </summary>
public ushort Reserved;
}
}

View File

@@ -0,0 +1,23 @@
namespace BurnOutSharp.Models.NewExecutable
{
/// <summary>
/// The location and size of the per-segment data is defined in the
/// segment table entry for the segment. If the segment has relocation
/// fixups, as defined in the segment table entry flags, they directly
/// follow the segment data in the file. The relocation fixup information
/// is defined as follows:
/// </summary>
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
public class PerSegmentData
{
/// <summary>
/// Number of relocation records that follow.
/// </summary>
public ushort RelocationRecordCount;
/// <summary>
/// A table of relocation records follows.
/// </summary>
public RelocationRecord[] RelocationRecords;
}
}

View File

@@ -0,0 +1,60 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <summary>
/// A table of relocation records follows. The following is the format
/// of each relocation record.
/// </summary>
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public class RelocationRecord
{
/// <summary>
/// Source type.
/// </summary>
public RelocationRecordSourceType SourceType;
/// <summary>
/// Flags byte.
///
/// The target value has four types that are defined in the flag
/// byte field.
/// </summary>
public RelocationRecordFlag Flags;
/// <summary>
/// Offset within this segment of the source chain.
/// If the ADDITIVE flag is set, then target value is added to
/// the source contents, instead of replacing the source and
/// following the chain. The source chain is an 0FFFFh
/// terminated linked list within this segment of all
/// references to the target.
/// </summary>
public ushort Offset;
/// <summary>
/// INTERNALREF
/// </summary>
/// <remarks>Must be <c>NULL</c> if <see cref="Flags"/> is not set to <see cref="RelocationRecordFlag.INTERNALREF"/></remarks>
public InternalRefRelocationRecord InternalRefRelocationRecord;
/// <summary>
/// IMPORTNAME
/// </summary>
/// <remarks>Must be <c>NULL</c> if <see cref="Flags"/> is not set to <see cref="RelocationRecordFlag.IMPORTNAME"/></remarks>
public ImportNameRelocationRecord ImportNameRelocationRecord;
/// <summary>
/// IMPORTORDINAL
/// </summary>
/// <remarks>Must be <c>NULL</c> if <see cref="Flags"/> is not set to <see cref="RelocationRecordFlag.IMPORTORDINAL"/></remarks>
public ImportOrdinalRelocationRecord ImportOrdinalRelocationRecord;
/// <summary>
/// IMPORTORDINAL
/// </summary>
/// <remarks>Must be <c>NULL</c> if <see cref="Flags"/> is not set to <see cref="RelocationRecordFlag.OSFIXUP"/></remarks>
public OSFixupRelocationRecord OSFixupRelocationRecord;
}
}

View File

@@ -0,0 +1,33 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <summary>
/// The resident-name table follows the resource table, and contains this
/// module's name string and resident exported procedure name strings. The
/// first string in this table is this module's name. These name strings
/// are case-sensitive and are not null-terminated. The following
/// describes the format of the name strings:
/// </summary>
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public class ResidentNameTableEntry
{
/// <summary>
/// Length of the name string that follows. A zero value indicates
/// the end of the name table.
/// </summary>
public byte Length;
/// <summary>
/// ASCII text of the name string.
/// </summary>
public byte[] NameString;
/// <summary>
/// Ordinal number (index into entry table). This value is ignored
/// for the module name.
/// </summary>
public ushort OrdinalNumber;
}
}

View File

@@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <summary>
/// The resource table follows the segment table and contains entries for
/// each resource in the executable file. The resource table consists of
/// an alignment shift count, followed by a table of resource records. The
/// resource records define the type ID for a set of resources. Each
/// resource record contains a table of resource entries of the defined
/// type. The resource entry defines the resource ID or name ID for the
/// resource. It also defines the location and size of the resource. The
/// following describes the contents of each of these structures:
/// </summary>
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public class ResourceTable
{
/// <summary>
/// Alignment shift count for resource data.
/// </summary>
public ushort AlignmentShiftCount;
/// <summary>
/// A table of resource type information blocks follows.
/// </summary>
public ResourceTypeInformationEntry[] ResourceTypes;
/// <summary>
/// Resource type and name strings are stored at the end of the
/// resource table.
/// </summary>
public Dictionary<ushort, ResourceTypeAndNameString> TypeAndNameStrings;
}
}

View File

@@ -0,0 +1,26 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <summary>
/// Resource type and name strings are stored at the end of the
/// resource table. Note that these strings are NOT null terminated and
/// are case sensitive.
/// </summary>
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public class ResourceTypeAndNameString
{
/// <summary>
/// Length of the type or name string that follows. A zero value
/// indicates the end of the resource type and name string, also
/// the end of the resource table.
/// </summary>
public byte Length;
/// <summary>
/// ASCII text of the type or name string.
/// </summary>
public byte[] Text;
}
}

View File

@@ -0,0 +1,37 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <summary>
/// A table of resource type information blocks follows. The following
/// is the format of each type information block:
/// </summary>
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public class ResourceTypeInformationEntry
{
/// <summary>
/// Type ID. This is an integer type if the high-order bit is
/// set (8000h); otherwise, it is an offset to the type string,
/// the offset is relative to the beginning of the resource
/// table. A zero type ID marks the end of the resource type
/// information blocks.
/// </summary>
public ushort TypeID;
/// <summary>
/// Number of resources for this type.
/// </summary>
public ushort ResourceCount;
/// <summary>
/// Reserved.
/// </summary>
public uint Reserved;
/// <summary>
/// A table of resources for this type follows.
/// </summary>
public ResourceTypeResourceEntry[] Resources;
}
}

View File

@@ -0,0 +1,44 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <summary>
/// A table of resources for this type follows. The following is
/// the format of each resource (8 bytes each):
/// </summary>
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public class ResourceTypeResourceEntry
{
/// <summary>
/// File offset to the contents of the resource data,
/// relative to beginning of file. The offset is in terms
/// of the alignment shift count value specified at
/// beginning of the resource table.
/// </summary>
public ushort Offset;
/// <summary>
/// Length of the resource in the file (in bytes).
/// </summary>
public ushort Length;
/// <summary>
/// Flag word.
/// </summary>
public ResourceTypeResourceFlag FlagWord;
/// <summary>
/// Resource ID. This is an integer type if the high-order
/// bit is set (8000h), otherwise it is the offset to the
/// resource string, the offset is relative to the
/// beginning of the resource table.
/// </summary>
public ushort ResourceID;
/// <summary>
/// Reserved.
/// </summary>
public uint Reserved;
}
}

View File

@@ -0,0 +1,38 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.NewExecutable
{
/// <summary>
/// The segment table contains an entry for each segment in the executable
/// file. The number of segment table entries are defined in the segmented
/// EXE header. The first entry in the segment table is segment number 1.
/// The following is the structure of a segment table entry.
/// </summary>
/// <see href="http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public class SegmentTableEntry
{
/// <summary>
/// Logical-sector offset (n byte) to the contents of the segment
/// data, relative to the beginning of the file. Zero means no
/// file data.
/// </summary>
public ushort Offset;
/// <summary>
/// Length of the segment in the file, in bytes. Zero means 64K.
/// </summary>
public ushort Length;
/// <summary>
/// Flag word.
/// </summary>
public SegmentTableEntryFlag FlagWord;
/// <summary>
/// Minimum allocation size of the segment, in bytes. Total size
/// of the segment. Zero means 64K.
/// </summary>
public ushort MinimumAllocationSize;
}
}

View File

@@ -0,0 +1,34 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// Describes the data in an individual accelerator table resource. The structure definition
/// provided here is for explanation only; it is not present in any standard header file.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/menurc/acceltableentry"/>
[StructLayout(LayoutKind.Sequential)]
public class AcceleratorTableEntry
{
/// <summary>
/// Describes keyboard accelerator characteristics.
/// </summary>
public AcceleratorTableFlags Flags;
/// <summary>
/// An ANSI character value or a virtual-key code that identifies the accelerator key.
/// </summary>
public ushort Ansi;
/// <summary>
/// An identifier for the keyboard accelerator. This is the value passed to the window
/// procedure when the user presses the specified key.
/// </summary>
public ushort Id;
/// <summary>
/// The number of bytes inserted to ensure that the structure is aligned on a DWORD boundary.
/// </summary>
public ushort Padding;
}
}

View File

@@ -0,0 +1,390 @@
using System.Xml.Serialization;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
[XmlRoot(ElementName = "assembly", Namespace = "urn:schemas-microsoft-com:asm.v1")]
public class AssemblyManifest
{
[XmlAttribute("manifestVersion")]
public string ManifestVersion;
#region Group
[XmlElement("assemblyIdentity")]
public AssemblyIdentity[] AssemblyIdentities;
[XmlElement("noInheritable")]
public AssemblyNoInheritable[] NoInheritables;
#endregion
#region Group
[XmlElement("description")]
public AssemblyDescription Description;
[XmlElement("noInherit")]
public AssemblyNoInherit NoInherit;
//[XmlElement("noInheritable")]
//public AssemblyNoInheritable NoInheritable;
[XmlElement("comInterfaceExternalProxyStub")]
public AssemblyCOMInterfaceExternalProxyStub[] COMInterfaceExternalProxyStub;
[XmlElement("dependency")]
public AssemblyDependency[] Dependency;
[XmlElement("file")]
public AssemblyFile[] File;
[XmlElement("clrClass")]
public AssemblyCommonLanguageRuntimeClass[] CLRClass;
[XmlElement("clrSurrogate")]
public AssemblyCommonLanguageSurrogateClass[] CLRSurrogate;
#endregion
[XmlAnyElement]
public object[] EverythingElse;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyActiveCodePage
{
[XmlText]
public string Value;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyAutoElevate
{
[XmlText]
public string Value;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyBindingRedirect
{
[XmlAttribute("oldVersion")]
public string OldVersion;
[XmlAttribute("newVersion")]
public string NewVersion;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyCOMClass
{
[XmlAttribute("clsid")]
public string CLSID;
[XmlAttribute("threadingModel")]
public string ThreadingModel;
[XmlAttribute("progid")]
public string ProgID;
[XmlAttribute("tlbid")]
public string TLBID;
[XmlAttribute("description")]
public string Description;
[XmlElement("progid")]
public AssemblyProgID[] ProgIDs;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyCOMInterfaceExternalProxyStub
{
[XmlAttribute("iid")]
public string IID;
[XmlAttribute("name")]
public string Name;
[XmlAttribute("tlbid")]
public string TLBID;
[XmlAttribute("numMethods")]
public string NumMethods;
[XmlAttribute("proxyStubClsid32")]
public string ProxyStubClsid32;
[XmlAttribute("baseInterface")]
public string BaseInterface;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyCOMInterfaceProxyStub
{
[XmlAttribute("iid")]
public string IID;
[XmlAttribute("name")]
public string Name;
[XmlAttribute("tlbid")]
public string TLBID;
[XmlAttribute("numMethods")]
public string NumMethods;
[XmlAttribute("proxyStubClsid32")]
public string ProxyStubClsid32;
[XmlAttribute("baseInterface")]
public string BaseInterface;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyCommonLanguageRuntimeClass
{
[XmlAttribute("name")]
public string Name;
[XmlAttribute("clsid")]
public string CLSID;
[XmlAttribute("progid")]
public string ProgID;
[XmlAttribute("tlbid")]
public string TLBID;
[XmlAttribute("description")]
public string Description;
[XmlAttribute("runtimeVersion")]
public string RuntimeVersion;
[XmlAttribute("threadingModel")]
public string ThreadingModel;
[XmlElement("progid")]
public AssemblyProgID[] ProgIDs;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyCommonLanguageSurrogateClass
{
[XmlAttribute("clsid")]
public string CLSID;
[XmlAttribute("name")]
public string Name;
[XmlAttribute("runtimeVersion")]
public string RuntimeVersion;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyDependency
{
[XmlElement("dependentAssembly")]
public AssemblyDependentAssembly DependentAssembly;
[XmlAttribute("optional")]
public string Optional;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyDependentAssembly
{
[XmlElement("assemblyIdentity")]
public AssemblyIdentity AssemblyIdentity;
[XmlElement("bindingRedirect")]
public AssemblyBindingRedirect[] BindingRedirect;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyDescription
{
[XmlText]
public string Value;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyDisableTheming
{
[XmlText]
public string Value;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyDisableWindowFiltering
{
[XmlText]
public string Value;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyDPIAware
{
[XmlText]
public string Value;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyDPIAwareness
{
[XmlText]
public string Value;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyFile
{
[XmlAttribute("name")]
public string Name;
[XmlAttribute("hash")]
public string Hash;
[XmlAttribute("hashalg")]
public string HashAlgorithm;
[XmlAttribute("size")]
public string Size;
#region Group
[XmlElement("comClass")]
public AssemblyCOMClass[] COMClass;
[XmlElement("comInterfaceProxyStub")]
public AssemblyCOMInterfaceProxyStub[] COMInterfaceProxyStub;
[XmlElement("typelib")]
public AssemblyTypeLib[] Typelib;
[XmlElement("windowClass")]
public AssemblyWindowClass[] WindowClass;
#endregion
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyGDIScaling
{
[XmlText]
public string Value;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyHeapType
{
[XmlText]
public string Value;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyHighResolutionScrollingAware
{
[XmlText]
public string Value;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyIdentity
{
[XmlAttribute("name")]
public string Name;
[XmlAttribute("version")]
public string Version;
[XmlAttribute("type")]
public string Type;
[XmlAttribute("processorArchitecture")]
public string ProcessorArchitecture;
[XmlAttribute("publicKeyToken")]
public string PublicKeyToken;
[XmlAttribute("language")]
public string Language;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyLongPathAware
{
[XmlText]
public string Value;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyNoInherit
{
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyNoInheritable
{
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyPrinterDriverIsolation
{
[XmlText]
public string Value;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyProgID
{
[XmlText]
public string Value;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblySupportedOS
{
[XmlAttribute("Id")]
public string Id;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyTypeLib
{
[XmlElement("tlbid")]
public string TLBID;
[XmlElement("version")]
public string Version;
[XmlElement("helpdir")]
public string HelpDir;
[XmlElement("resourceid")]
public string ResourceID;
[XmlElement("flags")]
public string Flags;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyUltraHighResolutionScrollingAware
{
[XmlText]
public string Value;
}
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-file-schema"/>
public class AssemblyWindowClass
{
[XmlAttribute("versioned")]
public string Versioned;
[XmlText]
public string Value;
}
// TODO: Left off at <ElementType name="progid" />
}

View File

@@ -0,0 +1,63 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// Attribute certificates can be associated with an image by adding an attribute
/// certificate table. The attribute certificate table is composed of a set of
/// contiguous, quadword-aligned attribute certificate entries. Zero padding is
/// inserted between the original end of the file and the beginning of the attribute
/// certificate table to achieve this alignment.
///
/// The virtual address value from the Certificate Table entry in the Optional
/// Header Data Directory is a file offset to the first attribute certificate
/// entry. Subsequent entries are accessed by advancing that entry's dwLength
/// bytes, rounded up to an 8-byte multiple, from the start of the current
/// attribute certificate entry. This continues until the sum of the rounded dwLength
/// values equals the Size value from the Certificates Table entry in the Optional
/// Header Data Directory. If the sum of the rounded dwLength values does not equal
/// the Size value, then either the attribute certificate table or the Size field
/// is corrupted.
///
/// The first certificate starts at offset 0x5000 from the start of the file on disk.
/// To advance through all the attribute certificate entries:
///
/// 1. Add the first attribute certificate's dwLength value to the starting offset.
/// 2. Round the value from step 1 up to the nearest 8-byte multiple to find the offset
/// of the second attribute certificate entry.
/// 3. Add the offset value from step 2 to the second attribute certificate entry's
/// dwLength value and round up to the nearest 8-byte multiple to determine the offset
/// of the third attribute certificate entry.
/// 4. Repeat step 3 for each successive certificate until the calculated offset equals
/// 0x6000 (0x5000 start + 0x1000 total size), which indicates that you've walked
/// the entire table.
///
/// Attribute certificate table entries can contain any certificate type, as long as
/// the entry has the correct dwLength value, a unique wRevision value, and a unique
/// wCertificateType value. The most common type of certificate table entry is a
/// WIN_CERTIFICATE structure, which is documented in Wintrust.h and discussed in
/// the remainder of this section.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class AttributeCertificateTableEntry
{
/// <summary>
/// Specifies the length of the attribute certificate entry.
/// </summary>
public uint Length;
/// <summary>
/// Contains the certificate version number.
/// </summary>
public WindowsCertificateRevision Revision;
/// <summary>
/// Specifies the type of content in Certificate.
/// </summary>
public WindowsCertificateType CertificateType;
/// <summary>
/// Contains a certificate, such as an Authenticode signature.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#certificate-data"/>
public byte[] Certificate;
}
}

View File

@@ -0,0 +1,51 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The base relocation table contains entries for all base relocations in
/// the image. The Base Relocation Table field in the optional header data
/// directories gives the number of bytes in the base relocation table. For
/// more information, see Optional Header Data Directories (Image Only).
/// The base relocation table is divided into blocks. Each block represents
/// the base relocations for a 4K page. Each block must start on a 32-bit boundary.
///
/// The loader is not required to process base relocations that are resolved by
/// the linker, unless the load image cannot be loaded at the image base that is
/// specified in the PE header.
///
/// To apply a base relocation, the difference is calculated between the preferred
/// base address and the base where the image is actually loaded. If the image is
/// loaded at its preferred base, the difference is zero and thus the base
/// relocations do not have to be applied.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class BaseRelocationBlock
{
/// <summary>
/// The image base plus the page RVA is added to each offset to create
/// the VA where the base relocation must be applied.
/// </summary>
public uint PageRVA;
/// <summary>
/// The total number of bytes in the base relocation block, including
/// the Page RVA and Block Size fields and the Type/Offset fields that
/// follow.
/// </summary>
public uint BlockSize;
/// <summary>
/// The Block Size field is then followed by any number of Type or Offset
/// field entries. Each entry is a WORD (2 bytes) and has the following
/// structure:
///
/// 4 bits - Type - Stored in the high 4 bits of the WORD, a value
/// that indicates the type of base relocation to be
/// applied. For more information, see <see cref="BaseRelocationTypes"/>
/// 12 bits - Offset - Stored in the remaining 12 bits of the WORD, an
/// offset from the starting address that was specified
/// in the Page RVA field for the block. This offset
/// specifies where the base relocation is to be applied.
/// </summary>
public BaseRelocationTypeOffsetFieldEntry[] TypeOffsetFieldEntries;
}
}

View File

@@ -0,0 +1,22 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// Type or Offset field entry is a WORD (2 bytes).
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class BaseRelocationTypeOffsetFieldEntry
{
/// <summary>
/// Stored in the high 4 bits of the WORD, a value that indicates the type
/// of base relocation to be applied. For more information, see <see cref="BaseRelocationTypes"/>
/// </summary>
public BaseRelocationTypes BaseRelocationType;
/// <summary>
/// Stored in the remaining 12 bits of the WORD, an offset from the starting
/// address that was specified in the Page RVA field for the block. This
/// offset specifies where the base relocation is to be applied.
/// </summary>
public ushort Offset;
}
}

View File

@@ -0,0 +1,56 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// At the beginning of an object file, or immediately after the signature
/// of an image file, is a standard COFF file header in the following format.
/// Note that the Windows loader limits the number of sections to 96.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Sequential)]
public class COFFFileHeader
{
/// <summary>
/// The number that identifies the type of target machine.
/// </summary>
public MachineType Machine;
/// <summary>
/// The number of sections. This indicates the size of the section table,
/// which immediately follows the headers.
/// </summary>
public ushort NumberOfSections;
/// <summary>
/// The low 32 bits of the number of seconds since 00:00 January 1, 1970
/// (a C run-time time_t value), which indicates when the file was created.
/// </summary>
public uint TimeDateStamp;
/// <summary>
/// The file offset of the COFF symbol table, or zero if no COFF symbol table
/// is present. This value should be zero for an image because COFF debugging
/// information is deprecated.
/// </summary>
public uint PointerToSymbolTable;
/// <summary>
/// The number of entries in the symbol table. This data can be used to locate
/// the string table, which immediately follows the symbol table. This value
/// should be zero for an image because COFF debugging information is deprecated.
/// </summary>
public uint NumberOfSymbols;
/// <summary>
/// The size of the optional header, which is required for executable files but
/// not for object files. This value should be zero for an object file.
/// </summary>
public ushort SizeOfOptionalHeader;
/// <summary>
/// The flags that indicate the attributes of the file.
/// </summary>
public Characteristics Characteristics;
}
}

View File

@@ -0,0 +1,42 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// COFF line numbers are no longer produced and, in the future, will
/// not be consumed.
///
/// COFF line numbers indicate the relationship between code and line
/// numbers in source files. The Microsoft format for COFF line numbers
/// is similar to standard COFF, but it has been extended to allow a
/// single section to relate to line numbers in multiple source files.
///
/// COFF line numbers consist of an array of fixed-length records.
/// The location (file offset) and size of the array are specified in
/// the section header.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Explicit)]
public class COFFLineNumber
{
/// <summary>
/// Used when Linenumber is zero: index to symbol table entry for a function.
/// This format is used to indicate the function to which a group of
/// line-number records refers.
/// </summary>
[FieldOffset(0)] public uint SymbolTableIndex;
/// <summary>
/// Used when Linenumber is non-zero: the RVA of the executable code that
/// corresponds to the source line indicated. In an object file, this
/// contains the VA within the section.
/// </summary>
[FieldOffset(0)] public uint VirtualAddress;
/// <summary>
/// When nonzero, this field specifies a one-based line number. When zero,
/// the Type field is interpreted as a symbol table index for a function.
/// </summary>
[FieldOffset(4)] public ushort Linenumber;
}
}

View File

@@ -0,0 +1,46 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// Object files contain COFF relocations, which specify how the section data
/// should be modified when placed in the image file and subsequently loaded
/// into memory.
///
/// Image files do not contain COFF relocations, because all referenced symbols
/// have already been assigned addresses in a flat address space. An image
/// contains relocation information in the form of base relocations in the
/// .reloc section (unless the image has the IMAGE_FILE_RELOCS_STRIPPED attribute).
///
/// For each section in an object file, an array of fixed-length records holds
/// the section's COFF relocations. The position and length of the array are
/// specified in the section header.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Sequential)]
public class COFFRelocation
{
/// <summary>
/// The address of the item to which relocation is applied. This is the
/// offset from the beginning of the section, plus the value of the
/// section's RVA/Offset field. See Section Table (Section Headers).
/// For example, if the first byte of the section has an address of 0x10,
/// the third byte has an address of 0x12.
/// </summary>
public uint VirtualAddress;
/// <summary>
/// A zero-based index into the symbol table. This symbol gives the address
/// that is to be used for the relocation. If the specified symbol has section
/// storage class, then the symbol's address is the address with the first
/// section of the same name.
/// </summary>
public uint SymbolTableIndex;
/// <summary>
/// A value that indicates the kind of relocation that should be performed.
/// Valid relocation types depend on machine type.
/// </summary>
public RelocationType TypeIndicator;
}
}

View File

@@ -0,0 +1,25 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// Immediately following the COFF symbol table is the COFF string table. The
/// position of this table is found by taking the symbol table address in the
/// COFF header and adding the number of symbols multiplied by the size of a symbol.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class COFFStringTable
{
/// <summary>
/// At the beginning of the COFF string table are 4 bytes that contain the
/// total size (in bytes) of the rest of the string table. This size includes
/// the size field itself, so that the value in this location would be 4 if no
/// strings were present.
/// </summary>
public uint TotalSize;
/// <summary>
/// Following the size are null-terminated strings that are pointed to by symbols
/// in the COFF symbol table.
/// </summary>
public string[] Strings;
}
}

View File

@@ -0,0 +1,315 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The symbol table in this section is inherited from the traditional
/// COFF format. It is distinct from Microsoft Visual C++ debug information.
/// A file can contain both a COFF symbol table and Visual C++ debug
/// information, and the two are kept separate. Some Microsoft tools use
/// the symbol table for limited but important purposes, such as
/// communicating COMDAT information to the linker. Section names and file
/// names, as well as code and data symbols, are listed in the symbol table.
///
/// The location of the symbol table is indicated in the COFF header.
///
/// The symbol table is an array of records, each 18 bytes long. Each record
/// is either a standard or auxiliary symbol-table record. A standard record
/// defines a symbol or name.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class COFFSymbolTableEntry
{
#region Standard COFF Symbol Table Entry
#region Symbol Name
/// <summary>
/// An array of 8 bytes. This array is padded with nulls on the right if
/// the name is less than 8 bytes long.
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] ShortName;
/// <summary>
/// A field that is set to all zeros if the name is longer than 8 bytes.
/// </summary>
public uint Zeroes;
/// <summary>
/// An offset into the string table.
/// </summary>
public uint Offset;
#endregion
/// <summary>
/// The value that is associated with the symbol. The interpretation of this
/// field depends on SectionNumber and StorageClass. A typical meaning is the
/// relocatable address.
/// </summary>
public uint Value;
/// <summary>
/// The signed integer that identifies the section, using a one-based index
/// into the section table. Some values have special meaning.
/// </summary>
public ushort SectionNumber;
/// <summary>
/// A number that represents type. Microsoft tools set this field to 0x20
/// (function) or 0x0 (not a function).
/// </summary>
public SymbolType SymbolType;
/// <summary>
/// An enumerated value that represents storage class.
/// </summary>
public StorageClass StorageClass;
/// <summary>
/// The number of auxiliary symbol table entries that follow this record.
/// </summary>
public byte NumberOfAuxSymbols;
#endregion
#region Auxiliary Symbol Records
// Auxiliary symbol table records always follow, and apply to, some standard
// symbol table record. An auxiliary record can have any format that the tools
// can recognize, but 18 bytes must be allocated for them so that symbol table
// is maintained as an array of regular size. Currently, Microsoft tools
// recognize auxiliary formats for the following kinds of records: function
// definitions, function begin and end symbols (.bf and .ef), weak externals,
// file names, and section definitions.
//
// The traditional COFF design also includes auxiliary-record formats for arrays
// and structures.Microsoft tools do not use these, but instead place that
// symbolic information in Visual C++ debug format in the debug sections.
#region Auxiliary Format 1: Function Definitions
// A symbol table record marks the beginning of a function definition if it
// has all of the following: a storage class of EXTERNAL (2), a Type value
// that indicates it is a function (0x20), and a section number that is
// greater than zero. Note that a symbol table record that has a section
// number of UNDEFINED (0) does not define the function and does not have
// an auxiliary record. Function-definition symbol records are followed by
// an auxiliary record in the format described below:
/// <summary>
/// The symbol-table index of the corresponding .bf (begin function)
/// symbol record.
/// </summary>
public uint AuxFormat1TagIndex;
/// <summary>
/// The size of the executable code for the function itself. If the function
/// is in its own section, the SizeOfRawData in the section header is greater
/// or equal to this field, depending on alignment considerations.
/// </summary>
public uint AuxFormat1TotalSize;
/// <summary>
/// The file offset of the first COFF line-number entry for the function, or
/// zero if none exists.
/// </summary>
public uint AuxFormat1PointerToLinenumber;
/// <summary>
/// The symbol-table index of the record for the next function. If the function
/// is the last in the symbol table, this field is set to zero.
/// </summary>
public uint AuxFormat1PointerToNextFunction;
/// <summary>
/// Unused
/// </summary>
public ushort AuxFormat1Unused;
#endregion
#region Auxiliary Format 2: .bf and .ef Symbols
// For each function definition in the symbol table, three items describe
// the beginning, ending, and number of lines. Each of these symbols has
// storage class FUNCTION (101):
//
// A symbol record named .bf (begin function). The Value field is unused.
//
// A symbol record named .lf (lines in function). The Value field gives the
// number of lines in the function.
//
// A symbol record named .ef (end of function). The Value field has the same
// number as the Total Size field in the function-definition symbol record.
//
// The .bf and .ef symbol records (but not .lf records) are followed by an
// auxiliary record with the following format:
/// <summary>
/// Unused
/// </summary>
public uint AuxFormat2Unused1;
/// <summary>
/// The actual ordinal line number (1, 2, 3, and so on) within the source file,
/// corresponding to the .bf or .ef record.
/// </summary>
public ushort AuxFormat2Linenumber;
/// <summary>
/// Unused
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] AuxFormat2Unused2;
/// <summary>
/// The symbol-table index of the next .bf symbol record. If the function is the
/// last in the symbol table, this field is set to zero. It is not used for
/// .ef records.
/// </summary>
public uint AuxFormat2PointerToNextFunction;
/// <summary>
/// Unused
/// </summary>
public ushort AuxFormat2Unused3;
#endregion
#region Auxiliary Format 3: Weak Externals
// "Weak externals" are a mechanism for object files that allows flexibility at
// link time. A module can contain an unresolved external symbol (sym1), but it
// can also include an auxiliary record that indicates that if sym1 is not
// present at link time, another external symbol (sym2) is used to resolve
// references instead.
//
// If a definition of sym1 is linked, then an external reference to the symbol
// is resolved normally. If a definition of sym1 is not linked, then all references
// to the weak external for sym1 refer to sym2 instead. The external symbol, sym2,
// must always be linked; typically, it is defined in the module that contains
// the weak reference to sym1.
//
// Weak externals are represented by a symbol table record with EXTERNAL storage
// class, UNDEF section number, and a value of zero. The weak-external symbol
// record is followed by an auxiliary record with the following format:
/// <summary>
/// The symbol-table index of sym2, the symbol to be linked if sym1 is not found.
/// </summary>
public uint AuxFormat3TagIndex;
/// <summary>
/// A value of IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY indicates that no library search
/// for sym1 should be performed.
/// A value of IMAGE_WEAK_EXTERN_SEARCH_LIBRARY indicates that a library search for
/// sym1 should be performed.
/// A value of IMAGE_WEAK_EXTERN_SEARCH_ALIAS indicates that sym1 is an alias for sym2.
/// </summary>
public uint AuxFormat3Characteristics;
/// <summary>
/// Unused
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte[] AuxFormat3Unused;
#endregion
#region Auxiliary Format 4: Files
// This format follows a symbol-table record with storage class FILE (103).
// The symbol name itself should be .file, and the auxiliary record that
// follows it gives the name of a source-code file.
/// <summary>
/// An ANSI string that gives the name of the source file. This is padded
/// with nulls if it is less than the maximum length.
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)]
public byte[] AuxFormat4FileName;
#endregion
#region Auxiliary Format 5: Section Definitions
// This format follows a symbol-table record that defines a section. Such a
// record has a symbol name that is the name of a section (such as .text or
// .drectve) and has storage class STATIC (3). The auxiliary record provides
// information about the section to which it refers. Thus, it duplicates some
// of the information in the section header.
/// <summary>
/// The size of section data; the same as SizeOfRawData in the section header.
/// </summary>
public uint AuxFormat5Length;
/// <summary>
/// The number of relocation entries for the section.
/// </summary>
public ushort AuxFormat5NumberOfRelocations;
/// <summary>
/// The number of line-number entries for the section.
/// </summary>
public ushort AuxFormat5NumberOfLinenumbers;
/// <summary>
/// The checksum for communal data. It is applicable if the IMAGE_SCN_LNK_COMDAT
/// flag is set in the section header.
/// </summary>
public uint AuxFormat5CheckSum;
/// <summary>
/// One-based index into the section table for the associated section. This is
/// used when the COMDAT selection setting is 5.
/// </summary>
public ushort AuxFormat5Number;
/// <summary>
/// The COMDAT selection number. This is applicable if the section is a
/// COMDAT section.
/// </summary>
public byte AuxFormat5Selection;
/// <summary>
/// Unused
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] AuxFormat5Unused;
#endregion
#region Auxiliary Format 6: CLR Token Definition (Object Only)
// This auxiliary symbol generally follows the IMAGE_SYM_CLASS_CLR_TOKEN. It is
// used to associate a token with the COFF symbol table's namespace.
/// <summary>
/// Must be IMAGE_AUX_SYMBOL_TYPE_TOKEN_DEF (1).
/// </summary>
public byte AuxFormat6AuxType;
/// <summary>
/// Reserved, must be zero.
/// </summary>
public byte AuxFormat6Reserved1;
/// <summary>
/// The symbol index of the COFF symbol to which this CLR token definition refers.
/// </summary>
public uint AuxFormat6SymbolTableIndex;
/// <summary>
/// Reserved, must be zero.
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
public byte[] AuxFormat6Reserved2;
#endregion
#endregion
}
}

View File

@@ -0,0 +1,40 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The system handles each icon and cursor as a single file. However, these are stored in
/// .res files and in executable files as a group of icon resources or a group of cursor
/// resources. The file formats of icon and cursor resources are similar. In the .res file
/// a resource group header follows all of the individual icon or cursor group components.
///
/// The format of each icon component closely resembles the format of the .ico file. Each
/// icon image is stored in a BITMAPINFO structure followed by the color device-independent
/// bitmap (DIB) bits of the icon's XOR mask. The monochrome DIB bits of the icon's AND
/// mask follow the color DIB bits.
///
/// The format of each cursor component resembles the format of the .cur file. Each cursor
/// image is stored in a BITMAPINFO structure followed by the monochrome DIB bits of the
/// cursor's XOR mask, and then by the monochrome DIB bits of the cursor's AND mask. Note
/// that there is a difference in the bitmaps of the two resources: Unlike icons, cursor
/// XOR masks do not have color DIB bits. Although the bitmaps of the cursor masks are
/// monochrome and do not have DIB headers or color tables, the bits are still in DIB
/// format with respect to alignment and direction. Another significant difference
/// between cursors and icons is that cursors have a hotspot and icons do not.
///
/// The group header for both icon and cursor resources consists of a NEWHEADER structure
/// plus one or more RESDIR structures. There is one RESDIR structure for each icon or
/// cursor. The group header contains the information an application needs to select the
/// correct icon or cursor to display. Both the group header and the data that repeats for
/// each icon or cursor in the group have a fixed length. This allows the application to
/// randomly access the information.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/menurc/resource-file-formats"/>
public class CursorAndIconResource
{
/// <summary>
/// Describes keyboard accelerator characteristics.
/// </summary>
public NewHeader NEWHEADER;
// TODO: Add array of entries in the resource
}
}

View File

@@ -0,0 +1,29 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// Each data directory gives the address and size of a table or string that Windows uses.
/// These data directory entries are all loaded into memory so that the system can use them
/// at run time.
///
/// Also, do not assume that the RVAs in this table point to the beginning of a section or
/// that the sections that contain specific tables have specific names.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Sequential)]
public class DataDirectory
{
/// <summary>
/// The first field, VirtualAddress, is actually the RVA of the table. The RVA
/// is the address of the table relative to the base address of the image when
/// the table is loaded.
/// </summary>
public uint VirtualAddress;
/// <summary>
/// The second field gives the size in bytes.
/// </summary>
public uint Size;
}
}

View File

@@ -0,0 +1,66 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// Image files contain an optional debug directory that indicates what form
/// of debug information is present and where it is. This directory consists
/// of an array of debug directory entries whose location and size are indicated
/// in the image optional header.
///
/// The debug directory can be in a discardable .debug section (if one exists),
/// or it can be included in any other section in the image file, or not be in
/// a section at all.
///
/// Each debug directory entry identifies the location and size of a block of
/// debug information. The specified RVA can be zero if the debug information
/// is not covered by a section header (that is, it resides in the image file
/// and is not mapped into the run-time address space). If it is mapped, the
/// RVA is its address.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Sequential)]
public class DebugDirectoryEntry
{
/// <summary>
/// Reserved, must be zero.
/// </summary>
public uint Characteristics;
/// <summary>
/// The time and date that the debug data was created.
/// </summary>
public uint TimeDateStamp;
/// <summary>
/// The major version number of the debug data format.
/// </summary>
public ushort MajorVersion;
/// <summary>
/// The minor version number of the debug data format.
/// </summary>
public ushort MinorVersion;
/// <summary>
/// The format of debugging information. This field enables support
/// of multiple debuggers.
/// </summary>
public DebugType DebugType;
/// <summary>
/// The size of the debug data (not including the debug directory itself).
/// </summary>
public uint SizeOfData;
/// <summary>
/// The address of the debug data when loaded, relative to the image base.
/// </summary>
public uint AddressOfRawData;
/// <summary>
/// The file pointer to the debug data.
/// </summary>
public uint PointerToRawData;
}
}

View File

@@ -0,0 +1,38 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The .debug section is used in object files to contain compiler-generated debug
/// information and in image files to contain all of the debug information that is
/// generated. This section describes the packaging of debug information in object
/// and image files.
///
/// The next section describes the format of the debug directory, which can be
/// anywhere in the image. Subsequent sections describe the "groups" in object
/// files that contain debug information.
///
/// The default for the linker is that debug information is not mapped into the
/// address space of the image. A .debug section exists only when debug information
/// is mapped in the address space.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class DebugTable
{
/// <summary>
/// Image files contain an optional debug directory that indicates what form
/// of debug information is present and where it is. This directory consists
/// of an array of debug directory entries whose location and size are
/// indicated in the image optional header.
///
/// The debug directory can be in a discardable .debug section (if one exists),
/// or it can be included in any other section in the image file, or not be
/// in a section at all.
///
/// Each debug directory entry identifies the location and size of a block of
/// debug information. The specified RVA can be zero if the debug information
/// is not covered by a section header (that is, it resides in the image
/// file and is not mapped into the run-time address space). If it is mapped,
/// the RVA is its address.
/// </summary>
public DebugDirectoryEntry[] DebugDirectoryTable;
}
}

View File

@@ -0,0 +1,107 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The delay-load directory table is the counterpart to the import directory
/// table. It can be retrieved through the Delay Import Descriptor entry in
/// the optional header data directories list (offset 200).
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Sequential)]
public class DelayLoadDirectoryTable
{
/// <summary>
/// Must be zero.
/// </summary>
/// <remarks>
/// As yet, no attribute flags are defined. The linker sets this field to
/// zero in the image. This field can be used to extend the record by
/// indicating the presence of new fields, or it can be used to indicate
/// behaviors to the delay or unload helper functions.
/// </remarks>
public uint Attributes;
/// <summary>
/// The RVA of the name of the DLL to be loaded. The name resides in the
/// read-only data section of the image.
/// </summary>
/// <remarks>
/// The name of the DLL to be delay-loaded resides in the read-only data
/// section of the image. It is referenced through the szName field.
/// </remarks>
public uint Name;
/// <summary>
/// The RVA of the module handle (in the data section of the image) of the DLL
/// to be delay-loaded. It is used for storage by the routine that is supplied
/// to manage delay-loading.
/// </summary>
/// <remarks>
/// The handle of the DLL to be delay-loaded is in the data section of the image.
/// The phmod field points to the handle. The supplied delay-load helper uses
/// this location to store the handle to the loaded DLL.
/// </remarks>
public uint ModuleHandle;
/// <summary>
/// The RVA of the delay-load import address table.
/// </summary>
/// <remarks>
/// The delay import address table (IAT) is referenced by the delay import
/// descriptor through the pIAT field. The delay-load helper updates these
/// pointers with the real entry points so that the thunks are no longer in
/// the calling loop. The function pointers are accessed by using the expression
/// pINT->u1.Function.
/// </remarks>
public uint DelayImportAddressTable;
/// <summary>
/// The RVA of the delay-load name table, which contains the names of the imports
/// that might need to be loaded. This matches the layout of the import name table.
/// </summary>
/// <remarks>
/// The delay import name table (INT) contains the names of the imports that might
/// require loading. They are ordered in the same fashion as the function pointers
/// in the IAT. They consist of the same structures as the standard INT and are
/// accessed by using the expression pINT->u1.AddressOfData->Name[0].
/// </remarks>
public uint DelayImportNameTable;
/// <summary>
/// The RVA of the bound delay-load address table, if it exists.
/// </summary>
/// <remarks>
/// The delay bound import address table (BIAT) is an optional table of
/// IMAGE_THUNK_DATA items that is used along with the timestamp field of the
/// delay-load directory table by a post-process binding phase.
/// </remarks>
public uint BoundDelayImportTable;
/// <summary>
/// The RVA of the unload delay-load address table, if it exists. This is an exact
/// copy of the delay import address table. If the caller unloads the DLL, this
/// table should be copied back over the delay import address table so that
/// subsequent calls to the DLL continue to use the thunking mechanism correctly.
/// </summary>
/// <remarks>
/// The delay unload import address table (UIAT) is an optional table of
/// IMAGE_THUNK_DATA items that the unload code uses to handle an explicit unload
/// request. It consists of initialized data in the read-only section that is an
/// exact copy of the original IAT that referred the code to the delay-load thunks.
/// On the unload request, the library can be freed, the *phmod cleared, and the
/// UIAT written over the IAT to restore everything to its preload state.
/// </remarks>
public uint UnloadDelayImportTable;
/// <summary>
/// The timestamp of the DLL to which this image has been bound.
/// </summary>
/// <remarks>
/// The delay bound import address table (BIAT) is an optional table of
/// IMAGE_THUNK_DATA items that is used along with the timestamp field of the
/// delay-load directory table by a post-process binding phase.
/// </remarks>
public uint TimeStamp;
}
}

View File

@@ -0,0 +1,46 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// A dialog box is also one resource entry in the resource file. It consists of one
/// DLGTEMPLATE dialog box header structure plus one DLGITEMTEMPLATE structure for each
/// control in the dialog box. The DLGTEMPLATEEX and the DLGITEMTEMPLATEEX structures
/// describe the format of extended dialog box resources.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/menurc/resource-file-formats"/>
public class DialogBoxResource
{
#region Dialog template
/// <summary>
/// Dialog box header structure
/// </summary>
public DialogTemplate DialogTemplate;
/// <summary>
/// Dialog box extended header structure
/// </summary>
public DialogTemplateExtended ExtendedDialogTemplate;
#endregion
#region Dialog item templates
/// <summary>
/// Following the DLGTEMPLATE header in a standard dialog box template are one or more
/// DLGITEMTEMPLATE structures that define the dimensions and style of the controls in the dialog
/// box. The cdit member specifies the number of DLGITEMTEMPLATE structures in the template.
/// These DLGITEMTEMPLATE structures must be aligned on DWORD boundaries.
/// </summary>
public DialogItemTemplate[] DialogItemTemplates;
/// <summary>
/// Following the DLGTEMPLATEEX header in an extended dialog box template is one or more
/// DLGITEMTEMPLATEEX structures that describe the controls of the dialog box. The cDlgItems
/// member of the DLGITEMTEMPLATEEX structure specifies the number of DLGITEMTEMPLATEEX
/// structures that follow in the template.
/// </summary>
public DialogItemTemplateExtended[] ExtendedDialogItemTemplates;
#endregion
}
}

View File

@@ -0,0 +1,133 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// Defines the dimensions and style of a control in a dialog box. One or more of these
/// structures are combined with a DLGTEMPLATE structure to form a standard template
/// for a dialog box.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-dlgitemtemplate"/>
[StructLayout(LayoutKind.Sequential)]
public class DialogItemTemplate
{
/// <summary>
/// The style of the control. This member can be a combination of window style values
/// (such as WS_BORDER) and one or more of the control style values (such as
/// BS_PUSHBUTTON and ES_LEFT).
/// </summary>
public WindowStyles Style;
/// <summary>
/// The extended styles for a window. This member is not used to create dialog boxes,
/// but applications that use dialog box templates can use it to create other types
/// of windows.
/// </summary>
public ExtendedWindowStyles ExtendedStyle;
/// <summary>
/// The x-coordinate, in dialog box units, of the upper-left corner of the control.
/// This coordinate is always relative to the upper-left corner of the dialog box's
/// client area.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values to screen
/// units (pixels) by using the MapDialogRect function.
/// </remarks>
public short PositionX;
/// <summary>
/// The y-coordinate, in dialog box units, of the upper-left corner of the control.
/// This coordinate is always relative to the upper-left corner of the dialog box's
/// client area.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values to screen
/// units (pixels) by using the MapDialogRect function.
/// </remarks>
public short PositionY;
/// <summary>
/// The width, in dialog box units, of the control.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values to screen
/// units (pixels) by using the MapDialogRect function.
/// </remarks>
public short WidthX;
/// <summary>
/// The height, in dialog box units, of the control.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values to screen
/// units (pixels) by using the MapDialogRect function.
/// </remarks>
public short HeightY;
/// <summary>
/// The control identifier.
/// </summary>
public ushort ID;
// In a standard template for a dialog box, the DLGITEMTEMPLATE structure is always immediately
// followed by three variable-length arrays specifying the class, title, and creation data for
// the control. Each array consists of one or more 16-bit elements.
//
// Each DLGITEMTEMPLATE structure in the template must be aligned on a DWORD boundary. The class
// and title arrays must be aligned on WORD boundaries. The creation data array must be aligned
// on a WORD boundary.
/// <summary>
/// Immediately following each DLGITEMTEMPLATE structure is a class array that specifies the window
/// class of the control. If the first element of this array is any value other than 0xFFFF, the
/// system treats the array as a null-terminated Unicode string that specifies the name of a
/// registered window class. If the first element is 0xFFFF, the array has one additional element
/// that specifies the ordinal value of a predefined system class.
/// </summary>
/// <remarks>
/// If you specify character strings in the class and title arrays, you must use Unicode strings. Use the
/// MultiByteToWideChar function to generate Unicode strings from ANSI strings.
/// </remarks>
public string ClassResource;
/// <summary>
/// The ordinal value of a predefined system class.
/// </summary>
public DialogItemTemplateOrdinal ClassResourceOrdinal;
/// <summary>
/// Following the class array is a title array that contains the initial text or resource identifier
/// of the control. If the first element of this array is 0xFFFF, the array has one additional element
/// that specifies an ordinal value of a resource, such as an icon, in an executable file. You can use
/// a resource identifier for controls, such as static icon controls, that load and display an icon
/// or other resource rather than text. If the first element is any value other than 0xFFFF, the system
/// treats the array as a null-terminated Unicode string that specifies the initial text.
/// </summary>
/// <remarks>
/// If you specify character strings in the class and title arrays, you must use Unicode strings. Use the
/// MultiByteToWideChar function to generate Unicode strings from ANSI strings.
/// </remarks>
public string TitleResource;
/// <summary>
/// An ordinal value of a resource, such as an icon, in an executable file
/// </summary>
public ushort TitleResourceOrdinal;
/// <summary>
/// The creation data array begins at the next WORD boundary after the title array. This creation data
/// can be of any size and format. If the first word of the creation data array is nonzero, it indicates
/// the size, in bytes, of the creation data (including the size word).
/// </summary>
public ushort CreationDataSize;
/// <summary>
/// The creation data array begins at the next WORD boundary after the title array. This creation data
/// can be of any size and format. The control's window procedure must be able to interpret the data.
/// When the system creates the control, it passes a pointer to this data in the lParam parameter of the
/// WM_CREATE message that it sends to the control.
/// </summary>
public byte[] CreationData;
}
}

View File

@@ -0,0 +1,131 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// A block of text used by an extended dialog box template to describe the extended dialog box.
/// For a description of the format of an extended dialog box template, see DLGTEMPLATEEX.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/dlgbox/dlgitemtemplateex"/>
[StructLayout(LayoutKind.Sequential)]
public class DialogItemTemplateExtended
{
/// <summary>
/// The help context identifier for the control. When the system sends a WM_HELP message,
/// it passes the helpID value in the dwContextId member of the HELPINFO structure.
/// </summary>
public uint HelpID;
/// <summary>
/// The extended styles for a window. This member is not used to create controls in dialog
/// boxes, but applications that use dialog box templates can use it to create other types
/// of windows.
/// </summary>
public ExtendedWindowStyles ExtendedStyle;
/// <summary>
/// The style of the control. This member can be a combination of window style values
/// (such as WS_BORDER) and one or more of the control style values (such as
/// BS_PUSHBUTTON and ES_LEFT).
/// </summary>
public WindowStyles Style;
/// <summary>
/// The x-coordinate, in dialog box units, of the upper-left corner of the control.
/// This coordinate is always relative to the upper-left corner of the dialog box's
/// client area.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values to screen
/// units (pixels) by using the MapDialogRect function.
/// </remarks>
public short PositionX;
/// <summary>
/// The y-coordinate, in dialog box units, of the upper-left corner of the control.
/// This coordinate is always relative to the upper-left corner of the dialog box's
/// client area.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values to screen
/// units (pixels) by using the MapDialogRect function.
/// </remarks>
public short PositionY;
/// <summary>
/// The width, in dialog box units, of the control.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values to screen
/// units (pixels) by using the MapDialogRect function.
/// </remarks>
public short WidthX;
/// <summary>
/// The height, in dialog box units, of the control.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values to screen
/// units (pixels) by using the MapDialogRect function.
/// </remarks>
public short HeightY;
/// <summary>
/// The control identifier.
/// </summary>
public uint ID;
/// <summary>
/// A variable-length array of 16-bit elements that specifies the window class of the control. If
/// the first element of this array is any value other than 0xFFFF, the system treats the array as
/// a null-terminated Unicode string that specifies the name of a registered window class.
///
/// If the first element is 0xFFFF, the array has one additional element that specifies the ordinal
/// value of a predefined system class.
/// </summary>
/// <remarks>
/// If you specify character strings in the class and title arrays, you must use Unicode strings. Use the
/// MultiByteToWideChar function to generate Unicode strings from ANSI strings.
/// </remarks>
public string ClassResource;
/// <summary>
/// The ordinal value of a predefined system class.
/// </summary>
public DialogItemTemplateOrdinal ClassResourceOrdinal;
/// <summary>
/// A variable-length array of 16-bit elements that contains the initial text or resource identifier of the
/// control. If the first element of this array is 0xFFFF, the array has one additional element that
/// specifies the ordinal value of a resource, such as an icon, in an executable file. You can use a
/// resource identifier for controls, such as static icon controls, that load and display an icon or other
/// resource rather than text. If the first element is any value other than 0xFFFF, the system treats the
/// array as a null-terminated Unicode string that specifies the initial text.
/// </summary>
/// <remarks>
/// If you specify character strings in the class and title arrays, you must use Unicode strings. Use the
/// MultiByteToWideChar function to generate Unicode strings from ANSI strings.
/// </remarks>
public string TitleResource;
/// <summary>
/// An ordinal value of a resource, such as an icon, in an executable file
/// </summary>
public ushort TitleResourceOrdinal;
/// <summary>
/// The creation data array begins at the next WORD boundary after the title array. This creation data
/// can be of any size and format. If the first word of the creation data array is nonzero, it indicates
/// the size, in bytes, of the creation data (including the size word).
/// </summary>
public ushort CreationDataSize;
/// <summary>
/// The creation data array begins at the next WORD boundary after the title array. This creation data
/// can be of any size and format. The control's window procedure must be able to interpret the data.
/// When the system creates the control, it passes a pointer to this data in the lParam parameter of the
/// WM_CREATE message that it sends to the control.
/// </summary>
public byte[] CreationData;
}
}

View File

@@ -0,0 +1,163 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// Defines the dimensions and style of a dialog box. This structure, always the first
/// in a standard template for a dialog box, also specifies the number of controls in
/// the dialog box and therefore specifies the number of subsequent DLGITEMTEMPLATE
/// structures in the template.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-dlgtemplate"/>
[StructLayout(LayoutKind.Sequential)]
public class DialogTemplate
{
/// <summary>
/// The style of the dialog box. This member can be a combination of window style
/// values (such as WS_CAPTION and WS_SYSMENU) and dialog box style values (such
/// as DS_CENTER).
///
/// If the style member includes the DS_SETFONT style, the header of the dialog box
/// template contains additional data specifying the font to use for text in the
/// client area and controls of the dialog box. The font data begins on the WORD
/// boundary that follows the title array. The font data specifies a 16-bit point
/// size value and a Unicode font name string. If possible, the system creates a
/// font according to the specified values. Then the system sends a WM_SETFONT
/// message to the dialog box and to each control to provide a handle to the font.
/// If DS_SETFONT is not specified, the dialog box template does not include the
/// font data.
///
/// The DS_SHELLFONT style is not supported in the DLGTEMPLATE header.
/// </summary>
public WindowStyles Style;
/// <summary>
/// The extended styles for a window. This member is not used to create dialog boxes,
/// but applications that use dialog box templates can use it to create other types
/// of windows.
/// </summary>
public ExtendedWindowStyles ExtendedStyle;
/// <summary>
/// The number of items in the dialog box.
/// </summary>
public ushort ItemCount;
/// <summary>
/// The x-coordinate, in dialog box units, of the upper-left corner of the dialog box.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values
/// to screen units (pixels) by using the MapDialogRect function.
/// </remarks>
public short PositionX;
/// <summary>
/// The y-coordinate, in dialog box units, of the upper-left corner of the dialog box.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values
/// to screen units (pixels) by using the MapDialogRect function.
/// </remarks>
public short PositionY;
/// <summary>
/// The width, in dialog box units, of the dialog box.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values
/// to screen units (pixels) by using the MapDialogRect function.
/// </remarks>
public short WidthX;
/// <summary>
/// The height, in dialog box units, of the dialog box.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values
/// to screen units (pixels) by using the MapDialogRect function.
/// </remarks>
public short HeightY;
// In a standard template for a dialog box, the DLGTEMPLATE structure is always immediately
// followed by three variable-length arrays that specify the menu, class, and title for the
// dialog box. When the DS_SETFONT style is specified, these arrays are also followed by a
// 16-bit value specifying point size and another variable-length array specifying a
// typeface name. Each array consists of one or more 16-bit elements. The menu, class, title,
// and font arrays must be aligned on WORD boundaries.
/// <summary>
/// Immediately following the DLGTEMPLATE structure is a menu array that identifies a menu
/// resource for the dialog box. If the first element of this array is 0x0000, the dialog box
/// has no menu and the array has no other elements. If the first element is 0xFFFF, the array
/// has one additional element that specifies the ordinal value of a menu resource in an
/// executable file. If the first element has any other value, the system treats the array as
/// a null-terminated Unicode string that specifies the name of a menu resource in an executable
/// file.
/// </summary>
/// <remarks>
/// If you specify character strings in the menu, class, title, or typeface arrays, you must use
/// Unicode strings.
/// </remarks>
public string MenuResource;
/// <summary>
/// The ordinal value of a menu resource in an executable file.
/// </summary>
public ushort MenuResourceOrdinal;
/// <summary>
/// Following the menu array is a class array that identifies the window class of the dialog box.
/// If the first element of the array is 0x0000, the system uses the predefined dialog box class
/// for the dialog box and the array has no other elements. If the first element is 0xFFFF,
/// the array has one additional element that specifies the ordinal value of a predefined system
/// window class. If the first element has any other value, the system treats the array as a
/// null-terminated Unicode string that specifies the name of a registered window class.
/// </summary>
/// <remarks>
/// If you specify character strings in the menu, class, title, or typeface arrays, you must use
/// Unicode strings.
/// </remarks>
public string ClassResource;
/// <summary>
/// The ordinal value of a predefined system class.
/// </summary>
public ushort ClassResourceOrdinal;
/// <summary>
/// Following the class array is a title array that specifies a null-terminated Unicode string
/// that contains the title of the dialog box. If the first element of this array is 0x0000,
/// the dialog box has no title and the array has no other elements.
/// </summary>
/// <remarks>
/// If you specify character strings in the menu, class, title, or typeface arrays, you must use
/// Unicode strings.
/// </remarks>
public string TitleResource;
/// <summary>
/// The 16-bit point size value and the typeface array follow the title array, but only if the
/// style member specifies the DS_SETFONT style. The point size value specifies the point size
/// of the font to use for the text in the dialog box and its controls. When these values are
/// specified, the system creates a font having the specified size and typeface (if possible)
/// and sends a WM_SETFONT message to the dialog box procedure and the control window
/// procedures as it creates the dialog box and controls.
/// </summary>
public ushort PointSizeValue;
/// <summary>
/// The 16-bit point size value and the typeface array follow the title array, but only if the
/// style member specifies the DS_SETFONT style. The typeface array is a null-terminated Unicode
/// string specifying the name of the typeface for the font. When these values are specified,
/// the system creates a font having the specified size and typeface (if possible) and sends a
/// WM_SETFONT message to the dialog box procedure and the control window procedures as it
/// creates the dialog box and controls.
/// </summary>
/// <remarks>
/// If you specify character strings in the menu, class, title, or typeface arrays, you must use
/// Unicode strings.
/// </remarks>
public string Typeface;
}
}

View File

@@ -0,0 +1,188 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// An extended dialog box template begins with a DLGTEMPLATEEX header that describes
/// the dialog box and specifies the number of controls in the dialog box. For each
/// control in a dialog box, an extended dialog box template has a block of data that
/// uses the DLGITEMTEMPLATEEX format to describe the control.
///
/// The DLGTEMPLATEEX structure is not defined in any standard header file. The
/// structure definition is provided here to explain the format of an extended template
/// for a dialog box.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/dlgbox/dlgtemplateex"/>
[StructLayout(LayoutKind.Sequential)]
public class DialogTemplateExtended
{
/// <summary>
/// The version number of the extended dialog box template. This member must be
/// set to 1.
/// </summary>
public ushort Version;
/// <summary>
/// Indicates whether a template is an extended dialog box template. If signature
/// is 0xFFFF, this is an extended dialog box template. In this case, the dlgVer
/// member specifies the template version number. If signature is any value other
/// than 0xFFFF, this is a standard dialog box template that uses the DLGTEMPLATE
/// and DLGITEMTEMPLATE structures.
/// </summary>
public ushort Signature;
/// <summary>
/// The help context identifier for the dialog box window. When the system sends a
/// WM_HELP message, it passes this value in the wContextId member of the HELPINFO
/// structure.
/// </summary>
public uint HelpID;
/// <summary>
/// The extended windows styles. This member is not used when creating dialog boxes,
/// but applications that use dialog box templates can use it to create other types
/// of windows.
/// </summary>
public ExtendedWindowStyles ExtendedStyle;
/// <summary>
/// The style of the dialog box.
///
/// If style includes the DS_SETFONT or DS_SHELLFONT dialog box style, the DLGTEMPLATEEX
/// header of the extended dialog box template contains four additional members (pointsize,
/// weight, italic, and typeface) that describe the font to use for the text in the client
/// area and controls of the dialog box. If possible, the system creates a font according
/// to the values specified in these members. Then the system sends a WM_SETFONT message
/// to the dialog box and to each control to provide a handle to the font.
/// </summary>
public WindowStyles Style;
/// <summary>
/// The number of controls in the dialog box.
/// </summary>
public ushort DialogItems;
/// <summary>
/// The x-coordinate, in dialog box units, of the upper-left corner of the dialog box.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values
/// to screen units (pixels) by using the MapDialogRect function.
/// </remarks>
public short PositionX;
/// <summary>
/// The y-coordinate, in dialog box units, of the upper-left corner of the dialog box.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values
/// to screen units (pixels) by using the MapDialogRect function.
/// </remarks>
public short PositionY;
/// <summary>
/// The width, in dialog box units, of the dialog box.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values
/// to screen units (pixels) by using the MapDialogRect function.
/// </remarks>
public short WidthX;
/// <summary>
/// The height, in dialog box units, of the dialog box.
/// </summary>
/// <remarks>
/// The x, y, cx, and cy members specify values in dialog box units. You can convert these values
/// to screen units (pixels) by using the MapDialogRect function.
/// </remarks>
public short HeightY;
/// <summary>
/// A variable-length array of 16-bit elements that identifies a menu resource for the dialog box.
/// If the first element of this array is 0x0000, the dialog box has no menu and the array has no
/// other elements. If the first element is 0xFFFF, the array has one additional element that
/// specifies the ordinal value of a menu resource in an executable file. If the first element has
/// any other value, the system treats the array as a null-terminated Unicode string that specifies
/// the name of a menu resource in an executable file.
/// </summary>
/// <remarks>
/// If you specify character strings in the class and title arrays, you must use Unicode strings. Use the
/// MultiByteToWideChar function to generate Unicode strings from ANSI strings.
/// </remarks>
public string MenuResource;
/// <summary>
/// The ordinal value of a menu resource in an executable file.
/// </summary>
public ushort MenuResourceOrdinal;
/// <summary>A variable-length array of 16-bit elements that identifies the window class of the
/// dialog box. If the first element of the array is 0x0000, the system uses the predefined dialog
/// box class for the dialog box and the array has no other elements. If the first element is 0xFFFF,
/// the array has one additional element that specifies the ordinal value of a predefined system
/// window class. If the first element has any other value, the system treats the array as a
/// null-terminated Unicode string that specifies the name of a registered window class.
/// </summary>
/// <remarks>
/// If you specify character strings in the class and title arrays, you must use Unicode strings. Use the
/// MultiByteToWideChar function to generate Unicode strings from ANSI strings.
/// </remarks>
public string ClassResource;
/// <summary>
/// The ordinal value of a predefined system window class.
/// </summary>
public ushort ClassResourceOrdinal;
/// <summary>
/// The title of the dialog box. If the first element of this array is 0x0000, the dialog box has no
/// title and the array has no other elements.
/// </summary>
/// <remarks>
/// If you specify character strings in the class and title arrays, you must use Unicode strings. Use the
/// MultiByteToWideChar function to generate Unicode strings from ANSI strings.
/// </remarks>
public string TitleResource;
/// <summary>
/// The point size of the font to use for the text in the dialog box and its controls.
///
/// This member is present only if the style member specifies DS_SETFONT or DS_SHELLFONT.
/// </summary>
public ushort PointSize;
/// <summary>
/// The weight of the font. Note that, although this can be any of the values listed for the lfWeight
/// member of the LOGFONT structure, any value that is used will be automatically changed to FW_NORMAL.
///
/// This member is present only if the style member specifies DS_SETFONT or DS_SHELLFONT.
/// </summary>
public ushort Weight;
/// <summary>
/// Indicates whether the font is italic. If this value is TRUE, the font is italic.
///
/// This member is present only if the style member specifies DS_SETFONT or DS_SHELLFONT.
/// </summary>
public byte Italic;
/// <summary>
/// The character set to be used. For more information, see the lfcharset member of LOGFONT.
///
/// This member is present only if the style member specifies DS_SETFONT or DS_SHELLFONT.
/// </summary>
public byte CharSet;
/// <summary>
/// The name of the typeface for the font.
///
/// This member is present only if the style member specifies DS_SETFONT or DS_SHELLFONT.
/// </summary>
/// <remarks>
/// If you specify character strings in the class and title arrays, you must use Unicode strings. Use the
/// MultiByteToWideChar function to generate Unicode strings from ANSI strings.
/// </remarks>
public string Typeface;
}
}

View File

@@ -0,0 +1,21 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// Contains the information necessary for an application to access a specific font. The structure
/// definition provided here is for explanation only; it is not present in any standard header file.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/menurc/direntry"/>
public class DirEntry
{
/// <summary>
/// A unique ordinal identifier for an individual font in a font resource group.
/// </summary>
public ushort FontOrdinal;
/// <summary>
/// The FONTDIRENTRY structure for the specified font directly follows the DIRENTRY structure
/// for that font.
/// </summary>
public FontDirEntry Entry;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,139 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The following list describes the Microsoft PE executable format, with the
/// base of the image header at the top. The section from the MS-DOS 2.0
/// Compatible EXE Header through to the unused section just before the PE header
/// is the MS-DOS 2.0 Section, and is used for MS-DOS compatibility only.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class Executable
{
/// <summary>
/// MS-DOS executable stub
/// </summary>
public MSDOS.Executable Stub { get; set; }
/// <summary>
/// After the MS-DOS stub, at the file offset specified at offset 0x3c, is a 4-byte
/// signature that identifies the file as a PE format image file. This signature is "PE\0\0"
/// (the letters "P" and "E" followed by two null bytes).
/// </summary>
public byte[] Signature { get; set; }
/// <summary>
/// COFF file header
/// </summary>
public COFFFileHeader COFFFileHeader { get; set; }
/// <summary>
/// Optional header
/// </summary>
public OptionalHeader OptionalHeader { get; set; }
// TODO: Support grouped sections in section reading and parsing
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#grouped-sections-object-only
// Grouped sections are ordered and mean that the data in the sections contributes
// to the "base" section (the one without the "$X" suffix). This may negatively impact
// the use of some of the different types of executables.
/// <summary>
/// Section table
/// </summary>
public SectionHeader[] SectionTable { get; set; }
/// <summary>
/// COFF symbol table
/// </summary>
public COFFSymbolTableEntry[] COFFSymbolTable { get; set; }
/// <summary>
/// COFF string table
/// </summary>
public COFFStringTable COFFStringTable { get; set; }
/// <summary>
/// Attribute certificate table
/// </summary>
public AttributeCertificateTableEntry[] AttributeCertificateTable { get; set; }
/// <summary>
/// Delay-load directory table
/// </summary>
public DelayLoadDirectoryTable DelayLoadDirectoryTable { get; set; }
#region Named Sections
// .cormeta - CLR metadata is stored in this section. It is used to indicate that
// the object file contains managed code. The format of the metadata is not
// documented, but can be handed to the CLR interfaces for handling metadata.
/// <summary>
/// Base relocation table (.reloc)
/// </summary>
public BaseRelocationBlock[] BaseRelocationTable { get; set; }
/// <summary>
/// Debug table (.debug*)
/// </summary>
public DebugTable DebugTable { get; set; }
// .drectve - A section is a directive section if it has the IMAGE_SCN_LNK_INFO
// flag set in the section header and has the .drectve section name. The linker
// removes a .drectve section after processing the information, so the section
// does not appear in the image file that is being linked.
//
// A .drectve section consists of a string of text that can be encoded as ANSI
// or UTF-8. If the UTF-8 byte order marker (BOM, a three-byte prefix that
// consists of 0xEF, 0xBB, and 0xBF) is not present, the directive string is
// interpreted as ANSI. The directive string is a series of linker options that
// are separated by spaces. Each option contains a hyphen, the option name, and
// any appropriate attribute. If an option contains spaces, the option must be
// enclosed in quotes. The .drectve section must not have relocations or line
// numbers.
//
// TODO: Can we implement reading/parsing the .drectve section?
/// <summary>
/// Export table (.edata)
/// </summary>
public ExportTable ExportTable { get; set; }
/// <summary>
/// Import table (.idata)
/// </summary>
public ImportTable ImportTable { get; set; }
/// <summary>
/// Resource directory table (.rsrc)
/// </summary>
public ResourceDirectoryTable ResourceDirectoryTable { get; set; }
// .sxdata - The valid exception handlers of an object are listed in the .sxdata
// section of that object. The section is marked IMAGE_SCN_LNK_INFO. It contains
// the COFF symbol index of each valid handler, using 4 bytes per index.
//
// Additionally, the compiler marks a COFF object as registered SEH by emitting
// the absolute symbol "@feat.00" with the LSB of the value field set to 1. A
// COFF object with no registered SEH handlers would have the "@feat.00" symbol,
// but no .sxdata section.
//
// TODO: Can we implement reading/parsing the .sxdata section?
#endregion
// TODO: Implement and/or document the following non-modeled parts:
// - Delay-Load Import Tables
// - [The Delay-Load Directory Table]
// - Delay Import Address Table
// - Delay Import Name Table
// - Delay Bound Import Address Table
// - Delay Unload Import Address Table
// - The .pdata Section [Multiple formats per entry]
// - The .tls Section
// - TLS Callback Functions
// - [The Load Configuration Structure (Image Only)]
// TODO: Determine if "Archive (Library) File Format" is worth modelling
}
}

View File

@@ -0,0 +1,37 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The export address table contains the address of exported entry points
/// and exported data and absolutes. An ordinal number is used as an index
/// into the export address table.
///
/// Each entry in the export address table is a field that uses one of two
/// formats in the following table. If the address specified is not within
/// the export section (as defined by the address and length that are
/// indicated in the optional header), the field is an export RVA, which is
/// an actual address in code or data. Otherwise, the field is a forwarder RVA,
/// which names a symbol in another DLL.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Explicit)]
public class ExportAddressTableEntry
{
/// <summary>
/// The address of the exported symbol when loaded into memory, relative to
/// the image base. For example, the address of an exported function.
/// </summary>
[FieldOffset(0)] public uint ExportRVA;
/// <summary>
/// The pointer to a null-terminated ASCII string in the export section. This
/// string must be within the range that is given by the export table data
/// directory entry. See Optional Header Data Directories (Image Only). This
/// string gives the DLL name and the name of the export (for example,
/// "MYDLL.expfunc") or the DLL name and the ordinal number of the export
/// (for example, "MYDLL.#27").
/// </summary>
[FieldOffset(0)] public uint ForwarderRVA;
}
}

View File

@@ -0,0 +1,81 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The export symbol information begins with the export directory table,
/// which describes the remainder of the export symbol information. The
/// export directory table contains address information that is used to resolve
/// imports to the entry points within this image.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Sequential)]
public class ExportDirectoryTable
{
/// <summary>
/// Reserved, must be 0.
/// </summary>
public uint ExportFlags;
/// <summary>
/// The time and date that the export data was created.
/// </summary>
public uint TimeDateStamp;
/// <summary>
/// The major version number. The major and minor version numbers can be set
/// by the user.
/// </summary>
public ushort MajorVersion;
/// <summary>
/// The minor version number.
/// </summary>
public ushort MinorVersion;
/// <summary>
/// The address of the ASCII string that contains the name of the DLL. This
/// address is relative to the image base.
/// </summary>
public uint NameRVA;
/// <summary>
/// ASCII string that contains the name of the DLL.
/// </summary>
public string Name;
/// <summary>
/// The starting ordinal number for exports in this image. This field specifies
/// the starting ordinal number for the export address table. It is usually set
/// to 1.
/// </summary>
public uint OrdinalBase;
/// <summary>
/// The number of entries in the export address table.
/// </summary>
public uint AddressTableEntries;
/// <summary>
/// The number of entries in the name pointer table. This is also the number of
/// entries in the ordinal table.
/// </summary>
public uint NumberOfNamePointers;
/// <summary>
/// The address of the export address table, relative to the image base.
/// </summary>
public uint ExportAddressTableRVA;
/// <summary>
/// The address of the export name pointer table, relative to the image base.
/// The table size is given by the Number of Name Pointers field.
/// </summary>
public uint NamePointerRVA;
/// <summary>
/// The address of the ordinal table, relative to the image base.
/// </summary>
public uint OrdinalTableRVA;
}
}

View File

@@ -0,0 +1,18 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The export name pointer table is an array of addresses (RVAs) into the export name table.
/// The pointers are 32 bits each and are relative to the image base. The pointers are ordered
/// lexically to allow binary searches.
///
/// An export name is defined only if the export name pointer table contains a pointer to it.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class ExportNamePointerTable
{
/// <summary>
/// The pointers are 32 bits each and are relative to the image base.
/// </summary>
public uint[] Pointers;
}
}

View File

@@ -0,0 +1,27 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The export name table contains the actual string data that was pointed to by the export
/// name pointer table. The strings in this table are public names that other images can use
/// to import the symbols. These public export names are not necessarily the same as the
/// private symbol names that the symbols have in their own image file and source code,
/// although they can be.
///
/// Every exported symbol has an ordinal value, which is just the index into the export
/// address table. Use of export names, however, is optional. Some, all, or none of the
/// exported symbols can have export names. For exported symbols that do have export names,
/// corresponding entries in the export name pointer table and export ordinal table work
/// together to associate each name with an ordinal.
///
/// The structure of the export name table is a series of null-terminated ASCII strings
/// of variable length.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class ExportNameTable
{
/// <summary>
/// A series of null-terminated ASCII strings of variable length.
/// </summary>
public string[] Strings;
}
}

View File

@@ -0,0 +1,42 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The export ordinal table is an array of 16-bit unbiased indexes into the export address table.
/// Ordinals are biased by the Ordinal Base field of the export directory table. In other words,
/// the ordinal base must be subtracted from the ordinals to obtain true indexes into the export
/// address table.
///
/// The export name pointer table and the export ordinal table form two parallel arrays that are
/// separated to allow natural field alignment. These two tables, in effect, operate as one table,
/// in which the Export Name Pointer column points to a public (exported) name and the Export
/// Ordinal column gives the corresponding ordinal for that public name. A member of the export
/// name pointer table and a member of the export ordinal table are associated by having the same
/// position (index) in their respective arrays.
///
/// Thus, when the export name pointer table is searched and a matching string is found at position
/// i, the algorithm for finding the symbol's RVA and biased ordinal is:
///
/// i = Search_ExportNamePointerTable(name);
/// ordinal = ExportOrdinalTable[i];
///
/// rva = ExportAddressTable[ordinal];
/// biased_ordinal = ordinal + OrdinalBase;
///
/// When searching for a symbol by(biased) ordinal, the algorithm for finding the symbol's RVA
/// and name is:
///
/// ordinal = biased_ordinal - OrdinalBase;
/// i = Search_ExportOrdinalTable(ordinal);
///
/// rva = ExportAddressTable[ordinal];
/// name = ExportNameTable[i];
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class ExportOrdinalTable
{
/// <summary>
/// An array of 16-bit unbiased indexes into the export address table
/// </summary>
public ushort[] Indexes;
}
}

View File

@@ -0,0 +1,53 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The export data section, named .edata, contains information about symbols that other images
/// can access through dynamic linking. Exported symbols are generally found in DLLs, but DLLs
/// can also import symbols.
///
/// An overview of the general structure of the export section is described below. The tables
/// described are usually contiguous in the file in the order shown (though this is not
/// required). Only the export directory table and export address table are required to export
/// symbols as ordinals. (An ordinal is an export that is accessed directly by its export
/// address table index.) The name pointer table, ordinal table, and export name table all
/// exist to support use of export names.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class ExportTable
{
/// <summary>
/// A table with just one row (unlike the debug directory). This table indicates the
/// locations and sizes of the other export tables.
/// </summary>
public ExportDirectoryTable ExportDirectoryTable;
/// <summary>
/// An array of RVAs of exported symbols. These are the actual addresses of the exported
/// functions and data within the executable code and data sections. Other image files
/// can import a symbol by using an index to this table (an ordinal) or, optionally, by
/// using the public name that corresponds to the ordinal if a public name is defined.
/// </summary>
public ExportAddressTableEntry[] ExportAddressTable;
/// <summary>
/// An array of pointers to the public export names, sorted in ascending order.
/// </summary>
public ExportNamePointerTable NamePointerTable;
/// <summary>
/// An array of the ordinals that correspond to members of the name pointer table. The
/// correspondence is by position; therefore, the name pointer table and the ordinal table
/// must have the same number of members. Each ordinal is an index into the export address
/// table.
/// </summary>
public ExportOrdinalTable OrdinalTable;
/// <summary>
/// A series of null-terminated ASCII strings. Members of the name pointer table point into
/// this area. These names are the public names through which the symbols are imported and
/// exported; they are not necessarily the same as the private names that are used within
/// the image file.
/// </summary>
public ExportNameTable ExportNameTable;
}
}

View File

@@ -0,0 +1,88 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// Contains version information for a file. This information is language and
/// code page independent.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo"/>
[StructLayout(LayoutKind.Sequential)]
public class FixedFileInfo
{
/// <summary>
/// Contains the value 0xFEEF04BD. This is used with the szKey member of the VS_VERSIONINFO
/// structure when searching a file for the FixedFileInfo structure.
/// </summary>
public uint Signature;
/// <summary>
/// The binary version number of this structure. The high-order word of this member contains
/// the major version number, and the low-order word contains the minor version number.
/// </summary>
public uint StrucVersion;
/// <summary>
/// The most significant 32 bits of the file's binary version number. This member is used with
/// FileVersionLS to form a 64-bit value used for numeric comparisons.
/// </summary>
public uint FileVersionMS;
/// <summary>
/// The least significant 32 bits of the file's binary version number. This member is used with
/// FileVersionMS to form a 64-bit value used for numeric comparisons.
/// </summary>
public uint FileVersionLS;
/// <summary>
/// The most significant 32 bits of the binary version number of the product with which this file
/// was distributed. This member is used with ProductVersionLS to form a 64-bit value used for
/// numeric comparisons.
/// </summary>
public uint ProductVersionMS;
/// <summary>
/// The least significant 32 bits of the binary version number of the product with which this file
/// was distributed. This member is used with ProductVersionMS to form a 64-bit value used for
/// numeric comparisons.
/// </summary>
public uint ProductVersionLS;
/// <summary>
/// Contains a bitmask that specifies the valid bits in FileFlags. A bit is valid only if it was
/// defined when the file was created.
/// </summary>
public uint FileFlagsMask;
/// <summary>
/// Contains a bitmask that specifies the Boolean attributes of the file.
/// </summary>
public FixedFileInfoFlags FileFlags;
/// <summary>
/// The operating system for which this file was designed.
/// </summary>
public FixedFileInfoOS FileOS;
/// <summary>
/// The general type of file.
/// </summary>
public FixedFileInfoFileType FileType;
/// <summary>
/// The function of the file. The possible values depend on the value of FileType. For all values
/// of FileType not described in the following list, FileSubtype is zero.
/// </summary>
public FixedFileInfoFileSubtype FileSubtype;
/// <summary>
/// The most significant 32 bits of the file's 64-bit binary creation date and time stamp.
/// </summary>
public uint FileDateMS;
/// <summary>
/// The least significant 32 bits of the file's 64-bit binary creation date and time stamp.
/// </summary>
public uint FileDateLS;
}
}

View File

@@ -0,0 +1,174 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// Contains information about an individual font in a font resource group. The structure definition
/// provided here is for explanation only; it is not present in any standard header file.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/menurc/fontdirentry"/>
public class FontDirEntry
{
/// <summary>
/// A user-defined version number for the resource data that tools can use to read and write
/// resource files.
/// </summary>
public ushort Version;
/// <summary>
/// The size of the file, in bytes.
/// </summary>
public uint Size;
/// <summary>
/// The font supplier's copyright information.
/// </summary>
/// <remarks>60 characters</remarks>
public byte[] Copyright;
/// <summary>
/// The type of font file.
/// </summary>
public ushort Type;
/// <summary>
/// The point size at which this character set looks best.
/// </summary>
public ushort Points;
/// <summary>
/// The vertical resolution, in dots per inch, at which this character set was digitized.
/// </summary>
public ushort VertRes;
/// <summary>
/// The horizontal resolution, in dots per inch, at which this character set was digitized.
/// </summary>
public ushort HorizRes;
/// <summary>
/// The distance from the top of a character definition cell to the baseline of the typographical
/// font.
/// </summary>
public ushort Ascent;
/// <summary>
/// The amount of leading inside the bounds set by the PixHeight member. Accent marks and other
/// diacritical characters can occur in this area.
/// </summary>
public ushort InternalLeading;
/// <summary>
/// The amount of extra leading that the application adds between rows.
/// </summary>
public ushort ExternalLeading;
/// <summary>
/// An italic font if not equal to zero.
/// </summary>
public byte Italic;
/// <summary>
/// An underlined font if not equal to zero.
/// </summary>
public byte Underline;
/// <summary>
/// A strikeout font if not equal to zero.
/// </summary>
public byte StrikeOut;
/// <summary>
/// The weight of the font in the range 0 through 1000. For example, 400 is roman and 700 is bold.
/// If this value is zero, a default weight is used. For additional defined values, see the
/// description of the LOGFONT structure.
/// </summary>
public ushort Weight;
/// <summary>
/// The character set of the font. For predefined values, see the description of the LOGFONT
/// structure.
/// </summary>
public byte CharSet;
/// <summary>
/// The width of the grid on which a vector font was digitized. For raster fonts, if the member
/// is not equal to zero, it represents the width for all the characters in the bitmap. If the
/// member is equal to zero, the font has variable-width characters.
/// </summary>
public ushort PixWidth;
/// <summary>
/// The height of the character bitmap for raster fonts or the height of the grid on which a
/// vector font was digitized.
/// </summary>
public ushort PixHeight;
/// <summary>
/// The pitch and the family of the font. For additional information, see the description of
/// the LOGFONT structure.
/// </summary>
public byte PitchAndFamily;
/// <summary>
/// The average width of characters in the font (generally defined as the width of the letter x).
/// This value does not include the overhang required for bold or italic characters.
/// </summary>
public ushort AvgWidth;
/// <summary>
/// The width of the widest character in the font.
/// </summary>
public ushort MaxWidth;
/// <summary>
/// The first character code defined in the font.
/// </summary>
public byte FirstChar;
/// <summary>
/// The last character code defined in the font.
/// </summary>
public byte LastChar;
/// <summary>
/// The character to substitute for characters not in the font.
/// </summary>
public byte DefaultChar;
/// <summary>
/// The character that will be used to define word breaks for text justification.
/// </summary>
public byte BreakChar;
/// <summary>
/// The number of bytes in each row of the bitmap. This value is always even so that the rows
/// start on word boundaries. For vector fonts, this member has no meaning.
/// </summary>
public ushort WidthBytes;
/// <summary>
/// The offset in the file to a null-terminated string that specifies a device name. For a
/// generic font, this value is zero.
/// </summary>
public uint Device;
/// <summary>
/// The offset in the file to a null-terminated string that names the typeface.
/// </summary>
public uint Face;
/// <summary>
/// This member is reserved.
/// </summary>
public uint Reserved;
/// <summary>
/// The name of the device if this font file is designated for a specific device.
/// </summary>
public string DeviceName;
/// <summary>
/// The typeface name of the font.
/// </summary>
public string FaceName;
}
}

View File

@@ -0,0 +1,21 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// Contains the information necessary for an application to access a specific font. The structure
/// definition provided here is for explanation only; it is not present in any standard header file.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/menurc/fontgrouphdr"/>
public class FontGroupHeader
{
/// <summary>
/// The number of individual fonts associated with this resource.
/// </summary>
public ushort NumberOfFonts;
/// <summary>
/// A structure that contains a unique ordinal identifier for each font in the resource. The DE
/// member is a placeholder for the variable-length array of DIRENTRY structures.
/// </summary>
public DirEntry[] DE;
}
}

View File

@@ -0,0 +1,23 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// One hint/name table suffices for the entire import section.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class HintNameTableEntry
{
/// <summary>
/// An index into the export name pointer table. A match is attempted first
/// with this value. If it fails, a binary search is performed on the DLL's
/// export name pointer table.
/// </summary>
public ushort Hint;
/// <summary>
/// An ASCII string that contains the name to import. This is the string that
/// must be matched to the public name in the DLL. This string is case sensitive
/// and terminated by a null byte.
/// </summary>
public string Name;
}
}

View File

@@ -0,0 +1,36 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The structure and content of the import address table are identical to those of
/// the import lookup table, until the file is bound. During binding, the entries in
/// the import address table are overwritten with the 32-bit (for PE32) or 64-bit
/// (for PE32+) addresses of the symbols that are being imported. These addresses are
/// the actual memory addresses of the symbols, although technically they are still
/// called "virtual addresses." The loader typically processes the binding.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class ImportAddressTableEntry
{
/// <summary>
/// If this bit is set, import by ordinal. Otherwise, import by name. Bit is
/// masked as 0x80000000 for PE32, 0x8000000000000000 for PE32+.
/// </summary>
/// <remarks>Bit 31/63</remarks>
public bool OrdinalNameFlag;
/// <summary>
/// A 16-bit ordinal number. This field is used only if the Ordinal/Name Flag
/// bit field is 1 (import by ordinal). Bits 30-15 or 62-15 must be 0.
/// </summary>
/// <remarks>Bits 15-0</remarks>
public ushort OrdinalNumber;
/// <summary>
/// A 31-bit RVA of a hint/name table entry. This field is used only if the
/// Ordinal/Name Flag bit field is 0 (import by name). For PE32+ bits 62-31
/// must be zero.
/// </summary>
/// <remarks>Bits 30-0</remarks>
public uint HintNameTableRVA;
}
}

View File

@@ -0,0 +1,53 @@
using System.Runtime.InteropServices;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The import information begins with the import directory table, which
/// describes the remainder of the import information. The import directory
/// table contains address information that is used to resolve fixup references
/// to the entry points within a DLL image. The import directory table consists
/// of an array of import directory entries, one entry for each DLL to which
/// the image refers. The last directory entry is empty (filled with null values),
/// which indicates the end of the directory table.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Sequential)]
public class ImportDirectoryTableEntry
{
/// <summary>
/// The RVA of the import lookup table. This table contains a name or ordinal
/// for each import. (The name "Characteristics" is used in Winnt.h, but no
/// longer describes this field.)
/// </summary>
public uint ImportLookupTableRVA;
/// <summary>
/// The stamp that is set to zero until the image is bound. After the image is
/// bound, this field is set to the time/data stamp of the DLL.
/// </summary>
public uint TimeDateStamp;
/// <summary>
/// The index of the first forwarder reference.
/// </summary>
public uint ForwarderChain;
/// <summary>
/// The address of an ASCII string that contains the name of the DLL. This address
/// is relative to the image base.
/// </summary>
public uint NameRVA;
/// <summary>
/// ASCII string that contains the name of the DLL.
/// </summary>
public string Name;
/// <summary>
/// The RVA of the import address table. The contents of this table are identical
/// to the contents of the import lookup table until the image is bound.
/// </summary>
public uint ImportAddressTableRVA;
}
}

View File

@@ -0,0 +1,36 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// An import lookup table is an array of 32-bit numbers for PE32 or an array of
/// 64-bit numbers for PE32+. Each entry uses the bit-field format that is described
/// in the following table. In this format, bit 31 is the most significant bit for
/// PE32 and bit 63 is the most significant bit for PE32+. The collection of these
/// entries describes all imports from a given DLL. The last entry is set to zero
/// (NULL) to indicate the end of the table.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class ImportLookupTableEntry
{
/// <summary>
/// If this bit is set, import by ordinal. Otherwise, import by name. Bit is
/// masked as 0x80000000 for PE32, 0x8000000000000000 for PE32+.
/// </summary>
/// <remarks>Bit 31/63</remarks>
public bool OrdinalNameFlag;
/// <summary>
/// A 16-bit ordinal number. This field is used only if the Ordinal/Name Flag
/// bit field is 1 (import by ordinal). Bits 30-15 or 62-15 must be 0.
/// </summary>
/// <remarks>Bits 15-0</remarks>
public ushort OrdinalNumber;
/// <summary>
/// A 31-bit RVA of a hint/name table entry. This field is used only if the
/// Ordinal/Name Flag bit field is 0 (import by name). For PE32+ bits 62-31
/// must be zero.
/// </summary>
/// <remarks>Bits 30-0</remarks>
public uint HintNameTableRVA;
}
}

View File

@@ -0,0 +1,45 @@
using System.Collections.Generic;
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// All image files that import symbols, including virtually all executable (EXE) files,
/// have an .idata section. A typical file layout for the import information follows:
///
/// - Directory Table
/// Null Directory Entry
/// - DLL1 Import Lookup Table
/// Null
/// - DLL2 Import Lookup Table
/// Null
/// - DLL3 Import Lookup Table
/// Null
/// - Hint-Name Table
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class ImportTable
{
/// <summary>
/// The import information begins with the import directory table, which describes the
/// remainder of the import information.
/// </summary>
public ImportDirectoryTableEntry[] ImportDirectoryTable;
/// <summary>
/// An import lookup table is an array of 32-bit numbers for PE32 or an array of 64-bit
/// numbers for PE32+.
/// </summary>
public Dictionary<int, ImportLookupTableEntry[]> ImportLookupTables;
/// <summary>
/// These addresses are the actual memory addresses of the symbols, although technically
/// they are still called "virtual addresses".
/// </summary>
public Dictionary<int, ImportAddressTableEntry[]> ImportAddressTables;
/// <summary>
/// One hint/name table suffices for the entire import section.
/// </summary>
public HintNameTableEntry[] HintNameTable;
}
}

View File

@@ -0,0 +1,343 @@
namespace BurnOutSharp.Models.PortableExecutable
{
/// <summary>
/// The data directory entry for a pre-reserved SEH load configuration
/// structure must specify a particular size of the load configuration
/// structure because the operating system loader always expects it to
/// be a certain value. In that regard, the size is really only a
/// version check. For compatibility with Windows XP and earlier versions
/// of Windows, the size must be 64 for x86 images.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class LoadConfigurationDirectory
{
/// <summary>
/// Flags that indicate attributes of the file, currently unused.
/// </summary>
public uint Characteristics;
/// <summary>
/// Date and time stamp value. The value is represented in the number of
/// seconds that have elapsed since midnight (00:00:00), January 1, 1970,
/// Universal Coordinated Time, according to the system clock. The time
/// stamp can be printed by using the C runtime (CRT) time function.
/// </summary>
public uint TimeDateStamp;
/// <summary>
/// Major version number.
/// </summary>
public ushort MajorVersion;
/// <summary>
/// Minor version number.
/// </summary>
public ushort MinorVersion;
/// <summary>
/// The global loader flags to clear for this process as the loader starts
/// the process.
/// </summary>
public uint GlobalFlagsClear;
/// <summary>
/// The global loader flags to set for this process as the loader starts
/// the process.
/// </summary>
public uint GlobalFlagsSet;
/// <summary>
/// The default timeout value to use for this process's critical sections
/// that are abandoned.
/// </summary>
public uint CriticalSectionDefaultTimeout;
#region DeCommitFreeBlockThreshold
/// <summary>
/// Memory that must be freed before it is returned to the system, in bytes.
/// </summary>
public uint DeCommitFreeBlockThreshold_PE32;
/// <summary>
/// Memory that must be freed before it is returned to the system, in bytes.
/// </summary>
public ulong DeCommitFreeBlockThreshold_PE32Plus;
#endregion
#region DeCommitTotalFreeThreshold
/// <summary>
/// Total amount of free memory, in bytes.
/// </summary>
public uint DeCommitTotalFreeThreshold_PE32;
/// <summary>
/// Total amount of free memory, in bytes.
/// </summary>
public ulong DeCommitTotalFreeThreshold_PE32Plus;
#endregion
#region LockPrefixTable
/// <summary>
/// [x86 only] The VA of a list of addresses where the LOCK prefix is used so
/// that they can be replaced with NOP on single processor machines.
/// </summary>
public uint LockPrefixTable_PE32;
/// <summary>
/// [x86 only] The VA of a list of addresses where the LOCK prefix is used so
/// that they can be replaced with NOP on single processor machines.
/// </summary>
public ulong LockPrefixTable_PE32Plus;
#endregion
#region MaximumAllocationSize
/// <summary>
/// Maximum allocation size, in bytes.
/// </summary>
public uint MaximumAllocationSize_PE32;
/// <summary>
/// Maximum allocation size, in bytes.
/// </summary>
public ulong MaximumAllocationSize_PE32Plus;
#endregion
#region VirtualMemoryThreshold
/// <summary>
/// Maximum virtual memory size, in bytes.
/// </summary>
public uint VirtualMemoryThreshold_PE32;
/// <summary>
/// Maximum virtual memory size, in bytes.
/// </summary>
public ulong VirtualMemoryThreshold_PE32Plus;
#endregion
#region ProcessAffinityMask
/// <summary>
/// Setting this field to a non-zero value is equivalent to calling
/// SetProcessAffinityMask with this value during process startup (.exe only)
/// </summary>
public uint ProcessAffinityMask_PE32;
/// <summary>
/// Setting this field to a non-zero value is equivalent to calling
/// SetProcessAffinityMask with this value during process startup (.exe only)
/// </summary>
public ulong ProcessAffinityMask_PE32Plus;
#endregion
/// <summary>
/// Process heap flags that correspond to the first argument of the
/// HeapCreate function. These flags apply to the process heap that
/// is created during process startup.
/// </summary>
public uint ProcessHeapFlags;
/// <summary>
/// The service pack version identifier.
/// </summary>
public ushort CSDVersion;
/// <summary>
/// Must be zero.
/// </summary>
public ushort Reserved;
#region EditList
/// <summary>
/// Reserved for use by the system.
/// </summary>
public uint EditList_PE32;
/// <summary>
/// Reserved for use by the system.
/// </summary>
public ulong EditList_PE32Plus;
#endregion
#region SecurityCookie
/// <summary>
/// A pointer to a cookie that is used by Visual C++ or GS implementation.
/// </summary>
public uint SecurityCookie_PE32;
/// <summary>
/// A pointer to a cookie that is used by Visual C++ or GS implementation.
/// </summary>
public ulong SecurityCookie_PE32Plus;
#endregion
#region SEHandlerTable
/// <summary>
/// [x86 only] The VA of the sorted table of RVAs of each valid, unique
/// SE handler in the image.
/// </summary>
public uint SEHandlerTable_PE32;
/// <summary>
/// [x86 only] The VA of the sorted table of RVAs of each valid, unique
/// SE handler in the image.
/// </summary>
public ulong SEHandlerTable_PE32Plus;
#endregion
#region SEHandlerCount
/// <summary>
/// [x86 only] The count of unique handlers in the table.
/// </summary>
public uint SEHandlerCount_PE32;
/// <summary>
/// [x86 only] The count of unique handlers in the table.
/// </summary>
public ulong SEHandlerCount_PE32Plus;
#endregion
#region GuardCFCheckFunctionPointer
/// <summary>
/// The VA where Control Flow Guard check-function pointer is stored.
/// </summary>
public uint GuardCFCheckFunctionPointer_PE32;
/// <summary>
/// The VA where Control Flow Guard check-function pointer is stored.
/// </summary>
public ulong GuardCFCheckFunctionPointer_PE32Plus;
#endregion
#region GuardCFDispatchFunctionPointer
/// <summary>
/// The VA where Control Flow Guard dispatch-function pointer is stored.
/// </summary>
public uint GuardCFDispatchFunctionPointer_PE32;
/// <summary>
/// The VA where Control Flow Guard dispatch-function pointer is stored.
/// </summary>
public ulong GuardCFDispatchFunctionPointer_PE32Plus;
#endregion
#region GuardCFFunctionTable
/// <summary>
/// The VA of the sorted table of RVAs of each Control Flow Guard
/// function in the image.
/// </summary>
public uint GuardCFFunctionTable_PE32;
/// <summary>
/// The VA of the sorted table of RVAs of each Control Flow Guard
/// function in the image.
/// </summary>
public ulong GuardCFFunctionTable_PE32Plus;
#endregion
#region GuardCFFunctionCount
/// <summary>
/// The count of unique RVAs in the above table.
/// </summary>
public uint GuardCFFunctionCount_PE32;
/// <summary>
/// The count of unique RVAs in the above table.
/// </summary>
public ulong GuardCFFunctionCount_PE32Plus;
#endregion
/// <summary>
/// Control Flow Guard related flags.
/// </summary>
public GuardFlags GuardFlags;
/// <summary>
/// Code integrity information.
/// </summary>
/// <remarks>12 bytes</remarks>
public byte[] CodeIntegrity;
#region GuardAddressTakenIatEntryTable
/// <summary>
/// The VA where Control Flow Guard address taken IAT table is stored.
/// </summary>
public uint GuardAddressTakenIatEntryTable_PE32;
/// <summary>
/// The VA where Control Flow Guard address taken IAT table is stored.
/// </summary>
public ulong GuardAddressTakenIatEntryTable_PE32Plus;
#endregion
#region GuardAddressTakenIatEntryCount
/// <summary>
/// The count of unique RVAs in the above table.
/// </summary>
public uint GuardAddressTakenIatEntryCount_PE32;
/// <summary>
/// The count of unique RVAs in the above table.
/// </summary>
public ulong GuardAddressTakenIatEntryCount_PE32Plus;
#endregion
#region GuardLongJumpTargetTable
/// <summary>
/// The VA where Control Flow Guard long jump target table is stored.
/// </summary>
public uint GuardLongJumpTargetTable_PE32;
/// <summary>
/// The VA where Control Flow Guard long jump target table is stored.
/// </summary>
public ulong GuardLongJumpTargetTable_PE32Plus;
#endregion
#region GuardLongJumpTargetCount
/// <summary>
/// The count of unique RVAs in the above table.
/// </summary>
public uint GuardLongJumpTargetCount_PE32;
/// <summary>
/// The count of unique RVAs in the above table.
/// </summary>
public ulong GuardLongJumpTargetCount_PE32Plus;
#endregion
}
}

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