点击蓝字 关注我们
前言
RASP技术可以快速的将安全防御功能整合到正在运行的应用程序中,它拦截从应用程序到系统的所有调用,确保它们是安全的,并直接在应用程序内验证数据请求。而这其中的部分实现依赖的就是javaagent这一技术。下面来记录我在这其中的学习过程。
注:本文只用来作为知识记录和分享,切勿用于任何非法的事情上面。
参考:《深入理解JVM字节码》
https://github.com/hawkingfoo/demo-agent
Class文件结构
class文件由魔数、版本号、常量池、类访问标记、类索引、超类索引、接口索引、接口表索引、字段表、方法表和属性表这十个部分组成。
对于魔数和版本号这个字段,可以直接用十六进制编辑器直接打开就可以看到,而常量池则可以用javap -v xxxx.class这个命令方便的查看,还可以用这个命令查看到字节码指令。
Class文件采用类似C语言的结构体来存储数据。我们这里以Code属性作为例子来介绍展开。
它的结构如下:
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
这里的u1、u2、u4三种数据结构是java虚拟机里来表示1、2、4字节无符号整数。
attribute_name_index指向常量池中CONSTATNT_Utf8_info常量,表示属性的名字
attribute_length代表属性长度
max_stack代表操作数栈的最大深度
max_locals表示局部变量表的大小
code_length和code用来表示字节码相关的信息。code_length代表字节码的长度,code是一个数组,字节码就存放在这里
而之后的exception_table_length和exception_table就是存放当代码发生异常时的信息
attributes_count和attributes[]则放着属性相关的附属属性。
字节码指令
字节码指令分为加载和存储指令、操作数栈指令、运算和类型转换指令、控制转移指令和方法调用指令。
Hotspot JVM是一个基于栈的虚拟机,每个线程都有一个虚拟机栈用来存储栈帧,每次方法调用伴随着栈帧的创建、销毁。
加载和(load)和存储(store)分为load类、store类、常量加载三种。
load根据不同的数据变量类型还有lload、fload、dload、aload
store根据不同的数据变量类型也可以分为lstore、fstore、dstore、astore
常量加载指令常见的有const类、push类、ldc类。
常见的操作数栈指令有pop、dup和swap。
运算和类型转换指令在不区分数据变量类型的时候有:add、sub、div、mul、rem、neg、and、or、xor这几个,而需要注意的点是and、or和xor这三个指令,float和double类型是没有的。
类型转换指令总结一下就是各个数据类型的英文开头中间加上一个2。
而顺序是前面的类型转换为后面的类型。
条件转移指令:
ifeq、iflt、ifle、ifne、ifgt、ifge、ifnull、ifnonnull、if_icmpeq、if_icmpne、if_icmplt, if_icmpgt、if_icmple、if_icmpge、if_acmpeq和if_acmpne。
复合条件指令(在switch中会用到):tableswitch、lookupswitch
无条件转移:goto、goto_w、jsr、jsr_w、ret。
方法调用指令的特定都是以invoke开头:
invokestatic:调用静态方法
invokespecial:调用私有实例方法、构造器方法以及使用super关键字调用父类的实例方法等
invokevirtual:用于调用非私有实例方法
invokeinterface:用于调用接口方法
invokedynamic:用于调用动态方法
时间监控的实现
Instrumentation使得开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至可以替换和修改某些类的定义。
这里我们搭配javassist来进行实现一个函数运行时间的监控demo.
这里先放上我们的pom配置
这里利用的是javassist,这个操作的会比ASM要简单一些,因为它需要的字节码知识会少一点。
这里先设置一个premain入口,再接着instrument调用。
再利用javassist对函数进行修改,在函数开始前设置一个前置时间,之后运行再输出现在时间和运行之后的时间之差,达到监控的目的。
方法instrument() 可以用来搜索方法体里的内容。比如调用一个方法,field访问,对象创建等。而这里的格式即是在$_ = $proceed($$);前后插入想要修改的代码。
agent破解
这里用到的demo是深入理解JVM字节码的作者提供的一个jar,不过如果直接用书上的代码进行的话,会发现存在问题,需要进行一定的修改,接下来放出的都是修改后的代码
这里用jd-gui打开,可以查看到它的源代码
发现这里判断valid的依据是return的返回值,因此这里用agent直接把返回值进行一个修改
因为笔者水平有限,有写的不好的地方,还请读者多多担待。
原文始发于微信公众号(Th0r安全):RASP前置知识点:监控与软件攻防
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论