Close

A Two-Bit Fix to a One-Grand Tool

lauri-pirttiahoLauri Pirttiaho wrote 10/17/2022 at 18:00 • 5 min read • Like

As computers and operating systems age and are replaced with "better" ones it frequently happens that we lose access to some old (but not obsolete) software, or worse yet, to hardware devices, that do not have any replacement available. Or if they do, they are either inferior to the old one or otherwise not suitable replacements (sometimes due to price, of course). So, we keep around old computer setups that we use only because of that particular SW or HW we like (or need) to use.

Sometimes it happens the old SW or HW "almost" works in the new environment, except a minor issue of it crashing in specific situations. If you can work around those situations, you are file, and that becomes part of your patterns, how you use it. Sometimes those patterns are so restricting, that you begin hitting your head to the crashes annoyingly frequently. So you begin thinking, something in the way of, this must be a small issue; would there be a simple work around, if only you had the source code. It would be so simple to fix. Wouldn't it?

This is the story of a small fix, a two-bit fix, that saves an old, but certainly not obsolete, at a time relatively expensive (for personal use) logic analyzer from refuse pile.

The device is LeCroy LogicStudio 16 and the SW is its UI, which crashes in very predictable way. If you keep in mind that you should not zoom in a certain way but instead do it in a bit more complicated way it won't crash. But that second you don't keep it in your mind, this is what happens:

Clicking the details announces:

System.ArgumentException: Rectangle '{X=0,Y=0,Width=0,Height=0}' cannot have a width or height equal to 0.
   at System.Drawing.Bitmap.Clone(RectangleF rect, PixelFormat format)
   at OpenTK.Graphics.Text.GdiPlusGlyphRasterizer.Rasterize(Glyph glyph, TextQuality quality)

This UI last released late 2014, it turns out, uses a beta release of the OpenTK 1.0 from late 2009 as its GUI library. That probably was OK with Windows Vista, but apparently won't co-operate well with more recent .NET environments. Fortunately, with some SW archaeology it was possible to unearth the source code of the Rasterize function behind this error, and this is what it does:

RectangleF r2 = new RectangleF();
...
r2 = FindEdges(glyph_surface, true);
...
return glyph_surface.Clone(r2, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

The problem is in finding the edges, which goes like:

Rectangle rect = Rectangle.FromLTRB(0, 0, FindRightEdge(bmp, data.Scan0), FindBottomEdge(bmp, data.Scan0));
...
return rect;

 The immediate questions are, why does it return rectangle of size 0 x 0 pixels, and why did it not crash in Vista?

The function for the right edge is:

int FindRightEdge(Bitmap bmp, IntPtr ptr)
{
    for (int x = bmp.Width - 1; x >= 0; x--)
        for (int y = 0; y < bmp.Height; y++)
            unsafe
            {
                if (((Pixel*)(ptr) + y * bmp.Width + x)->A != 0)
                    return x + 1;
            }

    return 0;
}

so if all pixels are transparent, this function return 0 as the right edge. Same happens with the bottom edge. Then without checking the cloning is attempted, leading to the crash. 

Hard to say why it worked in past. But it is clear that if these functions would return 1 instead of 0 when the bitmap appears to be empty, the crash would not happen.

Because of the way the .NET application is tied to DLLs it isn't possible to recompile the DLL. That seems not to work. But! As this change is very small maybe changing the return value by a binary edit of the DLL is possible.

And, indeed, dumping the responsible DLL with ildasm (comes with VisualStudio and located in Microsoft SDKs) tells:

    IL_0048:  /* 06   |                  */ ldloc.0
    IL_0049:  /* 17   |                  */ ldc.i4.1
    IL_004a:  /* 59   |                  */ sub
    IL_004b:  /* 0A   |                  */ stloc.0
    IL_004c:  /* 06   |                  */ ldloc.0
    IL_004d:  /* 16   |                  */ ldc.i4.0
    IL_004e:  /* 2F   | BB               */ bge.s      IL_000b

    IL_0050:  /* 16   |                  */ ldc.i4.0
    IL_0051:  /* 2A   |                  */ ret
  } // end of method GdiPlusGlyphRasterizer::FindRightEdge

Changing the last ldc.i4.0 (byte 0x16) to  ldc.i4.1 (byte 0x17) should do it. Enter HxD. And search for this sequence of bytes reveals

The two places are these two functions. Changing in both places the byte 0x16 to 0x17 before the return instruction 0x2A will effect the fix.

One software fixed. One definitely worth of saving tool, LogicStudio 16, given a new life.

Like

Discussions