Skip to content

PBO Deobfuscator

Matthew Barker edited this page Jun 14, 2026 · 1 revision

BIS.PBO.Deobfuscator

PBO obfuscation reversal with config-driven profile system and reference updaters. Recovers corrupted or obfuscated PBO files where standard tools fail.

Depends on: BIS.PBO, BIS.P3D

Background

Some PBO files use obfuscation techniques to hinder analysis:

  • Decoy injection — extra fake entries scattered through the archive
  • Cyrillic/garbage filenames — non-ASCII characters that crash naive parsers
  • Missing extensions — files with no extension that must be identified by header
  • Cross-file reference obfuscation — config.bin references pointing to Cyrillic names that were originally ASCII

The deobfuscator profiles detect these patterns structurally (not by tool signature), making them adaptable to new variants.

Key Types

PboDeobfuscator (in BIS.PBO.Deobfuscator)

Main entry point. Runs registered profiles against a PBO and merges results.

var deobf = new PboDeobfuscator();
var pbo = new PBO("obfuscated.pbo", keepStreamOpen: true);

// Analyse
var result = deobf.Process(pbo);
Console.WriteLine($"Matched profile: {result.MatchedProfile}");
Console.WriteLine($"Filtered decoys: {result.FilteredOut.Count}");
Console.WriteLine($"Recovered names: {result.RecoveredNames.Count}");

// Rebuild a clean PBO
deobf.Rebuild(pbo, result, "cleaned.pbo");
pbo.Dispose();

DeobfuscationResult

Holds the analysis results from all matched profiles.

Property Description
MatchedProfile Name of the profile(s) that matched the PBO
RecoveredNames Dictionary: entry index → recovered ASCII name
FilteredOut List of entry indices identified as decoys
Stats Per-profile diagnostic counters

Profiles (in BIS.PBO.Deobfuscator.Profiles)

Profile Description
DecoyInjectionProfile Detects and filters decoy/stub entries (small files, non-ASCII names, unusual size patterns)
ModularSuffixRecoveryProfile Handles complex Cyrillic-based renaming via modular detection modules
SuffixRecoveryProfile Recovers filenames from P3D model paths and known suffixes
HeuristicFallbackProfile Catch-all for structural anomalies (high small-file ratio, unusual naming density)

Interfaces

Interface Description
IObfuscationProfile Implement to add a custom detection/recovery profile
IDetectionModule A single detection heuristic used by multi-module profiles
IRecoveryModule A single recovery strategy for name reconstruction
IReferenceUpdater Updates cross-file references after renaming (e.g., config.bin → P3D paths)

Reference Updaters (in BIS.PBO.Deobfuscator)

Updater Description
P3DTextureReferenceUpdater Updates texture paths in P3D files to match recovered names
RVMATReferenceUpdater Updates material file references to match recovered names
ConfigReferenceUpdater Updates config.bin paths to match recovered names

Usage

Basic Analysis

var deobf = new PboDeobfuscator();
using var pbo = new PBO("suspicious.pbo", keepStreamOpen: true);

var result = deobf.Process(pbo);

if (result.MatchedProfile != null)
{
    Console.WriteLine($"Obfuscation detected: {result.MatchedProfile}");
    Console.WriteLine($"  Decoys removed: {result.FilteredOut.Count}");
    Console.WriteLine($"  Names recovered: {result.RecoveredNames.Count}");

    foreach (var kvp in result.RecoveredNames)
    {
        Console.WriteLine($"  [{kvp.Key}] -> {kvp.Value}");
    }
}

Full Recovery

var deobf = new PboDeobfuscator();
using var pbo = new PBO("obfuscated.pbo", keepStreamOpen: true);

var result = deobf.Process(pbo);
if (result.MatchedProfile != null)
{
    deobf.Rebuild(pbo, result, "recovered.pbo");
}

Custom Profile

public class MyCustomProfile : IObfuscationProfile
{
    public string ProfileName => "MyCustomDetector";

    public bool IsMatch(PBO pbo) { /* check signatures */ }

    public DeobfuscationResult Deobfuscate(PBO pbo) { /* custom logic */ }
}

var deobf = new PboDeobfuscator();
deobf.RegisterProfile(new MyCustomProfile());
deobf.RegisterReferenceUpdater(new MyCustomUpdater());

How Rebuilding Works

  1. Non-decoy entries are kept and their data preserved byte-for-byte.
  2. Decoy entries (in FilteredOut) and zero-byte entries are excluded.
  3. Recovered names are applied from RecoveredNames (from profile heuristics) or generated via class-name analysis of config.bin.
  4. Reference updaters scan kept entries (P3D files, config.bin, RVMATs) and rewrite internal paths to match the new filenames.
  5. A clean PBO is written with only essential properties (prefix, product) and a fresh SHA1 hash.

Test Data

PBO deobfuscator tests are extensive (191 unit tests) and use sample obfuscated PBOs. Test data is generated programmatically or sourced from _testdata/pbo/.

Clone this wiki locally