Shocker 发表于 2022-2-7 15:02

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

最后附上截图

李佑辰 发表于 2022-2-7 16:57

附件中的植物大战僵尸 转存到蓝奏了:有需要的人可以取:https://l13144.lanzoul.com/i8QOuzpkjmb

mustBePower 发表于 2023-12-31 17:11

你好,请问这里是不是有点问题啊?
String str =getBaseContext().getApplicationInfo().nativeLibraryDir+"/libjnidraw.so";
      os.write((str + "\n").getBytes());
      os.write("exit\n".getBytes());
这里是加载so文件吗?调起进程是这样写的吗?好像没有调起发送数据的进程啊

zheng10072 发表于 2022-2-7 15:53

很强,吃鸡或王者要是能用到就厉害了

bwuaich 发表于 2022-2-7 16:17

谢谢分享和交流; 学习下

耿梦莹123 发表于 2022-2-7 17:19

有没有单机版的植物大战僵尸可以保存在u盘里然后玩的

傲天越 发表于 2022-2-7 18:46

感谢楼主分享,!!!

hjsaiyy 发表于 2022-2-8 08:00

感谢楼主分享~~

icyjin 发表于 2022-2-8 09:45

感谢分享新技术,虽然我啥都不懂。

菠萝蜜 发表于 2022-2-8 10:04

感谢楼主分享~~

UndCover 发表于 2022-2-8 10:44

这个必须要root才能实现吗
页: [1] 2 3
查看完整版本: Android悬浮窗绘制