美文网首页
菱菱邦sd字段白盒AES逆向

菱菱邦sd字段白盒AES逆向

作者: ever_hu | 来源:发表于2022-01-07 10:59 被阅读0次

菱菱邦sd字段白盒AES逆向

decheckcode逆向

so初步分析

ida打开libencrypt.so,函数窗口搜索Java

image-20211230145549076

说明是静态注册的,打开checkcode发现有近2000行代码,那还是先看decheckcode吧。

image-20220106165938633

77上按r将其转为字符

image-20220106170045809

这就很明显了,由于sd字符串都是以M开头的,所以调用的是aes_decrypt1_ptr

image-20220106211253902 image-20220106170210672

看起来是个AES-128-CBC解密,看看CWAESCipher::WBACRAES128_DecryptCBC

image-20220106170323460

结构很清晰了,继续看看

image-20220106211733714 image-20220106211821169

this的值是off_1ADC0

image-20220106212010060 image-20220106212117547 image-20220106212202604
unidbg分析

so初步看完了,接下来就是结合unidbg进行分析

先hook看看base64_decode

image-20220106215444542

由于base64_decode多次被调用到,所以选择先在sub_138AC下断点

public LingLingBang() {
  //...
    emulator.attach().addBreakPoint(module.base + 0x138AC+1);
}

然后输入blr在函数返回处下断点,c继续运行到函数返回处,这时候输入b0xe535base64_decode函数下断点,c继续运行到base64_decode入口

image-20220107093354896

看看r0的数据

image-20220107093023968

blr在函数返回处下断点,c继续运行到函数返回处,看看原r1的值。

image-20220107093448376

和cyberchef对比一下

image-20220107093512475

是标准base64实现

接下来就是看看aes的实现,尝试网上搜索WBACRAESWBACR AES

image-20220106221231517 image-20220106221514540

从搜索结果看,是白盒AES,这就有点麻烦了。这样一来,我们无法直接拿到AES的keyiv,然后验证。虽然块解密的过程和标准AES实现不太一样,但是整体流程还是一样的。

AES-CBC-decrypt image-20220107095905688

接下来hook一下CWAESCipher::WBACRAES_DecryptOneBlock,看看块解密的输入输出

emulator.attach().addBreakPoint(module.base + 0x5910+1);
image-20220107100254521

和之前base64的结果对比可以看出,这就是最后一块的数据。

blr在函数返回处下断点,c继续运行到函数返回处,看看原r1的值。

image-20220107100416064

这就是最后一块解密后的数据,我们根据AES-CBC的流程,把它和前一块的数据进行异或试试。

s0 = bytes.fromhex('75 9E 38 5F F2 04 21 EC E9 11 8F EF C7 D9 BF 6D')
s1 = bytes.fromhex('4D AC 0C 0F B6 31 56 AD A8 50 CE D2 C3 DD BB 69')
s2 = bytes(map(lambda x:x[0]^x[1], zip(s0, s1)))
image-20220107100853991

数据正确,这样一来,我们只要解决块解密,就能逆向出算法了。

C++还原块解密

奈何想从白盒AES里找到key还是很困难的,所以我选择把块解密的方法抠出来,用C++实现,导出为dll文件,然后python再调用dll文件。实在是无能为力了。。


#include <stdio.h>
#include <string.h>

typedef          char   int8;
typedef   signed char   sint8;
typedef unsigned char   uint8;
typedef          short  int16;
typedef   signed short  sint16;
typedef unsigned short  uint16;
typedef          int    int32;
typedef   signed int    sint32;
typedef unsigned int    uint32;

#define _BYTE  uint8
#define _WORD  uint16
#define _DWORD uint32
#define _QWORD uint64

#define LOBYTE(x)   (*((_BYTE*)&(x)))   // low byte
#define LOWORD(x)   (*((_WORD*)&(x)))   // low word
#define LODWORD(x)  (*((_DWORD*)&(x)))  // low dword
#define HIBYTE(x)   (*((_BYTE*)&(x)+1))
#define HIWORD(x)   (*((_WORD*)&(x)+1))
#define HIDWORD(x)  (*((_DWORD*)&(x)+1))
#define BYTEn(x, n)   (*((_BYTE*)&(x)+n))
#define WORDn(x, n)   (*((_WORD*)&(x)+n))
#define BYTE1(x)   BYTEn(x,  1)         // byte 1 (counting from 0)
#define BYTE2(x)   BYTEn(x,  2)
#define BYTE3(x)   BYTEn(x,  3)
#define BYTE4(x)   BYTEn(x,  4)
#define BYTE5(x)   BYTEn(x,  5)
#define BYTE6(x)   BYTEn(x,  6)
#define BYTE7(x)   BYTEn(x,  7)
#define BYTE8(x)   BYTEn(x,  8)
#define BYTE9(x)   BYTEn(x,  9)
#define BYTE10(x)  BYTEn(x, 10)
#define BYTE11(x)  BYTEn(x, 11)
#define BYTE12(x)  BYTEn(x, 12)
#define BYTE13(x)  BYTEn(x, 13)
#define BYTE14(x)  BYTEn(x, 14)
#define BYTE15(x)  BYTEn(x, 15)
#define WORD1(x)   WORDn(x,  1)
#define WORD2(x)   WORDn(x,  2)         // third word of the object, unsigned
#define WORD3(x)   WORDn(x,  3)
#define WORD4(x)   WORDn(x,  4)
#define WORD5(x)   WORDn(x,  5)
#define WORD6(x)   WORDn(x,  6)
#define WORD7(x)   WORDn(x,  7)


_BYTE unk_18D8E[] = {0,0,1,3,2,2,3,1};

_BYTE invFirstRoundTable_auth1[] = {0x8d,0xba,0xd5,/* 省略 */0xd9,0x62,0x74,0xb8}; // invFirstRoundTable_auth1

_BYTE byte_invRoundTables_auth1[] = {0,0,0,0,/* 省略 */93,241,194,24,79};
_DWORD* invRoundTables_auth1 = (_DWORD*)byte_invRoundTables_auth1;


_BYTE invXorTables_auth1[] = {0, 0, 0, /* 省略 */6, 7, 13, 3, 11};

// extern "C" _declspec(dllexport) int WBACRAES_DecryptOneBlock(unsigned __int8 *a2, unsigned __int8 *a3, int a4);

int WBACRAES_DecryptOneBlock(unsigned __int8 *a2, unsigned __int8 *a3, int a4)
{
  unsigned __int8 (*v5)[8]; // r3
  int v6; // r11
  char *v7; // r1
  int v8; // r3
  _DWORD *v9; // r6
  int j; // r2
  int v11; // r10
  int v12; // r0
  int v13; // r0
  void **v14; // r8
  int v15; // r0
  int k; // r2
  char *v17; // r3
  char v18; // r5
  int v19; // r0
  char v20; // r1
  int v21; // r6
  unsigned int v22; // r5
  int v23; // r1
  char v24; // r8
  _BYTE *v25; // r12
  int v26; // r12
  int v27; // r7
  _BYTE *v28; // r8
  int v29; // r8
  int v30; // r7
  int v31; // r2
  char *v32; // r1
  int i; // r3
  _BYTE *v34; // r5
  int v35; // r8
  char *v36; // r6
  int v37; // r8
  int l; // r3
  int m; // r2
  int v41; // [sp+0h] [bp-E0h]
  _BYTE *v42; // [sp+4h] [bp-DCh]
  int v43; // [sp+8h] [bp-D8h]
  int v44; // [sp+Ch] [bp-D4h]
  int v47; // [sp+1Ch] [bp-C4h]
  char v49[4]; // [sp+30h] [bp-B0h] BYREF
  int v50; // [sp+34h] [bp-ACh]
  _DWORD s[42]; // [sp+38h] [bp-A8h] BYREF

  memset(s, 0, 0x20u);
//   v44 = CSecFunctProvider::PrepareAESMatrix((CSecFunctProvider *)a2, (unsigned __int8 *)&word_10, (int)s, v5);
  for (int i=0; i!=4; i++) {
    for(int j=0; j!=4; j++) {
      *((_BYTE *)s + i + 8 * j) = *((_BYTE *)a2 + 4 * i + j);
    }
  }
  // for (int i=0; i<2; i++) {
  //     for (int j=0; j<16; j++) {
  //         printf("%02x ", *((_BYTE *)s + i*16 + j));
  //     }
  //     printf("\n");
  // }
  //     printf("\n");
  
  v44 = 0;
  if ( !v44 )
  {
    v6 = 10;
    while ( v6 >= a4 )
    {
      if ( !--v6 )
      {
        if ( a4 == 1 )
        {
          s[8] = s[0];
          s[9] = s[1];
          s[10] = s[2];
          s[11] = s[3];
          s[12] = s[4];
          s[13] = s[5];
          s[14] = s[6];
          s[15] = s[7];
          v30 = 1;
          do
          {
            v31 = 0;
            v32 = (char *)&unk_18D8E;
            for ( i = 0; i != 4; ++i )
            {
            //if ( v30 == 1 || v30 != 2 )
            //{
                v34 = invFirstRoundTable_auth1;
                v35 = (v32[1] + (_BYTE)v6) & 3;
            //}
            //   else
            //   {
            //     v34 = &invFirstRoundTable_auth2_ptr;
            //     v35 = (v32[1] + (_BYTE)v6) & 3;
            //   }
              v32 += 2;
              v36 = (char *)&s[2 * i + 32] + v35;
              v37 = i + 4 * v35;
              *((_BYTE *)s + v6 + v31) = *((_BYTE *)v34 + 256 * v37 + (unsigned __int8)*(v36 - 96));
              v31 += 8;
            }
            ++v6;
          }
          while ( v6 != 4 );
        }
        break;
      }

      v7 = (char *)&unk_18D8E;
      v8 = 0;
      v47 = 4 * v6;
      do
      {
        v9 = &s[2 * v8 + 32];
        for ( j = 0; j != 4; ++j )
        {
          v11 = 1;
          // if ( v11 == 1 )
          // {
            v15 = (v7[1] + (_BYTE)j) & 3;
            v50 = invRoundTables_auth1[256 * (v8 + 4 * (v15 + v47)) + *((unsigned __int8 *)v9 + v15 - 128)];
          // }
        //   else
        //   {
        //     v12 = (v7[1] + (_BYTE)j) & 3;
        //     v13 = *((unsigned __int8 *)v9 + v12 - 128) + ((v8 + 4 * (v12 + v47)) << 8);
        //     if ( v11 == 2 )
        //       v14 = &invRoundTables_auth2_ptr;
        //     else
        //       v14 = &invRoundTables_auth1_ptr;
        //     v50 = *((_DWORD *)*v14 + v13);
        //   }
          s[4 * v8 + 16 + j] = v50;
        }
        ++v8;
        v7 += 2;
      }
      while ( v8 != 4 );

      for ( k = 0; k != 4; ++k )
      {
        v17 = (char *)&s[16] + k;
        v41 = 0;
        do
        {
          v43 = 0;
          v49[1] = v17[16];
          v18 = *v17;
          v19 = 96 * v6 + 24 * v41;
          v49[2] = v17[32];
          v20 = v17[48];
          v21 = v18 & 0xF;
          v49[0] = v18;
          v22 = v18 & 0xF0;
          v49[3] = v20;
          v23 = 6 * k;
          do
          {
            v24 = v49[++v43];
            // if ( v11 == 1 || v11 != 2 )
              v25 = invXorTables_auth1;
            // else
            //   v25 = &invXorTables_auth2_ptr;
            v42 = v25;
            v26 = v23 + 1;
            v27 = v24 & 0xF0 | (v22 >> 4);
            LOBYTE(v21) = v42[256 * (v19 + v23) + (v21 | (16 * (v24 & 0xF)))] & 0xF;
            // if ( v11 == 1 || v11 != 2 )
              v28 = invXorTables_auth1;
            // else
            //   v28 = &invXorTables_auth2_ptr;
            v23 += 2;
            v22 = (unsigned __int8)(16 * *((_BYTE *)v28 + 256 * (v26 + v19) + v27));
          }
          while ( v43 != 3 );
          v29 = v41;
          v17 += 4;
          *((_BYTE *)&s[2 * k] + v41++) = v22 | v21;
        }
        while ( v29 != 3 );
      }
    }
    for ( l = 0; l != 4; ++l )
    {
      for ( m = 0; m != 4; ++m )
        a3[4 * l + m] = *((_BYTE *)&s[2 * m] + l);
    }
  }
  return v44;
}

int main() {
    _BYTE a2[16] = {0x8D, 0x63, 0xD7, 0x56, 0xDB, 0x55, 0xCD, 0x06, 0x56, 0x70, 0xB9, 0x74, 0xE6, 0x24, 0xB5, 0x86};
    _BYTE a3[16];
    int ret;
    
    for(int i=0; i<16; i++){
        printf("%02X ", a2[i]);
    }
    printf("\n");
    ret = WBACRAES_DecryptOneBlock(a2, a3, 1);
    
    // 75 9E 38 5F F2 04 21 EC E9 11 8F EF C7 D9 BF 6D
    for(int i=0; i<16; i++){
        printf("%02X ", a3[i]);
    }
    printf("\n");

    return 0;
}

其中有些分支由于入参的原因,是不会进入的,所以直接去掉

image-20220107104958267

然后就是编译,运行,看看代码有没有问题

image-20220107105509651

和unidbg上是一样的,接下来就是把它编译成dll文件,供python调用了。

编译生成dll文件

visual studio选择新建项目->动态连接库

image-20220107102627044

右键文件,选择属性,修改为不使用预编译头。

image-20220107102934260

接下来就是把之前的C++代码复制到文件里,需要添加一行代码

extern "C" _declspec(dllexport) int WBACRAES_DecryptOneBlock(unsigned __int8 *a2, unsigned __int8 *a3, int a4);

由于我的python和windows是64位版本的,所以还要设置一下生成的版本

image-20220107104016304

最后就是选择生成->生成解决方案就行了。

python调用dll文件

接下来就是用python调用dll文件

import base64
import gzip

from ctypes import *

dll_path = r".\Dll1.dll"

cryptor = cdll.LoadLibrary(dll_path)
decrypt_block = cryptor.WBACRAES_DecryptOneBlock
decrypt_block.restype = c_int


def aes_decrypt(data):
    previous = bytes([0]*16)
    bucket = []
    for idx in range(0, len(data), 16):
        block = data[idx: idx+16]
        din = c_char_p(block)
        dout = c_char_p(bytes([0]*16))
        decrypt_block(din, dout, 1)
        out = bytes(map(lambda x: x[0] ^ x[1], zip(dout._objects, previous)))
        print(out)
        bucket.append(out)
        previous = block
    output = b''.join(bucket)
    output = output[:-output[-1]]
    return output

def decrypt(data):
    data1 = base64.b64decode(data[1:])
    data3 = aes_decrypt(data1)
    print(data3)
    data4 = base64.b64decode(data3)
    print(data4)
    data5 = gzip.decompress(data4)
    print(data5)

def test():
    data = 'MLj4JOPMKPdGeytkeBPy/TvgGCgIOWegKndYgDVbmrctTnrov1z7Uei09BKfKhlW/xnLMOg1HaIN7llJqgPFjmayzV1OI2Rqd7heXxQw1VAmVCe6yXyiiyQ5OHaZ7hhRm5BRpmlFqgY1ebjsv8WPnnw+OZXydnPcySv4Lt5bwmHrOQfygHI7KhDFN40NU8/Csuumues12zRmtaQH9ycfD8fnu27yxX0UQeZBL/VPS+UVNrAwPtjFWrahQztLD3btpjWPXVttVzQZWcLl05iS1hg=='
    decrypt(data)


if __name__ == '__main__':
    test()
image-20220107103545173

至此,AES-CBC-decrypt的逆向也就完成了,代码仅供把玩。

相关文章

  • 菱菱邦sd字段白盒AES逆向

    菱菱邦sd字段白盒AES逆向 decheckcode逆向 so初步分析 ida打开libencrypt.so,函数...

  • 菱菱邦signature及sd参数分析

    菱菱邦signature及sd参数分析 headers有signature,请求参数和返回结果里有sd apk有壳...

  • 【羊毛】菱菱邦13元话费,250元加油券

    菱菱邦是由上汽通用五菱开发的一个针对五菱宝骏车主专属定制化的线上销售、服务平台;通过各大应用商店都可以下载,话费和...

  • 白盒AES加密DFA逆向

    白盒AES加密DFA逆向 样本下载[https://pan.baidu.com/s/1QK_-qI92w2w4Sg...

  • 师父,你真好看

    白菱贪图她师父的美色,最喜欢做的事情就是撩她师父。 自从白菱上了紫阳山,李垣的耳边总是围绕着白菱的声音:“师父,我...

  • 嘉兴南湖菱,为什么角是圆的?可做零食、可当水果、可炒菜

    江南可采菱,南湖菱盛名。​ 采菱南湖间。 采菱南湖东,采菱南湖叶西,采菱南湖南,采菱南湖北。 我这么皮,把汉乐府这...

  • 菱角有多种形状,其中四角刻叶菱最为珍稀

    菱角有多种形状,其中四角刻叶菱最为珍稀 花木君 菱角大家都知道,又名腰菱、水栗、水菱、风菱、乌菱、菱实、芰实等等,...

  • 2019-06-24

    一日一景 菱叶萦波起,菱影映波中。 日暮菱歌归,晨露菱唱远。

  • 我,的旅行2

    小菱:啊!有鱼网! 我:小心小菱,鱼网在你那边。 小菱:啊啊啊啊啊啊! 我:怎么了? 小菱:啊…………… 我:小菱...

  • 菱菱傍水生

    ♥ LingDian 睡前故事 菱菱会突然地跑起来,似一阵风状,卷起整片绿苇。在湖水里游来游去激起浪来。 她真是一...

网友评论

      本文标题:菱菱邦sd字段白盒AES逆向

      本文链接:https://www.haomeiwen.com/subject/xfljcrtx.html