loudy 发表于 2017-8-7 21:28

Gslab游戏安全竞赛社会组WP(第二轮第二题)

本帖最后由 loudy 于 2017-10-17 08:37 编辑

最近新开了一个微信公众号:风云处(fyc1687),欢迎大家多多交流。其中我会逐步把我学习逆向的经验、实例、技巧分享出来。
一、整体轮廓
      IDA载入,发现了TLS和CreateThread反调试,直接nop掉即可。程序流程很清晰,但其中调用了mono生成的dll,且程序中自己实现了donet虚拟机(简单修改了msil指令集),导致dump出的encrypt.dll无法分析,也无法找出其中的函数。


二、encrypt.dll处理
找到关键位置,发现明显的PE文件特征,dump大小为0x1200。

该dll文件进行了人为修改,导致PE编辑工具无法识别。结合PE格式手工识别,容易发现下图红线处应该为0。
修改后PE编辑工具可识别,但其中的donet元数据识别不准确,元数据信息也被修改。

对照donet元数据格式,修改文件中下图标红出(自己生成一个donet的Dll文件,对照看更佳,本人就是用这种办法),第二条红线处改为‘s’,另外三条红线处清0(具体请参考doNet格式)。

对照修改,直到能识别所有元数据,修改完成后如下图所示。

此时可以用Reflector载入,但Refector只能识别类结构和函数名,却无法准确识别函数结构体,函数的实现看不到。实际上程序中用mono实现了一个本地.net运行时,经过分析发现,程序中实现的doNet虚拟机指令即在标准指令操作码的基础上加了0x0f,故只要找到函数体位置,手工或者自动实现将操作码数值减去0x0f(0xD0以上操作码不变),而操作数不变,即可还原。此处可以用IDA识别MSIL,依次手动修改操作码。还原后,用Reflector载入,可以看到函数已经正确识别。

本人是对照IDA手工修改的。

可能是由于手工修改有差错,导致ConvertUIntToBytes和EncryptDataFile两个函数没有反编译,但这两个函数比较简单,不影响分析。

二、主要算法
(1)ConvertBytesToUInt,将4字节转化为32位无符号整数。   
private static uint ConvertBytesToUInt(byte[] input, int pos)
       {
            // This item is obfuscated and can not be translated.
            uint num = (uint)(input) + (uint)(input << 0x8) + (uint)(input << 0x10) + (uint)(input << 0x18);
            return num;
}   

(2)ConvertUIntToBytes,将32位无符号整数转化为4字节。      
      private static byte[] ConvertUIntToBytes(uint x)
      {
            byte[] dst = new byte;
            for (int i = 0; i < 4; i++)
            {
                dst = (byte)(x & 0xff);
                x = x >> 8;
            }
            return dst;
      }
(3)CombineBytes,将byte[]连接起来。      
      private static byte[] CombineBytes(byte[] bytes1, byte[] bytes2)
      {
            byte[] dst = new byte;
            Buffer.BlockCopy(bytes1, 0, dst, 0, bytes1.Length);
            Buffer.BlockCopy(bytes2, 0, dst, bytes1.Length, bytes2.Length);
            return dst;
      }
(4)Code,主要的编码函数,主要的逆向对象,对输入进行0x20轮的编码加密。      
      private static uint[] Code(uint[] v, uint[] k)
      {
            uint num = v;//0x54d6f3ea
            uint num2 = v;//0x1e865afc
            uint num3 = 0;
            uint num4 = Convert.ToUInt32(Math.Floor((double)((Math.Pow(5.0, 0.5) - 1.0) * Math.Pow(2.0, 31.0))));
            uint num5 = 0x20;
            while (num5-- > 0)
            {
                num += ((num2 << 4) ^ ((num2 >> 5) + num2)) ^ (num3 + k[(ushort)(num3 & 3)]);
                num3 += num4;
                num2 += ((num << 4) ^ ((num >> 5) + num)) ^ (num3 + k[(ushort)((num3 >> 11) & 3)]);
            }
            return new uint[] { num, num2 }; //0xbfd3b3350xcc918c5e
      }
(5)Encrypt,对原数据加密,其中调用了上面这些函数。      
      public static byte[] Encrypt(byte[] input)
      {
            uint[] k = new uint[] { 0x54d6f3ea, 0x15ac3f5d, 0x1e865afc, 0x6583a5b1 };
            byte[] buffer = new byte;
            int length = input.Length;
            byte[] buffer2 = new byte;
            int num2 = 7 - (length % 8);
            buffer2 = (byte)num2;
            for (int i = 0; i < num2; i++)
            {
                buffer2 = (byte)((200 + num2) - i);
            }
            for (int j = 0; j < (7 - num2); j++)
            {
                buffer2[(j + num2) + 1] = input;
            }
            uint[] v = new uint[] { ConvertBytesToUInt(buffer2, 0), ConvertBytesToUInt(buffer2, 4) };
            v ^= k;
            v ^= k;
            v = Code(v, k);
            buffer = CombineBytes(CombineBytes(buffer, ConvertUIntToBytes(v)), ConvertUIntToBytes(v));
            for (int m = 7 - num2; m < length; m += 8)
            {
                v ^= ConvertBytesToUInt(input, m);
                v ^= ConvertBytesToUInt(input, m + 4);
                v = Code(v, k);
                buffer = CombineBytes(CombineBytes(buffer, ConvertUIntToBytes(v)), ConvertUIntToBytes(v));
            }
            return buffer;
      }
(6)EncryptDataFile,其主要逻辑即为读入文件(areyouok.png)内容,调用Encrypt对文件内容加密,将加密数据存入另一个文件(areyouok_encrypted)。

三、算法逆向
      Code函数中,num4虽然计算过程很复杂,但其实为固定值0x9e3779b9;num3初值为0,最后一轮结束后num3 为 0xc6ef3720。根据该函数的对成性,可以写出Code函数的逆函数InvCode如下。      
      private static uint[] InvCode(uint[] v, uint[] k)
      {
            uint num = v;
            uint num2 = v;
            uint num3 = 0xc6ef3720;
            uint num4 = Convert.ToUInt32(Math.Floor((double)((Math.Pow(5.0, 0.5) - 1.0) * Math.Pow(2.0, 31.0))));
            uint num5 = 0x20;
            while (num5-- > 0)
            {
                num2 -= ((num << 4) ^ ((num >> 5) + num)) ^ (num3 + k[(ushort)((num3 >> 11) & 3)]);
                num3 -= num4;
                num -= ((num2 << 4) ^ ((num2 >> 5) + num2)) ^ (num3 + k[(ushort)(num3 & 3)]);
            }
            return new uint[] { num, num2 };
      }
因此对文件解密过程如下。      
            int rNum = 0x1be8;
            byte[] rData = new byte;
            byte[] wData = new byte;
            FileStream rFile = new FileStream("data.encrypted", FileMode.Open);
            FileStream wFile = new FileStream("data.png",FileMode.Create);

            rFile.Read(rData, 0, rNum);

            uint x0 = 0, x1 = 0, x00 = 0, x11 = 0;
            uint[] k = new uint[] { 0x54d6f3ea, 0x15ac3f5d, 0x1e865afc, 0x6583a5b1 };
            for (int i = 0; i < rNum; i = i + 8)
            {
                uint[] v = new uint[] { ConvertBytesToUInt(rData, i), ConvertBytesToUInt(rData, i+4) };
                x00 = v;
                x11 = v;
                v = InvCode(v, k);
                if (i == 0)
                {
                  v ^= k;
                  v ^= k;
                }
                v ^= x0;
                v ^= x1;
                x0 = x00;
                x1 = x11;
                wData = CombineBytes(CombineBytes(wData, ConvertUIntToBytes(v)), ConvertUIntToBytes(v));
            }

            for (int i = 0; i < rNum - 7; i++)
            {
                wData = wData;
            }

            wFile.Write(wData, 0, rNum - 7);
解密得到文件data.png,打开如下。


通过以上代码对文件data.png加密,发现加密结果和题中给出结果(data.encrypted文件)一致,说明解密函数是正确的。用winhex打开data.png文件,发现图中阴影部分(第二个“sRGB”chunk)与png文件格式无关,且最后chunk最后的CRC32结果也不对,为无效chunk,猜测为嵌入的数据。

将该chunk数据提取出,用winhex载入,发现下图红线处恰好为其后数据大小,这和bmp文件格式一致。

将头两字节改为“BM”,然后另存为xy.bmp。

打开xy.bmp,得到最终结果。

全文完。

KaQqi 发表于 2017-8-9 07:22

本帖最后由 cqr2287 于 2017-8-9 07:23 编辑

loudy 发表于 2017-8-8 19:28
这个不知道怎么弄,我粘贴时是好的
可以试试 插入-插入代码-c-输入内容-插入

编辑最右边一格有一个 <>符号的按钮,点它就行

loudy 发表于 2017-8-10 22:47

cqr2287 发表于 2017-8-9 07:22
可以试试 插入-插入代码-c-输入内容-插入

编辑最右边一格有一个 符号的按钮,点它就行

改好了,新技能get

唯爱而生 发表于 2017-8-7 21:43

过来膜拜一下大神

zbnysjwsnd8 发表于 2017-8-7 22:18

{:1_910:}图片GG

loudy 发表于 2017-8-7 22:22

zbnysjwsnd8 发表于 2017-8-7 22:18
图片GG

现在呢,我这边一切正常,之前是外链图片,现在图片存本站

trombe108 发表于 2017-8-7 22:30

什么东东啊

安之噗嗤 发表于 2017-8-8 00:00

膜拜https://www.52pojie.cn/static/image/smiley/default/25.gif

qiaoqiaota 发表于 2017-8-8 00:33

谢谢!正好需要,十分感激!

KaQqi 发表于 2017-8-8 07:18

后面的主要算法那粘贴的代码有点散架了.看不太习惯

a38695746 发表于 2017-8-8 07:40

好好谢谢楼住

无痕软件 发表于 2017-8-8 08:39

有启发! {:1_927:}
页: [1] 2 3
查看完整版本: Gslab游戏安全竞赛社会组WP(第二轮第二题)