实例解说未知游戏文件花样的逆向剖析要领(下) | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

实例解说未知游戏文件花样的逆向剖析要领(下)

申博_新闻事件 申博 120次浏览 已收录 0个评论

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-

如今,我们晓得v8相对是一个类。接下来,让我们临时脱离这里,回到50B9A2,考核一下50B91E函数:

    _BYTE *__stdcall sub_50B91E(int a1, int a2, unsigned int a3)
    {
      unsigned int v3; // [email protected]
      unsigned int v4; // [email protected]
      _BYTE *result; // [email protected]
    
      v3 = 0;
      v4 = 0;
      if ( a3 )
      {
        do
        {
          result = (_BYTE *)(v4 + a2);
          *result ^= *(_BYTE *)(a1 + v3++ + 116);
          if ( v3 >= 0x1000 )
            v3 = 0;
          ++v4;
        }
        while ( v4 < a3 );
      }
      return result;
    }

起首,让我们重命名末了两个参数,因为我们已晓得它们是什么:

    _BYTE *__stdcall sub_50B91E(int a1, int pDestBuffer, unsigned int uiSize)
    {
      unsigned int v3; // [email protected]
      unsigned int v4; // [email protected]
      _BYTE *result; // [email protected]
    
      v3 = 0;
      v4 = 0;
      if ( uiSize )
      {
        do
        {
          result = (_BYTE *)(v4 + pDestBuffer);
          *result ^= *(_BYTE *)(a1 + v3++ + 116);
          if ( v3 >= 0x1000 )
            v3 = 0;
          ++v4;
        }
        while ( v4 < uiSize );
      }
      return result;
    }

在这里,最主要的一行代码是:

    *result ^= *(_BYTE *)(a1 + v3++ + 116);

我们晓得,XOR一般用于加密/解密历程。如您所见,它会逐字节地处置惩罚位于a1+116+v3处的数组,并对该数组的加密缓冲区中的每一个字节都举行异或操纵。若是v3即是4096(0x1000h),则将其重置为0。XOR处置惩罚将延续到v4(在每次迭代完毕时都邑递增)大于或即是参数uiSize时为止。

看上去,这能够有点难以明白,不外不要忧郁,我们可以或许借助于动态剖析器械来搞定它。

运用OllyDbg举行动态剖析

起首,在OllyDbg中加载pbclient.exe,然后,单击“E”按钮并挑选“pbclient.exe”模块,以确保我们处于准确的模块中。

我们将在CreateFileA挪用(还记得吗?它位于0x52AE36中)的第一个参数处设置一个断点,它现实上就是我们的文件名。

(注重:设置断点后不要删除,我们今后还会须要它们)

它位于0x52AE82处:

实例解说未知游戏文件花样的逆向剖析要领(下)

让我们如今就最先着手吧!若是它瓦解了,发起先下载Stealth64插件,若是您运用的是64位Windows 7体系的话。同时,我们还启用了除“Misc”局部以外的统统选项。

若是统统顺利,它如今将会在图示中的地位中缀:

实例解说未知游戏文件花样的逆向剖析要领(下)

我们已晓得,它将运用anim.bus(同时,我们也获得了它运用这个函数的确认)。让我们在挪用MapViewOfFile以后马上设置另一个断点,以猎取映照视图的地点(返回值一般存储在EAX中):

实例解说未知游戏文件花样的逆向剖析要领(下)

(特地说一句,一定要确保Anim.bus没有在任何处所翻开,不然,它能够会失利!)

若是胜利,映照视图的地点将寄存到EAX寄存器中,因而,请在“Registers”窗口中右键单击它,然后,单击“Follow in Dump”选项:

实例解说未知游戏文件花样的逆向剖析要领(下)

让我们直接转到0x50B91E(实行XOR的处所),并在那边设置另一个断点,然后规复游戏从而再次触发断点。

实例解说未知游戏文件花样的逆向剖析要领(下)

运用F8单步跟踪指令,直到:

  0050B931  |. 8A540A 74      |MOV DL,BYTE PTR DS:[EDX+ECX+74] ; 74h => 114

这里,ECX用作数组的计数器。这里,它会与EDX的值相加,而EDX就是a1(记着,a1是一个类)。然后,加上间隔数组开首的偏移量(74h/114)。

下面给出越发易于明白的伪代码:

    uint8_t DL = a1->xor_array[ECX]; //xor_array starts 74h into a1, and ECX is incremented after each iteration

我们来看看下面一行代码:

    0050B935  |. 03C6           |ADD EAX,ESI

EAX生存的是目的缓冲区的地点。它也会在每次迭代完毕时递增,直到“uiSize”字节被异或为止。

以后,继承在转储中跟踪EAX,并在轮回(POP ESI)指令后设置断点,规复游戏并检察转储窗口:

实例解说未知游戏文件花样的逆向剖析要领(下)

它彷佛已解密了缓冲区中的内容!

实例解说未知游戏文件花样的逆向剖析要领(下)

我们跳过33个字节,并解密了272个字节的内容。然则若是我们细致检察Data文件夹中的Anim.bus,会发明文件Anim.bus的巨细是305个字节。而272+33恰好即是……

305!这申明全部Anim.bus文件都被解密了。

那末,解密缓冲区后,我们可以或许获得哪些有效的信息呢?未几,只不外看起来更像个文件夹称号罢了。

接下来,我们可以或许编写一个简朴应用顺序,让它实行与游戏完整雷同的操纵,即运用CreateFile、OpenFileMapping和MapViewOfFile将文件映照到内存,并解密缓冲区中的内容(记着,肇端地位是缓冲区地点+33个字节!)。要想完成解密,还须要猎取用作XOR密钥的字节数组中的内容,该数组长度为4096字节(4KB)。为此,须要从新封闭并运转该游戏,并触发该指令:

    0050B931  |. 8A540A 74      |MOV DL,BYTE PTR DS:[EDX+ECX+74] ; 74h => 114

读取EDX的值,加上114,即数组开首的地点。在转储窗口中跟踪它,将该地点加上4096,即数组末端的地点。以后,只需复制这两个地点之间的统统字节,并应用Notepad++花样化统统内容便可。

若是在此历程当中碰到贫苦,可以或许接见以下地点:

· http://pastebin.com/skZKAx27

下面给出解密函数:

    bool DecryptData(uint8_t* puiBuffer, const size_t uiSize)
    {
             if (!uiSize)
             {
                     return false;
             }
    
             for (size_t i{ 0 }, j{ 0 }; i < uiSize; ++i)
             {
                     puiBuffer[i] ^= bus::crypt::XOR_ARRAY[j++];
                     if (xor_idx >= XOR_ARRAY_SIZE) { j = 0; }
             }
             return true;
    }

让我们回到IDA。我们已弄懂了这两个函数的用处:一个用于复制内存并挪用解密函数,而另一个则是解密函数自身。

既然如此,我们可以或许重命名这两个函数:

实例解说未知游戏文件花样的逆向剖析要领(下)

如许,当我们再次碰到它们时,我们就不须要再浪费时间来研讨它们了,因为我们已晓得其作用了。

如今,我们疾速总结一下前面的研讨成果:

· 大局部游戏数据都存储在.bus文件中

· 这些.bus文件可以或许同时存储多个文件,因而,它们现实上是文件存档

· 该游戏运用Gamebryo引擎,因为存储的文件好像是NIF/KFM/DDS花样的

· 游戏客户端运用CreateFile、OpenFileMapping和MapViewOfFile函数将全部.bus文件映照到内存中

· 然后,从映照视图的基地点+33字节处解密272个字节

让我们回到挪用CopyAndDecrypt的函数那边。起首,它搜检目的缓冲区是不是存在,若是不存在的话,就直接返回0。让我们直接跳到这一行:

    v11 = *(char **)(*(_DWORD *)(v5 + 104) + 268); //dest_buffer + 268

它起首猎取目的缓冲区的地点,然后,又偏移0x10C(268d)字节。下面的代码要更轻易明白一些:

    v11 = *reinterpret_cast<uint32_t*>(dest_buffer + 268); //this does the same

关于Anim.bus的情况中,v11为0。

让我们剖析下一行代码:

    v12 = 276 * *(_DWORD *)(*(_DWORD *)(v5 + 104) + 268) + 305;

我们可以或许将其简化为:

    v12 = (276 * v11) + 305;

305……是不是是有点耳熟啊?是的,这是Anim.bus的字节巨细。

如今,我们已搞清晰了两种分歧的组织:

· 第一个组织(我们称之为S1)的巨细为272字节,包罗一个字符数组情势的字符串,现实上是一个文件夹得称号。该组织的末了4个字节(偏移量268处)是一个整数(v11)。

· 第二个组织(我们称之为S2)的巨细为276字节,单个.bus文件好像可以或许存储多个这类组织。若是我们检察上面的代码,我们可以或许安全地假定.bus文件中至少有“v11”个S2组织。

· S2组织列表从偏移量305处最先。这是因为,第一个组织占用了272个字节,再加上文件的前33个字节(它们老是0),恰好即是305。

我们如今可以或许更好地相识.bus文件的元数据是怎样结构的:

OFFSET 0:零字节(巨细:33)

OFFSET 33:S1组织(尺寸:272)

OFFSET 305:第一个S2组织(巨细:276)

OFFSET 581(305+272):第二S2组织(巨细:276)

……

如今,应当更清晰为何Anim.bus的巨细只要305个字节了吧?这是因为该文件中根本就没有S2组织。

让我们继承往下剖析:

    if ( !v11 )
          {
            sub_498D74(&v45);
            v52 = 1;
            v13 = stlp_std::locale::locale((stlp_std::locale *)&Src);
            LOBYTE(v52) = 2;
            sub_42E050(v13);
            LOBYTE(v52) = 1;
            stlp_std::locale::~locale((stlp_std::locale *)&Src);
            v14 = stlp_std::locale::locale((stlp_std::locale *)&Src);
            LOBYTE(v52) = 3;
            sub_50C2A7(&v45, v14);
            LOBYTE(v52) = 1;
            stlp_std::locale::~locale((stlp_std::locale *)&Src);
            a3 = 0;
            v51 = 0;
            v49 = &v33;
            BYTE3(Src) = 0;
            BYTE3(a1) = 1;
            sub_52C06B(&v51, (int)&v33, (_BYTE *)&a1 + 3, (_BYTE *)&Src + 3, &a3, v5 + 68);
            v28 = v15;
            v27 = v15;
            LOBYTE(v52) = 4;
            Src = &v27;
            std::string::ctor_1(&v27, &v45);
            LOBYTE(v52) = 1;
            sub_52BFEC(&v41, v16, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36);
            LOBYTE(v52) = 6;
            sub_52C09F(&v42);
            LOBYTE(v52) = 7;
            sub_52BCA1(&v42);
            LOBYTE(v52) = 6;
            sub_50CA47(&v42);
            LOBYTE(v52) = 1;
            sub_50CA47(&v41);
            sub_52BCF1(a5);
            v52 = -1;
            stlp_std::string::dtor(&v45);
            return 1;
          }

这是我们如今感兴趣的局部,因为v11即是0,以是,这局部代码将被实行,然后,函数将返回。

然则等等……这真的还让我们感兴趣吗?anim.bus文件已完整解密,它看起来只是寄存了一个文件夹名。我们实在不愿望完整弄清晰客户端怎样处置惩罚统统事变的,我们只须要相识客户端怎样解密.bus文件并剖析它们,以便我们可以或许在没有客户端资助的情况下重现它并提取它们。

因而,下一步将是找到一个文件,个中包罗以下内容:

    *reinterpret_cast<uint32_t*>(dest_buffer + 268);

……不会返回0。接下来,我们须要:

起首,删除您在0x50B91E处设置的断点。

然后,将断点设置到0x52B94F处。这是初始化v11的指令。若是[EAX+0x10C](10Ch: 268)为0,那末,它就对我们没有什么资助。

按F9键规复游戏,这将再次触发CreateFileA断点。复制文件名,以防万一。再次按F9键,直到触发0x52B94F处断点。如上所述,若是[EAX+0x10C](EAX生存我们S1组织的地点)为0,我们就可以或许跳过它。

继承按F9键,直到[EAX+0x10C]不再为0为止。

准确完成统统操纵后,[EAX+0x10C]将即是0x47(或71),而且映照到内存的.bus文件的称号应当为“Anim_Mob_Zombie_Melee_Zako.bus”。依据我们之前提到的结论,这意味着文件中有71个S2组织,其地点从偏移量305处最先。

因为v11不再为0,因而,将实行以下指令:

    a1 = 0;
    if ( !v11 )
      return 1;
    a5 = 276;
    while ( 1 )
    {
      v52 = (int *)operator new(0x114u);
      v54 = 8;
      if ( v52 )
      {
        v19 = sub_52B7F1((int)v19);
        v12 = v53;
        v20 = v19;
      }
      else
      {
        v20 = 0;
      }
      v54 = -1;
      dest_buffer = (char *)v20;
      v41 = 276;
      v40 = (char *)Src + v50 + 305;
      v39 = Src;
      v21 = sub_40CF95();
      CopyAndDecrypt(v21, v40, v41, dest_buffer);
      if ( v20 )
      {
        v50 = a5;
        sub_49C45F(&v46, v20);
        v54 = 9;
        sub_498D74(&v47);
        LOBYTE(v54) = 10;
        v22 = stlp_std::locale::locale((stlp_std::locale *)&v49);
        LOBYTE(v54) = 11;
        sub_42E050(v22);
        LOBYTE(v54) = 10;
        stlp_std::locale::~locale((stlp_std::locale *)&v49);
        v23 = stlp_std::locale::locale((stlp_std::locale *)&v48);
        LOBYTE(v54) = 12;
        sub_50C2A7((int)&v47, v23);
        LOBYTE(v54) = 10;
        stlp_std::locale::~locale((stlp_std::locale *)&v48);
        v52 = (int *)(v12 + *(_DWORD *)(v20 + 264));
        v45 = &v35;
        sub_52C06B((_DWORD *)(v20 + 268), (int)&v35, (_BYTE *)(v20 + 273), (_BYTE *)(v20 + 272), &v52, v5 + 68);
        v30 = v24;
        v29 = v24;
        LOBYTE(v54) = 13;
        v52 = &v29;
        std::string::ctor_1(&v29, &v47);
        LOBYTE(v54) = 10;
        v26 = (_DWORD *)sub_52BFEC((int)&v43, v25, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38);
        LOBYTE(v54) = 15;
        sub_52C09F(v26, (int)&v44);
        LOBYTE(v54) = 16;
        sub_52BCA1(&v44);
        LOBYTE(v54) = 15;
        sub_50CA47(&v44);
        LOBYTE(v54) = 10;
        sub_50CA47(&v43);
        v27 = *(_DWORD *)(v5 + 96);
        dest_buffer = &v46;
        v41 = v27;
        if ( v27 == *(_DWORD *)(v5 + 100) )
        {
          sub_52BDBB(v41, dest_buffer);
        }
        else
        {
          sub_52C0F4(v41, dest_buffer);
          *(_DWORD *)(v5 + 96) += 24;
        }
        LOBYTE(v54) = 9;
        stlp_std::string::dtor(&v47);
        v54 = -1;
        sub_402821(&v46);
        v12 = v53;
      }
      v28 = *(_DWORD *)(v20 + 264) + *(_DWORD *)(v20 + 268);
      dest_buffer = (char *)v20;
      if ( v12 + v28 > (unsigned int)a3 )
        break;
      operator delete(dest_buffer);
      ++a1;
      a5 += 276;
      if ( a1 >= (unsigned int)v51 )
        return 1;
    }
    operator delete(dest_buffer);

我们马上注重到的第一件事是,在堆上分配了sizeof(S2) (276)字节的内存空间。sub_52B7F1好像是一个组织函数,让我们来看看其代码:

    int __usercall [email protected]<eax>(int [email protected]<esi>)
    {
      *(_BYTE *)(a1 + 272) = 0;
      *(_BYTE *)(a1 + 273) = 0;
      memset((void *)a1, 0, 261u);
      return a1;
    }

这为我们供应了S2组织方面的一些信息:

· O

· FFSET 272:未知字节(巨细:1)

· OFFSET 273:未知字节(巨细:1)

因为我们已重命名了CopyAndDecrypt函数,因而,它肯定会出如今反编译器的输出效果中,因而,我们可以或许修正响应变量的称号:

    v55 = -1;
    dest_buffer = (char *)v21;
    size = 276;
    curr_src_buffer = (char *)Src + v51 + 305;
    v40 = Src;
    v22 = sub_40CF95();
    CopyAndDecrypt(v22, curr_src_buffer, size, dest_buffer);

在这里,下面这行代码是最主要的:

    curr_src_buffer = (char *)Src + v51 + 305;

Src是映照文件视图的地点。我们晓得305是S2组织肇端地点的偏移量。至于v51,若是我们检察前面的代码,就会发明它被初始化为0。

太好了,下面最先解密S2组织!为此,请切换到OllyDbg,并单步跟踪代码,直到该挪用的参数被压入客栈为止:

    0052BACE  |. 57             |PUSH EDI
    0052BACF  |. 68 14010000    |PUSH 114
    0052BAD4  |. 8D8401 3101000>|LEA EAX,DWORD PTR DS:[ECX+EAX+131]
    0052BADB  |. 50             |PUSH EAX
    0052BADC  |. 51             |PUSH ECX
    0052BADD  |. E8 B314EEFF    |CALL pbclient.0040CF95
    0052BAE2  |. 59             |POP ECX                                 ; |
    0052BAE3  |. 50             |PUSH EAX                                ; |Arg1
    0052BAE4  |. E8 B9FEFDFF    |CALL pbclient.0050B9A2                  ; \pbclient.0050B9A2
    0052BAE9  |. 85FF           |TEST EDI,EDI

猎取EDI的值,并在转储窗口中跟踪它,这就是我们的目的缓冲区。如今,我们只须要单步实行代码,直到实行对CopyAndDecrypt(pbclient.0050B9A2)的挪用为止。

布拉格提案假借安全牌封锁华为? 专家:关注真5G安全

日前,美国联合众多西方国家在布拉格召开5G安全大会,拟定“布拉格提案”。提案直指“网络安全不仅是技术问题,应考虑第三方国家对供应商施加影响的‘总体风险’”,这被舆论视为“美国封锁华为5G”之举。5月6日,外交部发言人耿爽在例行记者会上表示,将政治因素人为引入5G开发,不仅不利于5G的发展,也有悖公平竞争原则。 不久前,华为曾对媒体回应称:网络安全本质上还是技术问题,技术问题要靠技术手段来解决。我们坚信任何未来的安全原则都应以可验证的事实和技术数据为基础,而不是基于供应商的属地和意识形态来进行鉴别。 对于5G安全,来自360的安全专家黄琳也对记者表示,基础通信网络设施中的相关安全问题是一个长期存在的话题,并不是因为5G新引入的。为了5G的发展,呼吁大家关注“真5G安全”——真正属于技术层面的安全问题。 黄琳指出,关键基础设施并不是从5G时代才开始连接网络的。以电力行业为例,目前的电网系统有自己的专网,现在大量在线运行的是2.5G的技术,基于4G的专网正在逐步部署之中。所以,关键基础设施连接无线网络的风险,过去就存在,现在也存在,将来依然存在。 实际上,无论是伊朗核电站震网病毒、乌克兰电网攻击,还是最近的委内瑞拉大停电,

下面展现的是挪用后的转储窗口:

实例解说未知游戏文件花样的逆向剖析要领(下)

在我看来,已不言而喻了!我们之前发明未加密的模版/纹理/动画都是存储在.bus文件中的。这个S2组织存储归档中文件的文件名。同时,因为我们也晓得在一个存档中可以或许有多个S2组织,这意味着每一个文件必需有一个这类组织。那末,我们怎样能力晓得有多少个S2组织呢?

    v11 = *reinterpret_cast<uint32_t*>(dest_buffer + 268); //S1 structure

对!我们如今可以或许将v11重命名为“file_count”,因为这就是其真正用处。从这里可以或许看出,Anim.bus中并没有存储文件,这恰好诠释了file_count被设置为0的缘由。

继承单步跟踪,直到运转至以下代码:

    0052BB62  |. 8B87 08010000  |MOV EAX,DWORD PTR DS:[EDI+108] ; + 264
    0052BB68  |. 03C6           |ADD EAX,ESI

我们如今晓得,该组织包罗4个字节的数据(一个整数),这恰好就是我们所感兴趣得,它们位于偏移量264处。然则,在这个组织中这个整数被设置为0。

EAX中寄存的就是这个表达式的效果(我们之前已考核过了,还记得吗?):

    v12 = (276 * v11) + 305;
    //Which we now know is...
    v12 = (sizeof(S2) * file_count) + sizeof(S1) + 33;

那v12终究是什么呢?它是S2组织列表末端的偏移量。下面,让我们将它添加到.bus元数据结构中:

[ METADATA]

OFFSET 0:零字节(巨细:33)

OFFSET 33:S1组织(尺寸:272)

OFFSET 305:第一个S2组织(巨细:276)

OFFSET 581(305+272):第二S2组织(巨细:276)

……

OFFSET v12:S2组织的尾部

让我们继承单步跟踪代码,直到跟踪至以下代码:

    0052BB75  |. 8D43 44        |LEA EAX,DWORD PTR DS:[EBX+44]
    0052BB78  |. 50             |PUSH EAX
    0052BB79  |. 8D45 EC        |LEA EAX,DWORD PTR SS:[EBP-14]
    0052BB7C  |. 50             |PUSH EAX
    0052BB7D  |. 8D87 10010000  |LEA EAX,DWORD PTR DS:[EDI+110] ;+ 272
    0052BB83  |. 50             |PUSH EAX
    0052BB84  |. 8D87 11010000  |LEA EAX,DWORD PTR DS:[EDI+111] ;+ 273
    0052BB8A  |. 50             |PUSH EAX
    0052BB8B  |. 8D87 0C010000  |LEA EAX,DWORD PTR DS:[EDI+10C] ;+ 268
    0052BB91  |. E8 D5040000    |CALL pbclient.0052C06B

风趣的是,它正在运用我们的一些S2组织数据并将其地点作为参数传递给该挪用。下面,让我们单步进入pbclient.0052C06B(F7):

    0052C06B  /$ 55             PUSH EBP
    0052C06C  |. 8BEC           MOV EBP,ESP
    0052C06E  |. 51             PUSH ECX
    0052C06F  |. 8B00           MOV EAX,DWORD PTR DS:[EAX]
    0052C071  |. FF75 14        PUSH DWORD PTR SS:[EBP+14]
    0052C074  |. 8365 FC 00     AND DWORD PTR SS:[EBP-4],0
    0052C078  |. 8906           MOV DWORD PTR DS:[ESI],EAX
    0052C07A  |. 8B45 08        MOV EAX,DWORD PTR SS:[EBP+8]
    0052C07D  |. 8A00           MOV AL,BYTE PTR DS:[EAX]
    0052C07F  |. 8846 04        MOV BYTE PTR DS:[ESI+4],AL
    0052C082  |. 8B45 0C        MOV EAX,DWORD PTR SS:[EBP+C]
    0052C085  |. 8A00           MOV AL,BYTE PTR DS:[EAX]
    0052C087  |. 8846 08        MOV BYTE PTR DS:[ESI+8],AL
    0052C08A  |. 8B45 10        MOV EAX,DWORD PTR SS:[EBP+10]
    0052C08D  |. 8B00           MOV EAX,DWORD PTR DS:[EAX]
    0052C08F  |. 8D4E 10        LEA ECX,DWORD PTR DS:[ESI+10]
    0052C092  |. 8946 0C        MOV DWORD PTR DS:[ESI+C],EAX
    0052C095  |. FF15 442DCD00  CALL DWORD PTR DS:[<&stlport.5.2.??0?$ba>;  [email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@[email protected]@@Z
    0052C09B  |. 8BC6           MOV EAX,ESI
    0052C09D  |. C9             LEAVE
    0052C09E  \. C3             RETN

这好像是在竖立另一种组织。这对我们很主要吗?如今来看,彷佛不是的……我们可以或许依据须要制造本身的附加组织,但我们最感兴趣的是这些字段的现实寄义,别的,因为这类组织能够对我们有所资助,以是,我们无妨记着它。

如今我们对S2组织有了更多的相识:

[S2 STRUCTURE]

OFFSET 0:文件名(巨细:264(它可以或许更小一些,该数字只是为了知足以后的请求))

OFFSET 264 [EDI + 0x108]:未知整数(巨细:4)

OFFSET 268 [EDI + 0x10C]:未知整数(巨细:4)

OFFSET 272 [EDI + 0x110]:未知字节(巨细:1)

OFFSET 273 [EDI + 0x111]:未知字节(巨细:1)

等一下……若是细致搜检每一个偏移量,就会发如今偏移量268处有一个值:0xA53F(十进制为42303)。而且,若是我们跳到这一行:

    v28 = *(_DWORD *)(v20 + 264) + *(_DWORD *)(v20 + 268);

这是什么意思呢?在偏移量264处也有一个值!将这两个偏移量处的值加在一起,若是效果大于“a3”(这是传递给以后函数的第三个参数),则会中缀。

如今,a3多是什么?让我们找到对以后函数(sub_52b8ba)的交织援用:

    if ( (unsigned __int8)sub_52B8BA(v12, (void *)v13, v57, a1 + 32, (int)&v48) )

a3是第三个参数,那末,v57是什么呢?现实上,若是我们向上翻看一下……

    v57 = GetFileSize(v7, 0);

这里就不消多诠释了吧?v57只是这个bus花样的文件的巨细,以是,文件的巨细将作为第三个参数传递给sub_52B8BA。

如今,我们可以或许更新一下参数称号了:

    char __stdcall sub_52B8BA(int a1, void *Src, int uiFileSize, int a4, int a5)

向下转动,我们将看到:

    v28 = *(_DWORD *)(v20 + 264) + *(_DWORD *)(v20 + 268);
     dest_buffer = (char *)v20;
     if ( v12 + v28 > (unsigned int)uiFileSize )

别的,到现在为止,我们对以后迭代的相识是:

ITERATION 0: OFFSET 264: 0 - OFFSET 268: 42303

若是我们看一下轮回的其余局部:

    ++a1;
    a5 += 276;
    if ( a1 >= (unsigned int)v51 )
       return 1;

若是向上翻看代码,会发明v51现实上就是我们的文件数。

我们如今晓得该函数将返回,若是知足以下前提的话:

·  [S2 + 264] + [S2 + 268] > 文件巨细

·  i (a1) >= 文件数目

别的,a5的增量为sizeof(S2) (276)。若是我们向上翻看,就会发明它之前被初始化为雷同的值(276)。

因为a1在每次迭代时期都邑递增,以是,我们可以或许将其视为for轮回:

    //pseudocode
    for(size_t i{ 0 }, j{ 0 }; i < file_count; ++i, j += sizeof(S2))
    {
        /*
        Decrypt data, ...
        */
       
        if(value_at_offset264 + value_at_offset268 > uiFileSize)
        {
          break;
        }
    }

让我们更新我们对迭代的认知:

ITERATION 0: OFFSET 264: 0 - OFFSET 268: 42303 - a5: 552 (276 + 276)

如今,最须要体贴的是第二次迭代。

    dest_buffer = (char *)v20;
    v41 = 276;
    v40 = (char *)Src + v50 + 305;

若是细致搜检代码,我们就会发明v51在函数开首局部被初始化为0,以后,它会取a5的值(即552)。

让我们返回OllyDbg并继承步进调试,直到轮回完毕为止:

    0052BACE  |. 57             |PUSH EDI
    0052BACF  |. 68 14010000    |PUSH 114
    0052BAD4  |. 8D8401 3101000>|LEA EAX,DWORD PTR DS:[ECX+EAX+131]
    0052BADB  |. 50             |PUSH EAX
    0052BADC  |. 51             |PUSH ECX
    0052BADD  |. E8 B314EEFF    |CALL pbclient.0040CF95
    0052BAE2  |. 59             |POP ECX                                 ; |
    0052BAE3  |. 50             |PUSH EAX                                ; |Arg1
    0052BAE4  |. E8 B9FEFDFF    |CALL pbclient.0050B9A2                  ; \pbclient.0050B9A2

一样,EDI是我们的目的缓冲区,以是,我们须要读取其值,并在转储中跟踪它。

不出所料,它是另一个文件名。我们更感兴趣的是偏移264和268处的值。记着,这是第一次迭代:

ITERATION 0: OFFSET 264: 0 - OFFSET 268: 42303 - a5: 552 (276 + 276)

如今,若是我们再次搜检这些偏移量处的值,我们会看到:

ITERATION 0: OFFSET 264: 42303 - OFFSET 268: 12125

这是不是变得更清晰了?若是赓续反复这些轮回,就可以清晰地看到一个形式。

OFFSET 264:存档中文件开首的地位

OFFSET 268:文件的长度

如许,我们就可以或许进一步更新我们的S2组织了!

OFFSET 0:文件名(巨细:264。实在它可以或许更小,这里取这个值只是便于申明)

OFFSET 264 [EDI + 0x108]:文件中的地位(巨细:4)

OFFSET 268 [EDI + 0x10C]:文件长度(巨细:4)

OFFSET 272 [EDI + 0x110]:未知字节(巨细:1)

OFFSET 273 [EDI + 0x111]:未知字节(巨细:1)

若是在存档中的地位为0的话,则没有任何意义,因为它必需与某些器械相关联……如今,无妨回忆一下前面提到的结构:

[ METADATA]

OFFSET 0:零字节(巨细:33)

OFFSET 33:S1组织(巨细:272)

OFFSET 305:第一个S2组织(巨细:276)

OFFSET 581(305 + 272):第二S2组织(巨细:276)

……

OFFSET v12:S2组织的尾部

因而,v12很多是现实文件数据的肇端地位!我们如今有充足的信息来考证这一点:

· 我们晓得.bus文件“Anim_Mob_Zombie_Melee_Zako.bus”包罗71个文件

· S2组织的巨细为276,因而276 * 71 = 19596

· 我们必需加上S1组织的巨细:19596 + 272 = 19868

· 末了,我们必需加上33个零字节:19868 + 33 = 19901

让我们运用HexEdit翻开Anim_Mob_Zombie_Melee_Zako.bus并转到该地位,然后,输入目的地点:

实例解说未知游戏文件花样的逆向剖析要领(下)

我们将来到这里:

实例解说未知游戏文件花样的逆向剖析要领(下)

它确实是文件的肇端地位。我们晓得文件的巨细是42303字节,以是,让我们将这个值与19901相加:

实例解说未知游戏文件花样的逆向剖析要领(下)

又一个文件!

虽然现在好像还不太显着,但我们已具有制造提取器所需的统统信息。下面,让我来展现一些你能够已想到的事变:

    //S1 Structure + 33 bytes
    struct FILE_HEADER
    {
             char data[33];
             char folder_path[256]; //0x0021
             char pad[12]; // 0x100
             size_t file_count; //0x10C
    }; //Size: 0x131 (305)
 
    #pragma pack(push, 1)
    S2 Structure
    struct FILE_INFO
    {
             char path[256]; //0x0000
             char pad[8]; //0x0100
             uint32_t offset; //0x0108
             uint32_t length; //0x010C
             uint8_t unk_01; //0x0110
             bool encrypted; //0x0111
             uint8_t unk_03; //0x0112
             uint8_t unk_04; //0x0113
    }; //Size: 0x114 (276)
    #pragma pack(pop)

下面,让我们直接运用响应的称号,而不是S1和S2,如许代码会更轻易明白。

那末,该怎样制造从.bus存档中提取文件的顺序呢?下面,让我来细致加以诠释:

· 运用MapViewOfFile将全部文件映照到内存中

· 解密位于映照视图的基址+33处的S1组织,而file_count值则可以或许指出该存档中有多少个FILE_INFO组织。现实上,FILE_INFO组织的数目与文件数目是一致的。

· 若是文件数目不为0,则让偏移量指向FILE_INFO组织列表的开首处:

    sizeof(FILE_HEADER); //305

遍历统统FILE_INFO组织:

    std::vector<FILE_INFO> v_info;
    for (uint32_t i{ 0 }; i < header.file_count; ++i)
    {
             auto fi = *reinterpret_cast<FILE_INFO*>(sizeof(FILE_HEADER) + (i * sizeof(FILE_INFO));
             v_info.push_back(fi);
    }

运用以下公式盘算指向文件数据开首的偏移量:

    sizeof(FILE_HEADER) + (file_count * sizeof(FILE_INFO))

如今,我们就可以或许读取每一个文件了,比方:

    const size_t file_data_offset{ sizeof(FILE_HEADER) + (header.file_count * sizeof(FILE_INFO) };
    size_t processed_size { 0 };
    for (const auto& it : v_info)
    {
             auto src_buffer = reinterpret_cast<uint8_t*>(file_data_offset + processed_size);
             auto dest_buffer = std::make_unique<uint8_t[]>(it.length);
             memcpy_s(dest_buffer.get(), it.length, src_buffer, it.length);
             /*
             The current file data will now be contained in dest_buffer. You can implement your own function that dumps it to a file and call it here.
             */
      processed_size += it.length;
      //Also, don't forget error checking, etc! I am only showing this as an example
    }

若是您想晓得提取各个文件所需的细致地位,请检察FILE_INFO组织中的“path”字段。

实例解说未知游戏文件花样的逆向剖析要领(下)

您可以或许运用这些信息从新建立全部目次组织。

提取操纵完成以后,可以或许在数据文件夹的根目次下面看到以下内容。


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明实例解说未知游戏文件花样的逆向剖析要领(下)
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址