关键字: 微信、duilib库hook,duilib模拟点击。
最近有朋友问我libcef如何注入js,然后又谈到微信用了libcef云云。
捣腾了一下午,知道怎么注入js后,发现朋友是需要auto control ui
瞬间崩溃。。。。
诚然微信有些浏览器功能用到了cef,但是主界面是duilib写的
跟cef没毛关系,(libcef封装到qbcore.dll里面,想注入js需要hook libcef的导出函数
拿到browser,替换callback……)
话题回到pc端微信怎么auto control ui,以及如何拿到”控件“上面的信息
下面为原理截图,看不懂请参考duilib源码
CWindowWnd 指针可以通过GetWindowLongPtr获取
CWindowWnd指针 == WindowImplBase指针
然后通过
static_cast(pWinImplBase)
转换得到INotifyUI指针,再通过虚函数地址表就能找到Notify指针
最后替换Notify指针就能监控到所有duilib事件了
事件结构体,pSender是事件的触发者,我们后面通过获取root节点就能得到所有CControlUI
这个结构我们可以伪造出来,然后直接调用原始Notify回调就能模拟界面操作了
每一个xml创建CControlUI 树的时候,都会调用CDialogBuilder::Create
我们通过hook这个函数,返回就能得到root节点
这里是duilib的container节点原理,如果父亲是一个容器节点,就把自己添加到父亲里面
得到节点树之后,我们需要枚举所有节点
看看这些虚函数是不是很可爱,很方便,有没有想到com技术
这个虚函数更可爱,哈哈
可爱到我不需要判断数量,只需要判断结果是否为NULL就能知道是否枚举结束
原理图都贴了,最好自己看看duilib源码,pc端微信界面是duilib写的,基本的虚函数偏移没有变化,说明腾讯没有大改动duilib(添加虚函数)
当然,我发现有些结构被改动过,这里就不提了。
关于duilib的消息流动,自己可以好好看看源码。
以下为部分代码,自行完成完整版
//hook CDialogBuilder::Create
/*
CControlUI* CDialogBuilder::Create(IDialogBuilderCallback* pCallback, CPaintManagerUI* pManager, CControlUI* pParent)
*/
VOID WINAPI CDialogBuilder_Create_Ret(CControlUI* pControlUI)
{
TRACE_LOG_INFO(_T("CDialogBuilder::Create Ret ..."));
if (pControlUI)
enumContainer(pControlUI);
}
//遍历控件的信息
void enumContainer(CControlUI * pControlUI)
{
LPCTSTR lpcszClass = pControlUI->GetClass();
LPCTSTR lpcszName = pControlUI->GetName();
LPCTSTR lpcszText = pControlUI->GetText();
if (lpcszClass)
TRACE_LOG_INFO(_T("%s"), lpcszClass);
if (lpcszName)
TRACE_LOG_INFO(_T("%s"), lpcszName);
if (lpcszText)
TRACE_LOG_INFO(_T("%s"), lpcszText);
if (CDuiString(_T("session_list")) == pControlUI->GetName())
{
g_session_list = pControlUI;
}
if (CDuiString(_T("contact_list")) == pControlUI->GetName())
{
g_contact_list = pControlUI;
}
IContainerUI *pContainerUI = static_cast(pControlUI->GetInterface(_T("IContainer")));
if (pContainerUI)
{
int i = 0;
CControlUI * pControlIter = NULL;
while (pControlIter = pContainerUI->GetItemAt(i++))
{
enumContainer(pControlIter);
}
}
}
//hook event notify
class CHookNotify : INotifyUI
{
public:
typedef void(CHookNotify::*pfn_Notify)(TNotifyUI& msg);
void Notify(TNotifyUI& msg)
{
extern CHookNotify::pfn_Notify g_pfn_oldnotify;
return (this->*g_pfn_oldnotify)(msg);
}
};
CHookNotify::pfn_Notify g_pfn_oldnotify = NULL;
DWORD WINAPI test_thread(
LPVOID lpThreadParameter
)
{
MessageBox(NULL, NULL, NULL, MB_OK);
HWND hMainWnd = FindWindow(_T("WeChatMainWndForPC"), NULL);
if (!hMainWnd)
{
TRACE_LOG_INFO(_T("%s"), _T("FindWindow Error ..."));
return 0;
}
CWindowWnd *pThis = reinterpret_cast(::GetWindowLongPtr(hMainWnd, GWLP_USERDATA));
if (!pThis)
{
TRACE_LOG_INFO(_T("%s"), _T("Get CWindowWnd Pointer Error ..."));
return 0;
}
HWND *phWnd = reinterpret_cast(((uintptr_t)pThis + sizeof(uintptr_t)));
if (IsBadReadPtr(phWnd, sizeof(HWND)))
{
TRACE_LOG_INFO(_T("%s"), _T("Pointer Invalid Error ..."));
return 0;
}
if (*phWnd != hMainWnd)
{
TRACE_LOG_INFO(_T("%s"), _T("Invalid CWindowWnd Pointer Error ..."));
return 0;
}
//WindowImplBase * == CWindowWnd *
WindowImplBase *pWinImplBase = reinterpret_cast(pThis);
INotifyUI *pNotifyThis = static_cast(pWinImplBase);
CHookNotify::pfn_Notify *p_pfn_oldnotify = *reinterpret_cast(pNotifyThis);
//set virtual table hook
g_pfn_oldnotify = *p_pfn_oldnotify;
CHookNotify::pfn_Notify temp;
__asm push CHookNotify::Notify;
__asm pop temp;
DWORD dwOldProtect = 0;
VirtualProtect(p_pfn_oldnotify, sizeof(void *), PAGE_EXECUTE_READWRITE, &dwOldProtect);
*p_pfn_oldnotify = temp;
return 0;
}
最后来一张效果图
本文由看雪论坛 tongzeyu 原创 转载请注明来自看雪社区
作者:看雪学院
链接:https://www.jianshu.com/p/41952403ba24
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。