Android悬浮窗绘制
本帖最后由 Shocker 于 2022-2-7 15:37 编辑## 前言
之前研究了安卓root环境下内存读写的方案,今天分享一下安卓的绘制方法.
## 环境
1.雷电模拟器3
2.Android5.1真机
3.Android Studio 2020.3.1 Patch4
4.ndk (22.1.7171670)
## 原理
1.app向WindowManager添加一个具有悬浮窗属性的view,并且设置为**透明不可点击**.这个view主要用来绘制.
```
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
getLayoutType(),
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
if (Build.VERSION.SDK_INT >= 28) {
params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES ;
}
//添加悬浮窗
windowManager.addView(overlayView, params);
```
2.app使用root权限启动一个**root进程**,该进程负责读取游戏内数据并用udp发送到app中.
```
public void StartSuProcess() throws Exception{
Process p = Runtime.getRuntime().exec("su");
OutputStream os = p.getOutputStream();
p.getErrorStream();
String str =getBaseContext().getApplicationInfo().nativeLibraryDir+"/libjni_draw.so";
os.write((str + "\n").getBytes());
os.write("exit\n".getBytes());
os.flush();
os.close();
}
```
3.app接收到进程发来的数据,在onDraw方法中调用drawRect函数进行绘制.
```
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
// DrawRect(canvas,255, 255, 255, 255,1,80,80,550,600);
Draw(this,canvas);
invalidate();
}
```
## 具体步骤
1.首先用ce搜出僵尸的坐标,搜未知的初始值,然后依次搜索减少的数值,最后搜索到僵尸的坐标地址特征码
```
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF
```
用之前内存读取的方法,结合udp,将读取到的地址坐标转为结构体发送给app端.
2.新建OverlayView类,该类继承View类并重写onDraw方法,之后调用**windowManager的addView**方法添加到系统中,然后在onDraw中调用native方法绘制方框,**并调用invalidate方法反复刷新**.
3.**在native中建立起udp连接**,新建一个线程接收root进程发送过来的数据,onDraw线程进入native后反射调用OverlayView类中的DrawRect方法绘制矩形.
## 有几个需要注意的地方
1.因为使用了udp,所以要在AndroidManifest.xml中添加权限
```
<uses-permission android:name="android.permission.INTERNET"/>
```
悬浮窗为
```
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
```
2.绘制的时候最好一次绘制所有的矩形,否则会出现绘制闪烁的情况.
3.悬浮窗创建的时候需要默认为横屏,否则竖屏模式创建的悬浮窗在转换到横屏后会出现显示问题.
需要在AndroidManifest.xml的activity中添加属性,**横屏显示**.
```
android:screenOrientation="landscape"
```
4.root进程用ndk-build编译,放在jni目录里,编译后需要将名字改为**libjni_draw.so**,配置build.gradle打包进app里,并在app启动时由**StartSuProcess**方法启动.
5.关闭app后root进程不会随之关闭,需要在root进程中不断检测app存活从而判断是否需要关闭.
```
void *checkApp(void *)
{
while (true)
{
if (find_pid(APP_PACKAGE_NAME) == -1)
{
exit(0);
}
sleep(5);
}
}
```
6.由于是用特征码进行搜索,因此会搜出许多非真实的数据.大家也可以进一步排查.
## 写在最后
除了使用udp传输数据外,还可以用mmap方式的共享内存进行数据传输.
绘制方面除了用悬浮窗外,还可以借助android源码和skia编译可执行文件绘制.
Andorid5.1测试通过(雷电模拟器3和安卓真机),
其余版本未知.
github地址:
https://github.com/PShocker/Android_float_view_draw
植物大战僵尸网盘地址:
链接:https://pan.baidu.com/s/15N_GaoDQBGPfIfXwfvA9dA
提取码:zd68
最后附上截图
附件中的植物大战僵尸 转存到蓝奏了:有需要的人可以取:https://l13144.lanzoul.com/i8QOuzpkjmb 你好,请问这里是不是有点问题啊?
String str =getBaseContext().getApplicationInfo().nativeLibraryDir+"/libjnidraw.so";
os.write((str + "\n").getBytes());
os.write("exit\n".getBytes());
这里是加载so文件吗?调起进程是这样写的吗?好像没有调起发送数据的进程啊 很强,吃鸡或王者要是能用到就厉害了 谢谢分享和交流; 学习下 有没有单机版的植物大战僵尸可以保存在u盘里然后玩的 感谢楼主分享,!!! 感谢楼主分享~~ 感谢分享新技术,虽然我啥都不懂。 感谢楼主分享~~ 这个必须要root才能实现吗