xx度灰app加密算法分析还原

文章正文
发布时间:2025-11-19 05:30

使用mt管理器重新签名后运行闪退

二、Java层静态分析

定位启动Activity

<activity android:name="com.tencent.mm.ui.LaunchActivity" android:screenOrientation="1" android:theme="@style/AppTransparentTheme">      <intent-filter>        <action android:name="android.intent.action.MAIN"/>        <category android:name="android.intent.category.LAUNCHER"/>      </intent-filter>    </activity>

分析onCreate函数

LauncheActivity继承自BaseActivity,BaseActivity中调用了几个函数,经分析确定了w5()为关键函数:

@Override  // android.support.v7.app.AppCompatActivity protected void onCreate(@nullable Bundle arg2) {    this.Z5();    super.onCreate(arg2);    try {        this.a = android.databinding.f.l(this.O5(), this.Q5());        this.V5();        com.tencent.mm.base.f.c().a(this);        this.W5(); // 关键函数        this.U5();        this.b6();    }    catch(Exception v2) {        v2.printStackTrace();    } }

跟踪w5()

在前面测试过程中有一个信息是:修改了签名后的app并不会直接闪退,而是在申请完相关使用权限后才会退出,如果没有通过权限的申请,会自动正常退出,而不是闪退;在w5()中找到了相关权限申请的函数T6()

@Override  // com.tencent.mm.base.BaseActivity public void W5() {    /* other code */    int v0_1 = this.checkSha1(this) ? 1 : 2;    com.tencent.mm.network.d.h = this.D6(this) + ":" + v0_1;    org.greenrobot.eventbus.c.f().t(this);    this.I = new LaunchModel(this);    this.z6();    this.T6(); // 在T6中进行权限申请    String v0_2 = i1.k().E();    if(!TextUtils.isEmpty(v0_2)) {        com.tencent.mm.l.j.d().v(((UserInfoBean)JSON.parseObject(v0_2, UserInfoBean.class)));    } }

在T6()中如果没有赋予应用相关权限,则会结束应用,否则进入B6()

public void T6() {    /* other code */    // if no permission, return and eixt    LaunchActivity.this.B6();

跟踪B6()后续的一系列函数调用,最终定位到一个向服务器发送请求的函数

public void q(String arg6) {    d.D1().N4(arg6);    d.D1().d4("http://xxxx/.../xxx", d.D1().x1(), new b("/api/xxx/xxx") {    } }

开启Fiddler抓报后,发现应用自启动到闪退没有发送任何请求,d4()函数中在发送请求前进行了一系列的数据操作

public void d4(String arg2, HttpParams arg3, com.tencent.mm.network.b arg4) {    ((PostRequest)((PostRequest)((PostRequest)((PostRequest)((PostRequest)OkGo.post(arg2).tag(arg4.b())).upJson(this.s2(arg3).toJSONString())).headers("token", i1.k().w())).cacheKey(this.C0(arg4.a()))).cacheMode(CacheMode.FIRST_CACHE_THEN_REQUEST)).execute(arg4); }

upJson()参数即为上传的数据,经过了s2()的处理,跟进s2(),最终数据的加密封装在com.szcx.lib.encrypt.c.k()中进行

public String k(String arg5) throws JSONException {    String v5 = this.e(arg5);    JSONObject v2 = new JSONObject();    v2.put("timestamp", "1663503240");    v2.put("_ver", "v1");    v2.put("data", v5);    v2.put("sign", this.j(a.e("_ver=v1&data=" + v5 + "×tamp=" + "1663503240" + this.e)));    return v2.toString(); }

经过对正常app运行时的抓包比较,此处的参数与实际一致,在e()中队数据进行了加密,最终调用native函数进行加密

public String f(String arg1, String arg2) {    return EncryptUtil.encrypt(arg1, arg2); } public static native String encrypt(String arg0, String arg1) { }

同时在代码中发现了多个密钥,包括但不限于,第一个base64编码的密钥在跟踪流程中传递给了native函数

BwcnBzRjN2U/MmZhYjRmND4xPjI+NWQwZWU0YmI2MWQ3YjAzKw8cEywsIS4BIg==

81d7beac44a86f4337f534ec9332837

三、Java层动态跟踪、Hook分析

将前面重新打包签名生成的可调试的apk安装到手机上,为防止应用直接闪退,拒绝其相关权限的申请,同时在程序判断权限申请结果处下断,动态修改权限申请的结果,使后续流程继续下去

调试跟踪函数,最终定位发现程序在加载上述native加密so库时闪退

.method static constructor <clinit>()V          .registers 1 00000000  const-string        v0, "sojm" 00000004  invoke-static       System->loadLibrary(String)V, v0 0000000A  return-void .end method

同时在上面的跟踪过程中还可以得到程序生成的一系列请求参数,包含了大量系统、设备信息,但没有hash相关的参数

使用 frida hook encrypt函数,主动调用其多次加密相同数据,可以发现每次得到的结果都不同,应该使用了某种随机量

四、so层静态分析

使用ida pro分析sojm 库,通过观察函数名可以得到其是通过静态注册的,这里的四个参数也符合常规的jni函数

// JNIEnv* env // jclass _clazz // jstring a3 // jstring a4 int __fastcall Java_com_qq_lib_EncryptUtil_encrypt(int a1, int a2, int a3, int a4) { int v8; // r4 int v10[4]; // [sp+4h] [bp-2Ch] BYREF int v11; // [sp+14h] [bp-1Ch] v8 = cgo_wait_runtime_init_done(); v10[3] = a4; // jstring v10[2] = a3; // jstring v10[1] = a2; // _clazz v10[0] = a1; // env v11 = 0; crosscall2(cgoexp_17c794619cba_Java_com_qq_lib_EncryptUtil_encrypt, v10, 20, v8); cgo_release_context(v8); return v11; // jstring }

但是后续的操作就不太常规了,可以看出它把参数依次赋给了一个数组;同时调用了crosscall2,其参数为:

一个函数地址

参数数组

应该是参数数组的长度,size

init函数的返回值

值得注意的是,v10明明只有四个元素,但是传入的参数size却是20 = 5 * 4,同时v11被置0后又没有显式的赋值,最终却被返回,猜测应该是在cgoexp_17c794619cba_Java_com_qq_lib_EncryptUtil_encrypt中被赋值了

进入cgoexp_17c794619cba_Java_com_qq_lib_EncryptUtil_encrypt后发现参数个数很奇怪,而且sub_BC3C4658传入了很多重复的参数;

int __fastcall cgoexp_17c794619cba_Java_com_qq_lib_EncryptUtil_encrypt(int a1, int a2, int a3, int a4, int a5, int a6) { int v6; // r10 int v7; // lr int v9; // [sp+14h] [bp-4h] int v10; // [sp+14h] [bp-4h] while ( (unsigned int)&a5 <= *(_DWORD *)(v6 + 8) )    sub_BC360D10();          sub_BC3C4658( // 通过这个函数可以推测a6为前面传入的数组地址        a6,        *(_DWORD *)a6,        *(_DWORD *)(a6 + 4),        *(_DWORD *)(a6 + 8),        v7,        *(_DWORD *)a6,        *(_DWORD *)(a6 + 4),        *(_DWORD *)(a6 + 8),        *(_DWORD *)(a6 + 12),        v9);    // sub_BC3C4658函数没有返回值,局部变量v10也没有被显式地赋值 *(_DWORD *)(a6 + 16) = v10; // 在这里对a6[4],也就是上面v11的地址处进行了赋值 sub_BC301DF8(); return sub_BC2FBDAC(); }

通过上面的观察分析,可以察觉到这不是常规的函数调用约定,而且肯定不是fastcall;注意到函数中出现了cgo字样,且在该so库的函数表中也有大量的cgo函数

分析:

这个so库的调用约定与常规的不同,很可能是全部通过栈进行的,包括参数的传递以及返回值的传递

golang是可以和c进行交叉调用的,而且可以编译成so库

这个so库的核心加密部分应该是由golang编写的,C接口函数就起到个连接、转发的作用

定位关键加密函数

虽然看起来有点奇怪,但是这并不妨碍定位到关键函数,跟进上面的sub_BC3C4658()函数:

int __fastcall sub_BC3C4658(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10) { int v10; // r10 int v11; // lr int result; // r0 int v13; // [sp+Ch] [bp-2Ch] _DWORD *v14; // [sp+10h] [bp-28h] int v15; // [sp+10h] [bp-28h] int v16; // [sp+14h] [bp-24h] int v17; // [sp+20h] [bp-18h] int v18; // [sp+24h] [bp-14h] int v19[2]; // [sp+30h] [bp-8h] BYREF while ( (unsigned int)&a5 <= *(_DWORD *)(v10 + 8) )    sub_BC360D10(); v19[0] = a6; sub_BC3BED84(); v19[1] = v13; sub_BC3BED84(); sub_BC3C0D0C(v13, (int)v14, v16, v16, v11, (int)v19, v13, (int)v14, v16, v13, v14, v16, v17, v18); sub_BC3BEC54(); result = v15; a10 = v15; return result; }

跟进sub_BC3C0D0C(),发现了package_name,pakcage_hash字样,且进行了大量函数调用,将动态调试的目光先锁定在它身上

int __fastcall sub_BC3C0D0C(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, _DWORD *a11, int a12, int a13, int a14) { while ( (unsigned int)&a5 <= *(_DWORD *)(v14 + 8) )    sub_BC360D10(); v34 = v15; a13 = 0; a14 = 0; v72 = &off_BC3DFD34; sub_BC3C42D8(); v38 = a11; sub_BC3C21F8(); v16 = v46; if ( v61 ) {    a13 = 0;    a14 = 0;    result = sub_BC3C0CDC(); } else {    v68 = v49;    v69 = v57;    sub_BC304C38();    v71 = v38;    sub_BC305928();    if ( dword_BC47A460 )    {      sub_BC36294C(dword_BC47A460, v17, v71, &unk_BC3D2F68);      v18 = v19;    }    else    {      v18 = v71;      *v71 = &unk_BC3D2F68;    }    v50 = (int)v18;    sub_BC3A70F8();    if ( v55 )    {      v20 = a9;      v21 = a8;      v22 = a7;    }    else    {      v23 = v65;      do        *v23++ = 0;      while ( (int)v23 <= (int)&v65[15] );      qmemcpy(v65, "__package_name__", sizeof(v65));      sub_BC34E438((int)&v65[15], (int)v23, 0, 95, v15, 0, (unsigned __int8 *)v65, 16, (int)&unk_BC3CF818, v50);      v67 = v47;      v64 = v51;      v24 = v65;      do        *v24++ = 0;      while ( (int)v24 <= (int)&v65[15] );      qmemcpy(v65, "__package_hash__", sizeof(v65));      sub_BC34E438(v47, (int)v24, v51, 0, v35, 0, (unsigned __int8 *)v65, 16, v47, v51);      v66 = v48;      v63 = v52;      sub_BC3C4318();      sub_BC34E438(0, v25, v26, v27, v36, 0, v39, v41, v48, v52);      sub_BC301F40();      v28 = (unsigned __int8 *)*v71;      v70 = v42;      v40 = v28;      v43 = v67;      sub_BC309480();      *v53 = &unk_BC3D2A70;      if ( dword_BC47A460 )        sub_BC36294C(v53 + 1, v29, v53 + 1, v70);      else        v53[1] = v70;      sub_BC3C440C();      sub_BC34E438(0, v30, v31, v32, v37, 0, v40, v43, v64, (int)v53);      sub_BC3C094C();      sub_BC301F40();      v70 = v44;      v45 = v66;      sub_BC309480();      *v54 = &unk_BC3D2A70;      if ( dword_BC47A460 )        sub_BC36294C(v54, dword_BC47A460, v54 + 1, v70);      else        v54[1] = v70;      sub_BC3AEB0C();      v22 = v45;      v21 = v63;      v20 = (int)v54;    }    if ( v16 )    {      a13 = 0;      a14 = 0;    }    else    {      sub_BC3C05EC(v56, v22, v21, v20, v34, v22, v21, v20, v69, v58, v59, v68, v55, v56, v59, 0);      a13 = v60;      a14 = v62;    }    result = sub_BC3C0CDC(); } return result; }

五、so层动态调试、分析调用约定

在jni接口处下断,符合fastcall调用约定

image-20220921175805969.png (102.07 KB, 下载次数: 266)

下载附件

2022-9-21 21:50 上传

传递给cgo的参数

image-20220921180034269.png (75.33 KB, 下载次数: 276)

下载附件

2022-9-21 21:51 上传

image-20220921180136723.png (40.66 KB, 下载次数: 280)

下载附件

2022-9-21 21:51 上传

进入cgo函数,首先观察栈平衡循环

image-20220921180451141.png (83.75 KB, 下载次数: 276)

下载附件

2022-9-21 21:52 上传

结束后

image-20220921180715293.png (109.45 KB, 下载次数: 267)

下载附件

2022-9-21 21:53 上传

观察从哪里取得参数

image-20220921181020664.png (104.08 KB, 下载次数: 281)

下载附件

2022-9-21 21:53 上传

在下一个函数调用前下断,观察参数传递

image-20220921181238897.png (132.09 KB, 下载次数: 279)

下载附件

2022-9-21 21:53 上传

f8步过,观察栈变化以及从哪里取的返回值

image-20220921181412980.png (103.19 KB, 下载次数: 266)

下载附件

2022-9-21 21:54 上传

总结得出函数调用:参数完全通过栈传递,返回值存储在参数往下的地址中

六、so层加密算法还原

前置工作分析:可以跟踪调试sub_BC3C0D0C函数,发现这里只是进行了一些参数以及其他操作,真正的加密处理函数在这个函数的结尾处调用,即:sub_C2DC05EC

需要说明的是,这个函数中对java层传入的key进行了base64解码,并得到两个密钥:

key1: 4c7e?2fab4f4>1>2>5d0ee4bb61d7b03

key2: mIZUjjghGd

在sub_C2DC05EC处下断,分析参数

image-20220921182713971.png (33.09 KB, 下载次数: 277)

下载附件

2022-9-21 21:54 上传

首先对传入的两个key进行了异或得到一个新key

image-20220921182838936.png (70.15 KB, 下载次数: 274)

下载附件

2022-9-21 21:54 上传

再对key进行了两次转换,得到

第一次得到

image-20220921182913411.png (20.14 KB, 下载次数: 279)

下载附件

2022-9-21 21:54 上传

第二次得到,此时密钥已经成为一个不可读的字节序列,这也是最终加密算法使用的密钥

image-20220921183107882.png (20.25 KB, 下载次数: 275)

下载附件

2022-9-21 21:55 上传

之后生成了一个长度为0x10的随机串,这是最终加密算法使用的初始向量

image-20220921183027646.png (66.45 KB, 下载次数: 261)

下载附件

2022-9-21 21:55 上传

之后传入密钥,调用一个函数后返回了一个全局地址和一个指针

image-20220921183450895.png (76.12 KB, 下载次数: 275)

下载附件

2022-9-21 21:55 上传

其中指针的内容是

image-20220921183648512.png (103.95 KB, 下载次数: 273)

下载附件

2022-9-21 21:55 上传

到这里的话,因为前面已经猜测这是一个golang编写的so库,此时基本可以确定这是使用的go的crypto/cipher加密库了;

通过查看go加密的源码,能发现其newCipher最终会生成两个长度为0x3C即60的密钥,分别用来加密和解密;

又由于是对称加密,因此使用的是用一个密钥,这里生成的两个密钥刚好是逆序的关系,可能是因为方便实现的原因

image-20220921195430035.png (33.27 KB, 下载次数: 267)

下载附件

2022-9-21 21:56 上传

image-20220921195522728.png (17.1 KB, 下载次数: 259)

下载附件

2022-9-21 21:56 上传

image-20220921195620953.png (28.55 KB, 下载次数: 288)

下载附件

2022-9-21 21:56 上传

说明:这里usb断了一次连接,因此下面一些参数的地址可能和上面不同

接着,调用了一个保存在寄存器的地址,并将明文和前面密钥生成的结构当作参数传了进去

image-20220921190408627.png (136.95 KB, 下载次数: 270)

下载附件

2022-9-21 21:59 上传

函数返回后,那片内存空间里已经由全0填充为了字节序列,可以确定其为加密函数

image-20220921190708165.png (64.11 KB, 下载次数: 268)

下载附件

2022-9-21 21:57 上传

image-20220921190906871.png (90.51 KB, 下载次数: 281)

下载附件

2022-9-21 21:57 上传

最后,又对加密生成的序列进行了一次字母表映射,字母表为16进制的16个字符

image-20220921191342058.png (48.45 KB, 下载次数: 278)

下载附件

2022-9-21 21:57 上传

最终得到的密文为

image-20220921191405844.png (53.37 KB, 下载次数: 266)

下载附件

2022-9-21 21:57 上传

算法还原

找到最后一步密钥转换后生成的字节序列,这就是真正的加密密钥

确定加密算法,根据几个特征,推测应该是一种有初始向量的流加密,最终确定为CFB模式的aes加密

go的加密库

有初始向量的加密算法

没有padding操作

之后,首先用go还原一下,验证加密算法无误

之后用python重现,这里要注意的是默认的python和go的CFB加密结果是不同的,需要在python加密中设置以下属性:

image-20220921191855157.png (13.15 KB, 下载次数: 272)

下载附件

2022-9-21 21:58 上传

最后验证python和go的加密结果一致即可

七、Java层签名算法分析还原

接下来就是java层sign字段的生成了,这个比较简单,它依次调用了两个哈希算法

sha256:根据post参数的格式及各个参数生成输入,经过sha256得到一个十六进制字符串

  public static String e(String arg2) {       try {           MessageDigest v0 = MessageDigest.getInstance("SHA-256");           v0.update(arg2.getBytes("UTF-8"));           return a.b(v0.digest()); // 将字节数组转换为16进制字符串       }       catch(NoSuchAlgorithmException v2_1) {           v2_1.printStackTrace();           return "";       }       catch(UnsupportedEncodingException v2) {           v2.printStackTrace();           return "";       }   }

md5:将上面的到的sha256字符串经过md5变换得到最终的sign值

  public static String b(String arg2) {       try { // a() 将字节数组转换为16进制字符串           return c.a(MessageDigest.getInstance("MD5").digest(arg2.getBytes("UTF-8")));       }       catch(Exception v2) {           v2.printStackTrace();           return "";       }   }

八、发包验证算法正确性

用python实现它的数据加解密以及签名、封装过程,生成请求数据,并向其服务器的一个接口发起请求验证:

image-20220921194210696.png (385.57 KB, 下载次数: 273)

下载附件

2022-9-21 21:58 上传

服务器正常返回数据,解密得到数据:

image-20220921194419585.png (133.61 KB, 下载次数: 282)

下载附件

2022-9-21 21:58 上传

 

免费评分 参与人数 60吾爱币 +68 热心值 +53 理由

T4DNA
  + 3   + 1   感谢发布原创作品,吾爱破解论坛因你更精彩!  

moyougege
  + 1     我很赞同!  

renhe
  + 1     我很赞同!  

sacmmqqll
  + 1   + 1   我很赞同!  

keaixiaoxiaoya
  + 1   + 1   牛哇牛哇  

lbj000307
  + 1   + 1   谢谢@Thanks!  

我不是神
  + 1   + 1   感谢发布原创作品,吾爱破解论坛因你更精彩!  

Civipojie
  + 1     有帮助  

week11
  + 1   + 1   热心回复!  

tianyucq
  + 1   + 1   我很赞同!  

ζ__ims_wang
  + 2     收费内容不能看扎心  

一米七五
  + 1   + 1   向大佬学习!  

sanyuebeichen
    + 1   我很赞同!  

awesomerickiii
  + 1   + 1   我很赞同!  

我的城市没有海
  + 1   + 1   大佬牛逼 我誓与赌毒不共戴天  

serge555
  + 1   + 1   用心讨论,共获提升!  

温馨提示
  + 1   + 1   热心回复!  

allspark
  + 1   + 1   用心讨论,共获提升!  

poisonbcat
  + 1   + 1   谢谢@Thanks!  

删掉丶关于n1
  + 1   + 1   我很赞同!  

dummersoul
  + 1   + 1   谢谢@Thanks!  

jyz0506
  + 1   + 1   谢谢@Thanks!  

maiwens
  + 1   + 1   谢谢@Thanks!  

lplp01110
  + 1     我很赞同!  

AZUZXC123
  + 1     我很赞同!  

zcchk135820
    + 1   我很赞同!  

慕冬
  + 1   + 1   感谢发布原创作品,吾爱破解论坛因你更精彩!  

静夜无缘
  + 1   + 1   我很赞同!  

xlwllm
  + 1   + 1   我很赞同!  

丶老衲徒伤悲
  + 2   + 1   厉害了老铁!  

1MajorTom1
    + 1   热心回复!  

htwl1023
  + 1   + 1   热心回复!  

笙若
  + 1   + 1   谢谢@Thanks!  

LinSir1216
  + 1   + 1   我很赞同!  

submarine1620
  + 1   + 1   感谢发布原创作品,吾爱破解论坛因你更精彩!  

procurve
  + 1   + 1   谢谢@Thanks!  

zhoumeto
  + 1   + 1   用心讨论,共获提升!  

zhixiangwangluo
  + 1   + 1   谢谢@Thanks!  

sywy
    + 1   &lt;font style=&quot;vertical-align: inherit;&quot;&gt;热心回复! &lt;/font  

hhh1234567
  + 1     热心回复!  

芽衣
  + 3   + 1   用心讨论,共获提升!  

pdcba
  + 1   + 1   谢谢@Thanks!  

billsmiless
  + 2   + 1   谢谢@Thanks!  

victos
  + 1   + 1   谢谢@Thanks!  

努力加载中
  + 1   + 1   热心回复!  

WUJH6699
  + 1   + 1   用心讨论,共获提升!  

Main233
  + 1   + 1   老色批是第一生产力  

cxs808
  + 1   + 1   5.5版本的没有自校验,改一个地方就成功破解了,一直能用。  

TinyBad
  + 2   + 1   之前分析过一回,用了没几天就又变了,实在是没精力对抗了  

iokeyz
  + 2   + 1   用心讨论,共获提升!  

wangguang
  + 1   + 1   不懂就问,是我们想的那种软件吗  

不爱everyone
  + 1   + 1   谢谢@Thanks!  

heartfilia
  + 1   + 1   我很赞同!  

fengbolee
  + 2   + 1   欢迎分析讨论交流,吾爱破解论坛有你更精彩!  

ddddhm
  + 1   + 1   热心回复!  

初七的果子狸
  + 1   + 1   师傅厉害啊  

lxhyjr
  + 1   + 1   谢谢@Thanks!  

1426868
  + 1   + 1   怎么没有问的你们就装吧,是50度灰吗  

漁滒
  + 3   + 1   我很赞同!  

一介书生
  + 1   + 1   用心讨论,共获提升!  

查看全部评分