更新时间:2022-05-19(第8天)(完结) 终于做完了。
序
之前没做过安卓的开发,现在突然要搞这个东西,很是头大。 写篇记录性质的博客,看看到底多少天能实现。 期限三个星期,只想弄完赶紧跑路了。
功能需求
提示可以使用HOOK实现,但是我连HOOK是什么都不知道。。。先从最基本的概念开始吧。
2022-05-19 20:57 终于做完了,效果差不多了,来写总结,放前面。踩坑学习日志就放后面了。
总结
项目代码:github: android_replay_test
目标程序 比较简单,就不上 github了。
目标程序:比较简单,主要就是几个组件的编写。 测试程序:在不改动 目标 程序代码的情况下,使用HOOK技术,完成需求。
环境:夜神模拟器7.0.2.7 额外模块:Xposed,需要使用到ROOT权限。
思路: (1)HOOK 目标程序 的组件,使得我们点击组件后,能够广播信息到 测试程序,并保存起来。 (2)同时,为 目标程序 添加 接收广播类。 (3)回放的时候,我们将保存的信息 广播 给 目标程序,由 接收广播类,“回放” 动作。
为什么要使用广播? 答:广播是跨程序通信方式之一。当前其他方式也可以,只是广播比较简单而已。 Xposed模块可以传递变量吗? 答:Xposed模块在hook时(package加载时),存在于不同的进程中。即使 定义static属性,也无法跨进程传递。 使用createPackageContext获得 目标程序 Context,再获得View不行吗? 答:确实不行,获得Context不是Activity,无法获得View。(目前没有找到方法)
还有,Xposed在某个类中,无法“添加”一个属性。只能获取、修改 已定义的属性。
编写目标程序
简单的需求分析:(后面慢慢详解) 我们需要两个程序。 (1)目标程序(被测程序)。我称为target app (2)测试程序。我称为hook app 。
为什么要编写 目标程序? 因为,我们需要了解 被测程序 的源码。否则,后面的hook比较麻烦。
目标程序比较简单,主要是两个文件。
activity_main.xml
主要给组件添加了点击涟漪效果。为了后面的测试点击效果 更加清晰。(其实没有清晰多少)
如何添加安卓原生点击效果?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="100sp"
android:text="Test1"
android:textSize="50sp"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="100sp"
android:orientation="horizontal"
android:layout_gravity="center_horizontal">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="请输入:"
android:textSize="30sp"/>
<EditText android:id="@+id/edit11"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="内容"
android:textSize="30sp"
android:width="235sp"
android:inputType="text"
android:background="?android:attr/selectableItemBackground"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20sp"
android:orientation="horizontal"
android:layout_gravity="center_horizontal">
<Button android:id="@+id/button11"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10sp"
android:text="确认"
android:textSize="40sp"
android:foreground="?android:attr/selectableItemBackgroundBorderless"/>
<Button android:id="@+id/button12"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="50sp"
android:text="清空"
android:textSize="40sp"
android:foreground="?android:attr/selectableItemBackgroundBorderless"/>
</LinearLayout>
<RadioGroup
android:id="@+id/radiogroup11"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20sp">
<RadioButton
android:id="@+id/radiobutton11"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:textSize="30sp"
android:text="是" />
<RadioButton
android:id="@+id/radiobutton12"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:textSize="30sp"
android:text="否"
android:layout_marginLeft="50sp"/>
</RadioGroup>
<TextView
android:id="@+id/text11"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginTop="30sp"
android:layout_marginLeft="10sp"
android:hint="显示内容"
android:textSize="30sp"
android:textColor="#B10909"
android:layout_marginStart="10sp"
android:background="?android:attr/selectableItemBackgroundBorderless"/>
</LinearLayout>
MainActivity.java
package com.example.myapplication;
import android.view.View;
import android.widget.*;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
EditText et11;
TextView tv11;
Button bt11;
Button bt12;
private RadioGroup rg11;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et11 = (EditText) this.findViewById(R.id.edit11);
tv11 = (TextView) this.findViewById(R.id.text11);
bt11 = (Button) this.findViewById(R.id.button11);
bt12 = (Button) this.findViewById(R.id.button12);
rg11 = (RadioGroup) this.findViewById(R.id.radiogroup11);
bt11.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String s1 = et11.getText().toString();
String text = "1输入的内容为:"+s1;
tv11.setText(text);
}
});
bt12.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
et11.setText("");
tv11.setText("");
Toast.makeText(MainActivity.this,"1清空",Toast.LENGTH_SHORT).show();
}
});
rg11.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
RadioButton rb = (RadioButton) findViewById(checkedId);
Toast.makeText(getApplicationContext(), rb.getText(),Toast.LENGTH_SHORT).show();
}
});
}
}
功能描述: (1)【输入框】输入内容 (2)点击【确定】后,显示在【显示内容】处。 (3)点击【清空】后,清空【输入框】、【显示内容】。 (4)【单选框】暂时无效果(只可以点击)。
【单选框】怎么写,就自己查一下吧。
展示如下:
测试程序编写
主要代码量 和 问题都在这个程序上。
测试程序界面代码
MainAcitivity 主要就是3个button。文章之后 还会添加新的逻辑。
public class MainActivity extends AppCompatActivity {
Button bt1,bt2,bt3;
TextView tv1;
static String TARGET_APP_NAME = "com.example.myapplication";
static String HOOP_APP_NAME = "com.example.hookreplay";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt1 = (Button) this.findViewById(R.id.button1);
bt2 = (Button) this.findViewById(R.id.button2);
bt3 = (Button) this.findViewById(R.id.button3);
tv1 = (TextView) this.findViewById(R.id.tv1);
bt1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tv1.setText("开始录制");
jumpApp();
}
});
bt2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tv1.setText("清空录制");
}
});
bt3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tv1.setText("回放");
jumpApp();
performScript();
}
});
}
}
activity_main.xml 主要就是3个button。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="24dp"
android:layout_marginLeft="24dp"
android:layout_marginStart="24dp">
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始录制"
android:textSize="20sp"
/>
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="清空录制"
android:textSize="20sp"/>
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="回放"
android:textSize="20sp"/>
</LinearLayout>
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
功能描述: (1)【开始录制】跳转到目标程序,开始录制操作。 (2)【清空录制】清空 录制内容。 (3)【回放】跳转到 目标程序,回放,清空录制内容。
题目要求 “不能记录点击坐标”。所以我们采用HOOK的方法 来实现。
我们需要使用到HOOK框架,Xposed 。下面是一大堆参考资料:
Xposed学习
参考资料: **github: XposedInstaller **XposedInstaller — 安装Xposed 百度百科-钩子函数 什么是钩子函数 Android——Hook(钩子函数)动态注入代码 *Android Hook 技术 Android 集成Xposed框架 *Android插件化原理解析——概要 手把手教你Hook Android 点击事件 **抱歉,Xposed真的可以为所欲为——1.基础知识储备(上) *抱歉,Xposed真的可以为所欲为——1.基础知识储备(下) ****Xposed的框架的使用 -----重点看这个 *初探Xposed,初学Xposed框架上手必看 *安卓Xposed简单实现
夜神模拟器下载安装
OK,因为Xposed 是 (1)需要ROOT权限,并且 (2)需要安装在基于ARM 架构的CPU上, 所以我们需要下载其他的安卓模拟器。
(1)IDEA自带的AVD,可以有基于x86的,也有基于ARM的。但是我试了下,基于ARM的运行太慢了。IDEA说,x86的比ARM的快10x。 但是我等了好久,始终没有启动成功基于ARM的AVD。 (2)Genymotion,很快,但是只支持x86。如需运行ARM程序,需要添加第三方插件—— github: Genymotion_ARM_Translation 。 但是,我试了很久,看上去Genymotion_ARM_Translation 是安装成功了。但是ARM程序仍然无法安装。
(1)下载夜神模拟器。 (2)使用Xposed installer安装Xposed 。 (3)下载后,直接拖入 夜神模拟器,即开始安装。
测试程序Hook思路
有了Xposed ,我们能怎么hook?我们能干嘛? (1)AOP的思想。对一个方法 进行 “增强”。 (2)反射的思想。反射调用 类的方法,属性。
我们去hook目标程序的组件的事件, (1)记录点击事件, (2)将该 信息 发送(广播) 给 测试程序。
编写HookTest 类,实现IXposedHookLoadPackage 接口,重写handleLoadPackage 方法。
我们使用XposedHelpers.findAndHookMethod Hook,被测程序的onCreate 方法。通过反射获得所有组件。
有人可能会问:你这个所有组件都放在 被测程序的 MainActivity 属性 中,如果 组件没有在MainActivity 的属性中呢? 答:简单起见。。。如果不在的话,也可以通过其他方式获取,就更复杂了。
XposedHelpers.findAndHookMethod(
TARGET_APP_NAME + ".MainActivity",
lpparam.classLoader,
"onCreate",
Bundle.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Class<?> c = lpparam.classLoader.loadClass(TARGET_APP_NAME + ".MainActivity");
Field field1 = c.getDeclaredField("et11");
...其他组件
field1.setAccessible(true);
...其他组件
EditText et11 = (EditText) field1.get(param.thisObject);
...其他组件
...其他代码
});
接下来hook修改组件的 事件。
hook EditText
对于EditText 。我们需要监听文本的变化。 查看源码可知TextChangedListener 是可以添加多个,这里我们直接添加即可。
et11.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
String ss = s.toString();
XposedBridge.log("id: "+ et11.getId() + " text after change...");
makeActionLog(et11, ss);
}
});
makeActionLog(et11, ss) 保存 点击动作,并广播动作给 测试程序。后面还会提及。
hook click listener
对于需要响应 点击 的事件, 这里分两种情况, (1)对于绑定了onClickListener 的组件,我们反射获得其onClickListener 对象,并添加新的功能 后 放回。
见:手把手教你Hook Android 点击事件
(2)对于未绑定onClickListener 的组件,我们直接添加新的onClickListener 即可。
使用view.hasOnClickListeners 判断是否存在onClickListener 。
ArrayList<View> onClickList = new ArrayList<>(Arrays.asList(et11, tv11, bt11, bt12));
for(View v: onClickList){
if(v.hasOnClickListeners()){
Method method = View.class.getDeclaredMethod("getListenerInfo");
method.setAccessible(true);
Object mListenerInfo = method.invoke(v);
Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");
Field field = listenerInfoClz.getDeclaredField("mOnClickListener");
final View.OnClickListener onClickListenerInstance = (View.OnClickListener) field.get(mListenerInfo);
View.OnClickListener proxyOnClickListener = v1 -> {
XposedBridge.log("id1: "+ v1.getId());
onClickListenerInstance.onClick(v1);
makeActionLog(v1);
};
field.set(mListenerInfo, proxyOnClickListener);
}else{
v.setOnClickListener(v1->{
XposedBridge.log("id2: " + v1.getId());
makeActionLog(v1);
});
}
}
java的lambda表达式别忘了。
() -> {
}
最近写Scala有点多,感觉Java的 (1)分号 有点多余。(不过,也减轻了 编译器压力?) (2)代码 有点冗长。(在其他语言,明明可以写得很简单,比如说 这个lambda表达式)
hook radioGroup
对于【单选框】,原理也是一样的,查看其源码后,可以发现比onClickListener 简单。 这里hook它的OnCheckedChangeListener 。其中参数checkedId 就是【单选框】button的ID。
Class<?> radioGroupClz = Class.forName("android.widget.RadioGroup");
Field field = radioGroupClz.getDeclaredField("mOnCheckedChangeListener");
field.setAccessible(true);
RadioGroup.OnCheckedChangeListener onCheckedChangeListenerInstance = (RadioGroup.OnCheckedChangeListener) field.get(rg11);
RadioGroup.OnCheckedChangeListener proxyOnCheckedChangeListener = (group, checkedId) -> {
XposedBridge.log("id3: " + checkedId);
onCheckedChangeListenerInstance.onCheckedChanged(group, checkedId);
View v = ((Activity)param.thisObject).findViewById(checkedId);
makeActionLog(v);
};
field.set(rg11, proxyOnCheckedChangeListener);
最后,别忘了把 代理类 放回。 field.set(rg11, proxyOnCheckedChangeListener);
广播
到目前为止,我们把组件 的 事件 都hook了,添加上了自己的逻辑。 自己的逻辑就是,将 组件的点击事件,发送给 测试程序。
我们定义一个 action 为一个字符串,使用空格分割,分别为
组件ID 组件名称 点击时间 [文本]
目前,[文本]仅在组件为EditText 中存在。
为什么需要【点击时间】? 因为我们需要 模拟触摸事件。使用view.performClick 是不会触发 点击动画的。所以需要模拟MotionEvent 。 参考: android MotionEvent.obtain模拟事件,自动触发 Android模拟点击的四种方式 ---------第一种。
public void makeActionLog(View v){
makeActionLog(v,"");
}
public void makeActionLog(View v, String extra){
long curTime = System.currentTimeMillis();
String action = "";
if(v instanceof EditText){
action = v.getId() + " " + EDIT_TEXT + " " + Long.toString(curTime) + " " + extra;
}else if(v instanceof Button){
action = v.getId() + " " + BUTTON + " " + Long.toString(curTime);
}else if(v instanceof TextView){
action = v.getId() + " " + TEXT_VIEW + " " + Long.toString(curTime);
}
XposedBridge.log("make log...: " + action);
Intent intent = new Intent(TARGET_APP_NAME);
intent.putExtra("action", action);
v.getContext().sendBroadcast(intent);
}
下面讲一下广播。 广播 是 安卓 跨程序通信 的一种方式。
参考: Android跨进程通信广播(Broadcast) Android 进程间通信IPC(二)—— Broadcast广播
广播是一种被动跨进程通讯的方式。当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据。
在应用程序中发送广播比较简单。只需要调用sendBroadcast 方法即可。该方法需要一个Intent对象。通过Intent对象可以发送需要广播的数据。
接收广播 需要继承BroadcastReceiver 类,并重写onReceive 方法。 该接收广播类,还需要在Context 中注册。
注册又分为,静态注册 和 动态注册。
这里,我们需要把接收的action 保存起来。所以,选择动态注册的方式。
Save data in Broadcast receiver
我们新建一个类MyBroadcastReceiver 。将得到的action 保存在,静态的actionList 中。
实现 单例 只是 为了方便 操作。。不是必须的。
public class MyBroadcastReceiver extends BroadcastReceiver {
public static final String SENDER_NAME = "com.example.myapplication";
public static ArrayList<String> actionList = new ArrayList<>();
private static MyBroadcastReceiver myBroadcastReceiver = null;
public static MyBroadcastReceiver getInstance(){
if(myBroadcastReceiver == null){
myBroadcastReceiver = new MyBroadcastReceiver();
}
return myBroadcastReceiver;
}
@Override
public void onReceive(Context context, Intent intent) {
if(!SENDER_NAME.equals(intent.getAction())){
return;
}
Bundle bundle = intent.getExtras();
if(bundle == null){
return;
}
String action = bundle.getString("action");
System.out.println("MyReceiver: " + action);
actionList.add(action);
}
}
之后,在测试程序的onCreate 方法 注册 该 接收广播类。
BroadcastReceiver myReceiver = MyBroadcastReceiver.getInstance();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(MyBroadcastReceiver.SENDER_NAME);
registerReceiver(myReceiver, intentFilter);
注意,onCreate 中只会注册一次。在其他方法,如onResume ,可能多次注册,广播将接收多次。
到目前为止, 我们 点击 目标程序 的组件之后,会向 测试程序 发送广播。 测试程序接收广播,并存储起来。
应用跳转
功能需求:在我们点击【开始录制】后,我们需要跳转到 目标程序。
How to switch between Android apps programmatically **Android 必知必会 - 使用 Intent 打开第三方应用及验证可用性
使用startActivity 方法。 (1)使用ComponentName 指定,跳转程序的入口。 (2)Intent.FLAG_ACTIVITY_NEW_TASK ,指明该Flag之后,会新启一个“页面”。
public void jumpApp(){
String packageName = HookTest.TARGET_APP_NAME;
String activityName = HookTest.TARGET_APP_NAME +".MainActivity";
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ComponentName componentName = new ComponentName(packageName, activityName);
intent.setComponent(componentName);
startActivity(intent);
}
回放功能的实现
我们将MyBroadcastReceiver 接收广播类中,保存的actionList 广播给 目标程序,又目标程序执行 记录的动作。
public void performScript(){
Intent intent = new Intent(HOOP_APP_NAME);
intent.putExtra("actionArray", MyBroadcastReceiver.actionList.toArray(new String[0]));
this.sendBroadcast(intent);
}
有同学可能会问:接收广播类 不是需要 注册 吗? 是的,所以我们需要 在 目标程序 中,动态注册 一个 广播接收类。
接着之前的代码,hook目标程序的onCreate 方法。
我们解析action ,并按照点击的时间进行延迟执行。使用handler.postDelayed 。
long curTime = Long.parseLong(e[2]) + ACTION_DELAY;
设第一个action 的时间为基准(为0),之后的动作的时间都减去第一的动作的时间。同时,我们将所有动作延迟ACTION_DELAY 。不延迟也行
class TargetReceiver extends BroadcastReceiver{
public static final String SENDER_NAME = "com.example.hookreplay";
public static final long ACTION_DELAY = 1000;
private void performTouch(View v){
long downTime = SystemClock.uptimeMillis();
MotionEvent downEvent = MotionEvent.obtain(downTime, downTime+200, MotionEvent.ACTION_DOWN, 0, 0 , 0);
downTime += 600;
MotionEvent upEvent = MotionEvent.obtain(downTime, downTime+100, MotionEvent.ACTION_UP, 0, 0 , 0);
v.onTouchEvent(downEvent);
v.onTouchEvent(upEvent);
upEvent.recycle();
downEvent.recycle();
}
@Override
public void onReceive(Context context, Intent intent) {
if(!SENDER_NAME.equals(intent.getAction())){
return;
}
Bundle bundle = intent.getExtras();
if(bundle == null){
return;
}
String[] actionArray = bundle.getStringArray("actionArray");
if(actionArray.length == 0){
Toast.makeText((Activity)param.thisObject,"请先录制",Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText((Activity)param.thisObject,"开始播放",Toast.LENGTH_SHORT).show();
final Handler handler = new Handler(Looper.getMainLooper());
long startTime = Long.parseLong(actionArray[0].split(" ")[2]);
long endTime = Long.parseLong(actionArray[actionArray.length-1].split(" ")[2]);
for(String action: actionArray) {
System.out.println("perform script: "+ action);
String[] e = action.split(" ");
if (e.length == 0) {
continue;
}
int id = Integer.parseInt(e[0]);
String compName = e[1];
long curTime = Long.parseLong(e[2]) + ACTION_DELAY;
View v = ((Activity)context).findViewById(id);
handler.postDelayed(() -> {
System.out.println("execute action: " + action);
switch (compName) {
case HookTest.BUTTON:
performTouch(v);
break;
case HookTest.TEXT_VIEW:
performTouch(v);
break;
case HookTest.EDIT_TEXT:
if(e.length >= 4){
String text = e[3];
((EditText) v).setText(text);
}else{
performTouch(v);
}
break;
default:
break;
}
}, (curTime - startTime));
}
handler.postDelayed(()->{
Toast.makeText((Activity)param.thisObject,"回放结束",Toast.LENGTH_SHORT).show();
}, (endTime-startTime) + ACTION_DELAY*2);
}
};
为什么不用view.performClick ? 参考: android MotionEvent.obtain模拟事件,自动触发 Android模拟点击的四种方式 ---------第一种。
因为我们需要 模拟触摸事件。使用view.performClick 是不会触发 点击动画的。所以需要模拟MotionEvent 。 * 需要注意的是,MotionEvent 的时间需要是SystemClock.uptimeMillis() 中获得的。
如何实现【延迟】执行代码? How to delay a loop in android without using thread.sleep? [solve] How to delay a loop in android without using thread.sleep?
最后,别忘记注册了。
BroadcastReceiver bcr = new TargetReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(TargetReceiver.SENDER_NAME);
((Activity)param.thisObject).registerReceiver(bcr,intentFilter);
OK,至此,我们已经完成所有逻辑的编写。
下面是这几天的 学习踩坑。仅作为记录,思路不完成正确。
2022-05-(12-13)
HOOK是什么?
参考: 百度百科 什么是钩子函数
钩子函数是Windows消息处理机制的一部分,通过设置“钩子”,应用程序可以在系统级对所有消息、事件进行过滤,访问在正常情况下无法访问的消息。钩子的本质是一段用以处理系统消息的程序,通过系统调用,把它挂入系统。
钩子函数:钩子函数是在一个事件触发的时候,在系统级捕获到了他,然后做一些操作。一段用以处理系统消息的程序。“钩子”就是在某个阶段给你一个做某些处理的机会。
钩子函数: 1、是个函数,在系统消息触发时被系统调用 2、不是用户自己触发的
钩子函数的名称是确定的,当系统消息触发,自动会调用。例如react的componentWillUpdate函数,用户只需要编写componentWillUpdate的函数体,当组件状态改变要更新时,系统就会调用componentWillUpdate。
简单地说,钩子函数是处理系统消息的机制。 但是上面说的都是桌面window的钩子函数吧,有没有安卓的?
一段简单的HOOK的例子
Android——Hook(钩子函数)动态注入代码
总结一下,HOOK对关键组件进行代理、替换,并增加新的功能,实现我们的目的。 HOOK用到的技术思想:AOP、代理、反射。
IDEA安卓开发
如何用idea进行安卓开发
遇到的问题: (1)bulid工程失败? 对build.gradle 添加
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation "androidx.test.ext:junit:1.1.3"
androidTestImplementation "androidx.test:core:1.4.0"
androidTestImplementation "androidx.fragment:fragment-testing:1.4.1"
}
同时修改android.compileSdk=31 ,android.defaultConfig.targetSdk=31
目标程序编写
因为功能简单(其实是不懂)所以就简单的写一个程序。
参考: 通过实例学Android应用开发01 - 根据此改写。 Android应用程序结构及运行原理
安卓模拟器genymotion
各位应该注意到了,idea自带的AVD模拟器很卡。为了进一步增加开发体验,我决定下一个安卓模拟器genymotion。
参考: Genymotion配置及使用教程(最新最完整版附各部分下载地址) Android模拟器Genymotion使用详解 genymotion官网
genymotion 官网下载,注意选择下载with virtual box 。
genymotion 下载完成后,点击右上角的+ ,下载安装安卓镜像。
idea打包apk
将之前写好的目标程序打包成apk,拖入模拟器中。
IntelliJ IDEA 如何导出安卓(Android)apk文件 详细教程
将得到的apk文件,直接拖入genymotion 模拟器中即可自动安装。 向上滑,打开应用程序目录。
不得不说,卡顿感明显少了许多。接下来是回放程序的制作。应该就是难点了。
genymotion不支持ARM?
解决Genymotion不兼容arm框架的问题
拖拽Genymotion-ARM-Translation.zip 无法直接安装?
github: Genymotion_ARM_Translation
找到adb 工具,genymotion 应该自带,比如说我的位置在E:\genymotion\tools 。
1. adb shell
2. cd /sdcard/Download/
3. sh /system/bin/flash-archive.sh /sdcard/Download/Genymotion-ARM-Translation.zip
4. adb reboot
执行后,会出现错误。一看,上传上去的文件名字不对。 自己上去修改。修改名字之后,再进行上面的命令。
cd /sdcard/Download/
mv ??? Genymotion-ARM-Translation.zip
2022-05-14
之前提到了HOOK。为什么要用到它,从功能是实现上来讲,我们需要做的是两部分功能。 (1)使用HOOK监听用户操作,并将操作记录 存储起来。 (2)读取操作记录,实现用户操作。 (3)HOOK程序,和 目标程序应该是两个程序。
操作记录的存储 可以 使用文件,不过这样会更复杂一点。 (1)大概了解了,是使用HOOK实现。 (2)到底要怎么实现呢? (3)两个程序 是需要考虑进程之间的通信?
HOOK框架?
*Android Hook 技术 Android 集成Xposed框架
好像目前HOOK框架,比如说Xposed这种,都需要ROOT权限,有没有更简单的方法???
VirtualXposed epic 可以实现任意方法 Hook,但只能对同一个 uid 的进程生效,VA 可以运行任意 APP,使其与自己使用同一个 uid。结合上面两种技术,有没有想到什么? 是的,这就是 VirtualXposed,结合了 epic 与 VA 的终极框架。无需 root,类 Xposed 的 API,可以说是一个相当完善的方案了。
2022-05-14 20:50
成功安装了Genymotion-ARM-Translation ,但是VirtualXposed.apk 还是无法安装,貌似还是一样的错误。(还以为很快解决这个问题的,太折磨了)
2022-05-14 21:20
忘了好多博客提醒的,路径不能含有中文!这样的话,Genymotion-ARM-Translation.zip 就可以直接拖拽安装了。
2022-05-15 10:20
失败,Genymotion 还是用不上ARM应用,也不知道是不是版本的问题。但是Genymotion 官网上已经没有其他的版本了。
android hook截取其他程序的按钮事件_android程序员hook技术之入门篇
2022-05-15
*Android插件化原理解析——概要
既然HOOK框架不行的话,或许不用框架也可以实现需要的功能?
2022-05-15 18:55 手把手教你Hook Android 点击事件 hook onClickListener
2022-05-15 23:43 下班,如果是只实现一个程序的话,大概知道要怎么做了。两个程序的话,有点麻烦。(明天再写吧 2022-05-17 20:52 需要两个应用,该思路忘记掉。
2022-05-16
夜神模拟器 & Xposed
(1)下载夜神模拟器。 (2)使用Xposed installer 安装Xposed 。
github: XposedInstaller XposedInstaller *初探Xposed,初学Xposed框架上手必看 *安卓Xposed简单实现 2022-05-16 19:46 编写第一个Xposed程序。
2022-05-17
*抱歉,Xposed真的可以为所欲为——1.基础知识储备(上) *抱歉,Xposed真的可以为所欲为——1.基础知识储备(下)
如何切换应用?
How to switch between Android apps programmatically *Android 必知必会 - 使用 Intent 打开第三方应用及验证可用性
2022-05-18
跨进程通信之——广播
Android跨进程通信广播(Broadcast) Android 进程间通信IPC(二)—— Broadcast广播
A应用如何在B应用上执行点击操作???
Xposed跨进程通信?
基于xposed实现android注册系统服务,解决跨进程共享数据问题 太复杂了。。。
获取其他应用Context
*Android获取其他包的Context实例,然后调用它的方法
广播数据如何保存?
Save data in Broadcast receiver
使用动态注册的广播对象,会一直保存数据。
PackageManager.NameNotFoundException
solved: PackageManager.NameNotFoundException
原因:api30之后安全策略的变化。 解决:添加配置文件。
如何获得其他应用的Context?
应用A,应用B 在A上 控制B的 UI。 =》在A中获得B的Context 或者 Hook B之后,在B中把Context实例发给A。
2022-05-20 该方法不行。
2022-05-19
2022-05-19 10:36
***Xposed的框架的使用
Activity生命周期
Activity生命周期
Service
Android Service详解 可能会用到。
Array[String] to String[]
Converting 'ArrayList to ‘String[]’ in Java
.toArray(new String[0])
如何获取当前系统时间?
Java如何获取当前日期和时间?
如何模拟触摸事件?
android MotionEvent.obtain模拟事件,自动触发 Android模拟点击的四种方式 ---------第一种。
使用view.performClick 是不会触发 点击动画的。所以需要模拟MotionEvent 。
|