在学习杨神的算法课程,有一个课后作业是base64非标准算法的apk案例 algorithmbase_13.apk;

算法还原

下面是主流程的伪代码

jstring __fastcall Java_com_kanxue_algorithmbase_MainActivity_encodeFromJni_113(_JNIEnv *a1, int a2, int a3, int a4)
{
  char *v6; // r6
  int v7; // r0
  int v8; // r5
  int v9; // r0
  const char *v10; // r0
  _BYTE *v11; // r1
  int v12; // r2
  const char *v13; // r1
  jstring v14; // r4
  _DWORD v16[2]; // [sp+4h] [bp-34h] BYREF
  const char *v17; // [sp+Ch] [bp-2Ch]
  unsigned __int8 v18; // [sp+10h] [bp-28h] BYREF
  _BYTE v19[3]; // [sp+11h] [bp-27h] BYREF
  int v20; // [sp+14h] [bp-24h]
  _BYTE *v21; // [sp+18h] [bp-20h]

  v16[0] = a2;
  v16[1] = a3;
  v17 = (const char *)a4;
  v6 = (char *)a1->functions->GetStringUTFChars(a1, a3, 0);
  sub_871C((int)&v18, v6);
  a1->functions->ReleaseStringUTFChars((JNIEnv *)a1, (jstring)a3, v6);
  v7 = v20;
  if ( !(v18 << 31) )
    v7 = v18 >> 1;
  v8 = sub_8ABC(v7);
  v9 = sub_8740(v16);
  sub_8758(v9, v8);
  v10 = v17;
  if ( !(LOBYTE(v16[0]) << 31) )
    v10 = (char *)v16 + 1;
  v11 = v21;
  if ( (v18 & 1) == 0 )
    v11 = v19;
  v12 = v20;
  if ( (v18 & 1) == 0 )
    v12 = v18 >> 1;
  sub_8B04(v10, v11, v12);
  v13 = v17;
  if ( !(LOBYTE(v16[0]) << 31) )
    v13 = (char *)v16 + 1;
  v14 = a1->functions->NewStringUTF(a1, v13);
  sub_875E(v16);
  sub_875E(&v18);
  return v14;
}

java层调用处为

public static native String encodeFromJni_13(String str);

直接看返回值,返回值是v14,跟踪一下,得到函数sub_8B04
用frida hook一下看看参数都是什么。

function hookJava() {
    Java.perform(function () {
        // let HelloJni = Java.use();
        let MainActivity = Java.use("com.kanxue.algorithmbase.MainActivity");
        MainActivity["encodeFromJni_13"].implementation = function (str) {
    // console.log(`MainActivity.encodeFromJni_11 is called: str=${str}`);
    let result = this["encodeFromJni_13"](str);
    console.log(`MainActivity.encodeFromJni_13 str=>${str}=====>${result}`);
    return result;
};
    })
}


function hookNative(){
    var base = Module.findBaseAddress("libnative-lib.so");
    console.log(base);
    if (! base){
        return;
    }
    var sub_8ABC = base.add(0x8ABC).add(1);
    var sub_1B000 = base.add(0x1B000);
    Interceptor.attach(sub_8ABC, {
        onEnter:function(args){
            this.arg0 = args[0];
            // this.arg1 = args[1];
            console.log("sub_8ABC onEnter:arg0:",(this.arg0));
        },onLeave:function(retVal){
            console.log("sub_8ABC onLeave:arg:", (retVal));
            // console.log("sub_1B000 onLeave:arg:TABLE", hexdump(sub_1B000));
        }
    })
    var sub_8B04 = base.add(0x8B04).add(1);
    Interceptor.attach(sub_8B04, {
        onEnter:function(args){
            this.arg0 = args[0];
            this.arg1 = args[1];
            this.arg2 = args[2];
            console.log("sub_8B04 onEnter:arg0:",hexdump(this.arg0), 
            "\r\n args1:", hexdump(this.arg1), 
            "\r\n args2:", (args[2]))
        },onLeave:function(retVal){
            console.log("sub_8B04 onLeave:outputStr:", hexdump(this.arg0, {length:parseInt(retVal)}), "\r\n inputStr:",hexdump(this.arg1, {length:parseInt(this.arg2)}), 
            "\r\narg2", (this.arg2), 
            "\r\n argretval:",(retVal));
            console.log("sub_1B000 onLeave:arg:TABLE", hexdump(sub_1B000));
        }
    })
}



// 主动调用
// function call(){
//     Java.perform(function () {
//         Java.choose(, {
//             onMatch: function (ins) {


//             }, onComplete: function () {

//             }
//         })
//     })
// }

function main(){
    hookJava();
    hookNative();
}

setImmediate(main)

下面是结果

[Nexus 6P::AlgorithmBase]-> sub_8ABC onEnter:arg0: 0x13
sub_8ABC onLeave:arg: 0x1d
sub_8B04 onEnter:arg0:            0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
d217da00  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d217da10  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d217da20  00 1d 76 d2 00 00 00 00 ff ff ff ff ff ff ff ff  ..v.............
d217da30  40 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00  @...............
d217da40  c0 90 af cd 00 00 00 00 ff ff ff ff 40 00 00 00  ............@...
d217da50  40 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00  @...............
d217da60  68 c8 5d da 08 00 00 00 68 c0 60 cc 00 00 00 00  h.].....h.`.....
d217da70  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d217da80  80 91 af cd 00 00 00 00 ff ff ff ff 00 00 00 00  ................
d217da90  04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d217daa0  80 91 af cd 00 00 00 00 ff ff ff ff ff ff ff ff  ................
d217dab0  40 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00  @...............
d217dac0  68 c8 5d da 10 00 00 00 a0 c0 60 cc 00 00 00 00  h.].......`.....
d217dad0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d217dae0  80 91 af cd 00 00 00 00 ff ff ff ff ff ff ff ff  ................
d217daf0  40 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00  @...............
 args1:            0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
d23ee6a0  63 78 45 4a 61 7e 31 4b 39 74 7a 62 5d 22 5d 64  cxEJa~1K9tzb]"]d
d23ee6b0  5a 2d 29 00 2d 00 00 00 00 00 18 42 00 00 00 00  Z-).-......B....
d23ee6c0  69 6e 64 69 72 65 63 74 20 72 65 66 20 74 61 62  indirect ref tab
d23ee6d0  6c 65 00 78 56 7a 3d 3d 00 00 00 06 00 00 00 00  le.xVz==........
d23ee6e0  7c 64 f6 e9 06 19 00 00 80 05 00 00 80 05 00 00  |d..............
d23ee6f0  00 00 00 00 03 00 00 00 00 00 00 00 00 19 eb dc  ................
d23ee700  01 00 00 00 11 00 00 00 00 50 1e 00 00 00 00 00  .........P......
d23ee710  00 00 d0 be 00 00 00 00 00 00 00 00 00 00 00 00  ................
d23ee720  01 00 00 00 28 00 00 00 00 30 00 00 00 00 00 00  ....(....0......
d23ee730  00 10 f4 be 00 00 00 00 00 00 00 00 00 00 00 00  ................
d23ee740  01 00 00 00 3e 00 00 00 00 10 02 00 00 00 00 00  ....>...........
d23ee750  00 00 91 be 00 00 00 00 00 00 00 00 00 00 00 00  ................
d23ee760  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d23ee770  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d23ee780  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d23ee790  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 args2: 0x13
sub_8B04 onLeave:outputStr:            0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
d217da00  4c 7c 35 21 6c 5b 32 6a 66 57 77 43 65 5a 63 25  L|5!l[2jfWwCeZc%
d217da10  4b 7f 72 76 4d 21 30 72 47 79 3d 3d 00           K.rvM!0rGy==.
 inputStr:            0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
d23ee6a0  63 78 45 4a 61 7e 31 4b 39 74 7a 62 5d 22 5d 64  cxEJa~1K9tzb]"]d
d23ee6b0  5a 2d 29                                         Z-)
arg2 0x13
 argretval: 0x1d
sub_1B000 onLeave:arg:TABLE            0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
d27bb000  52 4a 49 63 62 21 20 5a 59 61 47 55 75 74 7b 7a  RJIcb! ZYaGUut{z
d27bb010  79 78 7f 50 57 56 22 58 5f 5e 7e 51 77 76 60 67  yx.PWV"X_^~Qwv`g
d27bb020  46 26 25 24 2b 54 5b 69 23 70 66 65 64 72 71 5d  F&%$+T[i#pfedrq]
d27bb030  2a 38 3c 45 44 4b 7d 7c 5c 43 42 41 40 6b 6a 27  *8<EDK}|\CBA@kj'
d27bb040  00 00 00 00 fd a1 7a d2 d9 a2 7a d2 b8 57 7b d2  ......z...z..W{.
d27bb050  78 b2 7b d2 00 00 00 00 00 00 00 00 00 00 00 00  x.{.............
d27bb060  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d27bb070  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d27bb080  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d27bb090  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d27bb0a0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d27bb0b0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d27bb0c0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d27bb0d0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d27bb0e0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
d27bb0f0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
MainActivity.encodeFromJni_13 str=>cxEJa~1K9tzb]"]dZ-)=====>L|5!l[2jfWwCeZc%KrvM!0rGy==

在sub_8B04函数中看到一个aAyzpq23ijrtffg,点击进去发现类似base64的特征table, 发现有引用处,查看xref, 发现下面有诸字节做亦或处理。

int __fastcall sub_8ABC(int a1)
{
  size_t i; // r6

  for ( i = 0; i < strlen(aAyzpq23ijrtffg); ++i )
    aAyzpq23ijrtffg[i] = aAyzpq23ijrtffg_0[i] ^ a1;
  return 4 * sub_133F0(a1 + 2, 3) + 1;
}

那直接写c代码进行还原,声明一下我不会c,所以只能借助AI了,
先还原table。因为有不可见字符,所以下面借助hex转ascii

//
// Created by xiaosheng on 2026-07-03.
//

#include "base64_test.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include <string>


static const char aAyzpq23ijrtffg_0[] = "AYZpq23IJrTFfghijklCDE1KLMmBdestU5678GHz0cuvwabN9+/VWXnoOPQRSxy4";

// 可修改的字符表
static char aAyzpq23ijrtffg[sizeof(aAyzpq23ijrtffg_0)];


void sub_8ABC(int strLen) {
    size_t table_len = strlen(aAyzpq23ijrtffg_0);

    for (size_t i = 0; i < table_len; ++i) {
        aAyzpq23ijrtffg[i] = aAyzpq23ijrtffg_0[i] ^ strLen;
    }
    aAyzpq23ijrtffg[table_len] = '\0';  // 添加字符串结束符
}


// hex字符串转ascii
std::string hex_to_ascii(const char *hex) {
    std::string result;

    size_t len = strlen(hex);

    if (len % 2 != 0) {
        return "";
    }

    for (size_t i = 0; i < len; i += 2) {
        char buf[3] = {
            hex[i],
            hex[i + 1],
            0
        };

        char ch = (char)strtol(buf, nullptr, 16);
        result.push_back(ch);
    }

    return result;
}

int main() {
    const char *hex_str =
        "6c4d5831434f644c226779765b38612f75537e5f6334366f";
    std::string input = hex_to_ascii(hex_str);

    sub_8ABC(input.length());
    printf("table:%s\n", aAyzpq23ijrtffg);

    return 0;
}

打印table发现table是一致的,然后继续把table赋值给base64看看,发现结果不一致,
接下来我对比了一下下面sub_8B04的算法和标准base64的区别

// 魔改版
int __fastcall sub_8B04(int a1, int a2, int a3)
{
  int v3; // r12
  int v6; // r6
  int v7; // r5
  _BYTE *v8; // r3
  int v9; // r0
  unsigned int v10; // r2
  char v11; // r4
  int v12; // r6
  char v13; // r1

  v3 = a3 - 2;
  v6 = 0;
  v7 = 0;
  while ( 1 )
  {
    v8 = (_BYTE *)(a1 + v6);
    if ( v7 >= v3 )
      break;
    v9 = a2 + v7;
    v6 += 4;
    *v8 = aAyzpq23ijrtffg[*(unsigned __int8 *)(a2 + v7) >> 2] ^ a3;
    v10 = *(unsigned __int8 *)(a2 + v7 + 1);
    v11 = *(_BYTE *)(a2 + v7);
    v7 += 3;
    v8[1] = aAyzpq23ijrtffg[(v10 >> 4) & 0xFFFFFFCF | (16 * (v11 & 3))];
    v8[2] = aAyzpq23ijrtffg[(*(unsigned __int8 *)(v9 + 2) >> 6) & 0xFFFFFFC3 | (4 * (*(_BYTE *)(v9 + 1) & 0xF))] ^ a3;
    v8[3] = aAyzpq23ijrtffg[*(_BYTE *)(v9 + 2) & 0x3F];
  }
  if ( v7 < a3 )
  {
    *v8 = aAyzpq23ijrtffg[*(unsigned __int8 *)(a2 + v7) >> 2];
    v12 = (16 * *(unsigned __int8 *)(a2 + v7)) & 0x30;
    if ( a3 - 1 == v7 )
    {
      v13 = 61;
      v8[1] = aAyzpq23ijrtffg[v12];
    }
    else
    {
      v8[1] = aAyzpq23ijrtffg[v12 | (*(unsigned __int8 *)(a2 + v7 + 1) >> 4)];
      v13 = aAyzpq23ijrtffg[4 * (*(_BYTE *)(a2 + v7 + 1) & 0xF)];
    }
    v8[3] = 61;
    v8[2] = v13;
    v8 += 4;
  }
  *v8 = 0;
  return (int)&v8[-a1 + 1];
}
_BYTE *__fastcall sub_8AD0(int a1, int a2, int a3)
{
  int v3; // r12
  int v6; // r6
  int v7; // r5
  _BYTE *v8; // r3
  int v9; // r2
  unsigned int v10; // r0
  char v11; // r4
  int v12; // r6
  char v13; // r1

  v3 = a3 - 2;
  v6 = 0;
  v7 = 0;
  while ( 1 )
  {
    v8 = (_BYTE *)(a1 + v6);
    if ( v7 >= v3 )
      break;
    v9 = a2 + v7;
    v6 += 4;
    *v8 = aAyzabfghz0cmbd[*(unsigned __int8 *)(a2 + v7) >> 2];
    v10 = *(unsigned __int8 *)(a2 + v7 + 1);
    v11 = *(_BYTE *)(a2 + v7);
    v7 += 3;
    v8[1] = aAyzabfghz0cmbd[(v10 >> 4) & 0xFFFFFFCF | (16 * (v11 & 3))];
    v8[2] = aAyzabfghz0cmbd[(*(unsigned __int8 *)(v9 + 2) >> 6) & 0xFFFFFFC3 | (4 * (*(_BYTE *)(v9 + 1) & 0xF))];
    v8[3] = aAyzabfghz0cmbd[*(_BYTE *)(v9 + 2) & 0x3F];
  }
  if ( v7 < a3 )
  {
    *v8 = aAyzabfghz0cmbd[*(unsigned __int8 *)(a2 + v7) >> 2];
    v12 = (16 * *(unsigned __int8 *)(a2 + v7)) & 0x30;
    if ( a3 - 1 == v7 )
    {
      v13 = 61;
      v8[1] = aAyzabfghz0cmbd[v12];
    }
    else
    {
      v8[1] = aAyzabfghz0cmbd[v12 | (*(unsigned __int8 *)(a2 + v7 + 1) >> 4)];
      v13 = aAyzabfghz0cmbd[4 * (*(_BYTE *)(a2 + v7 + 1) & 0xF)];
    }
    v8[3] = 61;
    v8[2] = v13;
    v8 += 4;
  }
  *v8 = 0;
  return &v8[-a1 + 1];
}

发现魔改版相对于标准版加了两个亦或操作

 *v8 = aAyzpq23ijrtffg[*(unsigned __int8 *)(a2 + v7) >> 2] ^ a3;
   v8[2] = aAyzpq23ijrtffg[(*(unsigned __int8 *)(v9 + 2) >> 6) & 0xFFFFFFC3 | (4 * (*(_BYTE *)(v9 + 1) & 0xF))] ^ a3;

主要魔改点:

自定义编码表(打乱顺序)

第1和第3个字符查表后异或数据长度

对应python版本魔改

CUSTOM_TABLE = "AYZpq23IJrTFfghijklCDE1KLMmBdestU5678GHz0cuvwabN9+/VWXnoOPQRSxy4"

def custom_base64_encode(data):
    """魔改Base64编码"""
    if isinstance(data, str):
        data = data.encode()

    result = bytearray()
    data_len = len(data)
    a3 = data_len  # 长度作为异或密钥
    i = 0

    # 处理完整的3字节组
    while i < data_len - 2:
        byte0 = data[i]
        byte1 = data[i + 1]
        byte2 = data[i + 2]

        # 第一个字符:查表后异或
        c1 = ord(CUSTOM_TABLE[byte0 >> 2]) ^ a3
        # 第二个字符:标准查表
        c2 = ord(CUSTOM_TABLE[((byte1 >> 4) | ((byte0 & 0x03) << 4)) & 0x3F])
        # 第三个字符:查表后异或
        c3 = ord(CUSTOM_TABLE[((byte2 >> 6) | ((byte1 & 0x0F) << 2)) & 0x3F]) ^ a3
        # 第四个字符:标准查表
        c4 = ord(CUSTOM_TABLE[byte2 & 0x3F])

        result.extend([c1, c2, c3, c4])
        i += 3

    # 处理剩余字节
    if i < data_len:
        byte0 = data[i]
        c1 = ord(CUSTOM_TABLE[byte0 >> 2])  # 注意:这里没有异或
        result.append(c1)

        if i + 1 == data_len:
            # 只剩1个字节
            c2 = ord(CUSTOM_TABLE[(byte0 & 0x03) << 4])
            result.extend([c2, ord('='), ord('=')])
        else:
            # 剩2个字节
            byte1 = data[i + 1]
            c2 = ord(CUSTOM_TABLE[((byte1 >> 4) | ((byte0 & 0x03) << 4)) & 0x3F])
            c3 = ord(CUSTOM_TABLE[(byte1 & 0x0F) << 2])
            result.extend([c2, c3, ord('=')])

    return bytes(result)

# 测试
if __name__ == "__main__":
    test_data = b"Hello"
    encoded = custom_base64_encode(test_data)
    print(f"魔改Base64编码: {encoded}")

用c代码还原

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

// 原始编码表(不可修改)
static const char aAyzpq23ijrtffg_0[] = "AYZpq23IJrTFfghijklCDE1KLMmBdestU5678GHz0cuvwabN9+/VWXnoOPQRSxy4";

// 可修改的字符表
static char aAyzpq23ijrtffg[sizeof(aAyzpq23ijrtffg_0)];

/**
 * 根据输入长度生成魔改编码表
 * 将原始表每个字符与 strLen 进行异或
 */
void sub_8ABC(int strLen) {
    size_t table_len = strlen(aAyzpq23ijrtffg_0);

    for (size_t i = 0; i < table_len; ++i) {
        aAyzpq23ijrtffg[i] = aAyzpq23ijrtffg_0[i] ^ strLen;
    }
    aAyzpq23ijrtffg[table_len] = '\0';
}

/**
 * 魔改Base64编码函数
 */
int custom_base64_encode(char *output, const unsigned char *input, int inputLen)
{
    int i = 0;
    int j = 0;
    int a3 = inputLen;

    // 处理完整的3字节组
    while (i < inputLen - 2)
    {
        unsigned char byte0 = input[i];
        unsigned char byte1 = input[i + 1];
        unsigned char byte2 = input[i + 2];

        output[j++] = aAyzpq23ijrtffg[byte0 >> 2] ^ a3;
        output[j++] = aAyzpq23ijrtffg[((byte1 >> 4) | ((byte0 & 0x03) << 4)) & 0x3F];
        output[j++] = aAyzpq23ijrtffg[((byte2 >> 6) | ((byte1 & 0x0F) << 2)) & 0x3F] ^ a3;
        output[j++] = aAyzpq23ijrtffg[byte2 & 0x3F];

        i += 3;
    }

    // 处理剩余字节
    if (i < inputLen)
    {
        unsigned char byte0 = input[i];

        output[j++] = aAyzpq23ijrtffg[byte0 >> 2];

        if (i + 1 == inputLen)
        {
            output[j++] = aAyzpq23ijrtffg[(byte0 & 0x03) << 4];
            output[j++] = '=';
            output[j++] = '=';
        }
        else
        {
            unsigned char byte1 = input[i + 1];
            output[j++] = aAyzpq23ijrtffg[((byte1 >> 4) | ((byte0 & 0x03) << 4)) & 0x3F];
            output[j++] = aAyzpq23ijrtffg[(byte1 & 0x0F) << 2];
            output[j++] = '=';
        }
    }

    output[j] = '\0';
    return j;
}

// hex字符串转ascii
std::string hex_to_ascii(const char *hex) {
    std::string result;
    size_t len = strlen(hex);

    if (len % 2 != 0) {
        return "";
    }

    for (size_t i = 0; i < len; i += 2) {
        char buf[3] = {hex[i], hex[i + 1], 0};
        char ch = (char)strtol(buf, nullptr, 16);
        result.push_back(ch);
    }

    return result;
}

int main() {
    const char *hex_str = "413c3a4566594e76303850532b4440264d6a4e3968727d29724b";
    std::string input = hex_to_ascii(hex_str);
    int input_len = input.length();

    // 1. 先根据输入长度生成魔改编码表
    sub_8ABC(input_len);
    printf("输入长度: %d\n", input_len);
    printf("魔改编码表: %s\n\n", aAyzpq23ijrtffg);

    // 2. 使用魔改表进行Base64编码
    const unsigned char *data = (const unsigned char *)input.c_str();
    int max_output_len = ((input_len + 2) / 3) * 4 + 1;
    char *output = (char *)malloc(max_output_len);

    if (output)
    {
        int out_len = custom_base64_encode(output, data, input_len);

        printf("Hex输入: %s\n", hex_str);
        printf("原文: %s\n", input.c_str());
        printf("魔改Base64: %s\n", output);
        printf("十六进制: ");
        for (int i = 0; i < out_len; i++)
        {
            printf("%02X ", (unsigned char)output[i]);
        }
        printf("\n");

        free(output);
    }

    return 0;
}

如上,发现结果能对上了,那这就完了吗,! 没有

unidbg模拟调用

用凯神的unidbg试试。
先搭架子

public class Base64 extends AbstractJni {

    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;
    private final Memory memory;

    Base64() throws IOException {
        emulator = AndroidEmulatorBuilder
                .for32Bit()
                .setProcessName("com.xxxxx")
                .build();

        memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));

        vm = emulator.createDalvikVM(new File("unidbg-android\\src\\test\\java\\com\\kxbase64\\algorithmbase_13.apk"));
        vm.setVerbose(true);
        vm.setJni(this);

        DalvikModule dm = vm.loadLibrary(new File("unidbg-android\\src\\test\\java\\com\\kxbase64\\libnative-lib.so"), true);
        module = dm.getModule();

        dm.callJNI_OnLoad(emulator);

//        hookSub8B04();
        System.out.println("res:"+callEncodeBase64("A<:EfYNv08PS+D@&MjN9hr})rK"));
    }

通过上面的js hook可以发现arg0里面存的就是返回参数。

    public String callEncodeBase64(String input) {
        byte[] data = input.getBytes(StandardCharsets.UTF_8);
        int len = data.length;

        MemoryBlock block = memory.malloc(0x400, true);
        Pointer arg0 = block.getPointer();

        MemoryBlock block2 = memory.malloc(0x400, true);
        Pointer arg02 = block2.getPointer();

//        Pointer arg1 = arg0.share(0x20);
        arg02.write(0, data, 0, data.length);
        arg02.setByte(data.length, (byte) 0x00);

        module.callFunction(
                emulator,
                0x8B04 + 1,
                arg0,
                arg02,
                len
        );

        byte[] out = arg0.getByteArray(0, 0x100);

        String result = new String(out, 0,  out.length, StandardCharsets.UTF_8);

        block.free();

        return result;
    }

输出发现结果对不上,然后想到在这之前是有一个初始化table的函数sub_8ABC,现在直接调用相当于没初始化table, 那结果肯定对不上啊。

public String callEncode(String input) {
        byte[] data = input.getBytes(StandardCharsets.UTF_8);
        int inLen = data.length;

        Number sizeRet = module.callFunction(
                emulator,
                0x8ABC + 1,
                inLen
        );

        int outSize = sizeRet.intValue();

        MemoryBlock inBlock = memory.malloc(inLen + 1, true);
        Pointer in = inBlock.getPointer();
        in.write(0, data, 0, inLen);

        MemoryBlock outBlock = memory.malloc(outSize + 1, true);
        Pointer out = outBlock.getPointer();

        Number ret = module.callFunction(
                emulator,
                0x8B04 + 1,
                out,
                in,
                inLen
        );

        int retLen = ret.intValue(); // 包含末尾 \0
        String result = out.getString(0);

        System.out.println("outSize=" + outSize);
        System.out.println("retLen=" + retLen);
        System.out.println("result=" + result);

        inBlock.free();
        outBlock.free();

        return result;
    }

只调用这两个函数,发现结果终于对上了!!!

分类: app逆向java

0 条评论

发表回复

Avatar placeholder

您的邮箱地址不会被公开。 必填项已用 * 标注

站点统计

  • 文章总数:330 篇
  • 分类总数:20 个
  • 标签总数:193 个
  • 运行天数:1716 天
  • 访问总数:1257127 人次

浙公网安备33011302000604

辽ICP备20003309号