吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 26796|回复: 99
收起左侧

[Android 原创] 2017腾讯游戏安全技术竞赛 Round 2 第二题详细题解

  [复制链接]
Ericky 发表于 2017-12-1 20:55
PS:这是最后一题

前言

第二题如果说就让人望而却步的话,这道题怕是要让人吐血身亡了。自己写wp感觉也要写得头发掉光,非常慌。不过既然到了最后一题,为了完整性,硬着头皮也得做下来吧,打开程序一开,里面有dll,采用的是mono框架,是不折不扣的.net的程序。我是第一次接触.net,更别说里面的IL code,jit什么的妖魔鬼怪了,反正现学现卖,不过鬼怪见多了也就慢慢习惯它们的样子了,慢慢就适应了,呃。我还是选择的安卓平台的题目,从评分标准来看,这个题目就分为了3小问,所以我把这个题目分为三大部分,一一阐述每个部分。

0x1 多战寻路--寻到一个坑

正如标题所说,三战寻路--寻到一个坑。本来IDA里的函数多数都被我标记得差不多了,因为用IDA F9动态调试的缘故,函数名都丢了(这里我曾崩溃),凑合着看吧,请见谅。
先贴一个main函数:
[Asm] 纯文本查看 复制代码
signed int sub_9EC0()
{
  int v0; // r0@1
  char *v1; // r3@1
  int v2; // r7@1
  int v3; // r0@3
  int v4; // r4@3
  int v5; // r8@3
  int v6; // r0@6
  int v7; // r5@6
  int v8; // r6@7
  int v9; // r1@7
  int v10; // r0@7
  signed int result; // r0@8
  char v12; // [sp+8h] [bp-20h]@1
  int v13; // [sp+Ch] [bp-1Ch]@1
  pthread_create(&v12);
  v13 = 3;
  sub_6C130(&unk_2B4E38, 0);
  v0 = sub_A5B4(&unk_2B4E3A);
  v1 = &byte_36F000;
  v2 = v0;
  do
  {
    *v1 = ~(*v1 ^ 0x11);
    ++v1;
  }
  while ( (_UNKNOWN *)v1 != &unk_370200 );
  v3 = sub_B925C(&byte_36F000, 4608, 1, &v13, 0, &unk_2B4E3A);
  v4 = v13;
  v5 = v3;
  if ( !v13
    && v3
    && sub_6F0A4(v3, &unk_2B4E3A)
    && (v6 = sub_86FB0(v5, "encrypt", "Hello"), (v7 = v6) != 0)
    && (v8 = sub_1196BC(v2, v6), sub_10FA38(v8, v9), (v10 = sub_898DC(v7, "SayHello", v4)) != 0) )
  {
    sub_116D20(v10, v8, v4, v4);
    sub_B9988(v5);
    result = v4;
  }
  else
  {
    result = 1;
  }
  return result;
}

Bypass反调试

说一下反调试先,我一共发现两处。init_array段有一处反调是,虽然不知道怎么判断的,反正把exit nop掉就行了。然后在main函数的起始位置起了一个反调试的线程,把启动的内容nop掉就可以了,这样就可以调试了,也就是可以正式开始漫漫长征路。

主程序流程

先分析一下程序的流程,程序开始会释放一个4.5kb的dll,称之为关键dll,然后通过mono框架加载这个dll 以及在外面的mscorlib.dll,调用mono框架中的一系列函数,寻找到指定的类,再找到sayhello函数,最后调用method_runtime_invoke,print出hello 。

初步修复关键dll

先不管那么多,既然它偷偷的加载dll,直接dump出来看看再说。dump出来的dll为非法文件,使用010editor 已经跟踪代码发现几处问题,Patch了5字节,至少成为了一个看起来正常的dll。
问题1.
5字节之第1字节:PE文件的签名,正常的pe文件签名位PE00,而这个文件的签名为PE01,这会导致NT头找不到,无法被正常识别。
原因:通过跟踪代码可以知道,mono框架只检查了PE,没有检查后面的00是否正确。
附上一个pe文件格式链接:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms680547(v=vs.85).aspx#other_contents_of_the_file

5字节之第2字节:.net程序里面有一项叫做元数据流,附上链接:
http://www.cnblogs.com/ShaYeBlog/archive/2013/07/20/3202191.html
导致dll继续报错的原因是一个#String的堆,未能识别,导致后面一系列失败,将后面的00 patch成 73即可。
原因:找到解析元数据中堆的地方,贴代码如下:
[Asm] 纯文本查看 复制代码
else if ( !strncmp(v22 + 8, "#Strings", 7u) )
        {
          v8 = *(_DWORD *)(v15 + 52);
          *(_DWORD *)(v15 + 56) = v8 + read32(v22);
          *(_DWORD *)(v15 + 60) = read32(v22 + 4);
          v22 += 17;
        }
        else if ( !strncmp(v22 + 8, "#US", 4u) )


可以看到,"s"字节没有比较,从这里可以看出来这个mono框架被修改过,而且有点猥琐。而在正常的mono框架中,strncmp的是9位,这就是元数据流异常的原因。

5字节之后3字节:
都在解析和装在MetaData的过程中,有3处高位被改了,导致一般的工具报数组越界的错误。
原因:mono框架修改了read函数,在read的时候只取了低位,这样的话不影响它自身对dll的解析。

修复这5字节,大概dll可以算得上“正常”了,至少扔到反编译工具中不出错了。

三寻IL code 真身

第一寻
一开始没想太多,想的就是可能在loadtable的时候把IL code还原了,然后追踪了很多遍,并没有发现真正的IL code,这里被坑了。局面开始显得僵化,突然发现这样的一个文章:
http://www.cnblogs.com/northstar ... /07/04/1771021.html
以为是隐藏IL code,加了壳,于是就开始了漫漫长路第二寻

第二寻
既然是壳,总会解密,继续跟就行了,外面既然没修改IL code的话,那肯定在更底层了。为了方便调试跟踪以及分析,我把源码下载下来了,地址在这:
https://github.com/mono/mono
然后就变成了苦逼的阅读代码,好,突然发现一个函数,mono_jit_compile_method_with_opt,再跟到mono_jit_compile_method_inner,再跟到mini_method_compile,再跟到mono_runtime_invoke,sayhello都已经执行了,我并没有定位到恢复的IL code,又过了不久,开始第三寻。

第三寻
直接定位mono_method_to_ir,这是最底层解析IL opcode的函数了,我在这下断点跟踪,总能搞定吧。这个函数也是够大,20000行,摘出来,一点一点看,一直跟不到修改IL code 的地方,最后发现,这个函数出题方居然动了,居然用自己的一套来解析Opcode,难怪dll中的IL code乱七八糟也能被执行,终于找到原因了,剩下的就是人肉还原IL code了。

人肉还原IL code

先贴一张Opcode 对应表:
[Asm] 纯文本查看 复制代码
符号                  代码                      原来                       修改
CEE_RET                 ret                    0x2A                    0x39
CEE_BRFALSE_S                   brfalse.s              0x2C                    0x3B
CEE_BRTRUE_S                    brtrue.s               0x2D                    0x3C
CEE_BRFALSE                    brfalse                0x39                   0x48                       
CEE_BRTRUE                 brtrue                     0x3A                    0x49
CEE_BEQ                                                         0x3D
CEE_BGE                                                        0x3E
CEE_BGT                                                        0x3F
CEE_BLE                                                        0x40
CEE_BLT                                                        0x41
CEE_BNE_UN                                                         0x42
CEE_BGE_UN                                                         0x43
CEE_BGT_UN                                                         0x44
CEE_BLE_UN                                                         0x45
CEE_BLT_UN                                                         0x46
下面的有点乱,凑合着看
*************
0x4A
0x4B
0x4C
0x4D
0x4E
0x4F
0x50
0x51
0x52
0x53
CEE_LDIND_I1
CEE_LDIND_U1
CEE_LDIND_I2
CEE_LDIND_U2
CEE_LDIND_I4
CEE_LDIND_U4
CEE_LDIND_I8
CEE_LDIND_I
CEE_LDIND_R4
CEE_LDIND_R8
CEE_LDIND_REF
*************
0x60
0x61
0x62
0x63
0x64
0x65
0x66
0xDF
CEE_STIND_REF
CEE_STIND_I1
CEE_STIND_I2
CEE_STIND_I4
CEE_STIND_I8
CEE_STIND_R4
CEE_STIND_R8
CEE_STIND_I
***************
CEE_ADD
CEE_SUB
CEE_DIV
CEE_DIV_UN
CEE_REM
CEE_REM_UN
CEE_AND
CEE_OR
CEE_XOR
CEE_SHL
CEE_SHR
CEE_SHR_UN
0x67
0x68
0x6A
0x6B
0x6C
0x6D
0x6E
0x6F
0x70
0x71
0x72
0x73
***********************
CEE_LDOBJ 0x80
CEE_LDSTR 0x81
CEE_NEWOBJ 0x82
CEE_REFANYVAL 0xC2
CEE_LDTOKEN 0xD0
0x91
0x92
0x93
0x94
0x95
0x96
0x97
0x98
0x99
0x9A
0xD1
0xD2
0xD3
0xE0
CEE_CONV_OVF_I1_UN
CEE_CONV_OVF_I2_UN
CEE_CONV_OVF_I4_UN
CEE_CONV_OVF_I8_UN
CEE_CONV_OVF_U1_UN
CEE_CONV_OVF_U2_UN
CEE_CONV_OVF_U4_UN
CEE_CONV_OVF_U8_UN
CEE_CONV_OVF_I_UN
CEE_CONV_OVF_U_UN
CEE_CONV_U2
CEE_CONV_U1
CEE_CONV_I
CEE_CONV_U
****************
CEE_STELEM_I
CEE_STELEM_I1
CEE_STELEM_I2
CEE_STELEM_I4
CEE_STELEM_I8
CEE_STELEM_R4
CEE_STELEM_R8
CEE_STELEM_REF
CEE_STELEM
0xAA
0xAB
0xAC
0xAD
0xAE
0xAF
0xB0
0xB1
0xB3
***************


还原好之后效果(贴一个最大的函数):

[Asm] 纯文本查看 复制代码
public static byte[] Encrypt(byte[] input)
{
    uint[] array = new uint[]
    {
        1423373290u,
        363609949u,
        512121596u,
        1703126449u
    };
    byte[] array2 = new byte[0];
    int num = input.Length;
    byte[] array3 = new byte[8];
    int num2 = 7 - num % 8;
    array3[0] = (byte)num2;
    for (int i = 0; i < num2; i++)
    {
        array3[i + 1] = (byte)(200 + num2 - i);
    }
    for (int j = 0; j < 7 - num2; j++)
    {
        array3[j + num2 + 1] = input[j];
    }
    uint[] array4 = new uint[]
    {
        MyClass.ConvertBytesToUInt(array3, 0),
        MyClass.ConvertBytesToUInt(array3, 4)
    };
    array4[0] = (array4[0] ^ array[0]);
    array4[1] = (array4[1] ^ array[2]);
    array4 = MyClass.Code(array4, array);
    array2 = MyClass.CombineBytes(array2, MyClass.ConvertUIntToBytes(array4[0]));
    array2 = MyClass.CombineBytes(array2, MyClass.ConvertUIntToBytes(array4[1]));
    for (int k = 7 - num2; k < num; k += 8)
    {
        array4[0] ^= MyClass.ConvertBytesToUInt(input, k);
        array4[1] ^= MyClass.ConvertBytesToUInt(input, k + 4);
        array4 = MyClass.Code(array4, array);
        array2 = MyClass.CombineBytes(array2, MyClass.ConvertUIntToBytes(array4[0]));
        array2 = MyClass.CombineBytes(array2, MyClass.ConvertUIntToBytes(array4[1]));
    }
    return array2;


算法是XTEA的变种,链接这里:
https://en.wikipedia.org/wiki/XTEA

说实话,我这方法有点麻烦,你们出题有点猥琐。

0x2 解密函数解密data

直接上代码,wp写累了
[Asm] 纯文本查看 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace TENCENT2017
{
    class Program
    {
        static void Main(string[] args)
        {
            EncryptDataFile();
            DecryptDataFile();
            Console.WriteLine("Hello GSLab!");
        }
        public static void EncryptDataFile()
        {
            byte[] array;
            using (FileStream fileStream = new FileStream("areyouok.png", FileMode.Open))
            {
                using (BinaryReader binaryReader = new BinaryReader(fileStream))
                {
                    array = binaryReader.ReadBytes(Convert.ToInt32(fileStream.Length));
                    array = Encrypt(array);
                }
            }
            using (FileStream fileStream2 = new FileStream("areyouok_encrypted", FileMode.Create))
            {
                using (BinaryWriter binaryWriter = new BinaryWriter(fileStream2))
                {
                    binaryWriter.Write(array);
                }
            }
        }
        public static void DecryptDataFile()
        {
            byte[] array;
            using (FileStream fileStream = new FileStream("data.encrypted", FileMode.Open))
            {
                using (BinaryReader binaryReader = new BinaryReader(fileStream))
                {
                    array = binaryReader.ReadBytes(Convert.ToInt32(fileStream.Length));
                    array = Decrypt(array);
                }
            }
            using (FileStream fileStream2 = new FileStream("de-data.encrypted.png", FileMode.Create))
            {
                using (BinaryWriter binaryWriter = new BinaryWriter(fileStream2))
                {
                    binaryWriter.Write(array);
                }
            }
        }
        public static byte[] Decrypt(byte[] input)
        {
            int num = input.Length;
            uint[] array = new uint[]
            {
                1423373290u,
                363609949u,
                512121596u,
                1703126449u
            };
            byte[] array2 = new byte[0];
            uint[] array4 = new uint[]
            {
                0,
                0
            };
            uint[] array5 = new uint[]
            {
                0,
                0
            };
            for (int k = num-4; k >=12; k -= 8)
            {
               array4[1] = ConvertBytesToUInt(input, k );
               array4[0] = ConvertBytesToUInt(input, k-4);
               array4 = DeCode(array4, array);
               array4[1] ^= ConvertBytesToUInt(input, k -8);
               array4[0] ^= ConvertBytesToUInt(input, k - 12);
               array2 = CombineBytes2(array2, ConvertUIntToBytes(array4[1]));
               array2 = CombineBytes2(array2, ConvertUIntToBytes(array4[0]));
                
            }
            return array2;
        }
        private static uint[] DeCode(uint[] v, uint[] k)
        {
            uint num = v[0];
            uint num2 = v[1];
            uint num3 = 0u;
            uint num4 = Convert.ToUInt32(Math.Floor((Math.Pow(5.0, 0.5) - 1.0) * Math.Pow(2.0, 31.0)));
            uint num5 = 32u;
            while (num5-- > 0u)
            {
                num3 += num4;
            }
            num5 = 32u;
            while (num5-- > 0u)
            {
                num2-= (num << 4 ^ (num >> 5) + num ^ num3 + k[(int)((ushort)(num3 >> 11 & 3u))]);
                num3 -= num4;
                num -= (num2 << 4 ^ (num2 >> 5) + num2 ^ num3 + k[(int)((ushort)(num3 & 3u))]);
               Console.WriteLine(" {0} {1} {2}.", num, num2, num3);
                 
            }
            return new uint[]
    {
        num,
        num2
    };
        }
        public static byte[] Encrypt(byte[] input)
        {
            uint[] array = new uint[]
    {
        1423373290u,
        363609949u,
        512121596u,
        1703126449u
    };
            byte[] array2 = new byte[0];
            int num = input.Length;
            byte[] array3 = new byte[8];
            int num2 = 7 - num % 8;
            array3[0] = (byte)num2;
            for (int i = 0; i < num2; i++)
            {
                array3[i + 1] = (byte)(200 + num2 - i);
            }
            for (int j = 0; j < 7 - num2; j++)
            {
                array3[j + num2 + 1] = input[j];
            }
            uint[] array4 = new uint[]
    {
        ConvertBytesToUInt(array3, 0),
        ConvertBytesToUInt(array3, 4)
    };
            array4[0] = (array4[0] ^ array[0]);
            array4[1] = (array4[1] ^ array[2]);
            array4 = Code(array4, array);
            array2 = CombineBytes(array2, ConvertUIntToBytes(array4[0]));
            array2 = CombineBytes(array2, ConvertUIntToBytes(array4[1]));
            for (int k = 7 - num2; k < num; k += 8)
            {
                array4[0] ^= ConvertBytesToUInt(input, k);
                array4[1] ^= ConvertBytesToUInt(input, k + 4);
                array4 = Code(array4, array);
                array2 = CombineBytes(array2, ConvertUIntToBytes(array4[0]));
                array2 = CombineBytes(array2, ConvertUIntToBytes(array4[1]));
            }
            return array2;
        }
        private static byte[] ConvertUIntToBytes(uint input)
        {
            return new byte[]
                {
                    (byte)(input & 255u),
                    (byte)(input >> 8 & 255u),
                    (byte)(input >> 16 & 255u),
                    (byte)(input >> 24 & 255u)
                };
        }
        private static uint ConvertBytesToUInt(byte[] input, int pos)
        {
            uint num = (uint)input[pos];
            num += (uint)((uint)input[pos + 1] << 8);
            num += (uint)((uint)input[pos + 2] << 16);
            return num + (uint)((uint)input[pos + 3] << 24);
        }
        private static byte[] CombineBytes(byte[] bytes1, byte[] bytes2)
        {
            byte[] array = new byte[bytes1.Length + bytes2.Length];
            Buffer.BlockCopy(bytes1, 0, array, 0, bytes1.Length);
            Buffer.BlockCopy(bytes2, 0, array, bytes1.Length, bytes2.Length);
            return array;
        }
        private static byte[] CombineBytes2(byte[] bytes1, byte[] bytes2)
        {
            byte[] array = new byte[bytes1.Length + bytes2.Length];
            Buffer.BlockCopy(bytes2, 0, array, 0, bytes2.Length);
            Buffer.BlockCopy(bytes1, 0, array, bytes2.Length, bytes1.Length);
            return array;
        }
        private static uint[] Code(uint[] v, uint[] k)
        {
            uint num = v[0];
            uint num2 = v[1];
            uint num3 = 0u;
            uint num4 = Convert.ToUInt32(Math.Floor((Math.Pow(5.0, 0.5) - 1.0) * Math.Pow(2.0, 31.0)));
            uint num5 = 32u;
            while (num5-- > 0u)
            {
                num += (num2 << 4 ^ (num2 >> 5) + num2 ^ num3 + k[(int)((ushort)(num3 & 3u))]);
                num3 += num4;
                num2 += (num << 4 ^ (num >> 5) + num ^ num3 + k[(int)((ushort)(num3 >> 11 & 3u))]);
               // Console.WriteLine(" {0} {1} {2}.", num, num2, num3);
            }
            return new uint[]
    {
        num,
        num2
    };
        }
    }
}

0x3 最后的战役

这一问好开放,只能靠脑洞了。解密出来的是一个藏着数据的png。修复头之后提示格式问题,去熟悉格式。
又不会,直接链接如下,一起学习之:
https://wenku.baidu.com/view/a991270f76c66137ee061967.html
https://www.ietf.org/rfc/rfc1951.txt
http://www.sweetscape.com/010editor/repository/files/PNG.bt

发现第4个CHUNK 有问题,感觉这个就是目标数据。然后去修了很久,卡了很久这里。找了几个png的图片对照,发现这里多了很多FF,完全没有的东西,一开始还以为,然后各种找压缩算法,zlib,lz77的各种衍生算法,都没能压缩对。
后来找一些关于图片的分析,查到一个图像隐写,然后继续查资料,学习,一直纠结于这个东西,当然没有结果。
最后发现是位图格式,bmp,我倒。然后就OK了。
其实题目有提示,我想当然的理解错了。

整个比赛就结束了,谢谢观看,不足之处请看官斧正!

2017.7.23

By Ericky

免费评分

参与人数 35吾爱币 +36 热心值 +32 收起 理由
cq88998577 + 1 + 1 想请教大神一个问题,可以的话加一下我的扣,谢谢,88998577
zzzlucas + 1 + 1 谢谢@Thanks!
xqs2356231 + 1 + 1 用心讨论,共获提升!
52Tao + 1 + 1 已答复!
walq + 1 + 1 热心回复!
ak103 + 1 + 1 我很赞同!
welcome7758521 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
慕尛遥 + 1 + 1 请收下我的膝盖!!!!!!!!!!!
小老虎爱吃鱼 + 1 + 1 用心讨论,共获提升!
SomnusXZY + 1 + 1 热心回复!
刊登steam + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
KΟKΟ + 1 + 1 大神!!!!!!!!!!!!!!!!!!
22222 + 1 精华!
zamliage + 1 + 1 这确实是厉害啊!
土川鼠 + 1 + 1 我很赞同!
siuhoapdou + 1 + 1 谢谢@Thanks!
xiaoxiaoming + 1 + 1 我很赞同!
无痕软件 + 1 + 1 学习了,自定义opcode 和 path pe
aubu11 + 1 + 1 谢谢@Thanks!
大泰_ + 1 + 1 谢谢@Thanks!
Sm1Lin9Fac3 + 1 用心讨论,共获提升!
Three_fish + 1 + 1 谢谢@Thanks!
QNLY + 3 + 1 直接蒙B
邪颖 + 1 + 1 已答复!
旋转风律 + 1 + 1 热心回复!
posikid + 1 + 1 热心回复!
↑帝↑ + 1 + 1 学习了!!!
Kirito丶Asuna + 1 + 1 热心回复!
sunnylds7 + 1 已答复!
gongjiankk + 1 用心讨论,共获提升!
kyrzy0416 + 1 + 1 谢谢@Thanks!
NewShadow + 1 + 1 用心讨论,共获提升!
繁花似锦丿遇见 + 1 + 1 用心讨论,共获提升!
你的麦克菲 + 1 + 1 用心讨论,共获提升!
A.谭先生 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

ziyerain 发表于 2018-12-26 20:03
今夜打老虎 发表于 2017-12-4 14:56
看的一脸懵逼     膜拜大佬

https://www.upload.ee/files/9180 ... .0.306.x64.exe.html

别发消息浪费币!
funmxd123 发表于 2017-12-7 17:36
虽然看不懂,但是感觉到里面的知识,比如文件残缺根据文件结构修复,算法什么的!看来路还很长呀!
头像被屏蔽
冷小鑫 发表于 2017-12-1 21:11
繁花似锦丿遇见 发表于 2017-12-1 21:49
看不懂.膜拜大神
ftmovie 发表于 2017-12-1 21:58 来自手机
学习啦,学习了啊
gang891221 发表于 2017-12-1 22:11

看不懂...膜拜大神中
亦然2333 发表于 2017-12-2 00:18
柔柔弱弱可以
varg1714 发表于 2017-12-2 08:58
看不懂,膜拜大神ing
没想到吧? 发表于 2017-12-2 09:49

看不懂,膜拜大神ing
shian1988 发表于 2017-12-2 10:12
感谢分享,很不错的教程,来学习了。。
笑苏语97 发表于 2017-12-2 10:21
一点也看不懂  膜拜一下吧
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 13:55

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表