personnumber3377

Fuzzing microsoft office barcodes

There is a component in microsoft office which is called MSBARCODE.DLL which handles a very obscure functionality in the office suite mainly handling barcodes.

Here is a somewhat working program which renders a barcode and then saves it in a file in c++:


#include <windows.h>
#include <iostream>
#include <gdiplus.h>
using namespace Gdiplus;
typedef int (__stdcall *GetBarcodeRenderer_t)(int, void**); // Define function signature
typedef long (__thiscall *ValidateDataFunc)(void*, wchar_t*);
// typedef long (__thiscall *DrawToDCFunc)(void*, HDC*, RECT*, wchar_t*);
typedef long (__thiscall *DrawToDCFunc)(void*, HDC, RECT*, wchar_t*);


// #define BARCODE_TYPE 3 // 3 means EAN-8 and 7 means Code128   // Example: barcode type 3 (EAN-8)

#define BARCODE_TYPE 7

struct Example {
    void (*func)(wchar_t *data); // This is the prototype of Code128Renderer::ValidateData which only takes a wchar string pointing to the data...
};


int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) {
    UINT num = 0;          // Number of encoders
    UINT size = 0;         // Size of the image codec info array

    // Get the list of image encoders
    GetImageEncodersSize(&num, &size);
    if (size == 0) return -1;  // No encoders found

    ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
    if (!pImageCodecInfo) return -1;

    GetImageEncoders(num, size, pImageCodecInfo);

    // Find the correct encoder for the given format
    for (UINT i = 0; i < num; i++) {
        if (wcscmp(pImageCodecInfo[i].MimeType, format) == 0) {
            *pClsid = pImageCodecInfo[i].Clsid;
            free(pImageCodecInfo);
            return 0;  // Success
        }
    }

    free(pImageCodecInfo);
    return -1;  // Encoder not found
}




int main() {
    // This is just some test string:
    // wchar_t *test_str = L"Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!";
    wchar_t *test_str = L"Hello, world!\0";
    // Load the DLL
    HMODULE hDll = LoadLibrary("MSBARCODE.dll");
    if (!hDll) {
        std::cerr << "Failed to load DLL\n";
        return 1;
    }
    // Get function pointer
    GetBarcodeRenderer_t GetBarcodeRenderer =
        (GetBarcodeRenderer_t) GetProcAddress(hDll, "GetBarcodeRenderer");
    if (!GetBarcodeRenderer) {
        std::cerr << "Failed to get function address\n";
        FreeLibrary(hDll);
        return 1;
    }
    // Call the function
    void* renderer = nullptr;
    printf("Here is the value of the barcode renderer before: %d\n", renderer);
    int result = GetBarcodeRenderer(BARCODE_TYPE, &renderer);
    std::cout << "Function returned: " << std::hex << result << "\n";
    printf("Here is the value of the barcode renderer: %p\n", renderer);
    if ((uintptr_t)result > 0x80000000) {
        printf("Error: GetBarcodeRenderer failed with code: 0x%lX\n", (uintptr_t)result);
        return 1;
    }
    if (result) {
        printf("Exiting...\n");
        return 1;
    }

    // Now try to call the verifying of the data stuff...
    printf("Now we should do the stuff\n");
    // ValidateDataFunc func = *(ValidateDataFunc *)((char *)renderer + 0x30);
    // printf("Function pointer at offset 0x30: %p\n", *(void **)((char *)renderer + 0x30));


    void* vtable_ptr = *(void**)renderer;
    int function_offset = 8*5; // At index 5
    int draw_function_offset = 8*7; // At index 7 I think...
    ValidateDataFunc func = *(ValidateDataFunc *)((char *)vtable_ptr+function_offset);


    printf("Function pointer at offset 0x30: %p\n", *(void **)((char *)vtable_ptr+function_offset));

    DrawToDCFunc draw_func = *(DrawToDCFunc *)((char *)vtable_ptr + draw_function_offset);
    printf("DrawToDC function pointer: %p\n", draw_func);

    printf("Dumping vtable:\n");
    for (int i = 0; i < 10; i++) {
        printf("vtable[%d] = %p\n", i, *(void **)((char *)vtable_ptr + i * sizeof(void *)));
    }
    printf("Calling the stuff...\n");
    long res = func(vtable_ptr, test_str);
    printf("Returned...\n");
    printf("Returned this value here: 0x%llx\n", res);
    // Free the DLL



    HDC hdcMem = CreateCompatibleDC(NULL);  // Create a memory DC
    if (!hdcMem) {
        printf("Error: Could not create memory DC!\n");
        return 1;
    }

    // Define the barcode dimensions
    int width = 300;   // Set barcode width
    int height = 100;  // Set barcode height
    HBITMAP hBitmap = CreateCompatibleBitmap(hdcMem, width, height);
    SelectObject(hdcMem, hBitmap);  // Select the bitmap into the memory DC

    // Define the drawing rectangle
    RECT rect = {0, 0, width, height};

    // Call `DrawToDC`
    long res2 = draw_func(renderer, hdcMem, &rect, test_str);
    printf("DrawToDC returned: 0x%lX\n", res2);





    /*

    // Initialize GDI+
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    // Convert HBITMAP to a GDI+ Bitmap
    Bitmap bmp(hBitmap, NULL);

    // Save the image as a PNG file
    CLSID pngClsid;
    GetEncoderClsid(L"image/png", &pngClsid);
    bmp.Save(L"barcode.png", &pngClsid, NULL);

    // Clean up
    DeleteObject(hBitmap);
    DeleteDC(hdcMem);
    GdiplusShutdown(gdiplusToken);
    */


    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    // Create a memory DC and a compatible bitmap
    /*
    HDC hdcMem = CreateCompatibleDC(NULL);
    int width = 300, height = 100;
    HBITMAP hBitmap = CreateCompatibleBitmap(hdcMem, width, height);
    SelectObject(hdcMem, hBitmap);
    */

    // Properly create a new Bitmap from HBITMAP
    Bitmap* bmp = Bitmap::FromHBITMAP(hBitmap, NULL);
    if (!bmp || bmp->GetLastStatus() != Ok) {
        printf("Error: Failed to create Bitmap object!\n");
        return 1;
    }

    // Save as PNG
    CLSID pngClsid;
    if (GetEncoderClsid(L"image/png", &pngClsid) != 0) {
        printf("Error: Could not find PNG encoder!\n");
        return 1;
    }
    bmp->Save(L"barcode.png", &pngClsid, NULL);

    // Cleanup
    DeleteObject(hBitmap);
    DeleteDC(hdcMem);
    delete bmp; // Properly free Bitmap

    GdiplusShutdown(gdiplusToken);

    printf("Barcode saved as 'barcode.png'\n");


    FreeLibrary(hDll);
    return 0;
}


Now let’s clean it up a little and also implement some of the other formats… I think I also need to free the actual renderer by calling the function at offset 0 from the base because looking at the decompilation that destroys it. Here is the decompiled code stuff:


undefined8 * GetBarcodeRenderer(int param_1,undefined8 *param_2)

{
  undefined8 *puVar1;
  undefined8 *puVar2;
  undefined **ppuVar3;
  undefined8 *puVar4;

                    /* 0x1e70  1  GetBarcodeRenderer */
  puVar4 = (undefined8 *)0x0;
  if (param_2 == (undefined8 *)0x0) {
    return (undefined8 *)0x80004003;
  }
  puVar2 = puVar4;
  if (param_1 < 7) {
    if (param_1 == 6) {
      puVar1 = (undefined8 *)malloc(0x30);
      if (puVar1 == (undefined8 *)0x0) goto LAB_180002141;
      *(undefined4 *)(puVar1 + 1) = 6;
      ppuVar3 = &Code39Renderer::`vftable';
LAB_180001ef3:
      *(undefined4 *)(puVar1 + 5) = 0;
    }
    else if (param_1 == 0) {
      puVar1 = (undefined8 *)malloc(0x30);
      if (puVar1 == (undefined8 *)0x0) goto LAB_180002141;
      *(undefined4 *)(puVar1 + 1) = 0;
      ppuVar3 = &UPCARenderer::`vftable';
      *(undefined4 *)(puVar1 + 5) = 0;
    }
    else if (param_1 == 1) {
      puVar1 = (undefined8 *)malloc(0x30);
      if (puVar1 == (undefined8 *)0x0) goto LAB_180002141;
      *(undefined4 *)(puVar1 + 1) = 1;
      ppuVar3 = &UPCERenderer::`vftable';
      *(undefined4 *)(puVar1 + 5) = 0;
    }
    else if (param_1 == 2) {
      puVar1 = (undefined8 *)malloc(0x30);
      if (puVar1 == (undefined8 *)0x0) goto LAB_180002141;
      *(undefined4 *)(puVar1 + 1) = 2;
      ppuVar3 = &EAN13Renderer::`vftable';
      *(undefined4 *)(puVar1 + 5) = 0;
    }
    else {
      if (param_1 != 3) {
        if (param_1 == 4) {
          puVar1 = (undefined8 *)malloc(0x30);
          if (puVar1 == (undefined8 *)0x0) goto LAB_180002141;
          *(undefined4 *)(puVar1 + 1) = 4;
          ppuVar3 = &CaseRenderer::`vftable';
        }
        else {
          if (param_1 != 5) {
            return (undefined8 *)0x8004ef00;
          }
          puVar1 = (undefined8 *)malloc(0x30);
          if (puVar1 == (undefined8 *)0x0) goto LAB_180002141;
          *(undefined4 *)(puVar1 + 1) = 5;
          ppuVar3 = &NW7Renderer::`vftable';
        }
        goto LAB_180001ef3;
      }
      puVar1 = (undefined8 *)malloc(0x30);
      if (puVar1 == (undefined8 *)0x0) goto LAB_180002141;
      *(undefined4 *)(puVar1 + 1) = 3;
      ppuVar3 = &EAN8Renderer::`vftable';
      *(undefined4 *)(puVar1 + 5) = 0;
    }
  }
  else {
    if (param_1 != 7) {
      if (param_1 == 8) {
        puVar1 = (undefined8 *)malloc(0x30);
        if (puVar1 == (undefined8 *)0x0) goto LAB_180002141;
        puVar1[1] = 8;
        ppuVar3 = &PostnetRenderer::`vftable';
      }
      else {
        if (param_1 != 9) {
          if (param_1 == 10) {
            puVar1 = (undefined8 *)malloc(0x28);
            if (puVar1 != (undefined8 *)0x0) {
              puVar1[1] = 10;
              *puVar1 = &JPPostRenderer::`vftable';
              puVar1[2] = 2;
              *(undefined4 *)(puVar1 + 3) = 0;
              *(undefined8 *)((longlong)puVar1 + 0x1c) = 0xffffff;
              puVar2 = puVar1;
            }
            goto LAB_180002141;
          }
          if (param_1 != 0xb) {
            return (undefined8 *)0x8004ef00;
          }
          puVar1 = (undefined8 *)malloc(0x1870);
          if (puVar1 == (undefined8 *)0x0) goto LAB_180002141;
          *(undefined4 *)(puVar1 + 1) = 0xb;
          ppuVar3 = &QRRenderer::`vftable';
          *(undefined4 *)(puVar1 + 5) = 0;
          *(undefined8 *)((longlong)puVar1 + 0x2c) = 2;
          puVar1[7] = 0;
          puVar1[8] = 0;
          puVar1[9] = 0;
          *(undefined4 *)(puVar1 + 0xb) = 0;
          *(undefined2 *)(puVar1 + 0xe) = 0;
          goto LAB_18000211f;
        }
        puVar1 = (undefined8 *)malloc(0x30);
        if (puVar1 == (undefined8 *)0x0) goto LAB_180002141;
        puVar1[1] = 9;
        ppuVar3 = &FIMRenderer::`vftable';
      }
      *(undefined4 *)((longlong)puVar1 + 0x1c) = 0xffffff;
      *(undefined4 *)(puVar1 + 3) = 0;
      puVar1[2] = 2;
      *puVar1 = ppuVar3;
      *(undefined4 *)(puVar1 + 5) = 0;
      *(undefined4 *)(puVar1 + 4) = 2;
      puVar2 = puVar1;
      goto LAB_180002141;
    }
    puVar1 = (undefined8 *)malloc(0x28);
    if (puVar1 == (undefined8 *)0x0) goto LAB_180002141;
    *(undefined4 *)(puVar1 + 1) = 7;
    ppuVar3 = &Code128Renderer::`vftable';
  }
LAB_18000211f:
  *puVar1 = ppuVar3;
  *(undefined8 *)((longlong)puVar1 + 0x1c) = 0xffffff;
  *(undefined4 *)(puVar1 + 3) = 0;
  puVar1[2] = 2;
  *(undefined4 *)((longlong)puVar1 + 0xc) = 1;
  puVar2 = puVar1;
LAB_180002141:
  puVar1 = (undefined8 *)0x8007000e;
  if (puVar2 != (undefined8 *)0x0) {
    *param_2 = puVar2;
    puVar1 = puVar4;
  }
  return puVar1;
}

now let’s define each one of these things and see what happens…

Now my current fuzzing harness looks like this here:


#include <windows.h>
#include <iostream>
#include <gdiplus.h>
using namespace Gdiplus;
typedef int (__stdcall *GetBarcodeRenderer_t)(int, void**); // Define function signature
typedef long (__thiscall *ValidateDataFunc)(void*, wchar_t*);
// typedef long (__thiscall *DrawToDCFunc)(void*, HDC*, RECT*, wchar_t*);
typedef long (__thiscall *DrawToDCFunc)(void*, HDC, RECT*, wchar_t*);

typedef void (__thiscall *DestroyFunc)(void*);  // Function prototype


// #define BARCODE_TYPE 3 // 3 means EAN-8 and 7 means Code128   // Example: barcode type 3 (EAN-8)

#define BARCODE_TYPE 7

struct Example {
    void (*func)(wchar_t *data); // This is the prototype of Code128Renderer::ValidateData which only takes a wchar string pointing to the data...
};


int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) {
    UINT num = 0;          // Number of encoders
    UINT size = 0;         // Size of the image codec info array

    // Get the list of image encoders
    GetImageEncodersSize(&num, &size);
    if (size == 0) return -1;  // No encoders found

    ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
    if (!pImageCodecInfo) return -1;

    GetImageEncoders(num, size, pImageCodecInfo);

    // Find the correct encoder for the given format
    for (UINT i = 0; i < num; i++) {
        if (wcscmp(pImageCodecInfo[i].MimeType, format) == 0) {
            *pClsid = pImageCodecInfo[i].Clsid;
            free(pImageCodecInfo);
            return 0;  // Success
        }
    }

    free(pImageCodecInfo);
    return -1;  // Encoder not found
}

/*

BARCODE_TYPE 6 is Code39Renderer and it follows the usual convention...

BARCODE_TYPE 0 is UPCARenderer and it also follows the usual convention.

BARCODE_TYPE 1 is UPCERenderer and it follows the usual thing...

BARCODE_TYPE 2 is EAN13Renderer and it also follows the usual stuff...

BARCODE_TYPE 4 is CaseRenderer and it also follows the usual stuff...

BARCODE_TYPE 5 is NW7Renderer and it also follows the usual stuff...

BARCODE_TYPE 3 is EAN8Renderer and it also follows the usual stuff...

BARCODE_TYPE 8 is PostnetRenderer and it follows this stuff here:

                             *************************************************************
                             *  const PostnetRenderer::`vftable'
                             *************************************************************
                             ??_7PostnetRenderer@@6B@                        XREF[2]:     GetBarcodeRenderer:1800020d1 (*)
                             PostnetRenderer::`vftable'                                   GetBarcodeRenderer:1800020f8 (*)
       18000a728 c0  1d  00       addr       BarcodeRendererBase::Destroy
                 80  01  00
                 00  00
       18000a730 e0  1d  00       addr       BarcodeRendererBase::GetType
                 80  01  00
                 00  00
       18000a738 20  5d  00       addr       PostnetRenderer::GetProperty
                 80  01  00
                 00  00
       18000a740 50  5d  00       addr       PostnetRenderer::SetProperty
                 80  01  00
                 00  00
       18000a748 d0  35  00       addr       FIMRenderer::GetMinSize
                 80  01  00
                 00  00
       18000a750 d0  35  00       addr       FIMRenderer::GetMinSize
                 80  01  00
                 00  00
       18000a758 80  5c  00       addr       PostnetRenderer::Draw
                 80  01  00
                 00  00
       18000a760 20  22  00       addr       BarcodeRendererBase::DrawToDC
                 80  01  00
                 00  00
       18000a768 f0  1d  00       addr       NW7Renderer::`vector_deleting_destructor'
                 80  01  00
                 00  00
       18000a770 a0  ca  00       addr       JPPostRenderer::`RTTI_Complete_Object_Locator'   = 01h
                 80  01  00
                 00  00


BARCODE_TYPE 10 is JPPostRenderer is the usual stuff...

BARCODE_TYPE 9 is FIMRenderer and it follows this:


                             *************************************************************
                             *  const FIMRenderer::`vftable'
                             *************************************************************
                             ??_7FIMRenderer@@6B@                            XREF[2]:     GetBarcodeRenderer:1800020b8 (*)
                             FIMRenderer::`vftable'                                       GetBarcodeRenderer:1800020d1 (*)
       18000a6d8 c0  1d  00       addr       BarcodeRendererBase::Destroy
                 80  01  00
                 00  00
       18000a6e0 e0  1d  00       addr       BarcodeRendererBase::GetType
                 80  01  00
                 00  00
       18000a6e8 70  35  00       addr       FIMRenderer::GetProperty
                 80  01  00
                 00  00
       18000a6f0 a0  35  00       addr       FIMRenderer::SetProperty
                 80  01  00
                 00  00
       18000a6f8 d0  35  00       addr       FIMRenderer::GetMinSize
                 80  01  00
                 00  00
       18000a700 d0  35  00       addr       FIMRenderer::GetMinSize
                 80  01  00
                 00  00
       18000a708 f0  34  00       addr       FIMRenderer::Draw
                 80  01  00
                 00  00
       18000a710 20  22  00       addr       BarcodeRendererBase::DrawToDC
                 80  01  00
                 00  00
       18000a718 f0  1d  00       addr       NW7Renderer::`vector_deleting_destructor'
                 80  01  00
                 00  00
       18000a720 78  ca  00       addr       PostnetRenderer::`RTTI_Complete_Object_Locator   = 01h
                 80  01  00
                 00  00


BARCODE_TYPE 11 is QRRenderer and it follows the usual stuff...

so the only barcode types which we need to be wary of are type == 9 and type == 8 because those are special cases where the thing is not where it should be. I think those are used internally, because there is no way to activate those functions from ms word anyway, so I think I am just going to ban those two and see what happens... since the maximum barcode type is 11 we are just going to do modulo 12 and be done with it...





*/


int main() {
    // This is just some test string:
    // wchar_t *test_str = L"Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!";
    wchar_t *test_str = L"Hello, world!\0";
    // Load the DLL
    HMODULE hDll = LoadLibrary("MSBARCODE.dll");
    if (!hDll) {
        std::cerr << "Failed to load DLL\n";
        return 1;
    }
    // Get function pointer
    GetBarcodeRenderer_t GetBarcodeRenderer =
        (GetBarcodeRenderer_t) GetProcAddress(hDll, "GetBarcodeRenderer");
    if (!GetBarcodeRenderer) {
        std::cerr << "Failed to get function address\n";
        FreeLibrary(hDll);
        return 1;
    }
    // Call the function
    void* renderer = nullptr;
    printf("Here is the value of the barcode renderer before: %d\n", renderer);
    int result = GetBarcodeRenderer(BARCODE_TYPE, &renderer);
    std::cout << "Function returned: " << std::hex << result << "\n";
    printf("Here is the value of the barcode renderer: %p\n", renderer);
    if ((uintptr_t)result > 0x80000000) {
        printf("Error: GetBarcodeRenderer failed with code: 0x%lX\n", (uintptr_t)result);
        return 1;
    }
    if (result) {
        printf("Exiting...\n");
        return 1;
    }

    // Now try to call the verifying of the data stuff...
    printf("Now we should do the stuff\n");
    // ValidateDataFunc func = *(ValidateDataFunc *)((char *)renderer + 0x30);
    // printf("Function pointer at offset 0x30: %p\n", *(void **)((char *)renderer + 0x30));


    void* vtable_ptr = *(void**)renderer;
    int function_offset = 8*5; // At index 5
    int draw_function_offset = 8*7; // At index 7 I think...
    ValidateDataFunc func = *(ValidateDataFunc *)((char *)vtable_ptr+function_offset);


    printf("Function pointer at offset 0x30: %p\n", *(void **)((char *)vtable_ptr+function_offset));

    DrawToDCFunc draw_func = *(DrawToDCFunc *)((char *)vtable_ptr + draw_function_offset);
    printf("DrawToDC function pointer: %p\n", draw_func);

    printf("Dumping vtable:\n");
    for (int i = 0; i < 10; i++) {
        printf("vtable[%d] = %p\n", i, *(void **)((char *)vtable_ptr + i * sizeof(void *)));
    }
    printf("Calling the stuff...\n");
    long res = func(vtable_ptr, test_str);
    if (res != 0) {
        // Invalid data. Return here.
        printf("Invalid data passed. Returning...\n");
        return 1;
    }
    printf("Returned...\n");
    printf("Returned this value here: 0x%llx\n", res);
    // Free the DLL



    HDC hdcMem = CreateCompatibleDC(NULL);  // Create a memory DC
    if (!hdcMem) {
        printf("Error: Could not create memory DC!\n");
        return 1;
    }

    // Define the barcode dimensions
    int width = 300;   // Set barcode width
    int height = 100;  // Set barcode height
    HBITMAP hBitmap = CreateCompatibleBitmap(hdcMem, width, height);
    SelectObject(hdcMem, hBitmap);  // Select the bitmap into the memory DC

    // Define the drawing rectangle
    RECT rect = {0, 0, width, height};

    // Call `DrawToDC`
    long res2 = draw_func(renderer, hdcMem, &rect, test_str);
    printf("DrawToDC returned: 0x%lX\n", res2);

    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    // Properly create a new Bitmap from HBITMAP
    Bitmap* bmp = Bitmap::FromHBITMAP(hBitmap, NULL);
    if (!bmp || bmp->GetLastStatus() != Ok) {
        printf("Error: Failed to create Bitmap object!\n");
        return 1;
    }

    // Save as PNG
    CLSID pngClsid;
    if (GetEncoderClsid(L"image/png", &pngClsid) != 0) {
        printf("Error: Could not find PNG encoder!\n");
        return 1;
    }
    bmp->Save(L"barcode.png", &pngClsid, NULL);

    // Cleanup
    DeleteObject(hBitmap);
    DeleteDC(hdcMem);
    delete bmp; // Properly free Bitmap

    GdiplusShutdown(gdiplusToken);

    printf("Barcode saved as 'barcode.png'\n");
    printf("Freeing the renderer...\n");
    // *((void*)renderer)(); // Call the renderer free function which is always at index zero...
    DestroyFunc destroy_func = *(DestroyFunc *)((char *)vtable_ptr + 0x0);
    printf("Destroy function pointer: %p\n", destroy_func);

    // Call the function (destroy the object)
    destroy_func(renderer);
    printf("Returned from destroy function...\n");
    FreeLibrary(hDll);
    return 0;
}


Here is a fixed up harness:


#include <windows.h>
#include <iostream>
#include <vector>
#include <gdiplus.h>

// For input handling
#include <string>
#include <iostream>

using namespace Gdiplus;

#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "user32.lib")

typedef int (__stdcall *GetBarcodeRenderer_t)(int, void**);
typedef long (__thiscall *ValidateDataFunc)(void*, wchar_t*);
typedef long (__thiscall *DrawToDCFunc)(void*, HDC, RECT*, wchar_t*);
typedef void (__thiscall *DestroyFunc)(void*);

// Helper function to get the CLSID of an encoder (e.g., PNG)
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) {
    UINT num = 0, size = 0;
    GetImageEncodersSize(&num, &size);
    if (size == 0) return -1;

    ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
    if (!pImageCodecInfo) return -1;

    GetImageEncoders(num, size, pImageCodecInfo);
    for (UINT i = 0; i < num; i++) {
        if (wcscmp(pImageCodecInfo[i].MimeType, format) == 0) {
            *pClsid = pImageCodecInfo[i].Clsid;
            free(pImageCodecInfo);
            return 0;
        }
    }
    free(pImageCodecInfo);
    return -1;
}

int main() {
    // Read input from stdin
    std::string input;
    std::getline(std::cin, input);

    if (input.size() < 2) {  // Need at least 2 bytes (1 for type, rest for barcode data)
        std::cerr << "Error: Input must be at least 2 bytes long!\n";
        return 1;
    }

    // Extract first byte for barcode type and remove it from input
    int barcodeType = static_cast<unsigned char>(input[0]) % 12;
    input = input.substr(1);  // Remove first byte

    // Ban barcode types 8 and 9
    if (barcodeType == 8 || barcodeType == 9) {
        printf("Error: Barcode type %d is banned!\n", barcodeType);
        return 1;
    }

    // Convert remaining input to wide string (wchar_t*)
    std::vector<wchar_t> winput(input.begin(), input.end());
    winput.push_back(L'\0');  // Null-terminate
    wchar_t* test_str = winput.data();

    // Load MSBARCODE.DLL
    // HMODULE hDll = LoadLibrary(L"MSBARCODE.dll");
    HMODULE hDll = LoadLibrary("MSBARCODE.dll");
    if (!hDll) {
        std::cerr << "Failed to load DLL\n";
        return 1;
    }

    // Get function pointer for `GetBarcodeRenderer`
    GetBarcodeRenderer_t GetBarcodeRenderer =
        (GetBarcodeRenderer_t) GetProcAddress(hDll, "GetBarcodeRenderer");

    if (!GetBarcodeRenderer) {
        std::cerr << "Failed to get function address\n";
        FreeLibrary(hDll);
        return 1;
    }

    // Call the function to get the renderer object
    void* renderer = nullptr;
    int result = GetBarcodeRenderer(barcodeType, &renderer);
    if (!renderer) {
        printf("Error: Failed to get barcode renderer!\n");
        FreeLibrary(hDll);
        return 1;
    }

    // Get vtable and function pointers
    void* vtable_ptr = *(void**)renderer;
    int function_offset = 8 * 5;  // At index 5
    int draw_function_offset = 8 * 7;  // At index 7

    ValidateDataFunc validate_func = *(ValidateDataFunc *)((char *)vtable_ptr + function_offset);
    DrawToDCFunc draw_func = *(DrawToDCFunc *)((char *)vtable_ptr + draw_function_offset);
    DestroyFunc destroy_func = *(DestroyFunc *)((char *)vtable_ptr + 0x0);

    if (!validate_func || !draw_func || !destroy_func) {
        printf("Error: Failed to retrieve required function pointers!\n");
        FreeLibrary(hDll);
        return 1;
    }

    // Validate barcode data
    long res = validate_func(renderer, test_str);
    if (res != 0) {
        printf("Invalid data passed. Returning...\n");
        return 1;
    }

    // Create a memory DC
    HDC hdcScreen = GetDC(NULL);
    HDC hdcMem = CreateCompatibleDC(hdcScreen);
    ReleaseDC(NULL, hdcScreen);

    int width = 300, height = 100;
    HBITMAP hBitmap = CreateCompatibleBitmap(hdcMem, width, height);
    SelectObject(hdcMem, hBitmap);

    RECT rect = {0, 0, width, height};

    // Ensure the background is filled
    HBRUSH hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
    FillRect(hdcMem, &rect, hBrush);

    // Draw the barcode
    long res2 = draw_func(renderer, hdcMem, &rect, test_str);
    if (res2 != 0) {
        printf("Error: DrawToDC failed! Exiting...\n");
        return 1;
    }

    // Initialize GDI+
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    // Convert HBITMAP to Bitmap
    Bitmap* bmp = Bitmap::FromHBITMAP(hBitmap, NULL);
    if (!bmp || bmp->GetLastStatus() != Ok) {
        printf("Error: Failed to create Bitmap object!\n");
        return 1;
    }

    // Save as PNG
    CLSID pngClsid;
    if (GetEncoderClsid(L"image/png", &pngClsid) != 0) {
        printf("Error: Could not find PNG encoder!\n");
        return 1;
    }
    bmp->Save(L"barcode.png", &pngClsid, NULL);

    // Cleanup
    DeleteObject(hBitmap);
    DeleteDC(hdcMem);
    delete bmp;
    GdiplusShutdown(gdiplusToken);

    // Destroy the renderer
    destroy_func(renderer);

    FreeLibrary(hDll);
    printf("Barcode saved as 'barcode.png'\n");
    return 0;
}

now we just need to make the performance a bit better and then writeup a winafl script and start fuzzing!!!

Actually now we have a working fuzzer, but you should check that all of the verifydata functions actually only take the data stuff into them, otherwise you could be in troubel…