本文为看雪论坛优秀文章
看雪论坛作者ID:taobluesky
一 准备环境
jeb-3.24
jadx-gui-1.3.3-1
xcube
android 7.0(真机)
二 初探

然后接下来就跟预想的一样,有检测frida的代码,一旦spwan或者attach上去,app的进程立马就自动中止了。
[FRD AL00::com.
**.**
***
]-> Process terminated
[FRD AL00::com.
**.**
***
]->
三 正式开始
// okhttp4
try
{
var
CertificatePinner = Java.
use
(
'okhttp3.CertificatePinner'
);
CertificatePinner.check.overload(
'java.lang.String'
,
'java.util.List'
).implementation =
function
(str)
{
writeFile(
'! Intercepted okhttp4 in [check()]: '
+ str);
return
;
};
try
{
//.overload('java.lang.String', 'kotlin.jvm.functions.Function0')
CertificatePinner.check$okhttp.implementation =
function
(str, _)
{
writeFile(
'! Intercepted okhttp4 in [check$okhttp]: '
+ str);
return
;
};
}
catch
(ex) {
writeFile(
"is this Okhttp3 ?!"
);
}
writeFile(
'* Setup okhttp4 pinning'
)
}
catch
(err) {
writeFile(
'* Unable to hook into okhttp4 pinner'
)
writeFile(err);
}
可以看到登录接口的密码是加密的, 还有sign字段。首先寻找密码加密的关键代码如下:
再跟入:
使用rsa对密码进行加密,这个unname方法就是一个绝妙的注入点,编写获取public key的脚本:
// 密码加密:输出RSA的pubkey
var
this3 = Java.use(
"com.**.*****.encrypt.this3"
);
this3.unname.implementation =
function
(
str, str2
)
{
writeFile(
'unname is called'
);
writeFile(
"pubkey:"
+ str2);
var
ret =
this
.unname(str, str2);
writeFile(
'unname ret value is '
+ ret);
return
ret;
};
try
{
String
password =
"qed"
;
String
publicKey =
"MIGfMA0GCSqGSIb3D********qGWVMv5z6FwIDAQAB"
;
byte[] decoded = Base64.decode(publicKey, Base64.DEFAULT);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(
"RSA"
).generatePublic(
new
X509EncodedKeySpec(decoded));
Cipher instance = Cipher.getInstance(
"RSA/ECB/PKCS1Padding"
);
instance.init(ENCRYPT_MODE, pubKey);
String
pwdenc = Base64.encodeToString(instance.doFinal(password.getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT);
Log.e(TAG, pwdenc);
}
catch
(Exception e) {
e.printStackTrace();
}
再跟进SignUtil类里面:
可以看到关键的方法handle在native-lib里面, 通过hookget方法可以获取传入验签的参数和sign结果:
var
SignUtil = Java.use(
"com.**.****.encrypt.SignUtil"
);
SignUtil.
get
.implementation = function(str, str2, str3, str4){
writeFile(
'get is called'
);
writeFile(
"str:"
+ str);
writeFile(
"str2:"
+ str2);
writeFile(
"str3:"
+ str3);
writeFile(
"str4:"
+ str4);
var
ret =
this
.
get
(str, str2, str3, str4);
writeFile(
'get ret value is '
+ ret);
return
ret;
};
sub_45A40函数:
上图已经对关键的方法和变量做了注释,逻辑已经很清晰了,sign=sha256(_requestData+_requestDataList+_token+_clientTime+salt_key),为了验证拼接得对不对,可以把temp_str输出,寻找一出合适的hook点:
这处就可以进行hook来输出,对应的asm代码如下:
这边要注意一下这个temp_str的类型是std::string,要想办法转换成cstring才能打印输出,我们可以利用上面分析出来的string_to_cstr函数来转换:
分析完了,我们来写hook代码:
var
libnative_addr = Module.findBaseAddress(
"libnative-lib.so"
)
writeFile(
"libnative_addr is: "
+ libnative_addr)
// 内部一个std::string转cstring方法
var
str_to_c =
new
NativeFunction(libnative_addr.add(
0x45A85
),
"pointer"
, [
"pointer"
]);
// 输出签名字符串
try
{
var
addr_45266 = libnative_addr.add(
0x45267
);
writeFile(
"addr_45266: "
+ addr_45266);
Interceptor.attach(addr_45266, {
onEnter
:
function
(
args
)
{
writeFile(
"ohwawawa"
);
var
ret = str_to_c(
this
.context.r2);
writeFile(
"addr_45266 OnEnter sign string:"
+ Memory.readCString(ret));
},
onLeave
:
function
(
retval
)
{
//console.log("retval is :", retval)
}
});
}
catch
(err) {
writeFile(
"[!!!!!!!!!!!!] "
+ err);
}
原文始发于微信公众号(电子物证):【APP加密算法和签名算法的还原】
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论