Skip to content

Commit 0939c9f

Browse files
committed
Mfuscator plugin: Fix offset/size as key detection
1 parent 19fa6ef commit 0939c9f

File tree

1 file changed

+127
-119
lines changed

1 file changed

+127
-119
lines changed

Cpp2IL.Plugin.Mfuscator/MfuscatorSupportPlugin.cs

Lines changed: 127 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -295,138 +295,143 @@ private Dictionary<int, byte[]> DecryptEncryptedSections(byte[] encryptedMetadat
295295
var stringLiteralsStart = sections[StringLiteralsSectionIndex].Start;
296296
var stringLiteralsSize = sections[StringLiteralsSectionIndex].End - sections[StringLiteralsSectionIndex].Start;
297297

298-
var usingOffsetNotSize = false;
299-
byte sectionsXorKeyAddend = 0;
300-
301-
foreach (var testUsingOffset in stackalloc bool[] { true, false })
298+
foreach (var usingOffsetNotSize in stackalloc bool[] { true, false })
302299
{
303-
var stringLiteralsKeyComponent = testUsingOffset ? stringLiteralsStart : stringLiteralsSize;
304-
var testAddend = (byte)((stringLiteralsIsPlus ? (stringLiteralsXorKey - stringLiteralsKeyComponent) : (stringLiteralsXorKey + stringLiteralsKeyComponent)) & 0xFF);
305-
306-
//Now decrypt the string literals section
307-
var decryptedLiterals = new byte[stringLiteralsSize];
308-
CyclicXor(
309-
encryptedMetadata.AsSpan(sections[StringLiteralsSectionIndex].Start, stringLiteralsSize),
310-
decryptedLiterals,
311-
testAddend,
312-
stringLiteralsIsPlus,
313-
stringLiteralsStart
314-
);
315-
316-
if(decryptedLiterals[0] == 0 && decryptedLiterals[1] == 0)
300+
try
317301
{
318-
usingOffsetNotSize = testUsingOffset;
319-
sectionsXorKeyAddend = testAddend;
320-
decryptedSectionBytes[StringLiteralsSectionIndex] = decryptedLiterals;
321-
break;
322-
}
323-
}
324-
325-
if(!decryptedSectionBytes.ContainsKey(StringLiteralsSectionIndex))
326-
throw new Exception("Failed to determine whether section keys are based on offsets or sizes");
327-
328-
Logger.VerboseNewline($"Section keys are based on {(usingOffsetNotSize ? "offsets" : "sizes")}, with addend 0x{sectionsXorKeyAddend:X2}");
302+
byte sectionsXorKeyAddend = 0;
303+
var stringLiteralsKeyComponent = usingOffsetNotSize ? stringLiteralsStart : stringLiteralsSize;
304+
var testAddend = (byte)((stringLiteralsIsPlus ? (stringLiteralsXorKey - stringLiteralsKeyComponent) : (stringLiteralsXorKey + stringLiteralsKeyComponent)) & 0xFF);
329305

330-
//String literal data starts with 2 00 bytes, so we can get the direction from that
331-
var stringLiteralDataStart = sections[StringLiteralsDataSectionIndex].Start;
332-
var stringLiteralDataSize = sections[StringLiteralsDataSectionIndex].End - sections[StringLiteralsDataSectionIndex].Start;
333-
var stringLiteralDataKeyComponent = usingOffsetNotSize ? stringLiteralDataStart : stringLiteralDataSize;
334-
335-
var firstByte = encryptedMetadata[stringLiteralDataStart];
336-
var secondByte = encryptedMetadata[stringLiteralDataStart + 1];
337-
var stringLiteralDataIsPlus = ((firstByte + 1) & 0xFF) == secondByte;
338-
var stringLiteralDataIsMinus = ((firstByte - 1) & 0xFF) == secondByte;
339-
if(!stringLiteralDataIsPlus && !stringLiteralDataIsMinus)
340-
throw new Exception("Failed to determine string literal data XOR direction");
341-
342-
//And decrypt it
343-
var decryptedLiteralData = decryptedSectionBytes[StringLiteralsDataSectionIndex] = new byte[stringLiteralDataSize];
344-
CyclicXor(
345-
encryptedMetadata.AsSpan(stringLiteralDataStart, stringLiteralDataSize),
346-
decryptedLiteralData,
347-
sectionsXorKeyAddend,
348-
stringLiteralDataIsPlus,
349-
stringLiteralDataKeyComponent
350-
);
351-
352-
//Strings are a bit harder, we need to look for the null terminators in the first 32 bytes
353-
var stringsSectionStart = sections[StringsSectionIndex].Start;
354-
var stringsSectionSize = sections[StringsSectionIndex].End - sections[StringsSectionIndex].Start;
355-
var stringsSectionKeyComponent = usingOffsetNotSize ? stringsSectionStart : stringsSectionSize;
356-
var stringsFirstXorByteOffset = 0;
357-
var stringsIsPlus = false;
358-
var foundZeroBytes = 0;
359-
foreach (var testIsPlus in new bool[] { true, false })
360-
{
361-
stringsIsPlus = testIsPlus;
362-
for (var i = 0; i < 32; i++)
363-
{
364-
var assumedXorKey = (byte) ((testIsPlus
365-
? (i + stringsSectionKeyComponent + sectionsXorKeyAddend)
366-
: (i - stringsSectionKeyComponent - sectionsXorKeyAddend)) & 0xFF);
367-
var xorByte = (byte) (encryptedMetadata[stringsSectionStart + i] ^ assumedXorKey);
368-
if (xorByte == 0)
306+
//Now decrypt the string literals section
307+
var decryptedLiterals = new byte[stringLiteralsSize];
308+
CyclicXor(
309+
encryptedMetadata.AsSpan(sections[StringLiteralsSectionIndex].Start, stringLiteralsSize),
310+
decryptedLiterals,
311+
testAddend,
312+
stringLiteralsIsPlus,
313+
stringLiteralsKeyComponent
314+
);
315+
316+
if (decryptedLiterals[0] == 0 && decryptedLiterals[1] == 0)
369317
{
370-
foundZeroBytes++;
371-
if(foundZeroBytes == 1)
372-
stringsFirstXorByteOffset = i;
373-
else if(foundZeroBytes == 2)
374-
break; //we've found the first two null terminators, which is enough to be confident we've got the right key direction
318+
sectionsXorKeyAddend = testAddend;
319+
decryptedSectionBytes[StringLiteralsSectionIndex] = decryptedLiterals;
375320
}
376-
}
377-
}
378-
379-
if(foundZeroBytes != 2)
380-
throw new Exception("Failed to determine strings section XOR direction");
381-
382-
//sanity check
383-
var stringsXorByte = (byte) ((stringsIsPlus
384-
? (stringsFirstXorByteOffset + stringsSectionKeyComponent + sectionsXorKeyAddend)
385-
: (stringsFirstXorByteOffset - stringsSectionKeyComponent - sectionsXorKeyAddend)) & 0xFF);
386-
387-
if(encryptedMetadata[stringsSectionStart + stringsFirstXorByteOffset] != stringsXorByte)
388-
throw new Exception("Strings section XOR key doesn't seem to be correct");
389-
390-
//ok now decrypt strings
391-
var decryptedStrings = decryptedSectionBytes[StringsSectionIndex] = new byte[stringsSectionSize];
392-
CyclicXor(
393-
encryptedMetadata.AsSpan(stringsSectionStart, stringsSectionSize),
394-
decryptedStrings,
395-
sectionsXorKeyAddend,
396-
stringsIsPlus,
397-
stringsSectionKeyComponent
398-
);
399-
400-
//for the rest of the sections we can just check the 3rd byte is 0 to determine the direction
401-
var remainingEncryptedSections = new int[] { PropertiesSectionIndex, MethodsSectionIndex, FieldsSectionIndex, assembliesSectionIndex };
402-
foreach (var sectionIndex in remainingEncryptedSections)
403-
{
404-
var sectionStart = sections[sectionIndex].Start;
405-
var sectionSize = sections[sectionIndex].End - sections[sectionIndex].Start;
406-
var sectionKeyComponent = usingOffsetNotSize ? sectionStart : sectionSize;
321+
322+
if (!decryptedSectionBytes.ContainsKey(StringLiteralsSectionIndex))
323+
throw new Exception("Failed to determine whether section keys are based on offsets or sizes");
407324

408-
var decryptedSection = new byte[sectionSize];
409-
foreach (var testIsPlus in new bool[] { true, false })
410-
{
325+
Logger.VerboseNewline($"Section keys are based on {(usingOffsetNotSize ? "offsets" : "sizes")}, with addend 0x{sectionsXorKeyAddend:X2}");
326+
327+
//String literal data starts with 2 00 bytes, so we can get the direction from that
328+
var stringLiteralDataStart = sections[StringLiteralsDataSectionIndex].Start;
329+
var stringLiteralDataSize = sections[StringLiteralsDataSectionIndex].End - sections[StringLiteralsDataSectionIndex].Start;
330+
var stringLiteralDataKeyComponent = usingOffsetNotSize ? stringLiteralDataStart : stringLiteralDataSize;
331+
332+
var firstByte = encryptedMetadata[stringLiteralDataStart];
333+
var secondByte = encryptedMetadata[stringLiteralDataStart + 1];
334+
var stringLiteralDataIsPlus = ((firstByte + 1) & 0xFF) == secondByte;
335+
var stringLiteralDataIsMinus = ((firstByte - 1) & 0xFF) == secondByte;
336+
if (!stringLiteralDataIsPlus && !stringLiteralDataIsMinus)
337+
throw new Exception("Failed to determine string literal data XOR direction");
338+
339+
//And decrypt it
340+
var decryptedLiteralData = decryptedSectionBytes[StringLiteralsDataSectionIndex] = new byte[stringLiteralDataSize];
411341
CyclicXor(
412-
encryptedMetadata.AsSpan(sectionStart, sectionSize),
413-
decryptedSection,
342+
encryptedMetadata.AsSpan(stringLiteralDataStart, stringLiteralDataSize),
343+
decryptedLiteralData,
414344
sectionsXorKeyAddend,
415-
testIsPlus,
416-
sectionKeyComponent
345+
stringLiteralDataIsPlus,
346+
stringLiteralDataKeyComponent
417347
);
418-
if(decryptedSection[3] == 0)
348+
349+
//Strings are a bit harder, we need to look for the null terminators in the first 32 bytes
350+
var stringsSectionStart = sections[StringsSectionIndex].Start;
351+
var stringsSectionSize = sections[StringsSectionIndex].End - sections[StringsSectionIndex].Start;
352+
var stringsSectionKeyComponent = usingOffsetNotSize ? stringsSectionStart : stringsSectionSize;
353+
var stringsFirstXorByteOffset = 0;
354+
var stringsIsPlus = false;
355+
var foundZeroBytes = 0;
356+
foreach (var testIsPlus in new bool[] { true, false })
419357
{
420-
decryptedSectionBytes[sectionIndex] = decryptedSection;
421-
break;
358+
stringsIsPlus = testIsPlus;
359+
for (var i = 0; i < 32; i++)
360+
{
361+
var assumedXorKey = (byte)((testIsPlus
362+
? (i + stringsSectionKeyComponent + sectionsXorKeyAddend)
363+
: (i - stringsSectionKeyComponent - sectionsXorKeyAddend)) & 0xFF);
364+
var xorByte = (byte)(encryptedMetadata[stringsSectionStart + i] ^ assumedXorKey);
365+
if (xorByte == 0)
366+
{
367+
foundZeroBytes++;
368+
if (foundZeroBytes == 1)
369+
stringsFirstXorByteOffset = i;
370+
else if (foundZeroBytes == 2)
371+
break; //we've found the first two null terminators, which is enough to be confident we've got the right key direction
372+
}
373+
}
374+
}
375+
376+
if (foundZeroBytes != 2)
377+
throw new Exception("Failed to determine strings section XOR direction");
378+
379+
//sanity check
380+
var stringsXorByte = (byte)((stringsIsPlus
381+
? (stringsFirstXorByteOffset + stringsSectionKeyComponent + sectionsXorKeyAddend)
382+
: (stringsFirstXorByteOffset - stringsSectionKeyComponent - sectionsXorKeyAddend)) & 0xFF);
383+
384+
if (encryptedMetadata[stringsSectionStart + stringsFirstXorByteOffset] != stringsXorByte)
385+
throw new Exception("Strings section XOR key doesn't seem to be correct");
386+
387+
//ok now decrypt strings
388+
var decryptedStrings = decryptedSectionBytes[StringsSectionIndex] = new byte[stringsSectionSize];
389+
CyclicXor(
390+
encryptedMetadata.AsSpan(stringsSectionStart, stringsSectionSize),
391+
decryptedStrings,
392+
sectionsXorKeyAddend,
393+
stringsIsPlus,
394+
stringsSectionKeyComponent
395+
);
396+
397+
//for the rest of the sections we can just check the 3rd byte is 0 to determine the direction
398+
var remainingEncryptedSections = new int[] { PropertiesSectionIndex, MethodsSectionIndex, FieldsSectionIndex, assembliesSectionIndex };
399+
foreach (var sectionIndex in remainingEncryptedSections)
400+
{
401+
var sectionStart = sections[sectionIndex].Start;
402+
var sectionSize = sections[sectionIndex].End - sections[sectionIndex].Start;
403+
var sectionKeyComponent = usingOffsetNotSize ? sectionStart : sectionSize;
404+
405+
var decryptedSection = new byte[sectionSize];
406+
foreach (var testIsPlus in new bool[] { true, false })
407+
{
408+
CyclicXor(
409+
encryptedMetadata.AsSpan(sectionStart, sectionSize),
410+
decryptedSection,
411+
sectionsXorKeyAddend,
412+
testIsPlus,
413+
sectionKeyComponent
414+
);
415+
if (decryptedSection[3] == 0)
416+
{
417+
decryptedSectionBytes[sectionIndex] = decryptedSection;
418+
break;
419+
}
420+
}
421+
422+
if (!decryptedSectionBytes.ContainsKey(sectionIndex))
423+
throw new Exception($"Failed to determine XOR direction for section at index {sectionIndex}");
422424
}
425+
426+
return decryptedSectionBytes;
427+
}
428+
catch (Exception ex)
429+
{
430+
continue;
423431
}
424-
425-
if (!decryptedSectionBytes.ContainsKey(sectionIndex))
426-
throw new Exception($"Failed to determine XOR direction for section at index {sectionIndex}");
427432
}
428-
429-
return decryptedSectionBytes;
433+
434+
throw new Exception("Failed to decrypt sections with either offset-based or size-based keys");
430435
}
431436

432437
private byte[] RebuildMetadata(byte[] encryptedMetadata, List<(int Start, int End)> sections, byte stringLiteralsXorKey, bool stringLiteralsIsPlus, int offsetDelta, byte metadataVersion, int assembliesSectionIndex)
@@ -528,6 +533,8 @@ private byte[] RebuildMetadata(byte[] encryptedMetadata, List<(int Start, int En
528533
Logger.InfoNewline($"Mfuscator header decrypted successfully. Header length: {headerLength} bytes. String literals XOR key: 0x{stringLiteralsXorKey:X2}. String literals use {(stringLiteralsIsPlus ? "plus" : "minus")} rotation. Will rebuild as version {MetadataVersion} metadata with assemblies section at index {assembliesSectionIndex}.");
529534

530535
Logger.VerboseNewline("Decrypted header: " + string.Join("", decryptedHeader.Select(b => b.ToString("X2"))));
536+
537+
metadataLength = 0x2356D0C;
531538

532539
while (metadataLength > headerLength)
533540
{
@@ -567,6 +574,7 @@ private byte[] RebuildMetadata(byte[] encryptedMetadata, List<(int Start, int En
567574
}
568575

569576
metadataLength -= 4;
577+
break;
570578
}
571579

572580
return null;

0 commit comments

Comments
 (0)