2

There is a great little closed-source freeware Windows program (written in Delphi) that I have been using for years. WinBar shows a little bar above the TaskBar which gives some system information and provides access to a few useful functions.

Unfortunately it has a bug that is pretty annoying. The Memory Usage component only shows half of the installed memory and displays 0MB used until at least half of the memory has been used, then it starts counting up from there (for example, if 4GB are installed, and 1GB is in use, it says 0MB/2047MB; if 3GB are in use, it says 1024MB/2047MB).

It looks like this is due to it using a signed 32-bit value for the installed and used memory count. That should be a pretty easy fix, but alas, development of it has long since been discontinued (there is a 2.x version on SourceForge—also discontinued—which does not suffer from this bug, but it is too different and I do not like it).

I want to dig in and fix this by finding the bits of code that handle the relevant values and change them to treat it as unsigned. This might be very easy (change a printf format specifier) or it might be difficult, depending on the nature of the code (no way to tell without seeing the exact assembler instructions).

I tried running it through WDasm32 a while back but couldn’t find any good leads. It shows the value in a ThunderRT6PictureBoxDC control in the bar.

Does anybody have any advice on how to accomplish this?

3
  • (WinBar 1.2.95 is available on their old site.)
    – Synetech
    Jul 11, 2015 at 21:35
  • 3
    ThunderRT6 sounds like VB6, not Delphi.
    – Igor Skochinsky
    Jul 11, 2015 at 21:35
  • How about looking at the 2.x sources to find out how & where the output is generated? This should give you a clue as to what to look for, assuming that not everything changed from a previous version. (Oldest on SF seems to be from 2008, but already V2.0) - Or, go the straight way and contact the author for an old version of the source.
    – JimmyB
    Jul 13, 2015 at 11:56

1 Answer 1

4

As @Igor Skochinsky has already commented, the software is indeed written in Visual Basic 6 compiled in native mode (VB6 also supports P-mode). You can verify this with a PE Info tool like Detect It Easy (but before that you need to unpack the ASPack wrapper).

The cause of the bug is due to multiple reasons.

The software uses an WinApi Function GlobalMemoryStatus to retrieve the System Memory currently in use. Now there are a couple of quirks of this function:

First

On computers with more than 4 GB of memory, the GlobalMemoryStatus function can return incorrect information, reporting a value of –1 to indicate an overflow. For this reason, applications should use the GlobalMemoryStatusEx function instead.

Hence if your system has more than 4 GB of memory, the output from this function is not reliable, and hence may display erroneous values. Fixing this is difficult.

Second

On Intel x86 computers with more than 2 GB and less than 4 GB of memory, the GlobalMemoryStatus function will always return 2 GB in the dwTotalPhys member of the MEMORYSTATUS structure. Similarly, if the total available memory is between 2 and 4 GB, the dwAvailPhys member of the MEMORYSTATUS structure will be rounded down to 2 GB. If the executable is linked using the /LARGEADDRESSAWARE linker option, then the GlobalMemoryStatus function will return the correct amount of physical memory in both members

To fix this bug, you need to make the application Large Address Aware. This can be done by loading it in CFF Explorer and check the flag App can handle > 2GB address space under
Nt Headers -> File headers -> Characteristics.


Now, to convert the integer memory value obtained from GlobalMemoryStatus to a string the app uses the CStr function.

VB6 has no unsigned 32 bit integer data type. The only unsigned data type is an 8 bit Byte.

Suppose your system has more than 2GB but less than 4GB memory and that you have already fixed the second bug as mentioned above.

Let's assume your system has 3GB memory. This is equivalent to 3,22,12,25,472 bytes in decimal or 1100 0000 0000 0000 0000 0000 0000 0000 bytes in binary.
Since the MSB of this value is 1, it will be treated as negative when passed to CStr. Thus the resultant string would be - 1GB instead of 3GB. There is also no easy fix for this.

So all-in-all the software is only usable when your system has less than 2GB memory.

UPDATE (Signed Integer Bug Fix)

The signed integer bug can be patched to make the application usable to 4GB. Patch the binary so as to convert theSAR (signed shift) to SHR (unsigned shift) instruction.

Original Code

00092DCD  and edx,000FFFFF
00092DD3  add eax,edx
00092DD5  sar eax,14        ; <<<<<<<<<<<
00092DD8  mov dword ptr [ebp-28],eax
00092DDB  mov eax,dword ptr [ebp-54]
00092DDE  cdq
00092DDF  and edx,000FFFFF
00092DE5  add eax,edx
00092DE7  sar eax,14        ; <<<<<<<<<<<
00092DEA  mov dword ptr [ebp-30],eax

Patched Code

00092DCD  and edx,000FFFFF
00092DD3  add eax,edx
00092DD5  shr eax,14        ; <<<<<<<<<<<
00092DD8  mov dword ptr [ebp-28],eax
00092DDB  mov eax,dword ptr [ebp-54]
00092DDE  cdq
00092DDF  and edx,000FFFFF
00092DE5  add eax,edx
00092DE7  shr eax,14        ; <<<<<<<<<<<
00092DEA  mov dword ptr [ebp-30],eax

Incorporating the large address aware and signed integer patch, the application can be made usable to 4 GB of memory.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.