当前位置: 首页 > news >正文

JAVA安全入门之反射

反射

对于反射这个概念来说,直白的讲就是:

对象可以通过反射获取他的类,类可以通过反射拿到所有⽅法(包括私有),拿到的⽅法可以调⽤

而众所周知 JAVA 是一门静态语言,我们通过反射就可以达到动态的语言的特性。
我们可以类比一下我们的老朋友 PHP ,对于 PHP 来说,一个简单的 webshell 可以完成各种操作,而对于 JAVA 来说,我们就可以用 反射 达到一些动态的特性。
比如以下代码:

public void execute(String className, String methodName) throws Exception {Class clazz = Class.forName(className);clazz.getMethod(methodName).invoke(clazz.newInstance());
}

以上代码,有几个反射中重要的方法:

  • 获取类的⽅法: forName
  • 实例化类对象的方法:newInstance
  • 获取函数的方法:getMethod
  • 执行函数的方法:invoke

但是 forName 并不是获取类的唯一方法,对于反射来说,还有两种方法来获取类:
1、如果已经有了一个类的实例,我们只需要用 obj.getClass() 来获取他的类。
2、如果你知道某个类的名字,想获取到这个类,就可以使⽤ forName 来获取。
当然除了以上反射的方法,还有一种方法:
如果你已经加载了某个类,只是想获取到它的 java.lang.Class 对象,那么就直接拿它的 class 属性即可。

如果我们看一下 forName 会发现他有两个重载方法:

public static Class<?> forName(String className)  throws ClassNotFoundException {  Class<?> caller = Reflection.getCallerClass();  return forName0(className, true, ClassLoader.getClassLoader(caller), caller);  
}
public static Class<?> forName(String name, boolean initialize,  ClassLoader loader)  throws ClassNotFoundException  
{  Class<?> caller = null;  SecurityManager sm = System.getSecurityManager();  if (sm != null) {  // Reflective call to get caller class is only needed if a security manager  // is present.  Avoid the overhead of making this call otherwise.        caller = Reflection.getCallerClass();  if (sun.misc.VM.isSystemDomainLoader(loader)) {  ClassLoader ccl = ClassLoader.getClassLoader(caller);  if (!sun.misc.VM.isSystemDomainLoader(ccl)) {  sm.checkPermission(  SecurityConstants.GET_CLASSLOADER_PERMISSION);  }  }  }  return forName0(name, initialize, loader, caller);  
}

对比着来看,会发现第一个比第二个少了个点东西:

public static Class<?> forName(String className)public static Class<?> forName(String name, boolean initialize,  ClassLoader loader)

对于第二种:

  • name : 类名
  • initialize : 是否初始化
  • ClassLoader : 类的加载器

ClassLoader 看到这个名字就能看出来,他就是一个类的 加载器
Java 默认的 ClassLoader 是根据类的名字(类的全称,例如 : java.lang.runtime)加载类。

反射的主要目的就是不通过 import 来导入类,这一点是攻击者想要的,而 forName 就可以做到这一点。

Java 中可以在类中编写内部类,在编译的时候会产生两个类文件如下:

public class main {  public static void main(String[] args) throws Exception {  System.out.println("Hello World!");  }  class test1{  public String test =  "hello";  }  
}

会生成如下文件:

├── main$test1.class
└── main.class

而你想要通过反射拿到 test1 类的时候可以通过 Class.forName("main$test1") 的方式来加载内部类,从而一系列操作。

说了这么多,我们举个小例子:

public static void main(String[] args) throws Exception {  Class c = Class.forName("java.lang.Runtime");  c.getMethod("exec", String.class).invoke(c.newInstance(), "open -a Calculator ");  
}

发现会报错对吧!

Exception in thread "main" java.lang.IllegalAccessException: Class main can not access a member of class java.lang.Runtime with modifiers "private"

这里很好理解,c.newInstance() 的作用就是调用这个类的无参构造函数所以存在以下情况就会失败:

  • 加载的类没有无参构造函数
  • 加载的类的构造函数是私有的
    我们可以看一下 newInstance() 的代码:
if (cachedConstructor == null) {  if (this == Class.class) {  throw new IllegalAccessException(  "Can not call newInstance() on the Class for java.lang.Class"  );  }

这里就很明确了 if (cachedConstructor == null) 如果没有构造函数就抛出一个异常。

// Security check (same as in java.lang.reflect.Constructor)
int modifiers = tmpConstructor.getModifiers();  
if (!Reflection.quickCheckMemberAccess(this, modifiers)) {  Class<?> caller = Reflection.getCallerClass();  if (newInstanceCallerCache != caller) {  Reflection.ensureMemberAccess(caller, this, null, modifiers);  newInstanceCallerCache = caller;  }  
}

跟进 ensureMemberAccess 方法:

public static void ensureMemberAccess(Class<?> var0, Class<?> var1, Object var2, int var3) throws IllegalAccessException {  if (var0 != null && var1 != null) {  if (!verifyMemberAccess(var0, var1, var2, var3)) {  throw new IllegalAccessException("Class " + var0.getName() + " can not access a member of class " + var1.getName() + " with modifiers \"" + Modifier.toString(var3) + "\"");  }  } else {  throw new InternalError();  }  
}

可以看到在这里抛出了异常,在此先不细说中间是怎么校验的。

所以我们可以这样来改造:

public static void main(String[] args) throws Exception {  Class c = Class.forName("java.lang.Runtime");  c.getMethod("exec", String.class).invoke(c.getMethod("getRuntime").invoke(c), "open -a Calculator");  
}

这里有两个方法需要说一下 getMethodinvokegetMethod 的作用是通过反射获取一个类的某个特定的公有方法,然后利用这个方式来获取 Runtime.exec 方法。

invoke 大家应该很熟悉,我们可以看看他的源码:

public Object invoke(Object obj, Object... args)  throws IllegalAccessException, IllegalArgumentException,  InvocationTargetException  
{  if (!override) {  if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {  Class<?> caller = Reflection.getCallerClass();  checkAccess(caller, clazz, obj, modifiers);  }  }  MethodAccessor ma = methodAccessor;             // read volatile  if (ma == null) {  ma = acquireMethodAccessor();  }  return ma.invoke(obj, args);  
}

这里做了一些操作,可以自己跟一下,这里就不过多叙述了。

invoke 的作用是执行方法,它的第一个参数是:

  • 如果这个方法是一个普通方法,那么第一个参数是类对象
  • 如果这个方法是一个静态方法,那么第一个参数是类
    正常执行方法是 [1].method([2], [3], [4]...),在反射里就是这样的 method.invoke([1], [2], [3], [4]...)

相关文章:

  • mfc140u.dll丢失怎么修复,mfc140u.dll文件有什么作用
  • Android开发知识学习——Kotlin进阶
  • 科学化决策数据分析,先从量化开始
  • .net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池
  • 【Linux】第六站:Centos系统如何安装软件?
  • GCC 编译器 详细总结
  • 刷题笔记day08-字符串01
  • 编译支持GPU的opencv,并供python的import cv2调用
  • 程序员想要网上接单却看花了眼?那这几个平台你可得收藏好了!
  • C++之初始化列表详细剖析
  • 【Algorithm】最容易理解的蒙特卡洛树搜索(Monte Carlo Tree Search,MCTS)算法
  • 华纳云:centos系统中怎么查看cpu信息?
  • 【智能座舱系列】- 深度解密小米Hyper OS,华为HarmonyOS区别
  • 【从0到1设计一个网关】过滤器链的实现---实现负载均衡过滤器
  • postgresql|数据库|SQL语句冲突的解决
  • classpath对获取配置文件的影响
  • CSS 三角实现
  • Docker: 容器互访的三种方式
  • Electron入门介绍
  • HashMap剖析之内部结构
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • JavaScript 事件——“事件类型”中“HTML5事件”的注意要点
  • java第三方包学习之lombok
  • Java面向对象及其三大特征
  • Netty源码解析1-Buffer
  • Redis字符串类型内部编码剖析
  • springboot_database项目介绍
  • Vue2.0 实现互斥
  • 从0到1:PostCSS 插件开发最佳实践
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 从零开始在ubuntu上搭建node开发环境
  • 后端_ThinkPHP5
  • 回顾 Swift 多平台移植进度 #2
  • 简单数学运算程序(不定期更新)
  • 使用parted解决大于2T的磁盘分区
  • 数组的操作
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 通信类
  • 一个项目push到多个远程Git仓库
  • Spring Batch JSON 支持
  • #微信小程序(布局、渲染层基础知识)
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • (2)nginx 安装、启停
  • (aiohttp-asyncio-FFmpeg-Docker-SRS)实现异步摄像头转码服务器
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (论文阅读11/100)Fast R-CNN
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • (四)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)
  • (图)IntelliTrace Tools 跟踪云端程序
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • .gitignore文件_Git:.gitignore
  • .jks文件(JAVA KeyStore)
  • .NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
  • .NET Core 实现 Redis 批量查询指定格式的Key