deobfuscating most of the menus in gorilla tag comms (kinda)

February 16, 2026

in this post I will explain how I broke ArmDot’s protections (partially) and deobfuscated most of the menus in Gorilla Tag.

this only works with ArmDot obfuscator, doesn’t work with anything else! also this deobfuscator is partial, it won’t give you the original code for the menu.

I. ArmDot?

so in this part I will explain what ArmDot is, and why is it so famous in the gorilla tag comms.

ArmDot is an obfuscator for .NET apps that works perfectly when you’re trying to obfuscate Unity-based modules like BepinEx mods, that’s why most of the menu owners use armdot, here is an example of an obfuscated method with ArmDot:

static Main()
		{
			Main.InProcessHandlergetNow();
			int num = 26;
			int num2 = 26;
			num2 = 26;
			while (num2 != 0)
			{
				int num3;
				calli(System.Void(System.Int32&,System.Int32&,System.Int32&), ref num, ref num2, ref num3, Main.AddEventHandlerLMShare[num]);
			}
			num2 = 26;
		}

as you can see, it uses control flow obfuscation, calli and string encryption.

II. Protections

ArmDot offers different protections, but most of them break the dll when it’s obfuscated with them, so most of the owners use string encryption and control flow obfuscation, those 2 are great when obfuscating menus and doesn’t let the user get any overpowered methods or menu logic.

but, as one wise person said, EVERYTHING can be reverse-engineered. so I started looking at the patterns of the code, and noticed the string encryption was using XOR! what a surprise. so here comes the next part, string decryption.

III. String Decryption

ArmDot, instead of storing strings directly (like “Hello World”), they break them into char arrays and reconstruct them at runtime using XOR operations, my deobfuscator automates the decryption process and doesn’t do it at runtime, it uses dnlib to interact with the methods.

example of obfuscated method:

char[] array = new char[5];
array[0] = (char)(72 ^ 100);
array[1] = (char)(101 ^ 50);
array[2] = (char)(108 ^ 88);
array[3] = (char)(108 ^ 12);
array[4] = (char)(111 ^ 33);
string msg = new string(array);

the IL pattern this creates is basically:

  1. ldc.i4.5 - loads the constant 5 (in an array size).
  2. newarr System.Char - creates a new character array
  3. various stelem_i2 - store some characters in indices
  4. newobj System.String - finally constructs the string

now that I understood the pattern I made the string decryption code in .NET with dnlib:

Step 1: Finding Patterns

first of all, I scan through all the IL instructions and find patterns that match the example of an obfuscated method:

private List<Pattern> FindThePatterns(List<Instruction> instructions) 
{
	// you would look for: [int constants] + [newarr System.Char]
	// then search forward for: [newobj System.String]
}

I limit the array size to 1-1000 chars so I avoid false positives that aren’t strings.

Step 2: XOR Extraction

for each stelem_i2 instruction I find inside all the patterns, I extract the XOR like this:

for (int i = 0; i < arraySize; i++)
{
    if (assignments.TryGetValue(i, out var pair))
    {
        chars[i] = (char)(pair.Item1 ^ pair.Item2);
    }
}
string decoded = new string(chars).TrimEnd('\0');

this decodes all the array and prints out the decrypted string.

Step 3: Replacing

after I retrieved the decoded strings I focused on replacing them inside the dll, so it turned out like this:

for (int i = pattern.End; i >= pattern.Start; i--)
    method.Body.Instructions.RemoveAt(i);

method.Body.Instructions.Insert(pattern.Start, new Instruction(OpCodes.Ldstr, decoded));

by following this method, the example code that I shared before is replaced by this:

string msg = new string("Hello");

IV. Conclusion

don’t use ArmDot alone, use other things for string encryption, like Wyvern Protector, Rika.NET, and other obfuscators.

also I didn’t share it here, but I’ve got a control flow deobfuscation technique with a calli remover!

don’t ask me for the software please.

PepsiDee