一
用逆向做点正向
MacOS版本的微信在2021年已经支持深色模式:
Windows版本的微信到2024年还没有深色模式,看到下面链接说到:眼睛畏光的低视力人一直在反馈。
期盼尽快更新Windows版本(PC版本)微信深色模式(暗色、黑暗、暗夜、黑夜模式(https://developers.weixin.qq.com/community/develop/article/doc/00082aeaf0ca4009b620ec37e6b813)
试着用逆向去实现Windows版本的微信也支持深色模式,发现微信其实隐藏了深色模式,可惜不完善,修补之后效果如下:
二
隐藏的深色模式
先试着用IDA搜素深色模式的关键词dark,发现确实有深色模式:‘weuithemedarktheme.xml’
交叉索引一下引用字符串的地方:
int sub_1F35D30()
sub_1F33C40(dword_32C5500, L"weui\theme\dark\theme.xml");
sub_1F33C40(dword_32C54F4, L"Theme\dark\theme_dark.xml");
再看下sub_1F35D30调用的地方,dword_32C5500便是深色模式的资源路径:
int sub_1F35D00()
if ( !dword_32C5500 ) sub_1F35D30();
return dword_32C5500;
再看下sub_1F35D00调用的地方,可以看出byte_32B5474控制是否用深色模式:
int __thiscall sub_1EF62E0(_DWORD *this)
if ( byte_32B5474 ) sub_1F35D00();
else sub_1F35CF0();
交叉索引byte_32B5474,有两个地方写入byte_32B5474:
01EBC2ED 8A47 61 mov al, [edi+61h]
01EBC2F2 A2 74542B03 mov byte_32B5474, al
01EBCCE3 C605 74542B03 00 mov byte_32B5474, 0
在X86dbg上修改一下汇编:
7A47C2ED | B0 01 | mov al,1 |
7A47C2EF | 90 | nop |
7A47CCE3 | C605 7454877B 01 | mov byte ptr ds:[7B875474],1
修改之后还是浅色模式,但个人名片从白色是黑色,说明深色模式有效且不完善。
三
深色模式的原理
为了了解为什么深色模式不完善,需要梳理一下深色模式的底层原理。
1、微信里面有两个主题模式:
浅色:themedefaulttheme.xml
深色:themedarktheme.xml
2、不同主题模式的颜色配置文件不同:
浅色:themedefaulttheme.xml
<!-- color -->
<IncludeTheme source="weui/Theme/default/colors.xml" />
深色:themedarktheme.xml
<!-- color -->
<IncludeTheme source="weui/Theme/dark/colors.xml" />
3、同id不同主题模式下,具体值不同:
浅色:themedefaulttheme.xml
<!-- color -->
<IncludeTheme source="weui/Theme/default/colors.xml" />
<!-- 文字颜色 -->
<Color id="Text_1" opacity="1" color="#161616" /> // 浅色模式的文字比较深
深色:themedarktheme.xml
<!-- color -->
<IncludeTheme source="weui/Theme/dark/colors.xml" />
<!-- 文字颜色 -->
<Color id="Text_1" opacity="1" color="#F7F7F7" /> // 深色模式的文字比较浅
4、界面的属性值改为从固定值变成变量值:
textcolor="#FF000000"
改成
textcolor="@color:Text_1"
可以看出微信的底层已经有一套更换模式的基建,而深色模式之所以不完善是因为深色资源文件不完整。
themedefaultcolors.xml 5150 字节
themedarkcolors.xml 4370 字节
四
修改界面的资源
如果要完善深色模式,就需要修改界面资源,先了解一下原生Duilib生成界面的流程。
1、界面生成从OnCreate开始:
LRESULT WindowImplBase::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
switch (GetResourceType())
case UILIB_ZIP:
m_pm.SetResourceZip(GetZIPFileName().GetData(), true);
CControlUI* pRoot = builder.Create(xml, _T("xml"), this, &m_pm);
2、builder.Create加载界面资源:
CControlUI* CDialogBuilder::Create(STRINGorID xml, LPCTSTR type, IDialogBuilderCallback* pCallback, CPaintManagerUI* pManager, CControlUI* pParent)
if( !m_xml.Load(xml.m_lpstr) ) return NULL;
if( !m_xml.LoadFromFile(xml.m_lpstr) ) return NULL;
if( !m_xml.LoadFromMem((BYTE*)::LockResource(hGlobal), ::SizeofResource(dll_instence, hResource) )) return NULL;
return Create(pCallback, pManager, pParent);
bool CMarkup::LoadFromMem(BYTE* pByte, DWORD dwSize, int encoding)
DWORD nWide = ::MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)pByte, dwSize, NULL, 0 );
::MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)pByte, dwSize, m_pstrXML, nWide );
3、最后是解析界面资源的内容:
CControlUI* CDialogBuilder::Create(IDialogBuilderCallback* pCallback, CPaintManagerUI* pManager, CControlUI* pParent)
CMarkupNode root = m_xml.GetRoot();
for( CMarkupNode node = root.GetChild() ; node.IsValid(); node = node.GetSibling() )
"linkhoverfontcolor"
根据这个流程,就可以定位微信Duilib对应的函数:
1、搜索关键词”linkhoverfontcolor”
int __thiscall sub_1EDDBE0(_DWORD *this, int pCallback, int pManager, int pParent)
"linkhoverfontcolor"
2、再查看sub_1EDDBE0的引用
int __thiscall sub_1EDDB80(_DWORD *this, wchar_t *Source, int a3, int a4, int a5, int a6)
wcXMLData = sub_1F20210(wcXMLPath, 1);
if ( wcXMLData && sub_1EE0BE0(this, *(wcXMLData + 0x2000), *(wcXMLData + 0x2004), v8) )
return sub_1EDDBE0(this, a4, a5, a6);
3、sub_1F20210就是CMarkup::LoadFromMem
把界面资源的路径(wcXMLPath)变成界面资源的内容(wcXMLData ):
char __thiscall sub_1EE0BE0(WCHAR **this, LPCCH lpMultiByteStr, unsigned int cbMultiByte, int a4)
v6 = MultiByteToWideChar(65001u, 0, v5, v4, 0, 0);// CP_UTF8
MultiByteToWideChar(65001u, 0, v5, cbMultiByte, v7, v6);
用Frida的js脚本拦截修改(需要修改很多地方),就可以实现深色模式:
const targetFunctionAddress = WeChatWin.add(0x1F20210);
Interceptor.attach(targetFunctionAddress,
...
ModifyAttribute(this.wsXMLDataArray, '', 'bkcolor', '#FF000000');
五
修改业务的代码
修改资源的属性,发现了一个问题:编辑框的文字属性修改无效。原本是是白色背景加黑色字体,现在是黑色背景加黑色字体,看不清。这就意味着:控件的属性,不是完全依赖界面资源,还需要修改业务代码。
1、修改字体颜色的接口是SetAttribute:
void CRichEditUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
if( _tcscmp(pstrName, _T("textcolor")) == 0 )
if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue);
DWORD clrColor = _tcstoul(pstrValue, &pstr, 16);
SetTextColor(clrColor);
void CRichEditUI::SetTextColor(DWORD dwTextColor)
m_pTwh->SetColor(dwTextColor);
void CTxtWinHost::SetColor(DWORD dwColor)
cf.crTextColor = RGB(GetBValue(dwColor), GetGValue(dwColor), GetRValue(dwColor));
pserv->OnTxPropertyBitsChange(TXTBIT_CHARFORMATCHANGE, TXTBIT_CHARFORMATCHANGE);
注入DLL调用SetAttribute确实能改变字体颜色,但再编辑编辑框RichEdit的文本,颜色又变成黑色。
DWORD pControl = 0x15a72bb8;
const wchar_t* wcAttr = L"textcolor";
const wchar_t* wcValue = L"#FFFFFFFF"; // 白色
SetAttribute(pControl, wcAttr, wcValue);
2、修改字体颜色的另一接口是SetSelectionCharFormat:
#define WM_USER 0x0400
#define EM_GETCHARFORMAT (WM_USER + 58) // 0x43A
#define EM_SETCHARFORMAT (WM_USER + 68) // 0x444
#define SCF_SELECTION 0x0001
DWORD CRichEditUI::GetSelectionCharFormat(CHARFORMAT2 &cf) const
cf.cbSize = sizeof(CHARFORMAT2);
TxSendMessage(EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf, &lResult);
bool CRichEditUI::SetSelectionCharFormat(CHARFORMAT2 &cf)
cf.cbSize = sizeof(CHARFORMAT2);
TxSendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf, &lResult);
HRESULT CRichEditUI::TxSendMessage(UINT msg, WPARAM wparam, LPARAM lparam, LRESULT *plresult) const
return m_pTwh->GetTextServices()->TxSendMessage(msg, wparam, lparam, plresult);
X86dbg对TxSendMessage下条件断点并打印参数:
发现编辑文本的时候调用了GetSelectionCharFormat:
#define EM_GETCHARFORMAT (WM_USER + 58) // 0x43A
CRichEditUI::TxSendMessage this=15AA51E0 msg=43A wparam=1
添加暂停条件:[esp+4]==0x43A,看下哪里调用GetSelectionCharFormat:
发现确实调用了SetSelectionCharFormat:
#define EM_GETCHARFORMAT (WM_USER + 58) 0x43A
#define EM_SETCHARFORMAT (WM_USER + 68) 0x444
#define SCF_SELECTION 0x0001
int __thiscall sub_1F02FA0(int *this, _DWORD *pColor, int dwBegin, int dwEnd)
// GetSelectionCharFormat
(*(*this + 724))(this, 0x43A, 1, pcf, v10);
// SetSelectionCharFormat
(*(*this + 724))(this, 0x444, 1, pcfNew, v10);
再看下调用sub_1F02FA0的地方,发现微信在代码上直接写死成黑色:
void __thiscall sub_9FF580(int *this)
NewColor(pColor, 0xFF000000); // 写死黑色
sub_1F02FA0(this, pColor, 0, TextLength);
再看下调用sub_9FF580的上级函数,发现确实是有更新的时候修改字体颜色。
void __thiscall sub_01F03D70(char *this, unsigned int iNotify, int a3)
// #define EN_UPDATE 0x0400
if ( iNotify == 0x400 )
//GetManager()->SendNotify(this, DUI_MSGTYPE_TEXTCHANGED);
sub_1EC0390(*(this + 22), this, L"textchanged", 0, 0, this[3820]);
(*(*this + 740))(this); // sub_9FF580
简单的绕过方法就是修改一个字节:0x400改变成0x401:
7A4C3DBE | 81FB 00040000 | cmp ebx,400 |
改成
7A4C3DBE | 81FB 01040000 | cmp ebx,401
看雪ID:GhHei
https://bbs.kanxue.com/homepage-1000535.htm
# 往期推荐
4、HITCON CTF 2024 re AntiVirus wp clamav bytecode signature逆向
球分享
球点赞
球在看
点击阅读原文查看更多
原文始发于微信公众号(看雪学苑):修补微信Windows隐藏的深色模式