一眨眼就到春节了,好久没有写文章了,趁着新年空闲,赶紧把自己折腾了一段时间的东西整理了一下。在折腾的过程中,碰到了不少问题,感谢大佬们的帮助。目前这个工具不算是怎么完善吧,但是感觉能凑合使用了,剩下的部分在使用中再慢慢完善吧,其中部分代码我会开源,其实感觉实现的核心并不怎么复杂。算是一个萌新学习定制ROM过程的一个作品把。而且还有个调试超级慢的BUG,如果有大佬知道是啥原因,还请指导一下。万分感谢。
看雪高研班课程
FART脱壳王课程
珍惜大佬的android进阶课程
详细的介绍请看:FartExt之优化更深主动调用的FART10
FartExt是我之前学习脱壳实践时做的一个自动脱壳机,是基于FART的主动调用思想实现对特定的抽取壳进行优化处理的工具。由于原本的FART没有配置相关的,所以我增加了配置对指定app脱壳。大致就是对FART的简单优化。由于感觉当时做的功能并不怎么完善,所以只是短暂的放了下载地址,就删掉了。不知道有没有人实际使用。使用的效果到底咋样。现在放出开源代码。
github:FartExt
首先回顾一下当时FartExt文章最后的思考部分
整个流程梳理完成后,我们可以由此来借鉴来思考延伸一下。
比如,包装一些属于自己的系统层api调用。便于我们使用xposed或者是frida来调用一些功能。
再比如,加载应用时,读取配置文件作为开关,我们来对网络流量进行拦截写入保存,或者对所有的jni函数调用,或者是java函数调用进行trace。这种就属于是rom级别的打桩。
再比如,可以做一个应用来读写作为开关的配置文件,而rom读取配置文件后,对一些流程进行调整。例如控制FART是否使用更深调用。控制是否开启rom级别的打桩。
以上纯属个人瞎想。刚刚入门,想的有点多,以后了解更深了,我再看看如何定制一个专属的rom逆向集合
MikRom就是当时个人瞎想的成果,做一个Rom层面的逆向工具,为我们提供比较常用的插桩、注入、脱壳等一系列功能。下面列上目前MikRom中包含的功能
github:MikRom
由于功能做的更加复杂了,我们自行编辑配置文件不再那么方便了,所以我特地做了一个界面化的工具来操作。最后将我们的设置保存到文件,然后MikRom会在打开app时读取文件,解析后做对应的操作。MikManager就是用来管理这个配置的。界面较为简陋,如果对MikRom感兴趣的,但是感觉我的界面太丑,也可以自己编写一个界面管理工具。我正向开发比较渣,所以代码较为粗糙。不过目前使用没啥问题。
github:MikManager
虽然功能不是很多,但是做这个工具却折腾了我很长的时间,这里我将记录下这样一个简陋的Rom工具开发的历程,由于并不是很清楚其他大佬是如何处理的,所以有些功能都是我不断的试错中找到的方案。在试错的过程中,走了不少弯路,希望能帮到一些小伙伴。如果我采用的方案犯了什么比较低级的错误,还请大佬能指点一下。另外有大佬说,做越简单化的工具,越危险。如果有什么和谐的风险或者是法律的问题,还请联系我进行修改。
早先FartExt我采用的是aosp10r2的源码进行修改编译。后来有同学觉得界面太简陋了,看起来就像山寨系统。确实如此,然后我就参考了hanbingle老师在Fart脱壳王使用的rom,选择了PixelExperience来进行修改。编译的marlin版本,测试手机是pixel XL。
参考文章:https://blog.csdn.net/weixin_42443075/article/details/118084535
上面是我当时编译参考的文章。另外在编译PixelExperience时编译碰到错误,需要修改build/blueprint/Blueprints
另外一个问题就是这个rom居然没法在mac进行编译,最后没办法只能用ubuntu来开发了。
最早先在FartExt中的配置是我们自己在/data/local/tmp/中写入一个fext.config的文件,然后在应用启动过程中的handleBindApplication调用时,解析这个配置文件,来决定是否要脱壳。而现在配置更加复杂了,我们需要使用一个app来对配置进行管理生成,而app没有对/data/local/tmp写入文件的权限。所以我们这里就有了一个简单的需求,要将配置文件落地到一个所有应用都有权限访问的地方。
有人就要说了,我们可以落地到sdcard,是的,早期我就是这么干的。配置文件落地到sdcard后,所有的app要使用功能,就必须先打开sdcard,并且,由于我使用的rom版本是安卓10的,而安卓10中,你想要直接访问sdcard的任意目录,是需要设置requestLegacyExternalStorage="true"
。所以这就导致了,即使我们不嫌麻烦,每个想处理的app都打开sdcard,也会出现有些app无法访问到配置文件。
在这里我使用的方案是,创建一个系统服务,这个系统服务提供一个读和写的函数,然后通过调用系统服务将配置文件落地到/data/system/
目录中,然后每个应用打开时再通过这个系统服务来读取配置。
这个系统服务目前主要就是为配置管理提供读写权限。可能这样干会有些漏洞的问题,不过我这个rom本身就是逆向使用的工具,而面向正常用户,所以就暂时不考虑漏洞问题了。
参考文章:Android AOSP 添加系统服务【aidl接口服务】Java层
参考文章:android 10 添加系统服务步骤
下面贴上我定义的系统服务
至于详细的读写方法我就不贴了,就是和android正常的读写文件处理一样。我就说一下在这里我碰到的坑,我按照参考文章一样的方式做完之后,发现无法找到服务,但是service list|grep mikrom
是可以匹配到我自己定义的服务的。最后经过排查日志发现selinux提示缺少了find权限,于是我修改文件system/sepolicy/public/untrusted_app.te
,内容如下
最后成功找到服务,将配置文件写入到了/data/system/
目录中。
这里我只简单的贴一下相关
MikManager的写入的相关代码
MikRom中读取数据
测试的配置文件内容如下
到这里第一步的优化就完成了,读写配置成功脱离了对sdcard的权限依赖,以及能全局应用都可以访问到。具体的详细相关代码可参考我放出的部分源码。
由于我走向了另外一条偏向更易于使用的优化方向,所以关于脱壳的关键部分我并没有做什么优化,所以如果脱壳需求较大的朋友可以考虑看看hanbingle大佬的脱壳王是否能满足需求。
我对脱壳的优化主要分为两点,由于FartExt当时的脱壳结果保存是在sdcard中,所以对于权限有一定依赖,所以我优化掉了这块的依赖,让我们不用再手动开启sdcard权限,也能保存下脱壳结果。另外一点就是当时如果是抽取壳的情况,我们需要拿出两个文件来手动修复一下。我这里也优化了一下,会直接将函数执行完的dex重新再保存一份,这样就无需我们再手动修复了。当然同时也保留了原本的做法,仍然保存大佬说的几个重要元素。
我尝试了很多种办法来避免我们手动开启sdcard权限的情况下保存脱壳结果文件。最后测试成功了两种办法。这两种办法我都简单说下。
第一种办法,我们直接将数据可以写入应用本身的内部空间中。也就是/data/data/<PackageName>
中。但是这样有权限写入,但是没有root的话就无法访问到保存的结果了,当然没有root的情况也是能访问到应用内部数据的。我们可以通过命令run-as <PackageName>
来直接进入应用数据内部。但是也有人会问,如果对方应用没有开启debuggable,不就没办法使用run-as了吗?这就是改Rom的优势所在了。我们可以直接修改PackageParser中的函数parseBaseApplication。在里面为我们默认打开debuggable即可。这样即使对方没有设置,在加载xml的时候,也会打开这个功能。
第二种办法,既然第一个办法最后可以直接改xml为我们默认打开debuggable,那么我们解决sdcard权限,也可以修改PackageParser中的函数parseBaseApplication来直接打开sdcard权限。不过由于安卓10的特殊性,即使打开了sdcard权限,也只能在sdcard中自己的目录写入数据,所以使用这个办法时,脱壳结果应保存在/sdcard/Android/data/<PackageName>/
目录中。
下面贴上相关的代码,其中包含了两种解决方案
在优化这个问题前,首先要意识到为什么会需要手动修复,当我们理解了大佬的处理之后,就发现hanbingle大佬为了避免每个函数主动调用都将dex给保存,所以只有文件不存在的时候才保存。也就意味着我们保存的dex是第一个主动调用执行时的dex。如果这个抽取壳是必须函数执行后才会恢复的,那么后面的函数在这个保存dex中都依然是被抽取的。FART的做法是将codeitem保存出来后,然后再修复。所以我将这里优化了一下。
知道问题所在后,优化的思路就清晰了,我采用了比较简单的一种优化方式,就是每个dex文件保存时,将这个dex的地址以及长度给保存下来。最后在所有主动调用完成时,重新将所有dex文件再保存一次。下面看看优化后的相关代码
最后生成的_repair.dex文件就是无需再修复的了,如果想要手动修复按照原来的方式也可以。当然,如果主动调用未能成功跑完,这个repair文件也是不会生成的。
关于修改内核反调试已经有相当多的文章讲解了,我这里也是和他们用的同一个方案,基本都是根据正向检测调试的方式,来进行反调试,当然如果有人用其他方式来判断是否被调试,这个反调试就无效了。详细的过反调试原理可以看参考文章。
参考文章:修改内核源码绕过反调试检测(Android10)
虽然我改了。但是没测试效果咋样。修改的代码如下,kernel/google/marlin/fs/proc/array.c
当对方检测/proc/<pid>/status
文件时,获取到的TracerPid就会一直是0,并且应用的状态不会出现stopped
和tracing stop
。下面是status的部分内容
还有一处修改文件是kernel/google/marlin/fs/proc/base.c
通过sleep的反调试方式是在看雪高研班中学习到的,据说这个办法可以过掉一些在前期检测的native的反调试。思路就是需要调试的JNI函数执行前会调用JniMethodStart,那么我们只要在这里进行判断,如果这个函数是目标函数,就sleep睡眠若干秒。在睡眠期间,我们再调试附加上即可。相关代码如下
这个其实没什么难的,在关键的函数位置直接打印即可。我只讲一下jni的打桩部分,我查资料的时候看到了两种做法,第一种是打印jni部分是在jni函数中找比较通用的调用函数,例如InvokeVirtualOrInterfaceWithJValues、InvokeWithVarArgs、InvokeWithJValues
,然后在里面加上打印。第二种就是像jnitracer一样,通过偏移,找到所有的函数指针,然后再包装处理,比如加一个外层函数调用。由于我c++非常烂,所以我采用最笨的方法,我写了几个较为通用的打印函数,jni_internal.cc中的所有我关心的jni函数,就调用各自对应的打印函数。下面贴上我的例子。
处理完判断部分,接下来就是对于参数的打印部分了,这里我只处理了下将字符串参数打印出来,其他特殊的参数我就没有处理了。
搞定了封装部分,就看看调用部分是怎么使用的
到这里就搞定了jni的打印以及参数的打印,大多数调用我都打印了,由于我实战的少,所以可能会漏掉一些常用的打印,如果有发现什么比较有用的打印,也可以告诉我优化下。
这个frida持久化的问题,论坛里面也发过一遍又一遍了。我也是参考他们的文章,然后测试优化。先贴上参考文章。
参考文章:ubuntu 20.04系统AOSP(Android 11)集成Frida
参考文章:从0开始实现一个简易的主动调用框架
不过我的加载时机和他的不太一样,并且我扩展了可自行切换frida-gadget的版本,默认集成到系统中的版本是15.1,如果你喜欢用14.2的版本,可以自己上传到手机进行切换,如果你想不root的情况下,也可以通过MikManager设置切换的版本。下面贴上加载的部分代码。
同时也是支持以三种模式加载gadget的,比如默认加载脚本,或者监听模式,也就是可以frida连接上去,或者是监听并阻塞等待。不过我留意到,有些自己写的简单app似乎使用gadget注入似乎并不成功。好像需要app对native有什么实现才行。
前面gadget的部分实际就是注入so了,但是前面的部分主要是针对gadget的注入相关处理的。我又特地单独处理了一下对so的注入,如果我们自己写了一个so想要单独注入进去也是可以的。为了方便演示,我特地将dobby内置进去。由于dobby是一个hook框架,并且没有注入功能,往往都是需要通过其他手段注入到应用中。所以我直接内置进来了。下面贴上相关代码
实际上这个功能在ROM中是有的,就是TraceExecution函数,只是需要控制打开就行了,但是必须走switch解释器执行才能执行到便于我们修改的地方。所以这个就有两个地方需要修改,第一个是判断打印的部分,第二个是强制以解释器执行的部分。下面看看我的修改部分
另外就是强制以解释器执行的部分
除了这里,在LinkerCode的地方也需要修改
变量enter_interpreter决定了是否使用解释器执行,如果是quick快速模式执行,将无法执行到我们修改的函数。最后测试发现,函数第一次调用的时候,是走的解释执行,成功打印出trace记录,第二次调用时,又被优化到快速模式执行了。不过我们已经拿到想要的结果了,我就不继续优化了。关于LinkCode这里的修改如果不理解的,可以看看下面的参考文章,里面有详细的流程图了。我就不重复了。
参考文章:将FART和Youpk结合来做一次针对函数抽取壳的全面提升
这个功能我原本是想内置一个IO重定向,或者是内置一个Sandhook,然后直接通过配置勾选就能自动注入使用了。然后我们修改的dex直接注入进去即可使用。不过目前还没想好怎么设计里面的一些交互部分。所以只是简单的做了下注入的步骤。后续也不一定有时间继续实现,先暂时搁置把。
操作起来非常简单,选择想要处理的应用,然后选择对应功能即可。记得勾选完后,回到应用栏目点保存,如果是要使用frida脚本,或者是要选择so,需要把文件放在sdcard中的对应应用的目录中。如果sdcard中没有对应目录,可以打开一下应用会自动生成的。
脱壳功能的保存结果在/sdcard/Android/data/<PackageName>/dump
中查看
查看logcat输出日志统一搜索mikrom,jni调用的输出的格式如下
RegisterNative的输出格式如下
ArtInvoke的输出格式如下
smali trace日志的输出结果如下
so注入如果勾选自动注入dobby后,就会注入系统中自带的,如果你想注入自己编译的dobby,也可以在so注入里面导入dobby,注入顺序会默认按照导入顺序。
整理一遍之后,感觉好像做了很多,又感觉好像也没搞点啥。也碰到很多问题让我一遍又一遍的编译测试。不过总算大致实现了当时的想法。后续应该不会有啥升级了。调转方向研究点其他东西了。如果以后内核功底深一些了,可能会回头扩展把。重要的事情再提一次,不知道修改到了art部分的哪个点,导致了调试超级慢,如果有大佬知道啥原因的,麻烦指导一下。
bootstrap_go_package {
name:
"blueprint-pathtools"
,
pkgPath:
"github.com/google/blueprint/pathtools"
,
deps: [
"blueprint-deptools"
,
],
srcs: [
"pathtools/lists.go"
,
"pathtools/fs.go"
,
"pathtools/glob.go"
,
],
testSrcs: [
/
/
修改处,这里的内容删掉
],
}
bootstrap_go_package {
name:
"blueprint-pathtools"
,
pkgPath:
"github.com/google/blueprint/pathtools"
,
deps: [
"blueprint-deptools"
,
],
srcs: [
"pathtools/lists.go"
,
"pathtools/fs.go"
,
"pathtools/glob.go"
,
],
testSrcs: [
/
/
修改处,这里的内容删掉
],
}
interface IMikRom
{
/
/
读取文件
String readFile(String path);
/
/
写入文件
void writeFile(String path,String data);
/
/
执行shell命令
String shellExec(String cmd);
}
interface IMikRom
{
/
/
读取文件
String readFile(String path);
/
/
写入文件
void writeFile(String path,String data);
/
/
执行shell命令
String shellExec(String cmd);
}
type
untrusted_app, domain;
type
untrusted_app_27, domain;
type
untrusted_app_25, domain;
allow untrusted_app mikrom_service:service_manager find;
allow untrusted_app_27 mikrom_service:service_manager find;
allow untrusted_app_25 mikrom_service:service_manager find;
type
untrusted_app, domain;
type
untrusted_app_27, domain;
type
untrusted_app_25, domain;
allow untrusted_app mikrom_service:service_manager find;
allow untrusted_app_27 mikrom_service:service_manager find;
allow untrusted_app_25 mikrom_service:service_manager find;
/
/
获取服务
public
class
ServiceUtils {
private static IMikRom iMikRom
=
null;
public static IMikRom getiMikRom() {
if
(iMikRom
=
=
null) {
try
{
Class localClass
=
Class.forName(
"android.os.ServiceManager"
);
Method getService
=
localClass.getMethod(
"getService"
, new Class[] {String.
class
});
if
(getService !
=
null) {
Object
objResult
=
getService.invoke(localClass, new
Object
[]{
"mikrom"
});
if
(objResult !
=
null) {
IBinder binder
=
(IBinder) objResult;
iMikRom
=
IMikRom.Stub.asInterface(binder);
}
}
} catch (Exception e) {
Log.d(
"MikManager"
,e.getMessage());
e.printStackTrace();
}
}
return
iMikRom;
}
}
/
/
将json数据保存到指定路径
public static void SaveMikromConfig(
List
<PackageItem> packageList){
Log.e(ConfigUtil.TAG,
"SaveMikromConfig"
);
Gson gson
=
new Gson();
String savejson
=
gson.toJson(packageList);
try
{
ServiceUtils.getiMikRom().writeFile(ConfigUtil.configPath,savejson);
} catch (RemoteException e) {
Log.e(ConfigUtil.TAG,
"writeConfig err:"
+
e.getMessage());
}
}
/
/
获取服务
public
class
ServiceUtils {
private static IMikRom iMikRom
=
null;
public static IMikRom getiMikRom() {
if
(iMikRom
=
=
null) {
try
{
Class localClass
=
Class.forName(
"android.os.ServiceManager"
);
Method getService
=
localClass.getMethod(
"getService"
, new Class[] {String.
class
});
if
(getService !
=
null) {
Object
objResult
=
getService.invoke(localClass, new
Object
[]{
"mikrom"
});
if
(objResult !
=
null) {
IBinder binder
=
(IBinder) objResult;
iMikRom
=
IMikRom.Stub.asInterface(binder);
}
}
} catch (Exception e) {
Log.d(
"MikManager"
,e.getMessage());
e.printStackTrace();
}
}
return
iMikRom;
}
}
/
/
将json数据保存到指定路径
public static void SaveMikromConfig(
List
<PackageItem> packageList){
Log.e(ConfigUtil.TAG,
"SaveMikromConfig"
);
Gson gson
=
new Gson();
String savejson
=
gson.toJson(packageList);
try
{
ServiceUtils.getiMikRom().writeFile(ConfigUtil.configPath,savejson);
} catch (RemoteException e) {
Log.e(ConfigUtil.TAG,
"writeConfig err:"
+
e.getMessage());
}
}
private static IMikRom iMikRom
=
null;
public static IMikRom getiMikRom() {
if
(iMikRom
=
=
null) {
try
{
Class localClass
=
Class.forName(
"android.os.ServiceManager"
);
Method getService
=
localClass.getMethod(
"getService"
, new Class[] {String.
class
});
if
(getService !
=
null) {
Object
objResult
=
getService.invoke(localClass, new
Object
[]{
"mikrom"
});
if
(objResult !
=
null) {
IBinder binder
=
(IBinder) objResult;
iMikRom
=
IMikRom.Stub.asInterface(binder);
}
}
} catch (Exception e) {
Log.d(
"MikManager"
,e.getMessage());
e.printStackTrace();
}
}
return
iMikRom;
}
public static String getMikConfig(){
try
{
IMikRom mikrom
=
getiMikRom();
if
(mikrom
=
=
null){
return
"";
}
return
mikrom.readFile(
"/data/system/mik.conf"
);
} catch (RemoteException e) {
e.printStackTrace();
}
return
"";
}
private static IMikRom iMikRom
=
null;
public static IMikRom getiMikRom() {
if
(iMikRom
=
=
null) {
try
{
Class localClass
=
Class.forName(
"android.os.ServiceManager"
);
Method getService
=
localClass.getMethod(
"getService"
, new Class[] {String.
class
});
if
(getService !
=
null) {
Object
objResult
=
getService.invoke(localClass, new
Object
[]{
"mikrom"
});
if
(objResult !
=
null) {
IBinder binder
=
(IBinder) objResult;
iMikRom
=
IMikRom.Stub.asInterface(binder);
}
}
} catch (Exception e) {
Log.d(
"MikManager"
,e.getMessage());
e.printStackTrace();
}
}
return
iMikRom;
}
public static String getMikConfig(){
try
{
IMikRom mikrom
=
getiMikRom();
if
(mikrom
=
=
null){
return
"";
}
return
mikrom.readFile(
"/data/system/mik.conf"
);
} catch (RemoteException e) {
e.printStackTrace();
}
return
"";
}
[{
"appName"
:
"crackme"
,
"breakClass"
:"
","
dexClassName
":"
","
dexPath
":"
","
enabled
":true,"
fridaJsPath
":"
","
gadgetArm64Path
":"
","
gadgetPath
":"
","
isDeep
":false,"
isDobby
":false,"
isInvokePrint
":false,"
isJNIMethodPrint
":true,"
isRegisterNativePrint
":false,"
isTuoke
":false,"
packageName
":"
com.kanxue.crackme
","
port
":0,"
sleepNativeMethod
":"
","
soPath
":"
","
traceMethod
":"
","
whiteClass
":"
","
whitePath
":"
"}]
[{
"appName"
:
"crackme"
,
"breakClass"
:"
","
dexClassName
":"
","
dexPath
":"
","
enabled
":true,"
fridaJsPath
":"
","
gadgetArm64Path
":"
","
gadgetPath
":"
","
isDeep
":false,"
isDobby
":false,"
isInvokePrint
":false,"
isJNIMethodPrint
":true,"
isRegisterNativePrint
":false,"
isTuoke
":false,"
packageName
":"
com.kanxue.crackme
","
port
":0,"
sleepNativeMethod
":"
","
soPath
":"
","
traceMethod
":"
","
whiteClass
":"
","
whitePath
":"
"}]
/
/
这里我判断如果非系统应用才增加这些权限
if
((ai.flags&ApplicationInfo.FLAG_SYSTEM)!
=
1
){
/
/
试错中发现,开启这些权限的时候,有个google和message相关的进程会崩溃,所以过滤一下
if
(!ai.packageName.contains(
"google"
) && !ai.packageName.contains(
"message"
)){
/
/
下面是sdcard权限开启,以及debuggable权限开启。如果担心检测,可以关掉debuggable的默认开启
ai.privateFlags |
=
ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE;
ai.flags |
=
ApplicationInfo.FLAG_EXTERNAL_STORAGE;
ai.flags |
=
ApplicationInfo.FLAG_DEBUGGABLE;
/
/
Debuggable implies profileable
ai.privateFlags |
=
ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL;
}
}
/
/
这里我判断如果非系统应用才增加这些权限
if
((ai.flags&ApplicationInfo.FLAG_SYSTEM)!
=
1
){
/
/
试错中发现,开启这些权限的时候,有个google和message相关的进程会崩溃,所以过滤一下
if
(!ai.packageName.contains(
"google"
) && !ai.packageName.contains(
"message"
)){
/
/
下面是sdcard权限开启,以及debuggable权限开启。如果担心检测,可以关掉debuggable的默认开启
ai.privateFlags |
=
ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE;
ai.flags |
=
ApplicationInfo.FLAG_EXTERNAL_STORAGE;
ai.flags |
=
ApplicationInfo.FLAG_DEBUGGABLE;
/
/
Debuggable implies profileable
ai.privateFlags |
=
ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL;
}
}
/
/
存放dex的指针和长度
static std::
map
<void
*
,size_t> dex_map;
/
/
主动调用函数的dump处理
extern
"C"
void dumpArtMethod(ArtMethod
*
artmethod) REQUIRES_SHARED(Locks::mutator_lock_) {
...
int
dexfilefp
=
open
(dexfilepath,O_RDONLY,
0666
);
/
/
/
dex文件存在则不处理,避免主动调用每次都要重新保存dex
if
(dexfilefp>
0
){
close(dexfilefp);
dexfilefp
=
0
;
}
else
{
LOG(ERROR) <<
"mikrom ArtMethod::dumpdexfilebyArtMethod save dex_map"
;
/
/
将这个地址给保存下来
dex_map.insert(std::pair<void
*
,size_t>((void
*
)begin_,size_));
int
fp
=
open
(dexfilepath,O_CREAT|O_APPEND|O_RDWR,
0666
);
...
}
}
/
/
主动调用完成时,重新保存到文件名<dexSize>_dexfile_repair.dex中
extern
"C"
void dumpDexOver() REQUIRES_SHARED(Locks::mutator_lock_) {
if
(dex_map.size()<
=
0
){
LOG(ERROR) <<
"mikrom dumpDexOver dex_map.size()<=0"
;
return
;
}
char
*
dexfilepath
=
(char
*
)malloc(sizeof(char)
*
1000
);
LOG(ERROR) <<
"mikrom ArtMethod::dumpDexOver"
;
int
result
=
0
;
char
*
packageName
=
ArtMethod::GetPackageName();
std::
map
<void
*
, size_t>::iterator
iter
;
for
(
iter
=
dex_map.begin();
iter
!
=
dex_map.end();
iter
+
+
) {
void
*
begin_
=
iter
-
>first;
size_t size_
=
iter
-
>second;
int
size_int_
=
(
int
)size_;
memset(dexfilepath,
0
,
1000
);
sprintf(dexfilepath,
"/sdcard/Android/data/%s/files/dump"
,packageName);
mkdir(dexfilepath,
0777
);
memset(dexfilepath,
0
,
1000
);
sprintf(dexfilepath,
"/sdcard/Android/data/%s/files/dump/%d_dexfile_repair.dex"
,packageName,size_int_);
int
dexfilefp
=
open
(dexfilepath,O_RDONLY,
0666
);
if
(dexfilefp>
0
){
close(dexfilefp);
dexfilefp
=
0
;
}
else
{
int
fp
=
open
(dexfilepath,O_CREAT|O_APPEND|O_RDWR,
0666
);
if
(fp>
0
)
{
result
=
write(fp,(void
*
)begin_,size_);
if
(result<
0
)
{
LOG(ERROR) <<
"mikrom ArtMethod::dumpDexOver,open dexfilepath error"
;
}
fsync(fp);
close(fp);
memset(dexfilepath,
0
,
1000
);
}
}
}
if
(dexfilepath!
=
nullptr)
{
free(dexfilepath);
dexfilepath
=
nullptr;
}
}
/
/
存放dex的指针和长度
static std::
map
<void
*
,size_t> dex_map;
/
/
主动调用函数的dump处理
extern
"C"
void dumpArtMethod(ArtMethod
*
artmethod) REQUIRES_SHARED(Locks::mutator_lock_) {
...
int
dexfilefp
=
open
(dexfilepath,O_RDONLY,
0666
);
/
/
/
dex文件存在则不处理,避免主动调用每次都要重新保存dex
if
(dexfilefp>
0
){
close(dexfilefp);
dexfilefp
=
0
;
}
else
{
LOG(ERROR) <<
"mikrom ArtMethod::dumpdexfilebyArtMethod save dex_map"
;
/
/
将这个地址给保存下来
dex_map.insert(std::pair<void
*
,size_t>((void
*
)begin_,size_));
int
fp
=
open
(dexfilepath,O_CREAT|O_APPEND|O_RDWR,
0666
);
...
}
}
/
/
主动调用完成时,重新保存到文件名<dexSize>_dexfile_repair.dex中
extern
"C"
void dumpDexOver() REQUIRES_SHARED(Locks::mutator_lock_) {
if
(dex_map.size()<
=
0
){
LOG(ERROR) <<
"mikrom dumpDexOver dex_map.size()<=0"
;
return
;
}
char
*
dexfilepath
=
(char
*
)malloc(sizeof(char)
*
1000
);
LOG(ERROR) <<
"mikrom ArtMethod::dumpDexOver"
;
int
result
=
0
;
char
*
packageName
=
ArtMethod::GetPackageName();
std::
map
<void
*
, size_t>::iterator
iter
;
for
(
iter
=
dex_map.begin();
iter
!
=
dex_map.end();
iter
+
+
) {
void
*
begin_
=
iter
-
>first;
size_t size_
=
iter
-
>second;
int
size_int_
=
(
int
)size_;
memset(dexfilepath,
0
,
1000
);
sprintf(dexfilepath,
"/sdcard/Android/data/%s/files/dump"
,packageName);
mkdir(dexfilepath,
0777
);
memset(dexfilepath,
0
,
1000
);
sprintf(dexfilepath,
"/sdcard/Android/data/%s/files/dump/%d_dexfile_repair.dex"
,packageName,size_int_);
int
dexfilefp
=
open
(dexfilepath,O_RDONLY,
0666
);
if
(dexfilefp>
0
){
close(dexfilefp);
dexfilefp
=
0
;
}
else
{
int
fp
=
open
(dexfilepath,O_CREAT|O_APPEND|O_RDWR,
0666
);
if
(fp>
0
)
{
result
=
write(fp,(void
*
)begin_,size_);
if
(result<
0
)
{
LOG(ERROR) <<
"mikrom ArtMethod::dumpDexOver,open dexfilepath error"
;
}
fsync(fp);
close(fp);
memset(dexfilepath,
0
,
1000
);
}
}
}
if
(dexfilepath!
=
nullptr)
{
free(dexfilepath);
dexfilepath
=
nullptr;
}
}
static const char
*
const task_state_array[]
=
{
"R (running)"
,
/
*
0
*
/
"S (sleeping)"
,
/
*
1
*
/
"D (disk sleep)"
,
/
*
2
*
/
"S (sleeping)"
,
/
*
4
*
/
/
/
这里之前是
"T (stopped)"
,
"S (sleeping)"
,
/
*
8
*
/
/
/
这里之前是
"t (tracing stop)"
"X (dead)"
,
/
*
16
*
/
"Z (zombie)"
,
/
*
32
*
/
};
static inline void task_state(struct seq_file
*
m, struct pid_namespace
*
ns,
struct pid
*
pid, struct task_struct
*
p)
{
struct user_namespace
*
user_ns
=
seq_user_ns(m);
struct group_info
*
group_info;
int
g;
struct fdtable
*
fdt
=
NULL;
const struct cred
*
cred;
pid_t ppid
=
0
, tpid
=
0
;
struct task_struct
*
leader
=
NULL;
rcu_read_lock();
if
(pid_alive(p)) {
struct task_struct
*
tracer
=
ptrace_parent(p);
if
(tracer)
tpid
=
task_pid_nr_ns(tracer, ns);
ppid
=
task_tgid_nr_ns(rcu_dereference(p
-
>real_parent), ns);
leader
=
p
-
>group_leader;
}
/
/
这个直接强制改tpid为
0
。
tpid
=
0
;
cred
=
get_task_cred(p);
...
}
static const char
*
const task_state_array[]
=
{
"R (running)"
,
/
*
0
*
/
"S (sleeping)"
,
/
*
1
*
/
"D (disk sleep)"
,
/
*
2
*
/
"S (sleeping)"
,
/
*
4
*
/
/
/
这里之前是
"T (stopped)"
,
"S (sleeping)"
,
/
*
8
*
/
/
/
这里之前是
"t (tracing stop)"
"X (dead)"
,
/
*
16
*
/
"Z (zombie)"
,
/
*
32
*
/
};
static inline void task_state(struct seq_file
*
m, struct pid_namespace
*
ns,
struct pid
*
pid, struct task_struct
*
p)
{
struct user_namespace
*
user_ns
=
seq_user_ns(m);
struct group_info
*
group_info;
int
g;
struct fdtable
*
fdt
=
NULL;
const struct cred
*
cred;
pid_t ppid
=
0
, tpid
=
0
;
struct task_struct
*
leader
=
NULL;
rcu_read_lock();
if
(pid_alive(p)) {
struct task_struct
*
tracer
=
ptrace_parent(p);
if
(tracer)
tpid
=
task_pid_nr_ns(tracer, ns);
ppid
=
task_tgid_nr_ns(rcu_dereference(p
-
>real_parent), ns);
leader
=
p
-
>group_leader;
}
/
/
这个直接强制改tpid为
0
。
tpid
=
0
;
cred
=
get_task_cred(p);
...
}
Name: .kanxue.crackme
State: S (sleeping)
Tgid:
12142
Pid:
12142
PPid:
545
TracerPid:
0
Uid:
10027
10027
10027
10027
Gid:
10027
10027
10027
10027
Ngid:
0
FDSize:
128
Groups:
9997
20027
50027
VmPeak:
5657524
kB
VmSize:
5210040
kB
Name: .kanxue.crackme
State: S (sleeping)
Tgid:
12142
Pid:
12142
PPid:
545
TracerPid:
0
Uid:
10027
10027
10027
10027
Gid:
10027
10027
10027
10027
Ngid:
0
FDSize:
128
Groups:
9997
20027
50027
VmPeak:
5657524
kB
VmSize:
5210040
kB
static
int
proc_pid_wchan(struct seq_file
*
m, struct pid_namespace
*
ns,
struct pid
*
pid, struct task_struct
*
task)
{
unsigned
long
wchan;
char symname[KSYM_NAME_LEN];
wchan
=
get_wchan(task);
if
(lookup_symbol_name(wchan, symname) <
0
)
if
(!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
return
0
;
else
return
seq_printf(m,
"%lu"
, wchan);
else
if
(strstr(symname,
"trace"
)){
/
/
这里是新增的,如果符号名称包含trace,就固定改成sys_epoll_wait
return
seq_printf(m,
"%s"
,
"SyS_epoll_wait"
);
}
return
seq_printf(m,
"%s"
, symname);
}
static
int
proc_pid_wchan(struct seq_file
*
m, struct pid_namespace
*
ns,
struct pid
*
pid, struct task_struct
*
task)
{
unsigned
long
wchan;
char symname[KSYM_NAME_LEN];
wchan
=
get_wchan(task);
if
(lookup_symbol_name(wchan, symname) <
0
)
if
(!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
return
0
;
else
return
seq_printf(m,
"%lu"
, wchan);
else
if
(strstr(symname,
"trace"
)){
/
/
这里是新增的,如果符号名称包含trace,就固定改成sys_epoll_wait
return
seq_printf(m,
"%s"
,
"SyS_epoll_wait"
);
}
return
seq_printf(m,
"%s"
, symname);
}
extern uint32_t JniMethodStart(Thread
*
self
) {
JNIEnvExt
*
env
=
self
-
>GetJniEnv();
DCHECK(env !
=
nullptr);
uint32_t saved_local_ref_cookie
=
bit_cast<uint32_t>(env
-
>GetLocalRefCookie());
env
-
>SetLocalRefCookie(env
-
>GetLocalsSegmentState());
ArtMethod
*
native_method
=
*
self
-
>GetManagedStack()
-
>GetTopQuickFrame();
/
/
TODO: Introduce special entrypoint
for
synchronized @FastNative methods?
/
/
Or ban synchronized @FastNative outright to avoid the extra check here?
DCHECK(!native_method
-
>IsFastNative() || native_method
-
>IsSynchronized());
const char
*
methodname
=
native_method
-
>PrettyMethod().c_str();
if
(ArtMethod::GetDebugMethod()!
=
nullptr && strlen(ArtMethod::GetDebugMethod())>
0
){
if
(strstr(methodname,ArtMethod::GetDebugMethod())!
=
nullptr){
std::ostringstream oss1;
oss1<<
"fartext JniMethodStart methodname:"
<<methodname<<
" wait debug sleep 60..."
;
LOG(ERROR)<<oss1.
str
();
sleep(
60
);
}
}
if
(!native_method
-
>IsFastNative()) {
/
/
When
not
fast JNI we transition out of runnable.
self
-
>TransitionFromRunnableToSuspended(kNative);
}
return
saved_local_ref_cookie;
}
extern uint32_t JniMethodStart(Thread
*
self
) {
JNIEnvExt
*
env
=
self
-
>GetJniEnv();
DCHECK(env !
=
nullptr);
uint32_t saved_local_ref_cookie
=
bit_cast<uint32_t>(env
-
>GetLocalRefCookie());
env
-
>SetLocalRefCookie(env
-
>GetLocalsSegmentState());
ArtMethod
*
native_method
=
*
self
-
>GetManagedStack()
-
>GetTopQuickFrame();
/
/
TODO: Introduce special entrypoint
for
synchronized @FastNative methods?
/
/
Or ban synchronized @FastNative outright to avoid the extra check here?
DCHECK(!native_method
-
>IsFastNative() || native_method
-
>IsSynchronized());
const char
*
methodname
=
native_method
-
>PrettyMethod().c_str();
if
(ArtMethod::GetDebugMethod()!
=
nullptr && strlen(ArtMethod::GetDebugMethod())>
0
){
if
(strstr(methodname,ArtMethod::GetDebugMethod())!
=
nullptr){
std::ostringstream oss1;
oss1<<
"fartext JniMethodStart methodname:"
<<methodname<<
" wait debug sleep 60..."
;
LOG(ERROR)<<oss1.
str
();
sleep(
60
);
}
}
if
(!native_method
-
>IsFastNative()) {
/
/
When
not
fast JNI we transition out of runnable.
self
-
>TransitionFromRunnableToSuspended(kNative);
}
return
saved_local_ref_cookie;
}
void ShowVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
const char
*
jniMethod,
jmethodID mid,
va_list args){
if
(ArtMethod::IsJNIMethodPrint()){
ArtMethod
*
method
=
jni::DecodeArtMethod(mid);
uint32_t shorty_len
=
0
;
const char
*
shorty
=
method
-
>GetInterfaceMethodIfProxy(kRuntimePointerSize)
-
>GetShorty(&shorty_len);
JValue result;
ArgArray arg_array(shorty, shorty_len);
arg_array.VarArgsShowArg(soa, args,jniMethod,method
-
>PrettyMethod().c_str());
}
}
void ShowJValue(const ScopedObjectAccessAlreadyRunnable& soa,
const char
*
jniMethod,
jmethodID mid,
const jvalue
*
args){
if
(ArtMethod::IsJNIMethodPrint()){
ArtMethod
*
method
=
jni::DecodeArtMethod(mid);
uint32_t shorty_len
=
0
;
const char
*
shorty
=
method
-
>GetInterfaceMethodIfProxy(kRuntimePointerSize)
-
>GetShorty(&shorty_len);
JValue result;
ArgArray arg_array(shorty, shorty_len);
arg_array.JValuesShowArg(soa, args,jniMethod,method
-
>PrettyMethod().c_str());
}
}
void ShowJniField(const char
*
jniMethodName,jfieldID field) {
if
(ArtMethod::IsJNIMethodPrint()){
ArtField
*
f
=
jni::DecodeArtField(field);
std::ostringstream oss;
oss <<
"mikrom jni "
<<jniMethodName<<
"\t"
<<ArtField::PrettyField(f);
LOG(ERROR)<<oss.
str
();
}
}
void ShowArgStr(const char
*
jniMethod,const char
*
str1,const char
*
str2){
if
(ArtMethod::IsJNIMethodPrint()){
std::ostringstream oss;
if
(str1!
=
nullptr && str2!
=
nullptr){
oss <<
"mikrom jni "
<<jniMethod<<
"\t"
<<str1<<
"\t"
<<str2;
}
else
if
(str1!
=
nullptr ){
oss <<
"mikrom jni "
<<jniMethod<<
"\t"
<<str1;
}
else
{
oss <<
"mikrom jni "
<<jniMethod;
}
LOG(ERROR)<<oss.
str
();
}
}
void ShowVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
const char
*
jniMethod,
jmethodID mid,
va_list args){
if
(ArtMethod::IsJNIMethodPrint()){
ArtMethod
*
method
=
jni::DecodeArtMethod(mid);
uint32_t shorty_len
=
0
;
const char
*
shorty
=
method
-
>GetInterfaceMethodIfProxy(kRuntimePointerSize)
-
>GetShorty(&shorty_len);
JValue result;
ArgArray arg_array(shorty, shorty_len);
arg_array.VarArgsShowArg(soa, args,jniMethod,method
-
>PrettyMethod().c_str());
}
}
void ShowJValue(const ScopedObjectAccessAlreadyRunnable& soa,
const char
*
jniMethod,
jmethodID mid,
const jvalue
*
args){
if
(ArtMethod::IsJNIMethodPrint()){
ArtMethod
*
method
=
jni::DecodeArtMethod(mid);
uint32_t shorty_len
=
0
;
const char
*
shorty
=
method
-
>GetInterfaceMethodIfProxy(kRuntimePointerSize)
-
>GetShorty(&shorty_len);
JValue result;
ArgArray arg_array(shorty, shorty_len);
arg_array.JValuesShowArg(soa, args,jniMethod,method
-
>PrettyMethod().c_str());
}
}
void ShowJniField(const char
*
jniMethodName,jfieldID field) {
if
(ArtMethod::IsJNIMethodPrint()){
ArtField
*
f
=
jni::DecodeArtField(field);
std::ostringstream oss;
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)