Android+ImGui搭建

最近刚好想学一下android逆向!由于兴趣是最好的老师,因此从外挂开始做起,嗨嗨嗨!!!!

找了一圈android+imgui的资料,很可惜,找不到,因此转向github上看源码,发现主要分有两类:

1、android开发imgui后,so注入游戏。

2、外部imgui(类似windows上透明窗口+imgui)。

clone下来后发现跑不通,自己也不咋会android,因此放弃,目标投向youtube,查找了一翻发现了一个宝藏。

这个视频是手把手教学如何从新建一个native c++项目到imgui的使用,看了一遍并且手动跟着学习也能跑通,因此是一个不错的教学视频。但是视频最后主要是讲so注入游戏本体,这个操作我并没有跟着做,因为我是想实现一个外部的imgui,因此自己琢磨了一番。

一段瞎操作后在github上找到了另一个国人分享的宝藏。这个是基于外部的imgui实现,对这个代码进行学习后自己也是复现了。

youtube:[https://www.youtube.com/watch?v=vwhYcB5Ctzg](https://www.youtube.com/watch?v=vwhYcB5Ctzg)

github:`[https://github.com/ZarakiDev/External-ImGui-Android](https://github.com/ZarakiDev/External-ImGui-Android)`

一、创建Nativ C++项目

首先Android Studio(AS)创建一个Native C++项目,并且将C++标准拉到17

然后视频里还回去清理一些无关的东西,比如test目录还有MainActivity中的一些代码。其实这些无关紧要,文章这里就不清理了,直接搭建imgui。

二、导入IMGUI

GITHUB:https://github.com/ocornut/imgui

直接下载解压即可。

然后返回到as项目中规范化创建一下目录。

接着把imgui下列文件导入到对应的ImGui目录下。

Text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
./imconfig.h
./imgui.cpp
./imgui.h
./imgui_demo.cpp
./imgui_draw.cpp
./imgui_internal.h
./imgui_tables.cpp
./imgui_widgets.cpp
./imstb_rectpack.h
./imstb_textedit.h
./imstb_truetype.h
./backends/imgui_impl_android.cpp
./backends/imgui_impl_opengl3.h
./backends/imgui_impl_opengl3.cpp

native-lib.cpp修改为pch.cppcpp目录下新增一个pch.h。(这个改名其实是我在windows下的习惯,可以不改)

双击pch.h,对OpenGL开发的头文件和添加的imgui头文件都给include起来。

然后在pch.cpp#include "pch.h"。接着需要对CMakeList.txt添加链接内容:

1、add_library中新增所有的cpp项。

2、target_link_librarires中新增EGLGLESv3库。

IMGUI导入就完成了,这里Build看看是否通过。

三、初始化OpenGL

imgui是一种图形渲染UI库,因此不能直接在android的view视图上渲染,需要创建OpenGL图层;首先创建一个GLRenderView.java,使其继承GLSurfaceView.Renderer接口。

重写下列函数。

  • GLRenderView:构造函数,没啥好说。

  • onSurfaceCreated:当OpenGL视图被创建时调用。

  • onSurfaceChanged:当OpenGL视图尺寸发现改变时调用。

  • onDrawFrame:每帧调用,用于绘制。

  • onDetachedFromWindow:视图销毁时调用。

首先对构造函数添加如下代码:

接下来开始实现imgui绘制,由于该处代码均为C++代码,因此需要使用JNI特性进行编码。创建一个NativeMethods.java,用于声明native函数。

新建一个NativeMethods.h文件,用于java对应的native函数。对应代码被as默认生成到pch.cpp,这里做简单优化:

1、pch.h添加对应头文件。

2、把生成的代码扔到NativeMethods.h头文件。

最后返回到GLRenderView.java对其调用。

同理编译看一下是否能通过,确保代码无问题。

四、初始化IMGUI

目前OpenGL初始化完毕了,IMGUI库也导入了,现在开始初始化IMGUI。

1、初始化

新建一个Renderer.h头文件,编写下列代码。

打开./examples/example_android_opengl3/main.cpp,对函数void Init(struct android_app* app)中注释// Setup Dear ImGui context开始到函数结尾进行复制,粘贴到SetupRender中,然后把一些无关的代码和注释都删除一下。

这里修复一下报错:

1、pch.h中添加头文件。

2、Renderer.h中添加全局变量g_Initialized、添加防止重复初始化逻辑。

3、把g_App->window改为nullptr,这里需要调整imgui_impl_android.cpp中的函数ImGui_ImplAndroid_NewFrame。(因为我们填nullptr后,宽高为我们指定)

对应头文件也改一下

2、绘制

接下来是调用绘制了,这里也一样,还是从main.cpp里复制,直接把MainLoopStep函数拷贝过来,粘贴到Render里。

因为上边我们修改了ImGui_ImplAndroid_NewFrame,因此我们需要手动提供宽高。新增两个int全局变量g_scrrenWidthg_screenHeight

3、清理

拷贝main.cppvoid Shutdown()ShutDown。简单处理一下即可。

4、重设大小

基本上就完事了!!!直接回到NativeMethods.h里调用。

老规矩编译看一下。

五、绘制IMGUI

现在只需要把自写的GLRenderView类给添加到手机窗口上就能显示了~~~

回到MainActivity.java,删除掉一些无关紧要的代码。

添加如下代码:

这里解释一下,大概在干嘛。

1、app运行后首先判断当前应用是否有悬浮窗权限,因为我们的绘制是一层顶层窗口,这个在android叫做悬浮窗;如果没有则自动跳到设置窗口,然后需要用户手动开启。

2、获取系统级的窗口管理器,视频里用的是getWindowManager,这个是获取当前你应用的窗口管理器,用这个SERVER的级别比较高。

3、创建GLRenderView实例,也就是imgui,然后添加到窗口管理器中,这样就会显示imgui了。

然后GetLayoutParams这个函数是我封装好的(实际上是抄开头那个github库的),参数代表当前是否是绘制层窗口,里边注释也比较全,我不做太多解释;主要描述一下这个函数的作用,下边再说,现在运行app看看啥效果。

六、输入事件InputEvent

其实前边的操作与油管中的视频大差不差,但输入事件上需要做区分了,因为实现的原理不同。这里先大概实现一下对于IMGUI的事件触发代码。

NativeMethods.java中添加handleTouchEvent

接着我的做法就开始与视频不太一样了(但视频里的也是可以用,只不过是针对注入)。视频里的做法相当于是给游戏的InputEvent做了逻辑修改,

那么就相当于当你触屏了游戏时,就会触发自定义的InputEvent,这里模仿一下视频中的效果,对当前view的onTouchEvent事件重写如下:

重新运行app,你会发现可以触屏生效了!但如果你点击

返回桌面时,会发现触屏没效果了。最后你恍然大悟,老子触屏只在app内有效!这也很合理,因为相关代码是写在app的view中的。那怎么实现在app外也能触屏呢?答案有两个:

1、双悬浮窗。也就是上边那个github里的实现。

2、hook系统层的InputEvent,里边调用自定义的事件。

技术所限,暂时只会双悬浮窗,具体实现我就不详写,就大致说原理:

首先创建两个悬浮窗(vTouch、vDraw),vDraw用于imgui绘制,vTouch用于触屏。

然后给vTouch添加onTouchEvent监听,并且实时刷新vTouch的位置大小 == Imgui窗口的大小。

这样返回到桌面时也能点击imgui,但这个方案有个缺点,就是imgui只能有一个窗口(我自己是这样的,可能有其他解决方案我还不知道),因为要让vTouch时刻贴附在imgui窗口上,如果有了新窗口(比如颜色选择器),那么超出了原窗口的位置则无法进行触屏。

附上我自己整合后的代码:https://github1s.com/PlaneJun/android_imgui_demo

七、总结

总结个🐔毛!