articles
articles copied to clipboard
看雪腾讯ctf第五题(待完善)
writeup大纲
- GetMessageMap函数寻找
- int 2d处理 seh exception handler
- 大整数结构体
- 大整数之间的进制转换
- 矩阵转换 相乘 魔方转化 矩阵是不是也可以使用结构体 看到了有使用结构体解决
- ecc椭圆曲线加密算法
- 得到答案
GetMessageMap处理函数寻找
通过MessageBox寻找
通过od2 设置MessageBox
断点,断下来后,我们来看调用栈(这里比较了od1,od2,x32dbg,最终还是od2显示的效果最好!)
之后跟进每一个函数查看函数大致功能,因为真正的处理函数距离MessageBox
不会太远
函数40B1A2
可以看到该函数中MessageBox
所有的参数都已经确定了,再往前翻
函数40B71C
可以发现失败
这一状态,在这个函数前已经确认了,再往前翻:
函数4071FD
可以发现,真正的确认状态的函数就是4071FD
,成功找到MessageMap处理函数
通过od1 字符串智能搜索
直接搜到处理函数
通过查找GetMessageMap函数
介绍该方法时,首先要了解两方面的知识:
- c++的虚函数表相关知识
- MFC消息处理机制
c++ 虚函数表相关知识
c++普遍使用类,虚拟类派生的类都会维护一个虚函数表vtable
,并且编译器在编译时会将虚函数表插入到生成的二进制文件中,而且一般存在rodata
区块中。该类的每一个实例(对象)会维护一个vptr
,即指向虚函数表的指针。大概的空间布局是这样的
类中数据布局 |
---|
vptr虚表指针 |
基类数据 |
类成员 |
MFC消息处理机制
首先,我们先对MFC的消息映射做一个简单介绍。MFC为了实现消息映射在响应消息的类内部自动做了如下两方面的处理:
a、消息映射声明和实现 在类的定义(头文件)里,添加声明消息映射的宏DECLARE_MESSAGE_MAP,在类的实现(源文件)里,通过BEGIN_MESSAGE_MAP和END_MESSAGE_MAP()实现消息映射。 b、消息响应函数的声明和实现
当通过ClassWizard添加消息响应函数时就会自动添加函数的声明和实现,代码如下: 声明:
//{{AFX_MSG
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnPaint();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
映射:
BEGIN_MESSAGE_MAP(CTestDialog, CDialog)
//{{AFX_MSG_MAP(CTestDialog)
ON_WM_TIMER()
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
实现:
void CTestDialog::OnPaint()
{
}
void CTestDialog::OnTimer(UINT nIDEvent)
{
CDialog::OnTimer(nIDEvent);
}
void CTestDialog::OnPaint()
{
}
void CTestDialog::OnTimer(UINT nIDEvent)
{
CDialog::OnTimer(nIDEvent);
}
简单点说就是MFC
的类在声明时会调用DECLARE_MESSAGE_MAP()
声明消息映射,在生成对象后会映射AFX_MSG_MAP
。
MFC
源码大概是这样的
class CreverseApp : public CWinApp
{
public:
CreverseApp();
// Overrides
public:
virtual BOOL InitInstance();
// Implementation
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CreverseApp, CWinApp)
ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
// CreverseApp construction
CreverseApp::CreverseApp()
{
// support Restart Manager
m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
// The one and only CreverseApp object
CreverseApp theApp;
MFC
就是靠这样的机制来处理程序的整个消息的,现在需要理清的就是消息和消息处理函数的映射,其中涉及到主要的两个结构体:
struct AFX_MSGMAP
{
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)(); // 基类消息映射入口地址
const AFX_MSGMAP_ENTRY* lpEntries; // 当前类消息映射入口地址
};
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)
};
其中AFX_MSGMAP
由GetMessageMap
函数获取,其最后一个成员直接指向映射关系数组。GetMessagemap
函数是个虚函数,每个实现的类的虚函数表都有该函数,我们需要找到对应的窗口类或是对话框类的GetMessageMap
函数。而最终的目标就是找到这个映射关系数组,好方便找到对应的消息处理函数
ida中实践寻找映射关系
首先找到MFC
开始函数,大概是这样的
.text:00401CBB public start
.text:00401CBB call ___security_init_cookie
.text:00401CC0 jmp ___tmainCRTStartup
.text:004019FB ___tmainCRTStartup proc near ; CODE XREF: start+5j
.text:004019FB
.text:004019FB push 5Ch
.text:004019FD push offset unk_403DD8
.text:00401A02 call __SEH_prolog4
;... other initialization code
.text:00401B3E push ecx ; nShowCmd
.text:00401B3F push eax ; lpCmdLine
.text:00401B40 push ebx ; hPrevInstance
.text:00401B41 push 400000h ; hInstance
.text:00401B46 call _wWinMain@16 ; wWinMain(x,x,x,x)
; int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
_wWinMain@16 proc near
jmp ?AfxWinMain@@YGHPAUHINSTANCE__@@0PA_WH@Z ; AfxWinMain(HINSTANCE__ *,HINSTANCE__ *,wchar_t *,int)
_wWinMain@16 endp
再来看看AfxWinMain
的源代码
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
// AFX internal initialization
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;
// App global initializations (rare)
if (pApp != NULL && !pApp->InitApplication())
goto InitFailure;
// Perform specific initializations
if (!pThread->InitInstance())
{
if (pThread->m_pMainWnd != NULL)
{
TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode = pThread->ExitInstance();
goto InitFailure;
}
nReturnCode = pThread->Run();
InitFailure:
AfxWinTerm();
return nReturnCode;
}
不同版本的MFC
可能会有所区别,但是整体的结构基本不会变,再来对比一下第五题的反汇编代码
; int __stdcall AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
.text:00440E44 _wWinMain@16 proc near ; CODE XREF: ___tmainCRTStartup+115↑p
.text:00440E44
.text:00440E44 hInstance = dword ptr 8
.text:00440E44 hPrevInstance = dword ptr 0Ch
.text:00440E44 lpCmdLine = dword ptr 10h
.text:00440E44 nShowCmd = dword ptr 14h
.text:00440E44
.text:00440E44 ; FUNCTION CHUNK AT .text:00417073 SIZE 00000020 BYTES
.text:00440E44
.text:00440E44 mov edi, edi
.text:00440E46 push ebp
.text:00440E47 mov ebp, esp
.text:00440E49 pop ebp
.text:00440E4A jmp loc_440E62
.text:00440E4F ; ---------------------------------------------------------------------------
.text:00440E4F
.text:00440E4F ; void __cdecl loc_440E4F()
.text:00440E4F loc_440E4F: ; DATA XREF: sub_443CAE+5↓o
.text:00440E4F push 1
.text:00440E51 push 0
.text:00440E53 call sub_417001
.text:00440E58 call sub_417806
.text:00440E5D jmp loc_417073
.text:00440E62 ; ---------------------------------------------------------------------------
.text:00440E62
.text:00440E62 loc_440E62: ; CODE XREF: wWinMain(x,x,x,x)+6↑j
.text:00440E62 mov edi, edi
.text:00440E64 push ebp
.text:00440E65 mov ebp, esp
.text:00440E67 push ebx
.text:00440E68 push esi
.text:00440E69 push edi
.text:00440E6A or ebx, 0FFFFFFFFh
.text:00440E6D call AfxGetModuleThreadState
.text:00440E72 mov esi, eax ; pthread
.text:00440E74 call AfxGetModuleState
.text:00440E79 push [ebp+nShowCmd]
.text:00440E7C mov edi, [eax+4]
.text:00440E7F push [ebp+lpCmdLine]
.text:00440E82 push [ebp+hPrevInstance]
.text:00440E85 push [ebp+hInstance]
.text:00440E88 call AfxWinInit
.text:00440E8D test eax, eax
.text:00440E8F jz short loc_440ECD
.text:00440E91 test edi, edi
.text:00440E93 jz short loc_440EA3
.text:00440E95 mov eax, [edi]
.text:00440E97 mov ecx, edi // edi存储CXXApp对象指针,this
.text:00440E99 call dword ptr [eax+0ACh] ; pApp->InitApplication
.text:00440E9F test eax, eax
.text:00440EA1 jz short loc_440ECD
.text:00440EA3
.text:00440EA3 loc_440EA3: ; CODE XREF: wWinMain(x,x,x,x)+4F↑j
.text:00440EA3 mov eax, [esi]
.text:00440EA5 mov ecx, esi
.text:00440EA7 call dword ptr [eax+50h] ; pThread->InitInstance
.text:00440EAA test eax, eax
.text:00440EAC jnz short loc_440EC4
.text:00440EAE cmp [esi+20h], eax .text:00440EB1 jz short loc_440EBB
.text:00440EB3 mov ecx, [esi+20h]
.text:00440EB6 mov eax, [ecx]
.text:00440EB8 call dword ptr [eax+60h]
.text:00440EBB
.text:00440EBB loc_440EBB: ; CODE XREF: wWinMain(x,x,x,x)+6D↑j
.text:00440EBB mov eax, [esi]
.text:00440EBD mov ecx, esi
.text:00440EBF call dword ptr [eax+68h]
.text:00440EC2 jmp short loc_440ECB
.text:00440EC4 ; ---------------------------------------------------------------------------
.text:00440EC4
.text:00440EC4 loc_440EC4: ; CODE XREF: wWinMain(x,x,x,x)+68↑j
.text:00440EC4 mov eax, [esi]
.text:00440EC6 mov ecx, esi
.text:00440EC8 call dword ptr [eax+54h]
.text:00440ECB
.text:00440ECB loc_440ECB: ; CODE XREF: wWinMain(x,x,x,x)+7E↑j
.text:00440ECB mov ebx, eax
.text:00440ECD
.text:00440ECD loc_440ECD: ; CODE XREF: wWinMain(x,x,x,x)+4B↑j
.text:00440ECD ; wWinMain(x,x,x,x)+5D↑j
.text:00440ECD call sub_41776C
.text:00440ED2 pop edi
.text:00440ED3 pop esi
.text:00440ED4 mov eax, ebx
.text:00440ED6 pop ebx
.text:00440ED7 pop ebp
.text:00440ED8 retn 10h
.text:00440ED8 _AfxWinMain@16 endp ;
这里的InitInstance
很重要,其中有CXXDialog
的指针,顺着这个指针,我们能够找到第五题中涉及到的窗口处理类的虚函数表
可以先大概看一下InitInstance
在MFC
中的源码
start -> winmain -> AfxWinMain -> CXXAPP -> CXXDialog