Welcome to Community Server Sign in | Join | Help
In the course of security consulting, I often find myself in a situation where I've identified a security vulnerability but I need to create a proof-of-concept to show the feasibility of the vulnerability's exploitability.

Recently, I found an elevation-of-privilege vulnerability in which an application that runs as a privileged user loads a DLL from a location that is writeable by an unprivileged attacker. An unprivileged attacker could write a malicious DLL to this location, and when loaded by the given application, the DLL's code would execute in the context of a privileged user.

Ideally, we'd like the "malicious" DLL to have all the functionality of the DLL that the application expected to load, including the same exported functions. In other words, what I really wanted was an easy way to patch an existing DLL to inject my "malicious" code to run before the DLL's original DllMain code was executed, after which the original DllMain code would be called and the DLL would continue to operate as normal. Unfortunately, I know of no programs like this that patch DLLs on disk, so I made my own.

The program attached to this blog post redirects a given DLL's entrypoint (which originally pointed to DllMain) to point to code that has been patched in to the DLL. This patched in code will add a given user to the Administrators group in Windows (assuming that it's being run in the context of a privileged user), after which it will transfer control back to the DLL's original DllMain. The patcher also updates the Import Table for the DLL since the patched in code relies on the function NetLocalGroupAddMembers(...) from netapi32.dll. The only other side effect of the patcher is that it clears the Bound Imports for the DLL; the only adverse side effect of this is that this may cause the DLL to take a few extra milliseconds to load.

The patcher is compatible with both 32-bit and 64-bit DLLs.

You can run the patcher executable without command line arguments for usage instructions. This is version 1.0, so please e-mail me if you encounter any problems or have any questions.

I was developing some automation code recently and found that a process that I was injecting code into was crashing. At first I thought it was an error in my injected code, but when I looked at the crash-dump, I was amazed to see that the issue was in MFC42.DLL:

MOV EBX,104
PUSH EBX
LEA EAX,DWORD PTR SS:[EBP+szBuffer]
PUSH EAX
PUSH DWORD PTR DS:[ESI+6C]
CALL DWORD PTR DS:[<&KERNEL32.GetModuleFileNameA>
LEA EAX,DWORD PTR SS:[EBP+szBuffer]
PUSH 2E
PUSH EAX
CALL DWORD PTR DS:[<&msvcrt._mbsrchr>]
POP ECX
POP ECX
MOV DWORD PTR SS:[EBP-80],EAX
MOV BYTE PTR DS:[EAX],0     <-- Crash!

The code above is from MFC42.DLL, version 6.2.4131.0 from Windows XP SP2. It effectively does the following:

GetModuleFileName(NULL, szBuffer, MAX_PATH);
*(_mbsrchr(szBuffer, '.')) = 0;

The function _mbsrchr(...) returns NULL if the character searched for is not found. This means that if there is no '.' in the current process's filename (which was the case for the file I was testing) then the highlighted line above will try to write the byte 0x00 to address 0x00000000, which will cause a crash.

I figured that this was some obscure function from MFC42.DLL that most applications don't make use of, however, after a little digging it turns out that this code is in CWinApp::SetCurrentHandles(), which is called by AfxWinInit(...). From http://msdn2.microsoft.com/en-us/library/w04bs753(vs.80).aspx:

"[AfxWinInit] is called by the MFC-supplied WinMain function, as part of the CWinApp initialization of a GUI-based application, to initialize MFC."

In other words, almost every MFC GUI program executes the code snippet above!

AAs surprised as I was by this, I figured that surely this had been fixed for Vista. Believe it or not, the same issue exists! Below is the code from MFC42.DLL version 6.6.8063.0 from Windows Vista Gold:

PUSH 104
LEA EDX,DWORD PTR SS:[EBP+szBuffer]
MOV [EDI+0C],ECX
MOV EAX,DWORD PTR DS:[ESI+6C]
PUSH EDX
PUSH EAX
CALL DWORD PTR DS:[<&KERNEL32.GetModuleFileNameA>
TEST EAX,EAX
JZ LOC_722F1484
CMP EAX,104
JZ LOC_722F1484
LEA ECX,[EBP+szBuffer]
PUSH 2E
PUSH ECX
CALL __mbsrchr
MOV EBX,EAX
ADD ESP,8
TEST EBX,EBX
MOV [EBP+VAR_310],EBX
JZ LOC_7230DB7D
...



__mbsrchr:
MOV EDI,EDI
PUSH EBP
MOV EBP,ESP
POP EBP
JMP DWORD PTR DS:[<&msvcrt._mbsrchr>]


LOC_7230DB7D:
...
JMP DWORD PTR DS:[<&msvcrt.CxxThrowException>]

While the code above checks for the lack of a '.' in the filename, it still throws an exception and causes a crash if there's no '.'.

The good news is that it doesn't seem easy to accidentally execute an executable file without a '.' in the filename in Vista:

C:\>copy c:\windows\notepad.exe notepad_exe
1 file(s) copied.

C:\>notepad_exe
'notepad_exe' is not recognized as an internal or external command, operable program or batch file.

C:\>start notepad_exe
[This opens the "Open With" dialog box in Explorer instead of executing the file.]

However, it is still possible to run non-dotted-files via API functions like CreateProcess(...) to cause the crash described above.

I am working on an automation system that involves forcefully terminating a process that creates an icon in the Taskbar Notification Area (no, not the "system tray"). It is the responsibility of the process that creates an icon in the Taskbar Notification Area to remove the icon when the process exits, however, since I am using TerminateProcess(...) to remotely kill the process, the code to remove the icon never gets executed. As such, the icon remains in the Taskbar Notification Area until one moves the mouse cursor over the icon, at which point it disappears.

Since this is an automation system that's being developed, this icon-creating process will get executed many times, and if left unchecked would end up leaving hundreds of icons in the Taskbar Notification Area (one icon per execution). That's bad.

Despite my best Googling efforts ("refresh notification area", "redraw system tray", etc.), I wasn't able to find elegant code to solve this problem. I found some novel solutions, though. The most common suggestion was to use SetCursor(...) to drag the mouse cursor around the Taskbar Notification Area; while this works, it's an ugly hack and is actually quite slow. One of my "favorite" suggestions was to try to associate each icon in the Taskbar Notification Area with a process, then monitoring each process for termination, then deleting the icon once the given process terminates (talk about overkill... geeze).

When a user moves the mouse over a "dead icon" in the Taskbar Notification Area, some window message must get sent to the window to cause it to say to itself, "hey, the mouse is over me, so let me see if the process that created this icon is still alive.... Oh, it's not? Let me remove the icon, then." I wanted to find what window message was causing that code to fire so that I could send that message to the window myself.

I started up Microsoft Spy++ and saw the following information for the Taskbar Notification Area and its parent windows:

A useful feature of Microsoft Spy++ is that it allows you to monitor window messages sent to a given window. I started monitoring the window messages getting sent to the "Notification Area" window without moving my mouse over the window and saw the following messages getting sent:

The messages above clearly had nothing to do with me moving my mouse (since I wasn't moving my mouse over the window), so I configured Microsoft Spy++ to filter out those messages. Then I moved my mouse over the "dead icon" in question and saw the following messages:

<00001> 00010056 S WM_NCHITTEST xPos:1491 yPos:1024
<00002> 00010056 R WM_NCHITTEST nHittest:HTCLIENT
<00003> 00010056 S WM_SETCURSOR hwnd:00010056 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
<00004> 00010056 R WM_SETCURSOR fHaltProcessing:False
<00005> 00010056 P WM_MOUSEMOVE fwKeys:0000 xPos:5 yPos:0
<00006> 00010056 S TB_HITTEST pptHitTest:022BFC18
<00007> 00010056 R TB_HITTEST iIndex:0
<00008> 00010056 S TB_DELETEBUTTON iButton:0
<00009> 00010056 R TB_DELETEBUTTON fSucceeded:True

Aha! So either WM_NCHITTEST, WM_SETCURSOR, WM_MOUSEMOVE, or TB_HITTEST leads to the TB_DELETEBUTTON getting sent. After trying to send each window message manually with SendMessage(...), I found which window message was the catalyst: WM_MOUSEMOVE.

With this new-found knowledge, I was able to whip up the following code to refresh the Taskbar Notification Area:

#define FW(x,y) FindWindowEx(x, NULL, y, L"")

void RefreshTaskbarNotificationArea()
{
    HWND hNotificationArea;
    RECT r;

    GetClientRect(
        hNotificationArea = FindWindowEx(
            FW(FW(FW(NULL, L"Shell_TrayWnd"), L"TrayNotifyWnd"), L"SysPager"),
            NULL,
            L"ToolbarWindow32",
            L"Notification Area"),
        &r);
    
    for (LONG x = 0; x < r.right; x += 5)
        for (LONG y = 0; y < r.bottom; y += 5)
            SendMessage(
                hNotificationArea,
                WM_MOUSEMOVE,
                0,
                (y << 16) + x);
}

I'm reviewing the source code for a rather large project this week and I wanted to update my Facebook status by saying something like, "Jason is reviewing 100,000 lines of Java for security vulnerabilities." However, being the perfectionist that I am I wanted to give the real number of lines of code.

I wasn't aware of any built-in functionality in Visual Studio to do this, and after three minutes of Googling, I found a lot of Visual Studio plugins that could do this but unfortunately I didn't find any instructions on how to do this with just plain Visual Studio. And honestly, I didn't want to install a plugin (see http://blogs.msdn.com/oldnewthing/archive/2006/03/22/558007.aspx :)

I figured I could whip up a short C# program to do this, but even that seemed a little over-kill for such a simple task. Then I realized I could do this from a standard console window command prompt:

cmd /v:on
set lines = 0
for /r %a in (*.java) do (find /v /c "" "%a" > %temp%\temp.txt
for /f "tokens=6" %b in (%temp%\temp.txt) do (set /a lines += %b))
echo %lines%

The "tokens=6" part is specific to the source code directory structure for this particular project, and if any of the source code subdirectories contained spaces, you'd have to tweak the code above a little. But hey, it worked out quite nicely, and it was a much cleaner solution than installing a plugin.

And I'm sure there's an even shorter/simpler way to do this from a standard command prompt than with what I have above. Feel free to post cleaner "solutions" :)

(BTW, the actual number of lines turned out to be 348,523... that should keep me busy for a while.)

Please see PART 1.

While the return value of FindWindowA is used to determine whether or not Outlook terminates its process, there's another issues when it comes to using a separate profile.

Outlook calls MAPILogonEx without the MAPI_NEW_SESSION bit set. This causes Outlook to try to use an existing MAPI session if it can find one. Because of this, Outlook doesn't present the user with the option to choose a different profile in the second instance of Outlook; it will instead just use the profile that the first instance is using. (Why I didn't hit this issue in PART 1 is not clear.)

As such, to fully overcome Outlook's single-instance limitation, it is necessary to spoof the return value of the FindWindowA call in PART 1 and to set the MAPI_NEW_SESSION bit in the flFlags argument passed to MAPILogonEx.

After submitting my first patent two years ago to the US Patent Office, it has finally been published online!

You can read all the juicy details here and you can see diagrams here if you have a TIFF-renderer browser plug-in.

This patent was from when I was still on the Firewall team at Microsoft, so it's network-related. The other patents of mine that should get published on the web over the next two years are from when I was on the Anti-Malware team at Microsoft, so they're related to binary analysis... in other words, even cooler than this one ;)

If you use Outlook and have multiple e-mail account profiles, you know how frustrating it is to have Outlook restrict you to a single running instance of Outlook per interactive login. For those of you not familiar with this "feature", here's the scoop: if you have one instance of Outlook running and then launch another instance, a new Outlook window is created in the context of the original instance, but you don't have the option to load another e-mail account profile. This is a pain because it requires you to close and restart Outlook each time you want to check a different e-mail account (assuming you have a separate profile for each account).

Tim Mullen, a colleague of mine, had the ingenious idea of using RunAs to launch the second Outlook process as another user, to try to circumvent whatever "feature" was restricting Outlook to a single instance. "What a great idea!" I thought, and I kicked myself for not having thought of that myself! But when we tested it out, it had the same results as running a second instance of Outlook without RunAs; an extra window popped up for the first instance and we weren't given the option to load another profile.

This piqued my interest and I wondered how Outlook was determining whether or not another instance was already running in the interactive login session.

Typically when I'm trying to figure out how specific functionality works, I have an API function or string to use as my guide. For example, if I'm red-teaming a DRM solution and I get a message box saying, "Invalid license key." then I can search in the binary for that string to see what code references it, or I can set a breakpoint on the Windows API functions that display message boxes. However, for the case of Outlook here, I didn't have any strings to base my investigation on, and I didn't know which API function(s) were being used to check for the first instance.

My first idea was to use an API logging tool like AutoDebug and run it once on the first Outlook session and once on the second Outlook session. I could then compare the API call logs and see where they differed, and then begin to investigate what caused them to differ at that point. However, I quickly found that API loggers such as AutoDebug are not suited for such a heavyweight program as Outlook (which imports a few thousand DLLs and a few million API functions (yes, I'm exaggerating, but it's still a lot)).

My second idea was to use a conditional-branch logger, such as http://www.woodmann.com/ollystuph/Conditional_Branch_Logger_v1.0.zip and run the same comparison as described above. However, I didn't have that plugin downloaded at the time and I didn't have Internet access, so I had to make-do with what was already on my laptop.

I used Process Explorer to watch what happens when the second instance of Outlook is launched. Sure enough, the process starts and then terminates. So I used OllyDbg to set a breakpoint on ExitProcess(...) to see if I could get a decent call-stack to see what code in Outlook led to the ExitProcess(...) call. The good news is that this allowed me to find the code that led to the process termination. The bad news is that it was called via _cexit(...) from ___tmainCRTStartup(...), so whatever code was detecting the first instance of Outlook was bailing out via ret's, not via a direct call to _cexit(...) or ExitProcess(...).

This led me to the old trustworthy Trial-and-Error-with-F8 method. The idea is simple -- starting from the process's Entry Point, step over (F8 in OllyDbg) every function call until you see the desired results, at which point you know the code in question lies within that function call. For this case, I was watching for a new window to pop up in the context of the first Outlook instance; by that time the check would already have been made to see if another instance of Outlook was running. The great thing about this approach is that it's incredibly straight-forward. The downside is that if you're looking for functionality that doesn't happen near the beginning of the process execution, it can be very time consuming. Luckily though, this method worked like a charm for Outlook!

I started the second Outlook process in OllyDbg, stepped over the first call and into a jump. No windows popped up yet, so I hadn't yet stepped over the call-in-question. I kept pressing F8 until I found that when I tried stepping over the call from address 0x2FD251C8 (this of course is specific to my computer; your addresses will differ), an Outlook window popped up in the context of the first Outlook process. So I set a breakpoint on 0x2FD251C8 and restarted my second Outlook process, this time stepping in (F7) to that call and pressing F8 again until I found the next call that opened the first Outlook window. I found that stepping over the call at address 0x2FD25228 caused the window to pop up, so I set a breakpoint on that address, restarted, stepped in, and continued this process for about two minutes until I found the following code:

.text:30006BB7                 push    offset WindowName ; "Microsoft Outlook"
.text:30006BBC                 push    offset aMspim_wnd32 ; "mspim_wnd32"
.text:30006BC1                 mov     [ebp+var_42C], edi
.text:30006BC7                 call    ds:FindWindowA

This looks like the culprit! During Outlook's initialization, it checks to see if a window named "Microsoft Outlook" with class name "mspim_wnd32" exists, and if so, it assumes that another instance is already running. To test this, I set the return value of FindWindowA(...) from the call above to NULL, and Outlook opened a full second instance of itself in a separate process, and allowed me to use a different account profile.

This is a great example of where a very straight-forward reverse-engineering approach (Trial-and-Error-with-F8) can yield excellent results in just a few minutes given the right conditions.

As a disclaimer, I don't know the reason that the Outlook development team decided to restrict Outlook to a single instance. Perhaps multiple instances will cause massive data corruption. In other words, if you're going to patch your Outlook executable so that it does allow for multiple instances, do so at your own risk!

This post continued in Part 2.

Friday, April 20th will be my final day at Microsoft. I will be joining NGS in the coming weeks as a Principal Security Consultant.

I've copied all of my old blog posts from http://blogs.msdn.com to http://www.malwareanalysis.com though unfortunately I was not able to save the old comments.

My new personal e-mail address is jasonATmalwareanalysisDOTcom.

In a previous post, I talked about changing the Subsystem field in the IMAGE_OPTIONAL_HEADER to trick OllyDbg into loading a driver for the purpose of unpacking. However, making this single change is often not enough to be able to load the driver as an EXE in OllyDbg.

From my experience (in other words, I haven't verified this in the Windows source code and I'm not speaking authoritatively here), executable files need to have NTDLL.DLL in their Import Table or have another DLL in their Import Table that will eventually cause NTDLL.DLL to get loaded. I was looking at a driver today that only had NTOSKRNL.EXE and HAL.DLL in its Import Table. The former causes BOOTVID.DLL and KDCOM.DLL to get loaded as well, however nowhere in the import chain does NTDLL.DLL get loaded. Because of this, OllyDbg can't get the driver up and running after we make the Subsystem change.

To solve this problem, we can add NTDLL.DLL (or anything that imports NTDLL.DLL, like KERNEL32.DLL) to the Import Table of the driver and OllyDbg will then be able to load the driver as a new process.

While analyzing a malware sample today, I came across an interesting function. It uses red-herring local variables and red-herring global variables, and even once you get rid of that code, it's still unclear as to what the function does.

 Since you don't have access to the callers of this function, I'll tell you this:

  • The first argument is a null-terminated ASCII string.
  • The second argument is a null-terminated ASCII string.
  • The third argument is an integer.

Your challenge? Tell me what the function does. Your prize? You get to choose the name of the next malware family that I name. Stipulations:

  • Cannot refer to the name of a person, place, or time.
  • Cannot refer to anything obscene or offensive.
  • Cannot be found in a dictionary or web-search.
  • Cannot use camel-casing for compounding words -- must begin with one uppercase letter and end with all lowercase letters.
  • Must be a "generic" name (for example, shouldn't contain the word "bot" or "worm", since I have no idea what class of malware I'll end up naming next).
  • Must be humanly pronouncable.
  • Must be between four and eight letters in length.
  • I have final discretion over the name in case you think of something "bad" that isn't covered by one of the rules above.

The winner is the first person to post a comment that correctly and fully describes in high-level English (not in code) what the function does.

And to in case you think I'm "hiring cheap labor" to analyze this for me, I'll pull a Raymond Chen and say that the MD5 of my analysis is F2F3648B9BE371B4682B728A7A3D920F. Once the correct answer is posted, I'll post my analysis which hashes to that MD5.

Here's the function:

 sub_0           proc near

 var_10          = dword ptr -10h
 var_C           = dword ptr -0Ch
 var_8           = dword ptr -8
 var_4           = dword ptr -4
 arg_0           = dword ptr  8
 arg_4           = dword ptr  0Ch
 arg_8           = dword ptr  10h

                 push    ebp
                 mov     ebp, esp
                 sub     esp, 10h
                 push    ebx
                 push    esi
                 push    edi
                 mov     esi, [ebp+arg_4]
                 mov     [ebp+var_8], 697A259Dh
                 xor     [ebp+var_8], 182Ch
                 inc     dword ptr ds:42C094h
                 and     [ebp+var_C], 0
                 and     [ebp+var_4], 0
                 jmp     short loc_94
 ; ---------------------------------------------------------------------------

 loc_2A:                                 ; CODE XREF: sub_0+A6j
                 xor     ebx, ebx
                 add     [ebp+var_8], 3AA5h
                 inc     dword ptr ds:42C094h
                 xor     edi, edi
                 jmp     short loc_81
 ; ---------------------------------------------------------------------------

 loc_3D:                                 ; CODE XREF: sub_0+8Fj
                 mov     eax, [ebp+var_4]
                 add     eax, edi
                 mov     edx, [ebp+arg_0]
                 movsx   eax, byte ptr [edx+eax]
                 movsx   edx, byte ptr [esi+edi]
                 cmp     eax, edx
                 jnz     short loc_52
                 inc     ebx

 loc_52:                                 ; CODE XREF: sub_0+4Fj
                 mov     ecx, esi
                 or      eax, 0FFFFFFFFh

 loc_57:                                 ; CODE XREF: sub_0+5Cj
                 inc     eax
                 cmp     byte ptr [ecx+eax], 0
                 jnz     short loc_57
                 cmp     ebx, eax
                 jnz     short loc_72
                 inc     [ebp+var_C]
                 mov     eax, [ebp+arg_8]
                 cmp     [ebp+var_C], eax
                 jnz     short loc_72
                 mov     eax, [ebp+var_4]
                 jmp     short loc_C0
 ; ---------------------------------------------------------------------------

 loc_72:                                 ; CODE XREF: sub_0+60j
                                         ; sub_0+6Bj
                 mov     eax, 43C9h
                 mul     [ebp+var_8]
                 mov     [ebp+var_10], eax
                 mov     [ebp+var_8], eax
                 inc     edi

 loc_81:                                 ; CODE XREF: sub_0+3Bj
                 mov     ecx, esi
                 or      eax, 0FFFFFFFFh

 loc_86:                                 ; CODE XREF: sub_0+8Bj
                 inc     eax
                 cmp     byte ptr [ecx+eax], 0
                 jnz     short loc_86
                 cmp     edi, eax
                 jb      short loc_3D
                 inc     [ebp+var_4]

 loc_94:                                 ; CODE XREF: sub_0+28j
                 mov     eax, [ebp+arg_0]
                 mov     ecx, eax
                 or      eax, 0FFFFFFFFh

 loc_9C:                                 ; CODE XREF: sub_0+A1j
                 inc     eax
                 cmp     byte ptr [ecx+eax], 0
                 jnz     short loc_9C
                 cmp     [ebp+var_4], eax
                 jb      short loc_2A
                 mov     eax, 0FFFFh
                 jmp     short loc_C0
 ; ---------------------------------------------------------------------------
                 mov     eax, 514Ah
                 mul     dword ptr [ebp-8]
                 mov     [ebp-10h], eax
                 mov     eax, [ebp-10h]
                 mov     [ebp-8], eax

 loc_C0:                                 ; CODE XREF: sub_0+70j
                                         ; sub_0+ADj
                 pop     edi
                 pop     esi
                 pop     ebx
                 leave
                 retn
 sub_0           endp
 

And here's the raw byte-code for the function above:

5589E583EC105356578B750CC745F89D257A698175F82C180000FF0594C04200
8365F4008365FC00EB6A31DB8145F8A53A0000FF0594C0420031FFEB448B45FC
01F88B55080FBE04020FBE143E39D075014389F183C8FF40803C010075F939C3
7510FF45F48B45103945F475058B45FCEB4EB8C9430000F765F88945F08945F8
4789F183C8FF40803C010075F939C772ACFF45FC8B450889C183C8FF40803C01
0075F93945FC7282B8FFFF0000EB11B84A510000F765F88945F08B45F08945F8
5F5E5BC9C3

I bought my plane ticket a few hours ago for Virus Bulletin 2006. I'm looking forward to rubbing elbows with other virus analysts and discussing the latest and greatest reverse engineering tools and methods.

If you're going to VB'06 as well, send me an e-mail or find me in person and mention my blog and I'll buy you a beer (which shouldn't be too hard seeing as how the conference will be in Montreal)!

I was looking at a malware sample last week that used a variation of Joanna Rutkowska's infamous Red Pill (http://invisiblethings.org/papers/redpill.html) to determine whether or not the malware was being run from inside a Virtual Machine. Based on the Red Pill concept, the guest OS's IDTR should be different from the host OS's IDTR.

I was using Virtual PC to step through the malware sample in OllyDbg, with the goal of skipping the conditional-jump after SIDT led to the detection of my VM (see http://download.intel.com/design/Pentium4/manuals/25366720.pdf#page=275 for details on the SIDT instruction). You can imagine my surprise when SIDT returned 0x8003F400 as the base address of the IDT, which is the same base address of the IDT for my host Windows XP system!

My first thought was that maybe the Virtual PC team figured out some ingenious way to make this happen via the Virtual Machine Additions add-on (see http://www.microsoft.com/technet/prodtechnol/virtualserver/2005/proddocs/vs_tr_components_additions.mspx?mfr=true). So I uninstalled Virtual Machine Additions, rebooted, and tried again. To my continued surprise, OllyDbg was still showing the host OS's IDTR when stepping through the SIDT instruction on my guest OS.

After some more thinking, I thought, "maybe it has something to do with the fact that I'm single-stepping through SIDT in OllyDbg." To test this hypothesis, I set a breakpoint after the SIDT instruction, and ran the program from the start. Sure enough, SIDT returned 0xF9CB6440 as the base address of the IDT that time.

The whole trick behind the Red Pill is that VMs don't typically have the opportunity to intercept SIDT since it's not a privileged instruction. However, when the Trap Flag is set (due to single-stepping), Virtual PC intercepts the int 1 interrupt and can execute the current instruction however it pleases; when it has the opportunity, it will use the host's IDTR for the SIDT instruction.

Hopefully this knowledge will make the Red Pill a little easier for you to swallow (or spit-out if the Trap Flag is set).

People often ask me how to unpack DLLs and drivers. A common assumption is that it is necessary to use OllyDbg's LOADDLL for unpacking DLLs and that a ring-0 debugger such as SoftICE or WinDbg is necessary for unpacking drivers. With a little tweaking, we can use regular OllyDbg to unpack packed DLLs and even many packed drivers.

I don't know about you, but I've always had problems with LOADDLL. Even though it's well documented in OllyDbg's help file (the source is even included in the help file), I'd rather not use it if I don't have to. So how can we load a DLL into OllyDbg so that we can unpack it like we would a normal EXE?

All that you need to do is set the IMAGE_FILE_DLL bit to zero in the Characteristics field of the PE's IMAGE_FILE_HEADER structure. You could use a hex editor to make this change, but it's easier with a PE editor like LordPE. Once this flag is zeroed out, you can load the "DLL" into OllyDbg and OllyDbg and the OS will interpret it as an EXE. You can then unpack it as you would an EXE (trace to the OEP, dump, fix the imports, etc.), and then set the IMAGE_FILE_DLL bit back to one in the unpacked file.

The only catch is that many unpacking stubs check to see if [EBP+0x0C] == 1 (does the fdwReason argument to DllMain equal DLL_PROCESS_ATTACH), and if it doesn't equal 1 then it won't continue to unpack itself. You can fix this problem by looking for this comparison and forcing a jump/no-jump or by manually pushing three DWORDs onto the stack (before executing the first instruction at the EP), the second of which should be 1.

We can use the same PE header patching trick for loading drivers into OllyDbg for unpacking purposes. By setting the Subsystem field to 2 (IMAGE_SUBSYSTEM_WINDOWS_GUI) in the PE's IMAGE_OPTIONAL_HEADER, OllyDbg and the OS will interpret the file as an EXE instead of as a driver. This allows us to trace through the unpacking stub until the code and data are unpacked, and we can dump the process when we find the OEP. Of course if the unpacking stub is trying to execute instructions/functions that need to be executed from ring-0 then we won't be able to unpack it like this. However, if the unpacking stub is just doing a lot of simple XORing to unpack the original code and data, then we should be able to use this trick to successfully unpack the driver with OllyDbg.

It is common to hear reverse engineers throw around the phrase, “forty-thousand hex”. To someone unfamiliar with reverse engineering or debugging in Windows, this phrase would probably be interpreted to mean the value 0x00040000. However, when reverse engineers say, “forty-thousand hex”, they are actually referring to the value 0x00400000.

The value 0x00400000 is commonly seen when doing low-level work in Windows because this is the default base address of EXE files compiled by Microsoft’s C++ compiler.

So why say “forty-thousand hex” instead of “four-hundred-thousand hex”? For starters, the former is easier to say (one less syllable) than the latter. But more importantly, hexadecimal numbers are usually grouped in sets of 2-digits (bytes) instead of in groups of 3-digits as in base 10. As such, a reverse engineer could read 0x00400000 as 0x00,40,00,00. Going from right-to-left, we have 00 in the “tens” place, 00 in the “hundreds” place, and 40 in the “thousands” place.

I do most of my malware analysis statically, which is to say that I typically analyze malware by looking at a static disassembly of it as opposed to stepping through it in a debugger. However, sometimes I come across complicated or confusing code that would be easier to understand by walking through it in a debugger.

I came across such an example the other day. An important branch decision was being made based on the result of a function that used a stack variable that IDA Pro couldn't represent in a simple way. Here's a snippet from the function:

mov     edx, [ebp+arg_0]
add     edx, 108h
push    edx

I could have traced back in the disassembly to figure out what arg_0 + 108h was really pointing to (it turned out to be a global variable and arg_0 was set by the caller of the caller of this function), but I thought that I could save time by loading the target into a debugger and setting a breakpoint on the code above in order to determine what was actually being pushed.

There was a problem, though. This malware launced other instances of itself, and setting a breakpoint on the code above in a debugger didn't work since the parent process never executed that code, only the child instances did. I could have set a breakpoint on CreateProcessA(...), forced it to load the child processes in a suspended state, attached a debugger to the children, then resumed them, but this was more trouble than it was worth. Instead, I opted for another method of attack.

I configured my debugger for Just-In-Time (JIT) debugging (see http://support.microsoft.com/default.aspx?scid=kb;en-us;103861) so that I could attach to a crashed process via the Microsoft Application Error Reporting dialog box (also known as "Dr. Watson" -- see http://blogs.msdn.com/oldnewthing/archive/2005/08/10/449866.aspx). I then overwrote the code above with an int 3 and patched the file, with the expectation that after running the parent program that this would crash the child process, cause the Microsoft Application Error Reporting dialog box to pop up, and allow me to attach to the crashed child process. (It should be noted that this was done on an isolated network in a very controlled environment, and with all of our safeguards in place it was practically impossible for the modified malware to get out of our secure lab.)

I saved the patched file and ran it, waiting eagerly for the Microsoft Application Error Reporting dialog box to appear. To my surprise, nothing happened. As it turned out, the program was using custom Structured Exception Handling (SEH) routines and because of this the int 3 exception was never passed to the operating system so the Microsoft Application Error Reporting dialog box never popped up. To remediate this, I changed my int 3 patch to the following:

mov     eax, fs:[0]
mov     [eax+4], 7c8399f3h
int     3

This effectively overwrote the first exception handler in the SEH chain (see http://www.microsoft.com/msj/0197/exception/exception.aspx) with the default exception handler from kernel32.dll. The address of this handler is of course version-specific; in my case kernel32.dll was US English version 5.1.2600.2180.

With this patch in place, the Microsoft Application Error Reporting dialog box popped up for the child process and I was able to attach my debugger and determine the value of arg_0 + 108h from the original code above.

More Posts Next page »