PC wechat reverse: automatically save encrypted chat pictures

preface

This article is based on anhkgg big man's article "Research on wechat PC end technology (2) - win voice", the original link: [original] Research on wechat PC technology (2) - save chat voice - Software Reverse - Snow watching Forum - Security Community | security recruitment | bbs.pediy.com

In this article, the anhkgg boss found the interface to save voice messages. Here, the relevant feature codes are directly given for easy location (the wechat version I use is still 2.6.8.52)

The offset is 0x30E326, and the following characteristic code

 Copy code behind code
67E3E319    C745 FC 0100000>mov dword ptr ss:[ebp-0x4],0x1
67E3E320    FF77 34         push dword ptr ds:[edi+0x34]             ; length
67E3E323    FF77 30         push dword ptr ds:[edi+0x30]             ; content
67E3E326    E8 85F07300     call WeChatWi.6857D3B0
67E3E32B    8D85 58FFFFFF   lea eax,dword ptr ss:[ebp-0xA8]
67E3E331    50              push eax
67E3E332    E8 090E0000     call WeChatWi.67E3F140
 Copy code behind code
C745 FC 01000000 FF77 ?? FF77 ?? E8 ???????? 8D85 ???????? 50 E8 ????????

Correlation extension based on saved speech

In fact, this place not only has voice messages, but also picture messages. When we send a picture message

The content of [edi+0x30] stores the relevant data of the picture sent this time, including a series of original data such as wechat ID. Of course, we can write HOOK in this place to save pictures, but it's not necessary. Because there are too many messages here, it will be relatively troublesome to process.

Related processes of image processing

Since this place is the most original message content, the message will be processed later. And we already know that the pictures received by wechat will be saved locally by XOR encryption. Then we might as well guess the processing flow related to the picture.

First, after receiving the original message, a series of processing will be carried out on the message, including judging whether the message is a picture. If it is a picture, the picture data will be taken out and the picture will be encrypted in memory. After the encryption completes, the API of the file operation is called, and the encrypted picture is written to the local.

The whole process is shown in the figure:

Ideas about automatically saving pictures

Now that we know the process of image processing and have a call to receive image messages, we can find the algorithm and function to encrypt the image after receiving the image message and before CreateFileW creates the image, and save the image before encryption.

Save chat pictures in practice

Find the call to save voice in OD, send a picture message to disconnect the program, and disconnect CreateFileW at the same time. After that, F9 runs

At this time, the file path is xlog, which obviously does not meet our requirements. Continue to run F9

When the Image path with the Image keyword is always found, the Image is created

At this point, click K to display the stack, find the return address of the first layer, and right-click to display the call

When wechat runs here, the image encryption has been completed. We need to find the image encryption algorithm before this function. In fact, it is a little above, and you can see it by turning the mouse up a little

After finding this place, clear all the remaining breakpoints and keep only this one

Correlation analysis of saved picture call

Send a picture again and the program will be disconnected. This code first encrypts the picture in a circular way. The number of cycles is the value of ecx, that is, the size of the picture. There are two important data.

Let's first check the contents of [ebp-0x14] in memory. It's actually very simple to know what this data is. Go to the CreateFileW breakpoint first

When the CreateFileW breakpoint is broken, execute to return and view the open file handle

At this time, the open picture handle is 0xF80, and then the WriteFile breakpoint is set

After the WriteFile is disconnected, you can see that the handle is F80, the written buffer address is 39FE820, and the address of [ebp-0x14] is exactly 39FE820. That is to say, [ebp-0x14] stores the encrypted picture data

Return to the previous breakpoint and send a picture again to view the data of [ebp-0x4]

At this time, the received picture is saved in [ebp-0x4], and ecx saves the size of the picture

Here, use PCHunter view - > process memory to dump this data

The problem is that this is just a 4KB thumbnail. Go back to OD and press F9 again


The breakpoint is broken, but the value of ecx becomes 0x5A140

Use the same method to dump the memory. The size is 360KB. This is the original figure we need.

In other words, this place will be brought down twice. The first time is the thumbnail, and the second time is the original picture we want.

Code to save chat pictures

The example code is as follows:

 Copy code behind code
void HookSaveImages()
{
        DWORD dwBaseAddress = (DWORD)GetModuleHandle(TEXT("WeChatWin.dll"));
        //The address of the hook is required
        SaveImageAddress = dwBaseAddress + SaveImages;

        //Jump back to your address
        SaveImageAddressBackAddress = SaveImageAddress + 5;

        //Assembly jump data
        BYTE jmpCode[5] = { 0 };
        jmpCode[0] = 0xE9;

        //Calculate offset
        *(DWORD*)& jmpCode[1] = (DWORD)FnSaveImages - SaveImageAddress - 5;

        // Save previous properties for restore
        DWORD OldProtext = 0;

        // Because you want to write data to the code segment, and because the code segment is not writable, you need to modify the properties
        VirtualProtect((LPVOID)SaveImageAddress, 5, PAGE_EXECUTE_READWRITE, &OldProtext);

        //Write your own code
        memcpy((void*)SaveImageAddress, jmpCode, 5);

        // Restore is required after the operation
        VirtualProtect((LPVOID)SaveImageAddress, 5, OldProtext, &OldProtext);
}
 Copy code behind code
__declspec(naked) void FnSaveImages()
{
        __asm
        {
                mov ebx, dword ptr ss : [ebp - 0x4];
                mov ImageData, ebx;
                mov ImageDataLen, ecx;
                pushad;
                pushfd;
        }

        //Call the function that receives the message
        FnSaveImagesCore();

        //Restore site
        __asm
        {
                popfd
                popad
                //Jump back to the next instruction that is the HOOK instruction
                jmp SaveImageAddressBackAddress;
        }
}
 Copy code behind code
void FnSaveImagesCore()
{
        //If the picture is longer than 10KB, save it
        if (ImageDataLen >= 10240)
        {

                //Get temporary folder directory
                char temppath[MAX_PATH] = { 0 };
                GetTempPathA(MAX_PATH, temppath);
                char imagedir[20] = { "WeChatRecordImages" };

                //Splice catalog
                char WeChatExpressionsPath[MAX_PATH] = { 0 };
                sprintf_s(WeChatExpressionsPath, "%s%s\\", temppath, imagedir);
                //Create a directory to store pictures
                CreateDir(WeChatExpressionsPath);

                //Save picture
                CreateFileWithCurrentTime(WeChatExpressionsPath, (char*)".jpg", ImageData, ImageDataLen);
        }
}

Actual effect

Tags: C++

Posted on Wed, 03 Nov 2021 18:23:14 -0400 by eightonesix