谁将平生葬倾城 发表于 2023-9-27 16:49

一个C#编写的神奇的KeyGenMe

本帖最后由 谁将平生葬倾城 于 2023-9-27 21:41 编辑

# 一个C#编写的神奇的KeyGenMe

## 保护措施

无壳,有简单混淆。混淆强度不是太高。

## 题目要求

输入正确的序列号,提示 "注册成功!" 即可。

这是KeyGenMe,爆破和修改字符串提示信息不算。

回帖提供正确的序列号视为攻击成功!

## 成功截图:




## 附件:








duwenjie15 发表于 2023-10-6 11:19

使用de4dot反混淆失败,使用ilspy查看失败,使用dnspy关键函数无法打开反编译查看,楼主反反编译器太厉害了。
所以这里使用的是自己写的IL查看工具

可以看到Rgdhkl4pre0继承来自Form是该程序的主窗体,其中ObSFH4就是刚才在dnspy中无法查看反编译的类型,推测验证代码就藏在其中。
从Metadata信息来看这个方法没有异常处理程序,没有swich分支混淆,不过体积达到了惊人的280K,77039条指令。
其中在GyTJb9nLa中的DjAzNWBx应该是Program的Main入口函数

看到其中拦截了异常
                AppDomain.CurrentDomain.UnhandledException += GyTJb9nLa.u8klHZPY;
这里应该就是验证失败的分支,作者降低了难度让所有的失败都以throw的形式抛出异常,使得验证逻辑简化了不少。
回到ObSFH4方法中,把方法跟踪一下,其7万余条指令最终产生了高达1112514002次指令执行,最高一条指令执行了35366次。

观察其大部分指令为ldc.i4 ceq brfalse这类型的分支指令,我们忽略执行次数过高的指令关注执行次数为1的指令,很快发现了

这里开始应该就是访问txtbox控件获取字符串的地方,但是指令还是太多了,我们需要对指令流反混淆。
这里反混淆包括去除Nop,将SizeOf转为立即数,利用虚拟机合并可计算指令
这里包括但操作数指令
            //neg       0x65
            //not0x66
            //conv.i10x67
            //conv.i20x68
            //conv.i40x69
            //conv.i80x6A
            //conv.r40x6B
            //conv.r80x6C
            //conv.u40x6D
            //conv.u80x6E
双操作数指令
               //add0x58
                //sub0x59
                //mul0x5A
                //div0x5B
                //div.un   0x5C x
                //rem0x5D
                //rem.un   0x5E x
                //and0x5F
                //or   0x60
                //xor0x61
                //shl0x62
                //shr0x63
                //shr.un   0x64 x

                //add.ovf       0xD6
                //add.ovf.un   0xD7 x
                //mul.ovf0xD8
                //mul.ovf.un   0xD9 x
                //sub.ovf0xDA
                //sub.ovf.un   0xDB x

                //ceq0xFE01
                //cgt0xFE02
                //cgt.un   0xFE03
                //clt0xFE04
                //clt.un   0xFE05
移除无用分支指令
brfalse.s
brtrue.s
beq.s
bge.s
bgt.s
ble.s
blt.s
bne.un.s
bge.un.s
bgt.un.s
ble.un.s
blt.un.s
brfalse
brtrue
beq
bge
bgt
ble
blt
bne.un
bge.un
bgt.un
ble.un
blt.un
switch
模拟合并System.Math,System.Convert等数学运算,
最后我们得到了一个较为清洁的代码

将反混淆后的代码写入并重新跟踪,并对关键clt、Ldsfld、Stsfld、Stelem_I1指令和String.get_Length()、Object.ToString()、Encoding.GetString()、StringBuilder.GetLength()、StringBuilder.Chars、Striing.IndexOff方法下断,我们得以分析到其关键指令流程

这里获取输入并转为byte[]数组

将输入转为字符串

然后使用StringBuilder.Append组装了一个日语字典,并使用ToString()取出

这里开始组装最终密钥
我们跳到源码看一下组装流程
          0          callvirt        System.Char System.Text.StringBuilder.get_Chars(Int32 index)
          0          callvirt        System.Int32 System.String.IndexOf(Char value)
          0             add       
          0          stsfld        System.Int32 <Module>.FixedUpdate
          0          ldsfld        System.Byte[] <Module>.GetTypes
          0          ldsfld        System.Int32 <Module>.get_Syntax
          0             dup       
          0          ldc.i4        1
          0             add       
          0          stsfld        System.Int32 <Module>.get_Syntax
          0          ldsfld        System.Int32 <Module>.FixedUpdate
          0          ldc.i4        16
          0             shr       
          0          ldc.i4        255
          0             and       
          0           conv.u1       
          0       stelem.i1       
虽然我们发现了
          0          ldsfld        System.Byte[] <Module>.GetTypes
          0             ldlen       
          0           conv.i4       
          0          ldc.i4        9
          0             ceq       
          0          ldc.i4        0
          0             ceq       
          0          stsfld        System.Boolean <Module>.LogWarning
          0          ldsfld        System.Boolean <Module>.LogWarning
          0           brfalse        76710
          0          newobj        System.Exception..ctor()
          0             throw       
          0          ldsfld        System.Byte[] <Module>.GetTypes
          0          ldc.i4        0
          0       ldelem.u1       
          0          ldc.i4        215
          0          bne.un        76914
          0          ldsfld        System.Byte[] <Module>.GetTypes
          0          ldc.i4        8
          0       ldelem.u1       
          0          ldc.i4        33
          0             ceq       
结合上下逻辑我们得到其Key验证代码为
      public static bool Key(string hex)
      {
            List<byte> temp = new List<byte>();
            for (int i = 0; i < (hex.Length / 2); i++)
            {
                byte m = Convert.ToByte(hex.Substring(i * 2, 2), 16);
                temp.Add(m);
            }
            string key = Encoding.Default.GetString(temp.ToArray());
            string dic = "あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやよらりるれろわをぐげござじずぞだぢづでばびぶべぱぴぷぺぽ";

            int round = key.Length / 4;
            byte[] temp2 = new byte;

            for (int i = 0; i < round; i ++)
            {
                int start = i * 4;
                int m1 = dic.IndexOf(key);
                int m2 = dic.IndexOf(key);
                int m3 = dic.IndexOf(key);
                int m4 = dic.IndexOf(key);
                int m5 = ((m1 << 18) + (m2 << 12) + (m3 << 6) + m4);
                start = i * 3;
                temp2 = (byte)(m5 >> 16);
                temp2 = (byte)(m5 >> 8);
                temp2 = (byte)(m5);
            }

            if (temp2.Length != 9)
                return false;
            return temp2 == 215 && temp2 == 33;
      }

所以其KeyGen代码应为
      public static string KeyGen()
      {
            byte[] m = Encoding.Default.GetBytes("成功");
            string dic = "あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやよらりるれろわをぐげござじずぞだぢづでばびぶべぱぴぷぺぽ";
            int[] indexs = new int;
            Random random = new Random();
            indexs = 0x35;
            indexs = 0x30;
            indexs = 0x00;
            indexs = 0x21;
            for (int i = 2; i < indexs.Length - 2; i++)
                indexs = random.Next(dic.Length);
            StringBuilder b2 = new StringBuilder();
            foreach (int index in indexs)
                b2.Append(dic);
            byte[] data = Encoding.Default.GetBytes(b2.ToString());
            b2.Clear();
            foreach (byte b in data)
                b2.Append(b.ToString("X2"));
            return b2.ToString();
      }
生成一个实验一下A4C5A4B8A4D5A4DDA4C0A4D9A4D4A4B1A4BAA4BFA4A2A4E1

qq465881818 发表于 2023-9-28 13:26

谁将平生葬倾城 发表于 2023-9-28 14:43

本帖最后由 谁将平生葬倾城 于 2023-9-28 17:41 编辑

qq465881818 发表于 2023-9-28 13:26

加油,离成功还有很远的距离。:lol

taotao1997 发表于 2023-9-30 21:48

罗萨 发表于 2023-10-4 17:21

https://www.52pojie.cn/thread-1840543-1-1.html

谁将平生葬倾城 发表于 2023-10-4 20:37

罗萨 发表于 2023-10-4 17:21
https://www.52pojie.cn/thread-1840543-1-1.html

什么玩意,帖子被删了?

谁将平生葬倾城 发表于 2023-10-5 09:47

罗萨 发表于 2023-10-4 17:21
https://www.52pojie.cn/thread-1840543-1-1.html

这是控制流混淆,直接特征码替换可能会导致代码执行流程错乱。

罗萨 发表于 2023-10-5 12:26

谁将平生葬倾城 发表于 2023-10-5 09:47
这是控制流混淆,直接特征码替换可能会导致代码执行流程错乱。

试一下就知道了 换完干净的很

月之点点 发表于 2023-10-5 13:47

谁将平生葬倾城 发表于 2023-10-5 17:46

月之点点 发表于 2023-10-5 13:47
我现在很想知道。这是用什么工具混淆的。

github上随便搜的
页: [1] 2
查看完整版本: 一个C#编写的神奇的KeyGenMe