Code for Concinnity

beautiful and elegant solutions


Tutorial: Using WinDBG to bypass specific functions — WinDBG kung-fu series

Continuing our series, this time we’ll try to use WinDBG to bypass certain functions at runtime. In this example, we’ll try to use WinDBG to ignore assert errors.

Example program

1
2
3
4
5
6
7
8
9
10
11
12
#include <cassert>
#include <iostream>

int main()
{
    int x = 10 - 10;
    assert(x);

    std::cout << "hello world" << std::endl;

    return 0;
}

Objective

The assertion dialog box should not pop up, and the console should show “hello world”.

Here we go

1
2
> cl /EHsc /Zi /Fefred.exe /Fdfred.pdb fred.cpp
> windbg fred.exe

First we need to set a breakpoint at the function we want to bypass. We’ve located the function name to be MSVCR100D!_wassert. You can find this out by looking at the call stack k

1
2
0:000> bp MSVCR100D!_wassert
0:000> g

Now our breakpoint’s been hit, let’s look at what we’ve got on the stack:

1
2
3
0:000> kp1
ChildEBP RetAddr  
0028f754 002a14d6 MSVCR100D!_wassert(wchar_t * expr = 0x002a7840 "x", wchar_t * filename = 0x002a7848 "k:\vsprojects\fred\fred\fred.cpp", unsigned int lineno = 7) [f:\dd\vctools\crt_bld\self_x86\crt\src\assert.c @ 113]

Nice, let’s try printing out the arguments

1
2
0:000> .printf "assertion error: %mu, %mu, %N", @@(expr), @@(filename), @@(lineno)
assertion error: x, k:\vsprojects\fred\fred\fred.cpp, 00000007

Now how do we go about “bypassing” this function? There are basically 2 ways to do it:

  1. Jumping to the end of the function, i.e. the ret instruction.
  2. Popping the stack manually and jumping back to the return address.

You can choose whichever method you want depending on what information you have. #1 requires you to know the exact offset of the ret instruction; #2 requires you to know the calling convention. Neither is much more robust than the other, but #2 requires a little more assembly knowledge. So we’ll do the easier approach with #1 here:

Now, let’s get a disassembly by u:

1
2
3
4
5
6
7
8
9
10
0:000> u
MSVCR100D!_wassert+0x2d [f:\dd\vctools\crt_bld\self_x86\crt\src\assert.c @ 121]:
60494afd 83c404          add     esp,4
60494b00 85c0            test    eax,eax
60494b02 0f85fc000000    jne     MSVCR100D!_wassert+0x134 (60494c04)
60494b08 833df01d4e6001  cmp     dword ptr [MSVCR100D!__app_type (604e1df0)],1
60494b0f 0f85ef000000    jne     MSVCR100D!_wassert+0x134 (60494c04)
60494b15 6af4            push    0FFFFFFF4h
60494b17 ff152c103860    call    dword ptr [MSVCR100D!_imp__GetStdHandle (6038102c)]
60494b1d 8985b8fbffff    mov     dword ptr [ebp-448h],eax

Arg… We need to look further down. Let’s use a real text editor for the job.

1
2
0:000> .shell -ci "u eip eip+0x1000" gvim -
0:000> * If you don't have vi installed, you're doomed. Too bad.

In my case, I found the return address here:

1
2
3
4
5
MSVCR100D!_wassert [f:\dd\vctools\crt_bld\self_x86\crt\src\assert.c @ 113]:
60494ad0 8bff            mov     edi,edi
60494ad2 55              push    ebp
.....
60495656 c3              ret

So 0x60495656 - 0x60494ad0 = 0xb86. We’ve got our offset. All we need to do is to rig eip to point the that instruction:

1
2
0:000> r eip = eip+0xb86
0:000> g

The thing should have run and we’ve been greeting with a nice “hello world.”

To sum it up, we could have converted the assertion error dialog box to console output with a one liner:

1
bp MSVCR100D!_wassert ".printf \"assertion error: %mu, %mu, %N\", @@(expr), @@(filename), @@(lineno); .echo; r eip = eip+0xb86; g"

The above would print “assertion error” message in the debug output and continue as if nothing happened.

Published by kizzx2, on November 17th, 2010 at 11:27 pm. Filled under: Uncategorized Tags: No Comments