【软件逆向-修补微信Windows隐藏的深色模式】此文章归类为:软件逆向。
用逆向做点正向
macOS版本的微信在2021年已经支持深色模式
Windows版本的微信到2024年还没有深色模式
看到下面链接说到:眼睛畏光的低视力人一直在反馈
期盼尽快更新Windows版本(PC版本)微信深色模式(暗色、黑暗、暗夜、黑夜模式)
试着用逆向去实现Windows版本的微信也支持深色模式
发现微信其实隐藏了深色模式,可惜不完善,修补之后效果如下
隐藏的深色模式
先试着用IDA搜素深色模式的关键词dark
发现确实有深色模式:'weui\theme\dark\theme.xml'
交叉索引一下引用字符串的地方
1 2 3 | 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便是深色模式的资源路径
1 2 3 | int sub_1F35D00()
if ( !dword_32C5500 ) sub_1F35D30();
return dword_32C5500;
|
再看下sub_1F35D00调用的地方
可以看出byte_32B5474控制是否用深色模式
1 2 3 | int __thiscall sub_1EF62E0(_DWORD * this )
if ( byte_32B5474 ) sub_1F35D00();
else sub_1F35CF0();
|
交叉索引byte_32B5474,有两个地方写入byte_32B5474
1 2 3 4 | 01EBC2ED 8A47 61 mov al, [edi+61h]
01EBC2F2 A2 74542B03 mov byte_32B5474, al
01EBCCE3 C605 74542B03 00 mov byte_32B5474, 0
|
在X86dbg上修改一下汇编
1 2 3 4 | 7A47C2ED | B0 01 | mov al,1 |
7A47C2EF | 90 | nop |
7A47CCE3 | C605 7454877B 01 | mov byte ptr ds:[7B875474],1
|
修改之后还是浅色模式
但个人名片从白色是黑色
说明深色模式有效且不完善
深色模式的原理
为了了解为什么深色模式不完善
需要梳理一下深色模式的底层原理
1、微信里面有两个主题模式
1 2 3 | 浅色:theme\ default \theme.xml
深色:theme\dark\theme.xml
|
2、不同主题模式的颜色配置文件不同
1 2 3 4 5 6 7 | 浅色:theme\ default \theme.xml
<!-- color -->
<IncludeTheme source= "weui/Theme/default/colors.xml" />
深色:theme\dark\theme.xml
<!-- color -->
<IncludeTheme source= "weui/Theme/dark/colors.xml" />
|
3、同id不同主题模式下,具体值不同
1 2 3 4 5 6 7 8 9 10 11 | 浅色:theme\ default \theme.xml
<!-- color -->
<IncludeTheme source= "weui/Theme/default/colors.xml" />
<!-- 文字颜色 -->
<Color id= "Text_1" opacity= "1" color= "#161616" />
深色:theme\dark\theme.xml
<!-- color -->
<IncludeTheme source= "weui/Theme/dark/colors.xml" />
<!-- 文字颜色 -->
<Color id= "Text_1" opacity= "1" color= "#F7F7F7" />
|
4、界面的属性值改为从固定值变成变量值
1 2 3 | textcolor= "#FF000000"
改成
textcolor= "@color:Text_1"
|
可以看出微信的底层已经有一套更换模式的基建
而深色模式之所以不完善是因为深色资源文件不完整
1 2 | theme\default\colors.xml 5150 字节
theme\dark\colors.xml 4370 字节
|
修改界面的资源
如果要完善深色模式,就需要修改界面资源
先了解一下原生Duilib生成界面的流程
1、界面生成从OnCreate开始
1 2 3 4 5 6 | 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加载界面资源
1 2 3 4 5 6 7 8 9 | 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、最后是解析界面资源的内容
1 2 3 4 | 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"
1 2 | int __thiscall sub_1EDDBE0(_DWORD * this , int pCallback, int pManager, int pParent)
"linkhoverfontcolor"
|
2、再查看sub_1EDDBE0的引用
1 2 3 4 | 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 )
1 2 3 | char __thiscall sub_1EE0BE0( WCHAR ** this , LPCCH lpMultiByteStr, unsigned int cbMultiByte, int a4)
v6 = MultiByteToWideChar(65001u, 0, v5, v4, 0, 0);
MultiByteToWideChar(65001u, 0, v5, cbMultiByte, v7, v6);
|
用Frida的js脚本拦截修改(需要修改很多地方),就可以实现深色模式
1 2 3 4 | const targetFunctionAddress = WeChatWin.add(0x1F20210);
Interceptor.attach(targetFunctionAddress,
...
ModifyAttribute( this .wsXMLDataArray, '' , 'bkcolor' , '#FF000000' );
|
修改业务的代码
修改资源的属性,发现了一个问题:编辑框的文字属性修改无效
原本是是白色背景加黑色字体,现在是黑色背景加黑色字体,看不清
这就意味着:控件的属性,不是完全依赖界面资源,还需要修改业务代码
1、修改字体颜色的接口是SetAttribute
1 2 3 4 5 6 7 8 9 10 11 12 | 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的文本,颜色又变成黑色
1 2 3 4 | DWORD pControl = 0x15a72bb8;
const wchar_t * wcAttr = L "textcolor" ;
const wchar_t * wcValue = L "#FFFFFFFF" ;
SetAttribute(pControl, wcAttr, wcValue);
|
2、修改字体颜色的另一接口是SetSelectionCharFormat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #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
1 2 | #define EM_GETCHARFORMAT (WM_USER + 58) // 0x43A
CRichEditUI::TxSendMessage this =15AA51E0 msg=43A wparam=1
|
添加暂停条件:[esp+4]==0x43A
看下哪里调用GetSelectionCharFormat
发现确实调用了SetSelectionCharFormat
1 2 3 4 5 6 7 8 | #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)
(*(* this + 724))( this , 0x43A, 1, pcf, v10);
(*(* this + 724))( this , 0x444, 1, pcfNew, v10);
|
再看下调用sub_1F02FA0的地方
发现微信在代码上直接写死成黑色
1 2 3 | void __thiscall sub_9FF580( int * this )
NewColor(pColor, 0xFF000000);
sub_1F02FA0( this , pColor, 0, TextLength);
|
再看下调用sub_9FF580的上级函数
发现确实是有更新的时候修改字体颜色
1 2 3 4 5 6 | void __thiscall sub_01F03D70( char * this , unsigned int iNotify, int a3)
if ( iNotify == 0x400 )
sub_1EC0390(*( this + 22), this , L "textchanged" , 0, 0, this [3820]);
(*(* this + 740))( this );
|
简单的绕过方法就是修改一个字节:0x400改变成0x401
1 2 3 | 7A4C3DBE | 81FB 00040000 | cmp ebx,400 |
改成
7A4C3DBE | 81FB 01040000 | cmp ebx,401
|
最后于 2天前
被GhHei编辑
,原因:
更多【软件逆向-修补微信Windows隐藏的深色模式】相关视频教程:www.yxfzedu.com