【Android逆向】静态分析+frida破解test2.apk

有了上一篇的基础
https://www.cnblogs.com/gradyblog/p/17152108.html

现在尝试静态分析的方式来处理
为什么还要多此一举,因为题眼告诉了我们是五位数字,所以可以爆破,不告诉这个题眼的话,就得分析

1. IDA 打开libroysue.so,查看JNI_OnLoad, 从method_table里找到Sign对应的函数

.data:00080000                               ; ===========================================================================
.data:00080000
.data:00080000                               ; Segment type: Pure data
.data:00080000                               AREA .data, DATA
.data:00080000                               ; ORG 0x80000
.data:00080000                               ; JNINativeMethod method_table[1]
.data:00080000 1C 55 07 00 21 55 07 00 25 71+_ZL12method_table JNINativeMethod <aSign, aLjavaLangStrin_1, _Z4fuckP7_JNIEnvP7_jclassP8_jstring+1>
.data:00080000 03 00                                                                 ; DATA XREF: JNI_OnLoad+EA↑o
.data:00080000                                                                       ; JNI_OnLoad+EC↑o
.data:00080000                                                                       ; .text:off_376D0↑o
.data:00080000                                                                       ; _Unwind_VRS_Interpret+1B2↑o
.data:00080000                                                                       ; _Unwind_VRS_Interpret:def_68226↑o
.data:00080000                                                                       ; .text:off_68354↑o
.data:00080000                                                                       ; fuck(_JNIEnv *,_jclass *,_jstring *) ...

从这里可以看出,底层是这个 fuck函数与之对应,看一下它,整理后

jstring __fastcall fuck(JNIEnv *env, jclass jcls, jstring str_)
{
  jstring v4; // [sp+14h] [bp-BCh]
  _jmethodID *methodID; // [sp+18h] [bp-B8h]
  jstring v6; // [sp+30h] [bp-A0h]
  int i; // [sp+38h] [bp-98h]
  jbyte *ByteArrayElements; // [sp+3Ch] [bp-94h]
  _jbyteArray *array; // [sp+40h] [bp-90h]
  jobject j_str_bytes; // [sp+44h] [bp-8Ch]
  _jmethodID *v11; // [sp+48h] [bp-88h]
  _jclass *v12; // [sp+4Ch] [bp-84h]
  _jmethodID *digest_method_id; // [sp+50h] [bp-80h]
  _jobject *md_instance; // [sp+54h] [bp-7Ch]
  _jclass *Class; // [sp+5Ch] [bp-74h]
  _jobject *j_str; // [sp+60h] [bp-70h]
  _jfieldID *fieldID; // [sp+68h] [bp-68h]
  _jclass *clazz; // [sp+6Ch] [bp-64h]
  unsigned __int8 *src; // [sp+74h] [bp-5Ch]
  char *v23; // [sp+9Ch] [bp-34h]
  _WORD v24[4]; // [sp+A3h] [bp-2Dh] BYREF

  if ( !str_ )
    return 0;
  src = (unsigned __int8 *)_JNIEnv::GetStringUTFChars(env, str_, 0);
  clazz = _JNIEnv::FindClass(env, "android/os/Build");
  fieldID = _JNIEnv::GetStaticFieldID(env, clazz, "FINGERPRINT", "Ljava/lang/String;");
  _JNIEnv::GetStaticObjectField(env, clazz, fieldID);
  strcat((char *)src, "REAL");
  j_str = (_jobject *)j_o0OoOOOO(env, src);
  _android_log_print(4, "roysuejni", "before entering aes => %s", (const char *)src);
  Class = _JNIEnv::FindClass(env, "java/security/MessageDigest");
  methodID = _JNIEnv::GetStaticMethodID(env, Class, "getInstance", "(Ljava/lang/String;)Ljava/security/MessageDigest;");
  v4 = j_o0OoOOOO(env, "MD5");
  md_instance = _JNIEnv::CallStaticObjectMethod(env, Class, methodID, v4);
  digest_method_id = _JNIEnv::GetMethodID(env, Class, "digest", "([B)[B");
  v12 = _JNIEnv::FindClass(env, "java/lang/String");
  v11 = _JNIEnv::GetMethodID(env, v12, "getBytes", "()[B");
  j_str_bytes = _JNIEnv::CallObjectMethod(env, j_str, v11);
  array = (_jbyteArray *)_JNIEnv::CallObjectMethod(env, md_instance, digest_method_id, j_str_bytes);
  ByteArrayElements = _JNIEnv::GetByteArrayElements(env, array, 0);
  for ( i = 0; i <= 15; ++i )
    sprintf((char *)&v24[i], "%02x", (unsigned __int8)ByteArrayElements[i]);
  v23 = (char *)j_ll11l1l1ll(src);
  strcat(v23, (const char *)v24);
  v6 = j_o0OoOOOO(env, (const unsigned __int8 *)v23);
  _android_log_print(4, "roysuejni", "result is => %s ", v23);
  _JNIEnv::ReleaseStringUTFChars(env, str_, src);
  free(v23);
  return v6;
}

静态分析可知,大概逻辑如下

1. 给输入拼接个 REAL,比如输入 1111 变成 1111REAL
2. 然后执行给MD5,得到MD5的值(32位)
3. 将拼接后的输入由j_ll11l1l1ll处理一下,得到返回值
4. 将返回值和md5进行拼接,返回

2. 点进去看看j_ll11l1l1ll在干什么

unsigned __int8 *__fastcall ll11l1l1ll(const unsigned __int8 *input)
{
  unsigned __int8 *v2; // [sp+8h] [bp-30h]
  uint8_t *output; // [sp+Ch] [bp-2Ch]
  size_t byte_count; // [sp+10h] [bp-28h]
  uint8_t *iv; // [sp+18h] [bp-20h]
  uint8_t *key; // [sp+1Ch] [bp-1Ch]
  char *v8; // [sp+2Ch] [bp-Ch]

  key = (uint8_t *)ll11lll1l1();
  iv = (uint8_t *)ll11l1l1l1();
  v8 = (char *)ll11l1l11l(input);
  byte_count = strlen(v8);
  output = (uint8_t *)malloc(byte_count);
  j_qpppqp(output, (uint8_t *)v8, byte_count, key, iv);
  v2 = j_bbddbbdbb(output, byte_count);
  free(v8);
  free(output);
  free(key);
  free(iv);
  return v2;
}

看到 key iv ,八成就是AES加密了,再看看是哪种模式的加密,点进j_qpppqp看看

void __fastcall qpppqp(uint8_t *output, uint8_t *input, uint32_t length, const uint8_t *key, const uint8_t *iv)
{
  __int64 v5; // d17
  unsigned __int8 v6; // [sp+Bh] [bp-2Dh]
  uint32_t i; // [sp+Ch] [bp-2Ch]

  v6 = length & 0xF;
  if ( key )
  {
    Key = key;
    KeyExpansion();
  }
  if ( iv )
    Iv = (uint8_t *)iv;
  for ( i = 0; i < length; i += 16 )
  {
    v5 = *((_QWORD *)input + 1);
    *(_QWORD *)output = *(_QWORD *)input;
    *((_QWORD *)output + 1) = v5;
    XorWithIv(output);
    state = (state_t *)output;
    Cipher();
    Iv = output; // 这里加密后的数据变下一次IV,是典型的CBC模式
    input += 16;
    output += 16;
  }
  if ( v6 )
  {
    qmemcpy(output, input, v6);
    memset(&output[v6], 0, 16 - v6);
    XorWithIv(output);
    state = (state_t *)output;
    Cipher();
  }
}

由代码特征可知: 这里加密后的数据变下一次IV,是典型的CBC模式

那么只要拿到key 和iv 试一下就可确认算法是不是预测正确

frida代码

function print_dump(arg, size) {
    console.log(hexdump(arg, {
        offset: 0,
        length: size,
        header: true,
        ansi: true
    }))
}

function main() {
    Java.perform(function () {

        var lib_hanlder = Process.findModuleByName("libroysue.so");
        console.log("lib_handler: " + lib_hanlder)

        if (lib_hanlder) {
            var addr_ll11l1l1ll = lib_hanlder.base.add(0x0003C9E4 + 0x1)
            
            Interceptor.attach(addr_ll11l1l1ll,{
                onEnter: function(args) {
                    console.log(" === hook before")
                    var arg = args[0]
                    print_dump(arg, 128)
                },
                onLeave:function(retVal) {
                    console.log(" === hook after: " + retVal)
                    print_dump(retVal, 128)
                }
            })

            var addr_qpppqp = lib_hanlder.base.add(0x0003B868 + 0x1)

            var output
            Interceptor.attach(addr_qpppqp,{
                onEnter: function(args) {
                    output = args[0]
                    console.log(" === hook qpppqp before: " + output)
                    console.log(" === hook qpppqp before KEY: ")
                    var arg = args[3]
                    print_dump(arg, 128)
                    console.log(" === hook qpppqp before IV: ")
                    var arg = args[4]
                    print_dump(arg, 128)
                },
                onLeave:function(retVal) {
                    console.log(" === hook qpppqp after==>: " + output)
                    print_dump(output, 128)
                }
            })

        }

    })

}

setTimeout(main, 3000)

输出日志

           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
dbf3a038  31 31 31 31 52 45 41 4c 00 84 f1 db 10 33 f0 db  1111REAL.....3..
dbf3a048  00 f2 58 d9 00 00 00 00 58 a0 f3 db 00 00 00 00  ..X.....X.......
dbf3a058  bc 6e c1 c6 44 00 00 00 48 ac 20 c7 58 ab 20 c7  .n..D...H. .X. .
dbf3a068  00 00 00 00 00 00 96 42 00 ad 20 c7 00 00 00 00  .......B.. .....
dbf3a078  00 00 00 00 00 00 00 00 18 e2 11 d6 00 00 00 00  ................
dbf3a088  64 e2 11 d6 00 00 00 00 d0 f0 9b e4 00 00 00 00  d...............                                                                                                                                                 
dbf3a098  00 20 bb c6 00 00 00 00 80 27 f2 db 00 00 00 00  . .......'......
dbf3a0a8  00 00 00 00 00 00 00 00 00 29 f2 db 00 00 00 00  .........)......
 === hook qpppqp before: 0xcd33c9d0
 === hook qpppqp before KEY: 
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
e42d0350  67 6f 6f 64 6c 2d 61 65 73 2d 6b 65 79 31 32 34  goodl-aes-key124
e42d0360  00 52 34 cd 88 52 34 cd 67 6f 6f 64 6c 2d 61 65  .R4..R4.goodl-ae
e42d0370  73 2d 69 76 31 32 33 35 00 d8 07 dc 88 d8 07 dc  s-iv1235........
e42d0380  31 31 31 31 52 45 41 4c 08 08 08 08 08 08 08 08  1111REAL........
e42d0390  00 00 00 00 c7 c3 00 00 80 03 2d e4 9b 87 77 11  ..........-...w.
e42d03a0  00 00 00 00 bd 04 1b e7 1e 00 00 00 c8 c3 00 00  ................
e42d03b0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
e42d03c0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 === hook qpppqp before IV: 
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
e42d0368  67 6f 6f 64 6c 2d 61 65 73 2d 69 76 31 32 33 35  goodl-aes-iv1235
e42d0378  00 d8 07 dc 88 d8 07 dc 31 31 31 31 52 45 41 4c  ........1111REAL
e42d0388  08 08 08 08 08 08 08 08 00 00 00 00 c7 c3 00 00  ................
e42d0398  80 03 2d e4 9b 87 77 11 00 00 00 00 bd 04 1b e7  ..-...w.........
e42d03a8  1e 00 00 00 c8 c3 00 00 00 00 00 00 00 00 00 00  ................
e42d03b8  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
e42d03c8  00 00 00 00 00 00 00 00 e8 04 2d e4 01 05 1b e7  ..........-.....
e42d03d8  00 10 24 e5 68 e7 60 da 80 28 e1 e5 b0 9f 4f d6  ..$.h.`..(....O.
 === hook qpppqp after==>: 0xcd33c9d0
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
cd33c9d0  72 cb e2 0c 9e 28 ec 25 e0 0f e3 bf 7a 37 c1 8e  r....(.%....z7..
cd33c9e0  2c 60 f6 e4 00 00 00 00 13 00 00 00 00 00 00 00  ,`..............
cd33c9f0  44 0b e1 e5 00 00 00 00 40 51 9a e4 00 00 00 00  D.......@Q......
cd33ca00  2c 60 f6 e4 00 00 00 00 13 00 00 00 01 20 00 00  ,`........... ..
cd33ca10  a4 f4 38 c6 80 fc 97 c6 00 b8 2d c6 00 40 f0 db  ..8.......-..@..
cd33ca20  a4 f4 2c c6 f0 fb 97 c6 50 ec 33 c6 00 40 f0 db  ..,.....P.3..@..
cd33ca30  3e 00 00 00 44 00 00 00 4b 00 00 00 53 00 00 00  >...D...K...S...
cd33ca40  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 === hook after: 0xda60e740
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
da60e740  37 32 63 62 65 32 30 63 39 65 32 38 65 63 32 35  72cbe20c9e28ec25
da60e750  65 30 30 66 65 33 62 66 37 61 33 37 63 31 38 65  e00fe3bf7a37c18e
da60e760  00 66 34 65 33 62 30 65 38 61 33 32 35 63 66 37  .f4e3b0e8a325cf7
da60e770  63 65 66 32 38 39 61 62 31 66 35 36 32 64 66 65  cef289ab1f562dfe
da60e780  00 20 00 00 03 00 00 00 00 00 00 00 00 00 00 00  . ..............
da60e790  00 00 80 3f 00 00 00 00 00 00 00 00 00 00 00 00  ...?............
da60e7a0  00 00 80 3f 00 00 00 00 00 00 00 00 00 00 00 00  ...?............
da60e7b0  00 00 80 3f 10 00 00 00 00 d0 86 e4 68 ad 04 00  ...?........h...

那么可以得出
key: goodl-aes-key124
iv: goodl-aes-iv1235
加密后的数据为: 72cbe20c9e28ec25e00fe3bf7a37c18e4f4e3b0e8a325cf7cef289ab1f562dfe
去掉32位的md5,aes加密部分为72cbe20c9e28ec25e00fe3bf7a37c18e

4. 打开在线AES解密,得出结果确实是1111REAL

5. 取出apk中的校验字符串4143cb60bf8083ac94c57418a9a7ff5a14a63feade6b46d9d0af3182ccbdf7af,去掉后面的32个字符得到4143cb60bf8083ac94c57418a9a7ff5a,解密得到45678REAL

6. 得到flag为45678,手机验证,通过

posted @ 2023-02-24 18:28  明月照江江  阅读(79)  评论(0编辑  收藏  举报