硬核java hook(直接改jdk)

背景:

        工作需要,需要修改一个java的程序逻辑,之前都是用的frida修改的,但是现在的工作场景,重视效率,所以frida这种重工具被pass了,只能重新选其他工具,初始的时候是想用java本身的一些修改工具的,上网发现了很多。比如cglib,javaassist,asm,byte buddy等;

java本身hook工具的限制:

        但是发现,这些都不符合我的应用场景,首先,我这个应用的jar包本身是加密的,需要加载的过程中在自定义的classloader中进行解密才能得到最终的dex文件,而这些工具大多都是通过调用默认的classloader来加载的,直接就会检测当前dex文件不符合文件格式而报错;

        有一些可以绕过的,又只能修改未加载的dex;

        后来redefineClass绕过了,发现他只能修改部分逻辑,而我要修改的变量是一个private的,redefineClass不能修改函数的签名,即不能增加一个方法来操作这个private变量;

        再后来我又绕过了,可以修改了,但是又发现我的判断条件位于上一层,而这个类的实例中所有变量都无法判断我要执行的修改逻辑。。。。

        越看问题越多,要修改的逻辑也越多;

        同时程序中本身会打印日志,我修改了字节码,那他的日志中打印的行数也会有问题。。。。感觉爆炸了,最后想到通过修改jdk来实现java逻辑的修改。

jdk修改:

        为了修改这个,大概了解了下一个实例在内存中的分布以及jdk的实现。

        首先想到jdk的类的实现,主要通过两个类来实现,一个是oopDesc,一个是Klass;我是这么理解的,Klass代表一个类的结构,oopDesc代表一个类的实例的结构;这样把实现和实例分离开来,多个类的实例oopDesc也只需要指向同一个Klass结构,这样可以大幅节省空间;

        我计划的实现hook修改某个实例的大致流程如下:

        1、在实例初始化的时候获取这个实例的地址

        2、通过实例指向的Klass结构,找到我想修改的这个变量或方法的偏移地址

        3、根据偏移,获取我想要的变量地址

        4、在需要修改的地方,修改之前获取的变量地址中的内存数据。

        下面一步步来解决:

        1、我想修改的是某个实例,那么就在某个实例开始的地方来hook他。实例初始化的流程大概是:

        ->加载一个类,也就是把dex解析为一个klass类,存储在内存中

        ->根据klass分配一个指定大小的内存空间

        ->初始化这个内存空间为一个Oop结构

        ->根据Klass的定义,初始化成员变量

        这里,我是在第二步获取的空间,反正实现就行,后续没跟了。我是用的openjdk17,修改了位于instanceKlass.cpp的allocate_instance函数,其中的oop返回之后,根据函数的入参获取当前的klass 的name,判断是否是自己需要的klass,来定位最终的oop的内存空间

        

        看到这里的判断逻辑,为什么我加了个长度判断,而不是直接精准匹配某个字符串,是因为这里jdk有个问题,我同样新建一个a.b.c.d的类,他这里klass的名字,有的时候是"a.b.c.d",有的时候"a.b.c.d ",tmd多了一个" ",搞了我好久!

        2、找偏移,硬核可以直接jdk里面找,我反正就该一个类,所以简单点,通过开启java的配置启动项PrintFieldLayout来设置打印的,可以得到如下的内容:

        

        这里就是Person在内存中的实例,打个比方,我要修改的是b变量,位于20偏移处,这里是10进制,不是16进制

        3、根据偏移地址找到数据。这里借助了工具jhsdb,我从官网下载的jdk里面编译出来的,都是不带这个工具的,正好还下载了graal jdk,他里面是携带的。

        jhsdb启动后注入进程,看看上面这个变量的内存分布:

       上面是实例的结构,我们想要的b,发现并不是oop的地址加上20,那这里是为什么呢?

        因为我想要获取的是一个AtomicInteger类的实例,而不是简单的int实例,存储的是指向相应的实例oop的指针,而非简单的一个int数据。

        再看看下面内存的16进制分布吧 ,20的16进制是0x14,我们偏移一加,得到是0x8adade24,这里我取了四字节,是因为这里jdk采用了一个叫压缩指针的方式来存储实际的oop数据,直接使用0x8adade24 * 8 = 0x456d6f120,就是我要去的实例地址,然后再取这个AtomicInteger的数据的偏移,即可得到实际我要改的那个b的地址;

        4 、在最终要修改的时候修改。这里就是我程序的逻辑了,各不一样了

 总结

        这个就是最终的流程了,优势就是不改java逻辑,java程序无法感知应该

        这个后来凉凉了,今天跑发现java对象在内存中地址还是不断变得呀,国庆正好再看看这个垃圾回收了。。看看能解决不

        

        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值