反编译工具
抓包或者逆向使用到的反编译工具:
- IDA,需要找破解版
- JEB,需要找破解版
- GDA,这是免费版的,不需要破解,一般功能可以够用了,也有收费的版本,官网
启动Frida客户端
#spwan模式,会重启app
frida -U -f com.piaoyou.piaoxingqiu -l d:/hook/objection_hooking_watch_class.txt -o d:/hook/hook.log
#attach模式,不会重启app
frida -U com.piaoyou.piaoxingqiu -l d:/hook/objection_hooking_watch_class.txt -o d:/hook/hook.log
参数-o,指定Frida的输出日志到文件
延迟执行
//立即执行
setImmediate(function() {
//延迟1秒执行test方法
setTimeout(test, 1000);
});
jnitrace 工具
jnitrace是一个Frida模块,跟踪Android应用程序中JNIAPI的使用情况
jnitrace -l libc.so com.piao.piaoxing
打印调用栈
//java打印堆栈
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
//SO打印堆栈,objection 的import命令,导入脚本,脚本中使用此语句打印调用栈,直接用frida执行脚本,会报错
console.log(' called from:\n' +Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join('\n') + '\n');
证书校验Hook
方式一checkServerTrusted
Java.perform(function() {
var ClassName = "com.android.org.conscrypt.Platform";
var Platform = Java.use(ClassName);
var targetMethod = "checkServerTrusted";
var len = Platform[targetMethod].overloads.length;
console.log(len);
for(var i = 0; i < len; ++i) {
Platform[targetMethod].overloads[i].implementation = function () {
console.log("class:", ClassName, "target:", targetMethod, " i:", i, arguments);
//printStack(ClassName + "." + targetMethod);
}
}
});
方法二 objection
使用objection
工具的下面这个命令,直接将SSL pinning
给disable
掉
android sslpinning disable
方法三 DroidSSLUnpinning
使用DroidSSLUnpinning项目,这是github上开源项目,项目地址
方法四 frida-interception-and-unpinning
同样是github上开源项目,项目地址
项目下载下来之后,执行以下命令:
frida -U -l ./config.js -l ./native-connect-hook.js -l ./android/android-proxy-override.js -l ./android/android-system-certificate-injection.js -l ./android/android-certificate-unpinning.js -l ./android/android-certificate-unpinning-fallback.js -f com.piao.piaoxing
枚举所有类
Java.perform(function(){
var classz = Java.enumerateLoadedClassesSync();
for(var i=0;i<classz.length;i++){
if (classz[i].indexOf("com.xiaojianbang.app") != -1){
console.log(classz[i])
}
}
})
枚举一个类的所有方法
var MainActivity = Java.use("com.xiaojianbang.app.MainActivity");
var methods = MainActivity.class.getDeclaredMethods();
for(var i=0;i<methods.length;i++){
console.log(methods[i].getName())
}
hook重载方法
var Utils = Java.use("com.xiaojianbang.app.Utils");
var overloads = Utils["test"].overloads;
for (var i=0;i< overloads.length;i++){
overloads[i].implementation = function () {
var params = "";
for(var j=0;j<arguments.length;j++){
params = params +arguments[j]+" "
}
console.log("params",params)
return this.test.apply(this,arguments);
};
}
hook所有方法
把想Hook的类的完整包名,卸载distClassArr数组中
setTimeout(function(){
Java.perform(function () {
try {
var distClassArr = ["com.efs.sdk.base.core.b.a$a","com.efs.sdk.base.core.b.a","com.efs.sdk.base.core.a.d","com.efs.sdk.base.core.a.c","com.efs.sdk.base.core.a.b$a","com.efs.sdk.base.core.a.b","com.efs.sdk.base.core.a.a$a","com.efs.sdk.base.core.a.a"]
var classz = Java.enumerateLoadedClassesSync();
for (var i = 0; i < classz.length; i++) {
try {
var class_name = classz[i];
if (class_name.indexOf("android") != -1
|| class_name.indexOf("androidx") != -1 || class_name.indexOf("sun.") != -1
|| class_name.indexOf("java") != -1 || class_name.indexOf("javax") != -1 || class_name.indexOf("kotlin") != -1
|| class_name.indexOf("okhttp") != -1 || class_name.indexOf("int") != -1
|| class_name.indexOf("org.") != -1 || class_name.indexOf("retrofit2") != -1
|| class_name.indexOf("libcore") != -1 || class_name.indexOf("dalvik") != -1
|| class_name.indexOf("char") != -1
|| class_name.indexOf("double") != -1|| class_name.indexOf("float") != -1
|| class_name.indexOf("long") != -1 || class_name.indexOf("short") != -1
|| class_name.indexOf("void") != -1 || class_name.indexOf("boolean") != -1
|| class_name.indexOf("byte") != -1 || class_name.indexOf("r2.") != -1
|| class_name.indexOf("okio.") != -1 || class_name.indexOf("junit.") != -1
|| class_name.indexOf("[") != -1 || class_name.indexOf("io.") != -1
) {
continue;
}
if (class_name.indexOf("com") != -1 && class_name.indexOf("piaoxingqiu") === -1) {
continue;
}
if (class_name.indexOf("com") != -1 && class_name.indexOf("juqi") === -1) {
continue;
}
if(distClassArr.indexOf(class_name) === -1){
continue;
}
console.log("Java.use:",class_name)
var cz = Java.use(class_name);
if(!cz){
continue;
}
//获取该类的所有公共方法(包含父类的),得到的是一个数组
//var methods = cz.class.getMethods();
//获取该类所有的私有方法(不包含父类),得到的是一个数组
var methods = cz.class.getDeclaredMethods();
console.log("all methods",class_name,methods)
for (var i1 = 0; i1 < methods.length-1; i1++) {
var classz_method_name = methods[i1].getName();
if(classz_method_name.indexOf("hashCode") != -1){
continue;
}
try {
var overloadAyy = cz[classz_method_name].overloads;
if (overloadAyy && overloadAyy.length>0){
for (var i2 = 0; i2 < overloadAyy.length; i2++) {
console.log("will be hook overload impl method: ",class_name,overloadAyy[i2])
overloadAyy[i2].implementation = function () {
console.log("call overload method ", class_name, classz_method_name)
console.log("this[classz_method_name]", this[classz_method_name])
if (arguments.length);
for (var j = 0; j < arguments.length; j++) {
console.log("param[" + j + "]: " + arguments[j]);
}
if(!this[classz_method_name] || this[classz_method_name] === 'undefined'){
console.log("method null..",classz_method_name)
}
try {
var retval = this[classz_method_name].apply(this, arguments);
console.log("retval=",retval)
return retval
} catch (error) {
console.log(error)
console.log("-----1-----", class_name, classz_method_name)
}
};
}
}else{
console.log("hook impl method: ",cz[classz_method_name])
cz[classz_method_name].implementation = function () {
if (arguments.length) console.log();
for (var j = 0; j < arguments.length; j++) {
console.log("param[" + j + "]: " + arguments[j]);
}
console.log("call method params", class_name, classz_method_name)
console.log("cz[classz_method_name]", cz[classz_method_name])
try {
var retval = cz[classz_method_name].apply(this, arguments);
console.log("retval=",retval)
return retval
} catch (error) {
console.log(error)
console.log("-----2-----", class_name, classz_method_name)
}
};
}
} catch (error) {
console.log(error)
console.log("-----3-----", class_name, classz_method_name)
}
}
}catch (error) {
console.log(error)
console.log("-----4-----", class_name)
}
}
} catch (error) {
console.log(error)
console.log("33333")
}
});
},10000);
hook 所有Http请求
下面不一定全
Java.perform(function() {
var URL = Java.use("java.net.URL");
var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection");
var HttpURLConnection = Java.use("java.net.HttpURLConnection");
var OutputStreamWriter = Java.use("java.io.OutputStreamWriter");
var BufferedReader = Java.use("java.io.BufferedReader");
var InputStreamReader = Java.use("java.io.InputStreamReader");
var urloverloads = URL["$init"].overloads;
for (var i=0;i< urloverloads.length;i++){
urloverloads[i].implementation = function () {
var params = "";
for(var j=0;j<arguments.length;j++){
params = params +arguments[j]+" "
}
console.log("params",params)
return this.$init.apply(this,arguments);
};
}
HttpsURLConnection.setDoOutput.implementation = function(value) {
console.log("[*] setDoOutput:", value);
return this.setDoOutput(value);
};
HttpsURLConnection.setRequestProperty.implementation = function(key, value) {
console.log("[*] setRequestProperty:", key, value);
return this.setRequestProperty(key, value);
};
HttpsURLConnection.getOutputStream.implementation = function() {
console.log("[*] getOutputStream");
return this.getOutputStream();
};
HttpURLConnection.setRequestProperty.implementation = function(key, value) {
console.log("[*] setRequestProperty:", key, value);
return this.setRequestProperty(key, value);
};
HttpURLConnection.setDoOutput.implementation = function(value) {
console.log("[*] setDoOutput:", value);
return this.setDoOutput(value);
};
HttpURLConnection.getOutputStream.implementation = function() {
console.log("[*] getOutputStream");
return this.getOutputStream();
};
var writeoverloads = OutputStreamWriter["write"].overloads;
for (var i=0;i< writeoverloads.length;i++){
writeoverloads[i].implementation = function () {
var params = "";
for(var j=0;j<arguments.length;j++){
params = params +arguments[j]+" "
}
console.log("params",params)
return this.write.apply(this,arguments);
};
}
var readLineoverloads = BufferedReader["readLine"].overloads;
for (var i=0;i< readLineoverloads.length;i++){
readLineoverloads[i].implementation = function () {
var params = "";
for(var j=0;j<arguments.length;j++){
params = params +arguments[j]+" "
}
console.log("params",params)
return this.readLine.apply(this,arguments);
};
}
InputStreamReader.$init.overload('java.io.InputStream').implementation = function(stream) {
console.log("[*] InputStreamReader.init:", stream);
return this.$init(stream);
};
});
hook 所有Socket请求
function print_bytes(bytes) {
var buf = Memory.alloc(bytes.length);
Memory.writeByteArray(buf, byte_to_ArrayBuffer(bytes));
console.log(hexdump(buf, {offset: 0, length: bytes.length, header: false, ansi: true}));
}
function byte_to_ArrayBuffer(bytes) {
var size=bytes.length;
var tmparray = [];
for (var i = 0; i < size; i++) {
var val = bytes[i];
if(val < 0){
val += 256;
}
tmparray[i] = val
}
return tmparray;
}
function hook_tcp(){
var socketClass= Java.use("java.net.Socket");
socketClass.$init.overload('java.net.InetAddress', 'int').implementation=function(ip,port){
console.log("socket$init ",ip,":",port);
return this.$init(ip,port);
};
var outputClass=Java.use("java.net.SocketOutputStream");
outputClass.socketWrite0.implementation=function(fd,buff,off,len){
console.log("tcp write fd:",fd);
print_bytes(buff);
return this.socketWrite0(fd,buff,off,len);
};
var inputClass=Java.use("java.net.SocketInputStream");
inputClass.socketRead0.implementation=function(fd,buff,off,len,timeout){
var res=this.socketRead0(fd,buff,off,len,timeout);
console.log("tcp read fd:",fd)
print_bytes(buff);
return res;
};
}
//SSL_write(long sslNativePointer, FileDescriptor fd,
// SSLHandshakeCallbacks shc, byte[] b, int off, int len, int writeTimeoutMillis)
//SSL_read(long sslNativePointer, FileDescriptor fd, SSLHandshakeCallbacks shc,
// byte[] b, int off, int len, int readTimeoutMillis)
function getSocketData(fd){
console.log("fd:",fd);
var socketType=Socket.type(fd)
if(socketType!=null){
var res="type:"+socketType+",loadAddress:"+JSON.stringify(Socket.localAddress(fd))+",peerAddress"+JSON.stringify(Socket.peerAddress(fd));
return res;
}else{
return "type:"+socketType;
}
}
function getSocketData2(stream){
var data0=stream.this$0.value;
var sockdata=data0.socket.value;
return sockdata;
}
function hook_ssl(){
var NativeCryptoClass= Java.use("com.android.org.conscrypt.NativeCrypto");
NativeCryptoClass.SSL_write.implementation=function(sslPtr,nativeSsl,fd,shc,b,off,len,timeout){
console.log("enter SSL_write");
print_bytes(b);
return this.SSL_write(sslPtr,nativeSsl,fd,shc,b,off,len,timeout);
};
NativeCryptoClass.SSL_read.implementation=function(sslPtr,nativeSsl,fd,shc,b,off,len,timeout){
console.log("enter SSL_read");
var res=this.SSL_read(sslPtr,nativeSsl,fd,shc,b,off,len,timeout);
print_bytes(b);
return res;
};
}
//SSLOutputStream write(byte[] buf, int offset, int byteCount)
//SSLInputStream read(byte[] buf, int offset, int byteCount)
function hook_ssl2(){
var SSLOutputClass=Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream");
SSLOutputClass.write.overload('[B', 'int', 'int').implementation=function(buf,off,cnt){
console.log(getSocketData2(this));
print_bytes(buf);
return this.write(buf,off,cnt);
};
var SSLInputClass=Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream");
SSLInputClass.read.overload('[B', 'int', 'int').implementation=function(buf,off,cnt){
var res=this.read(buf,off,cnt);
console.log(getSocketData2(this));
print_bytes(buf);
return res;
}
}
function hook_jni_ssl_enc(){
var writePtr=Module.getExportByName("libc.so","write");
var readPtr=Module.getExportByName("libc.so","read");
console.log("write:",writePtr,",read:",readPtr);
Interceptor.attach(writePtr,{
onEnter:function(args){
var fd=args[0];
var buff=args[1];
var size=args[2];
var sockdata=getSocketData(fd.toInt32());
if(sockdata.indexOf("tcp") != -1){
console.log(sockdata);
console.log(hexdump(buff,{length:size.toInt32()}));
}
},
onLeave:function(retval){
}
});
Interceptor.attach(readPtr,{
onEnter:function(args){
this.fd=args[0];
this.buff=args[1];
this.size=args[2];
},
onLeave:function(retval){
var sockdata=getSocketData(this.fd.toInt32());
if(sockdata.indexOf("tcp") != -1){
console.log(sockdata);
console.log(hexdump(this.buff,{length:this.size.toInt32()}));
}
}
});
}
function hook_jni_ssl(){
var sslWritePtr=Module.getExportByName("libssl.so","SSL_write");
var sslReadPtr=Module.getExportByName("libssl.so","SSL_read");
console.log("sslWrite:",sslWritePtr,",sslRead:",sslReadPtr);
var sslGetFdPtr=Module.getExportByName("libssl.so","SSL_get_rfd");
var sslGetFdFunc=new NativeFunction(sslGetFdPtr,'int',['pointer']);
//int SSL_write(SSL *ssl, const void *buf, int num)
Interceptor.attach(sslWritePtr,{
onEnter:function(args){
var sslPtr=args[0];
var buff=args[1];
var size=args[2];
var fd=sslGetFdFunc(sslPtr);
var sockdata=getSocketData(fd);
console.log(sockdata);
console.log(hexdump(buff,{length:size.toInt32()}));
},
onLeave:function(retval){
}
});
//int SSL_read(SSL *ssl, void *buf, int num)
Interceptor.attach(sslReadPtr,{
onEnter:function(args){
this.sslPtr=args[0];
this.buff=args[1];
this.size=args[2];
},
onLeave:function(retval){
var fd=sslGetFdFunc(this.sslPtr);
var sockdata=getSocketData(fd);
console.log(sockdata);
console.log(hexdump(this.buff,{length:this.size.toInt32()}));
}
});
}
function hook_jni_tcp(){
var sendtoPtr=Module.getExportByName("libc.so","sendto");
var recvfromPtr=Module.getExportByName("libc.so","recvfrom");
console.log("sendto:",sendtoPtr,",recvfrom:",recvfromPtr);
//sendto(int fd, const void *buf, size_t n, int flags, const struct sockaddr *addr, socklen_t addr_len)
Interceptor.attach(sendtoPtr,{
onEnter:function(args){
var fd=args[0];
var buff=args[1];
var size=args[2];
var sockdata=getSocketData(fd.toInt32());
console.log(sockdata);
console.log(hexdump(buff,{length:size.toInt32()}));
},
onLeave:function(retval){
}
});
//recvfrom(int fd, void *buf, size_t n, int flags, struct sockaddr *addr, socklen_t *addr_len)
Interceptor.attach(recvfromPtr,{
onEnter:function(args){
this.fd=args[0];
this.buff=args[1];
this.size=args[2];
},
onLeave:function(retval){
var sockdata=getSocketData(this.fd.toInt32());
console.log(sockdata);
console.log(hexdump(this.buff,{length:this.size.toInt32()}));
}
});
}
function hook_java(){
Java.perform(function(){
// hook_tcp();
// hook_ssl();//不打印socket IP地址
// hook_ssl2();//打印socket IP地址
// hook_jni_tcp();
hook_jni_ssl_enc();//密文
//hook_jni_ssl();//明文
})
}
function getFullName(name){
Java.perform(function(){
Java.enumerateLoadedClassesSync().forEach(function(className){
if(className.indexOf(name)!=-1){
console.log(className);
}
})
});
}
function main(){
hook_java();
// getFullName("SSLOutputStream")
}
setImmediate(main);
实现okhttp3.Interceptor
Java.perform(function () {
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
var Buffer = Java.use("com.android.okhttp.okio.Buffer");
var Interceptor = Java.use("okhttp3.Interceptor");
var MyInterceptor = Java.registerClass({
name: "okhttp3.MyInterceptor",
implements: [Interceptor],
methods: {
intercept: function (chain) {
var request = chain.request();
try {
console.log("MyInterceptor.intercept onEnter:", request, "\nrequest headers:\n", request.headers());
var requestBody = request.body();
var contentLength = requestBody ? requestBody.contentLength() : 0;
if (contentLength > 0) {
var BufferObj = Buffer.$new();
requestBody.writeTo(BufferObj);
try {
console.log("\nrequest body String:\n", BufferObj.readString(), "\n");
} catch (error) {
try {
console.log("\nrequest body ByteString:\n", ByteString.of(BufferObj.readByteArray()).hex(), "\n");
} catch (error) {
console.log("error 1:", error);
}
}
}
} catch (error) {
console.log("error 2:", error);
}
var response = chain.proceed(request);
try {
console.log("MyInterceptor.intercept onLeave:", response, "\nresponse headers:\n", response.headers());
var responseBody = response.body();
var contentLength = responseBody ? responseBody.contentLength() : 0;
if (contentLength > 0) {
console.log("\nresponsecontentLength:", contentLength, "responseBody:", responseBody, "\n");
var ContentType = response.headers().get("Content-Type");
console.log("ContentType:", ContentType);
if (ContentType.indexOf("video") == -1) {
if (ContentType.indexOf("application") == 0) {
var source = responseBody.source();
if (ContentType.indexOf("application/zip") != 0) {
try {
console.log("\nresponse.body StringClass\n", source.readUtf8(), "\n");
} catch (error) {
try {
console.log("\nresponse.body ByteString\n", source.readByteString().hex(), "\n");
} catch (error) {
console.log("error 4:", error);
}
}
}
}
}
}
} catch (error) {
console.log("error 3:", error);
}
return response;
}
}
});
var ArrayList = Java.use("java.util.ArrayList");
var OkHttpClient = Java.use("okhttp3.OkHttpClient");
console.log(OkHttpClient);
OkHttpClient.$init.overload('okhttp3.OkHttpClient$Builder').implementation = function (Builder) {
console.log("OkHttpClient.$init:", this, Java.cast(Builder.interceptors(), ArrayList));
this.$init(Builder);
};
var MyInterceptorObj = MyInterceptor.$new();
var Builder = Java.use("okhttp3.OkHttpClient$Builder");
console.log(Builder);
Builder.build.implementation = function () {
this.interceptors().clear();
//var MyInterceptorObj = MyInterceptor.$new();
this.interceptors().add(MyInterceptorObj);
var result = this.build();
return result;
};
Builder.addInterceptor.implementation = function (interceptor) {
this.interceptors().clear();
//var MyInterceptorObj = MyInterceptor.$new();
this.interceptors().add(MyInterceptorObj);
return this;
//return this.addInterceptor(interceptor);
};
console.log("hook_okhttp3...");
});