首页
社区
课程
招聘
[原创]某站平台的签名算法分享
2024-1-16 17:30 7625

[原创]某站平台的签名算法分享

2024-1-16 17:30
7625
  • 先charles抓包,api.xxxxxx.com域名的包

  • 分析包 看到路径参数如下
    图片描述
    appkey=1d8b6e7d45233436&build=5531000&channel=dw056&mobi_app=android&mode=0&oid=326052200&plat=2&platform=android&ps=20&statistics=%7B%22appId%22%3A1%2C%22platform%22%3A3%2C%22version%22%3A%225.53.1%22%2C%22abtest%22%3A%22%22%7D&ts=1705305495&type=1&sign=2c9086d4acc853a017ec087699902634
    可以看到一个sign,是个32字符,128位,直觉告诉我可能是个md5签名

  • 然后用jadx打开xxx.apk包,全局搜索sign字符串,在众多函数方法中,找到一个SignedQuery函数,跟进去看,发现又个loadLibrary(xxx),那么直接告诉我是在一个so里

  • 用apktool把xxx.apk decode出来,拿到libxxx.so

  • 用ida打开该libxxx.so,直接看JNI_OnLoad函数,找到这一行

    if ( (*v5)->RegisterNatives(v5, v4, (const JNINativeMethod *)&register_native_struct, 5) < 0

  • register_native_struct跟进去
    图片描述

  • 每个函数进去看下,有惊喜,确实是个md5算法,找到 md5_impl,跟进去看看算法参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    int __fastcall md5_impl(JNIEnv *a1, jobject (*a2)(JNIEnv *, jclass, jmethodID, ...))
    {
    JNIEnv *v2; // r11
    jobject (*v3)(JNIEnv *, jclass, jmethodID, ...); // r6
    const char *v4; // r2
    const char *v5; // r1
    int result; // r0
    int v7; // r0
    JNIEnv v8; // r1
    int v9; // r0
    int v10; // r4
    int v11; // r0
    int v12; // r10
    signed int v13; // r9
    int v14; // r5
    int v15; // r8
    time_t v16; // r0
    int v17; // r6
    int v18; // r5
    const char *input_str; // r8
    _DWORD *v20; // r10
    int v21; // r4
    _DWORD *v22; // r0
    int v23; // r1
    int v24; // r2
    int v25; // r0
    unsigned int input_len; // r5
    signed int v27; // r4
    char *v28; // r5
    int v29; // r4
    const char *v30; // [sp+8h] [bp-C0h]
    int v31; // [sp+8h] [bp-C0h]
    int v32; // [sp+Ch] [bp-BCh]
    char v33; // [sp+10h] [bp-B8h]
    int s; // [sp+38h] [bp-90h]
    int v35; // [sp+3Ch] [bp-8Ch]
    int v36; // [sp+40h] [bp-88h]
    int v37; // [sp+44h] [bp-84h]
    char v38[24]; // [sp+90h] [bp-38h]
    int v39; // [sp+A8h] [bp-20h]
     
    v2 = a1;
    v3 = a2;
    if ( !sub_2D20((int)a1) )
    goto LABEL_5;
    v4 = "com.xxxxxxxx.nativelibrary.SignedQuery";
    v5 = "java/lang/ClassNotFoundException";
    LABEL_3:
    sub_401C((int)v2, (int)v5, (int)v4);
    result = 0;
    while ( _stack_chk_guard != v39 )
    {
    LABEL_5:
    if ( !v3 )
    {
      v5 = "java/lang/NullPointerException";
      v4 = "Null params!";
      goto LABEL_3;
    }
    v7 = is_empty(v2, (int)v3);
    v8 = *v2;
    if ( v7 )
    {
      v3 = v8->NewObject;
      result = ((int (__fastcall *)(JNIEnv *, int, int, _DWORD, _DWORD))v3)(
                 v2,
                 cls_signed_query,
                 signed_query_init,
                 0,
                 0);
    }
    else
    {
      v9 = ((int (__fastcall *)(JNIEnv *, const char *))v8->NewStringUTF)(v2, "appkey");
      v10 = v9;
      v11 = sub_60CC(v2, (int)v3, v9);          // // 校验appkey
      v12 = v11;
      if ( v11 )
      {
        v30 = (const char *)((int (__fastcall *)(JNIEnv *, int, _DWORD))(*v2)->GetStringUTFChars)(v2, v11, 0);
        v13 = check_app_key_get_type(v30);
      }
      else
      {
        v13 = -1;
        v30 = 0;
      }
      v14 = ((int (__fastcall *)(JNIEnv *, const char *))(*v2)->NewStringUTF)(v2, "ts");
      v15 = sub_60CC(v2, (int)v3, v14);
      if ( !v15 )
      {
        v36 = 0;
        v37 = 0;
        s = 0;
        v35 = 0;
        v16 = time(0);
        sprintf((char *)&s, "%ld", v16);
        ((void (__fastcall *)(JNIEnv *, int *))(*v2)->NewStringUTF)(v2, &s);
        sub_6188((int)v2);
      }
      sub_41D0(v2, v14);
      sub_41D0(v2, v15);
      v17 = ((int (__fastcall *)(JNIEnv *, int, int, jobject (*)(JNIEnv *, jclass, jmethodID, ...)))(*v2)->CallStaticObjectMethod)(
              v2,
              cls_signed_query,
              method_signed_query_r,
              v3);
      v18 = 0;
      if ( sub_3F70((int)v2) )
        v17 = 0;
      v32 = v17;
      input_str = (const char *)((int (__fastcall *)(JNIEnv *, int, _DWORD))(*v2)->GetStringUTFChars)(v2, v17, 0);
      if ( v13 != -1 )
      {
        ((void (__fastcall *)(JNIEnv *, int, const char *))(*v2)->ReleaseStringUTFChars)(v2, v12, v30);
        v20 = malloc(0x10u);
        if ( v20 )
        {
          v31 = v10;
          v21 = global_key[v13];
          v22 = &global_key[v13];
          v23 = v22[5];
          v24 = v22[10];
          v25 = v22[15];
          *v20 = v21;
          v20[1] = v23;
          v20[2] = v24;
          v20[3] = v25;
          _aeabi_memclr8(&v33, 33);
          input_len = strlen(input_str);
          _aeabi_memclr8(v38, 24);
          _aeabi_memclr8(&s, 88);
          md5_init(&s);
          md5_update((int)&s, (int)input_str, input_len);
          sprintf(v38, "%08x", v21);
          md5_update((int)&s, (int)v38, 8u);
          v27 = 1;
          do
          {
            sprintf(v38, "%08x", v20[v27]);
            md5_update((int)&s, (int)v38, 8u);
            ++v27;
          }
          while ( v27 != 4 );
          md5_final((int)v38, (int)&s);
          v28 = &v33;
          v29 = 0;
          do
          {
            sprintf(v28, "%02x", (unsigned __int8)v38[v29++]);
            v28 += 2;
          }
          while ( v29 != 16 );
          free(v20);
          v10 = v31;
          v18 = ((int (__fastcall *)(JNIEnv *, char *))(*v2)->NewStringUTF)(v2, &v33);
        }
        else
        {
          v18 = 0;
        }
      }
      sub_41D0(v2, v10);
      v3 = (*v2)->NewObject;
      result = ((int (__fastcall *)(JNIEnv *, int, int, int, int))v3)(v2, cls_signed_query, signed_query_init, v32, v18);
    }
    }
    return result;} 
  • 分析代码,发现关键在 check_app_key_get_type和global_key,一个是app key获取type,还有一个存着类似于secret key的 global key

  • 整体看下来,结论就是 md5加密,按参数字典序排序,然后拼接SecretKey,再md5得到sign,取其中一个type来验证下:

  • 图片描述
    和app请求生成的sign一样,secret key打码,如果想要,可以按步骤自己去试试,实测成功,关键地方都写清楚了
    图片描述

  • 最后,写个python脚本试试,确实都能访问,这里脚本就不放了,求赞求加精,求账号升级,感谢


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2024-1-16 17:33 被mb_qjzbupqg编辑 ,原因: 标题重复
收藏
点赞4
打赏
分享
最新回复 (1)
雪    币: 19461
活跃值: (29125)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2024-1-18 10:14
2
1
感谢分享
游客
登录 | 注册 方可回帖
返回