• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SamboyCoding / Cpp2IL / 15172218253

21 May 2025 08:44PM UTC coverage: 34.282% (+0.2%) from 34.047%
15172218253

Pull #462

github

web-flow
Merge 9de1a7abb into 5807d2b6c
Pull Request #462: Support overriding member types

1801 of 6650 branches covered (27.08%)

Branch coverage included in aggregate %.

127 of 226 new or added lines in 35 files covered. (56.19%)

22 existing lines in 6 files now uncovered.

4199 of 10852 relevant lines covered (38.69%)

186271.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.79
/Cpp2IL.Core/ProcessingLayers/DeobfuscationMapProcessingLayer.cs
1
using System;
2
using System.IO;
3
using System.IO.Compression;
4
using System.Linq;
5
using System.Net.Http;
6
using System.Text;
7
using Cpp2IL.Core.Api;
8
using Cpp2IL.Core.Logging;
9
using Cpp2IL.Core.Model.Contexts;
10

11
namespace Cpp2IL.Core.ProcessingLayers;
12

13
public class DeobfuscationMapProcessingLayer : Cpp2IlProcessingLayer
14
{
15
    private static bool _logUnknownTypes;
16
    private static int _numUnknownTypes;
17

18
    public override string Name => "Deobfuscation Map";
×
19
    public override string Id => "deobfmap";
1✔
20

21
    public override void Process(ApplicationAnalysisContext appContext, Action<int, int>? progressCallback = null)
22
    {
23
        var mapPath = appContext.GetExtraData<string>("deobf-map-path");
×
24
        _logUnknownTypes = appContext.GetExtraData<string>("deobf-map-log-unknown-types") != null;
×
25

26
        if (mapPath == null)
×
27
        {
28
            Logger.WarnNewline("No deobfuscation map specified - processor will not run. You need to provide the deobf-map-path, either by programmatically adding it as extra data in the app context, or by specifying it in the --processor-config command line option.", "DeobfuscationMapProcessingLayer");
×
29
            return;
×
30
        }
31

32
        byte[] deobfMap;
33
        if (mapPath.StartsWith("http://") || mapPath.StartsWith("https://"))
×
34
        {
35
            try
36
            {
37
                Logger.InfoNewline($"Downloading deobfuscation map from {mapPath}...", "DeobfuscationMapProcessingLayer");
×
38
                //Blocking call, but it's fine. All of cpp2il is blocking.
39
                deobfMap = new HttpClient().GetByteArrayAsync(mapPath).Result;
×
40
            }
×
41
            catch (Exception e)
×
42
            {
43
                Logger.ErrorNewline($"Could not download remote deobfuscation map from {mapPath}: {e.Message}. Deobfuscation will not run.", "DeobfuscationMapProcessingLayer");
×
44
                return;
×
45
            }
46
        }
47
        else if (File.Exists(mapPath))
×
48
        {
49
            deobfMap = File.ReadAllBytes(mapPath);
×
50
        }
51
        else
52
        {
53
            Logger.ErrorNewline($"File not found: {Path.GetFullPath(mapPath)}. Deobfuscation will not run.", "DeobfuscationMapProcessingLayer");
×
54
            return;
×
55
        }
56

57
        Logger.InfoNewline("Parsing deobfuscation map...", "DeobfuscationMapProcessingLayer");
×
58

59
        string deobfMapContent;
60
        //Check if gzipped.
61
        if (deobfMap.Length > 2 && deobfMap[0] == 0x1F && deobfMap[1] == 0x8B)
×
62
        {
63
            using var ms = new MemoryStream(deobfMap);
×
64
            using var gzip = new GZipStream(ms, CompressionMode.Decompress);
×
65
            using var reader = new StreamReader(gzip);
×
66

67
            deobfMapContent = reader.ReadToEnd();
×
68
        }
69
        else
70
        {
71
            deobfMapContent = Encoding.UTF8.GetString(deobfMap);
×
72
        }
73

74
        Deobfuscate(appContext, deobfMapContent);
×
75
    }
×
76

77
    private static void Deobfuscate(ApplicationAnalysisContext appContext, string deobfMap)
78
    {
79
        var lines = deobfMap.Split('\n');
×
80
        _numUnknownTypes = 0;
×
81

82
        var typeLines = lines.Where(t => !t.Contains("::"));
×
83
        var memberLines = lines.Where(t => t.Contains("::"));
×
84

85
        Logger.InfoNewline($"Applying deobfuscation map ({lines.Length} entries)...", "DeobfuscationMapProcessingLayer");
×
86
        foreach (var line in typeLines)
×
87
            ProcessLine(appContext, line);
×
88

89
        foreach (var line in memberLines)
×
90
            ProcessLine(appContext, line);
×
91

92
        if (!_logUnknownTypes)
×
93
        {
94
            //Print a summary if we didn't log each individually.
95
            Logger.WarnNewline($"Encountered {_numUnknownTypes} unknown types in deobf map. Add the configuration option deobf-map-log-unknown-types=yes to log them.", "DeobfuscationMapProcessingLayer");
×
96
        }
97
    }
×
98

99
    private static void ProcessLine(ApplicationAnalysisContext appContext, string line)
100
    {
101
        //Obfuscated;deobfuscated[;priority]
102
        var split = line.Split(';');
×
103

104
        if (split.Length < 2)
×
105
            return;
×
106

107
        var (obfuscated, deobfuscated) = (split[0], split[1]);
×
108

109
        ProcessRemapping(appContext, obfuscated, deobfuscated);
×
110
    }
×
111

112
    private static void ProcessRemapping(ApplicationAnalysisContext appContext, string obfuscated, string deobfuscated)
113
    {
114
        if (obfuscated.Contains("::"))
×
115
        {
116
            var index = obfuscated.IndexOf("::", StringComparison.Ordinal);
×
117
            var typeName = obfuscated[..index];
×
118
            var memberName = obfuscated[(index + 2)..];
×
119

120
            var type = GetTypeByObfName(appContext, typeName);
×
121

122
            var member = type?.Fields.FirstOrDefault(f => f.Name == memberName);
×
123
            if (member == null)
×
124
            {
125
                //TODO Non-fields? Currently only used for enums
126
                return;
×
127
            }
128

129
            member.OverrideName = deobfuscated;
×
130
            return;
×
131
        }
132

133
        var matchingType = GetTypeByObfName(appContext, obfuscated);
×
134

135
        if (matchingType == null)
×
136
            return;
×
137

138
        //The way the rename maps work is something like this:
139
        //  If the obfuscated name was a nested type, the deobfuscated name is just the new name of the nested type
140
        //  If the obfuscated name was a top-level type, the deobfuscated name is the new namespace + . + new name
141

142
        // var originalName = matchingType.FullName;
143

144
        if (matchingType.DeclaringType != null)
×
145
        {
146
            matchingType.OverrideName = deobfuscated;
×
147
            // Logger.VerboseNewline($"Renamed nested type {originalName} to {matchingType.FullName}", "DeobfuscationMapProcessingLayer");
148
            return;
×
149
        }
150

151
        var lastDot = deobfuscated.LastIndexOf('.');
×
152

153
        if (lastDot != -1)
×
154
        {
155
            var namespaceName = deobfuscated[..lastDot];
×
156
            var typeName = deobfuscated[(lastDot + 1)..];
×
157

NEW
158
            matchingType.OverrideNamespace = namespaceName;
×
159
            matchingType.OverrideName = typeName;
×
160
        }
161
        else
162
        {
163
            matchingType.OverrideName = deobfuscated;
×
164
        }
165

166
        // Logger.VerboseNewline($"Renamed {originalName} to {matchingType.FullName}", "DeobfuscationMapProcessingLayer");
167
    }
×
168

169
    private static TypeAnalysisContext? GetTypeByObfName(ApplicationAnalysisContext appContext, string obfuscated)
170
    {
171
        if (obfuscated.StartsWith("."))
×
172
            //If there's no namespace, strip the leading dot
173
            obfuscated = obfuscated[1..];
×
174

175
        //Create another copy of obfuscated name with last . replaced with a /, for subclasses
176
        var lastDot = obfuscated.LastIndexOf('.');
×
177
        var withSlash = obfuscated;
×
178
        if (lastDot > 0)
×
179
            withSlash = obfuscated[..lastDot] + "/" + obfuscated[(lastDot + 1)..];
×
180

181
        //TODO Change this to a dict at some point and recalculate between type and member names.
182
        var matchingType = appContext.AllTypes.AsParallel().FirstOrDefault(t => t.FullName == obfuscated || t.FullName == withSlash);
×
183

184
        if (matchingType == null)
×
185
        {
186
            if (_logUnknownTypes)
×
187
                Logger.WarnNewline("Could not find type " + obfuscated, "DeobfuscationMapProcessingLayer");
×
188
            else
189
                _numUnknownTypes++;
×
190
            return null;
×
191
        }
192

193
        return matchingType;
×
194
    }
195
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc