直接进入正题。
在完成正式的shellcode代码导出之前,我们先手动的对代码进行导出,以使各位同学更加了解其原理。
手动注入shellcode
1、我们利用DLE工具找到上一节中我们生成的PE文件的代码段位置
上述图片就是我们的代码段位置
2、利用WinHex工具我们将这一段代码复制出来
通过以上步骤我们就已经将shellcode的代码复制到我们的剪切板了,当我们复制的代码注入到任何进程的时候它就会出现一个弹窗。
3、随便写一个可执行程序,然后将我们复制的代码注入进去,然后我们来看一下效果
#pragma comment(linker,"/entry:ShellCodeEntry")int ShellCodeEntry()
{return 0;
}
上边是一个简单的代码,什么也不做。
然后我们来看一下上一节我们写的shellcode生成的PE文件效果
是一个简单的弹窗。
使用x64dbg工具打开上述写的简单程序
首先,先看一下没有注入的情况:
可以看到,什么也没有发生,因为我们什么也没做,啥也没有是理所当然的了。
接下来,我们将我们的shellcode代码注入进去:
可以看到,当我们的shellcode注入进去后,它执行的就是我们写的shellcode代码。
现在我们手动对shellcode代码段复制并随便注入到一个进程的过程已经全部结束。当然,实际应用中我们当然不可能手动复制这段shellcode的代码段了,所以接下来就是重点了,我们可以依赖程序的自动提取,把shellcode的代码段提取出来,然后直接全选就可以了。重点!重点!重点!重要的事说三遍。
自动提取程序中的shellcode代码
首先,我们需要搞清楚一个问题,我们写的函数是如何保存到生成的PE文件中的,我们可以使用IDA看一下。实际上它就是以cpp文件的从上到下函数地址保存的,所以要获取我们的shellcode代码,我们就可以这么写:
#include <Windows.h>//定义函数入口点
#pragma comment(linker,"/entry:ShellCodeEntry")int ShellCodeEntry()
{HANDLE hFile = CreateFile(L"shellcode.bin", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);//shellcode代码段长度DWORD dwShellcodeSize = (DWORD)ShellCodeEnd - (DWORD)ShellCodeStart;DWORD dwRetSize;WriteFile(hFile, ShellCodeStart, dwShellcodeSize, &dwRetSize, NULL);CloseHandle(hFile);
#endifreturn 0;
}
其中需要注意的是"ShellCodeStart"就是我们shellcode代码的起始函数,"ShellCodeEnd"是我们结束的代码,它实际上是没有任何功能的,只是一个shellcode结束的标志。
全部代码如下:
#include <Windows.h>
#include <winternl.h>#pragma comment(linker,"/entry:ShellCodeEntry")HMODULE GetKernel32BaseAddress();
FARPROC GetPorcAddressBaseAddress();void ShellCodeStart()
{typedef FARPROC(WINAPI* FN_GetProcAddress)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName);typedef HMODULE(WINAPI* FN_LoadLibraryA)(_In_ LPCSTR lpLibFileName);FN_GetProcAddress fn_GetProcAddress;fn_GetProcAddress = (FN_GetProcAddress)GetPorcAddressBaseAddress();if (!fn_GetProcAddress)return;FN_LoadLibraryA fn_LoadlibraryA;//LoadLibraryAchar szLoadLibraryA[] = { 'L','o','a','d','L','i','b','r','a','r','y','A',0 };HMODULE hKernel32Address = GetKernel32BaseAddress();fn_LoadlibraryA = (FN_LoadLibraryA)fn_GetProcAddress(hKernel32Address, szLoadLibraryA);if (!fn_LoadlibraryA)return;typedef int (WINAPI* FM_MessageBoxA)(__in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType);char szUser32[] = { 'U','s','e','r','3','2','.','d','l','l',0 };char szMessageBoxA[] = { 'M','e','s','s','a','g','e','B','o','x','A',0 };FM_MessageBoxA fn_MessageBoxA = (FM_MessageBoxA)(fn_GetProcAddress(fn_LoadlibraryA(szUser32), szMessageBoxA));if (fn_MessageBoxA){char szText[] = { 'H','e','l','l','o',0 };fn_MessageBoxA(NULL, szText, 0, 0);}
}//获取kernel32的基址
HMODULE GetKernel32BaseAddress()
{HMODULE hKernel32 = NULL;//保存模块名WCHAR wszModuleName[MAX_PATH];#ifdef _WIN64//获取gs偏移60hPPEB lpPeb = (PPEB)__readgsqword(0x60);
#else//获取fs偏移30hPPEB lpPeb = (PPEB)__readfsdword(0x30);
#endif//模块列表PLIST_ENTRY pListHead = &lpPeb->Ldr->InMemoryOrderModuleList;PLIST_ENTRY pListData = pListHead->Flink;while (pListData != pListHead){PLDR_DATA_TABLE_ENTRY pLDRData = CONTAINING_RECORD(pListData, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);//模块路径字符串数量DWORD dwLen = pLDRData->FullDllName.Length / 2;if (dwLen > 12){for (size_t i = 0; i < 12; i++){wszModuleName[11 - i] = pLDRData->FullDllName.Buffer[dwLen - 1 - i];}//kernel32.dllif ((wszModuleName[0] == 'k' || wszModuleName[0] == 'K') &&(wszModuleName[1] == 'e' || wszModuleName[1] == 'E') &&(wszModuleName[2] == 'r' || wszModuleName[2] == 'R') &&(wszModuleName[3] == 'n' || wszModuleName[3] == 'N') &&(wszModuleName[4] == 'e' || wszModuleName[4] == 'E') &&(wszModuleName[5] == 'l' || wszModuleName[5] == 'L') &&wszModuleName[6] == '3' &&wszModuleName[7] == '2' &&wszModuleName[8] == '.' &&(wszModuleName[9] == 'd' || wszModuleName[9] == 'D') &&(wszModuleName[10] == 'l' || wszModuleName[10] == 'L') &&(wszModuleName[11] == 'l' || wszModuleName[11] == 'L')){//kernel32.dll在进程中的基址hKernel32 = (HMODULE)pLDRData->DllBase;break;}}pListData = pListData->Flink;}return hKernel32;
}//获取GetPorcAddress地址
FARPROC GetPorcAddressBaseAddress()
{FARPROC pGetPorcAddress = NULL;HMODULE hKernel32 = GetKernel32BaseAddress();if (!hKernel32)return pGetPorcAddress;//获取Dos头PIMAGE_DOS_HEADER lpDosHeader = (PIMAGE_DOS_HEADER)hKernel32;//获取NT头PIMAGE_NT_HEADERS lpNtHeader = (PIMAGE_NT_HEADERS)((unsigned char*)hKernel32 + lpDosHeader->e_lfanew);if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size &&!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress){return pGetPorcAddress;}//导出表PIMAGE_EXPORT_DIRECTORY lpExports = (PIMAGE_EXPORT_DIRECTORY)((unsigned char*)hKernel32 + lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);//函数名PDWORD lpdwFunName = (PDWORD)((unsigned char*)hKernel32 + lpExports->AddressOfNames);//函数序号PWORD lpdwOrd = (PWORD)((unsigned char*)hKernel32 + lpExports->AddressOfNameOrdinals);//函数地址PDWORD lpdwFunAddr = (PDWORD)((unsigned char*)hKernel32 + lpExports->AddressOfFunctions);for (DWORD dwLoop = 0; dwLoop < lpExports->NumberOfNames; dwLoop++){char* pFunName = (char*)(lpdwFunName[dwLoop] + (unsigned char*)hKernel32);//GetProcAddressif (pFunName[0] == 'G' && pFunName[1] == 'e' &&pFunName[2] == 't' && pFunName[3] == 'P' &&pFunName[4] == 'r' && pFunName[5] == 'o' &&pFunName[6] == 'c' && pFunName[7] == 'A' &&pFunName[8] == 'd' && pFunName[9] == 'd' &&pFunName[10] == 'r' && pFunName[11] == 'e' &&pFunName[12] == 's' && pFunName[13] == 's'){pGetPorcAddress = (FARPROC)(lpdwFunAddr[lpdwOrd[dwLoop]] + (unsigned char*)hKernel32);break;}}return pGetPorcAddress;
}void ShellCodeEnd()
{
}int ShellCodeEntry()
{HANDLE hFile = CreateFile(L"shellcode.bin", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);DWORD dwShellcodeSize = (DWORD)ShellCodeEnd - (DWORD)ShellCodeStart;DWORD dwRetSize;WriteFile(hFile, ShellCodeStart, dwShellcodeSize, &dwRetSize, NULL);CloseHandle(hFile);return 0;
}
如此,运行程序之后,我们的shellcode代码段就直接保存到"shellcode.bin"文件中了。
生成的文件内容如下:
可以看到,这个文件和我们的shellcode代码完全一样。
到这里,我们的shellcode生成基础基本就完事了。感谢大家的阅读,后边可能还会出一些高级的,大家期待吧… ^ _ ^