├── .gitattributes ├── .github └── workflows │ └── publish.yml ├── .gitignore ├── .idea └── .idea.ExtensionBlocks │ └── .idea │ ├── .gitignore │ ├── encodings.xml │ ├── indexLayout.xml │ └── vcs.xml ├── ExtensionBlocks.Test ├── ExtensionBlocks.Test.csproj ├── ExtensionBlocks.Test.v2.ncrunchproject └── TestMain.cs ├── ExtensionBlocks.Test2 ├── App.config ├── ExtensionBlocks.Test2.csproj └── Program.cs ├── ExtensionBlocks.sln ├── ExtensionBlocks.v2.ncrunchsolution ├── ExtensionBlocks.v3.ncrunchsolution ├── ExtensionBlocks ├── Beef0000.cs ├── Beef0001.cs ├── Beef0002.cs ├── Beef0003.cs ├── Beef0004.cs ├── Beef0005.cs ├── Beef0006.cs ├── Beef0008.cs ├── Beef0009.cs ├── Beef000a.cs ├── Beef000c.cs ├── Beef000e.cs ├── Beef0010.cs ├── Beef0013.cs ├── Beef0014.cs ├── Beef0016.cs ├── Beef0017.cs ├── Beef0019.cs ├── Beef001a.cs ├── Beef001b.cs ├── Beef001d.cs ├── Beef001e.cs ├── Beef0021.cs ├── Beef0024.cs ├── Beef0025.cs ├── Beef0026.cs ├── Beef0027.cs ├── Beef0029.cs ├── BeefBase.cs ├── BeefPlaceHolder.cs ├── BeefUnknown.cs ├── ExtensionBlocks.csproj ├── ExtensionBlocks.v2.ncrunchproject ├── IExtensionBlock.cs ├── IShellBag.cs ├── MFTInformation.cs ├── PropertySheet.cs ├── PropertyStore.cs ├── ShellBag.cs ├── ShellBag0X31.cs └── Utils.cs ├── License.md ├── README.md └── icon.png /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | 3 | name: publish 4 | on: 5 | workflow_dispatch: # Allow running the workflow manually from the GitHub UI 6 | push: 7 | branches: 8 | - 'main' # Run the workflow when pushing to the main branch 9 | pull_request: 10 | branches: 11 | - '*' # Run the workflow for all pull requests 12 | release: 13 | types: 14 | - published # Run the workflow when a new GitHub release is published 15 | 16 | env: 17 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 18 | DOTNET_NOLOGO: true 19 | NuGetDirectory: ${{ github.workspace}}/nuget 20 | 21 | defaults: 22 | run: 23 | shell: pwsh 24 | 25 | jobs: 26 | create_nuget: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@v4 30 | with: 31 | fetch-depth: 0 # Get all history to allow automatic versioning using MinVer 32 | 33 | # Install the .NET SDK indicated in the global.json file 34 | - name: Setup .NET 35 | uses: actions/setup-dotnet@v4 36 | 37 | # Create the NuGet package in the folder from the environment variable NuGetDirectory 38 | - run: dotnet pack --configuration Release --output ${{ env.NuGetDirectory }} 39 | 40 | # Publish the NuGet package as an artifact,so they can be used in the following jobs 41 | - uses: actions/upload-artifact@v4 42 | with: 43 | name: nuget 44 | if-no-files-found: error 45 | retention-days: 7 46 | path: ${{ env.NuGetDirectory }}/*.nupkg 47 | 48 | validate_nuget: 49 | runs-on: ubuntu-latest 50 | needs: [ create_nuget ] 51 | steps: 52 | # Install the .NET SDK indicated in the global.json file 53 | - name: Setup .NET 54 | uses: actions/setup-dotnet@v4 55 | 56 | # Download the NuGet package created in the previous job 57 | - uses: actions/download-artifact@v4 58 | with: 59 | name: nuget 60 | path: ${{ env.NuGetDirectory }} 61 | 62 | - name: Install nuget validator 63 | run: dotnet tool update Meziantou.Framework.NuGetPackageValidation.Tool --global 64 | 65 | # Validate metadata and content of the NuGet package 66 | # https://www.nuget.org/packages/Meziantou.Framework.NuGetPackageValidation.Tool#readme-body-tab 67 | # If some rules are not applicable, you can disable them 68 | # using the --excluded-rules or --excluded-rule-ids option 69 | - name: Validate package 70 | run: meziantou.validate-nuget-package (Get-ChildItem "${{ env.NuGetDirectory }}/*.nupkg") 71 | 72 | run_test: 73 | runs-on: ubuntu-latest 74 | steps: 75 | - uses: actions/checkout@v4 76 | - name: Setup .NET 77 | uses: actions/setup-dotnet@v4 78 | - name: Run tests 79 | run: dotnet test --configuration Release 80 | 81 | deploy: 82 | # Publish only when creating a GitHub Release 83 | # https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository 84 | # You can update this logic if you want to manage releases differently 85 | if: github.event_name == 'release' 86 | runs-on: ubuntu-latest 87 | needs: [ validate_nuget ] 88 | steps: 89 | # Download the NuGet package created in the previous job 90 | - uses: actions/download-artifact@v4 91 | with: 92 | name: nuget 93 | path: ${{ env.NuGetDirectory }} 94 | 95 | # Install the .NET SDK indicated in the global.json file 96 | - name: Setup .NET Core 97 | uses: actions/setup-dotnet@v4 98 | 99 | # Publish all NuGet packages to NuGet.org 100 | # Use --skip-duplicate to prevent errors if a package with the same version already exists. 101 | # If you retry a failed workflow, already published packages will be skipped without error. 102 | - name: Publish NuGet package 103 | run: | 104 | foreach($file in (Get-ChildItem "${{ env.NuGetDirectory }}" -Recurse -Include *.nupkg)) { 105 | dotnet nuget push $file --api-key "${{ secrets.NUGET_APIKEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate 106 | } 107 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | # DNX 42 | project.lock.json 43 | artifacts/ 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | *.vspx 85 | 86 | # TFS 2012 Local Workspace 87 | $tf/ 88 | 89 | # Guidance Automation Toolkit 90 | *.gpState 91 | 92 | # ReSharper is a .NET coding add-in 93 | _ReSharper*/ 94 | *.[Rr]e[Ss]harper 95 | *.DotSettings.user 96 | 97 | # JustCode is a .NET coding add-in 98 | .JustCode 99 | 100 | # TeamCity is a build add-in 101 | _TeamCity* 102 | 103 | # DotCover is a Code Coverage Tool 104 | *.dotCover 105 | 106 | # NCrunch 107 | _NCrunch_* 108 | .*crunch*.local.xml 109 | 110 | # MightyMoose 111 | *.mm.* 112 | AutoTest.Net/ 113 | 114 | # Web workbench (sass) 115 | .sass-cache/ 116 | 117 | # Installshield output folder 118 | [Ee]xpress/ 119 | 120 | # DocProject is a documentation generator add-in 121 | DocProject/buildhelp/ 122 | DocProject/Help/*.HxT 123 | DocProject/Help/*.HxC 124 | DocProject/Help/*.hhc 125 | DocProject/Help/*.hhk 126 | DocProject/Help/*.hhp 127 | DocProject/Help/Html2 128 | DocProject/Help/html 129 | 130 | # Click-Once directory 131 | publish/ 132 | 133 | # Publish Web Output 134 | *.[Pp]ublish.xml 135 | *.azurePubxml 136 | ## TODO: Comment the next line if you want to checkin your 137 | ## web deploy settings but do note that will include unencrypted 138 | ## passwords 139 | #*.pubxml 140 | 141 | *.publishproj 142 | 143 | # NuGet Packages 144 | *.nupkg 145 | # The packages folder can be ignored because of Package Restore 146 | **/packages/* 147 | # except build/, which is used as an MSBuild target. 148 | !**/packages/build/ 149 | # Uncomment if necessary however generally it will be regenerated when needed 150 | #!**/packages/repositories.config 151 | 152 | # Windows Azure Build Output 153 | csx/ 154 | *.build.csdef 155 | 156 | # Windows Store app package directory 157 | AppPackages/ 158 | 159 | # Visual Studio cache files 160 | # files ending in .cache can be ignored 161 | *.[Cc]ache 162 | # but keep track of directories ending in .cache 163 | !*.[Cc]ache/ 164 | 165 | # Others 166 | ClientBin/ 167 | [Ss]tyle[Cc]op.* 168 | ~$* 169 | *~ 170 | *.dbmdl 171 | *.dbproj.schemaview 172 | *.pfx 173 | *.publishsettings 174 | node_modules/ 175 | orleans.codegen.cs 176 | 177 | # RIA/Silverlight projects 178 | Generated_Code/ 179 | 180 | # Backup & report files from converting an old project file 181 | # to a newer Visual Studio version. Backup files are not needed, 182 | # because we have git ;-) 183 | _UpgradeReport_Files/ 184 | Backup*/ 185 | UpgradeLog*.XML 186 | UpgradeLog*.htm 187 | 188 | # SQL Server files 189 | *.mdf 190 | *.ldf 191 | 192 | # Business Intelligence projects 193 | *.rdl.data 194 | *.bim.layout 195 | *.bim_*.settings 196 | 197 | # Microsoft Fakes 198 | FakesAssemblies/ 199 | 200 | # Node.js Tools for Visual Studio 201 | .ntvs_analysis.dat 202 | 203 | # Visual Studio 6 build log 204 | *.plg 205 | 206 | # Visual Studio 6 workspace options file 207 | *.opt 208 | 209 | # LightSwitch generated files 210 | GeneratedArtifacts/ 211 | _Pvt_Extensions/ 212 | ModelManifest.xml 213 | -------------------------------------------------------------------------------- /.idea/.idea.ExtensionBlocks/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /modules.xml 6 | /contentModel.xml 7 | /projectSettingsUpdater.xml 8 | /.idea.ExtensionBlocks.iml 9 | # Editor-based HTTP Client requests 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | -------------------------------------------------------------------------------- /.idea/.idea.ExtensionBlocks/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/.idea.ExtensionBlocks/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/.idea.ExtensionBlocks/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ExtensionBlocks.Test/ExtensionBlocks.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net462;net6.0 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ExtensionBlocks.Test/ExtensionBlocks.Test.v2.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | true 3 | 1000 4 | false 5 | false 6 | false 7 | true 8 | false 9 | false 10 | false 11 | false 12 | false 13 | true 14 | false 15 | true 16 | false 17 | true 18 | true 19 | true 20 | 60000 21 | 22 | 23 | 24 | AutoDetect 25 | STA 26 | x86 27 | -------------------------------------------------------------------------------- /ExtensionBlocks.Test/TestMain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using NUnit.Framework; 9 | 10 | namespace ExtensionBlocks.Test 11 | { 12 | [TestFixture] 13 | public class TestMain 14 | { 15 | [Test] 16 | public void foobar() 17 | { 18 | var fooBytes = File.ReadAllBytes(@"C:\Temp\beef0024.bin"); 19 | 20 | 21 | var aa = Utils.GetExtensionBlockFromBytes(0xbeef0024, fooBytes); 22 | 23 | } 24 | 25 | 26 | [Test] 27 | public void guidLookup() 28 | { 29 | 30 | var aaa = Utils.GetDescriptionFromGuidAndKey("de35258c-c695-4cbc-b982-38b0ad24ced0", 2); 31 | 32 | Assert.Equals(aaa,"Shell Omit From View"); 33 | 34 | 35 | // {"de35258c-c695-4cbc-b982-38b0ad24ced0",new HashSet() 36 | // { 37 | // new IdName (2, "Shell Omit From View"), 38 | // } }, 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ExtensionBlocks.Test2/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ExtensionBlocks.Test2/ExtensionBlocks.Test2.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net462;net6.0 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ExtensionBlocks.Test2/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ExtensionBlocks.Test2 10 | { 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | var fooBytes = File.ReadAllBytes(@"C:\Temp\prop.bin"); 16 | 17 | var foo = new PropertyStore(fooBytes); 18 | Debug.WriteLine(foo); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ExtensionBlocks.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29215.179 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtensionBlocks", "ExtensionBlocks\ExtensionBlocks.csproj", "{1F3896D7-60D9-4D39-BE3E-55071D157D39}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtensionBlocks.Test", "ExtensionBlocks.Test\ExtensionBlocks.Test.csproj", "{7869D7FF-6BD3-41BC-87AC-F8AF28AE9F30}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtensionBlocks.Test2", "ExtensionBlocks.Test2\ExtensionBlocks.Test2.csproj", "{BBFBA517-895C-4B94-A2A2-28252A390F1A}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {1F3896D7-60D9-4D39-BE3E-55071D157D39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {1F3896D7-60D9-4D39-BE3E-55071D157D39}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {1F3896D7-60D9-4D39-BE3E-55071D157D39}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {1F3896D7-60D9-4D39-BE3E-55071D157D39}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {7869D7FF-6BD3-41BC-87AC-F8AF28AE9F30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {7869D7FF-6BD3-41BC-87AC-F8AF28AE9F30}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {7869D7FF-6BD3-41BC-87AC-F8AF28AE9F30}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {7869D7FF-6BD3-41BC-87AC-F8AF28AE9F30}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {BBFBA517-895C-4B94-A2A2-28252A390F1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {BBFBA517-895C-4B94-A2A2-28252A390F1A}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {BBFBA517-895C-4B94-A2A2-28252A390F1A}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {BBFBA517-895C-4B94-A2A2-28252A390F1A}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {1EFC709B-40FB-4C31-ADD6-24A62191D72D} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /ExtensionBlocks.v2.ncrunchsolution: -------------------------------------------------------------------------------- 1 |  2 | 1 3 | false 4 | false 5 | true 6 | UseDynamicAnalysis 7 | UseStaticAnalysis 8 | UseStaticAnalysis 9 | UseStaticAnalysis 10 | UseDynamicAnalysis 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /ExtensionBlocks.v3.ncrunchsolution: -------------------------------------------------------------------------------- 1 |  2 | 3 | False 4 | True 5 | 6 | -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0000.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | namespace ExtensionBlocks 6 | { 7 | public class Beef0000 : BeefBase 8 | { 9 | public Beef0000(byte[] rawBytes) 10 | : base(rawBytes) 11 | { 12 | if (Signature != 0xbeef0000) 13 | { 14 | throw new Exception($"Signature mismatch! Should be 0xbeef0000 but is 0x{Signature:X}"); 15 | } 16 | 17 | GUID1 = Utils.ExtractGuidFromShellItem(rawBytes.Skip(8).Take(16).ToArray()); 18 | 19 | GUID1Folder = Utils.GetFolderNameFromGuid(GUID1); 20 | 21 | GUID2 = Utils.ExtractGuidFromShellItem(rawBytes.Skip(24).Take(16).ToArray()); 22 | 23 | GUID2Folder = Utils.GetFolderNameFromGuid(GUID2); 24 | 25 | VersionOffset = BitConverter.ToInt16(rawBytes, 40); 26 | } 27 | 28 | public string GUID1 { get; } 29 | 30 | public string GUID1Folder { get; } 31 | 32 | public string GUID2 { get; } 33 | 34 | public string GUID2Folder { get; } 35 | 36 | public override string ToString() 37 | { 38 | var sb = new StringBuilder(); 39 | 40 | sb.AppendLine(base.ToString()); 41 | 42 | sb.AppendLine(); 43 | 44 | sb.AppendLine($"GUID 1: {GUID1}"); 45 | sb.AppendLine($"GUID 1 Folder: {GUID1Folder}"); 46 | sb.AppendLine($"GUID 2: {GUID2}"); 47 | sb.AppendLine($"GUID 2 Folder: {GUID2Folder}"); 48 | 49 | return sb.ToString(); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0001.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace ExtensionBlocks 4 | { 5 | public class Beef0001 : BeefBase 6 | { 7 | public Beef0001(byte[] rawBytes) 8 | : base(rawBytes) 9 | { 10 | Message = 11 | "Unsupported Extension block. Please report to Report to saericzimmerman@gmail.com to get it added!"; 12 | } 13 | 14 | public override string ToString() 15 | { 16 | var sb = new StringBuilder(); 17 | 18 | sb.AppendLine(base.ToString()); 19 | 20 | return sb.ToString(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0002.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace ExtensionBlocks 4 | { 5 | public class Beef0002 : BeefBase 6 | { 7 | public Beef0002(byte[] rawBytes) 8 | : base(rawBytes) 9 | { 10 | Message = 11 | "Unsupported Extension block. Please report to Report to saericzimmerman@gmail.com to get it added!"; 12 | } 13 | 14 | public override string ToString() 15 | { 16 | var sb = new StringBuilder(); 17 | 18 | sb.AppendLine(base.ToString()); 19 | 20 | return sb.ToString(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0003.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | namespace ExtensionBlocks 6 | { 7 | public class Beef0003 : BeefBase 8 | { 9 | public Beef0003(byte[] rawBytes) 10 | : base(rawBytes) 11 | { 12 | if (Signature != 0xbeef0003) 13 | { 14 | throw new Exception($"Signature mismatch! Should be 0xbeef0003 but is 0x{Signature:X}"); 15 | } 16 | 17 | GUID1 = Utils.ExtractGuidFromShellItem(rawBytes.Skip(8).Take(16).ToArray()); 18 | 19 | GUID1Folder = Utils.GetFolderNameFromGuid(GUID1); 20 | 21 | VersionOffset = BitConverter.ToInt16(rawBytes, 24); 22 | } 23 | 24 | public string GUID1 { get; } 25 | 26 | public string GUID1Folder { get; } 27 | 28 | public override string ToString() 29 | { 30 | var sb = new StringBuilder(); 31 | 32 | sb.AppendLine(base.ToString()); 33 | 34 | sb.AppendLine(); 35 | 36 | sb.AppendLine($"GUID 1: {GUID1}"); 37 | sb.AppendLine($"GUID 1 Folder: {GUID1Folder}"); 38 | 39 | return sb.ToString(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0004.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace ExtensionBlocks 7 | { 8 | public class Beef0004 : BeefBase 9 | { 10 | public Beef0004(byte[] rawBytes) 11 | : base(rawBytes) 12 | { 13 | LocalisedName = string.Empty; 14 | 15 | MFTInformation = new MFTInformation(); 16 | 17 | if (Signature != 0xbeef0004) 18 | { 19 | throw new Exception($"Signature mismatch! Should be 0xbeef0004 but is 0x{Signature:X}"); 20 | } 21 | 22 | var createdDate = 23 | Utils.ExtractDateTimeOffsetFromBytes(rawBytes.Skip(8).Take(4).ToArray()); 24 | 25 | CreatedOnTime = createdDate; 26 | 27 | var lastAccessDate = 28 | Utils.ExtractDateTimeOffsetFromBytes(rawBytes.Skip(12).Take(4).ToArray()); 29 | 30 | LastAccessTime = lastAccessDate; 31 | 32 | Identifier = BitConverter.ToInt16(rawBytes, 16); 33 | 34 | var index = 18; 35 | 36 | if (Version >= 7) 37 | { 38 | index += 2; // skip empty 2 39 | 40 | MFTInformation = new MFTInformation(rawBytes.Skip(index).Take(8).ToArray()); 41 | 42 | index += 8; //skip mft data 43 | 44 | // skip 8 unknown 45 | index += 8; 46 | } 47 | 48 | var longstringsize = 0; 49 | 50 | if (Version >= 3) 51 | { 52 | index += 2; 53 | } 54 | 55 | if (Version >= 9) 56 | { 57 | //skip 4 unknown 58 | index += 4; 59 | } 60 | 61 | if (Version >= 8) 62 | { 63 | // unknown, but skip 4 64 | index += 4; 65 | } 66 | 67 | // in this case we want the rest of the extension data, but again, we arent interested in the version offset yet 68 | longstringsize = rawBytes.Length - index; 69 | 70 | var stringBytes = rawBytes.Skip(index).Take(longstringsize).ToArray(); 71 | 72 | var stringpieces = 73 | Utils.GetStringsFromMultistring(stringBytes); 74 | 75 | if (stringpieces.Count == 1) 76 | { 77 | LongName = stringpieces[0]; 78 | } 79 | else 80 | { 81 | LongName = String.Empty; 82 | } 83 | 84 | if (stringpieces.Count > 1) 85 | { 86 | LocalisedName = stringpieces[1]; 87 | } 88 | 89 | index += longstringsize - 2; 90 | 91 | if (index > rawBytes.Length) 92 | { 93 | index = rawBytes.Length - 2; 94 | } 95 | 96 | VersionOffset = BitConverter.ToUInt16(rawBytes, index); 97 | 98 | index += 2; 99 | 100 | #if DEBUG 101 | Trace.Assert(rawBytes.Length == index, "Still have bytes in beef0004"); 102 | #endif 103 | } 104 | 105 | /// 106 | /// Created time of BagPath 107 | /// 108 | public DateTimeOffset? CreatedOnTime { get; } 109 | 110 | /// 111 | /// Last access time of BagPath 112 | /// 113 | public DateTimeOffset? LastAccessTime { get; } 114 | 115 | public int Identifier { get; set; } 116 | 117 | public MFTInformation MFTInformation { get; } 118 | 119 | public string LongName { get; } 120 | 121 | public string LocalisedName { get; } 122 | 123 | public override string ToString() 124 | { 125 | var sb = new StringBuilder(); 126 | 127 | sb.AppendLine(base.ToString()); 128 | 129 | var os = "Unknown operating system"; 130 | switch (Identifier) 131 | { 132 | case 0x14: 133 | os = "Windows XP, 2003"; 134 | break; 135 | case 0x26: 136 | os = "Windows Vista"; 137 | break; 138 | case 0x2a: 139 | os = "Windows 2008, 7, 8"; 140 | break; 141 | case 0x2e: 142 | os = "Windows 8.1, 10"; 143 | break; 144 | 145 | } 146 | 147 | sb.AppendLine($"Identifier: {Identifier:X2} ({os})"); 148 | 149 | if (CreatedOnTime.HasValue) 150 | { 151 | sb.AppendLine(); 152 | 153 | sb.AppendLine($"Created On: {CreatedOnTime.Value}"); 154 | } 155 | 156 | if (LastAccessTime.HasValue) 157 | { 158 | sb.AppendLine($"Last Access: {LastAccessTime.Value}"); 159 | } 160 | 161 | sb.AppendLine(); 162 | 163 | sb.AppendLine($"Long Name: {LongName}"); 164 | 165 | if (LocalisedName.Length > 0) 166 | { 167 | sb.AppendLine($"Localised Name: {LocalisedName}"); 168 | } 169 | 170 | if (MFTInformation.MFTEntryNumber.HasValue) 171 | { 172 | sb.AppendLine(); 173 | sb.AppendLine($"MFT Entry Number: {MFTInformation.MFTEntryNumber.Value}"); 174 | } 175 | 176 | if (MFTInformation.MFTSequenceNumber.HasValue) 177 | { 178 | sb.AppendLine($"MFT Sequence Number: {MFTInformation.MFTSequenceNumber.Value}"); 179 | } 180 | 181 | sb.AppendLine(); 182 | 183 | 184 | if (MFTInformation.MFTEntryNumber.HasValue && MFTInformation.MFTSequenceNumber.HasValue) 185 | { 186 | if (MFTInformation.MFTEntryNumber.Value > 0 && MFTInformation.MFTSequenceNumber.Value > 0) 187 | { 188 | MFTInformation.Note = "NTFS"; 189 | } 190 | } 191 | 192 | if (MFTInformation.MFTEntryNumber.HasValue && MFTInformation.MFTSequenceNumber.HasValue == false) 193 | { 194 | if (LastAccessTime.HasValue) 195 | { 196 | if (LastAccessTime.Value.Minute == 0 && LastAccessTime.Value.Second == 0 && 197 | LastAccessTime.Value.Millisecond == 0) 198 | { 199 | MFTInformation.Note = "FAT"; 200 | } 201 | else 202 | { 203 | MFTInformation.Note = "exFAT"; 204 | } 205 | } 206 | } 207 | 208 | 209 | sb.AppendLine($"File system hint: {MFTInformation.Note}"); 210 | 211 | return sb.ToString(); 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0005.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | namespace ExtensionBlocks 6 | { 7 | public class Beef0005 : BeefBase 8 | { 9 | public Beef0005(byte[] rawBytes) 10 | : base(rawBytes) 11 | { 12 | if (Signature != 0xbeef0005) 13 | { 14 | throw new Exception($"Signature mismatch! Should be 0xbeef0005 but is 0x{Signature:X}"); 15 | } 16 | 17 | var guid = Utils.ExtractGuidFromShellItem(rawBytes.Skip(8).Take(16).ToArray()); 18 | 19 | Message = 20 | "Unsupported Extension block. Please report to Report to saericzimmerman@gmail.com to get it added!"; 21 | 22 | VersionOffset = BitConverter.ToInt16(rawBytes, 12); 23 | } 24 | 25 | public override string ToString() 26 | { 27 | var sb = new StringBuilder(); 28 | 29 | sb.AppendLine(base.ToString()); 30 | 31 | return sb.ToString(); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0006.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ExtensionBlocks 5 | { 6 | public class Beef0006 : BeefBase 7 | { 8 | public Beef0006(byte[] rawBytes) 9 | : base(rawBytes) 10 | { 11 | if (Signature != 0xbeef0006) 12 | { 13 | throw new Exception($"Signature mismatch! Should be 0xbeef0006 but is 0x{Signature:X}"); 14 | } 15 | 16 | var len = 0; 17 | var index = 8; 18 | 19 | while ((rawBytes[index + len] != 0x0 || rawBytes[index + len + 1] != 0x0)) 20 | { 21 | len += 1; 22 | } 23 | 24 | var uname = Encoding.Unicode.GetString(rawBytes, index, len + 1); 25 | 26 | UserName = uname; 27 | 28 | index += len + 3; // move past string and end of string marker 29 | 30 | VersionOffset = BitConverter.ToInt16(rawBytes, index); 31 | } 32 | 33 | public string UserName { get; } 34 | 35 | public override string ToString() 36 | { 37 | var sb = new StringBuilder(); 38 | 39 | sb.AppendLine(base.ToString()); 40 | 41 | sb.AppendLine(); 42 | 43 | sb.AppendLine($"User Name: {UserName}"); 44 | 45 | return sb.ToString(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0008.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace ExtensionBlocks 4 | { 5 | public class Beef0008 : BeefBase 6 | { 7 | public Beef0008(byte[] rawBytes) 8 | : base(rawBytes) 9 | { 10 | Message = 11 | "Unsupported Extension block. Please report to Report to saericzimmerman@gmail.com to get it added!"; 12 | } 13 | 14 | public override string ToString() 15 | { 16 | var sb = new StringBuilder(); 17 | 18 | sb.AppendLine(base.ToString()); 19 | 20 | return sb.ToString(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0009.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace ExtensionBlocks 4 | { 5 | public class Beef0009 : BeefBase 6 | { 7 | public Beef0009(byte[] rawBytes) 8 | : base(rawBytes) 9 | { 10 | Message = 11 | "Unsupported Extension block. Please report to Report to saericzimmerman@gmail.com to get it added!"; 12 | } 13 | 14 | public override string ToString() 15 | { 16 | var sb = new StringBuilder(); 17 | 18 | sb.AppendLine(base.ToString()); 19 | 20 | return sb.ToString(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef000a.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ExtensionBlocks 5 | { 6 | public class Beef000a : BeefBase 7 | { 8 | public Beef000a(byte[] rawBytes) 9 | : base(rawBytes) 10 | { 11 | if (Signature != 0xbeef000a) 12 | { 13 | throw new Exception($"Signature mismatch! Should be 0xbeef000a but is 0x{Signature:X}"); 14 | } 15 | 16 | VersionOffset = BitConverter.ToInt16(rawBytes, 12); 17 | } 18 | 19 | public override string ToString() 20 | { 21 | var sb = new StringBuilder(); 22 | 23 | sb.AppendLine(base.ToString()); 24 | 25 | return sb.ToString(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef000c.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace ExtensionBlocks 4 | { 5 | public class Beef000c : BeefBase 6 | { 7 | public Beef000c(byte[] rawBytes) 8 | : base(rawBytes) 9 | { 10 | Message = 11 | "Unsupported Extension block. Please report to Report to saericzimmerman@gmail.com to get it added!"; 12 | } 13 | 14 | public override string ToString() 15 | { 16 | var sb = new StringBuilder(); 17 | 18 | sb.AppendLine(base.ToString()); 19 | 20 | return sb.ToString(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef000e.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | // namespaces... 7 | namespace ExtensionBlocks 8 | { 9 | // internal classes... 10 | internal class Beef000e : BeefBase 11 | { 12 | // public constructors... 13 | public Beef000e(byte[] rawBytes) 14 | : base(rawBytes) 15 | { 16 | if (Signature != 0xbeef000e) 17 | { 18 | throw new Exception($"Signature mismatch! Should be 0xbeef000e but is {Signature}"); 19 | } 20 | 21 | ExtensionBlocks = new List(); 22 | 23 | Bags = new List(); 24 | 25 | var rawguid1 = new byte[16]; 26 | 27 | var index = 16; 28 | 29 | Array.Copy(rawBytes, index, rawguid1, 0, 16); 30 | 31 | var rawguid = Utils.ExtractGuidFromShellItem(rawguid1); 32 | 33 | var foldername = Utils.GetFolderNameFromGuid(rawguid); 34 | 35 | GUIDName = foldername; 36 | 37 | index += 16; 38 | 39 | index += 18; 40 | 41 | PropertyStores = new List(); 42 | 43 | for (var i = 0; i < 3; i++) 44 | { 45 | var len = BitConverter.ToUInt32(rawBytes, index); 46 | 47 | var propStore = new PropertyStore(rawBytes.Skip(index).Take((int)len).ToArray()); 48 | 49 | PropertyStores.Add(propStore); 50 | 51 | index += (int)len; 52 | } 53 | 54 | 55 | 56 | index += 11; 57 | 58 | 59 | 60 | var chunks = new List(); 61 | var len1 = 0; 62 | var s1 = string.Empty; 63 | 64 | var maxLoop = 0; 65 | 66 | while (maxLoop < 3) 67 | { 68 | len1 = 0; 69 | while (rawBytes[ index + len1] != 0x00) 70 | { 71 | len1 += 1; 72 | } 73 | 74 | s1 = Encoding.ASCII.GetString(rawBytes, index, len1); 75 | 76 | chunks.Add(s1); 77 | index += len1 + 1; 78 | 79 | maxLoop += 1; 80 | } 81 | 82 | 83 | 84 | index += 16; 85 | 86 | 87 | while (rawBytes[index + len1] != 0x00) 88 | { 89 | len1 += 1; 90 | } 91 | 92 | s1 = Encoding.ASCII.GetString(rawBytes, index, len1); 93 | 94 | chunks.Add(s1); 95 | index += len1 + 1; 96 | 97 | 98 | index += 1; 99 | 100 | var extSize = 0; 101 | extSize = BitConverter.ToUInt16(rawBytes, index); 102 | 103 | var sig = BitConverter.ToUInt32(rawBytes, index + 4); 104 | 105 | var block = Utils.GetExtensionBlockFromBytes(sig, rawBytes.Skip(index).Take(extSize).ToArray()); 106 | 107 | ExtensionBlocks.Add(block); 108 | index += extSize; 109 | 110 | extSize = BitConverter.ToUInt16(rawBytes, index); 111 | 112 | sig = BitConverter.ToUInt32(rawBytes, index + 4); 113 | 114 | block = Utils.GetExtensionBlockFromBytes(sig, rawBytes.Skip(index).Take(extSize).ToArray()); 115 | 116 | ExtensionBlocks.Add(block); 117 | index += extSize; 118 | 119 | extSize = BitConverter.ToUInt16(rawBytes, index); 120 | 121 | 122 | 123 | while (extSize > 0) 124 | { 125 | var sb = new ShellBag0X31(-1, -1, rawBytes.Skip(index).Take(extSize).ToArray(), "Inside Beef000e block"); 126 | 127 | Bags.Add(sb); 128 | 129 | index += extSize; // end of the bag 130 | extSize = BitConverter.ToUInt16(rawBytes, index); 131 | } 132 | 133 | index += 2; //skip empty bag 134 | 135 | 136 | 137 | VersionOffset = BitConverter.ToInt16(rawBytes, rawBytes.Length - 2); 138 | } 139 | 140 | // public properties... 141 | public List ExtensionBlocks { get; set; } 142 | public string GUIDName { get; set; } 143 | public List PropertyStores { get; private set; } 144 | 145 | public List Bags; 146 | 147 | // public methods... 148 | public override string ToString() 149 | { 150 | var sb = new StringBuilder(); 151 | 152 | sb.AppendLine(base.ToString()); 153 | 154 | var sheetNumber = 0; 155 | 156 | sb.AppendLine($"{GUIDName}"); 157 | sb.AppendLine(); 158 | 159 | foreach (var propertyStore in PropertyStores) 160 | { 161 | foreach (var propertySheet in propertyStore.Sheets) 162 | { 163 | sb.AppendLine($"Sheet #{sheetNumber} => {propertySheet}"); 164 | 165 | sheetNumber += 1; 166 | 167 | 168 | foreach (var propertyName in propertySheet.PropertyNames) 169 | { 170 | sb.AppendLine($"Key: {propertyName.Key}, Value: {propertyName.Value}"); 171 | } 172 | } 173 | } 174 | 175 | if (ExtensionBlocks.Count > 0) 176 | { 177 | var extensionNumber = 0; 178 | 179 | sb.AppendLine(); 180 | sb.AppendLine($"Extension blocks found: {ExtensionBlocks.Count}"); 181 | 182 | foreach (var extensionBlock in ExtensionBlocks) 183 | { 184 | sb.AppendLine($"---------------------- Block {extensionNumber:N0} ----------------------"); 185 | 186 | sb.AppendLine(extensionBlock.ToString()); 187 | 188 | extensionNumber += 1; 189 | } 190 | 191 | sb.AppendLine("--------------------------------------------------"); 192 | } 193 | 194 | if (Bags.Count > 0) 195 | { 196 | var bagNumber = 0; 197 | 198 | sb.AppendLine(); 199 | sb.AppendLine($"Internal ShellBags found: {Bags.Count}"); 200 | 201 | foreach (var bag in Bags) 202 | { 203 | sb.AppendLine($"---------------------- Bag {bagNumber:N0} ----------------------"); 204 | 205 | sb.AppendLine(bag.ToString()); 206 | 207 | 208 | 209 | bagNumber += 1; 210 | } 211 | 212 | sb.AppendLine("--------------------------------------------------"); 213 | } 214 | 215 | 216 | 217 | return sb.ToString(); 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0010.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | namespace ExtensionBlocks 6 | { 7 | public class Beef0010 : BeefBase 8 | { 9 | public Beef0010(byte[] rawBytes) 10 | : base(rawBytes) 11 | { 12 | if (Signature != 0xbeef0010) 13 | { 14 | throw new Exception($"Signature mismatch! Should be 0xbeef0010 but is 0x{Signature:X}"); 15 | } 16 | 17 | 18 | var propStore = new PropertyStore(rawBytes.Skip(16).ToArray()); 19 | 20 | 21 | PropertyStore = propStore; 22 | 23 | 24 | VersionOffset = BitConverter.ToInt16(rawBytes, rawBytes.Length - 2); 25 | } 26 | 27 | public PropertyStore PropertyStore { get; } 28 | 29 | 30 | public override string ToString() 31 | { 32 | var sb = new StringBuilder(); 33 | 34 | sb.AppendLine(base.ToString()); 35 | 36 | var sheetNumber = 0; 37 | 38 | foreach (var propertySheet in PropertyStore.Sheets) 39 | { 40 | sb.AppendLine($"Sheet #{sheetNumber} => {propertySheet}"); 41 | 42 | sheetNumber += 1; 43 | 44 | //foreach (var propertyName in propertySheet.PropertyNames) 45 | //{ 46 | // sb.AppendLine(string.Format("Key: {0}, Value: {1}", propertyName.Key, propertyName.Value)); 47 | //} 48 | } 49 | 50 | return sb.ToString(); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0013.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ExtensionBlocks 5 | { 6 | public class Beef0013 : BeefBase 7 | { 8 | public Beef0013(byte[] rawBytes) 9 | : base(rawBytes) 10 | { 11 | if (Signature != 0xbeef0013) 12 | { 13 | throw new Exception($"Signature mismatch! Should be 0xbeef0013 but is 0x{Signature:X}"); 14 | } 15 | 16 | Message = "The purpose of this extension block is unknown"; 17 | 18 | VersionOffset = BitConverter.ToInt16(rawBytes, 40); 19 | } 20 | 21 | 22 | public override string ToString() 23 | { 24 | var sb = new StringBuilder(); 25 | 26 | sb.AppendLine(base.ToString()); 27 | 28 | 29 | return sb.ToString(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0014.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace ExtensionBlocks 4 | { 5 | public class Beef0014 : BeefBase 6 | { 7 | public Beef0014(byte[] rawBytes) 8 | : base(rawBytes) 9 | { 10 | Message = 11 | "Unsupported Extension block. Please report to Report to saericzimmerman@gmail.com to get it added!"; 12 | } 13 | 14 | public override string ToString() 15 | { 16 | var sb = new StringBuilder(); 17 | 18 | sb.AppendLine(base.ToString()); 19 | 20 | sb.AppendLine(); 21 | 22 | return sb.ToString(); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0016.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ExtensionBlocks 5 | { 6 | public class Beef0016 : BeefBase 7 | { 8 | public Beef0016(byte[] rawBytes) 9 | : base(rawBytes) 10 | { 11 | if (Signature != 0xbeef0016) 12 | { 13 | throw new Exception($"Signature mismatch! Should be 0xbeef0016 but is 0x{Signature:X}"); 14 | } 15 | 16 | Value = Encoding.Unicode.GetString(rawBytes, 10, rawBytes.Length - 14); 17 | 18 | VersionOffset = BitConverter.ToInt16(rawBytes, rawBytes.Length - 2); 19 | } 20 | 21 | public string Value { get; } 22 | 23 | 24 | public override string ToString() 25 | { 26 | var sb = new StringBuilder(); 27 | 28 | sb.AppendLine(base.ToString()); 29 | 30 | sb.AppendLine($"Value: {Value}"); 31 | 32 | return sb.ToString(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0017.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ExtensionBlocks 5 | { 6 | public class Beef0017 : BeefBase 7 | { 8 | public Beef0017(byte[] rawBytes) 9 | : base(rawBytes) 10 | { 11 | Message = "The purpose of this extension block is unknown"; 12 | 13 | VersionOffset = BitConverter.ToInt16(rawBytes, rawBytes.Length - 2); 14 | } 15 | 16 | public override string ToString() 17 | { 18 | var sb = new StringBuilder(); 19 | 20 | sb.AppendLine(base.ToString()); 21 | 22 | return sb.ToString(); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0019.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | namespace ExtensionBlocks 6 | { 7 | public class Beef0019 : BeefBase 8 | { 9 | public Beef0019(byte[] rawBytes) 10 | : base(rawBytes) 11 | { 12 | if (Signature != 0xbeef0019) 13 | { 14 | throw new Exception($"Signature mismatch! Should be 0xbeef0019 but is 0x{Signature:X}"); 15 | } 16 | 17 | GUID1 = Utils.ExtractGuidFromShellItem(rawBytes.Skip(8).Take(16).ToArray()); 18 | 19 | GUID1Folder = Utils.GetFolderNameFromGuid(GUID1); 20 | 21 | GUID2 = Utils.ExtractGuidFromShellItem(rawBytes.Skip(24).Take(16).ToArray()); 22 | 23 | GUID2Folder = Utils.GetFolderNameFromGuid(GUID2); 24 | 25 | VersionOffset = BitConverter.ToInt16(rawBytes, 40); 26 | } 27 | 28 | public string GUID1 { get; } 29 | 30 | public string GUID1Folder { get; } 31 | 32 | public string GUID2 { get; } 33 | 34 | public string GUID2Folder { get; } 35 | 36 | public override string ToString() 37 | { 38 | var sb = new StringBuilder(); 39 | 40 | sb.AppendLine(base.ToString()); 41 | 42 | sb.AppendLine(); 43 | 44 | sb.AppendLine($"GUID 1: {GUID1}"); 45 | sb.AppendLine($"GUID 1 Folder: {GUID1Folder}"); 46 | sb.AppendLine($"GUID 2: {GUID2}"); 47 | sb.AppendLine($"GUID 2 Folder: {GUID2Folder}"); 48 | 49 | return sb.ToString(); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef001a.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ExtensionBlocks 5 | { 6 | public class Beef001a : BeefBase 7 | { 8 | public Beef001a(byte[] rawBytes) 9 | : base(rawBytes) 10 | { 11 | if (Signature != 0xbeef001a) 12 | { 13 | throw new Exception($"Signature mismatch! Should be Beef001a but is 0x{Signature:X}"); 14 | } 15 | 16 | var len = 0; 17 | var index = 10; 18 | 19 | while ((rawBytes[index + len] != 0x0 || rawBytes[index + len + 1] != 0x0)) 20 | { 21 | len += 1; 22 | } 23 | 24 | var uname = Encoding.Unicode.GetString(rawBytes, index, len + 1); 25 | 26 | FileDocumentTypeString = uname; 27 | 28 | index += len + 3; // move past string and end of string marker 29 | 30 | //is index 24? 31 | 32 | //TODO get shell item list 33 | 34 | // if (index != 38) 35 | // { 36 | // Message = 37 | // "Unsupported Extension block. Please report to Report to saericzimmerman@gmail.com to get it added!"; 38 | // } 39 | 40 | VersionOffset = BitConverter.ToInt16(rawBytes, index); 41 | } 42 | 43 | public string FileDocumentTypeString { get; } 44 | 45 | public override string ToString() 46 | { 47 | var sb = new StringBuilder(); 48 | 49 | sb.AppendLine(base.ToString()); 50 | 51 | sb.AppendLine(); 52 | 53 | sb.AppendLine($"File Document Type String: {FileDocumentTypeString}"); 54 | 55 | return sb.ToString(); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef001b.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ExtensionBlocks 5 | { 6 | public class Beef001b : BeefBase 7 | { 8 | public Beef001b(byte[] rawBytes) 9 | : base(rawBytes) 10 | { 11 | if (Signature != 0xbeef001b) 12 | { 13 | throw new Exception($"Signature mismatch! Should be Beef001b but is 0x{Signature:X}"); 14 | } 15 | 16 | var len = 0; 17 | var index = 10; 18 | 19 | while ((rawBytes[index + len] != 0x0 || rawBytes[index + len + 1] != 0x0)) 20 | { 21 | len += 1; 22 | } 23 | 24 | var uname = Encoding.Unicode.GetString(rawBytes, index, len + 1); 25 | 26 | FileDocumentTypeString = uname; 27 | 28 | index += len + 3; // move past string and end of string marker 29 | 30 | //is index 24? 31 | 32 | //TODO get shell item list 33 | 34 | // if (index != 38) 35 | // { 36 | // Message = 37 | // "Unsupported Extension block. Please report to Report to saericzimmerman@gmail.com to get it added!"; 38 | // } 39 | 40 | VersionOffset = BitConverter.ToInt16(rawBytes, index); 41 | } 42 | 43 | public string FileDocumentTypeString { get; } 44 | 45 | public override string ToString() 46 | { 47 | var sb = new StringBuilder(); 48 | 49 | sb.AppendLine(base.ToString()); 50 | 51 | sb.AppendLine(); 52 | 53 | sb.AppendLine($"File Document Type String: {FileDocumentTypeString}"); 54 | 55 | return sb.ToString(); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef001d.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ExtensionBlocks 5 | { 6 | public class Beef001d : BeefBase 7 | { 8 | public Beef001d(byte[] rawBytes) 9 | : base(rawBytes) 10 | { 11 | if (Signature != 0xbeef001d) 12 | { 13 | throw new Exception($"Signature mismatch! Should be Beef001d but is 0x{Signature:X}"); 14 | } 15 | 16 | var index = 10; 17 | 18 | 19 | var uname = Encoding.Unicode.GetString(rawBytes, index, rawBytes.Length- 10 - 2).Trim('\0'); 20 | 21 | Executable = uname; 22 | 23 | 24 | VersionOffset = BitConverter.ToInt16(rawBytes, rawBytes.Length-2); 25 | } 26 | 27 | public string Executable { get; } 28 | 29 | public override string ToString() 30 | { 31 | var sb = new StringBuilder(); 32 | 33 | sb.AppendLine(base.ToString()); 34 | 35 | sb.AppendLine(); 36 | 37 | sb.AppendLine($"Executable: {Executable}"); 38 | 39 | return sb.ToString(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef001e.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ExtensionBlocks 5 | { 6 | public class Beef001e : BeefBase 7 | { 8 | public Beef001e(byte[] rawBytes) 9 | : base(rawBytes) 10 | { 11 | if (Signature != 0xbeef001e) 12 | { 13 | throw new Exception($"Signature mismatch! Should be Beef001e but is 0x{Signature:X}"); 14 | } 15 | 16 | var index = 10; 17 | 18 | 19 | 20 | var uname = Encoding.Unicode.GetString(rawBytes, index, rawBytes.Length- 10 - 2).Trim('\0'); 21 | 22 | PinType = uname; 23 | 24 | 25 | VersionOffset = BitConverter.ToInt16(rawBytes, rawBytes.Length-2); 26 | } 27 | 28 | public string PinType { get; } 29 | 30 | public override string ToString() 31 | { 32 | var sb = new StringBuilder(); 33 | 34 | sb.AppendLine(base.ToString()); 35 | 36 | sb.AppendLine(); 37 | 38 | sb.AppendLine($"Pin Type: {PinType}"); 39 | 40 | return sb.ToString(); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0021.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | namespace ExtensionBlocks 6 | { 7 | public class Beef0021 : BeefBase 8 | { 9 | public Beef0021(byte[] rawBytes) 10 | : base(rawBytes) 11 | { 12 | if (Signature != 0xbeef0021) 13 | { 14 | throw new Exception($"Signature mismatch! Should be 0xbeef0021 but is 0x{Signature:X}"); 15 | } 16 | 17 | var propStore = new PropertyStore(rawBytes.Skip(8).ToArray()); 18 | 19 | 20 | PropertyStore = propStore; 21 | 22 | 23 | VersionOffset = BitConverter.ToInt16(rawBytes, rawBytes.Length - 2); 24 | } 25 | 26 | public PropertyStore PropertyStore { get; } 27 | 28 | 29 | public override string ToString() 30 | { 31 | var sb = new StringBuilder(); 32 | 33 | sb.AppendLine(base.ToString()); 34 | 35 | var sheetNumber = 0; 36 | 37 | foreach (var propertySheet in PropertyStore.Sheets) 38 | { 39 | sb.AppendLine($"Sheet #{sheetNumber} => {propertySheet}"); 40 | 41 | sheetNumber += 1; 42 | } 43 | 44 | 45 | return sb.ToString(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0024.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | namespace ExtensionBlocks 6 | { 7 | public class Beef0024 : BeefBase 8 | { 9 | public Beef0024(byte[] rawBytes) 10 | : base(rawBytes) 11 | { 12 | if (Signature != 0xbeef0024) 13 | { 14 | throw new Exception($"Signature mismatch! Should be 0xbeef0024 but is 0x{Signature:X}"); 15 | } 16 | 17 | var propStore = new PropertyStore(rawBytes.Skip(8).ToArray()); 18 | 19 | 20 | PropertyStore = propStore; 21 | 22 | 23 | VersionOffset = BitConverter.ToInt16(rawBytes, rawBytes.Length - 2); 24 | } 25 | 26 | public PropertyStore PropertyStore { get; } 27 | 28 | 29 | public override string ToString() 30 | { 31 | var sb = new StringBuilder(); 32 | 33 | sb.AppendLine(base.ToString()); 34 | 35 | var sheetNumber = 0; 36 | 37 | foreach (var propertySheet in PropertyStore.Sheets) 38 | { 39 | sb.AppendLine($"Sheet #{sheetNumber} => {propertySheet}"); 40 | 41 | sheetNumber += 1; 42 | } 43 | 44 | 45 | return sb.ToString(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0025.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ExtensionBlocks 5 | { 6 | public class Beef0025 : BeefBase 7 | { 8 | public Beef0025(byte[] rawBytes) 9 | : base(rawBytes) 10 | { 11 | if (Signature != 0xbeef0025) 12 | { 13 | throw new Exception($"Signature mismatch! Should be Beef0025 but is 0x{Signature:X}"); 14 | } 15 | 16 | var ft1 = DateTimeOffset.FromFileTime((long) BitConverter.ToUInt64(rawBytes, 12)); 17 | 18 | FileTime1 = ft1.ToUniversalTime(); 19 | 20 | 21 | var ft2 = DateTimeOffset.FromFileTime((long) BitConverter.ToUInt64(rawBytes, 20)); 22 | 23 | FileTime2 = ft2.ToUniversalTime(); 24 | 25 | 26 | VersionOffset = BitConverter.ToInt16(rawBytes, rawBytes.Length - 4); 27 | } 28 | 29 | public DateTimeOffset? FileTime1 { get; } 30 | 31 | public DateTimeOffset? FileTime2 { get; } 32 | 33 | public override string ToString() 34 | { 35 | var sb = new StringBuilder(); 36 | 37 | sb.AppendLine(base.ToString()); 38 | sb.AppendLine(); 39 | 40 | if (FileTime1.HasValue) 41 | { 42 | sb.AppendLine( 43 | $"FileTime 1: {FileTime1.Value.ToString(Utils.GetDateTimeFormatWithMilliseconds())}"); 44 | } 45 | 46 | if (FileTime2.HasValue) 47 | { 48 | sb.AppendLine( 49 | $"FileTime 2: {FileTime2.Value.ToString(Utils.GetDateTimeFormatWithMilliseconds())}"); 50 | } 51 | 52 | return sb.ToString(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0026.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | namespace ExtensionBlocks 6 | { 7 | public class Beef0026 : BeefBase 8 | { 9 | public Beef0026(byte[] rawBytes) 10 | : base(rawBytes) 11 | { 12 | if (Signature != 0xbeef0026) 13 | { 14 | throw new Exception($"Signature mismatch! Should be Beef0026 but is {Signature}"); 15 | } 16 | 17 | 18 | if (rawBytes[8] == 0x11 || rawBytes[8] == 0x10|| rawBytes[8] == 0x12 || rawBytes[8] == 0x34 || rawBytes[8] == 0x31) 19 | { 20 | var ft1 = DateTimeOffset.FromFileTime((long) BitConverter.ToUInt64(rawBytes, 12)).ToUniversalTime(); 21 | 22 | CreatedOn = ft1.ToUniversalTime(); 23 | 24 | 25 | var ft2 = DateTimeOffset.FromFileTime((long) BitConverter.ToUInt64(rawBytes, 20)).ToUniversalTime(); 26 | 27 | LastModified = ft2.ToUniversalTime(); 28 | 29 | 30 | var ft3 = DateTimeOffset.FromFileTime((long) BitConverter.ToUInt64(rawBytes, 28)).ToUniversalTime(); 31 | 32 | LastAccessed = ft3.ToUniversalTime(); 33 | 34 | 35 | return; 36 | } 37 | 38 | var shellPropertySheetListSize = BitConverter.ToUInt16(rawBytes, 8); 39 | 40 | if (shellPropertySheetListSize > rawBytes.Length - 8) 41 | { 42 | //not enough data for there to be a property store, so bail 43 | return; 44 | } 45 | 46 | var propBytes = rawBytes.Skip(8).Take(shellPropertySheetListSize).ToArray(); 47 | PropertyStore = new PropertyStore(propBytes); 48 | 49 | VersionOffset = BitConverter.ToInt16(rawBytes, rawBytes.Length - 4); 50 | } 51 | 52 | public PropertyStore PropertyStore { get; } 53 | 54 | public DateTimeOffset? CreatedOn { get; } 55 | 56 | public DateTimeOffset? LastModified { get; } 57 | public DateTimeOffset? LastAccessed { get; } 58 | 59 | public override string ToString() 60 | { 61 | var sb = new StringBuilder(); 62 | 63 | sb.AppendLine(base.ToString()); 64 | sb.AppendLine(); 65 | 66 | if (CreatedOn.HasValue) 67 | { 68 | sb.AppendLine( 69 | $"Created: {CreatedOn.Value.ToString("yyyy-MM-dd HH:mm:ss.fffffff")}"); 70 | } 71 | 72 | if (LastModified.HasValue) 73 | { 74 | sb.AppendLine( 75 | $"Last modified: {LastModified.Value.ToString("yyyy-MM-dd HH:mm:ss.fffffff")}"); 76 | } 77 | 78 | if (LastAccessed.HasValue) 79 | { 80 | sb.AppendLine( 81 | $"Last accessed: {LastAccessed.Value.ToString("yyyy-MM-dd HH:mm:ss.fffffff")}"); 82 | } 83 | 84 | if (PropertyStore != null) 85 | { 86 | if (PropertyStore.Sheets.Count > 0) 87 | { 88 | sb.AppendLine("Property Sheets"); 89 | 90 | sb.AppendLine(PropertyStore.ToString()); 91 | sb.AppendLine(); 92 | } 93 | } 94 | 95 | return sb.ToString(); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0027.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | namespace ExtensionBlocks 6 | { 7 | public class Beef0027 : BeefBase 8 | { 9 | public Beef0027(byte[] rawBytes) 10 | : base(rawBytes) 11 | { 12 | if (Signature != 0xbeef0027) 13 | { 14 | throw new Exception($"Signature mismatch! Should be 0xbeef0027 but is {Signature}"); 15 | } 16 | 17 | var propStore = new PropertyStore(rawBytes.Skip(8).ToArray()); 18 | 19 | 20 | PropertyStore = propStore; 21 | 22 | 23 | VersionOffset = BitConverter.ToInt16(rawBytes, rawBytes.Length - 2); 24 | } 25 | 26 | public PropertyStore PropertyStore { get; } 27 | 28 | 29 | public override string ToString() 30 | { 31 | var sb = new StringBuilder(); 32 | 33 | sb.AppendLine(base.ToString()); 34 | 35 | var sheetNumber = 0; 36 | 37 | foreach (var propertySheet in PropertyStore.Sheets) 38 | { 39 | sb.AppendLine($"Sheet #{sheetNumber} => {propertySheet}"); 40 | 41 | sheetNumber += 1; 42 | } 43 | 44 | 45 | return sb.ToString(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /ExtensionBlocks/Beef0029.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ExtensionBlocks 5 | { 6 | internal class Beef0029 : BeefBase 7 | { 8 | public Beef0029(byte[] rawBytes) 9 | : base(rawBytes) 10 | { 11 | if (Signature != 0xbeef0029) 12 | { 13 | throw new Exception($"Signature mismatch! Should be 0xbeef0029 but is {Signature}"); 14 | } 15 | 16 | Message = "The purpose of this extension block is unknown"; 17 | 18 | VersionOffset = BitConverter.ToInt16(rawBytes, rawBytes.Length - 2); 19 | } 20 | 21 | 22 | public override string ToString() 23 | { 24 | var sb = new StringBuilder(); 25 | 26 | sb.AppendLine(base.ToString()); 27 | 28 | 29 | return sb.ToString(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /ExtensionBlocks/BeefBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ExtensionBlocks 5 | { 6 | public abstract class BeefBase : IExtensionBlock 7 | { 8 | protected BeefBase(byte[] rawBytes) 9 | { 10 | if (rawBytes == null) 11 | { 12 | return; 13 | } 14 | 15 | Size = BitConverter.ToUInt16(rawBytes, 0); 16 | 17 | Version = BitConverter.ToUInt16(rawBytes, 2); 18 | 19 | Signature = BitConverter.ToUInt32(rawBytes, 4); 20 | 21 | Message = ""; 22 | } 23 | 24 | public string Message { get; set; } 25 | public int Size { get; } 26 | 27 | public int Version { get; } 28 | 29 | public uint Signature { get; } 30 | 31 | public int VersionOffset { get; set; } 32 | 33 | public override string ToString() 34 | { 35 | var sb = new StringBuilder(); 36 | 37 | sb.AppendLine($"Signature: 0x{Signature:x8}"); 38 | sb.AppendLine($"Size: {Size:N0}"); 39 | sb.AppendLine($"Version: {Version:N0}"); 40 | sb.AppendLine($"Version Offset: 0x{VersionOffset:X2}"); 41 | 42 | if (Message.Length > 0) 43 | { 44 | sb.AppendLine(); 45 | sb.AppendLine($"Message: {Message}"); 46 | } 47 | 48 | return sb.ToString(); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /ExtensionBlocks/BeefPlaceHolder.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace ExtensionBlocks 4 | { 5 | public class BeefPlaceHolder : BeefBase 6 | { 7 | public BeefPlaceHolder(byte[] rawBytes) 8 | : base(rawBytes) 9 | { 10 | } 11 | 12 | 13 | public override string ToString() 14 | { 15 | var sb = new StringBuilder(); 16 | 17 | sb.AppendLine( 18 | "This is a placeholder bag to account for additional extension blocks inside internal ShellBags"); 19 | 20 | 21 | return sb.ToString(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /ExtensionBlocks/BeefUnknown.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace ExtensionBlocks 4 | { 5 | public class BeefUnknown : BeefBase 6 | { 7 | public BeefUnknown(byte[] rawBytes) 8 | : base(rawBytes) 9 | { 10 | Message = 11 | "********UNKNOWN Extension block. Please report to Report to saericzimmerman@gmail.com to get it added!"; 12 | } 13 | 14 | public override string ToString() 15 | { 16 | var sb = new StringBuilder(); 17 | 18 | sb.AppendLine(base.ToString()); 19 | 20 | return sb.ToString(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /ExtensionBlocks/ExtensionBlocks.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | Eric Zimmerman 7 | Eric Zimmerman 8 | Extension blocks 9 | ExtensionBlocks 10 | Eric Zimmerman 11 | https://github.com/EricZimmerman/ExtensionBlocks 12 | https://github.com/EricZimmerman/ExtensionBlocks 13 | 1.4.2 14 | MIT 15 | 16 | 17 | beef, extension block 18 | README.md 19 | icon.png 20 | True 21 | 22 | $(NoWarn);CS1591 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | all 36 | runtime; build; native; contentfiles; analyzers; buildtransitive 37 | 38 | 39 | all 40 | runtime; build; native; contentfiles; analyzers; buildtransitive 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /ExtensionBlocks/ExtensionBlocks.v2.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | true 3 | 1000 4 | false 5 | false 6 | false 7 | true 8 | false 9 | false 10 | false 11 | false 12 | false 13 | true 14 | false 15 | true 16 | false 17 | true 18 | true 19 | true 20 | 60000 21 | 22 | 23 | 24 | AutoDetect 25 | STA 26 | x86 27 | -------------------------------------------------------------------------------- /ExtensionBlocks/IExtensionBlock.cs: -------------------------------------------------------------------------------- 1 | namespace ExtensionBlocks 2 | { 3 | public interface IExtensionBlock 4 | { 5 | int Size { get; } 6 | 7 | int Version { get; } 8 | 9 | uint Signature { get; } 10 | 11 | int VersionOffset { get; } 12 | } 13 | } -------------------------------------------------------------------------------- /ExtensionBlocks/IShellBag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ExtensionBlocks 5 | { 6 | public interface IShellBag 7 | { 8 | /// 9 | /// ID used for uniqueness. can be used to find a shellbag among a collection of shellbags 10 | /// 11 | string InternalId { get; } 12 | 13 | /// 14 | /// Used for tracking purposes by end user 15 | /// 16 | int NodeId { get; set; } 17 | 18 | /// 19 | /// A nice looking name vs the technical representation of the ShellBag item 20 | /// 21 | string FriendlyName { get; } 22 | 23 | /// 24 | /// ShellBag data in its unparsed format as a string of hex characters separated by - 25 | /// 26 | byte[] HexValue { get; } 27 | 28 | /// 29 | /// BagPath is the *root* path to the ShellBag. 30 | /// 31 | string BagPath { get; } 32 | 33 | /// 34 | /// AbsolutePath is the path to the ShellBag. 35 | /// 36 | string AbsolutePath { get; set; } 37 | 38 | /// 39 | /// Slot is the value name in BagPath 40 | /// 41 | int Slot { get; } 42 | 43 | /// 44 | /// True if the ShellBag is from a deleted Registry key 45 | /// 46 | bool IsDeleted { get; set; } 47 | 48 | Utils.ShellBagTypes ShellBagType { get; set; } 49 | 50 | /// 51 | /// The position this ShellBag item was opened 52 | /// 53 | int MruPosition { get; } 54 | 55 | /// 56 | /// Gets the node slot. 57 | /// 58 | /// The node slot. 59 | int NodeSlot { get; set; } 60 | 61 | /// 62 | /// Child ShellBag items for this ShellBag 63 | /// 64 | List ChildShellBags { get; set; } 65 | 66 | /// 67 | /// The name of the ShellBag. Can be based on file name, directory name, or GUID 68 | /// 69 | string Value { get; } 70 | 71 | /// 72 | /// last write time of BagPath key 73 | /// 74 | DateTimeOffset? LastWriteTime { get; set; } 75 | 76 | /// 77 | /// First explored time 78 | /// 79 | DateTimeOffset? FirstInteracted { get; set; } 80 | 81 | bool HasExplored { get; set; } 82 | 83 | /// 84 | /// First explored time 85 | /// 86 | DateTimeOffset? LastInteracted { get; set; } 87 | 88 | List ExtensionBlocks { get; set; } 89 | 90 | } 91 | } -------------------------------------------------------------------------------- /ExtensionBlocks/MFTInformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ExtensionBlocks 4 | { 5 | public class MFTInformation 6 | { 7 | public MFTInformation() 8 | { 9 | Note = string.Empty; 10 | } 11 | 12 | public MFTInformation(byte[] rawMFTInfo) 13 | { 14 | if (rawMFTInfo.Length != 8) 15 | { 16 | throw new ArgumentException("rawMFTInfo must be 8 bytes long!"); 17 | } 18 | 19 | var sequenceNumber = BitConverter.ToUInt16(rawMFTInfo, 6); 20 | 21 | ulong entryIndex = 0; 22 | 23 | ulong entryIndex1 = BitConverter.ToUInt32(rawMFTInfo, 0); 24 | ulong entryIndex2 = BitConverter.ToUInt16(rawMFTInfo, 4); 25 | 26 | if (entryIndex2 == 0) 27 | { 28 | entryIndex = entryIndex1; 29 | } 30 | else 31 | { 32 | entryIndex2 = (entryIndex2*16777216); //2^24 33 | entryIndex = (entryIndex1 + entryIndex2); 34 | } 35 | 36 | MFTEntryNumber = entryIndex; 37 | MFTSequenceNumber = sequenceNumber; 38 | 39 | if (sequenceNumber == 0) 40 | { 41 | MFTSequenceNumber = null; 42 | } 43 | 44 | 45 | if (entryIndex > 0 && sequenceNumber > 0) 46 | { 47 | Note = "NTFS"; 48 | } 49 | 50 | if (entryIndex > 0 && sequenceNumber == 0) 51 | { 52 | Note = "FAT"; 53 | } 54 | 55 | if (entryIndex == 0 && sequenceNumber == 0) 56 | { 57 | Note = "Network/special item"; 58 | } 59 | } 60 | 61 | public ulong? MFTEntryNumber { get; set; } 62 | 63 | public int? MFTSequenceNumber { get; set; } 64 | 65 | public string Note { get; set; } 66 | } 67 | } -------------------------------------------------------------------------------- /ExtensionBlocks/PropertySheet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace ExtensionBlocks 9 | { 10 | public class PropertySheet 11 | { 12 | public enum PropertySheetTypeEnum 13 | { 14 | Named, 15 | Numeric 16 | } 17 | 18 | public PropertySheet(byte[] contents) 19 | { 20 | PropertyNames = new Dictionary(); 21 | 22 | var sheetindex = 0; 23 | 24 | var serializedSize = BitConverter.ToInt32(contents, sheetindex); 25 | sheetindex = 4; //skip size 26 | 27 | Size = serializedSize; 28 | 29 | var serializedVersion = BitConverter.ToString(contents, sheetindex, 4); 30 | 31 | sheetindex += 4; 32 | 33 | if (serializedVersion != "31-53-50-53") 34 | { 35 | throw new Exception($"Version mismatch! {serializedVersion} != 31-53-50-53"); 36 | } 37 | 38 | Version = serializedVersion; 39 | 40 | var rawguidshellProperty = new byte[16]; 41 | 42 | Array.Copy(contents, sheetindex, rawguidshellProperty, 0, 16); 43 | 44 | var formatClassIdguid = Utils.ExtractGuidFromShellItem(rawguidshellProperty); 45 | 46 | sheetindex += 16; 47 | 48 | GUID = formatClassIdguid; 49 | 50 | if (formatClassIdguid == "d5cdd505-2e9c-101b-9397-08002b2cf9ae") 51 | { 52 | //all serialized property values are named properties 53 | PropertySheetType = PropertySheetTypeEnum.Named; 54 | 55 | var valueSize = 0; 56 | var propertyName = ""; 57 | 58 | var propertyValues = new Dictionary(); 59 | var propertySlotNumber = 0; 60 | 61 | while (sheetindex < contents.Length) 62 | { 63 | //cut up shellPropertySheetList into byte arrays based on length, then process each one 64 | valueSize = BitConverter.ToInt32(contents, sheetindex); 65 | 66 | if (valueSize == 0) 67 | { 68 | break; // we are out of lists 69 | } 70 | 71 | var sheetListBytes = new byte[valueSize]; 72 | Array.Copy(contents, sheetindex, sheetListBytes, 0, valueSize); 73 | 74 | propertyValues.Add(propertySlotNumber, sheetListBytes); 75 | propertySlotNumber += 1; 76 | 77 | sheetindex += valueSize; 78 | } //end of while in shellPropertySheetList 79 | 80 | foreach (var propertyValue in propertyValues) 81 | { 82 | var propertyIndex = 0; 83 | 84 | valueSize = BitConverter.ToInt32(propertyValue.Value, propertyIndex); 85 | propertyIndex += 4; 86 | 87 | var nameSize = BitConverter.ToInt32(propertyValue.Value, propertyIndex); 88 | propertyIndex += 4; 89 | 90 | propertyIndex += 1; //reserved 91 | 92 | propertyName = Encoding.Unicode.GetString(propertyValue.Value, propertyIndex, nameSize - 2); 93 | 94 | propertyIndex += (nameSize); 95 | 96 | var namedType = BitConverter.ToUInt16(propertyValue.Value, propertyIndex); 97 | 98 | propertyIndex += 2; //skip type 99 | propertyIndex += 2; //skip padding? 100 | 101 | //TODO Combine these with what is below. Make a function to take the type, process and return a string? 102 | switch (namedType) 103 | { 104 | case 0x000b: 105 | //VT_BOOL (0x000B) 106 | var boolInt = BitConverter.ToInt32(propertyValue.Value, propertyIndex); 107 | propertyIndex += 8; 108 | 109 | var boolval = boolInt > 0; 110 | 111 | PropertyNames.Add(propertyName, 112 | boolval.ToString(CultureInfo.InvariantCulture)); 113 | 114 | break; 115 | 116 | case 0x0: 117 | case 0x1: 118 | PropertyNames.Add(propertyName, ""); 119 | break; 120 | 121 | case 0x0002: 122 | PropertyNames.Add(propertyName, 123 | BitConverter.ToInt16(propertyValue.Value, propertyIndex) 124 | .ToString(CultureInfo.InvariantCulture)); 125 | break; 126 | 127 | case 0x0003: 128 | PropertyNames.Add(propertyName, 129 | BitConverter.ToInt32(propertyValue.Value, propertyIndex) 130 | .ToString(CultureInfo.InvariantCulture)); 131 | break; 132 | 133 | case 0x0004: 134 | PropertyNames.Add(propertyName, 135 | BitConverter.ToSingle(propertyValue.Value, propertyIndex) 136 | .ToString(CultureInfo.InvariantCulture)); 137 | break; 138 | 139 | case 0x0005: 140 | PropertyNames.Add(propertyName, 141 | BitConverter.ToDouble(propertyValue.Value, propertyIndex) 142 | .ToString(CultureInfo.InvariantCulture)); 143 | break; 144 | 145 | case 0x0008: 146 | 147 | var uniLength = BitConverter.ToInt32(propertyValue.Value, propertyIndex); 148 | propertyIndex += 4; 149 | 150 | var unicodeName = Encoding.Unicode.GetString(propertyValue.Value, propertyIndex, 151 | uniLength - 2); 152 | propertyIndex += (uniLength); 153 | 154 | PropertyNames.Add(propertyName, unicodeName); 155 | 156 | // PropertyNames.Add(propertyName, BitConverter.ToDouble(propertyValue.Value, propertyIndex).ToString(CultureInfo.InvariantCulture)); 157 | break; 158 | 159 | 160 | case 0x000a: 161 | PropertyNames.Add(propertyName, 162 | BitConverter.ToUInt32(propertyValue.Value, propertyIndex) 163 | .ToString(CultureInfo.InvariantCulture)); 164 | break; 165 | 166 | case 0x0014: 167 | //VT_I8 (0x0014) MUST be an 8-byte signed integer. 168 | 169 | PropertyNames.Add(propertyName, 170 | BitConverter.ToInt64(propertyValue.Value, propertyIndex) 171 | .ToString(CultureInfo.InvariantCulture)); 172 | 173 | break; 174 | 175 | case 0x0015: 176 | //VT_I8 (0x0014) MUST be an 8-byte unsigned integer. 177 | 178 | PropertyNames.Add(propertyName, 179 | BitConverter.ToUInt64(propertyValue.Value, propertyIndex) 180 | .ToString(CultureInfo.InvariantCulture)); 181 | 182 | break; 183 | 184 | case 0x0016: 185 | //VT_I8 (0x0014) MUST be an 4-byte signed integer. 186 | 187 | PropertyNames.Add(propertyName, 188 | BitConverter.ToInt32(propertyValue.Value, propertyIndex) 189 | .ToString(CultureInfo.InvariantCulture)); 190 | 191 | break; 192 | 193 | case 0x0013: 194 | case 0x0017: 195 | //VT_I8 (0x0014) MUST be an 4-byte unsigned integer. 196 | 197 | PropertyNames.Add(propertyName, 198 | BitConverter.ToUInt32(propertyValue.Value, propertyIndex) 199 | .ToString(CultureInfo.InvariantCulture)); 200 | 201 | break; 202 | 203 | case 0x001f: //unicode string 204 | 205 | uniLength = BitConverter.ToInt32(propertyValue.Value, propertyIndex); 206 | propertyIndex += 4; 207 | 208 | if (uniLength <= 0) 209 | { 210 | PropertyNames.Add(propertyName, string.Empty); 211 | break; 212 | } 213 | 214 | unicodeName = Encoding.Unicode.GetString(propertyValue.Value, propertyIndex, 215 | (uniLength*2) - 2); 216 | propertyIndex += (uniLength*2); 217 | 218 | PropertyNames.Add(propertyName, unicodeName); 219 | 220 | break; 221 | 222 | case 0x0040: 223 | // VT_FILETIME 0x0040 Type is FILETIME, and the minimum property set version is 0. 224 | 225 | var hexNumber = BitConverter.ToInt64(propertyValue.Value, propertyIndex); 226 | // "01CDF407"; 227 | 228 | propertyIndex += 8; 229 | 230 | var dd = DateTime.FromFileTimeUtc(hexNumber); 231 | 232 | PropertyNames.Add(propertyName, 233 | dd.ToString(CultureInfo.InvariantCulture)); 234 | 235 | break; 236 | 237 | case 0x0041: 238 | //VT_BLOB 0x0041 Type is binary large object (BLOB), and the minimum property set version is 0 239 | 240 | //TODO FINISH THIS 241 | 242 | var blobSize = BitConverter.ToInt32(propertyValue.Value, propertyIndex); 243 | propertyIndex += 4; 244 | 245 | var bytes = propertyValue.Value.Skip(0x69).ToArray(); 246 | 247 | var props = new PropertyStore(bytes); 248 | 249 | PropertyNames.Add(propertyName, 250 | $"BLOB data: {BitConverter.ToString(propertyValue.Value, propertyIndex)}"); 251 | 252 | foreach (var prop in props.Sheets) 253 | { 254 | foreach (var name in prop.PropertyNames) 255 | { 256 | PropertyNames.Add($"{name.Key}", name.Value); // (From BLOB data) 257 | } 258 | } 259 | 260 | propertyIndex += blobSize; 261 | 262 | break; 263 | 264 | case 0x0042: 265 | //TODO FINISH THIS 266 | 267 | //Type is Stream, and the minimum property set version is 0. VT_STREAM is not allowed in a simple property set. 268 | PropertyNames.Add(propertyName, 269 | "VT_STREAM not implemented (yet) See extension block section for contents for now"); 270 | 271 | break; 272 | 273 | default: 274 | PropertyNames.Add(propertyName, 275 | $"Unknown named property type: {namedType.ToString("X")}, Hex data (after property type): {BitConverter.ToString(propertyValue.Value, propertyIndex)}. Send file to saericzimmerman@gmail.com to get support added"); 276 | break; 277 | //throw new Exception($"Unknown named property type: {namedType.ToString("X")}, Hex data (after property type): {BitConverter.ToString(propertyValue.Value, propertyIndex)}"); 278 | } 279 | } 280 | 281 | var terminator = BitConverter.ToInt32(contents, sheetindex); 282 | 283 | if (terminator != 0) 284 | { 285 | throw new Exception($"Expected terminator of 0, but got {terminator}"); 286 | } 287 | } 288 | else 289 | { 290 | //treat as numeric property values 291 | 292 | PropertySheetType = PropertySheetTypeEnum.Numeric; 293 | 294 | var valueSize = 0; 295 | var propertyId = 0; 296 | 297 | var propertyValues = new Dictionary(); 298 | var propertySlotNumber = 0; 299 | 300 | while (sheetindex < contents.Length) 301 | { 302 | //cut up shellPropertySheetList into byte arrays based on length, then process each one 303 | var sheetSize = BitConverter.ToInt32(contents, sheetindex); 304 | 305 | if (sheetSize == 0 || (uint)sheetSize >= contents.Length) 306 | { 307 | break; // we are out of lists 308 | } 309 | 310 | var sheetListBytes = new byte[sheetSize]; 311 | Array.Copy(contents, sheetindex, sheetListBytes, 0, sheetSize); 312 | 313 | propertyValues.Add(propertySlotNumber, sheetListBytes); 314 | propertySlotNumber += 1; 315 | 316 | sheetindex += sheetSize; 317 | } //end of while in shellPropertySheetList 318 | 319 | foreach (var propertyValue in propertyValues) 320 | { 321 | var propertyIndex = 0; 322 | 323 | valueSize = BitConverter.ToInt32(propertyValue.Value, propertyIndex); 324 | propertyIndex += 4; 325 | 326 | propertyId = BitConverter.ToInt32(propertyValue.Value, propertyIndex); 327 | propertyIndex += 4; 328 | 329 | propertyIndex += 1; //skip reserved 330 | 331 | var numericType = BitConverter.ToUInt16(propertyValue.Value, propertyIndex); 332 | 333 | propertyIndex += 2; //skip type 334 | propertyIndex += 2; //skip padding? 335 | 336 | //TODO Combine these with what is below. Make a function to take the type, process and return a string? 337 | switch (numericType) 338 | { 339 | case 0x1048: 340 | //MUST be a VectorHeader followed by a sequence of GUID (Packet Version) packets. 341 | 342 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), 343 | "VT_VECTOR data not implemented (yet)"); 344 | 345 | break; 346 | case 0x01e: 347 | var uniLength1e = BitConverter.ToInt32(propertyValue.Value, propertyIndex); 348 | propertyIndex += 4; 349 | 350 | var unicodeName1e = 351 | Encoding.Unicode.GetString(propertyValue.Value, propertyIndex, uniLength1e) 352 | .Split('\0') 353 | .First(); 354 | 355 | // Debug.WriteLine($"Find me: {BitConverter.ToString(propertyValue.Value)}, propertyIndex: {propertyIndex} unicodeName1e: {unicodeName1e}"); 356 | 357 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), unicodeName1e); 358 | 359 | break; 360 | case 0x001f: //unicode string 361 | 362 | var uniLength = BitConverter.ToInt32(propertyValue.Value, propertyIndex); 363 | propertyIndex += 4; 364 | 365 | 366 | if (uniLength <= 0) 367 | { 368 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), string.Empty); 369 | break; 370 | } 371 | 372 | var unicodeName = Encoding.Unicode.GetString(propertyValue.Value, propertyIndex, 373 | (uniLength*2) - 2); 374 | propertyIndex += (uniLength*2); 375 | 376 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), unicodeName); 377 | 378 | break; 379 | 380 | case 0x000b: 381 | //VT_BOOL (0x000B) MUST be a VARIANT_BOOL as specified in [MS-OAUT] section 2.2.27, followed by zero padding to 4 bytes. 382 | 383 | var boolInt = BitConverter.ToInt32(propertyValue.Value, propertyIndex); 384 | propertyIndex += 8; 385 | 386 | var boolval = boolInt > 0; 387 | 388 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), 389 | boolval.ToString(CultureInfo.InvariantCulture)); 390 | 391 | break; 392 | 393 | case 0x0003: 394 | //VT_I4 (0x0003) MUST be a 32-bit signed integer. 395 | 396 | var signedInt = BitConverter.ToInt32(propertyValue.Value, propertyIndex); 397 | propertyIndex += 4; 398 | 399 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), 400 | signedInt.ToString(CultureInfo.InvariantCulture)); 401 | 402 | break; 403 | 404 | case 0x0015: 405 | //VT_UI8 (0x0015) MUST be an 8-byte unsigned integer 406 | 407 | var unsigned8int = BitConverter.ToUInt64(propertyValue.Value, propertyIndex); 408 | propertyIndex += 8; 409 | 410 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), 411 | unsigned8int.ToString(CultureInfo.InvariantCulture)); 412 | 413 | break; 414 | 415 | case 0x0042: 416 | //VT_STREAM (0x0042) MUST be an IndirectPropertyName. The storage representing the 417 | //(non-simple) property set MUST have a stream element with this name 418 | 419 | //defer for now 420 | 421 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), 422 | "VT_STREAM not implemented"); 423 | 424 | break; 425 | 426 | case 0x0013: 427 | //VT_UI4 (0x0013) MUST be a 4-byte unsigned integer 428 | 429 | var unsigned4int = BitConverter.ToUInt32(propertyValue.Value, propertyIndex); 430 | propertyIndex += 4; 431 | 432 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), 433 | unsigned4int.ToString(CultureInfo.InvariantCulture)); 434 | 435 | break; 436 | 437 | case 0x0001: 438 | //VT_NULL (0x0001) MUST be zero bytes in length. 439 | 440 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), "Null"); 441 | 442 | break; 443 | 444 | case 0x0002: 445 | //VT_I2 (0x0002) Either the specified type, or the type of the element or contained field MUST be a 2-byte signed int 446 | 447 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), BitConverter.ToUInt16(propertyValue.Value, propertyIndex).ToString(CultureInfo.InvariantCulture)); 448 | 449 | break; 450 | 451 | case 0x101f: 452 | //VT_VECTOR | VT_LPWSTR 0x101F Type is Vector of UnicodeString, and the minimum property set version is 0 453 | 454 | propertyIndex += 4; 455 | 456 | unicodeName = string.Empty; 457 | 458 | if (propertyValue.Value.Length>propertyIndex) 459 | { 460 | uniLength = BitConverter.ToInt32(propertyValue.Value, propertyIndex); 461 | propertyIndex += 4; 462 | 463 | unicodeName = Encoding.Unicode.GetString(propertyValue.Value, propertyIndex, 464 | (uniLength * 2) - 2); 465 | propertyIndex += (uniLength * 2); 466 | } 467 | 468 | 469 | 470 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), unicodeName); 471 | 472 | break; 473 | 474 | case 0x0048: 475 | //VT_CLSID 0x0048 Type is CLSID, and the minimum property set version is 0. 476 | 477 | var rawguid1 = new byte[16]; 478 | 479 | Array.Copy(propertyValue.Value, propertyIndex, rawguid1, 0, 16); 480 | 481 | propertyIndex += 16; 482 | 483 | var rawguid = Utils.ExtractGuidFromShellItem(rawguid1); 484 | 485 | var foldername = Utils.GetFolderNameFromGuid(rawguid); 486 | 487 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), foldername); 488 | 489 | break; 490 | 491 | case 0x1011: 492 | //VT_VECTOR | VT_UI1 0x1011 Type is Vector of 1-byte unsigned integers, and the minimum property set version is 0. 493 | 494 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), 495 | "VT_VECTOR data not implemented (yet) See extension block section for contents for now"); 496 | 497 | //TODO i see indicators from 0x00, case 0x23febbee: ProcessPropertyViewGUID(rawBytes) in the bits for this 498 | // can we pull out the property sheet and add them to the property names here? 499 | 500 | break; 501 | 502 | case 0x0040: 503 | //VT_FILETIME 0x0040 Type is FILETIME, and the minimum property set version is 0. 504 | 505 | var hexNumber = BitConverter.ToInt64(propertyValue.Value, propertyIndex); 506 | // "01CDF407"; 507 | 508 | propertyIndex += 8; 509 | 510 | var dd = DateTime.FromFileTimeUtc(hexNumber); 511 | 512 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), 513 | dd.ToString(CultureInfo.InvariantCulture)); 514 | 515 | break; 516 | 517 | case 0x0008: 518 | 519 | var codePageSize = BitConverter.ToInt32(propertyValue.Value, propertyIndex); 520 | propertyIndex += 4; 521 | 522 | var codePageName = Encoding.Unicode.GetString(propertyValue.Value, propertyIndex, 523 | codePageSize - 2); 524 | propertyIndex += (codePageSize); 525 | 526 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), codePageName); 527 | 528 | break; 529 | 530 | default: 531 | PropertyNames.Add(propertyId.ToString(CultureInfo.InvariantCulture), 532 | $"Unknown numeric property type: {numericType.ToString("X")}, Hex data (after property type): {BitConverter.ToString(propertyValue.Value, propertyIndex)}. Send file to saericzimmerman@gmail.com to get support added"); 533 | break; 534 | // throw new Exception($"Unknown numeric property type: {numericType.ToString("X")}, Hex data (after property type): {BitConverter.ToString(propertyValue.Value, propertyIndex)}"); 535 | } 536 | } 537 | 538 | var terminator = BitConverter.ToInt32(contents, sheetindex); 539 | 540 | if (terminator != 0) 541 | { 542 | throw new Exception($"Expected terminator of 0, but got {terminator}"); 543 | } 544 | } 545 | } 546 | 547 | public int Size { get; private set; } 548 | 549 | public string Version { get; private set; } 550 | 551 | public string GUID { get; private set; } 552 | 553 | public byte[] HexValue { get; set; } 554 | 555 | public Dictionary PropertyNames { get; } 556 | 557 | public PropertySheetTypeEnum PropertySheetType { get; private set; } 558 | 559 | public override string ToString() 560 | { 561 | var sb = new StringBuilder(); 562 | 563 | if (PropertySheetType == PropertySheetTypeEnum.Numeric) 564 | { 565 | var s = string.Join("; ", PropertyNames.Select(x => $"Guid: {GUID}, Key: {x.Key} ==> {Utils.GetDescriptionFromGuidAndKey(GUID, int.Parse(x.Key))}, Value: {x.Value}")); 566 | 567 | sb.Append(s); 568 | } 569 | else 570 | { 571 | var s = string.Join("; ", PropertyNames.Select(x => $"Guid: {GUID}, Key: {x.Key} ==> {x.Key}, Value: {x.Value}")); 572 | 573 | sb.Append(s); 574 | } 575 | 576 | 577 | 578 | return sb.ToString(); 579 | } 580 | } 581 | } 582 | -------------------------------------------------------------------------------- /ExtensionBlocks/PropertyStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace ExtensionBlocks 6 | { 7 | public class PropertyStore 8 | { 9 | public PropertyStore() 10 | { 11 | Sheets = new List(); 12 | } 13 | 14 | public PropertyStore(byte[] rawBytes) 15 | { 16 | Sheets = new List(); 17 | 18 | //shellPropertySheetList now contains what we need to parse for the rest of this process 19 | 20 | var shellPropertyIndex = 0; 21 | 22 | var sheetLists = new Dictionary(); 23 | var sheetListslot = 0; 24 | 25 | while (shellPropertyIndex < rawBytes.Length) 26 | { 27 | //cut up shellPropertySheetList into byte arrays based on length, then process each one 28 | var serializedSize = BitConverter.ToInt32(rawBytes, shellPropertyIndex); 29 | 30 | if (serializedSize == 0 || (uint)serializedSize >= rawBytes.Length) 31 | { 32 | break; // we are out of lists 33 | } 34 | 35 | var sheetListBytes = new byte[serializedSize]; 36 | Array.Copy(rawBytes, shellPropertyIndex, sheetListBytes, 0, serializedSize); 37 | 38 | sheetLists.Add(sheetListslot, sheetListBytes); 39 | sheetListslot += 1; 40 | 41 | shellPropertyIndex += serializedSize; 42 | } //end of while in shellPropertySheetList 43 | 44 | foreach (var sheetList in sheetLists) 45 | { 46 | var sheet = new PropertySheet(sheetList.Value); 47 | 48 | Sheets.Add(sheet); 49 | } 50 | } 51 | 52 | public List Sheets { get; } 53 | 54 | public override string ToString() 55 | { 56 | var sb = new StringBuilder(); 57 | 58 | var sheetNumber = 0; 59 | 60 | foreach (var propertySheet in Sheets) 61 | { 62 | sb.AppendLine($"Sheet #{sheetNumber} => {propertySheet}"); 63 | 64 | sheetNumber += 1; 65 | 66 | } 67 | 68 | return sb.ToString(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ExtensionBlocks/ShellBag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace ExtensionBlocks 6 | { 7 | public abstract class ShellBag : IShellBag 8 | { 9 | public static bool ShowHexInString { get; set; } 10 | public string InternalId { get; set; } 11 | 12 | public string FriendlyName { get; set; } 13 | 14 | public byte[] HexValue { get; set; } 15 | 16 | public string BagPath { get; set; } 17 | 18 | public virtual string AbsolutePath { get; set; } 19 | 20 | public int Slot { get; set; } 21 | 22 | public bool IsDeleted { get; set; } 23 | 24 | public int MruPosition { get; set; } 25 | public int NodeSlot { get; set; } 26 | 27 | public List ChildShellBags { get; set; } 28 | 29 | public string Value { get; set; } 30 | 31 | public DateTimeOffset? LastWriteTime { get; set; } 32 | 33 | public DateTimeOffset? FirstInteracted { get; set; } 34 | public bool HasExplored { get; set; } 35 | 36 | public DateTimeOffset? LastInteracted { get; set; } 37 | 38 | public Utils.ShellBagTypes ShellBagType { get; set; } 39 | 40 | 41 | public List ExtensionBlocks { get; set; } 42 | 43 | public int NodeId { get; set; } 44 | 45 | public override string ToString() 46 | { 47 | var sb = new StringBuilder(); 48 | 49 | sb.AppendLine($"Value: {Value}"); 50 | sb.AppendLine($"Shell Type: {FriendlyName}"); 51 | 52 | sb.AppendLine(); 53 | 54 | if (BagPath.Length > 0) 55 | { 56 | sb.AppendLine($"Bag Path: {BagPath}, Slot #: {Slot}, MRU Position: {MruPosition}, Node Slot: {NodeSlot}"); 57 | sb.AppendLine($"Absolute Path: {AbsolutePath}"); 58 | sb.AppendLine(); 59 | } 60 | 61 | sb.AppendLine($"Has been explored: {HasExplored}"); 62 | sb.AppendLine(); 63 | 64 | if (IsDeleted) 65 | { 66 | sb.AppendLine("Deleted: True"); 67 | sb.AppendLine(); 68 | } 69 | 70 | sb.AppendLine($"# Child Bags: {ChildShellBags.Count}"); 71 | 72 | if (FirstInteracted.HasValue) 73 | { 74 | sb.AppendLine(); 75 | sb.AppendLine( 76 | $"First interacted: {FirstInteracted.Value.ToString(Utils.GetDateTimeFormatWithMilliseconds())}"); 77 | } 78 | 79 | if (LastInteracted.HasValue) 80 | { 81 | sb.AppendLine(); 82 | sb.AppendLine( 83 | $"Last interacted: {LastInteracted.Value.ToString(Utils.GetDateTimeFormatWithMilliseconds())}"); 84 | } 85 | 86 | if (ExtensionBlocks.Count > 0) 87 | { 88 | var extensionNumber = 0; 89 | 90 | sb.AppendLine(); 91 | sb.AppendLine($"Extension blocks found: {ExtensionBlocks.Count}"); 92 | 93 | foreach (var extensionBlock in ExtensionBlocks) 94 | { 95 | if (extensionBlock is BeefPlaceHolder) 96 | { 97 | continue; 98 | } 99 | 100 | sb.AppendLine($"---------------------- Block {extensionNumber:N0} ----------------------"); 101 | 102 | sb.AppendLine(extensionBlock.ToString()); 103 | 104 | extensionNumber += 1; 105 | } 106 | 107 | sb.AppendLine("--------------------------------------------------"); 108 | } 109 | 110 | if (LastWriteTime.HasValue) 111 | { 112 | sb.AppendLine(); 113 | sb.AppendLine( 114 | $"Last Write Time: {LastWriteTime.Value.ToString(Utils.GetDateTimeFormatWithMilliseconds())}"); 115 | } 116 | 117 | if (ShowHexInString) 118 | { 119 | sb.AppendLine($"\r\nHex Value: {BitConverter.ToString(HexValue)}"); 120 | } 121 | 122 | return sb.ToString(); 123 | } 124 | } 125 | } -------------------------------------------------------------------------------- /ExtensionBlocks/ShellBag0X31.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace ExtensionBlocks 7 | { 8 | public class ShellBag0X31 : ShellBag 9 | { 10 | public ShellBag0X31(int slot, int mruPosition, byte[] rawBytes, string bagPath) 11 | { 12 | Slot = slot; 13 | MruPosition = mruPosition; 14 | 15 | FriendlyName = "Directory"; 16 | ShellBagType = Utils.ShellBagTypes.Directory; 17 | 18 | ChildShellBags = new List(); 19 | 20 | InternalId = Guid.NewGuid().ToString(); 21 | 22 | HexValue = rawBytes; 23 | 24 | ExtensionBlocks = new List(); 25 | 26 | BagPath = bagPath; 27 | 28 | var index = 2; 29 | 30 | 31 | index += 1; 32 | 33 | //skip unknown byte 34 | index += 1; 35 | 36 | index += 4; // skip file size since always 0 for directory 37 | 38 | LastModificationTime = Utils.ExtractDateTimeOffsetFromBytes(rawBytes.Skip(index).Take(4).ToArray()); 39 | 40 | index += 4; 41 | 42 | index += 2; 43 | 44 | var len = 0; 45 | 46 | 47 | var beefPos = BitConverter.ToString(rawBytes).IndexOf("04-00-EF-BE", StringComparison.InvariantCulture) / 3; 48 | 49 | if (beefPos == 0) 50 | { 51 | var hackName = CodePagesEncodingProvider.Instance.GetEncoding(1252).GetString(rawBytes, index, rawBytes.Length - index); 52 | 53 | var segs = hackName.Split(new[] {'\0'}, StringSplitOptions.RemoveEmptyEntries); 54 | 55 | ShortName = string.Join("|", segs); 56 | 57 | Value = ShortName; 58 | return; 59 | } 60 | 61 | 62 | beefPos = beefPos - 4; //add header back for beef 63 | 64 | var strLen = beefPos - index; 65 | 66 | if (rawBytes[2] == 0x35|| rawBytes[2] == 0x36) 67 | { 68 | len = strLen; 69 | } 70 | else 71 | { 72 | while (rawBytes[index + len] != 0x0) 73 | { 74 | len += 1; 75 | } 76 | } 77 | 78 | var tempBytes = new byte[len]; 79 | Array.Copy(rawBytes, index, tempBytes, 0, len); 80 | 81 | var shortName = ""; 82 | 83 | if (rawBytes[2] == 0x35|| rawBytes[2] == 0x36) 84 | { 85 | shortName = Encoding.Unicode.GetString(tempBytes); 86 | } 87 | else 88 | { 89 | shortName = CodePagesEncodingProvider.Instance.GetEncoding(1252).GetString(tempBytes); 90 | } 91 | 92 | ShortName = shortName; 93 | 94 | Value = shortName; 95 | 96 | index = beefPos; 97 | 98 | // here is where we need to cut up the rest into extension blocks 99 | var chunks = new List(); 100 | 101 | while (index < rawBytes.Length) 102 | { 103 | var subshellitemdatasize = BitConverter.ToInt16(rawBytes, index); 104 | //index += 2; 105 | 106 | if (subshellitemdatasize == 0) 107 | { 108 | index += 2; //some kind of separator 109 | } 110 | 111 | if (subshellitemdatasize == 1) 112 | { 113 | //some kind of separator 114 | index += 2; 115 | } 116 | else 117 | { 118 | if (subshellitemdatasize > 0) 119 | { 120 | var shellBuff = new byte[subshellitemdatasize]; 121 | Buffer.BlockCopy(rawBytes, index, shellBuff, 0, subshellitemdatasize); 122 | 123 | chunks.Add(shellBuff); 124 | index += subshellitemdatasize; 125 | } 126 | } 127 | } 128 | 129 | foreach (var bytes in chunks) 130 | { 131 | index = 0; 132 | 133 | var extsize = BitConverter.ToInt16(bytes, index); 134 | 135 | var signature = BitConverter.ToUInt32(bytes, 0x04); 136 | 137 | //TODO does this need to check if its a 0xbeef?? regex? 138 | var block = Utils.GetExtensionBlockFromBytes(signature, bytes); 139 | 140 | if (block.Signature.ToString("X").StartsWith("BEEF00")) 141 | { 142 | ExtensionBlocks.Add(block); 143 | } 144 | 145 | 146 | var beef0004 = block as Beef0004; 147 | if (beef0004 != null) 148 | { 149 | Value = beef0004.LongName; 150 | } 151 | 152 | 153 | 154 | index += extsize; 155 | } 156 | } 157 | 158 | /// 159 | /// last modified time of BagPath 160 | /// 161 | public DateTimeOffset? LastModificationTime { get; set; } 162 | 163 | 164 | /// 165 | /// Last access time of BagPath 166 | /// 167 | public DateTimeOffset? LastAccessTime { get; set; } 168 | 169 | 170 | public string ShortName { get; } 171 | 172 | 173 | 174 | public override string ToString() 175 | { 176 | var sb = new StringBuilder(); 177 | 178 | if (ShortName.Length > 0) 179 | { 180 | sb.AppendLine($"Short name: {ShortName}"); 181 | } 182 | 183 | if (LastModificationTime.HasValue) 184 | { 185 | sb.AppendLine( 186 | $"Modified: {LastModificationTime.Value.ToString(Utils.GetDateTimeFormatWithMilliseconds())}"); 187 | } 188 | 189 | if (LastAccessTime.HasValue) 190 | { 191 | sb.AppendLine( 192 | $"Last Access: {LastAccessTime.Value.ToString(Utils.GetDateTimeFormatWithMilliseconds())}"); 193 | } 194 | 195 | sb.AppendLine(); 196 | sb.AppendLine(base.ToString()); 197 | 198 | return sb.ToString(); 199 | } 200 | } 201 | } -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Eric Zimmerman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ExtensionBlocks 2 | Extension blocks as found in ShellBags and other places in the Registry 3 | 4 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EricZimmerman/ExtensionBlocks/58e35b8457bf3006f672c972619bc0fb913fb7e4/icon.png --------------------------------------------------------------------------------