WeChat PC terminal technology research - how to find the message sending interface by anhkgg (official account: Han Ke Er) February 18, 2019
0x0. Preface
Preparation tools: cheat engine, OllyDbg, IDA.
The previous article (Research on wechat PC technology (2) - save chat voice) has said what CE is, and also applied CE to study how to save wechat voice. This article continues to use CE and OD to study the message sending interface of wechat.
The idea is as follows: after entering the content in the message box, find the content address through CE, and then find the relevant code to send the data through the memory breakpoint, so as to find the message sending interface.
0x2. Analysis process
Find key data address
After entering a special text content in the input box (to avoid too much memory when searching), use CE to search the content address.
Since the exact message content has been known, it is easy to find the content address through CE's exact value - > string, modify the content for multiple filtering, and leave two results (see the previous article for details).
Modify the content of the memory through CE, and the content in the wechat input box changes synchronously, indicating that this memory address is the content address in the input box, and the final confirmed address is 2a1e1a8.
2A1E1A8
After clicking the send button, the content of the input box will be cleared, so the first idea is to write a breakpoint to the memory under the memory address, and you can find the code to clear the content during the sending process.
Open OD, mount to wechat.exe process, enter 2a1e1a8 in the data window at the bottom right corner Ctrl + G, and then right-click to select breakpoint - > memory write breakpoint.
2A1E1A8
F9 let od run, and then click the wechat send button. Unexpectedly, the input box is empty, but the breakpoint is not triggered.
What's going on? Wrong breakpoint? Wrong address? There is no answer for the time being.
Repeat the previous operation with CE for many times. The address is still the address. The breakpoint is not triggered.
Through OD, it can be seen that after the input box is cleared, the content of 2a1e1a8 does not change, just like before sending, and after re entering the new content, the memory content is updated synchronously.
2A1E1A8
So the conclusion is that the content address of the input box is indeed 2a1e1a8, but clearing the input box does not empty the memory content. It is speculated that the edit box may control the display by controlling the length of the string. Clearing the content of the input box is to set the string length to 0.
2A1E1A8
Find input box class
Clearing the input box has not progressed, so what should I do?
Try to find other data, such as sending button (s), sending button's prompt content can't send blank content, etc. the data address can also be found quickly, but it's too far away from our analysis target.
发送(S)
不能发送空白内容
After a few twists and turns, let go.
Think about it for a second. It's not good to clear. Send the contents of the total read input box. Try using the memory access breakpoint instead.
Still, in the data window at the bottom right corner, Ctrl + G, enter 2a1e1a8, then right-click to select breakpoint - > memory access breakpoint.
2A1E1A8
After that, I went back to the wechat interface. Unexpectedly, it was disconnected. I haven't clicked the send button yet. According to previous experience, the subconscious thought that the interface refresh display text triggered the breakpoint, which may affect the analysis, and there is no way to trigger the memory access breakpoint by sending the button at all.
The general solutions are:
- Conditional breakpoint. That is to say, the breakpoint triggered by the refresh interface is shielded, but it seems that the memory breakpoint does not support the conditional breakpoint, or it can be completed by script, which is troublesome.
Conditional breakpoint. That is to say, the breakpoint triggered by the refresh interface is shielded, but it seems that the memory breakpoint does not support the conditional breakpoint, or it can be completed by script, which is troublesome.
- Find other entry points. Nonsense, the road over there is broken. Please give up.
Find other entry points. Nonsense, the road over there is broken. Please give up.
- Other things I don't know
Other things I don't know
Having given up the general solution, I decided to see what happened to this breakpoint.
Note that the breakpoint is not in wechat module wechatwin.dll, but in msftedit.dll, a rare module. According to the directory, it can be seen that it is a module of Microsoft system, and the edit in the name can also be seen that it should be an edit box related module.
WeChatWin.dll
msftedit.dll
edit
可执行模块, 条目 20
基址=6F050000
大小=00094000 (606208.)
入口=6F05D53D msftedit.<ModuleEntryPoint>
名称=msftedit (系统)
文件版本=5.41.21.2510
路径=C:\Windows\System32\msftedit.dll
It seems to be very close to our analysis goal. In OD, right-click the data window breakpoint - > delete the memory breakpoint, and then press Alt + F9 to return to the user module airspace, that is, skip the system module code, and directly return to the wechat module code to save the analysis of the system code.
See the return to 6e20ccc2, the last line of code is to call the function of msftedit.dll. For its next breakpoint, click 6e20ccbf, press F2 next int 3 breakpoint, and then F9 to skip this analysis.
6E20CCC2
msftedit.dll
6E20CCBF
F2
int 3
F9
Od continues to break. This time, it directly breaks at 6e20ccbf. You can see that call calls msftedit.6f05ad69. What function is this?
6E20CCBF
call
msftedit.6F05AD69
Since msftedit.dll is a Microsoft module, it must be signed, hehe.
msftedit.dll
Here you can directly load symbols in OD for analysis. The method of use is:
1.在WingDbg目录下拷贝dbgeng.dll,dbghelp.dll,srcsrv.dll,symbolcheck.dll,symsrv.dll,symsrv.yes,一共6个文件至OD目录下。
2.打开OD,设置符号路径。调试--->选择符号路径。
3.设置StrongOD的插件选项。选择加载符号。
原文:https://blog.csdn.net/sr0ad/article/details/8253311
But it only supports local symbols, that is to say, you have to download the corresponding symbols of the module to the local, OD sets the symbol file path, and then it can be used normally, which is a bit troublesome.
I usually use IDA at this time, because it will download the symbols corresponding to the module online, which is very convenient.
Open msftedit.dll with IDA, wait for some time, IDA download symbols, parse and so on. After that, we will find out what the corresponding function of msftedit.6f05ad69 is.
msftedit.dll
msftedit.6F05AD69
But here the base address of msftedit.6f05ad69 module is 6f050000, and the default base address 0x6fcd0000 is used for IDA resolution. Either modify the base address of IDA resolution to 6f050000, wait for IDA to resolve again, or calculate the corresponding address by offset.
msftedit.6F05AD69
6F050000
0x6FCD0000
6F050000
It's too long to parse again. Just calculate directly. So I need a small tool (offset calculation tool) written by Amway to calculate the address quickly. Please refer to the relevant articles for specific use.
Press g in IDA, input 6fcdad69, and find the corresponding function of msftedit.6f05ad69 is ctxtedit:: ontxinplaceactivate.
6fcdad69
msftedit.6F05AD69
CTxtEdit::OnTxInPlaceActivate
Obviously, through the name ontxinplaceactivate, it can be seen that the function will be triggered when the text in the edit box is activated (displayed), which is not the point.
OnTxInPlaceActivate
Focus on ctxtedit, it is self-evident that this is the class of edit box implemented in msftedit.dll.
CTxtEdit
msftedit.dll
If you have written MFC related codes, you should soon think that ctxtedit must have other functions for reading and writing content, called getxxx or setXXX.
MFC
CTxtEdit
GetXXX
SetXXX
Check the function list of IDA, and you will find ctxtedit:: gettextex and ctxtedit:: settext soon.
CTxtEdit::GetTextEx
CTxtEdit::SetText
But are these two functions the functions of editing box reading and writing? Let's try the breakpoints of these two functions. Through the tool, we find that the corresponding addresses of these two functions in ID debugging are 6f068437 and 6f056d37.
6f068437
6f056d37
Enter BP 6f068437 and BP 6f056d37 in the command window at the bottom of OD, delete the previous breakpoint of ctxtedit:: ontxinplaceactivate, and then F9 runs.
bp 6f068437
bp 6f056d37
CTxtEdit::OnTxInPlaceActivate
F9
Return to the wechat interface, which can be displayed normally this time. Click the send button. Od triggers the breakpoint, which is 6f068437, ctxtedit:: gettextex. It is obvious that the sending function is reading the input box.
6f068437
CTxtEdit::GetTextEx
Trace back to find the sending function
The call stack is as follows:
调用堆栈
地址 堆栈 函数过程 / 参数 调用来自 结构
0026E280 6F06842D msftedit.6F068437 msftedit.6F068428 0026E3FC //CTxtEdit::GetTextEx
0026E400 6E20D239 包含msftedit.6F06842D WeChatWi.6E20D233 0026E3FC
0026E43C 6DBD38EB 包含WeChatWi.6E20D239 WeChatWi.6DBD38E8 0026E438 //TxtEdit_GetText
0026E5AC 6DC15B65 ? WeChatWi.6DBD3860 WeChatWi.6DC15B60 0026E5A8 //sendBtn_GetText
0026E60C 6DC15DEE WeChatWi.6DC15B10 WeChatWi.6DC15DE9 0026E608 //sendbtn_click
0026E618 6E20BFB8 WeChatWi.6E20BEF4 WeChatWi.6E20BFB3 0026E614
0026E62C 6E20362E WeChatWi.6E20BF90 WeChatWi.6E203629 0026E628
0026E6CC 6E203589 WeChatWi.6E2035A7 WeChatWi.6E203584 0026E6C8
0026E820 6DC53695 ? WeChatWi.6E20352E WeChatWi.6DC53690 0026E81C
Backtrack the call stack trace in OD and return it to wechatwi.6e20d239. You can see that the right stack window has obtained the content in the input box, which proves that the previous analysis has no problem.
WeChatWi.6E20D239
Go back two layers to wechatwi.6dc15b60 again, and you can see that the parameters in the stack are still the input box content obtained.
WeChatWi.6DC15B60
[0026E5E4] = 0828C070
[0828C070 + 4] = 0828CAF0 => a12bcAAAAA
At this time, the first address of the function is wechatwi.6dc15b10. Enter the corresponding function 100d5b10 in IDA (why do you want to ask me to enter IDA to view it at this time? I can only say that in fact, this step takes a lot of time, i.e. od debugging, IDA auxiliary confirmation, etc., the process is not so smooth, the reason for the space is omitted), then press X to return to the upper function, and see the following code:
WeChatWi.6DC15B10
IDA
100d5b10
It is obvious from the click that this is the response function of the send button (relevant knowledge can understand the duilib programming, and the wechat interface is implemented by duilib).
click
duilib
duilib
Up to now, we have found the function to send messages, but it is not the message sending interface, it is only the operation function of the interface, and the specific message sending interface should be called inside the function.
Have skills to find the sending interface
First, roughly follow the code logic of WeChatWi.6DC15B10 in the OD. There are many functions. It is impossible to quickly confirm which function is the message sending interface.
WeChatWi.6DC15B10
Take part of the code and feel about 11 functions. According to the logic of OD trace, it is probably sendbtn > gettext > sub > sub > 100dd340 > sub > 100c50c0 > sub > 10094100 > sub > 100dd9d0 > sub > 100c4450 > sub > 100de120.
sendBtn_GetText_10093860
sub_100DD340
sub_100C50C0
sub_10094100
sub_100DD9D0
sub_100C4450
sub_10323DF0
sub_100DE120
if ( sendBtn_GetText_10093860(a1->unk_560, (int)&savedregs, a2, a3, msg) <= 0 )// 这里是获取msg
{ // x
//省略一大段逻辑
}
if ( msg[0] != msg[1] )
{ // x
//省略一大段逻辑
}
if ( sub_100DD340() )
{ // x
//省略一大段逻辑
sub_1047C070(&v34, v23);
sub_100DB8C0((int)a1_, v34, v35, (int)v36, v37, (int)v38, v39, v40, (int)v41, msg_);
}
if ( sub_100C50C0((_DWORD *)(a1_->unk_558 + 2528), (int)msg, (int)v43) )
{
sub_10094100((_DWORD *)a1_->unk_560);//
sub_100DD9D0(msg);
sub_100C4450((_DWORD *)(a1_->unk_558 + 2528), (_msg *)msg);//
v31 = sub_10323DF0();
sub_100DE120(v31, (int)a1_, (int)sub_100D6C40, 0, v40, (int)v41, msg_);// retn 18
v12 = 1;
}
else
{
//省略一大段逻辑
sub_10108D60(v30, *(&a1_->unk_558 + 1), v33, (int)v34, v35, v36, (int)v37, v38, v39, v40, v41);
}
Usually, by debugging the parameters of each function and returning results, we can guess the function functions, and then find the message sending interface.
But I'm lazy here. Because the parameter structure is complex, I can't find the key point for a while. I'm a little dizzy.
So I filter functions one by one through exclusion, and I can find the message sending interface about 11 times at most. For example, if sub dd340 is a message sending interface, after I manually shield its function, the message will not be sent out. Then I can confirm whether sub dd340 is the message sending interface to be found by the result I see (whether it is sent successfully or not).
sub_100DD340
sub_100DD340
Specific shielding method:
- Enter the sub_100dd340 function through IDA or OD, find the end of the function, and find the Retn XX similar code
Enter the sub_100dd340 function through IDA or OD, find the end of the function, and find the Retn XX similar code
sub_100DD340
- Use od to modify the assembly code to Retn XX in the sub dd340 function. Double click and enter Retn XX
Use od to modify the assembly code to Retn XX in the sub dd340 function. Double click and enter Retn XX
sub_100DD340
In this way, the sub dd340 function is returned directly at the entry, and the function is not available, which also ensures the stack balance when the function is called.
sub_100DD340
After confirming that the sub dd340 does not affect the sending of the message, right-click to cancel the selection to modify and restore the modified content.
sub_100DD340
Filter other functions repeatedly, and finally confirm that sub_100c4450 is the sending message function. The code interface is as follows:
sub_100C4450
sub_100C4450((_DWORD *)(a1_->unk_558 + 2528), (_msg *)msg);//
MSG is the sending content, A1 > unk + 2528) is the friend information of the current chat window, including wxid and name information.
msg
a1_->unk_558 + 2528)
wxid
However, as the interface is still not simple enough, it needs to construct friend information, which is more complex. Therefore, continue to go deep into the sub c4450 to see if the simplest interface can be found, such as:
sub_100C4450
sendmsg(wxid, msg); //传入发给谁,发什么即可
The interior of sub c4450 is still very complex. In the same way as before, the execution process is roughly followed first, and then filtered one by one through exclusion.
sub_100C4450
if ( !sub_100C43D0(msg_.buf, msg_.len, msg_.maxlen, wxid_) )// 是不是全是特殊字符\r\n\t等,是返回1,不是返回0
{
sub_1007D390();
msg_packet = sub_102DA4A0((int)wxid, (int)&v67, msg__, &unk, 1);// 数据打包,发送
sub_100494E0(msg_packet_, (size_t)msg_packet);//
sub_1004B550(&v67); //
v11 = sub_102478D0();
v12 = sub_10402C10((int)v11);
v89 = (void **)v13;
if ( sub_10402C10((int)msg_packet_) != v12 || v14 != v89 )
{
if ( sub_100C6770(this_) ) //
{
sub_1004BBF0((int *)&msgpacket);//
sub_10056940((int *)&msgpacket, (size_t)msg_packet_);//
sub_100C56D0(this___, (size_t)&msgpacket, 1);
sub_10081210((LPVOID *)&msgpacket);
v16 = sub_100C0EC0();
sub_10247250((int **)v16, (int)path);
}
}
}
if ( (signed int)(msg->msgend - (unsigned int)msg->msg) / 0x24 != 1 )
v9 = sub_10323DF0();
sub_10324E70(v9, msg_.len, msg_.maxlen, (int)wxid_, (int)path);
sub_100ADA10(&msg___);
This time, there is another way to filter and screen. Directly after a function is executed, jump to the end of sub c4450 through JMP. If a message is sent successfully, the last function executed is the interface we want.
jmp
sub_100C4450
Fortunately, the message sending function sub da4a0 is found in the third function this time. Take a look at its parameters:
sub_102DA4A0
sub_102DA4A0((int)wxid, (int)&v67, msg__, &unk, 1);
sub_102DA4A0@<eax>(int wxid@<edx>, int a2@<ecx>, wxstring *msg, _DWORD *a4, int a5)
The figure below shows the data seen in debugging, and confirms that there is no problem with the interface. As for the other two parameters, they are used to receive the output after analysis and have no practical effect. I will not elaborate here.
After analyzing the work of message sending interface, we find the interface function which is basically consistent with the expectation.
0x3. summary
It seems that the length is a little long. At last, make a summary of this analysis:
- CE finds the content memory in the edit box
CE finds the content memory in the edit box
- After sending, the content of the edit box is deleted, and the write breakpoint is invalid and magical. Guessing controls the display by setting the length
After sending, the content of the edit box is deleted, and the write breakpoint is invalid and magical. Guessing controls the display by setting the length
- Change to the memory access breakpoint, the interface will break when you enter it. After wandering for several times, you decide to analyze. Unexpectedly, you find the key point ctxtedit:: ontxinplaceactivate
Change to the memory access breakpoint, the interface will break when you enter it. After wandering for several times, you decide to analyze. Unexpectedly, you find the key point ctxtedit:: ontxinplaceactivate
- Know that the edit box uses the ctxtedit class of msftedit.dll, and use IDA to find the symbol
Know that the edit box uses the ctxtedit class of msftedit.dll, and use IDA to find the symbol
- Query the interface similar to getValue, find settext, gettextex, etc., and make breakpoints for these two functions
Query the interface similar to getValue, find settext, gettextex, etc., and make breakpoints for these two functions
- As expected, the message response function sent was found by backtracking
As expected, the message response function sent was found by backtracking
- Analyze the response function in detail, exclude it through Retn and JMP for many times, find the real message sending function, and finally analyze the interface function
Analyze the response function in detail, exclude it through Retn and JMP for many times, find the real message sending function, and finally analyze the interface function
In this analysis, CE finding the address is the key point of the first step. It directly enters the function call stack, which is very significant for this analysis.
Then, in the message sending response function, we find the message sending interface function one by one, and modify the instructions to mask the function function function to confirm the function function function, which is faster and more effective than each function to analyze the parameter guess confirmation function.
Debugging tools are very important. The combination of dynamic (OD) and static (IDA) analysis can improve the analysis speed.
Od is suitable for analyzing function parameters, analyzing data structure, confirming function function, IDA is suitable for analyzing function logic, overall function structure, code framework and so on.
Finally, Amway's open source project https://github.com/anhkgg/superwechatpc again. The message sending interface of this analysis will be integrated into the project later. Welcome to star and PR.
star
PR
Related articles:
- Research on WeChat PC Technology (2) - save chat voice
Research on wechat PC technology (2) - save chat voice
- Offset calculation tool
Offset calculation tool