There are ILs generating ILs in two way:
-
by using opcodes
-
by bytes (which are XORed by result of 1)
-
by using opcodes
// CrackMe.Form1
public static Type IIIIIllllIIIIlllIlllIIIlIIlIll11II1Ill()
{
AppDomain domain = Thread.GetDomain();
AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(new AssemblyName
{
Name = "IIIII1111I1111II11I1I1I11I11I1I11"
}, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("IIIIIIllllllIIIll1111llllIII");
TypeBuilder typeBuilder = moduleBuilder.DefineType("IIIII11111II1111II1I11I1llllll", TypeAttributes.Public);
MethodBuilder methodBuilder = typeBuilder.DefineMethod("IIIIllllIIllIIIIlllIIIIIIIIIIlllIIllIIlIlIlIlllllIIIllllllIIIIl", MethodAttributes.FamANDAssem | MethodAttributes.Family | MethodAttributes.Static, typeof(int[]), new Type[]
{
typeof(byte),
typeof(byte),
typeof(byte),
typeof(byte),
typeof(byte),
typeof(byte),
typeof(byte),
typeof(byte)
});
ILGenerator iLGenerator = methodBuilder.GetILGenerator();
Label label = iLGenerator.DefineLabel();
Label label2 = iLGenerator.DefineLabel();
...
iLGenerator.Emit(OpCodes.Nop);
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Ldc_I4_8);
iLGenerator.Emit(OpCodes.Shl);
iLGenerator.Emit(OpCodes.Ldarg_1);
iLGenerator.Emit(OpCodes.Add);
iLGenerator.Emit(OpCodes.Stloc_0);
iLGenerator.Emit(OpCodes.Ldarg_2);
...
return typeBuilder.CreateType();
And this function has input from user.
// CrackMe.Form1
private void button1_Click(object sender, EventArgs e)
{
string text = this.textBox1.Text;
bool flag = text == null || text.Length != 8;
if (flag)
{
MessageBox.Show("ILKey variable must have 8 characters.");
}
else
{
this.IIIIllllIIllIIIIlllIIIIIIIIIIlllIIllIIlIlIlIl(text);
}
}
And..
// CrackMe.Form1
private void IIIIllllIIllIIIIlllIIIIIIIIIIlllIIllIIlIlIlIl(string I1111lll11l1111ll111l)
{
byte[] bytes = Encoding.ASCII.GetBytes(I1111lll11l1111ll111l);
Type type = Form1.IIIIIllllIIIIlllIlllIIIlIIlIll11II1Ill();
int num = 0;
object target = Activator.CreateInstance(type, new object[0]);
object obj = type.InvokeMember("IIIIllllIIllIIIIlllIIIIIIIIIIlllIIllIIlIlIlIlllllIIIllllllIIIIl", BindingFlags.InvokeMethod, null, target, new object[]
{
bytes[0],
bytes[1],
bytes[2],
bytes[3],
bytes[4],
bytes[5],
bytes[6],
bytes[7]
});
IEnumerable enumerable = obj as IEnumerable;
bool flag = enumerable != null;
if (flag)
{
foreach (object current in enumerable)
{
int num2 = (int)current;
this.l1l1111l11IllI1l1IIl[num] = (byte)((num2 & 65280) >> 8);
this.l1l111l1l11l1l1l1l11l[num] = (byte)(num2 & 255);
num++;
}
}
this.button3.Visible = true;
}
The array initialized on the binary is used on XOR-ing opcode bytes on 2.
// CrackMe.Form1
public byte[] lIIl11111111111111111111111llllllllll11111111 = new byte[]
{
112,
3,
88,
4,
90,
5,
90,
86,
4,
97,
124,
5,
89,
2,
3,
4,
22,
100,
38,
110
};
// CrackMe.Form1
private void button3_Click(object sender, EventArgs e)
{
for (int i = 0; i < 6; i++)
{
byte b = this.lIIl11111111111111111111111llllllllll11111111[(int)this.l1l1111l11IllI1l1IIl[i]] ^ this.l1l111l1l11l1l1l1l11l[i];
this.lIIl11111111111111111111111llllllllll11111111[(int)this.l1l1111l11IllI1l1IIl[i]] = b;
}
...
So the array is used on XOR location-XOR value pair. Let's reverse function on 1. First, I could recompile it by ilasm + some replacements. I've attached the IL source code. After compilation, I gave it to ILSpy again. I've attached the decompiled C# source code. Since the returned value is used on XOR location-XOR value, the location must be in the range of opcode array index(0~20).
So some if-elses and some invalid conditions can be removed. I've attached the simplified IL source code.
There are multiple input[i]^input[j]=CONSTANT
like expressions, so I just feeded it to Z3, removing some cases by seeing it on IL.
IL can be viewed on IDA Pro, using IDAPython.
[PatchBytes(here()+i, s[i]) for i in range(len(s))] # s is opcode array.
And I've attached final solution code too. It uses Z3Py.