JAVA层HOOK
在开始之前,依旧是熟悉的工具官方文档。因本人英文水平不高,大佬们可通过官网(https://frida.re/docs/home/)或看雪(https://bbs.pediy.com/thread-222729.htm)自取准确说明文档观看。
1.JAVA方法基础
Java方法是语句的集合,它们在一起执行一个功能。
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
在Java中方法有一个的样子;
修饰符:告诉编译器如何调用该方法。定义了该方法的访问类型。
返回值类型:就是功能结果的数据类型。当这个方法没有返回值的时候,返回值的类型为void;当这个方法有返回值的时候,返回值的类型根据返回的数据来确定*(如果有返回值,必须使用return)**。*
方法名:符合命名规则即可。
参数:实际参数:就是实际参与运算的。形式参数;就是方法定义上的,用于接收实际参数的。
参数类型:就是参数的数据类型
参数名:就是变量名
方法体语句:就是完成功能的代码。
return*:结束方法的。*
返回值:就是功能的结果,由return带给调用者。
在知晓基本的Java方法结构后,开始本处的重点之一。
1) 普通方法
定义:简单的说方法就是完成特定功能的代码块。
普通方法分为:有明确返回值的方法和没有明确返回值的方法。
A.有明确返回值的方法的调用
可以单独调用、输出调用、赋值调用。
B.没有明确返回值的方法调用(void类型方法的调用)
只能单独使用。
举个简单的例子:
public class Fangfa {
// 主方法
public static void main(String args[]) {
System.out.println(add(10,20));
}
//返回两个整数相加
public static int add(int a , int b){
return a + b ;
}
}
通过例子可以发现,普通方法就是我们写的最常见、最大众化的。满足方法基本结构。
2) 构造方法
构造方法是类的一种特殊方法,用来初始化类的一个新的对象,在创建对象(new 运算符)之后自动调用。Java 中的每个类都有一个默认的构造方法,并且可以有一个以上的构造方法。
构造方法需满足条件:
1、方法名与类名同名。
2、在方法中不能使用return 语句返回即为该方法没有任何返回值,包括 void。
3、在方法名的前面没有返回值的类型声明
通过上述条件可发现,构造方法会在普通的方法结构上发生一些变化,举个例子:
public class Constructor {
// 给Constructor类定义了一个成员变量name
private String name;
// 无参构造方法,方法名必须跟类名一致,即首字母需要大写
public Constructor() {
System.out.println("调用无参数的构造方法。。");
}
// 带一个参数的构造方法
public Constructor(String name) {
// 通过this()调用无参构造方法
this();
this.name = name;
// 构造方法里可以调用普通方法
commonMethod();
}
public void commonMethod() {
System.out.println("调用普通方法");
}
// 写个主函数测试一下
public static void main(String[] args) {
// 通过无参构造创建一个Constructor对象
Constructor cons1 = new Constructor();
cons1.name = "我是通过无参构造器创建的对象";
System.out.println(cons1);
System.out.println("=====================我是分隔线=====================");
// 通过有参构造方法创建另一个对象
Constructor cons2 = new Constructor("我是通过有参构造方法创建的对象");
System.out.println(cons2);
}
public String toString() {
return this.getClass().getSimpleName() + "[我的名字是:" + name + "]";
}
}
运行结果如下:
3) 重载方法
重载方法(Overloading)的定义:如果有两个方法的方法名相同,但参数不一致,哪么可以说一个方法是另一个方法的重载。 具体满足条件包括:
- 方法名相同
- 方法的参数类型,参数个不一样
- 方法的返回类型可以不相同
- 方法的修饰符可以不相同
- main 方法也可以被重载
举个例子:
class MyClass {
int height;
MyClass() {
System.out.println("无参数构造函数");
height = 4;
}
MyClass(int i) {
System.out.println("房子高度为 " + i + " 米");
height = i;
}
void info() {
System.out.println("房子高度为 " + height + " 米");
}
void info(String s) {
System.out.println(s + ": 房子高度为 " + height + " 米");
}
}
public class MainClass {
public static void main(String[] args) {
MyClass t = new MyClass(3);
t.info();
t.info("重载方法");
//重载构造函数
new MyClass();
}
}
程序执行结果如下:
2.Frida安装
对于安装这部分,大家可在互联网自行搜索安装。个人建议是使用python2的旧版本,因为在burp中有一个brida工具来帮助使用Frida。Brida的使用可参考下述文章(https://blog.csdn.net/weixin_39190897/article/details/102691898),网络上存在说python3也可使用该工具,不过本人尝试后未成功。
在安装Frida时要主要参考最新安装教程所涉及的Frida及frida-tools的版本,直接使用pip进行安装存在frida安装最新版可以成功,但是安装frida-tools时则会报错。
3.普通方法HOOK
针对普通方法、构造方法、重载方法可使用APP安装包:方法hook.app
安装并启动APP:
此处插入一个题外话,大佬们是不是看见一个可爱的群号呀,是不是想去搜一下呀。我替大家搜过了,知道了结果也就不打码了。结果是这个样子滴:
看到这个结果是不是可以放下手机了???
言归正传,发现APP在启动后弹出3000,点击普通方法弹出4000。
使用JADX等反编译工具对APP进行反编译操作,获取到源码。
在安卓源码中存在文件AndroidManifest.xml,在这个文件内可寻找到中找到app的入口地址。访问MainActivity,其代码如下:
通过代码可看出应用在运行后执行了一个onCreate方法,方法中包含一个方法为getCalc。这部分主要是写了三个按键的事件和监听,在方法最后一行出现了和按键无关的数字1000,2000。这个时候还记得应用运行时的弹出的3000吗?
本节是使用Frida对普通函数进行hook,在app界面对应为按钮1(button1),对于按键的响应事件被定义在onClick方法里,效果为点击button1后弹出一个文本。在代码中的关键方法为Utils.getCalc,这也是要hook的关键。在查看该方法的代码:
发现该方法为简单的加法运算。启动firda对该函数进行hook。需要编写一个脚本来进行操作,具体代码这么写呢?不要着急,frida官网给出了脚本的模版(https://frida.re/docs/examples/android/)通过本例写出的脚本,告诉大家每部分是干嘛的。
frida的hook代码如下:
import frida, sys
//导入脚本所需的包
jscode = """
//hook主要执行的代码区域
Java.perform(function () {
//hook核心代码包含在Java.perform函数之中。
var util = Java.use('com.xiaojianbang.app.Utils');
//此处com.xiaojianbang.app.Utils为将要hook的方法所处的类的路径。
util.getCalc.implementation = function (a, b) {
// getCalc为在获取类路径后将要hook方法名。在这里更改类的方法的实现(implementation)
console.log("Hook Start...");
send(a);
send(b);
send("Success!");
return this.getCalc(a,b)
//以上三行为hook代码的执行代码。
} });
"""
def message(message, data):
if message["type"] == 'send':
print("[] {0}".format(message['payload']))
else:
print(message)
process = frida.get_remote_device().attach('com.xiaojianbang.app')
//利用Frida包中函数对要hook的app包名创建一个进程
script= process.create_script(jscode)
script.on("message", message)
script.load()
sys.stdin.read()
对代码了解之后可发现只需对标红处代码进更改即可。编写的重点在于执行代码处。对于代码标绿处有如图两种情况:
代码正常执行后结果为:
将执行代码进行如下修改:
执行结果如下:
通过以上执行结果发现传入的100,300成功的进行了加运算。说明对该函数成功进行了hook。
本节所涉及代码框架应用于全文,建议大家理解普通方法的hook脚本后继续向下。后文代码将以hook处理代码为主。
4.构造方法HOOK
本节利用上节APP进行构造方法hook,对应功能为button2,点击后效果如下:
按键代码如下:
查看代码中关键方法Utils.getMoney。
发现getMoney方法内存在一个new的实例化方法Money,查看该方法的代码。根据最开始对Java方法的基础学习发现这个方法符合构造方法的特征。
要hook这个函数,依据上节的经验开始编写:
正常的写代码到这个位置了,但是这时突然想起构造方法名是不是和类名一模一样的。那就没有必要在这个地方再写方法名称。这个时候拿出了一个固定写法为$init。将上述代码改为:
代码执行结果如下:
出现上述结果说明hook成功了,但是要利用工具对我们输入的值进行操作,可将代码改写为:
执行代码之后,app的执行结果应为下图所示,说明hook并成功利用了方法。
5.重载方法HOOK
本节进入重载的hook,事件绑定在button3。其按键监听代码为:
代码中关键方法为Utils.test。查看其代码:
发现有两个方法都叫test,根据掌握的知识,这两个方法互为重载。对该函数进行hook。根据上面两节的经验在进行更改类的实现的时候,会有一个疑惑:两个方法名字一样,代码如何去选择?这个地方引入一个overload(),该方法()填写内容为hook方法的参数的类型,空参时该括号内容也为空。例如:overload(“int”)
hook的测试代码为:
执行结果如下:
通过上述过程,脚本在hook同名方法时,只需在overload()中将参数的类型依次填入。在遇到string类型的参数时,应填写为:Java.lang.String,因为string是Java提供用来来创建和操作字符串的类。
6. 函数参数类型表示
不同的参数类型都有自己的表示方法
对于基本类型,直接用它在Java中的表示方法就可以了,不用改变,例如:
· int
· short
· char
· byte
· boolean
· float
· double
· long
基本类型数组,用左中括号接上基本类型的缩写
基本类型缩写表示表:
基本类型 | 缩写 |
---|---|
boolean | Z |
byte | B |
char | C |
double | D |
float | F |
int | I |
long | J |
short | S |
例如:int[]类型,在重载时要写成[I
任意类,直接写完整类名即可
例如:java.lang.String
对象数组,用左中括号接上完整类名再接上分号
例如:[java.lang.String;
1) 带参数的构造函数
修改参数为byte[]类型的构造函数的实现
ClassName.$init.overload('[B').implementation=function(param){
//do something
}
注:ClassName是使用Java.use定义的类;param是可以在函数体中访问的参数
修改多参数的构造函数的实现
ClassName.$init.overload('[B','int','int').implementation=function(param1,param2,param3){
//do something
}
2) 无参数构造函数
ClassName.$init.overload().implementation=function(){
//do something
}
调用原构造函数
ClassName.$init.overload().implementation=function(){
//do something
this.$init();
//do something
}
注意:当构造函数(函数)有多种重载形式,比如一个类中有两个形式的func:void func()和void func(int),要加上overload来对函数进行重载,否则可以省略overload
3) 一般函数
修改函数名为func,参数为byte[]类型的函数的实现
ClassName.func.overload('[B').implementation=function(param){
//do something
//return ...
}
4) 无参数的函数
ClassName.func.overload().implementation=function(){
//do something
}
注: 在修改函数实现时,如果原函数有返回值,那么我们在实现时也要返回合适的值
ClassName.func.overload().implementation=function(){
//do something
return this.func();
}
7. 构造对象参数HOOK
本节进入重载的hook,事件绑定在button4。执行效果为:
其按键监听代码为:
发现关键方法test中传入了一个对象。查看关键函数代码:
对比三个test方法,发现最后一个test的参数为一个对象还是一个重载。
按照重载方法hook的方式,我们可以编写代码:
var utils = Java.use('com.xiaojianbang.app.Utils');
utils.test.overload().implementation = function (a)
但是因为本次传入的内容是个人定义的内容。overload方法这里填写内容这个时候可参考string的传入。我们将overload内容要求变成传入方法的全路径。
utils.test.overload('com.xiaojianbang.app.Money').implementation
代码整理后为:
运行结果如下:
这个时候在输出中文时会出现如下报错:
解决办法是:
但是本节的结果是构造对象参数,将随机输入的参数利用hook函数进行处理。操作代码如下;
jscode = """
Java.perform(function () {
var utils = Java.use('com.xiaojianbang.app.Utils');
var money = Java.use('com.xiaojianbang.app.Money');
//上述两行是将hook方法中涉及的方法所在类导入
utils.test.overload('com.xiaojianbang.app.Money').implementation = function (a) {
send("Hook Start...");
send(a.getInfo());
//利用hook方法内方式进行输出,简单就是将money这个参数值标示变成了a
var mon = money.$new(2000,'gangyuan');
//因为test内输入参数所利用的Money为构造函数,在使用时需利用$new进行实例化使用,可当一个通用方式来写。
send(mon.getInfo());
return this.test(mon);
}
});
"""
执行结果如下,说明我们成功对目标方法进行了hook,并修改了传入值。
8. 实例
在渗透测试过程中常会遇到数据包加密的情况。如下APP
用户登陆数据如下:
针对数据包提示信息在源码中进行搜索。
或者依据数据包提示接口位置进行搜索,在代码附近找寻加密方式。各位大佬也可以用自己习惯的方式去找寻。
通过搜索关键字等方式,是为找到代码中执行加密的部分:
我们发现传输的内容经过RequestUtil.encodeDesMap进行了处理。处理的数据内容为addMap,数据传输流程如下:
1.函数接受输入参数发送,在login方法流程最后调用了requesNetwork方法。
2.requestNetwork方法中para2接受的参数一路传递至addRequestMap。
加密流程至加密处理时要加密内容进入到addMap。故获取encodeDesMap相关信息。
RequestUtil.paraMap相关信息如下:
按上文获取到加密代码,目前可确定将要HOOK内容为paraMap:
在查看处理代码时发现paraMap处理的内容为Map集合,直接HOOK该处内容为Map集合,涉及到字符串的拼接等不便于数据展示。在该方法的代码中可发现Utils.md5方法,该方法对拼接后的数据进行了处理:
此处HOOK结果如下:
我们对encodeDesMap进行HOOK可获取到加密后的结果。
9. 工具使用HOOK
这里用到的工具及环境有
-
BurpSuite,
-
HTTPDecrypt(https://github.com/lyxhh/lxhToolHTTPDecrypt),
-
Python3。
注意这个地方环境变成了python3,对应的Frida和frida-tools要重新安装。
拿到一个新的app之后,总会有一段懵逼🤔寻找hook点的时候。HTTPDecrypt可以帮我较快的获取到相应的内容。
- 获取到源码,安装所需要的环境库。
- 浏览器访问地址,加载应用列表。如列表为空,多刷新刷新。
将想要操作的APP的报名填入空格,点击confirm。
- 使用Finds功能对app内类、方法进行搜索。
这个时候可以将目标类进行HOOKS,会讲类中存在的方法进行逐一HOOK。
进入HOOKS功能后,可看到上步选择的类。点击一下confirm。然后运行APP,进行正常的操作。
这时在该页面会出现对HOOK函数结果的输出:
在结果中寻找自己想要的结果,查看对应的方法。之后就可以自己写脚本或者使用工具自带的方法进行加密操作。
大佬们后可使用一下该工具与burp的结果使用,比brida较为方便。Brida涉及自己js脚本编写,使用复杂度比较高,推荐使用该工具。
隐私合规涉及部分方法HOOK
HOOK脚本
//此脚本为基于Frida的安卓权限检测脚本,HOOK函数因Android版本问题,存在误差,可自行测试
//打印堆栈信息,需要手动在下面的函数里自己添加调用,也可以自己再写一个输出函数。
//function showStacks() {
// var Exception = Java.use("java.lang.Exception");
// var ins = Exception.$new("Exception");
// var straces = ins.getStackTrace();
// if (undefined == straces || null == straces) {
// return;
// }
// console.log("============================= Stack strat=======================\r\n");
// console.log("");
// for (var i = 0; i < straces.length; i++) {
// var str = " " + straces[i].toString();
// console.log(str\r\n);
// }
// console.log("");
// console.log("============================= Stack end=======================\r\n");
// Exception.$dispose();
//}
// APP申请权限
function checkRequestPermission() {
try {
var ActivityCompat = Java.use("androidx.core.app.ActivityCompat")
} catch (e) {
console.log(e)
return
}
ActivityCompat.requestPermissions.overload('android.app.Activity', '[Ljava.lang.String;', 'int').implementation = function(p1, p2, p3) {
var temp = this.requestPermissions(p1, p2, p3);
console.log("APP申请权限", "申请权限为: " + p2);
return temp
}
}
// APP获取IMEI/IMSI
function getPhoneState() {
try {
var TelephonyManager = Java.use("android.telephony.TelephonyManager");
} catch (e) {
console.log(e)
return
}
// API level 26 获取单个IMEI的方法
TelephonyManager.getDeviceId.overload().implementation = function() {
var temp = this.getDeviceId();
console.log("获取IMEI", "获取的IMEI为: " + temp)
return temp;
};
//API level 26 获取多个IMEI的方法
TelephonyManager.getDeviceId.overload('int').implementation = function(p) {
var temp = this.getDeviceId(p);
console.log("获取IMEI", "获取(" + p + ")的IMEI为: " + temp);
return temp;
};
//API LEVEL26以上的获取单个IMEI方法
TelephonyManager.getImei.overload().implementation = function() {
var temp = this.getImei();
console.log("获取IMEI", "获取的IMEI为: " + temp)
return temp;
};
// API LEVEL26以上的获取多个IMEI方法
TelephonyManager.getImei.overload('int').implementation = function(p) {
var temp = this.getImei(p);
console.log("获取IMEI", "获取(" + p + ")的IMEI为: " + temp);
return temp;
};
//imsi/iccid
TelephonyManager.getSimSerialNumber.overload().implementation = function() {
var temp = this.getSimSerialNumber();
console.log("获取IMSI/iccid", "获取IMSI/iccid为(String): " + temp);
return temp;
};
//imsi
TelephonyManager.getSubscriberId.overload().implementation = function() {
var temp = this.getSubscriberId();
console.log("获取IMSI", "获取IMSI为(int): " + temp);
return temp;
}
//imsi/iccid
TelephonyManager.getSimSerialNumber.overload('int').implementation = function(p) {
var temp = this.getSimSerialNumber(p);
console.log("获取IMSI/iccid", "参数为:(" + p + "), 获取IMSI/iccid为(int): " + temp);
return temp;
}
}
// 获取系统属性(记录关键的)。意义不大,frida获取不到。暂时留着
function getSystemProperties() {
try {
var SystemProperties = Java.use("android.os.SystemProperties");
} catch (e) {
console.log(e)
return
}
SystemProperties.get.overload('java.lang.String').implementation = function(p1) {
var temp = this.get(p1);
if (p1 == "ro.serialno") {
console.log("获取设备序列号", "获取(" + p1 + "),值为:" + temp);
}
if (p1 == "ro.build.display.id") {
console.log("获取版本号", "获取(" + p1 + "),值为:" + temp);
}
//MEID
if (p1 == "ril.cdma.meid") {
console.log("获取MEID", "获取(" + p1 + "),值为:" + temp);
}
//手机型号
if (p1 == "ro.product.model") {
console.log("获取手机型号", "获取(" + p1 + "),值为:" + temp);
}
//手机厂商
if (p1 == "ro.product.manufacturer") {
console.log("获取手机厂商", "获取(" + p1 + "),值为:" + temp);
}
return temp;
}
SystemProperties.get.overload('java.lang.String', 'java.lang.String').implementation = function(p1, p2) {
var temp = this.get(p1, p2)
if (p1 == "ro.serialno") {
console.log("获取设备序列号", "获取(" + p1 + " 、 " + p2 + "),值为:" + temp);
}
if (p1 == "ro.build.display.id") {
console.log("获取版本号", "获取(" + p1 + " 、 " + p2 + "),值为:" + temp);
}
//MEID
if (p1 == "ril.cdma.meid") {
console.log("获取MEID", "获取(" + p1 + " 、 " + p2 + "),值为:" + temp);
}
//手机型号
if (p1 == "ro.product.model") {
console.log("获取手机型号", "获取(" + p1 + " 、 " + p2 + "),值为:" + temp);
}
//手机厂商
if (p1 == "ro.product.manufacturer") {
console.log("获取手机厂商", "获取(" + p1 + " 、 " + p2 + "),值为:" + temp);
}
return temp;
}
SystemProperties.getInt.overload('java.lang.String', 'int').implementation = function(p1, p2) {
var temp = this.getInt(p1, p2)
if (p1 == "ro.build.version.sdk") {
console.log("获取SDK版本号", "获取(" + p1 + "),值为:" + temp);
}
return temp;
}
}
//获取content敏感信息
function getContentProvider() {
try {
// 通讯录内容
var ContactsContract = Java.use("android.provider.ContactsContract");
var contact_authority = ContactsContract.class.getDeclaredField("AUTHORITY").get('java.lang.Object');
} catch (e) {
console.log(e)
}
try {
// 日历内容
var CalendarContract = Java.use("android.provider.CalendarContract");
var calendar_authority = CalendarContract.class.getDeclaredField("AUTHORITY").get('java.lang.Object');
} catch (e) {
console.log(e)
}
try {
// 浏览器内容
var BrowserContract = Java.use("android.provider.BrowserContract");
var browser_authority = BrowserContract.class.getDeclaredField("AUTHORITY").get('java.lang.Object');
} catch (e) {
console.log(e)
}
try {
var ContentResolver = Java.use("android.content.ContentResolver");
ContentResolver.query.overload('android.net.Uri', '[Ljava.lang.String;', 'android.os.Bundle', 'android.os.CancellationSignal').implementation = function(p1, p2, p3, p4) {
var temp = this.query(p1, p2, p3, p4);
if (p1.toString().indexOf(contact_authority) != -1) {
console.log("获取content敏感信息", "获取手机通信录内容");
} else if (p1.toString().indexOf(calendar_authority) != -1) {
console.log("获取content敏感信息", "获取日历内容");
} else if (p1.toString().indexOf(browser_authority) != -1) {
console.log("获取content敏感信息", "获取浏览器内容");
}
return temp;
}
} catch (e) {
console.log(e)
return
}
}
// 获取安卓ID
function getAndroidId() {
try {
var SettingsSecure = Java.use("android.provider.Settings$Secure");
} catch (e) {
console.log(e)
return
}
SettingsSecure.getString.implementation = function(p1, p2) {
if (p2.indexOf("android_id") < 0) {
return this.getString(p1, p2);
}
var temp = this.getString(p1, p2);
console.log("获取Android ID", "参数为:" + p2 + ",获取到的ID为:" + temp.toString());
return temp;
}
}
//获取其他app信息
function getPackageManager() {
try {
var PackageManager = Java.use("android.content.pm.PackageManager");
PackageManager.getInstalledPackages.overload('int').implementation = function(p1) {
var temp = this.getInstalledPackages(p1);
console.log("获取其他app信息", "1获取的数据为:" + temp);
return temp;
};
PackageManager.getInstalledApplications.overload('int').implementation = function(p1) {
var temp = this.getInstalledApplications(p1);
console.log("获取其他app信息", "getInstalledApplications获取的数据为:" + temp);
return temp;
};
} catch (e) {
console.log(e)
}
try {
var ApplicationPackageManager = Java.use("android.app.ApplicationPackageManager");
ApplicationPackageManager.getInstalledPackages.overload('int').implementation = function(p1) {
var temp = this.getInstalledPackages(p1);
console.log("获取其他app信息", "getInstalledPackages获取的数据为:" + temp);
return temp;
};
ApplicationPackageManager.getInstalledApplications.overload('int').implementation = function(p1) {
var temp = this.getInstalledApplications(p1);
console.log("获取其他app信息", "getInstalledApplications获取的数据为:" + temp);
return temp;
};
ApplicationPackageManager.queryIntentActivities.implementation = function(p1, p2) {
var temp = this.queryIntentActivities(p1, p2);
console.log("获取其他app信息", "参数为:" + p1 + p2 + ",queryIntentActivities获取的数据为:" + temp);
return temp;
};
ApplicationPackageManager.getApplicationInfo.implementation = function(p1, p2) {
var temp = this.getApplicationInfo(p1, p2);
var string_to_recv;
// 判断是否为自身应用,是的话不记录
console.log("APP包名是:"+ p1);
recv(function(received_json_object) {
string_to_recv = received_json_object.my_data;
}).wait();
if (string_to_recv) {
console.log("获取其他app信息", "getApplicationInfo获取的数据为:" + temp);
}
return temp;
};
} catch (e) {
console.log(e)
}
try {
var ActivityManager = Java.use("android.app.ActivityManager");
ActivityManager.getRunningAppProcesses.implementation = function() {
var temp = this.getRunningAppProcesses();
console.log("获取其他app信息", "获取了正在运行的App"+temp);
return temp;
};
} catch (e) {
console.log(e)
}
}
// 获取位置信息
function getGSP() {
try {
var locationManager = Java.use("android.location.LocationManager");
} catch (e) {
console.log(e)
return
}
locationManager.getLastKnownLocation.overload("java.lang.String").implementation = function(p1) {
var temp = this.getLastKnownLocation(p1);
console.log("获取位置信息", "获取位置信息,参数为:" + p1)
return temp;
}
locationManager.requestLocationUpdates.overload("java.lang.String", "long", "float", "android.location.LocationListener").implementation = function(p1, p2, p3, p4) {
var temp = this.requestLocationUpdates(p1, p2, p3, p4);
console.log("获取位置信息", "获取位置信息");
return temp;
}
}
// 调用摄像头(hook,防止静默拍照)
function getCamera() {
try {
var Camera = Java.use("android.hardware.Camera");
} catch (e) {
console.log(e)
return
}
Camera.open.overload("int").implementation = function(p1) {
var temp = this.open(p1);
console.log("调用摄像头", "调用摄像头id:" + p1.toString());
return temp;
}
}
//获取网络信息
function getNetwork() {
try {
var WifiInfo = Java.use("android.net.wifi.WifiInfo");
//获取ip
WifiInfo.getIpAddress.implementation = function() {
var temp = this.getIpAddress();
var _ip = new Array();
_ip[0] = (temp >>> 24) >>> 0;
_ip[1] = ((temp << 8) >>> 24) >>> 0;
_ip[2] = (temp << 16) >>> 24;
_ip[3] = (temp << 24) >>> 24;
var _str = String(_ip[3]) + "." + String(_ip[2]) + "." + String(_ip[1]) + "." + String(_ip[0]);
console.log("获取网络信息", "获取IP地址:" + _str);
return temp;
}
//获取mac地址
WifiInfo.getMacAddress.implementation = function() {
var temp = this.getMacAddress();
console.log("获取Mac地址", "获取到的Mac地址: " + temp);
return temp;
}
WifiInfo.getSSID.implementation = function() {
var temp = this.getSSID();
console.log("获取wifi SSID", "获取到的SSID: " + temp);
return temp;
}
WifiInfo.getBSSID.implementation = function() {
var temp = this.getBSSID();
console.log("获取wifi BSSID", "获取到的BSSID: " + temp);
return temp;
}
} catch (e) {
console.log(e)
}
try {
var WifiManager = Java.use("android.net.wifi.WifiManager");
// 获取wifi信息
WifiManager.getConnectionInfo.implementation = function() {
var temp = this.getConnectionInfo();
console.log("获取wifi信息", "获取wifi信息");
return temp;
};
} catch (e) {
console.log(e)
}
try {
var InetAddress = Java.use("java.net.InetAddress");
//获取IP
InetAddress.getHostAddress.implementation = function() {
var temp = this.getHostAddress();
console.log("获取网络信息", "获取IP地址:" + temp.toString());
return temp;
}
} catch (e) {
console.log(e)
}
try {
var NetworkInterface = Java.use("java.net.NetworkInterface");
//获取mac
NetworkInterface.getHardwareAddress.overload().implementation = function() {
var temp = this.getHardwareAddress();
console.log("获取Mac地址", "获取到的Mac地址: " + temp);
return temp;
}
} catch (e) {
console.log(e)
}
try {
var NetworkInfo = Java.use("android.net.NetworkInfo");
NetworkInfo.getType.implementation = function() {
var temp = this.getType();
console.log("获取网络信息", "获取网络类型:" + temp.toString());
return temp;
}
NetworkInfo.getTypeName.implementation = function() {
var temp = this.getTypeName();
console.log("获取网络信息", "获取网络类型名称:" + temp);
return temp;
}
NetworkInfo.getExtraInfo.implementation = function() {
var temp = this.getExtraInfo();
console.log("获取网络信息", "获取网络名称:" + temp);
return temp;
}
NetworkInfo.isAvailable.implementation = function() {
var temp = this.isAvailable();
console.log("获取网络信息", "获取网络是否可用:" + temp.toString());
return temp;
}
NetworkInfo.isConnected.implementation = function() {
var temp = this.isConnected();
console.log("获取网络信息", "获取网络是否连接:" + temp.toString());
return temp;
}
} catch (e) {
console.log(e)
}
}
//获取蓝牙设备信息
function getBluetooth() {
try {
var BluetoothDevice = Java.use("android.bluetooth.BluetoothDevice");
//获取蓝牙设备名称
BluetoothDevice.getName.overload().implementation = function() {
var temp = this.getName();
console.log("获取蓝牙信息", "获取到的蓝牙设备名称: " + temp)
return temp;
}
//获取蓝牙设备mac
BluetoothDevice.getAddress.implementation = function() {
var temp = this.getAddress();
console.log("获取蓝牙信息", "获取到的蓝牙设备mac: " + temp)
return temp;
}
} catch (e) {
console.log(e)
}
try {
var BluetoothAdapter = Java.use("android.bluetooth.BluetoothAdapter");
//获取蓝牙设备名称
BluetoothAdapter.getName.implementation = function() {
var temp = this.getName();
console.log("获取蓝牙信息", "获取到的蓝牙设备名称: " + temp)
return temp;
};
} catch (e) {
console.log(e)
}
}
//获取基站信息
function getCidorLac() {
try {
// 电信卡cid lac
var CdmaCellLocation = Java.use("android.telephony.cdma.CdmaCellLocation");
CdmaCellLocation.getBaseStationId.implementation = function() {
var temp = this.getBaseStationId();
console.log("获取基站信息", "获取到的cid: " + temp);
return temp
}
CdmaCellLocation.getNetworkId.implementation = function() {
var temp = this.getNetworkId();
console.log("获取基站信息", "获取到的lac: " + temp);
return temp
}
} catch (e) {
console.log(e)
}
try {
// 移动 联通卡 cid/lac
var GsmCellLocation = Java.use("android.telephony.gsm.GsmCellLocation");
GsmCellLocation.getCid.implementation = function() {
var temp = this.getCid();
console.log("获取基站信息", "获取到的cid: " + temp);
return temp
}
GsmCellLocation.getLac.implementation = function() {
var temp = this.getLac();
console.log("获取基站信息", "获取到的lac: " + temp);
return temp
}
} catch (e) {
console.log(e)
}
}
Java.perform(function() {
console.log("合规检测敏感接口开始监控...");
checkRequestPermission();
getPhoneState();
getContentProvider();
getAndroidId();
getPackageManager();
getGSP();
getCamera();
getNetwork();
getBluetooth();
getCidorLac();
//getSystemProperties();
})
脚本执行结果
不足
学习环境为Android 8.0版本,因后期安卓版本更新带来的安全特性,使得脚本在高版本下使用效果不是很理想。