├── .gitignore ├── README.md └── WxamFuzzer └── WxamFuzzer.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BasicWXAMFuzzer 2 | 3 | ## Info 4 | See: https://www.signal-labs.com/blog/fuzzing-wechats-wxam-parser 5 | 6 | ## Jackalope Command Line: 7 | fuzzer.exe -in C:\tmp\wxam_inputs -out C:\tmp\jOut -t 30000 -nthreads 2 -delivery shmem -max_sample_size 1000000 -instrument_module WeChatWin.dll -instrument_module voipEngine.dll -persist -target_module WxamFuzzer.exe -target_method fuzz -nargs 0 -iterations 80000 -cmp_coverage -- WxamFuzzer.exe @@ 8 | 9 | ## Notes 10 | WxamFuzzer.exe needs to find the voipEngine.dll and WeChatWin.dll files, alongside their dependancies (so the DLLs need to be in the search path or folder), additionally 11 | the target tries to find its own data folder (usually in C:\ProgramData\Tencent), so you'll need these too. The easiest way would be to simply install WeChat on your fuzzing machine before fuzzing, you can do this in a VM instead of your host. 12 | 13 | Also note you'll have to update the pointer offsets in the harness to match your version of WeChatWin.dll (assuming the functions haven't undergone breaking changes between whatever version you're using, and the version I tested) 14 | -------------------------------------------------------------------------------- /WxamFuzzer/WxamFuzzer.cpp: -------------------------------------------------------------------------------- 1 | // wxamFuzzer.cpp : This file contains the 'main' function. Program execution begins and ends there. 2 | // 3 | 4 | #include 5 | #include 6 | 7 | HMODULE hDll_win; 8 | HMODULE hDll_voip; 9 | //char* inputBuf; 10 | 11 | // Wxam -> JPEG: flag = 0 12 | // Wxam -> Gif: flag = ? 13 | typedef bool (__cdecl* _isWxGF)( 14 | unsigned char* fileBuffer, 15 | DWORD flag 16 | ); 17 | 18 | struct InputStruct { 19 | unsigned char* inputBuf; 20 | uint32_t inputBuf_sz; 21 | }; 22 | 23 | typedef bool(__fastcall* _wxam_decoder_helper_DecodeWxamToJpeg)( 24 | InputStruct* fileBuffer, 25 | int* pOut 26 | ); 27 | 28 | unsigned char* sample_bytes; 29 | unsigned char* shm_data; 30 | #define MAX_SAMPLE_SIZE 1000000 31 | #define SHM_SIZE (4 + MAX_SAMPLE_SIZE) 32 | 33 | _isWxGF isWxGF; 34 | _wxam_decoder_helper_DecodeWxamToJpeg wxam_decoder_helper_DecodeWxamToJpeg; 35 | 36 | 37 | int setup_shmem(const wchar_t* name) { 38 | HANDLE map_file; 39 | 40 | map_file = OpenFileMapping( 41 | FILE_MAP_ALL_ACCESS, // read/write access 42 | FALSE, // do not inherit the name 43 | name); // name of mapping object 44 | 45 | if (map_file == NULL) { 46 | printf("Error accessing shared memory\n"); 47 | return 0; 48 | } 49 | 50 | shm_data = (unsigned char*)MapViewOfFile(map_file, // handle to map object 51 | FILE_MAP_ALL_ACCESS, // read/write permission 52 | 0, 53 | 0, 54 | SHM_SIZE); 55 | 56 | if (shm_data == NULL) { 57 | printf("Error accessing shared memory\n"); 58 | return 0; 59 | } 60 | // Optimization: setup shared memory as a global page we just overwrite at each fuzz iteration 61 | // as opposed to malloc/free all the time 62 | sample_bytes = (unsigned char*)calloc(1,MAX_SAMPLE_SIZE); 63 | 64 | 65 | return 1; 66 | } 67 | 68 | void init() { 69 | // Load target DLLs 70 | hDll_voip = LoadLibraryA("voipEngine.dll"); 71 | if (hDll_voip == NULL) { 72 | printf("Failed to load voipEngine.dll\n"); 73 | exit(1); 74 | } 75 | hDll_win = LoadLibraryA("WeChatWin.dll"); 76 | if (hDll_win == NULL) { 77 | printf("Failed to load WeChatWin.dll, err:%x\n", GetLastError()); 78 | exit(1); 79 | } 80 | // Create function pointers 81 | isWxGF = (_isWxGF)GetProcAddress(hDll_voip, "isWxGF"); 82 | wxam_decoder_helper_DecodeWxamToJpeg = (_wxam_decoder_helper_DecodeWxamToJpeg)((unsigned int)hDll_win + 0x7D3DA0); 83 | } 84 | 85 | /* 86 | void test_load_buffer() { 87 | // Load file from hardcoded location 88 | HANDLE hFile = CreateFileW( 89 | L"C:\\dev\\file.bin", 90 | GENERIC_READ, 91 | FILE_SHARE_READ, 92 | NULL, 93 | OPEN_EXISTING, 94 | FILE_ATTRIBUTE_NORMAL, 95 | NULL 96 | ); 97 | if (hFile == INVALID_HANDLE_VALUE) { 98 | printf("Failed to open C:\\dev\\file.bin\n"); 99 | exit(1); 100 | } 101 | LARGE_INTEGER totalSize = {}; 102 | BOOL res = GetFileSizeEx(hFile, &totalSize); 103 | if (!res) { 104 | printf("GetFileSizeEx failed!\n"); 105 | exit(2); 106 | } 107 | DWORD bRet = 0; 108 | inputBuf = (char*)calloc(1, totalSize.QuadPart); 109 | res = ReadFile(hFile, inputBuf, totalSize.QuadPart, &bRet, NULL); 110 | if (!res) { 111 | printf("ReadFile failed!\n"); 112 | exit(3); 113 | } 114 | if (bRet != totalSize.QuadPart) { 115 | printf("Read:%d bytes, expected:%d bytes\n", bRet, totalSize.QuadPart); 116 | } 117 | return; 118 | } 119 | */ 120 | 121 | 122 | #pragma optimize( "", off ) 123 | extern "C" 124 | __declspec(dllexport) 125 | bool fuzz() { 126 | 127 | uint32_t sample_size = 0; 128 | sample_size = *(uint32_t*)(shm_data); 129 | if (sample_size > MAX_SAMPLE_SIZE) sample_size = MAX_SAMPLE_SIZE-4; 130 | // Update the bytes of our buffer 131 | memcpy(sample_bytes, shm_data + sizeof(uint32_t), sample_size); 132 | // Input struct is actually: 133 | // 0: pointer to input buffer 134 | // 1: input buffer size 135 | // so, lets create that: 136 | InputStruct inputStruct; 137 | inputStruct.inputBuf = sample_bytes; 138 | inputStruct.inputBuf_sz = sample_size; 139 | 140 | 141 | 142 | // Call first function as per wxam 143 | bool res = isWxGF(inputStruct.inputBuf, sample_size); 144 | // 1 = error, 0 = success 145 | if (res) { 146 | //printf("isWxGF failed\n"); 147 | return false; 148 | } 149 | int pOut = 0; 150 | res = wxam_decoder_helper_DecodeWxamToJpeg(&inputStruct, &pOut); 151 | // 1 = success, 0 = error 152 | return res; 153 | 154 | 155 | } 156 | #pragma optimize( "", on ) 157 | 158 | int wmain(int argc, wchar_t* argv[]) 159 | { 160 | init(); 161 | setup_shmem(argv[1]); 162 | while (true) { 163 | fuzz(); 164 | } 165 | /* 166 | // 1 = success, 0 = fail 167 | if (res) { 168 | printf("Finished fuzzing successfully!\n"); 169 | } 170 | else { 171 | printf("Fuzzing returned an error!\n"); 172 | } 173 | */ 174 | return 0; 175 | } 176 | 177 | // Run program: Ctrl + F5 or Debug > Start Without Debugging menu 178 | // Debug program: F5 or Debug > Start Debugging menu 179 | 180 | // Tips for Getting Started: 181 | // 1. Use the Solution Explorer window to add/manage files 182 | // 2. Use the Team Explorer window to connect to source control 183 | // 3. Use the Output window to see build output and other messages 184 | // 4. Use the Error List window to view errors 185 | // 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project 186 | // 6. In the future, to open this project again, go to File > Open > Project and select the .sln file 187 | --------------------------------------------------------------------------------