【Android逆向】破解看雪test3.apk方案一
1. test3.apk 安装到手机
2. 发现其实际逻辑和之前的test2.apk基本一致,逆向so查看到加入了一些检查逻辑
代码:
jstring __fastcall fuck(JNIEnv *env, jclass jcls, jstring str_)
{
......
if ( !str_ )
return 0;
src = (unsigned __int8 *)_JNIEnv::GetStringUTFChars(env, str_, 0);
v20 = "REAL";
clazz = _JNIEnv::FindClass(env, "android/os/Build");
fieldID = _JNIEnv::GetStaticFieldID(env, clazz, "FINGERPRINT", "Ljava/lang/String;");
StaticObjectField = (_jstring *)_JNIEnv::GetStaticObjectField(env, clazz, fieldID);
if ( function_check_tracerPID()
|| system_getproperty_check()
|| (StringUTFChars = (char *)_JNIEnv::GetStringUTFChars(env, StaticObjectField, 0), strstr(StringUTFChars, "aosp")) )
{
v20 = "FAKE";
}
strcat((char *)src, v20);
obj = (_jobject *)j_o0OoOOOO(env, 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");
v14 = _JNIEnv::CallStaticObjectMethod(env, Class, methodID, v4);
v13 = _JNIEnv::GetMethodID(env, Class, "digest", "([B)[B");
v12 = _JNIEnv::FindClass(env, "java/lang/String");
v11 = _JNIEnv::GetMethodID(env, v12, "getBytes", "()[B");
v10 = _JNIEnv::CallObjectMethod(env, obj, v11);
array = (_jbyteArray *)_JNIEnv::CallObjectMethod(env, v14, v13, v10);
ByteArrayElements = _JNIEnv::GetByteArrayElements(env, array, 0);
for ( i = 0; i <= 15; ++i )
sprintf((char *)&v27[i], "%02x", (unsigned __int8)ByteArrayElements[i]);
v26 = (char *)j_ll11l1l1ll(src);
strcat(v26, (const char *)v27);
v6 = j_o0OoOOOO(env, (const unsigned __int8 *)v26);
_JNIEnv::ReleaseStringUTFChars(env, str_, src);
free(v26);
return v6;
}
从这里可以看到,增加了一个检查system_getproperty_check
和strstr(StringUTFChars, "aosp")
,如果满足条件就会把 REAL 改为 FAKE,追加到字符串后面,那么就怎么都不会成功得到正确结果了
就不会进入到if块中;方案一是strcat
的入参,让第二个参数传入时永远是REAL
方案一代码:
function main() {
Java.perform(function () {
hookCheck()
var MainActivityHandler = Java.use('com.roysue.easyso1.MainActivity')
for (var i = 87650; i <= 87700; i++) {
var str = i + ""
var ret = MainActivityHandler.Sign(str)
if (i % 1000 == 0) {
console.log("now is", str);
}
//console.log(ret)
if (ret == "57fdeca2cac0509b2e9e5c52a5b573c1608a33ac1ffb9e8210d2e129557e7f1b") {
console.log("find it : " + str)
break
}
}
console.log("end : ")
})
}
function hookCheck() {
var lib_hanlder = Process.findModuleByName("libroysue.so");
console.log("lib_handler: " + lib_hanlder)
if (lib_hanlder) {
var addr_hook = lib_hanlder.base.add(0x00037226 + 0x1)
Interceptor.attach(addr_hook,{
onEnter: function(args) {
console.log(" === hook before")
console.log("=== r1:" + this.context.r1.readCString())
Memory.protect(this.context.r1, 1024, 'rw-')
this.context.r1.writeUtf8String("REAL")
console.log("=== r1:" + this.context.r1.readCString())
},
onLeave:function(retVal) {
console.log(" === hook after: ")
//print_dump(retVal, 128)
console.log("=== after r1:" + this.context.r1.readCString())
}
})
}
}
setTimeout(main, 2000)
关键汇编代码
.text:00037220 24 91 STR R1, [SP,#0xD8+var_48]
.text:00037222 26 98 LDR R0, [SP,#0xD8+var_40] ; char *
.text:00037224 24 99 LDR R1, [SP,#0xD8+var_48] ; char *
.text:00037226 FC F7 00 ED BLX strcat
这里的 0x00037226 就是 strcat 对应的偏移地址,其实也可以hook 0x00037224,结果是一样的(hook的这一行相当于是执行后进入onEnter)
日志:
=== hook before
=== r1:FAKE
=== r1:REAL
=== hook before
=== r1:REAL
=== r1:REAL
=== hook before
=== r1:REAL
=== r1:REAL
=== hook before
=== r1:REAL
=== r1:REAL
=== hook before
=== r1:REAL
=== r1:REAL
find it : 87654
end :
从日志上课 onLeave没执行,原因是这里不是一个标准的函数特征,所以没有入栈出栈逻辑,所以就onLeave没执行