文章例子已经上传到github:
https://github.com/aprz512/Android-Crack/tree/master/unicorn
打开app:

我们的目标是模拟点击LV1按钮所执行的 so 中的算法逻辑。
找到对应的jni函数:
int __fastcall Java_com_sec_udemo_MainActivity_sign_1lv1(_JNIEnv *a1, int a2, int a3)
{
const char *v3; // r0
int v5; // [sp+4h] [bp-8Ch]
const char *v6; // [sp+Ch] [bp-84h]
int StringUTFChars; // [sp+28h] [bp-68h]
char v10[12]; // [sp+38h] [bp-58h] BYREF
char v11[64]; // [sp+44h] [bp-4Ch] BYREF
StringUTFChars = _JNIEnv::GetStringUTFChars(a1, a3, 0);
sub_9D84(v10, StringUTFChars);
_JNIEnv::ReleaseStringUTFChars(a1, a3, StringUTFChars);
memset(v11, 0, sizeof(v11));
v3 = (const char *)sub_9DE6(v10);
sign_lv1(v3, v11);
v6 = (const char *)sub_9DE6(v10);
_android_log_print(3, "udemo", "sign_1lv1 was called. sign data:%s sign:%s", v6, v11);
v5 = _JNIEnv::NewStringUTF(a1, v11);
std::string::~string(v10);
return v5;
}
上面的反汇编代码中,关于GetStringUTFChars的那一坨是将java层传递过来的字符串转成c层的字符串,不用关心。
核心代码在 sign_lv1 中,如果有怀疑的地方,可以利用 frida hook 确认一下。
a3 是 java 层字符串,转换过之后储存到了 v3 里面,v3 经过 sign_lv1 变化,将结果又储存到 v11 里面。
v11 再转成java层字符串,储存到 v5 里面,返回给java层。
看 sign_lv1 里面的逻辑:
int __fastcall sign_lv1(const char *a1, char *a2)
{
int v2; // r0
int v4; // [sp+Ch] [bp-5Ch]
int v5; // [sp+18h] [bp-50h]
int j; // [sp+1Ch] [bp-4Ch]
int i; // [sp+20h] [bp-48h]
unsigned __int8 v10[4]; // [sp+2Ch] [bp-3Ch] BYREF
int v11; // [sp+30h] [bp-38h]
int v12; // [sp+34h] [bp-34h]
int v13; // [sp+38h] [bp-30h]
unsigned __int8 v14[32]; // [sp+3Ch] [bp-2Ch] BYREF
v13 = 986637;
v12 = 202050057;
v11 = 134678021;
*(_DWORD *)v10 = 67305728;
for ( i = 0; i <= 31; ++i )
v14[i] = 0;
for ( j = 0; ; ++j )
{
v4 = 0;
if ( j <= 31 )
{
v2 = (unsigned __int8)a1[j];
if ( a1[j] )
v2 = 1;
v4 = v2;
}
if ( !(v4 << 31) )
break;
v14[j] = a1[j];
}
v5 = 0;
tea_encrypt(v10, v14, 4);
while ( v5 <= 15 )
sprintf(a2, "%s%02x", a2, v14[v5++]);
return _stack_chk_guard;
}
这里面大致可以分为三段逻辑: