Java反射(Reflection)是Java非常重要的动态特性,通过使用反射我们不仅可以获取到任何类的成员方法(Methods)、成员变量(Fields)、构造方法(Constructors)等信息,还可以动态创建Java类实例、调用任意的类方法、修改任意的类成员变量值等。Java反射机制是Java语言的动态性的重要体现,也是Java的各种框架底层实现的灵魂。
反射机制的相关类在哪个包下?
反射机制相关的重要的类有哪些?
类
含义
a.lang.Class
代表整个字节码。代表一个类型,代表整个类。
java.lang.reflect.Method
代表字节码中的方法字节码。代表类中的方法。
java.lang.reflect.Constructor
代表字节码中的构造方法字节码。代表类中的构造方法。
java.lang.reflect.Field
代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。
注:必须先获得Class才能获取Method、Constructor、Field。
1获取Class对象 Java反射操作的是java.lang.Class对象,所以我们需要先想办法获取到Class对象,通常我们有如下几种方式获取一个类的Class对象:
类名.class,如:com.anbai.sec.classloader.TestHelloWorld.class。
Class.forName(“com.anbai.sec.classloader.TestHelloWorld”)。
classLoader.loadClass(“com.anbai.sec.classloader.TestHelloWorld”);
通过反射实例化对象
注 :newInstance()方法内部实际上调用了无参数构造方法 ,必须保证无参构造存在才可以。 否则会抛出java.lang.InstantiationException异常。
1 2 3 4 5 6 7 8 9 10 11 12 class ReflectTest02 { public static void main (String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class c = Class.forName("javase.reflectBean.User" ); Object obj = c.newInstance(); System.out.println(obj); } }
如果你只是希望一个类的静态代码块 执行,其它代码一律不执行,可以使用:
这个方法的执行会导致类加载 ,类加载时,静态代码块执行。
2反射Filed【反射/反编译一个类的属性】 2.1Class类方法
2.2Field类方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class ReflectTest06 { public static void main (String[] args) throws ClassNotFoundException { StringBuilder s = new StringBuilder (); Class studentClass = Class.forName("javase.reflectBean.Student" ); s.append(Modifier.toString(studentClass.getModifiers()) + " class " + studentClass.getSimpleName() + " {\n" ); Field[] fields = studentClass.getDeclaredFields(); for (Field f : fields){ s.append("\t" ); s.append(Modifier.toString(f.getModifiers())); if (f.getModifiers() != 0 ) s.append(" " ); s.append(f.getType().getSimpleName()); s.append(" " ); s.append(f.getName()); s.append(";\n" ); } s.append("}" ); System.out.println(s); } }
2.3通过反射机制访问一个java对象的属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class ReflectTest07 { public static void main (String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException { Student student = new Student (); student.no = 1111 ; System.out.println(student.no); Class studentClass = Class.forName("javase.reflectBean.Student" ); Object obj = studentClass.newInstance(); Field noField = studentClass.getDeclaredField("no" ); noField.set(obj, 22222 ); System.out.println(noField.get(obj)); }
2.3.1set()可以访问私有属性嘛? 不可以,需要打破封装,才可以。
1 2 3 4 5 6 7 8 9 10 Field nameField = studentClass.getDeclaredField("name" );nameField.setAccessible(true ); nameField.set(obj, "xiaowu" ); System.out.println(nameField.get(obj));
3.反射Method【反射/反编译一个类的方法】 method类方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class ReflectTest09 { public static void main (String[] args) throws ClassNotFoundException { StringBuilder s = new StringBuilder (); Class userServiceClass = Class.forName("java.lang.String" ); s.append(Modifier.toString(userServiceClass.getModifiers())); s.append(" class " ); s.append(userServiceClass.getSimpleName()); s.append(" {\n" ); Method[] methods = userServiceClass.getDeclaredMethods(); for (Method m : methods){ s.append("\t" ); s.append(Modifier.toString(m.getModifiers())); s.append(" " ); s.append(m.getReturnType().getSimpleName()); s.append(" " ); s.append(m.getName()); s.append("(" ); Class[] parameterTypes = m.getParameterTypes(); for (int i = 0 ; i < parameterTypes.length; i++){ s.append(parameterTypes[i].getSimpleName()); if (i != parameterTypes.length - 1 ) s.append(", " ); } s.append(") {}\n" ); } s.append("}" ); System.out.println(s); } }
3.1.1、调用方法四要素 调用对象userService的login方法
要素1:对象 userService
要素2:login 方法名
要素3:实参列表
要素4:返回值
注: Method类中invoke()使用注意事项:
通过反射机制调用一个对象的方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class ReflectTest10 { public static void main (String[] args) throws Exception { UserService userService = new UserService (); System.out.println(userService.login("admin" , "123" ) ? "登入成功!" : "登入失败!" ); Class userServiceClass = Class.forName("javase.reflectBean.UserService" ); Object obj = userServiceClass.newInstance(); Method loginMethod = userServiceClass.getDeclaredMethod("login" , String.class, String.class); Object resValues = loginMethod.invoke(obj, "admin" , "123" ); System.out.println(resValues); } }
4反射Constructor【反射/反编译一个类的构造方法】
反编译一个类的构造方法Constructor 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class ReflectTest11 { public static void main (String[] args) throws ClassNotFoundException { StringBuilder s = new StringBuilder (); Class vipClass = Class.forName("javase.reflectBean.Vip" ); s.append(Modifier.toString(vipClass.getModifiers())); s.append(" class " ); s.append(vipClass.getSimpleName()); s.append("{\n" ); Constructor[] constructors = vipClass.getDeclaredConstructors(); for (Constructor c : constructors){ s.append("\t" ); s.append(Modifier.toString(c.getModifiers())); s.append(" " ); s.append(vipClass.getSimpleName()); s.append("(" ); Class[] parameterTypes = c.getParameterTypes(); for (int i = 0 ; i < parameterTypes.length; i++){ s.append(parameterTypes[i].getSimpleName()); if (i != parameterTypes.length - 1 ) s.append(", " ); } s.append("){}\n" ); } s.append("}" ); System.out.println(s); } }
4.1反射机制创建对象两步骤 1.先获取到这个有参数的构造方法【用ClassgetDeclaredConstructor()方法获取】
2.调用构造方法new对象【用Constructor类的newInstance()方法new对象】
4.1.1通过反射机制调用构造方法实例化java对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class ReflectTest12 { public static void main (String[] args) throws Exception { Vip vip1 = new Vip (); Vip vip2 = new Vip (123 , "zhangsan" , "2001-10-19" , false ); Class vipClass = Class.forName("javase.reflectBean.Vip" ); Object obj1 = vipClass.newInstance(); System.out.println(obj1); Constructor c1 = vipClass.getDeclaredConstructor(int .class, String.class, String.class, boolean .class); Object obj2 = c1.newInstance(321 , "lsi" , "1999-10-11" , true ); System.out.println(obj2); Constructor c2 = vipClass.getDeclaredConstructor(); Object obj3 = c2.newInstance(); System.out.println(obj3); } }
5.获取一个类的父类以及实现的接口 两个方法【Class类中的】
public native Class<? super T> getSuperclass ()
public Class<?>[] getInterfaces ()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class ReflectTest13 { public static void main (String[] args) throws Exception{ Class vipClass = Class.forName("java.lang.String" ); Class superclass = vipClass.getSuperclass(); Class[] interfaces = vipClass.getInterfaces(); System.out.println(superclass.getName()); for (Class i : interfaces) { System.out.println(i.getName()); } } }
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 package javase;import javase.reflectBean.Student;import javase.reflectBean.UserService;import javase.reflectBean.Vip;import java.io.*;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Modifier;import java.util.Date;import java.util.Properties;import java.util.ResourceBundle;class ReflectTest01 { public static void main (String[] args) throws ClassNotFoundException{ Class c1 = Class.forName("java.lang.String" ); Class c2 = Class.forName("java.lang.Integer" ); Class c3 = Class.forName("java.util.Date" ); String a = "abc" ; Class c4 = a.getClass(); System.out.println(c4 == c1); Date time = new Date (); Class c5 = time.getClass(); System.out.println(c5 == c3); Class i = Integer.class; Class d = Date.class; Class f = float .class; System.out.println(i == c2); } } class ReflectTest02 { public static void main (String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class c = Class.forName("javase.reflectBean.User" ); Object obj = c.newInstance(); System.out.println(obj); } } class ReflectTest03 { public static void main (String[] args) throws Exception{ FileReader reader = new FileReader ("Practice/reflectClassInfo1.properties" ); Properties pro = new Properties (); pro.load(reader); reader.close(); String className = pro.getProperty("className" ); Class c = Class.forName(className); Object obj = c.newInstance(); System.out.println(obj); } } class ReflectTest04 { public static void main (String[] args) throws ClassNotFoundException { Class.forName("javase.MyClass" ); } } class MyClass { static { System.out.println("MyClass中的静态代码块执行了!" ); } } class AboutPath { public static void main (String[] args) throws FileNotFoundException { File reader = new File ("Practice/src/reflectClassInfo2.properties" ); System.out.println(reader.exists() + " " + reader.getPath()); String path = Thread.currentThread().getContextClassLoader().getResource("reflectClassInfo2.properties" ).getPath(); System.out.println(path); String path2 = Thread.currentThread().getContextClassLoader().getResource("javase/reflectBean/db.properties" ).getPath(); System.out.println(path2); } } class IoPropertiesTest { public static void main (String[] args) throws IOException { InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("reflectClassInfo2.properties" ); Properties pro = new Properties (); pro.load(reader); reader.close(); String className = pro.getProperty("className" ); System.out.println(className); } } class ResourceBundleTest { public static void main (String[] args) { ResourceBundle bundle = ResourceBundle.getBundle("javase/reflectBean/db" ); String className = bundle.getString("className" ); System.out.println(className); } } class ReflectTest05 { public static void main (String[] args) throws Exception { Class studentClass = Class.forName("javase.reflectBean.Student" ); String className = studentClass.getName(); System.out.println("完整类名: " + className); String simpleName = studentClass.getSimpleName(); System.out.println("简类名: " + simpleName); Field[] fields1 = studentClass.getFields(); System.out.println(fields1.length); System.out.println(fields1[0 ].getName() + " " + fields1[1 ].getName()); System.out.println("----------------------" ); Field[] fields2 = studentClass.getDeclaredFields(); System.out.println(fields2.length); for (Field f : fields2){ System.out.println(f.getName()); } System.out.println("----------------------" ); for (Field f : fields2){ System.out.println(Modifier.toString(f.getModifiers())); System.out.println(f.getType().getSimpleName()); System.out.println(f.getName()); } } } class ReflectTest06 { public static void main (String[] args) throws ClassNotFoundException { StringBuilder s = new StringBuilder (); Class studentClass = Class.forName("javase.reflectBean.Student" ); s.append(Modifier.toString(studentClass.getModifiers()) + " class " + studentClass.getSimpleName() + " {\n" ); Field[] fields = studentClass.getDeclaredFields(); for (Field f : fields){ s.append("\t" ); s.append(Modifier.toString(f.getModifiers())); if (f.getModifiers() != 0 ) s.append(" " ); s.append(f.getType().getSimpleName()); s.append(" " ); s.append(f.getName()); s.append(";\n" ); } s.append("}" ); System.out.println(s); } } class ReflectTest07 { public static void main (String[] args) throws Exception { Student student = new Student (); student.no = 1111 ; System.out.println(student.no); Class studentClass = Class.forName("javase.reflectBean.Student" ); Object obj = studentClass.newInstance(); Field noField = studentClass.getDeclaredField("no" ); noField.set(obj, 22222 ); System.out.println(noField.get(obj)); Field nameField = studentClass.getDeclaredField("name" ); nameField.setAccessible(true ); nameField.set(obj, "xiaowu" ); System.out.println(nameField.get(obj)); } } class ReflectTest08 { public static void main (String[] args) throws ClassNotFoundException { Class userServiceClass = Class.forName("javase.reflectBean.UserService" ); Method[] methods = userServiceClass.getDeclaredMethods(); for (Method m : methods){ System.out.println(Modifier.toString(m.getModifiers())); System.out.println(m.getReturnType().getSimpleName()); System.out.println(m.getName()); Class[] parameterTypes = m.getParameterTypes(); for (Class pts : parameterTypes){ System.out.println(pts.getSimpleName()); } System.out.println("------------------------" ); } } } class ReflectTest09 { public static void main (String[] args) throws ClassNotFoundException { StringBuilder s = new StringBuilder (); Class userServiceClass = Class.forName("java.lang.String" ); s.append(Modifier.toString(userServiceClass.getModifiers())); s.append(" class " ); s.append(userServiceClass.getSimpleName()); s.append(" {\n" ); Method[] methods = userServiceClass.getDeclaredMethods(); for (Method m : methods){ s.append("\t" ); s.append(Modifier.toString(m.getModifiers())); s.append(" " ); s.append(m.getReturnType().getSimpleName()); s.append(" " ); s.append(m.getName()); s.append("(" ); Class[] parameterTypes = m.getParameterTypes(); for (int i = 0 ; i < parameterTypes.length; i++){ s.append(parameterTypes[i].getSimpleName()); if (i != parameterTypes.length - 1 ) s.append(", " ); } s.append(") {}\n" ); } s.append("}" ); System.out.println(s); } } class ReflectTest10 { public static void main (String[] args) throws Exception { UserService userService = new UserService (); System.out.println(userService.login("admin" , "123" ) ? "登入成功!" : "登入失败!" ); Class userServiceClass = Class.forName("javase.reflectBean.UserService" ); Object obj = userServiceClass.newInstance(); Method loginMethod = userServiceClass.getDeclaredMethod("login" , String.class, String.class); Object resValues = loginMethod.invoke(obj, "admin" , "123" ); System.out.println(resValues); } } class ReflectTest11 { public static void main (String[] args) throws ClassNotFoundException { StringBuilder s = new StringBuilder (); Class vipClass = Class.forName("javase.reflectBean.Vip" ); s.append(Modifier.toString(vipClass.getModifiers())); s.append(" class " ); s.append(vipClass.getSimpleName()); s.append("{\n" ); Constructor[] constructors = vipClass.getDeclaredConstructors(); for (Constructor c : constructors){ s.append("\t" ); s.append(Modifier.toString(c.getModifiers())); s.append(" " ); s.append(vipClass.getSimpleName()); s.append("(" ); Class[] parameterTypes = c.getParameterTypes(); for (int i = 0 ; i < parameterTypes.length; i++){ s.append(parameterTypes[i].getSimpleName()); if (i != parameterTypes.length - 1 ) s.append(", " ); } s.append("){}\n" ); } s.append("}" ); System.out.println(s); } } class ReflectTest12 { public static void main (String[] args) throws Exception { Vip vip1 = new Vip (); Vip vip2 = new Vip (123 , "zhangsan" , "2001-10-19" , false ); Class vipClass = Class.forName("javase.reflectBean.Vip" ); Object obj1 = vipClass.newInstance(); System.out.println(obj1); Constructor c1 = vipClass.getDeclaredConstructor(int .class, String.class, String.class, boolean .class); Object obj2 = c1.newInstance(321 , "lsi" , "1999-10-11" , true ); System.out.println(obj2); Constructor c2 = vipClass.getDeclaredConstructor(); Object obj3 = c2.newInstance(); System.out.println(obj3); } } class ReflectTest13 { public static void main (String[] args) throws Exception{ Class vipClass = Class.forName("java.lang.String" ); Class superclass = vipClass.getSuperclass(); Class[] interfaces = vipClass.getInterfaces(); System.out.println(superclass.getName()); for (Class i : interfaces) { System.out.println(i.getName()); } } }
参考:https://blog.csdn.net/qq_44715943/article/details/120587716
6.nenstance的使用 class.newInstance() 的作用就是调用这个类的无参构造函数,这个比较好理解。不过,我们有时候 在写漏洞利用方法的时候,会发现使用 newInstance 总是不成功,这时候原因可能是:
你使用的类没有无参构造函数
你使用的类构造函数是私有的 最最最常见的情况就是 java.lang.Runtime ,这个类在我们构造命令执行Payload的时候很常见,但 我们不能直接这样来执行命令:
1 2 Class clazz = Class.forName("java.lang.Runtime" );clazz.getMethod("exec" , String.class).invoke(clazz.newInstance(), "id" );
报错
原因是 Runtime 类的构造方法是私有的。 有同学就比较好奇,为什么会有类的构造方法是私有的,难道他不想让用户使用这个类吗?这其实涉及 到很常见的设计模式:“单例模式”。(有时候工厂模式也会写成类似) 比如,对于Web应用来说,数据库连接只需要建立一次,而不是每次用到数据库的时候再新建立一个连 接,此时作为开发者你就可以将数据库连接使用的类的构造函数设置为私有,然后编写一个静态方法来 获取:
1 2 3 4 5 6 7 8 9 10 public class TrainDB {private static TrainDB instance = new TrainDB ();public static TrainDB getInstance () {return instance;} private TrainDB () {} }
这样,只有类初始化的时候会执行一次构造函数,后面只能通过 getInstance 获取这个对象,避免建 立多个数据库连接。 回到正题,Runtime类就是单例模式,我们只能通过 Runtime.getRuntime() 来获取到 Runtime 对 象。我们将上述Payload进行修改即可正常执行命令了:
1 2 3 4 5 Class clazz = Class.forName("java.lang.Runtime" );clazz.getMethod("exec" , String.class).invoke(clazz.getMethod("getRuntime" ).invoke(clazz), "calc.exe" );
这里用到了 getMethod 和 invoke 方法。 getMethod 的作用是通过反射获取一个类的某个特定的公有方法。而学过Java的同学应该清楚,Java中 支持类的重载,我们不能仅通过函数名来确定一个函数。所以,在调用 getMethod 的时候,我们需要 传给他你需要获取的函数的参数类型列表。 比如这里的 Runtime.exec 方法有6个重载:
我们使用最简单的,也就是第一个,它只有一个参数,类型是String,所以我们使用 getMethod(“exec”, String.class) 来获取 Runtime.exec 方法。 invoke 的作用是执行方法,它的第一个参数是: 如果这个方法是一个普通方法,那么第一个参数是类对象 如果这个方法是一个静态方法,那么第一个参数是类 这也比较好理解了,我们正常执行方法是 [1].method([2], [3], [4]…) ,其实在反射里就是 method.invoke([1], [2], [3], [4]…) 。 所以我们将上述命令执行的Payload分解一下就是:
1 2 3 4 5 Class clazz = Class.forName("java.lang.Runtime" );Method execMethod = clazz.getMethod("exec" , String.class);Method getRuntimeMethod = clazz.getMethod("getRuntime" );Object runtime = getRuntimeMethod.invoke(clazz);execMethod.invoke(runtime, "calc.exe" );
7.反射的进阶 如果一个类没有无参构造方法,也没有类似单例模式里的静态方法,我们怎样通过反射实例化该类 呢? 如果一个方法或构造方法是私有方法,我们是否能执行它呢?
第一个问题,我们需要用到一个新的反射方法 getConstructor 。 和 getMethod 类似, getConstructor 接收的参数是构造函数列表类型,因为构造函数也支持重载, 所以必须用参数列表类型才能唯一确定一个构造函数。 获取到构造函数后,我们使用 newInstance 来执行。 比如,我们常用的另一种执行命令的方式ProcessBuilder,我们使用反射来获取其构造函数,然后调用 start() 来执行命令:
1 2 Class clazz = Class.forName("java.lang.ProcessBuilder" );((ProcessBuilder)clazz.getConstructor(List.class).newInstance(Arrays.asList("calc.exe" ))).start();
ProcessBuilder有两个构造函数:
public ProcessBuilder(List command)
public ProcessBuilder(String… command)
我上面用到了第一个形式的构造函数,所以我在 getConstructor 的时候传入的是 List.class 。 但是,我们看到,前面这个Payload用到了Java里的强制类型转换,有时候我们利用漏洞的时候(在表达式上下文中)是没有这种语法的。所以,我们仍需利用反射来完成这一步。
1 2 3 Class clazz = Class.forName("java.lang.ProcessBuilder" );clazz.getMethod("start" ).invoke(clazz.getConstructor(List.class).newInstance( Arrays.asList("calc.exe" )));
通过 getMethod(“start”) 获取到start方法,然后 invoke 执行, invoke 的第一个参数就是 ProcessBuilder Object了。 那么,如果我们要使用 public ProcessBuilder(String… command) 这个构造函数,需要怎样用反 射执行呢? 这又涉及到Java里的可变长参数(varargs)了。正如其他语言一样,Java也支持可变长参数,就是当你 定义函数的时候不确定参数数量的时候,可以使用 … 这样的语法来表示“这个函数的参数个数是可变 的”。 对于可变长参数,Java其实在编译的时候会编译成一个数组,也就是说,如下这两种写法在底层是等价 的(也就不能重载):
1 2 3 public void hello (String[] names) {}public void hello (String...names) {}
也由此,如果我们有一个数组,想传给hello函数,只需直接传即可:
1 2 3 String[] names = {"hello" , "world" }; hello(names);
那么对于反射来说,如果要获取的目标函数里包含可变长参数,其实我们认为它是数组就行了。 所以,我们将字符串数组的类 String[].class 传给 getConstructor ,获取 ProcessBuilder 的第二 种构造函数:
1 2 Class clazz = Class.forName("java.lang.ProcessBuilder" );clazz.getConstructor(String[].class)
在调用 newInstance 的时候,因为这个函数本身接收的是一个可变长参数,我们传给 ProcessBuilder 的也是一个可变长参数,二者叠加为一个二维数组,所以整个Payload如下:
1 2 3 4 Class clazz = Class.forName("java.lang.ProcessBuilder" );((ProcessBuilder)clazz.getConstructor(String[].class).newInstance(new String [][]{{"calc.exe" }})).start();
再说到今天第二个问题,如果一个方法或构造方法是私有方法,我们是否能执行它呢?
这就涉及到 getDeclared 系列的反射了,与普通的 getMethod 、 getConstructor 区别是:
getMethod 系列方法获取的是当前类中所有公共方法,包括从父类继承的方法
getDeclaredMethod 系列方法获取的是当前类中“声明”的方法,是实在写在这个类里的,包括私 有的方法,但从父类里继承来的就不包含了
getDeclaredMethod 的具体用法和 getMethod 类似, getDeclaredConstructor 的具体用法和 getConstructor 类似,我就不再赘述。 举个例子,前文我们说过Runtime这个类的构造函数是私有的,我们需要用 Runtime.getRuntime() 来 获取对象。其实现在我们也可以直接用 getDeclaredConstructor 来获取这个私有的构造方法来实例 化对象,进而执行命令:
1 2 3 4 5 Class clazz = Class.forName("java.lang.Runtime" );Constructor m = clazz.getDeclaredConstructor();m.setAccessible(true ); clazz.getMethod("exec" , String.class).invoke(m.newInstance(), "calc.exe" );
可见,这里使用了一个方法 setAccessible ,这个是必须的。我们在获取到一个私有方法后,必须用 setAccessible 修改它的作用域,否则仍然不能调用。