菱菱邦sd字段白盒AES逆向

菱菱邦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的逆向也就完成了,代码仅供把玩。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容