直接进入正题。
在完成正式的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);
#endif
return 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;
//LoadLibraryA
char 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偏移60h
PPEB lpPeb = (PPEB)__readgsqword(0x60);
#else
//获取fs偏移30h
PPEB 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.dll
if ((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);
//GetProcAddress
if (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生成基础基本就完事了。感谢大家的阅读,后边可能还会出一些高级的,大家期待吧… ^ _ ^