接到一个简单的需求, 客户自己的扫描工具把我们的代码,误认为是webshell。经过简单排查, 确认了误报 原因, 这个确认思路,也可以作为webshell的免杀思路。
前提条件是,源码类型的webshell,没有经过加密的。
而且代码被杀软识别成webshell
思路
- 截取文件部分代码,然后对文件进行扫描, 如果不报毒,则表示存在特征码使得代码被识别为webshell。
- 截取文件部分代码,然后对截取进行扫描, 如果报毒,则表示存在特征码使得代码被识别为webshell。
- 在代码中随机截取一定数量行的代码,生成样本, 对大量的样本进行扫描,再对有毒样本,进行思路1方法处理。
再对代码量相对比较小的样本,进行人工分析, 基本是可以完全找出所有的特征码关键字。
代码片段
之前排查问题写的一些代码片段。
#1 截取文件部分代码,然后对文件进行扫描
<?php
ini_set('display_errors', 'On');
/**
* 特征码查找
*/
class KeyFind {
public $config;
public $valider = null;
public $filepath = '';
public $file = null;
public function __construct($config)
{
$default_config = array(
'line' => 3,
'source_file' => '',
'source_dir' => dirname(__FILE__) . '/',
'dest_file' => dirname(__FILE__) . '/dest/file.php',
'log_file' => dirname(__FILE__) . '/log.txt'
);
$this->config = array_merge($default_config, $config);
}
//记录日志
public function log( $info ){
if (!file_exists($this->config['log_file'])) {
$myfile = fopen($this->config['log_file'], "w") or die("Unable to open file!");
$txt = "log\n";
fwrite($myfile, $txt);
fclose($myfile);
}
file_put_contents($this->config['log_file'], $info, FILE_APPEND);
}
public function _desturct() {
// if (!is_null($this->file)) {
// $this->file = null;
// }
}
public function valider()
{
$number = 0;
$command = "./webshellscanner -d dest";
exec($command, $output);
foreach( $output as $str) {
if ( preg_match('/发现webshell数量(.*?)(\d{1,})(.*?)$/', $str, $matches)) {
$number = $matches[2];
}
}
return $number;
}
public function fileCreate($array, $temp_str)
{
file_put_contents($this->config['dest_file'], implode("",$array));
}
/**
* 打开文件
*/
public function preCheck() {
$this->filepath = $this->config['source_dir'] . $this->config['source_file'];
if (empty($this->config['source_file'])
|| !file_exists($this->filepath)) {
throw new Exception('File ['.$this->filepath.'] not Exists');
}
// if (is_null($this->valider)) {
// throw new Exception('Valider not Set');
// }
//$this->filepath = $this->config['source_dir'] . $this->config['source_file'];
// $this->file = new SplFileObject($this->filepath, 'r');
}
public function readFile() {
$array = array();
$file = new SplFileObject($this->filepath, 'r');
while (!$file->eof()) {
$array[] = $file->current();
$file->next();
}
$file = null;
return $array;
}
public function search() {
$this->preCheck();
$problem = array();
$array = $this->readFile($this->filepath);
$line_count = count($array);
$line_deal = $this->config['line'];
$temp_str = '';
for ( $i=0; $i<$line_count; $i=$i+$line_deal) {
//置空
$temp_str = $array[$i];
$array[$i] = '';
for ( $j=1; $j<$line_deal; $j++) {
$temp_str = $temp_str . $array[$i+$j];
$array[$i+$j] = '';
}
//$temp_str = $array[$i];
$this->fileCreate($array, $temp_str);
if ( $this->valider($this->config['dest_file']) ) {
// 存在关键字
$this->log( $i . ' line exists key' . "\r\n");
} else {
$this->log( $i . ' line nonexists key' . "\r\n");
}
unlink($this->config['dest_file']);
if (file_exists($this->config['dest_file'])) {
die('file not delete');
}
//恢复数据
$temp_list = explode("\r\n", $temp_str);
$temp_str = '';
for ( $j=0; $j<$line_deal; $j++) {
if ($array[$i+$j] != '') {
echo 'error';
}
$array[$i+$j] = $temp_list[$j] . "\r\n";
}
$temp_list = array();
}
}
}
if ( $argc < 2 ) {
echo "\r\n Usage : scan.php file_need_scan";
exit();
}
$config = array(
'source_file' => $argv[1]
);
$search = new KeyFind($config);
$search->search();
#2 截取文件部分代码,然后对截取的代码进行扫描
<?php
ini_set('display_errors', 'On');
/**
* 特征码查找
*/
class KeyFind {
public $config;
public $valider = null;
public $filepath = '';
public $file = null;
public function __construct($config)
{
$default_config = array(
'line' => 100,
'source_file' => '',
'source_dir' => dirname(__FILE__) . '/',
'dest_file' => dirname(__FILE__) . '/dest/file.php',
'log_file' => dirname(__FILE__) . '/log.txt',
'result_dir' => dirname(__FILE__) . '/result/'
);
$this->config = array_merge($default_config, $config);
}
//记录日志
public function log( $info ){
if (!file_exists($this->config['log_file'])) {
$myfile = fopen($this->config['log_file'], "w") or die("Unable to open file!");
$txt = "log\n";
fwrite($myfile, $txt);
fclose($myfile);
}
file_put_contents($this->config['log_file'], $info, FILE_APPEND);
}
public function _desturct() {
// if (!is_null($this->file)) {
// $this->file = null;
// }
}
public function valider()
{
$number = 0;
$command = "./webshellscanner -d dest";
exec($command, $output);
foreach( $output as $str) {
if ( preg_match('/发现webshell数量(.*?)(\d{1,})(.*?)$/', $str, $matches)) {
$number = $matches[2];
}
}
return $number;
}
public function fileCreate($array, $temp_str)
{
if ($dest_file == '') {
$dest_file = $this->config['dest_file'];
}
file_put_contents($dest_file, "<?php", FILE_APPEND);
file_put_contents($dest_file, $temp_str, FILE_APPEND);
}
/**
* 打开文件
*/
public function preCheck() {
$this->filepath = $this->config['source_dir'] . $this->config['source_file'];
if (empty($this->config['source_file'])
|| !file_exists($this->filepath)) {
throw new Exception('File ['.$this->filepath.'] not Exists');
}
// if (is_null($this->valider)) {
// throw new Exception('Valider not Set');
// }
//$this->filepath = $this->config['source_dir'] . $this->config['source_file'];
// $this->file = new SplFileObject($this->filepath, 'r');
}
public function readFile() {
$array = array();
$file = new SplFileObject($this->filepath, 'r');
while (!$file->eof()) {
$array[] = $file->current();
$file->next();
}
$file = null;
return $array;
}
public function search() {
$this->preCheck();
$problem = array();
$array = $this->readFile($this->filepath);
$line_count = count($array);
$line_deal = $this->config['line'];
$temp_str = '';
for ( $i=0; $i<$line_count; $i=$i+$line_deal) {
//$temp_str = $array[$i];
$this->fileCreate($array, $temp_str);
if ( $this->valider($this->config['dest_file']) ) {
// 存在关键字
$this->log( $i . ' line exists key' . "\r\n");
echo "发现异常代码串" . "\r\n";
$this->fileCreate($array, $temp_str, $this->config['result_dir'] . rand(1000,9999) . time() . '.php');
} else {
$this->log( $i . ' line nonexists key' . "\r\n");
}
unlink($this->config['dest_file']);
if (file_exists($this->config['dest_file'])) {
die('file not delete');
}
}
}
}
if ( $argc < 2 ) {
echo "\r\n Usage : scan.php file_need_scan";
exit();
}
$config = array(
'source_file' => $argv[1]
);
$search = new KeyFind($config);
$search->search();
#3 生成随机样本
<?php
ini_set('display_errors', 'On');
/**
* 特征码查找
*/
class KeyFind {
public $config;
public $valider = null;
public $filepath = '';
public $file = null;
public function __construct($config)
{
$default_config = array(
'line' => 100,
'source_file' => '',
'source_dir' => dirname(__FILE__) . '/',
'dest_file' => dirname(__FILE__) . '/dest/file.php',
'log_file' => dirname(__FILE__) . '/log.txt',
'result_dir' => dirname(__FILE__) . '/result/'
);
$this->config = array_merge($default_config, $config);
}
//记录日志
public function log( $info ){
if (!file_exists($this->config['log_file'])) {
$myfile = fopen($this->config['log_file'], "w") or die("Unable to open file!");
$txt = "log\n";
fwrite($myfile, $txt);
fclose($myfile);
}
file_put_contents($this->config['log_file'], $info, FILE_APPEND);
}
public function _desturct() {
// if (!is_null($this->file)) {
// $this->file = null;
// }
}
public function valider()
{
$number = 0;
$command = "./webshellscanner -d dest";
exec($command, $output);
foreach( $output as $str) {
if ( preg_match('/发现webshell数量(.*?)(\d{1,})(.*?)$/', $str, $matches)) {
$number = $matches[2];
}
}
return $number;
}
public function fileCreate($array, $temp_str)
{
if ($dest_file == '') {
$dest_file = $this->config['dest_file'];
}
file_put_contents($dest_file, "<?php", FILE_APPEND);
file_put_contents($dest_file, $temp_str, FILE_APPEND);
}
/**
* 打开文件
*/
public function preCheck() {
$this->filepath = $this->config['source_dir'] . $this->config['source_file'];
if (empty($this->config['source_file'])
|| !file_exists($this->filepath)) {
throw new Exception('File ['.$this->filepath.'] not Exists');
}
// if (is_null($this->valider)) {
// throw new Exception('Valider not Set');
// }
//$this->filepath = $this->config['source_dir'] . $this->config['source_file'];
// $this->file = new SplFileObject($this->filepath, 'r');
}
public function readFile() {
$array = array();
$file = new SplFileObject($this->filepath, 'r');
while (!$file->eof()) {
$array[] = $file->current();
$file->next();
}
$file = null;
return $array;
}
/**
* 生成随机行数
*
* @param [type] $line_count
* @param [type] $line_deal
* @return void
*/
public function getRandLine($line_count, $line_deal)
{
if ( $line_count <= $line_deal) {
die('fatel error!!!!');
}
$line_array = array(); //保存行数
// 生成行数
for ( $i=0; $i<$line_deal; $i++) {
$rand_line = rand(1,$line_count-1); //不需要第0行 <?php
while( in_array($rand_line, $line_array) ) {
$rand_line = rand(1, $line_count-1);
}
$line_array[]= $rand_line;
}
return $line_array;
}
/**
* 文件创建
*
* @param [type] $array
* @param [type] $line_array
* @return void
*/
public function createSample($array, $line_array)
{
$dir = $this->config['result_dir'];
$lines = join(',', $line_array);
$file = $dir . md5($lines) . '.php';
file_put_contents($file, '<?php');
file_put_contents($file, "//$lines", FILE_APPEND);
$temp_str = '';
foreach( $line_array as $line) {
$temp_str .= $array[$line];
}
file_put_contents($file, $temp_str, FILE_APPEND);
}
public function search() {
$this->preCheck();
$problem = array();
$array = $this->readFile($this->filepath);
$line_count = count($array)-1;
$line_deal = $this->config['line'];
$temp_str = '';
$line_array = $this->getRandLine($line_count, $line_deal); //保存行数
$this->createSample($array, $line_array);
for ( $i=0; $i<10000;$i++) {
$line_array = array();
$line_array = $this->getRandLine($line_count, $line_deal); //保存行数
$this->createSample($array, $line_array);
}
}
}
if ( $argc < 2 ) {
echo "\r\n Usage : scan.php file_need_scan";
exit();
}
$config = array(
'source_file' => $argv[1]
);
$search = new KeyFind($config);
$search->search();
实现了简单的脚本辅助手工确认特征码, 根据这个思路,应该是可以做到自动化的
网友评论