安卓逆向经典案例——XX牛
按钮绑定方式
1.抓包
2.查看界面元素,找到控件id
通过抓包,发现点击登录后,才会出现Encrpt加密信息,所以我们通过控件找到对应id:btn_login
按钮绑定方法——第四种
public class LoginActivity extends BasicActivity implements View.OnClickListener {
private Type DEFAULT_TYPE;
private EditText etMobile;
private EditText etPwd;
private long firstime = 0;
private Map<String, String> para;
private JsonRequest request;
case C0828R.C0830id.btn_login /* 2131558593 */:
String mobile = ((Object) this.etMobile.getText()) + "".trim();
String pwd = ((Object) this.etPwd.getText()) + "".trim();
Utils.hideSoftInput(this, this.etPwd);
if (checkInput(mobile, pwd)) {
login(mobile, pwd);
return;
}
return;
如果获取的id是btn_login,会执行获取账号和密码。
这里@override是注解,当编译时,用于检查父类中有没有对应方法
比如这里的View.onClickListener接口中有名为onClick的方法。如果没有编译器会报错
法2:手动搜索字符串定位关键代码
POST /api/user/login HTTP/1.1
Content-Type: application/json; charset=utf-8
User-Agent: Dalvik/2.1.0 (Linux; U; Android 13; Mi 10 Build/TKQ1.221114.001)
Host: api.dodovip.com
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Length: 228
{"Encrypt":"NIszaqFPos1vd0pFqKlB42Np5itPxaNH\/\/FDsRnlBfgL4lcVxjXii\/UNcdXYMk0EHdbdwRDGADMQ\nFdnMX5Qq4ZVksebFB9u5C7wf1Tg0iB6TtO6yIRklzyaNV1Emb4ZfamTp8A17jqtT53LRz7g8cWbm\ntmuSM4kXcPN8tgfiwijQvx+XNn\/8BMVPVolqQvWQlI7hPd2OtWw=\n"}
搜索链接——/api/user/login
使用搜索的方法有很大的局限性。
因为关键函数(加密方法)可以被替换分散在多个类文件中,所以必须要在系统函数层面进行逆向,比如在这里MessageDigest.getInstance是系统函数。
- 使用系统函数,函数名不会改变
MessageDigest md5 = MessageDigest.getInstance("MD5")
//Class<?> a = Class.forName(dd("..."))这样很难找到
//
搜索技巧,把链接拆分,因为链接可能是被拼接在一起的,所以拆分才能检索到关键代码
优先找到和包名相关的代码。
发现两个可能的方法,通过动态调试和hook的方法,分析
// 使用Frida的Java.perform函数来确保我们的代码在Java虚拟机的上下文中执行。
Java.perform(function (){
// 使用Java.use来获取Java类的引用。这里我们获取的是com.dodonew.online.http.JsonRequest类的引用。
var jsonRequest = Java.use("com.dodonew.online.http.JsonRequest");
// 打印获取到的类引用到控制台,用于调试。
console.log("jsonRequest:", jsonRequest);
// 修改JsonRequest类的paraMap方法的实现。这里我们使用了Frida的implementation属性来实现方法的挂钩。
jsonRequest.paraMap.implementation = function(a){
// 当paraMap方法被调用时,打印传递给它的参数a。
console.log("params1", a);
// 调用原始的paraMap方法。这里的this指的是JsonRequest类的当前实例。
this.paraMap(a);
}
})
使用paraMap方法无法hook到账号和密码,尝试另一个方法
Java.perform(function (){
var jsonRequest =Java.use("com.dodonew.online.http.JsonRequest");//对特定类名发起hook
console.log("jsonRequest:",jsonRequest);//打印类的信息
jsonRequest.paraMap.implementation = function(a){
console.log("params1",a);//将类中到的params用a替换
this.paraMap(a);//
}
jsonRequest.addRequestMap.implementation = function(a,b){
console.log("params1",a,b);//将类中到的params用a替换
this.addRequestMap(a,b);//
}
})
//frida -UF -l test.js
Error: addRequestMap(): has more than one overload, use .overload(<signature>) to choose from:
.overload('java.util.Map')
.overload('java.util.Map', 'int')
.overload('java.util.Map', 'boolean')
有多个重载函数选择.overload('java.util.Map', 'int')
jsonRequest.addRequestMap.overload('java.util.Map', 'int').implementation = function(a,b){
console.log("params1",a,b);//将类中到的params用a替换
this.addRequestMap(a,b);//
}
这样成功hook到函数。可以使用java对象Hashmap的toString方法(以键值对的形式被hashmap重载输出)
import java.util.HashMap;
public class Main {
public static void main(String[] args){
HashMap<Object,Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("username","arii");
objectObjectHashMap.put("password","123456");
System.out.println(objectObjectHashMap.toString());
}
}
jsonRequest.addRequestMap.overload('java.util.Map', 'int').implementation = function(a,b){
console.log("addRequestMap params",a,b);//将类中到的params用a替换
var bb = Java.cast(a,Java.use("java.util.HashMap"));//类型转换
console.log("addRequestMap params",bb.toString());
// console.log("addRequestMap params",a.get("username"));//这里hook的是java的类型,所以可以调用java中的toString方法
this.addRequestMap(a,b);//
}
配置webstorm代码提示
npm i @types/frida-gum
hook addRequestMap方法
通过hookRequestUtil.paraMap方法去分析返回的值。这里虽然能看到IV和KEY的值,但是防止热加载,热修复(在程序编译运行时,会修改参数),要用hook
结果
[Mi 10::XX在线 ]-> RequestUtilparams1 {"equtype":"ANDROID","loginImei":"Androidnull","sign":"45D0209BE26E599E4E6FF49A2B3E84A9","timeStamp":"1717570679351","userPw
d":"ffg","username":"225"} 65102933 32028092
hook前后对比:
DES 算法是分组密码算法,这意味着它将明文数据分割成固定长度的块(通常为 64 位,对应 8 个字节),并对每个块进行独立的加密操作。
关键代码快速定位
var btn_login_id = Java.use("com.dodonew.online.R$id").btn_login.value;
console.log("btn_login_id", btn_login_id);
var appCompatActivity = Java.use("android.support.v7.app.AppCompatActivity");
appCompatActivity.findViewById.implementation = function (a) {
if(a == btn_login_id){
showStacks();
console.log("appCompatActivity.findViewById: ", a);
}
return this.findViewById(a);
}