没赚到钱,发个干货
新年,作为新客户,祝大家新春快乐![]()
虚表是多态的机制用到的,研究这个很有必要,不论是普通程序还是内核程序,破解软件,或者游戏逆向,都可以通过过滤虚表来做检测,看过《iOS应用逆向工程》里面有些工具就是利用这点
游戏里面可以实现一些寻路,使用技能,一系列的方法,破解软件更是一绝,软件被vm咯无从下口,老火吧,冒痘?
更可恶的是栈的破坏,调用回溯gg了
MFC程序的宏展开就不多说了,网上一大堆,实现不想看别人的,自己动手写一个,下个断,通过调用堆栈视图,可以找到AfxWndProc这样一个函数,判断了一个消息宏,这个宏的值是0x360,来判断是不是MFC程序,给程序发消息,值就是这个宏,就能直接判断是不是MFC程序
然后调用了一个函数,FromHandlePermanent,通过句柄可以获取到CWnd类,有这个类就什么都可以获取到,可以调用RuntimeClass得到类名,一些信息
反汇编的代码每个版本都有所不同动态编译的还是静态编译的,发布版本还是调试版本都有所不同,只能每个版本做一个case
然后找到GetMeassgeMap的偏移,这个可以每个编译的版本偏移不一样,消息映射表就获取到,这里参考了git上面的项目
这里可以对虚表做一个过滤,观察所有虚表里的函数参数,这里参考《游戏外挂开发艺术》的代码,有兴趣可以看下这本书
虚表就利用完了,下面是没有写完的代码,可以拿去研究,对逆向非常有帮助,祝在逆向的道路上越来越强,成为为数不多的技术牛人。
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
HHOOK g_hHook = NULL;
HINSTANCE g_hInstance = NULL;
//////////////////////////过滤虚表的代码/////////
#define JMP_TOTAL_NUM 40000
typedef struct _JMP_TABLE_ITEM
{
UCHAR uPushEbx; //push ebx 0x53 存真正的虚表地址
UCHAR uPuhsad; //保存堆栈
UCHAR uPushfd; //保存堆栈
UCHAR uMovEax[5]; //mov eax, 0xffffffff(B8ffffffff)将偏移存放在eax
UCHAR uPushEax; //push eax eax虚表的编号 将eax保存在堆栈中
UCHAR uJmp[5]; //jmp 0xXXXXXXXX(E9 XXXXXXXX) 统一跳转到我们指定的地址中
UCHAR uSave; //保留以后在用
}JMP_TABLE_ITEM, *PJMP_TABLE_ITEM;
void Monitor() {
}
///
/// 对象基地址,指向的就是一个虚表,所以把这个虚表改成伪造的
/// 过滤函数的地址
///
void fakeFun(DWORD dwObjectBaseAddr, DWORD *pdwMonitorFn)
{
//
JMP_TABLE_ITEM *pJmpTable = NULL;
DWORD *pdwFakeVTblItem = NULL;
pdwFakeVTblItem = (DWORD*)VirtualAlloc(NULL, JMP_TOTAL_NUM *sizeof(DWORD), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!pdwFakeVTblItem)
{
OutputDebugString("pdwFakeVTblItem is NULL");
return;
}
ZeroMemory(pdwFakeVTblItem, JMP_TOTAL_NUM * sizeof(DWORD));
pJmpTable = (JMP_TABLE_ITEM*)VirtualAlloc(NULL, JMP_TOTAL_NUM * sizeof(JMP_TABLE_ITEM),MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!pJmpTable)
{
OutputDebugString("pJmpTable is NULL");
return;
}
ZeroMemory(pJmpTable, JMP_TOTAL_NUM *sizeof(JMP_TABLE_ITEM));
for (DWORD dwLoop = 0; dwLoop < JMP_TOTAL_NUM; dwLoop++)
{
pJmpTable->uPushEbx = 0x53;
pJmpTable->uPuhsad = 0x60;
pJmpTable->uPushfd = 0x9C;
pJmpTable->uMovEax[0] = 0xB8;
*((DWORD*)&(pJmpTable->uMovEax[1])) = dwLoop;
pJmpTable->uPushEax = 0x50;
pJmpTable->uJmp[0] = 0xE9;
*( (DWORD*) & (pJmpTable->uJmp[1]) ) = (DWORD)(pdwMonitorFn) - ( (DWORD)(pJmpTable) + FIELD_OFFSET(JMP_TABLE_ITEM, uSave) );
*pdwFakeVTblItem = (DWORD)pJmpTable;
pdwFakeVTblItem++;
pJmpTable++;
}
*(PDWORD)dwObjectBaseAddr = (DWORD)pdwFakeVTblItem;
}
////////////////////////////////////////////////////下面是hook代码////////////
class CObject{};
struct CRuntimeClass
{
LPCSTR m_lpszClassName;
int m_nObjectSize;
UINT m_wSchema;
CObject* (*m_pfnCreateObject)();
CRuntimeClass *m_pBaseClass;
};
class CCmdTarget {
public:
virtual AFX_MSGMAP* GetMessageMap() { return NULL; };
};//可以用这个来get网上的代码这样写的
typedef void (CCmdTarget::* AFX_PMSG)(void);
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT_PTR nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
struct AFX_MSGMAP
{
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
const AFX_MSGMAP_ENTRY* lpEntries;
};
class CWnd{
public:
virtual CRuntimeClass* GetRuntimeClass() = 0;
};
typedef CWnd* (__stdcall *FromHandlePermanent)(HWND);
/*
LRESULT CALLBACK
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
// special message which identifies the window as using AfxWndProc
if (nMsg == WM_QUERYAFXWNDPROC)
return 1;
// all other messages route through message map
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
ASSERT(pWnd != NULL);
ASSERT(pWnd==NULL || pWnd->m_hWnd == hWnd);
if (pWnd == NULL || pWnd->m_hWnd != hWnd)
return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
}
*/
LRESULT CALLBACK CallWndProc(int nCode, // hook code
WPARAM wParam, // current-process flag
LPARAM lParam // message data
){
static WNDPROC s_WndProc = NULL;
PCWPSTRUCT pInfo = (PCWPSTRUCT)lParam;
if (pInfo->message == WM_USER+1)
{
HWND hWnd = (HWND)wParam;
if (IsWindowUnicode(pInfo->hwnd))
{
//32位 64位都可以拿
s_WndProc = (WNDPROC)GetWindowLongPtrW(pInfo->hwnd, GWLP_WNDPROC);
}
else
{
s_WndProc = (WNDPROC)GetWindowLongPtrA(pInfo->hwnd, GWLP_WNDPROC);
}
unsigned char* pCode = (unsigned char*)s_WndProc;
if (*pCode == 0x55)
{
//找到call 第一个call是不是找到E8就可以了
while (*pCode != 0xE8) //位置找错的话有风险
{
pCode++;
}
FromHandlePermanent pfnFromHandlePermanent = (FromHandlePermanent)(pCode + *(DWORD*)(pCode + 1) + 5);
CWnd *pWnd = pfnFromHandlePermanent(pInfo->hwnd);
CRuntimeClass *pRuntimeClass = pWnd->GetRuntimeClass();
char bufff[255] = {};
while (pRuntimeClass != NULL)
{
sprintf(bufff, "%s 0x%p \n", pRuntimeClass->m_lpszClassName, pRuntimeClass->m_pfnCreateObject);
OutputDebugString(bufff);
if (!strcmp(pRuntimeClass->m_lpszClassName,"CWnd"))
{
::MessageBox(NULL, pRuntimeClass->m_lpszClassName, NULL, NULL);
}
pRuntimeClass = pRuntimeClass->m_pBaseClass;
}
/*
DWORD* pVFunc = *(DWORD**)pCWnd;
while (*pVFunc)
{
g_SpyInfo.vTable[g_SpyInfo.n_numOfVFunc] = *pVFunc;
g_SpyInfo.n_numOfVFunc++;
pVFunc++;
}
// 5.获取消息映射表,先得到GetMessageMap在虚表中的位置(与版本,编译选项相关)
CCmdTarget obj;
CCmdTarget* pCmdTarget = &obj;
//修改虚表位置,让GetMessageMap成为第一项
*(int*)pCmdTarget = *(int*)pCWnd + 12 * sizeof(uintptr_t);
AFX_MSGMAP* pMsgMap = pCmdTarget->GetMessageMap();
AFX_MSGMAP_ENTRY* pEntry = pMsgMap->lpEntries;
while (pEntry != NULL && pEntry->nSig != 0)
{
g_SpyInfo.g_MsgMap[g_SpyInfo.g_nMsgEntry] = *pEntry;
pEntry++;
g_SpyInfo.g_nMsgEntry++;
}
*/
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
void SetHook(HWND hWnd)
{
if (IsWindowUnicode(hWnd))
{
g_hHook = SetWindowsHookExW(WH_CALLWNDPROC, &CallWndProc, g_hInstance, 0);
}
else
{
g_hHook = SetWindowsHookExA(WH_CALLWNDPROC, &CallWndProc, g_hInstance, 0);
}
}
void UnSetHook()
{
UnhookWindowsHookEx(g_hHook);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
g_hInstance = hModule;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}