├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── dotnetcore.yml ├── .gitignore ├── EatPdb.sln ├── EatPdb ├── Config.cs ├── EatPdb.csproj ├── PEAction.cs ├── Program.cs └── SymbolDatabase.cs ├── PEReader ├── PEReader.csproj └── Program.cs ├── PESupport ├── AddressResolver.cs ├── BinaryEx.cs ├── FileEx.cs ├── PESupport.cs ├── PESupport.csproj └── StructOP.cs ├── PdbReader ├── PdbReader.csproj └── Program.cs ├── README.md └── omnisharp.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories 2 | root = true 3 | 4 | # C# files 5 | [*.cs] 6 | 7 | #### Core EditorConfig Options #### 8 | 9 | # Indentation and spacing 10 | indent_size = 4 11 | indent_style = space 12 | tab_width = 4 13 | 14 | # New line preferences 15 | end_of_line = crlf 16 | insert_final_newline = false 17 | 18 | #### .NET Coding Conventions #### 19 | 20 | # Organize usings 21 | dotnet_separate_import_directive_groups = false 22 | dotnet_sort_system_directives_first = false 23 | 24 | # this. and Me. preferences 25 | dotnet_style_qualification_for_event = false:suggestion 26 | dotnet_style_qualification_for_field = false:suggestion 27 | dotnet_style_qualification_for_method = false:suggestion 28 | dotnet_style_qualification_for_property = false:suggestion 29 | 30 | # Language keywords vs BCL types preferences 31 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 32 | dotnet_style_predefined_type_for_member_access = true:suggestion 33 | 34 | # Parentheses preferences 35 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent 36 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent 37 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent 38 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent 39 | 40 | # Modifier preferences 41 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent 42 | 43 | # Expression-level preferences 44 | dotnet_style_coalesce_expression = true:suggestion 45 | dotnet_style_collection_initializer = true:suggestion 46 | dotnet_style_explicit_tuple_names = true:suggestion 47 | dotnet_style_null_propagation = true:suggestion 48 | dotnet_style_object_initializer = true:suggestion 49 | dotnet_style_prefer_auto_properties = true:silent 50 | dotnet_style_prefer_compound_assignment = true:suggestion 51 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent 52 | dotnet_style_prefer_conditional_expression_over_return = true:silent 53 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 54 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 55 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 56 | 57 | # Field preferences 58 | dotnet_style_readonly_field = true:suggestion 59 | 60 | # Parameter preferences 61 | dotnet_code_quality_unused_parameters = all:suggestion 62 | 63 | #### C# Coding Conventions #### 64 | 65 | # var preferences 66 | csharp_style_var_elsewhere = true:suggestion 67 | csharp_style_var_for_built_in_types = true:suggestion 68 | csharp_style_var_when_type_is_apparent = true:suggestion 69 | 70 | # Expression-bodied members 71 | csharp_style_expression_bodied_accessors = true:suggestion 72 | csharp_style_expression_bodied_constructors = true:suggestion 73 | csharp_style_expression_bodied_indexers = true:suggestion 74 | csharp_style_expression_bodied_lambdas = true:silent 75 | csharp_style_expression_bodied_local_functions = false:silent 76 | csharp_style_expression_bodied_methods = true:suggestion 77 | csharp_style_expression_bodied_operators = true:suggestion 78 | csharp_style_expression_bodied_properties = true:suggestion 79 | 80 | # Pattern matching preferences 81 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 82 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 83 | csharp_style_prefer_switch_expression = true:suggestion 84 | 85 | # Null-checking preferences 86 | csharp_style_conditional_delegate_call = true:suggestion 87 | 88 | # Modifier preferences 89 | csharp_prefer_static_local_function = true:suggestion 90 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async 91 | 92 | # Code-block preferences 93 | csharp_prefer_braces = false:suggestion 94 | csharp_prefer_simple_using_statement = true:suggestion 95 | 96 | # Expression-level preferences 97 | csharp_prefer_simple_default_expression = true:suggestion 98 | csharp_style_deconstructed_variable_declaration = true:suggestion 99 | csharp_style_inlined_variable_declaration = true:suggestion 100 | csharp_style_pattern_local_over_anonymous_function = true:suggestion 101 | csharp_style_prefer_index_operator = true:suggestion 102 | csharp_style_prefer_range_operator = true:suggestion 103 | csharp_style_throw_expression = true:suggestion 104 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion 105 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent 106 | 107 | # 'using' directive preferences 108 | csharp_using_directive_placement = outside_namespace:silent 109 | 110 | #### C# Formatting Rules #### 111 | 112 | # New line preferences 113 | csharp_new_line_before_catch = false 114 | csharp_new_line_before_else = false 115 | csharp_new_line_before_finally = false 116 | csharp_new_line_before_members_in_anonymous_types = false 117 | csharp_new_line_before_members_in_object_initializers = false 118 | csharp_new_line_before_open_brace = none 119 | csharp_new_line_between_query_expression_clauses = true 120 | 121 | # Indentation preferences 122 | csharp_indent_block_contents = true 123 | csharp_indent_braces = false 124 | csharp_indent_case_contents = true 125 | csharp_indent_case_contents_when_block = false 126 | csharp_indent_labels = one_less_than_current 127 | csharp_indent_switch_labels = false 128 | 129 | # Space preferences 130 | csharp_space_after_cast = true 131 | csharp_space_after_colon_in_inheritance_clause = true 132 | csharp_space_after_comma = true 133 | csharp_space_after_dot = false 134 | csharp_space_after_keywords_in_control_flow_statements = true 135 | csharp_space_after_semicolon_in_for_statement = true 136 | csharp_space_around_binary_operators = before_and_after 137 | csharp_space_around_declaration_statements = ignore 138 | csharp_space_before_colon_in_inheritance_clause = true 139 | csharp_space_before_comma = false 140 | csharp_space_before_dot = false 141 | csharp_space_before_open_square_brackets = false 142 | csharp_space_before_semicolon_in_for_statement = false 143 | csharp_space_between_empty_square_brackets = false 144 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 145 | csharp_space_between_method_call_name_and_opening_parenthesis = false 146 | csharp_space_between_method_call_parameter_list_parentheses = false 147 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 148 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 149 | csharp_space_between_method_declaration_parameter_list_parentheses = false 150 | csharp_space_between_parentheses = false 151 | csharp_space_between_square_brackets = false 152 | 153 | # Wrapping preferences 154 | csharp_preserve_single_line_blocks = true 155 | csharp_preserve_single_line_statements = false 156 | 157 | #### Naming styles #### 158 | 159 | # Naming rules 160 | 161 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning 162 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface 163 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i 164 | 165 | dotnet_naming_rule.types_should_be_pascal_case.severity = warning 166 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types 167 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case 168 | 169 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning 170 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members 171 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case 172 | 173 | # Symbol specifications 174 | 175 | dotnet_naming_symbols.interface.applicable_kinds = interface 176 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 177 | dotnet_naming_symbols.interface.required_modifiers = 178 | 179 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum 180 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 181 | dotnet_naming_symbols.types.required_modifiers = 182 | 183 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method 184 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 185 | dotnet_naming_symbols.non_field_members.required_modifiers = 186 | 187 | # Naming styles 188 | 189 | dotnet_naming_style.pascal_case.required_prefix = 190 | dotnet_naming_style.pascal_case.required_suffix = 191 | dotnet_naming_style.pascal_case.word_separator = 192 | dotnet_naming_style.pascal_case.capitalization = pascal_case 193 | 194 | dotnet_naming_style.begins_with_i.required_prefix = I 195 | dotnet_naming_style.begins_with_i.required_suffix = 196 | dotnet_naming_style.begins_with_i.word_separator = 197 | dotnet_naming_style.begins_with_i.capitalization = pascal_case 198 | -------------------------------------------------------------------------------- /.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/dotnetcore.yml: -------------------------------------------------------------------------------- 1 | name: .NET Core 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | os: [win-x64, linux-x64] 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Setup .NET Core 15 | uses: actions/setup-dotnet@v1 16 | with: 17 | dotnet-version: 3.1.100 18 | - name: Build with dotnet 19 | run: dotnet build -c Release 20 | - name: Publish 21 | run: dotnet publish -c Release -r ${{ matrix.os }} -o dist --self-contained 22 | - name: Upload artifact 23 | uses: actions/upload-artifact@v1.0.0 24 | with: 25 | name: ${{ matrix.os }} 26 | path: dist 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | *- Backup*.rdl 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush personal settings 299 | .cr/personal 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | 336 | # Local History for Visual Studio 337 | .localhistory/ 338 | 339 | # BeatPulse healthcheck temp database 340 | healthchecksdb -------------------------------------------------------------------------------- /EatPdb.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29728.190 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PdbReader", "PdbReader\PdbReader.csproj", "{B9770D48-849A-4A4C-A061-B31DF6FF4288}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CEBC03DA-6D63-4A0E-A039-C1B7CE53A9BF}" 9 | ProjectSection(SolutionItems) = preProject 10 | .editorconfig = .editorconfig 11 | .github\workflows\dotnetcore.yml = .github\workflows\dotnetcore.yml 12 | README.md = README.md 13 | EndProjectSection 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PEReader", "PEReader\PEReader.csproj", "{DC713E81-ADF4-4422-ADCE-B98E2CEC998F}" 16 | EndProject 17 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PESupport", "PESupport\PESupport.csproj", "{FC808605-A648-4034-BB43-F6E652D82C89}" 18 | EndProject 19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EatPdb", "EatPdb\EatPdb.csproj", "{5719CBF6-D08C-4F9E-BC97-8FC5BB9A6228}" 20 | EndProject 21 | Global 22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 23 | Debug|Any CPU = Debug|Any CPU 24 | Release|Any CPU = Release|Any CPU 25 | EndGlobalSection 26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 27 | {B9770D48-849A-4A4C-A061-B31DF6FF4288}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {B9770D48-849A-4A4C-A061-B31DF6FF4288}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {B9770D48-849A-4A4C-A061-B31DF6FF4288}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {B9770D48-849A-4A4C-A061-B31DF6FF4288}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {DC713E81-ADF4-4422-ADCE-B98E2CEC998F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {DC713E81-ADF4-4422-ADCE-B98E2CEC998F}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {DC713E81-ADF4-4422-ADCE-B98E2CEC998F}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {DC713E81-ADF4-4422-ADCE-B98E2CEC998F}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {FC808605-A648-4034-BB43-F6E652D82C89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {FC808605-A648-4034-BB43-F6E652D82C89}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {FC808605-A648-4034-BB43-F6E652D82C89}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {FC808605-A648-4034-BB43-F6E652D82C89}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {5719CBF6-D08C-4F9E-BC97-8FC5BB9A6228}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {5719CBF6-D08C-4F9E-BC97-8FC5BB9A6228}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {5719CBF6-D08C-4F9E-BC97-8FC5BB9A6228}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {5719CBF6-D08C-4F9E-BC97-8FC5BB9A6228}.Release|Any CPU.Build.0 = Release|Any CPU 43 | EndGlobalSection 44 | GlobalSection(SolutionProperties) = preSolution 45 | HideSolutionNode = FALSE 46 | EndGlobalSection 47 | GlobalSection(ExtensibilityGlobals) = postSolution 48 | SolutionGuid = {4A09B149-93AE-4471-8EB0-D74D9AE42C79} 49 | EndGlobalSection 50 | EndGlobal 51 | -------------------------------------------------------------------------------- /EatPdb/Config.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text.RegularExpressions; 7 | using YamlDotNet.Core; 8 | using YamlDotNet.Core.Events; 9 | using YamlDotNet.Serialization; 10 | 11 | namespace EatPdb { 12 | #nullable enable 13 | public class Config { 14 | public interface IFilter { 15 | public bool Filter(bool isCode, string name); 16 | } 17 | public interface IFilterData : IFilter { } 18 | public class IsCodeFilter : IFilterData { 19 | public IsCodeFilter(bool isCode) => IsCode = isCode; 20 | 21 | public bool IsCode { get; set; } 22 | 23 | public bool Filter(bool isCode, string name) => isCode == IsCode; 24 | } 25 | public class FullNameFilter : IFilterData { 26 | public FullNameFilter(string name) => Name = name; 27 | 28 | public string Name { get; set; } = ""; 29 | 30 | public bool Filter(bool isCode, string name) => name == Name; 31 | } 32 | public class PrefixFilter : IFilterData { 33 | public PrefixFilter(string prefix) => Prefix = prefix; 34 | 35 | public string Prefix { get; set; } = ""; 36 | public bool Filter(bool isCode, string name) => name.StartsWith(Prefix); 37 | } 38 | public class RegexFilter : IFilterData { 39 | public RegexFilter(string pattern) => regex = new Regex(pattern, RegexOptions.Compiled); 40 | 41 | public Regex regex; 42 | public bool Filter(bool isCode, string name) => regex.IsMatch(name); 43 | } 44 | public class WhitelistFilter : IFilter { 45 | public WhitelistFilter(IEnumerable? filters) => Filters = filters; 46 | 47 | public IEnumerable? Filters { get; set; } 48 | public bool Filter(bool isCode, string name) => Filters == null ? false : Filters.Select(filter => filter.Filter(isCode, name)).Contains(true); 49 | } 50 | public class BlacklistFilter : IFilter { 51 | public BlacklistFilter(IEnumerable? filters) => Filters = filters; 52 | 53 | public IEnumerable? Filters { get; set; } 54 | public bool Filter(bool isCode, string name) => Filters == null ? true : !Filters.Select(filter => filter.Filter(isCode, name)).Contains(true); 55 | } 56 | public class YamlTypeConverter : IYamlTypeConverter { 57 | public bool Accepts(Type type) => type == typeof(WhitelistFilter) || type == typeof(BlacklistFilter); 58 | public object? ReadYaml(IParser parser, Type type) { 59 | var data = new List(); 60 | parser.Consume(); 61 | while (!parser.TryConsume(out var _)) { 62 | if (parser.TryConsume(out var scalar)) { 63 | data.Add(new FullNameFilter(scalar.Value)); 64 | } else if (parser.TryConsume(out var _)) { 65 | if (parser.TryConsume(out var key)) { 66 | var value = parser.Consume(); 67 | switch (key.Value) { 68 | case "is_code": 69 | data.Add(new IsCodeFilter(bool.Parse(value.Value))); 70 | break; 71 | case "full": 72 | case "name": 73 | data.Add(new FullNameFilter(value.Value)); 74 | break; 75 | case "prefix": 76 | data.Add(new PrefixFilter(value.Value)); 77 | break; 78 | case "regex": 79 | case "pattern": 80 | data.Add(new RegexFilter(value.Value)); 81 | break; 82 | default: 83 | throw new NotImplementedException("Unknown key: " + value.Value); 84 | } 85 | } 86 | parser.Consume(); 87 | } else { 88 | throw new NotImplementedException("Unknown list"); 89 | } 90 | } 91 | if (type == typeof(WhitelistFilter)) { 92 | return new WhitelistFilter(data); 93 | } else if (type == typeof(BlacklistFilter)) { 94 | return new BlacklistFilter(data); 95 | } 96 | throw new NotImplementedException(); 97 | } 98 | public void WriteYaml(IEmitter emitter, object? value, Type type) => throw new NotImplementedException(); 99 | } 100 | [Required, YamlMember(Alias = "in")] 101 | public string InputFile { get; set; } = ""; 102 | [Required, YamlMember(Alias = "out")] 103 | public string OutputFile { get; set; } = ""; 104 | [YamlMember(Alias = "pdb")] 105 | public string PdbFile { get; set; } = ""; 106 | [YamlMember(Alias = "def")] 107 | public string DefFile { get; set; } = ""; 108 | [YamlMember(Alias = "dll_name")] 109 | public string DllName { get; set; } = ""; 110 | [YamlMember(Alias = "filter")] 111 | public IFilter? Filter { get; set; } 112 | [YamlMember(Alias = "filterdb")] 113 | public string FilterOutDatabase { get; set; } = ""; 114 | 115 | public void ApplyDefault() { 116 | if (PdbFile == "") 117 | PdbFile = Path.Join(Path.GetDirectoryName(InputFile), Path.GetFileNameWithoutExtension(InputFile) + ".pdb"); 118 | if (DefFile == "") 119 | DefFile = Path.Join(Path.GetDirectoryName(OutputFile), Path.GetFileNameWithoutExtension(OutputFile) + ".def"); 120 | if (DllName == "") 121 | DllName = Path.GetFileName(OutputFile); 122 | } 123 | } 124 | #nullable restore 125 | } 126 | -------------------------------------------------------------------------------- /EatPdb/EatPdb.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | eatpdb 5 | Exe 6 | netcoreapp3.1 7 | 0.0.4 8 | CodeHz 9 | CodeHz 10 | false 11 | CodeHz 12 | MIT 13 | https://github.com/codehz/EatPdb/ 14 | https://github.com/codehz/EatPdb/ 15 | git 16 | 17 | en-US 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /EatPdb/PEAction.cs: -------------------------------------------------------------------------------- 1 | using PESupport; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace EatPdb { 8 | 9 | internal class PEAction : IDisposable { 10 | private readonly FileStream File; 11 | private readonly DataDir[] Dirs; 12 | public readonly BinaryReader Reader; 13 | public readonly BinaryWriter Writer; 14 | public readonly AddressResolver Resolver; 15 | private readonly uint NTHeaderOffset; 16 | private NtHeader NtHeader; 17 | 18 | internal PEAction(FileStream file) { 19 | File = file; 20 | Reader = new BinaryReader(file); 21 | Writer = new BinaryWriter(file); 22 | 23 | NTHeaderOffset = Reader.ReadStruct().AddressOfNewExeHeader; 24 | File.Seek(NTHeaderOffset, SeekOrigin.Begin); 25 | NtHeader = Reader.ReadStruct(); 26 | NtHeader.AssertHealth(); 27 | Dirs = new DataDir[16]; 28 | for (var i = 0; i < 16; i++) 29 | Dirs[i] = Reader.ReadStruct(); 30 | Resolver = new AddressResolver(NtHeader.FileHeader.NumberOfSections); 31 | for (uint i = 0; i < NtHeader.FileHeader.NumberOfSections; i++) 32 | Resolver.Put(Reader.ReadStruct()); 33 | } 34 | 35 | internal uint GetEnding() => NtHeader.OptionalHeader.SizeOfImage; 36 | 37 | internal void PatchNtHeader(uint addition) => NtHeader.OptionalHeader.SizeOfImage += addition; 38 | 39 | internal void PatchDir(uint index, uint address, uint length) { 40 | Dirs[index].VirtualAddress = address; 41 | Dirs[index].Size = length; 42 | File.Seek(NTHeaderOffset + Marshal.SizeOf() + index * Marshal.SizeOf(), SeekOrigin.Begin); 43 | Writer.WriteStruct(Dirs[index]); 44 | } 45 | 46 | internal void AppendSection(SectionHeader header) { 47 | var lastSec = NtHeader.FileHeader.NumberOfSections; 48 | NtHeader.FileHeader.NumberOfSections += 1; 49 | File.Seek(NTHeaderOffset, SeekOrigin.Begin); 50 | Writer.WriteStruct(NtHeader); 51 | File.Seek(16 * Marshal.SizeOf() + lastSec * Marshal.SizeOf(), SeekOrigin.Current); 52 | Writer.WriteStruct(header); 53 | Resolver.Put(header); 54 | } 55 | 56 | internal void Seek(uint RVA) => File.SeekRVA(Resolver, RVA); 57 | 58 | internal void WriteExport(ExportDir expdir) { 59 | Seek(Dirs[0].VirtualAddress); 60 | Writer.WriteStruct(expdir); 61 | } 62 | 63 | internal IEnumerable GetImportSymbols() { 64 | if (Dirs[1].VirtualAddress != 0) { 65 | var pos = Resolver.Resolve(Dirs[1].VirtualAddress); 66 | for (uint i = 0; ; i++) { 67 | File.Seek(pos, SeekOrigin.Begin); 68 | var imp = Reader.ReadStruct(); 69 | pos = (uint) File.Position; 70 | if (imp.Name == 0) 71 | break; 72 | var pos2 = Resolver.Resolve(imp.FirstThunk); 73 | for (uint j = 0; ; j++) { 74 | File.Seek(pos2, SeekOrigin.Begin); 75 | var thunk = Reader.ReadStruct(); 76 | pos2 = (uint) File.Position; 77 | if (thunk.IsEmpty()) 78 | break; 79 | if (thunk.TryGetOrdinal(out var _)) { 80 | } else { 81 | File.SeekRVA(Resolver, (uint) thunk.Value); 82 | Reader.ReadStruct(); 83 | yield return Reader.ReadByteString(); 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | public void Dispose() => Reader.Dispose(); 91 | } 92 | } -------------------------------------------------------------------------------- /EatPdb/Program.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using PESupport; 3 | using SharpPdb.Native; 4 | using System; 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Data.SQLite; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Runtime.InteropServices; 11 | 12 | namespace EatPdb { 13 | 14 | internal class Program { 15 | [Verb("rebuild", HelpText = "Rebuild PE file from PDB file (deprecated)")] 16 | public class DeprecatedOptions { 17 | 18 | [Value(0, HelpText = "Input File")] 19 | public string InputFile { get; set; } 20 | 21 | [Option("pdb", HelpText = "Pdb File")] 22 | public string PdbFile { get; set; } 23 | 24 | [Option('o', "out", Required = true, HelpText = "Output File")] 25 | public string OutputFile { get; set; } 26 | 27 | [Option("dll", HelpText = "DllName")] 28 | public string DllName { get; set; } 29 | 30 | [Option("def", HelpText = "Module-Definition file")] 31 | public string Definition { get; set; } 32 | 33 | [Option('v', "berbose", Default = false, HelpText = "Verbose Output")] 34 | public bool Verbose { get; set; } 35 | } 36 | 37 | [Verb("exec", HelpText = "Using config")] 38 | public class ConfigOptions { 39 | [Value(0)] 40 | public string ConfigFile { get; set; } 41 | [Option('d', "debug", Default = false, HelpText = "Debug mode")] 42 | public bool Debug { get; set; } 43 | [Option('v', "verbose", Default = false, HelpText = "Verbose Output")] 44 | public bool Verbose { get; set; } 45 | } 46 | 47 | private static void Main(string[] args) => Parser.Default.ParseArguments(args) 48 | .WithParsed(ConfigMain) 49 | .WithParsed(DeprecatedMain); 50 | 51 | private static uint GetAlign(uint length, uint align = 512) => length % align == 0 ? length : (length / align + 1) * align; 52 | 53 | private static void ConfigMain(ConfigOptions options) { 54 | var deserializer = new YamlDotNet.Serialization.DeserializerBuilder() 55 | .WithTagMapping("!whitelist", typeof(Config.WhitelistFilter)) 56 | .WithTagMapping("!blacklist", typeof(Config.BlacklistFilter)) 57 | .WithTypeConverter(new Config.YamlTypeConverter()) 58 | .Build(); 59 | var cfg = deserializer.Deserialize(File.ReadAllText(options.ConfigFile)); 60 | cfg.ApplyDefault(); 61 | if (options.Debug) 62 | Console.WriteLine("{0}", ObjectDumper.Dump(cfg)); 63 | RealMain(cfg, options.Verbose); 64 | } 65 | 66 | private static void DeprecatedMain(DeprecatedOptions options) { 67 | var filter = new Config.BlacklistFilter(new List { 68 | new Config.PrefixFilter("_"), 69 | new Config.PrefixFilter("?__"), 70 | new Config.PrefixFilter("??_"), 71 | new Config.PrefixFilter("??@"), 72 | new Config.PrefixFilter("?$TSS"), 73 | new Config.RegexFilter("std@@[QU]"), 74 | }); 75 | var cfg = new Config 76 | { 77 | InputFile = options.InputFile, 78 | OutputFile = options.OutputFile, 79 | PdbFile = options.PdbFile, 80 | DefFile = options.Definition, 81 | DllName = options.DllName, 82 | Filter = filter 83 | }; 84 | cfg.ApplyDefault(); 85 | RealMain(cfg, options.Verbose); 86 | } 87 | 88 | private static void RealMain(Config config, bool verbose) { 89 | 90 | // Copy input file to output file, so we do not need to touch input file again 91 | File.Copy(config.InputFile, config.OutputFile, true); 92 | using var file = File.Open(config.OutputFile, FileMode.Open, FileAccess.ReadWrite); 93 | using var exp = File.CreateText(config.DefFile); 94 | using var action = new PEAction(file); 95 | using var pdb = new PdbFileReader(config.PdbFile); 96 | using var db = new SQLiteConnection("" + new SQLiteConnectionStringBuilder 97 | { 98 | DataSource = config.FilterOutDatabase, 99 | SyncMode = SynchronizationModes.Off, 100 | NoSharedFlags = true, 101 | FailIfMissing = false 102 | }); 103 | db.Open(); 104 | var filterout = new SortedDictionary(StringComparer.Ordinal); 105 | var symdb = new SymbolDatabase(); 106 | Console.WriteLine("filtering ({0})", pdb.PublicSymbols.Length); 107 | 108 | // Collect all symbols 109 | Parallel.ForEach(pdb.PublicSymbols, (item) => { 110 | if (config.Filter?.Filter(item.IsCode, item.Name) ?? true) { 111 | lock (symdb) { symdb.Add((uint) item.RelativeVirtualAddress, item.Name); } 112 | } else { 113 | lock (filterout) { filterout.Add(item.Name, (uint) item.RelativeVirtualAddress); } 114 | } 115 | }); 116 | 117 | Console.WriteLine("symbols collected: {0}, filtered: {1}", symdb.Count(), filterout.Count()); 118 | 119 | // Exclude imported symbols 120 | foreach (var sym in action.GetImportSymbols()) 121 | if (symdb.RemoveName(sym) && verbose) 122 | Console.WriteLine("Removed {0}", sym); 123 | else if (filterout.Remove(sym) && verbose) 124 | Console.WriteLine("Removed {0} (filtered)", sym); 125 | 126 | Console.WriteLine("symbols fixed: {0}, filtered fixed: {1}", symdb.Count(), filterout.Count()); 127 | 128 | // Write to filter out db 129 | using (var transition = db.BeginTransaction()) { 130 | using (var dropCommand = db.CreateCommand()) { 131 | dropCommand.CommandText = "DROP TABLE IF EXISTS symbols"; 132 | dropCommand.Transaction = transition; 133 | dropCommand.ExecuteNonQuery(); 134 | } 135 | using (var createCommand = db.CreateCommand()) { 136 | createCommand.CommandText = "CREATE TABLE symbols(symbol TEXT PRIMARY KEY, rva INTEGER) WITHOUT ROWID"; 137 | createCommand.Transaction = transition; 138 | createCommand.ExecuteNonQuery(); 139 | } 140 | using (var insertCommand = db.CreateCommand()) { 141 | insertCommand.CommandText = "INSERT INTO symbols VALUES ($symbol, $rva)"; 142 | insertCommand.Transaction = transition; 143 | foreach (var item in filterout) { 144 | insertCommand.Parameters.AddWithValue("$symbol", item.Key); 145 | insertCommand.Parameters.AddWithValue("$rva", item.Value); 146 | insertCommand.ExecuteNonQuery(); 147 | insertCommand.Parameters.Clear(); 148 | insertCommand.Reset(); 149 | } 150 | } 151 | transition.Commit(); 152 | } 153 | 154 | // Build cache 155 | var cache = symdb.ToArray(); 156 | var sorted = symdb.Build(); 157 | 158 | // Print exported symbols 159 | exp.Write("LIBRARY " + config.DllName + "\n"); 160 | exp.Write("EXPORTS\n"); 161 | foreach (var (name, idx) in sorted) 162 | exp.Write(string.Format("\t{0}\n", name)); 163 | 164 | Console.WriteLine("starting rebuild PE file"); 165 | 166 | // Calculate append length 167 | uint length = 0; 168 | var expdirlength = (uint) Marshal.SizeOf(); 169 | var dllnamelength = (uint) (config.DllName.Length + 1); 170 | var NumberOfFunctions = (uint) cache.Count(); 171 | var functionslength = NumberOfFunctions * 4; 172 | var NumberOfNames = (uint) cache.Select(item => item.Value.Count).Aggregate(0, (a, b) => a + b); 173 | var ordinalslength = NumberOfNames * 2; 174 | var nameslength = NumberOfNames * 4; 175 | var stringslength = (uint) sorted.Select(kv => kv.Key.Length + 1).Aggregate(0, (a, b) => a + b); 176 | Console.WriteLine("NumberOfFunctions: {0}", NumberOfFunctions); 177 | Console.WriteLine("NumberOfNames: {0}", NumberOfNames); 178 | Console.WriteLine("Length Of ExportDir: {0}", expdirlength); 179 | Console.WriteLine("Length Of DllName: {0}", dllnamelength); 180 | Console.WriteLine("Length Of Functions: {0}", functionslength); 181 | Console.WriteLine("Length Of Ordinals: {0}", ordinalslength); 182 | Console.WriteLine("Length Of Names: {0}", nameslength); 183 | Console.WriteLine("Length Of Strings: {0}", stringslength); 184 | length = expdirlength + dllnamelength + functionslength + ordinalslength + nameslength + stringslength; 185 | Console.WriteLine("Addition length: {0}", length); 186 | 187 | // Start modify header 188 | var VirtualEnd = action.GetEnding(); 189 | var OriginalSize = (uint) file.Length; 190 | action.PatchNtHeader(GetAlign(length)); 191 | action.PatchDir(0, VirtualEnd, length); 192 | { 193 | var header = new SectionHeader { }; 194 | header.SetName(".hacked"); 195 | header.Misc.VirtualSize = length; 196 | header.VirtualAddress = VirtualEnd; 197 | header.SizeOfRawData = GetAlign(length); 198 | header.PointerToRawData = OriginalSize; 199 | header.Characteristics = 0x40000040; 200 | action.AppendSection(header); 201 | } 202 | 203 | // Write export table 204 | file.SetLength(OriginalSize + GetAlign(length)); 205 | { 206 | var expdir = new ExportDir { }; 207 | expdir.Name = VirtualEnd + expdirlength; 208 | expdir.Base = 1; 209 | expdir.NumberOfFunctions = NumberOfFunctions; 210 | expdir.NumberOfNames = NumberOfNames; 211 | expdir.AddressOfFunctions = VirtualEnd + expdirlength + dllnamelength; 212 | expdir.AddressOfOrdinals = VirtualEnd + expdirlength + dllnamelength + functionslength; 213 | expdir.AddressOfNames = VirtualEnd + expdirlength + dllnamelength + functionslength + ordinalslength + stringslength; 214 | action.WriteExport(expdir); 215 | } 216 | action.Writer.WriteByteString(config.DllName); 217 | foreach (var (key, _) in cache) 218 | action.Writer.WriteStruct(new RVA { Value = key }); 219 | foreach (var (_, idx) in sorted) 220 | action.Writer.WriteStruct(new Ordinal { Value = idx }); 221 | { 222 | var strscache = new List(); 223 | var baseoff = (uint) file.Position; 224 | var baserva = VirtualEnd + expdirlength + dllnamelength + functionslength + ordinalslength; 225 | foreach (var (name, _) in sorted) { 226 | strscache.Add((uint) file.Position - baseoff + baserva); 227 | if (verbose) 228 | Console.WriteLine("{0:X8} -> {1:X8}", file.Position, file.Position - baseoff + baserva); 229 | action.Writer.WriteByteString(name); 230 | } 231 | 232 | foreach (var add in strscache) 233 | action.Writer.WriteStruct(new RVA { Value = add }); 234 | 235 | Console.WriteLine("VirtualEnd: {0} {0:X8}", VirtualEnd); 236 | Console.WriteLine("OriginalSize: {0} {0:X8}", OriginalSize); 237 | Console.WriteLine("RealAppend: {0} {0:X8}", file.Position - OriginalSize); 238 | } 239 | } 240 | } 241 | } -------------------------------------------------------------------------------- /EatPdb/SymbolDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Linq; 6 | 7 | namespace EatPdb { 8 | 9 | internal class SymbolDatabase : IEnumerable>> { 10 | private readonly SortedDictionary> fullmap = new SortedDictionary>(); 11 | private readonly SortedDictionary revmap = new SortedDictionary(StringComparer.Ordinal); 12 | 13 | public void Add(uint RVA, string name) { 14 | if (fullmap.TryGetValue(RVA, out var set)) 15 | set.Add(name); 16 | else 17 | fullmap.Add(RVA, new SortedSet { name }); 18 | revmap.Add(name, RVA); 19 | } 20 | 21 | public bool RemoveName(string name) { 22 | if (revmap.TryGetValue(name, out var RVA)) { 23 | fullmap.Remove(RVA); 24 | revmap.Remove(name); 25 | return true; 26 | } 27 | return false; 28 | } 29 | 30 | public IEnumerator>> GetEnumerator() => fullmap.GetEnumerator(); 31 | 32 | IEnumerator IEnumerable.GetEnumerator() => fullmap.GetEnumerator(); 33 | 34 | public KeyValuePair[] Build() { 35 | var tempid = new Dictionary(); 36 | ushort idx = 0; 37 | foreach (var (key, _) in fullmap) 38 | tempid.Add(key, idx++); 39 | var ret = new SortedDictionary(StringComparer.Ordinal); 40 | foreach (var (name, rva) in revmap) { 41 | if (tempid.TryGetValue(rva, out var id)) { 42 | ret.Add(name, id); 43 | } else { 44 | throw new IndexOutOfRangeException(); 45 | } 46 | } 47 | return ret.ToArray(); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /PEReader/PEReader.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | dumppe 5 | Exe 6 | netcoreapp3.1 7 | 0.0.1 8 | CodeHz 9 | EatPdb 10 | CodeHz 11 | MIT 12 | https://github.com/codehz/EatPdb/ 13 | git 14 | English (United States) 15 | 16 | 17 | 18 | false 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /PEReader/Program.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using PESupport; 3 | using System; 4 | using System.IO; 5 | 6 | namespace PEReader { 7 | 8 | internal class Program { 9 | 10 | public class Options { 11 | 12 | [Option('i', "Input", Required = true, HelpText = "Input File")] 13 | public string InputFile { get; set; } 14 | } 15 | 16 | private static void Main(string[] args) => Parser.Default.ParseArguments(args) 17 | .WithParsed(RealMain); 18 | 19 | private static readonly string[] DataDirName = new string[]{ 20 | "Export", 21 | "Import", 22 | "Resource", 23 | "Exception", 24 | "Security", 25 | "BaseRelocationTable", 26 | "DebugDirectory", 27 | "CopyrightOrArchitectureSpecificData", 28 | "GlobalPtr", 29 | "TLSDirectory", 30 | "LoadConfigurationDirectory", 31 | "BoundImportDirectory", 32 | "ImportAddressTable", 33 | "DelayLoadImportDescriptors", 34 | "COMRuntimedescriptor", 35 | "Reserved", 36 | }; 37 | 38 | private static void RealMain(Options options) { 39 | try { 40 | using var file = File.OpenRead(options.InputFile); 41 | using var reader = new BinaryReader(file); 42 | var header = reader.ReadStruct(); 43 | var ntheader_address = header.AddressOfNewExeHeader; 44 | file.Seek(ntheader_address, SeekOrigin.Begin); 45 | var ntheader = reader.ReadStruct(); 46 | if (ntheader.Signature != 0x4550) { 47 | Console.WriteLine("Failed to parse nt header"); 48 | return; 49 | } 50 | if (ntheader.FileHeader.Machine != 0x8664) { 51 | Console.WriteLine("Failed to parse nt header: only accept AMD64 architect: {0:X}", ntheader.FileHeader.Machine); 52 | return; 53 | } 54 | if (ntheader.OptionalHeader.Magic != 0x20B) { 55 | Console.WriteLine("Failed to parse nt optional header: only accept AMD64 architect: magic {0:X}", ntheader.OptionalHeader.Magic); 56 | return; 57 | } 58 | var imagesize = ntheader.OptionalHeader.SizeOfImage; 59 | Console.WriteLine("Image Size: {0:X}", imagesize); 60 | var dirs = new DataDir[16]; 61 | for (var i = 0; i < 16; i++) { 62 | var dir = reader.ReadStruct(); 63 | Console.WriteLine("Data dir: {0:X8} {1:X8} ({2})", dir.VirtualAddress, dir.Size, DataDirName[i]); 64 | dirs[i] = dir; 65 | } 66 | var resolver = new AddressResolver(ntheader.FileHeader.NumberOfSections); 67 | for (uint i = 0; i < ntheader.FileHeader.NumberOfSections; i++) { 68 | var secheader = reader.ReadStruct(); 69 | Console.WriteLine("{0, 8}\n\t{1:X8}(Virtual Size)\n\t{2:X8}(Virtual Address)\n\t{3:X8}(Raw Data Size)\n\t{4:X8}(Raw Data Offset)\n\t{5:X8}(Diff)\n\tBITMAP: {6:X8}", 70 | secheader.GetName(), 71 | secheader.Misc.VirtualSize, 72 | secheader.VirtualAddress, 73 | secheader.SizeOfRawData, 74 | secheader.PointerToRawData, 75 | secheader.VirtualAddress - secheader.PointerToRawData, 76 | secheader.Characteristics); 77 | resolver.Put(secheader); 78 | } 79 | // Export table 80 | if (dirs[0].VirtualAddress != 0) { 81 | file.SeekRVA(resolver, dirs[0].VirtualAddress); 82 | var exp = reader.ReadStruct(); 83 | file.SeekRVA(resolver, exp.Name); 84 | Console.WriteLine("Dll Name: {0}", reader.ReadByteString()); 85 | file.SeekRVA(resolver, exp.AddressOfFunctions); 86 | var addrs = new uint[exp.NumberOfFunctions]; 87 | for (uint i = 0; i < exp.NumberOfFunctions; i++) 88 | addrs[i] = reader.ReadStruct().Value; 89 | file.SeekRVA(resolver, exp.AddressOfOrdinals); 90 | var ords = new ushort[exp.NumberOfNames]; 91 | for (uint i = 0; i < exp.NumberOfNames; i++) 92 | ords[i] = reader.ReadStruct().Value; 93 | var names = new string[exp.NumberOfNames]; 94 | for (uint i = 0; i < exp.NumberOfNames; i++) { 95 | file.SeekRVA(resolver, exp.AddressOfNames + i * sizeof(uint)); 96 | file.SeekRVA(resolver, reader.ReadStruct().Value); 97 | names[i] = reader.ReadByteString(); 98 | Console.WriteLine("export: {2:X8} <- {1:X4}:{0}", names[i], ords[i], addrs[ords[i]]); 99 | } 100 | } 101 | // Import table 102 | if (dirs[1].VirtualAddress != 0) { 103 | var pos = resolver.Resolve(dirs[1].VirtualAddress); 104 | for (uint i = 0; ; i++) { 105 | file.Seek(pos, SeekOrigin.Begin); 106 | var imp = reader.ReadStruct(); 107 | pos = (uint) file.Position; 108 | if (imp.Name == 0) 109 | break; 110 | file.SeekRVA(resolver, imp.Name); 111 | Console.WriteLine("import from {0}", reader.ReadByteString()); 112 | var pos2 = resolver.Resolve(imp.FirstThunk); 113 | for (uint j = 0; ; j++) { 114 | file.Seek(pos2, SeekOrigin.Begin); 115 | var thunk = reader.ReadStruct(); 116 | pos2 = (uint) file.Position; 117 | if (thunk.IsEmpty()) 118 | break; 119 | if (thunk.TryGetOrdinal(out var ord)) { 120 | Console.WriteLine("#{0}", ord); 121 | } else { 122 | file.SeekRVA(resolver, (uint) thunk.Value); 123 | var hint = reader.ReadStruct().Hint; 124 | Console.WriteLine("\t{0:X4}:{1}", hint, reader.ReadByteString()); 125 | } 126 | } 127 | } 128 | } 129 | } catch (Exception e) { 130 | Console.WriteLine(e.ToString()); 131 | } 132 | } 133 | } 134 | } -------------------------------------------------------------------------------- /PESupport/AddressResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace PESupport { 5 | 6 | public class AddressResolver { 7 | private readonly List Sections; 8 | 9 | [Serializable] 10 | private class AddressNotFoundException : Exception { 11 | 12 | public AddressNotFoundException(uint RVA) : base(string.Format("RVA {0} not mapped in this PE file", RVA)) { 13 | } 14 | } 15 | 16 | public AddressResolver(uint number) => Sections = new List(); 17 | 18 | public void Put(SectionHeader header) => Sections.Add(header); 19 | 20 | public SectionHeader GetSection(uint RVA) { 21 | foreach (var secheader in Sections) { 22 | if (secheader.VirtualAddress <= RVA && secheader.VirtualAddress + secheader.Misc.VirtualSize > RVA) { 23 | return secheader; 24 | } 25 | } 26 | throw new AddressNotFoundException(RVA); 27 | } 28 | 29 | public uint Resolve(uint RVA) { 30 | foreach (var secheader in Sections) { 31 | if (secheader.VirtualAddress <= RVA && secheader.VirtualAddress + secheader.Misc.VirtualSize > RVA) { 32 | return RVA - secheader.VirtualAddress + secheader.PointerToRawData; 33 | } 34 | } 35 | throw new AddressNotFoundException(RVA); 36 | } 37 | 38 | public uint RevResolve(uint FOA) { 39 | foreach (var secheader in Sections) { 40 | if (secheader.PointerToRawData <= FOA && secheader.Misc.VirtualSize > FOA) { 41 | return FOA - secheader.PointerToRawData + secheader.VirtualAddress; 42 | } 43 | } 44 | throw new AddressNotFoundException(FOA); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /PESupport/BinaryEx.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | 4 | namespace PESupport { 5 | 6 | public static class BinaryEx { 7 | 8 | public static string ReadByteString(this BinaryReader reader) { 9 | var builder = new StringBuilder(); 10 | while (true) { 11 | var ch = reader.ReadByte(); 12 | if (ch == 0) 13 | break; 14 | builder.Append((char) ch); 15 | } 16 | return builder.ToString(); 17 | } 18 | 19 | public static void WriteByteString(this BinaryWriter writer, string data) { 20 | var bytes = Encoding.ASCII.GetBytes(data); 21 | writer.Write(bytes); 22 | writer.Write((byte) 0); 23 | } 24 | 25 | public static T ReadStruct(this BinaryReader reader) where T : unmanaged => StructOP.Read(reader); 26 | 27 | public static void WriteStruct(this BinaryWriter reader, T input) where T : unmanaged => StructOP.Write(input, reader); 28 | } 29 | } -------------------------------------------------------------------------------- /PESupport/FileEx.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace PESupport { 4 | 5 | public static class FileEx { 6 | 7 | public static void SeekRVA(this FileStream file, AddressResolver resolver, uint RVA) => file.Seek(resolver.Resolve(RVA), SeekOrigin.Begin); 8 | } 9 | } -------------------------------------------------------------------------------- /PESupport/PESupport.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace PESupport { 6 | 7 | [StructLayout(LayoutKind.Sequential)] 8 | public unsafe struct DosHeader { 9 | public fixed byte MZSignature[2]; 10 | public ushort UsedBytesInTheLastPage; 11 | public ushort FileSizeInPages; 12 | public ushort NumberOfRelocationItems; 13 | public ushort HeaderSizeInParagraphs; 14 | public ushort MinimumExtraParagraphs; 15 | public ushort MaximumExtraParagraphs; 16 | public ushort InitialRelativeSS; 17 | public ushort InitialSP; 18 | public ushort Checksum; 19 | public ushort InitialIP; 20 | public ushort InitialRelativeCS; 21 | public ushort AddressOfRelocationTable; 22 | public ushort OverlayNumber; 23 | public fixed ushort Reserved[4]; 24 | public ushort OEMid; 25 | public ushort OEMinfo; 26 | public fixed ushort Reserved2[10]; 27 | public uint AddressOfNewExeHeader; 28 | } 29 | 30 | [StructLayout(LayoutKind.Sequential)] 31 | public struct NtHeader { 32 | public uint Signature; 33 | public FileHeader FileHeader; 34 | public OptionalHeader OptionalHeader; 35 | 36 | public bool CheckHealth() => Signature == 0x4550 && FileHeader.Machine == 0x8664 && OptionalHeader.Magic == 0x20B; 37 | 38 | public void AssertHealth() { 39 | if (!CheckHealth()) 40 | throw new NotSupportedException("x86_64 PE file expected"); 41 | } 42 | } 43 | 44 | [StructLayout(LayoutKind.Sequential)] 45 | public struct FileHeader { 46 | public ushort Machine; 47 | public ushort NumberOfSections; 48 | public uint TimeDateStamp; 49 | public uint PointerToSymbolTable; 50 | public uint NumberOfSymbols; 51 | public ushort SizeOfOptionalHeaders; 52 | public ushort Characteristics; 53 | } 54 | 55 | [StructLayout(LayoutKind.Sequential)] 56 | public struct OptionalHeader { 57 | public ushort Magic; 58 | public byte MajorLinkerVersion; 59 | public byte MinorLinkerVersion; 60 | public uint SizeOfCode; 61 | public uint SizeOfInitializedData; 62 | public uint SizeOfUninitializedData; 63 | public uint AddressOfEntryPoint; 64 | public uint BaseOfCode; 65 | public ulong ImageBase; 66 | public uint SectionAligment; 67 | public uint FileAligment; 68 | public ushort MajorOperationSystemVersion; 69 | public ushort MinorOperationSystemVersion; 70 | public ushort MajorImageVersion; 71 | public ushort MinorImageVersion; 72 | public ushort MajorSubsystemVersion; 73 | public ushort MinorSubsystemVersion; 74 | public uint Win32VersionValue; 75 | public uint SizeOfImage; 76 | public uint SizeOfHeaders; 77 | public uint CheckSum; 78 | public ushort Subsystem; 79 | public ushort DllCharacteristics; 80 | public ulong SizeOfStackReserve; 81 | public ulong SizeOfStackCommit; 82 | public ulong SizeOfHeapReserve; 83 | public ulong SizeOfHeapCommit; 84 | public uint LoaderFlags; 85 | public uint NumberOfRvaAndSizes; 86 | } 87 | 88 | [StructLayout(LayoutKind.Sequential)] 89 | public struct DataDir { 90 | public uint VirtualAddress; 91 | public uint Size; 92 | } 93 | 94 | [StructLayout(LayoutKind.Sequential)] 95 | public unsafe struct SectionHeader { 96 | public fixed byte Name[8]; 97 | public SectionHeaderMisc Misc; 98 | public uint VirtualAddress; 99 | public uint SizeOfRawData; 100 | public uint PointerToRawData; 101 | public uint PointerToRelocations; 102 | public uint PointerToLinenumbers; 103 | public ushort NumberOfRelocations; 104 | public ushort NumberOfLinenumbers; 105 | public uint Characteristics; 106 | 107 | public unsafe string GetName() { 108 | fixed (byte* name = Name) { 109 | return Marshal.PtrToStringAnsi((IntPtr) name, 8); 110 | } 111 | } 112 | 113 | public unsafe void SetName(string name) { 114 | var len = name.Length; 115 | if (len >= 8) 116 | throw new IndexOutOfRangeException(); 117 | var data = Encoding.ASCII.GetBytes(name); 118 | for (uint i = 0; i < 8; i++) { 119 | if (i < len) { 120 | Name[i] = data[i]; 121 | } else { 122 | Name[i] = 0; 123 | } 124 | } 125 | } 126 | } 127 | 128 | [StructLayout(LayoutKind.Explicit)] 129 | public unsafe struct SectionHeaderMisc { 130 | 131 | [FieldOffset(0)] 132 | public uint PhysicalAddress; 133 | 134 | [FieldOffset(0)] 135 | public uint VirtualSize; 136 | } 137 | 138 | [StructLayout(LayoutKind.Sequential)] 139 | public struct ExportDir { 140 | public uint Characteristics; 141 | public uint TimeDateStamp; 142 | public ushort MajorVersion; 143 | public ushort MinorVersion; 144 | public uint Name; // RVA 145 | public uint Base; 146 | public uint NumberOfFunctions; 147 | public uint NumberOfNames; 148 | public uint AddressOfFunctions; // RVA 149 | public uint AddressOfNames; // RVA 150 | public uint AddressOfOrdinals; // RVA 151 | } 152 | 153 | [StructLayout(LayoutKind.Sequential)] 154 | public struct Ordinal { 155 | public ushort Value; 156 | } 157 | 158 | [StructLayout(LayoutKind.Sequential)] 159 | public struct RVA { 160 | public uint Value; 161 | } 162 | 163 | [StructLayout(LayoutKind.Sequential)] 164 | public struct ImportDir { 165 | public uint OriginalFirstThunk; // RVA 166 | public uint TimeDateStamp; 167 | public uint ForwardChain; 168 | public uint Name; // RVA 169 | public uint FirstThunk; // RVA 170 | } 171 | 172 | [StructLayout(LayoutKind.Sequential)] 173 | public struct ImportDirThunk { 174 | public ulong Value; 175 | 176 | public bool IsEmpty() => Value == 0; 177 | 178 | public bool TryGetOrdinal(out uint ordinal) { 179 | if ((Value & 0x8000000000000000) != 0) { 180 | ordinal = (uint) Value; 181 | return true; 182 | } 183 | ordinal = 0; 184 | return false; 185 | } 186 | } 187 | 188 | [StructLayout(LayoutKind.Sequential)] 189 | public struct ImportDirThunkHint { 190 | public ushort Hint; 191 | } 192 | } -------------------------------------------------------------------------------- /PESupport/PESupport.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pe_support 5 | netstandard2.0 6 | EatPdb 7 | CodeHz 8 | CodeHz 9 | 0.0.1 10 | CodeHz 11 | MIT 12 | https://github.com/codehz/EatPdb/ 13 | https://github.com/codehz/EatPdb/ 14 | git 15 | en-US 16 | 17 | 18 | 19 | true 20 | 21 | 22 | 23 | true 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /PESupport/StructOP.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace PESupport { 5 | 6 | internal static class StructOP where T : unmanaged { 7 | private static readonly int Size = Marshal.SizeOf(); 8 | 9 | public static unsafe T Read(BinaryReader reader) { 10 | T val; 11 | var ptr = (byte*) &val; 12 | for (uint i = 0; i < Size; i++) 13 | *ptr++ = reader.ReadByte(); 14 | return val; 15 | } 16 | 17 | public static unsafe void Write(T input, BinaryWriter writer) { 18 | var ptr = (byte*) &input; 19 | for (uint i = 0; i < Size; i++) 20 | writer.Write(*ptr++); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /PdbReader/PdbReader.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | dumppdb 5 | Exe 6 | netcoreapp3.1 7 | English (United States) 8 | 0.0.2 9 | CodeHz 10 | EatPdb 11 | CodeHz 12 | MIT 13 | https://github.com/codehz/EatPdb/ 14 | https://github.com/codehz/EatPdb/ 15 | git 16 | 17 | 18 | 19 | false 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /PdbReader/Program.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using SharpPdb.Native; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Linq; 7 | 8 | namespace PdbReader { 9 | 10 | internal class Program { 11 | 12 | public class Options { 13 | 14 | [Option('v', "Verbose", Default = false, HelpText = "Verbose output")] 15 | public bool Verbose { get; set; } 16 | 17 | [Option('d', "Demangle", Default = false, HelpText = "Demangle function name")] 18 | public bool Demangle { get; set; } 19 | 20 | [Option('i', "Input", Required = true, HelpText = "Input File")] 21 | public string InputFile { get; set; } 22 | } 23 | 24 | private static void Main(string[] args) => Parser.Default.ParseArguments(args) 25 | .WithParsed(RealMain); 26 | 27 | private class SymbolEqualityComparer : IEqualityComparer { 28 | 29 | public bool Equals([AllowNull] PdbPublicSymbol x, [AllowNull] PdbPublicSymbol y) => x.RelativeVirtualAddress == y.RelativeVirtualAddress; 30 | 31 | public int GetHashCode([DisallowNull] PdbPublicSymbol obj) => obj.RelativeVirtualAddress.GetHashCode(); 32 | } 33 | 34 | private static void RealMain(Options options) { 35 | try { 36 | using var reader = new PdbFileReader(options.InputFile); 37 | if (options.Verbose) 38 | foreach (var item in from item in reader.PublicSymbols where !item.Name.StartsWith("_") orderby item.RelativeVirtualAddress select item) 39 | Console.WriteLine("{0}:{1:X8} {2}", item.Segment, item.RelativeVirtualAddress, options.Demangle ? item.GetUndecoratedName() : item.Name); 40 | else { 41 | var syms = from item in reader.PublicSymbols 42 | where !item.Name.StartsWith("_") 43 | select item; 44 | Console.WriteLine("Symbol FullCount: {0}", syms.Count()); 45 | Console.WriteLine("Symbol DistinctCount: {0}", syms.Distinct(new SymbolEqualityComparer()).Count()); 46 | 47 | var functions = reader.Functions; 48 | Console.WriteLine("Functions FullCount: {0}", functions.Count()); 49 | } 50 | } catch (Exception e) { 51 | Console.WriteLine(e.ToString()); 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EatPdb 2 | 3 | [![.NET Core](https://github.com/codehz/EatPdb/workflows/.NET%20Core/badge.svg?branch=master)](https://github.com/codehz/EatPdb/actions/) 4 | 5 | Export all symbols of PE file in pdb file, so you can easily import them. 6 | 7 | Only support x86_64 for now. 8 | 9 | ## Usage 10 | 11 | You need a config file to describe the filter 12 | 13 | ```bash 14 | eatpdb exec config.yaml 15 | ``` 16 | 17 | config.yaml: 18 | 19 | ```yaml 20 | in: your.exe 21 | out: your_mod.exe 22 | filterdb: extra.db 23 | filter: !blacklist 24 | - prefix: "_" 25 | - prefix: "?__" 26 | - prefix: "??_" 27 | - prefix: "??@" 28 | - prefix: "?$TSS" 29 | - regex: "std@@[QU]" 30 | - name: "atexit" 31 | ``` 32 | 33 | ## LICENSE 34 | 35 | MIT 36 | -------------------------------------------------------------------------------- /omnisharp.json: -------------------------------------------------------------------------------- 1 | { 2 | "FormattingOptions": { 3 | "IndentationSize": 4, 4 | "NewLinesForBracesInLambdaExpressionBody": false, 5 | "NewLinesForBracesInAnonymousMethods": false, 6 | "NewLinesForBracesInAnonymousTypes": false, 7 | "NewLinesForBracesInControlBlocks": false, 8 | "NewLinesForBracesInTypes": false, 9 | "NewLinesForBracesInMethods": false, 10 | "NewLinesForBracesInProperties": false, 11 | "NewLinesForBracesInAccessors": false, 12 | "NewLineForElse": false, 13 | "NewLineForCatch": false, 14 | "NewLineForFinally": false, 15 | "SpaceAfterCast": true, 16 | "SpaceBetweenEmptySquareBrackets": false 17 | } 18 | } 19 | --------------------------------------------------------------------------------