加壳 原理:
改变原始OEP的指向 指向壳程序 壳程序可以添加其它程序 如弹密码框 只有密码正确就运行(只是所有函数动态加载)
typedef int (WINAPI *LPMESSAGEBOX)(HWND, LPCTSTR, LPCTSTR, UINT); //MessageBoxW
typedef DWORD(WINAPI *LPGETPROCADDRESS)(HMODULE, LPCSTR); // GetProcAddress
typedef HMODULE(WINAPI *LPLOADLIBRARYEX)(LPCTSTR, HANDLE, DWORD); // LoadLibaryEx
typedef HMODULE(WINAPI *GETModuleHandle)(
_In_opt_ LPCTSTR lpModuleName
);
typedef BOOL(WINAPI* SHOWWINDOW)(
_In_ HWND hWnd,
_In_ int nCmdShow
);
typedef BOOL(WINAPI* GteMessage)(
_Out_ LPMSG lpMsg,
_In_opt_ HWND hWnd,
_In_ UINT wMsgFilterMin,
_In_ UINT wMsgFilterMax
);
typedef LRESULT(WINAPI* DISpatchMessage)(
_In_ const MSG *lpmsg
);
typedef ATOM(WINAPI* REGisterClass)(
_In_ const WNDCLASS *lpWndClass
);
typedef HWND(WINAPI *CREateWindowEx)(
_In_ DWORD dwExStyle,
_In_opt_ LPCTSTR lpClassName,
_In_opt_ LPCTSTR lpWindowName,
_In_ DWORD dwStyle,
_In_ int x,
_In_ int y,
_In_ int nWidth,
_In_ int nHeight,
_In_opt_ HWND hWndParent,
_In_opt_ HMENU hMenu,
_In_opt_ HINSTANCE hInstance,
_In_opt_ LPVOID lpParam
);
typedef VOID(WINAPI* POSTQuitMessage)(
_In_ int nExitCode
);
typedef LRESULT(WINAPI* DEFWindowProc)(
_In_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
typedef BOOL(*UPDateWindow)(
_In_ HWND hWnd
);
typedef int (WINAPI* GETWindowText)(
_In_ HWND hWnd,
_Out_ LPTSTR lpString,
_In_ int nMaxCount
);
typedef int (WINAPI* GETWindowTextLength)(
_In_ HWND hWnd
);
typedef HWND(WINAPI* GETDlgItem)(
_In_opt_ HWND hDlg,
_In_ int nIDDlgItem
);
typedef BOOL(WINAPI* SETWindowText)(
_In_ HWND hWnd,
_In_opt_ LPCTSTR lpString
);
typedef BOOL(WINAPI* TRanslateMessage)(
_In_ const MSG *lpMsg
);
typedef LPVOID(WINAPI *MYVIRTUALALLOC)(
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect
);
typedef BOOL(WINAPI *MYVIRTUALFREE)(
_In_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD dwFreeType
);
typedef HMODULE(WINAPI *MYLOADLIBRARY)(
_In_ LPCSTR lpLibFileName
);
wchar_t g_wcbuf100[100] = { 0 };
wchar_t g_MIMA100[100] = L"haidragon";
wchar_t wStrtext[100] = L"请输入密码";
_declspec(thread) int g_num;
/////////////////////////////////////////////////////////////
//初始化
LPGETPROCADDRESS g_funGetProcAddress = nullptr;
LPLOADLIBRARYEX g_funLoadLibraryEx = nullptr;
HMODULE hModuleKernel32 = nullptr;
HMODULE hModuleUser32 = nullptr;
GETModuleHandle g_funGetModuleHandle = nullptr;
LPMESSAGEBOX g_funMessageBox = nullptr;
CREateWindowEx g_funCreateWindowEx = nullptr;
POSTQuitMessage g_funPostQuitMessage = nullptr;
DEFWindowProc g_funDefWindowProc = nullptr;
GteMessage g_funGetMessage = nullptr;
REGisterClass g_funRegisterClass = nullptr;
SHOWWINDOW g_funShowWindow = nullptr;
UPDateWindow g_funUpdateWindow = nullptr;
DISpatchMessage g_funDispatchMessage = nullptr;
GETWindowText g_funGetWindowText = nullptr;
GETWindowTextLength g_funGetWindowTextLength = nullptr;
GETDlgItem g_funGetDlgItem = nullptr;
SETWindowText g_funSetWindowText = nullptr;
TRanslateMessage g_funTranslateMessage = nullptr;
MYVIRTUALALLOC g_VirtualAlloc = nullptr;
MYVIRTUALFREE g_VirtualFree = nullptr;
MYLOADLIBRARY g_LoadLibraryA = nullptr;
DWORD g_dwImageBase;
DWORD g_oep;
void start();
PACKINFO g_PackInfo = { (DWORD)start };
//获取kernel32模块加载基址
DWORD GetKernel32Base()
{
DWORD dwKernel32Addr = 0;
__asm
{
push eax
mov eax, dword ptr fs : [0x30] // eax = PEB的地址
mov eax, [eax + 0x0C] // eax = 指向PEB_LDR_DATA结构的指针
mov eax, [eax + 0x1C] // eax = 模块初始化链表的头指针InInitializationOrderModuleList
mov eax, [eax] // eax = 列表中的第二个条目
mov eax, [eax + 0x08] // eax = 获取到的Kernel32.dll基址(Win7下获取的是KernelBase.dll的基址)
mov dwKernel32Addr, eax
pop eax
}
return dwKernel32Addr;
}
//获取GetProcAddress的基址
DWORD GetGPAFunAddr()
{
DWORD dwAddrBase = GetKernel32Base();
// 1. 获取DOS头、NT头
PIMAGE_DOS_HEADER pDos_Header;
PIMAGE_NT_HEADERS pNt_Header;
pDos_Header = (PIMAGE_DOS_HEADER)dwAddrBase;
pNt_Header = (PIMAGE_NT_HEADERS)(dwAddrBase + pDos_Header->e_lfanew);
// 2. 获取导出表项
PIMAGE_DATA_DIRECTORY pDataDir;
PIMAGE_EXPORT_DIRECTORY pExport;
pDataDir = pNt_Header->OptionalHeader.DataDirectory;
pDataDir = &pDataDir[IMAGE_DIRECTORY_ENTRY_EXPORT];
pExport = (PIMAGE_EXPORT_DIRECTORY)(dwAddrBase + pDataDir->VirtualAddress);
// 3、获取导出表的必要信息
DWORD dwModOffset = pExport->Name; // 模块的名称
DWORD dwFunCount = pExport->NumberOfFunctions; // 导出函数的数量
DWORD dwNameCount = pExport->NumberOfNames; // 导出名称的数量
PDWORD pEAT = (PDWORD)(dwAddrBase + pExport->AddressOfFunctions); // 获取地址表的RVA
PDWORD pENT = (PDWORD)(dwAddrBase + pExport->AddressOfNames); // 获取名称表的RVA
PWORD pEIT = (PWORD)(dwAddrBase + pExport->AddressOfNameOrdinals); //获取索引表的RVA
// 4、获取GetProAddress函数的地址
for (DWORD i = 0; i < dwFunCount; i++)
{
if (!pEAT[i])
{
continue;
}
// 4.1 获取序号
DWORD dwID = pExport->Base + i;
// 4.2 变量EIT 从中获取到 GetProcAddress的地址
for (DWORD dwIdx = 0; dwIdx < dwNameCount; dwIdx++)
{
// 序号表中的元素的值 对应着函数地址表的位置
if (pEIT[dwIdx] == i)
{
//根据序号获取到名称表中的名字
DWORD dwNameOffset = pENT[dwIdx];
char * pFunName = (char*)(dwAddrBase + dwNameOffset);
//判断是否是GetProcAddress函数
if (!strcmp(pFunName, "GetProcAddress"))
{
// 获取EAT的地址 并将GetProcAddress地址返回
DWORD dwFunAddrOffset = pEAT[i];
return dwAddrBase + dwFunAddrOffset;
}
}
}
}
return -1;
}
//初始化API
bool InitializationAPI()
{
g_num;//使用tls变量,产生tls节表
//初始化
g_funGetProcAddress = (LPGETPROCADDRESS) GetGPAFunAddr();
g_funLoadLibraryEx = (LPLOADLIBRARYEX) g_funGetProcAddress((HMODULE)GetKernel32Base(), "LoadLibraryExW");
hModuleKernel32 = g_funLoadLibraryEx(L"Kernel32.dll", NULL, NULL);
hModuleUser32 = g_funLoadLibraryEx(L"user32.dll", NULL, NULL);
g_LoadLibraryA = (MYLOADLIBRARY) g_funGetProcAddress(hModuleKernel32, "LoadLibraryA");
g_funGetModuleHandle = (GETModuleHandle) g_funGetProcAddress(hModuleKernel32, "GetModuleHandleW");
g_VirtualAlloc = (MYVIRTUALALLOC) g_funGetProcAddress(hModuleKernel32, "VirtualAlloc");
g_VirtualFree = (MYVIRTUALFREE) g_funGetProcAddress(hModuleKernel32, "VirtualFree");
g_funMessageBox = (LPMESSAGEBOX) g_funGetProcAddress(hModuleUser32, "MessageBoxW");
g_funCreateWindowEx = (CREateWindowEx) g_funGetProcAddress(hModuleUser32, "CreateWindowExW");
g_funPostQuitMessage = (POSTQuitMessage) g_funGetProcAddress(hModuleUser32, "PostQuitMessage");
g_funDefWindowProc = (DEFWindowProc) g_funGetProcAddress(hModuleUser32, "DefWindowProcW");
g_funGetMessage = (GteMessage) g_funGetProcAddress(hModuleUser32, "GetMessageW");
g_funRegisterClass = (REGisterClass) g_funGetProcAddress(hModuleUser32, "RegisterClassW");
g_funShowWindow = (SHOWWINDOW) g_funGetProcAddress(hModuleUser32, "ShowWindow");
g_funUpdateWindow = (UPDateWindow) g_funGetProcAddress(hModuleUser32, "UpdateWindow");
g_funDispatchMessage = (DISpatchMessage) g_funGetProcAddress(hModuleUser32, "DispatchMessageW");
g_funGetWindowText = (GETWindowText) g_funGetProcAddress(hModuleUser32, "GetWindowTextW");
g_funGetWindowTextLength = (GETWindowTextLength)g_funGetProcAddress(hModuleUser32, "GetWindowTextLengthW");
g_funGetDlgItem = (GETDlgItem) g_funGetProcAddress(hModuleUser32, "GetDlgItem");
g_funSetWindowText = (SETWindowText) g_funGetProcAddress(hModuleUser32, "SetWindowTextW");
g_funTranslateMessage = (TRanslateMessage) g_funGetProcAddress(hModuleUser32, "TranslateMessage");
g_dwImageBase = (DWORD)g_funGetModuleHandle(NULL);
g_oep = g_PackInfo.TargetOepRva + g_dwImageBase;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
) {
switch (uMsg)
{
case WM_CREATE: {
wchar_t wStr[20] = L"窗口回调函数触发";
wchar_t wStr2[20] = L"haha";
g_funMessageBox(NULL, wStr, wStr2, NULL);
/////////////////////////////////////////////////////////////////////////////////////
DWORD dwStyle = ES_LEFT | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE;
DWORD dwExStyle = WS_EX_CLIENTEDGE | WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR;
HWND hWnd = g_funCreateWindowEx(
dwExStyle, //dwExStyle 扩展样式
L"Edit", //lpClassName 窗口类名
wStrtext, //lpWindowName 窗口标题
dwStyle, //dwStyle 窗口样式
150, //x 左边位置
100, //y 顶边位置
200, //nWidth 宽度
20, //nHeight 高度
hwnd, //hWndParent 父窗口句柄
(HMENU)0x1002, //ID
g_funGetModuleHandle(0), //hInstance 应用程序句柄
NULL //lpParam 附加参数
);
return 0;
/////////////////////////////////////////////////////////////////////////////////
}
case WM_COMMAND: {
WORD wId = LOWORD(wParam);
WORD wCode = HIWORD(wParam);
HANDLE hChild = (HANDLE)lParam;
if (wId == 0x1001 && wCode == BN_CLICKED)
{
HWND hwndCombo = g_funGetDlgItem(hwnd, 0x1002);
int cTxtLen = g_funGetWindowTextLength(hwndCombo);
g_funGetWindowText(hwndCombo, g_wcbuf100, 100);
wchar_t wStr[20] = L"按钮触发";
wchar_t wStr2[20] = L"haha";
g_funMessageBox(NULL, wStr, wStr2, NULL);
wchar_t wStr3[20] = L"";
if (decide() == 1) {
//g_funPostQuitMessage(0);
g_funShowWindow(hwnd, SW_HIDE);
//运行壳代码
FusedFunc((DWORD)AllFunc);
//_asm jmp g_PackInfo.TargetOep;
wchar_t wStr[20] = L"密码正确!!!";
wchar_t wStr2[20] = L"haha";
g_funMessageBox(NULL, wStr, wStr2, NULL);
_asm jmp g_oep;
}
else {
wchar_t wStr[20] = L"密码错误请重新输入!!!";
wchar_t wStr2[20] = L"haha";
g_funMessageBox(NULL, wStr, wStr2, NULL);
}
g_funSetWindowText(hwndCombo, wStr3);
return 1;
}
break;
}
case WM_CLOSE:
{
g_funPostQuitMessage(0);
//return 0;
break;
}
}
// 返回默认的窗口处理过程
return g_funDefWindowProc(hwnd, uMsg, wParam, lParam);
}
void CtrateWin() {
MSG msg = { 0 };
wchar_t wStr[20] = L"allenboy";
wchar_t wStr2[20] = L"haha";
g_funMessageBox(NULL, wStr, wStr2, NULL);
// 先注册窗口类
WNDCLASS wcs = {};
wcs.lpszClassName = L"dragon";
wcs.lpfnWndProc = WindowProc;
wcs.hbrBackground = (HBRUSH)(COLOR_CAPTIONTEXT + 1);
/////////////////////////////////////////////////////////////////////////////////////////
//RegisterClass
//RegisterClass(&wcs);
g_funRegisterClass(&wcs);
//#define CreateWindowW(lpClassName, lpWindowName, dwStyle, x, y,\
//nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)
//注册窗口
//CreateWindowEx
//窗口类名一定要与上面的一致
HWND hWnd = g_funCreateWindowEx(0L, L"dragon", L"haidragon", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
500, 200, 500, 500,
NULL, NULL, NULL, NULL);
// 三种风格 WS_OVERLAPPEDWINDOW WS_POPUPWINDOW WS_CHILDWINDOW
g_funCreateWindowEx(0L, L"BUTTON", L"ok", WS_CHILD | WS_VISIBLE,
200, 150,// 在父窗口的客户区的位置,
100, 50,// 宽 高
hWnd,// 父窗口
(HMENU)0x1001,// 如果是顶层窗口 就是菜单句柄 子窗口就是本身的ID
//GetModuleHandle
g_funGetModuleHandle(0), NULL);
//ShowWindow(hWnd, SW_SHOW);
g_funShowWindow(hWnd, SW_SHOW);
g_funUpdateWindow(hWnd);
/*while (GetMessage(&msg, 0, 0, 0))
{
DispatchMessage(&msg);
}*/
while (g_funGetMessage(&msg, 0, 0, 0))
{
//DispatchMessage(&msg);
g_funTranslateMessage(&msg);
g_funDispatchMessage(&msg);
}
}
效果图
加密代码段 简单异或
//对代码段进行加密
void PEpack::Encode()
{
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)m_pNewBuf;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + m_pNewBuf);
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
// 定位到代码段,并将每个段加密
pSection = pSection + m_codeIndex;
PCHAR pStart = pSection->PointerToRawData + m_pNewBuf;
for (int i = 0; i < (pSection->Misc.VirtualSize); i++)
{
pStart[i] ^= 0x20;
}
}
////////////壳中解密//////////////////////
//解密
void Decode()
{
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)g_dwImageBase;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + g_dwImageBase);
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
// 找到.text段,并解密
while (TRUE)
{
if (!strcmp((char*)pSection->Name, ".text"))
{
PCHAR pStart = pSection->VirtualAddress + (PCHAR)g_dwImageBase;
for (int i = 0; i < pSection->Misc.VirtualSize; i++)
{
pStart[i] ^= 0x20;
}
break;
}
pSection = pSection + 1;
}
}
压缩(除tls 表 资源表 与 头除外 这里先于拷贝壳的重定位表 不然也会被压缩 就运行不起来了 )
压缩时的问题有 资源段问题 tls段问题
首先 运行时很会用到 资源 同时当运行壳代码之前会启动线程,因些就会用到tls数据段
所以这俩个都不压 其它全部压然后 放到最后,同时在压缩的时候重建PE(也就是后面的往上移)
最重的一点是在所有操作前 保存相关节与节的前后顺序与在原来大小和压缩后的大小等等
效果图
压缩 (同时重建PE)
//压缩PE文件
void PEpack::CompressPE(PPACKINFO &pPackInfo)
{
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)m_pNewBuf;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + m_pNewBuf);
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
// 用于记录压缩区段的个数
pPackInfo->PackSectionNumber = 0;
// 1.1 获取文件头的大小,并获取除资源段,tls之外段的文件中的总大小
DWORD SecSizeWithOutResAndTls = 0;
PIMAGE_SECTION_HEADER pSectionTmp1 = pSection;
BOOL isFirstNoEmptySec = TRUE;
DWORD dwHeaderSize = 0;
//先计算要拷贝的总大小
for (SIZE_T i = 0; i < pNt->FileHeader.NumberOfSections; i++)
{
// 用于获得第一个非空区段的文件偏移,也就是文件头大小
if (isFirstNoEmptySec && pSectionTmp1->SizeOfRawData != 0)
{
dwHeaderSize = pSectionTmp1->PointerToRawData;
isFirstNoEmptySec = FALSE;
}
// 用于获取 非 rsrc/tls段的总大小
if (pSectionTmp1->VirtualAddress != m_pResSectionRva &&
pSectionTmp1->VirtualAddress != m_pTlsSectionRva)
{
SecSizeWithOutResAndTls += pSectionTmp1->SizeOfRawData;
}
pSectionTmp1 = pSectionTmp1 + 1;
}
// 1.2 读取要压缩的段到内存
// 申请内存
PCHAR memWorked = new CHAR[SecSizeWithOutResAndTls];
// 已经拷贝的内存大小
DWORD dwCopySize = 0;
// 保存这些区段到内存
PIMAGE_SECTION_HEADER pSectionTmp2 = pSection;
//再次循环
for (SIZE_T i = 0; i < pNt->FileHeader.NumberOfSections; i++)
{
//判断是否为tls与资源表
if (pSectionTmp2->VirtualAddress != m_pResSectionRva &&
pSectionTmp2->VirtualAddress != m_pTlsSectionRva)
{
//开始拷贝
memcpy_s(memWorked + dwCopySize, pSectionTmp2->SizeOfRawData,
m_pNewBuf + pSectionTmp2->PointerToRawData, pSectionTmp2->SizeOfRawData);
dwCopySize += pSectionTmp2->SizeOfRawData;
}
pSectionTmp2 = pSectionTmp2 + 1;
}
// 1.3 压缩,并获取压缩后的大小(文件对齐在添加区段中进行)
LONG blen;
PCHAR packBuf = Compress(memWorked, SecSizeWithOutResAndTls, blen);
// 1.4 保存资源段 到内存空间
PCHAR resBuffer = new CHAR[m_ResSizeOfRawData];
PCHAR tlsBuffer = new CHAR[m_TlsSizeOfRawData];
//原样
memcpy_s(resBuffer, m_ResSizeOfRawData, m_ResPointerToRawData + m_pNewBuf, m_ResSizeOfRawData);
memcpy_s(tlsBuffer, m_TlsSizeOfRawData, m_TlsPointerToRawData + m_pNewBuf, m_TlsSizeOfRawData);
// 1.6 设置压缩信息到信息结构体中
// 并且将m_pBuf中的非资源段和非tls段的区段文件偏移和大小置为0
PIMAGE_DOS_HEADER pOriDos = (PIMAGE_DOS_HEADER)m_pBuf;
PIMAGE_NT_HEADERS pOriNt = (PIMAGE_NT_HEADERS)(pOriDos->e_lfanew + m_pBuf);
PIMAGE_SECTION_HEADER pOriSection = IMAGE_FIRST_SECTION(pOriNt);
for (int i = 0; i < pOriNt->FileHeader.NumberOfSections; i++)
{
if (pOriSection->VirtualAddress != m_pResSectionRva&&
pOriSection->VirtualAddress != m_pTlsSectionRva)
{
// 用于获取压缩节区的数量
pPackInfo->PackSectionNumber++;
// 设置第i个节的压缩节区index
pPackInfo->PackInfomation[pPackInfo->PackSectionNumber][0] = i;
// 设置压缩节区的文件大小
pPackInfo->PackInfomation[pPackInfo->PackSectionNumber][1] = pOriSection->SizeOfRawData;
// 设置原来的节区的文件中偏移和大小为0
pOriSection->SizeOfRawData = 0;
pOriSection->PointerToRawData = 0;
}
pOriSection = pOriSection + 1;
}
// 1.6 申请新空间,使m_pNewBuf指向之,将m_pBuf文件头拷贝
m_FileSize = dwHeaderSize + m_ResSizeOfRawData + m_TlsSizeOfRawData;
//开始上移
m_pNewBuf = nullptr;
m_pNewBuf = new CHAR[m_FileSize];
// 1.6 修改res段的区段头,并拷贝
pOriSection = IMAGE_FIRST_SECTION(pOriNt);
//开始修改资源与tls段的头 先判断它俩个位置
if (m_ResSectionIndex < m_TlsSectionIndex)
{
pOriSection[m_ResSectionIndex].PointerToRawData = dwHeaderSize;
pOriSection[m_TlsSectionIndex].PointerToRawData = dwHeaderSize + m_ResSizeOfRawData;
memcpy_s(m_pNewBuf, dwHeaderSize, m_pBuf, dwHeaderSize);
memcpy_s(m_pNewBuf + dwHeaderSize, m_ResSizeOfRawData, resBuffer, m_ResSizeOfRawData);
memcpy_s(m_pNewBuf + dwHeaderSize + m_ResSizeOfRawData
, m_TlsSizeOfRawData, tlsBuffer, m_TlsSizeOfRawData);
}
else if (m_ResSectionIndex > m_TlsSectionIndex)
{
pOriSection[m_TlsSectionIndex].PointerToRawData = dwHeaderSize;
pOriSection[m_ResSectionIndex].PointerToRawData = dwHeaderSize + m_TlsSizeOfRawData;
memcpy_s(m_pNewBuf, dwHeaderSize, m_pBuf, dwHeaderSize);
memcpy_s(m_pNewBuf + dwHeaderSize, m_TlsSizeOfRawData, tlsBuffer, m_TlsSizeOfRawData);
memcpy_s(m_pNewBuf + dwHeaderSize + m_TlsSizeOfRawData
, m_ResSizeOfRawData, resBuffer, m_ResSizeOfRawData);
}
else
{
memcpy_s(m_pNewBuf, dwHeaderSize, m_pBuf, dwHeaderSize);
}
/*else if(m_ResSectionIndex == -1 && m_TlsSectionIndex == -1)
{
}*/
delete[] m_pBuf;
m_pBuf = m_pNewBuf;
// 添加区段(压缩后的所有数据)
pPackInfo->packSectionRva = AddSection(".pack", packBuf, blen, 0xC0000040);
pPackInfo->packSectionSize = CalcAlignment(blen, 0x200);
// 1.7 添加.pack段
delete[] memWorked;
free(packBuf);
delete[] resBuffer;
}
解压
void Decompress()
{
// 1.获取节区头首地址
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)g_dwImageBase;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + g_dwImageBase);
PIMAGE_SECTION_HEADER pSecHeader = IMAGE_FIRST_SECTION(pNtHeader);
// 2.解压压缩区段
PCHAR lpPacked = ((PCHAR)g_dwImageBase + g_PackInfo.packSectionRva);// 内存地址
DWORD dwPackedSize = aPsafe_get_orig_size(lpPacked);// 获取解压后的大小
PCHAR lpBuffer = (PCHAR)g_VirtualAlloc(NULL, dwPackedSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);//申请内存
aPsafe_depack(lpPacked, g_PackInfo.packSectionSize, lpBuffer, dwPackedSize);// 解压
// 3.将各区段还原回去
DWORD offset = 0;
for (int i = 0; i < g_PackInfo.PackSectionNumber; i++)
{
// 区段的标号
int index = g_PackInfo.PackInfomation[i][0];
// 这个区段的SizeOfRawData
int size = g_PackInfo.PackInfomation[i][1];
int * pint = &size;
PCHAR destionVA = (PCHAR)g_dwImageBase + pSecHeader[index].VirtualAddress;
PCHAR srcVA = lpBuffer + offset;
_asm {
mov eax, eax
mov eax, eax
mov eax, eax
mov eax, eax
mov eax, eax
mov eax, eax
}
//memcpy(destionVA, srcVA, size);
_asm {
mov esi, srcVA
mov edi, destionVA
mov ebx, pint
mov ecx, [ebx]
cld;地址增量传送
rep movsb;rep执行一次串指令后ecx减一
}
offset += size;
}
g_VirtualFree(lpBuffer, dwPackedSize, MEM_DECOMMIT);
}
增加随机基址
回顾 重定位表
要想实现随机基址 必须解决俩个问题
第一 壳代码的重定位 问题
第二 目标程序(被加壳程序 )的重定位
解决第一个问题 由于壳程序 区段都在text中 把text区段拷贝到目标程序时,同时也要拷贝重定位 把原来目标程序的重定位目录表的指向改为拷贝的重定位的地址
解决第二个问题 原来的重定位表 被压缩了 壳代码来修复
拷贝重定位 同时修改指向
//对于动态加载基址,需要将stub的重定位区段(.reloc)修改后保存,将PE重定位信息指针指向该地址
void PEpack::ChangeReloc(PCHAR pBuf)
{
// 定位到第一个重定位块
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + pBuf);
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
PIMAGE_DATA_DIRECTORY pRelocDir = (pNt->OptionalHeader.DataDirectory + 5);
PIMAGE_BASE_RELOCATION pReloc =
(PIMAGE_BASE_RELOCATION)(pRelocDir->VirtualAddress + pBuf);
// 开始更改重定位
while (pReloc->SizeOfBlock != 0)
{
// 重定位项开始的项,将其定位到在此之前添加allen段(要他原来的RVA 变为壳代码中的RVA)
//原理:重位表的每一块中 pReloc->VirtualAddress 为每一页的起始地址(也就是0x1000 0x2000...),根据页分块
//原来的代码段在相对于加载基址(假如0x400000)的第2页也就是0x1000(第二页的起始地址)表示PE中的头与数据目录表等等
// 而这个重定位是dll 的也就是他没有头了 要减去0x1000(所有的) 同时加了节的RVA,也就是最后一节壳代码的开始(RVA) 相当于代码段从这里开始
//例如要找到需要重定位的那个地址的va va=加载基址 +页基址(pReloc->VirtualAddress)+页内地址(重定位块中的一个数组保存就是)
pReloc->VirtualAddress = (DWORD)(pReloc->VirtualAddress - 0x1000 + GetLastSectionRva());
// 定位到下一个重定位块
pReloc = (PIMAGE_BASE_RELOCATION)((PCHAR)pReloc + pReloc->SizeOfBlock);
}
DWORD dwRelocRva = 0;
DWORD dwRelocSize = 0;
DWORD dwSectionAttribute = 0;
while (TRUE)
{
if (!strcmp((char*)pSection->Name, ".reloc"))
{
dwRelocRva = pSection->VirtualAddress;
dwRelocSize = pSection->SizeOfRawData;
dwSectionAttribute = pSection->Characteristics;
break;
}
pSection = pSection + 1;
}
// 将stubdll的.reloc添加到PE文件的最后,命名为.nreloc,返回该区段的Rva
DWORD RelocRva = AddSection(".nreloc", dwRelocRva + pBuf, dwRelocSize, dwSectionAttribute);
// 将重定位信息指向新添加的区段
PIMAGE_DOS_HEADER pExeDos = (PIMAGE_DOS_HEADER)m_pNewBuf;
PIMAGE_NT_HEADERS pExeNt = (PIMAGE_NT_HEADERS)(pExeDos->e_lfanew + m_pNewBuf);
pExeNt->OptionalHeader.DataDirectory[5].VirtualAddress = RelocRva;
pExeNt->OptionalHeader.DataDirectory[5].Size = dwRelocSize;
}
修复重定位
void FixReloc()
{
//以下是重定位
DWORD *tmp;
if (g_PackInfo.RelocRva) //如果没有重定位表表示不用重定位,跳过重定位代码
{
DWORD relocation = (DWORD)g_dwImageBase - g_PackInfo.ImageBase;
IMAGE_BASE_RELOCATION *relocationAddress = (IMAGE_BASE_RELOCATION*)(g_PackInfo.RelocRva + (DWORD)g_dwImageBase);
while (relocationAddress->VirtualAddress != 0)
{
LPVOID rva = (LPVOID)((DWORD)g_dwImageBase + relocationAddress->VirtualAddress);
DWORD BlockNum = (relocationAddress->SizeOfBlock - 8) / 2;
if (BlockNum == 0) break;
WORD *Offset = (WORD *)((DWORD)relocationAddress + 8);
for (int i = 0; i < (int)BlockNum; i++)
{
if ((Offset[i] & 0xF000) != 0x3000) continue;
tmp = (DWORD*)((Offset[i] & 0xFFF) + (DWORD)rva);
*tmp = (*tmp) + relocation;
}
relocationAddress = (IMAGE_BASE_RELOCATION*)((DWORD)relocationAddress + relocationAddress->SizeOfBlock);
}
}
}
tls处理
tls回顾
https://www.cnblogs.com/iBinary/p/7697355.html
先简单说下
1。PE目录表第10项为tls目录它指向tls表,tls表保存在rdata段 表的一些地址又指向另外的地址就是 tls数据段
2。有个这个消息之后,如果压缩所有,有几个问题:
第一 rdata很定要压缩,但压缩完后 表也就没了
第二 有的程序本就没有线程 也就对应着没有索引数组
第三 tls被我处理了 加载器不知道了,怎么处理
一个个来
先解决第一个 rdata段压缩了 没了tls表 那就用stub的(壳对应的tls 由于它的段都被合成text了 但是要做一些地址转换问题)
解决第二个 有的程序没有 那我就在壳程序里创建一个线程局部变量 对应着 它就给我生成一个tls表
解决第三个 tls表不就是保存一些回调函数嘛 加载器也就是判断你有没有回调 有调用 没有不调用 那处理了tls表 加载器不会调用(处理tls也就是把回调地址变为0) 那就壳代码来调用
总结:
在加壳之前我先做相关处理 处理tls表
然后加壳之后 设置tls表 为stub中的表也就是设置 目录表第10项tls表的指向
还有一个最重要的就是 不管你有没有我先创建一个线程(索引数组)也就是创建一个线程局部变量 这个在壳代码里创建的 所以放在共享结构体中 tls表中保存的都是VA (逆向工程核心原理第454页有讲)
效果图
代码
1。第一步先读文件 设置相应的地址
//读取要加密文件到内存 同时获取相关的区段信息
void PEpack::ReadTargetFile(char* pPath, PPACKINFO& pPackInfo)
{
DWORD dwRealSize = 0;
//1 打开文件
HANDLE hFile = CreateFileA(
pPath, 0x0001, FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL
);
//2 获取文件大小
m_FileSize = GetFileSize(hFile, NULL);
m_dwNewFileSize = m_FileSize;
//3 申请这么大的空间
m_pBuf = new CHAR[m_FileSize];
m_pNewBuf = m_pBuf;
memset(m_pBuf, 0, m_FileSize);
//4 把文件内容读取到申请出的空间中
ReadFile(hFile, m_pBuf, m_FileSize, &dwRealSize, NULL);
m_pDos = (PIMAGE_DOS_HEADER)m_pNewBuf;
m_pNt = (PIMAGE_NT_HEADERS)(m_pDos->e_lfanew + m_pNewBuf);
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(m_pNt);
// 保存原始节区数
m_OriSectionNumber = m_pNt->FileHeader.NumberOfSections;
// 获取OEP
DWORD dwOEP = m_pNt->OptionalHeader.AddressOfEntryPoint;
// 获得资源段的信息
m_pResRva = m_pNt->OptionalHeader.DataDirectory[2].VirtualAddress;
m_pResSectionRva = 0;
m_ResSectionIndex = -1;
m_ResPointerToRawData = 0;
m_ResSizeOfRawData = 0;
// 获取tls的信息
m_pTlsSectionRva = 0;
m_TlsSectionIndex = -1;
m_TlsPointerToRawData = 0;
m_TlsSizeOfRawData = 0;
if (m_pNt->OptionalHeader.DataDirectory[9].VirtualAddress)
{
// 获得tls表指针
PIMAGE_TLS_DIRECTORY32 g_lpTlsDir =
(PIMAGE_TLS_DIRECTORY32)(RvaToOffset(m_pNt->OptionalHeader.DataDirectory[9].VirtualAddress) + m_pNewBuf);
// 获得tls数据起始rva
m_pTlsDataRva = g_lpTlsDir->StartAddressOfRawData - m_pNt->OptionalHeader.ImageBase;
}
for (int i = 0; i < m_pNt->FileHeader.NumberOfSections; i++)
{
// 如果oep在这个区段,就判断这个区段是代码段
if (dwOEP >= pSection->VirtualAddress &&
dwOEP <= pSection->VirtualAddress + pSection->Misc.VirtualSize)
{
// 获取代码段所在区段序号[通过oep判断]
m_codeIndex = i;
}
if (m_pResRva >= pSection->VirtualAddress &&
m_pResRva <= pSection->VirtualAddress + pSection->Misc.VirtualSize)
{
// 获取rsrc段的信息
m_pResSectionRva = pSection->VirtualAddress;
m_ResPointerToRawData = pSection->PointerToRawData;
m_ResSizeOfRawData = pSection->SizeOfRawData;
m_ResSectionIndex = i;
}
// 获取tls信息
if (m_pNt->OptionalHeader.DataDirectory[9].VirtualAddress)
{
if (m_pTlsDataRva >= pSection->VirtualAddress &&
m_pTlsDataRva <= pSection->VirtualAddress + pSection->Misc.VirtualSize)
{
m_pTlsSectionRva = pSection->VirtualAddress;
m_TlsSectionIndex = i;
m_TlsPointerToRawData = pSection->PointerToRawData;
m_TlsSizeOfRawData = pSection->SizeOfRawData;
}
}
pSection = pSection + 1;
}
//5 关闭文件
CloseHandle(hFile);
}
2.第二步
处理tls
BOOL PEpack::DealwithTLS(PPACKINFO & pPackInfo)
{
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)m_pNewBuf;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + m_pNewBuf);
DWORD dwImageBase = pNt->OptionalHeader.ImageBase;
//先判断
if (pNt->OptionalHeader.DataDirectory[9].VirtualAddress == 0)
{
pPackInfo->bIsTlsUseful = FALSE;
return FALSE;
}
else
{
//设置为处理了tls
pPackInfo->bIsTlsUseful = TRUE;
PIMAGE_TLS_DIRECTORY32 g_lpTlsDir =
(PIMAGE_TLS_DIRECTORY32)(RvaToOffset(pNt->OptionalHeader.DataDirectory[9].VirtualAddress) + m_pNewBuf);
// 获取tlsIndex(索引地址)的Offset
DWORD indexOffset = RvaToOffset(g_lpTlsDir->AddressOfIndex - dwImageBase);
// 读取设置tlsIndex的值
pPackInfo->TlsIndex = 0;//index一般默认值为0
//这里是文件中的
if (indexOffset != -1)
{
//取内容
pPackInfo->TlsIndex = *(DWORD*)(indexOffset + m_pNewBuf);
}
// 设置tls表中的信息
m_StartOfDataAddress = g_lpTlsDir->StartAddressOfRawData;
m_EndOfDataAddress = g_lpTlsDir->EndAddressOfRawData;
m_CallBackFuncAddress = g_lpTlsDir->AddressOfCallBacks;
// 将tls回调函数rva设置到共享信息结构体
pPackInfo->TlsCallbackFuncRva = m_CallBackFuncAddress;
return TRUE;
}
}
3.第三步 压缩后 设置tls
void PEpack::SetTls(DWORD NewSectionRva, PCHAR pStubBuf, PPACKINFO pPackInfo)
{
PIMAGE_DOS_HEADER pStubDos = (PIMAGE_DOS_HEADER)pStubBuf;
PIMAGE_NT_HEADERS pStubNt = (PIMAGE_NT_HEADERS)(pStubDos->e_lfanew + pStubBuf);
PIMAGE_DOS_HEADER pPeDos = (PIMAGE_DOS_HEADER)m_pNewBuf;
PIMAGE_NT_HEADERS pPeNt = (PIMAGE_NT_HEADERS)(pPeDos->e_lfanew + m_pNewBuf);
//0 将pe目录表9指向stub的tls表
pPeNt->OptionalHeader.DataDirectory[9].VirtualAddress =
(pStubNt->OptionalHeader.DataDirectory[9].VirtualAddress - 0x1000) + NewSectionRva;
pPeNt->OptionalHeader.DataDirectory[9].Size =
pStubNt->OptionalHeader.DataDirectory[9].Size;
PIMAGE_TLS_DIRECTORY32 pITD =
(PIMAGE_TLS_DIRECTORY32)(RvaToOffset(pPeNt->OptionalHeader.DataDirectory[9].VirtualAddress) + m_pNewBuf);
// 获取公共结构体中tlsIndex的va
//共享信息结构体的首地址-stubdll在内存的指针+4(在结构体的中偏移)-stub头部+NewSectionRva+加载基址
DWORD indexva = ((DWORD)pPackInfo - (DWORD)pStubBuf + 4) - 0x1000 + NewSectionRva + pPeNt->OptionalHeader.ImageBase;
pITD->AddressOfIndex = indexva;
pITD->StartAddressOfRawData = m_StartOfDataAddress;
pITD->EndAddressOfRawData = m_EndOfDataAddress;
// 这里先取消tls的回调函数,向共享信息结构体中传入tls回调函数指针,在stub解壳的过程中手动调用tls,并将tls回调函数指针设置回去
pITD->AddressOfCallBacks = 0;
m_pBuf = m_pNewBuf;
}
IAT修复
压缩后加载器不会修复了(因为设置为0了)壳里修复 简单的处理
主要是有一个地址,就是自己申请一块内存空间,构造一段硬编码,将原函数地址填到这个硬编码的指定位置,然后将内存空间首地址写到IAT表
void IATReloc()
{
//读取IAT的dll , 获得dll加载基址; 读取IAT中的函数名 , 获得函数地址; 申请指定大小的空间
// 1.获取第一项iat项
PIMAGE_IMPORT_DESCRIPTOR pImportTable =
(PIMAGE_IMPORT_DESCRIPTOR)((DWORD)g_PackInfo.ImportTableRva + g_dwImageBase);
if (g_PackInfo.ImportTableRva) //如果没用导入表则跳过
{
HMODULE lib;
IMAGE_THUNK_DATA *IAT, *INTable;
IMAGE_IMPORT_BY_NAME *IatByName;
while (pImportTable->Name)//(pImportTable->FirstThunk)
{
lib = g_LoadLibraryA((char *)(pImportTable->Name + (DWORD)g_dwImageBase));
IAT = (IMAGE_THUNK_DATA *)(pImportTable->FirstThunk + (DWORD)g_dwImageBase);
INTable = (IMAGE_THUNK_DATA *)((pImportTable->OriginalFirstThunk ? pImportTable->OriginalFirstThunk : pImportTable->FirstThunk) + (DWORD)g_dwImageBase);
while (INTable->u1.AddressOfData)
{
DWORD dwAddress;
if ((((DWORD)INTable->u1.Function) & 0x80000000) == 0)
{
IatByName = (IMAGE_IMPORT_BY_NAME *)((DWORD)INTable->u1.AddressOfData + (DWORD)g_dwImageBase);
dwAddress = (DWORD)g_funGetProcAddress(lib, (char *)(IatByName->Name));
}
else
{
dwAddress = (DWORD)g_funGetProcAddress(lib, (LPCSTR)(INTable->u1.Ordinal & 0xFFFF));
}
char *dllName = (char *)(pImportTable->Name + (DWORD)g_dwImageBase);
// 只重定向这几个dll,如果所有的都重定向会出错
if ((!strcmp(dllName, "kernel32.dll"))
|| (!strcmp(dllName, "user32.dll"))
|| (!strcmp(dllName, "advapi32.dll"))
|| (!strcmp(dllName, "gdi32.dll")))
{
// 申请虚拟内存
PCHAR virBuf = (PCHAR)g_VirtualAlloc(NULL, 7, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// 赋值机器码
// mov ebx,address ;jmp address 0xbb 00 00 00 00 ff e3
virBuf[0] = 0xBB;
*(DWORD*)(virBuf + 1) = dwAddress;
virBuf[5] = 0xFF;
virBuf[6] = 0xE3;
// 将iat表填充为这个
IAT->u1.Function = (DWORD)virBuf;
}
else
{
IAT->u1.Function = dwAddress;
}
INTable++;
IAT++;
}
pImportTable++;
}
}
}
转载于:https://blog.51cto.com/haidragon/2129607