Hacking Book | Free Online Hacking Learning


how to find the message sending interface

Posted by truschel at 2020-03-27

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.


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 - > write in memory breakpoint.


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.


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.


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.


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.

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.

Find other entry points. Nonsense, the road over there is broken. Please give up.

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 a module related to the edit box.

WeChatWin.dll msftedit.dll edit 可执行模块, 条目 20 基址=6F050000 大小=00094000 (606208.) 入口=6F05D53D msftedit.<ModuleEntryPoint> 名称=msftedit (系统) 文件版本= 路径=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.


Here you can directly load symbols in OD for analysis. The method of use is as follows:

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 related articles for specific use.

Press g in IDA, enter 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.


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.


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.


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 OD. There are many functions, so it is impossible to quickly confirm which function is the message sending interface.


Take part of the code and feel about 11 functions. According to the logic of OD trace, it is probably sendbtn > gettext > sub > sub > dd340 > sub > c50c0 > sub > 10094100 > sub > dd9d0 > sub > c4450 > sub > dd100de120.

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 bit 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


Use od to modify the assembly code to Retn XX in the sub dd340 function. Double click and enter Retn XX


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.


After confirming that the sub dd340 does not affect the sending of the message, the modified content can be recovered by right clicking the undo selection.

sub_100DD340 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); [email protected]<eax>(int [email protected]<edx>, int [email protected]<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

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

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

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

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, etc., each with its own advantages.

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

Offset calculation tool