VMProtect and dbghelp.dll bug in export processing

kao

If your Olly is crashing when loading executable protected by VMProtect, you most likely have outdated dbghelp.dll somewhere on your path. Grab the latest version from Microsoft and put it in the Olly folder.

Well, that might be enough to work around the issue that I had - but I still wanted to know what's causing the crash.

Cause of the problem

If you try to debug Olly with another Olly, you'll see the Access Violation happening somewhere in dbghelp.dll:

Log data, item 0
 Address=6D529B91
 Message=Access violation when reading [C4983C3E]
6D529B8E   8B55 F4          MOV EDX,DWORD PTR SS:[EBP-C]
6D529B91   66:833C42 00     CMP WORD PTR DS:[EDX+EAX*2],0
6D529B96   75 07            JNZ SHORT DBGHELP.6D529B9F

Check register values in Olly:

EAX 00000000   <----------------
ECX 00000001
EDX C4983C3E   <----------------
EBX 0458A390
ESP 0018A450
EBP 0018AC94
ESI 045EE7E8
EDI 045EF738
EIP 6D529B91 DBGHELP.6D529B91

For some reason, value in EDX is garbage and therefore access violation happens.

Call stack doesn't tell us much:

Call stack of main thread
Procedure / arguments                 Called from              Name from PDB
DBGHELP.6D52997D                      DBGHELP.6D52ACFD         LoadExportSymbols(struct _MODULE_ENTRY *, struct _IMGHLP_DEBUG_DATA *)
DBGHELP.6D52A755                      DBGHELP.6D52B035         load(char *, DWORD)
DBGHELP.6D52ADB8                      DBGHELP.6D5264B2         InternalLoadModule(char *, char *FullPath, char *Str1, unsigned __int64, unsigned __int32, void *, struct _DBGHELP_MODLOAD_DATA *, unsigned __int32)
DBGHELP.SymLoadModuleEx               DBGHELP.6D526502              
DBGHELP.SymLoadModule64               DBGHELP.6D526522            
DBGHELP.SymLoadModule                 OLLYDBG.00491502

And same piece of code in IDA doesn't help much either:

.text:6D529B8E loop_check_something:                   ; CODE XREF: LoadExportSymbols(_MODULE_ENTRY *,_IMGHLP_DEBUG_DATA *)+225j
.text:6D529B8E                 mov     edx, [ebp+ptrAllocatedMemory]
.text:6D529B91                 cmp     word ptr [edx+eax*2], 0 ; <----------------
.text:6D529B96                 jnz     short loc_6D529B9F
.text:6D529B98                 add     [ebp+var_10], 10h
.text:6D529B9C                 inc     [ebp+arg_4]
.text:6D529B9F
.text:6D529B9F loc_6D529B9F:                           ; CODE XREF: LoadExportSymbols(_MODULE_ENTRY *,_IMGHLP_DEBUG_DATA *)+219j
.text:6D529B9F                 inc     eax
.text:6D529BA0                 cmp     eax, ecx
.text:6D529BA2                 jb      short loop_check_something

So, it's debugging time! Set breakpoint to start of LoadExportSymbols, then set hardware breakpoint on write to address [ebp+ptrAllocatedMemory].

First hit is initialization of variable with 0:

.text:6D529986                 xor     ecx, ecx
.text:6D529988                 test    byte ptr dword_6D57F438+1, 4
.text:6D52998F                 mov     [ebp+ptrAllocatedMemory], ecx

Second hit stores the address of allocated memory:

.text:6D529AA7                 call    _pMemAlloc@4    ; pMemAlloc(x)
.text:6D529AAC                 xor     ecx, ecx
.text:6D529AAE                 cmp     eax, ecx
.text:6D529AB0                 mov     [ebp+ptrAllocatedMemory], eax
.text:6D529AB3                 jz      loc_6D529D56

And third time is a charm:

.text:6D529AF5                 lea     edx, [ebp+exportFunctionName] ; 0018A45C
.text:6D529AFB                 sub     edx, eax        ; EDX = FBAD009A
.text:6D529AFD
.text:6D529AFD loop_strcpy_overflows:                  ; CODE XREF: LoadExportSymbols(_MODULE_ENTRY *,_IMGHLP_DEBUG_DATA *)+188j
.text:6D529AFD                 mov     cl, [eax]
.text:6D529AFF                 mov     [edx+eax], cl   ; <----------------
.text:6D529B02                 inc     eax
.text:6D529B03                 test    cl, cl
.text:6D529B05                 jnz     short loop_strcpy_overflows

Good folks at Microsoft have left us with a nice buffer overflow. exportFunctionName is defined as byte array of size 2048 bytes. Any exported function name longer than that will cause stack overflow and (possibly) subsequent crash.

010Editor with PETemplate confirms that the export name is indeed very long (3100 chars):

From what I can tell, it's a similar (but not the same) bug to what was described by j00ru at http://j00ru.vexillium.org/?p=405 (see "PE Image Fuzzing (environment + process)")

Stay safe!

P.S Here's an example file, if you want to test your Olly: https://forum.tuts4you.com/topic/38963-vmprotect-professional-v-309-custom-protection/
P.P.S. CFF Explorer, HIEW and IDA do not show us any exports in this example file - but that's a matter of another story..

IDA bug in PE export processing

kao

Hi, I'm back from vacation. And now I'm catching up on all the things that have happened during that time. So, here's a short writeup regarding publicly-known IDA bug and how it will (not) affect reversers.

It was supposed to be a long post showing how to use PatchDiff to locate patched code and then backport it. But, as you'll see later, that's not necessary at all. Maybe another time..

Initial research by Palo Alto

When checking my RSS feed, I stumbled upon the article by Palo Alto researchers called "The Dukes R&D Finds a New Anti-Analysis Technique". It stated:

Using the exported functions by ordinal meant the exported function name was unnecessary, which allowed the developer of this DLL to leave the names for the exported functions blank ... The less obvious reason is that it takes advantage of a bug in the popular IDA disassembler that was recently fixed in the latest version of IDA.

Bug in IDA?! How nice, I want to test this!

Testing the bug

Palo Alto report contained most of the information to reproduce the issue. But IDA 6.95 changelog was even more detailed about what was fixed:

BUGFIX: PE: IDA would not detect DLL exports with empty names
BUGFIX: PE: IDA would show no exports if the export directory's DLL name was an empty string

Armed with the detailed description, I used MASM32 package and their Examples to build a DLL file.

Empty DLL name

First, I took hex editor and changed DLL name in export directory.
export_dll_name_1
export_dll_name_2
Now the exported DLL name is 0-length string. Let's see what IDA does..

I started with IDA 6.95 Demo you can download from official site. No surprises here, the bug is fixed:
export_dll_name_IDA695

Then I took legit copy of IDA 6.90. As already demonstrated by Palo Alto, it's buggy:
export_dll_name_IDA690

Naturally, I wanted to see how old this bug is. So, I took a copy of IDA 6.80. Surprise, surprise, it's not buggy!
export_dll_name_IDA680
So, it looks like this bug was introduced in IDA 6.90.

Empty export name

For completeness sake, I repeated the experiment with empty exported API name.
export_api_name_1
export_api_name_2
The results were identical, the bug is only present in IDA 6.90.

How it affects you?

If you're using IDA Free, latest version is 6.95. You're good.
If you're using legit IDA, you have received the updated version 6.95. You're good.
If you're using the latest publicly leaked version of IDA (6.80), it didn't have the bug. So, you're good, too.

To sum it up - it's a fun bit of information but no one is really affected. Good news, I guess. 🙂

Example DLL files if you want to verify your tools: https://www.mediafire.com/?c9t6hm4icd3kk46