Results 1 to 6 of 6

Thread: Girls just want to have fun RE challenge

  1. #1
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Blog Entries

    Girls just want to have fun RE challenge

    A new malware reversing challenge from Halvar Flake.

    Submissions will only be accepted from female reverse engineers, but that doesn't mean guys can't enjoy it as well.

    Now this is an ongoing challenge, so no spoilers. All I will say is that I found the ms_ehseh.idc IDA script from the following link very useful to get started. That should give you some idea of what you're getting into.

    Microsoft VC++ Reversing Helpers

    Have fun girls and guys

  2. #2
    Red wine, not vodka! ZaiRoN's Avatar
    Join Date
    Oct 2001
    Blog Entries
    And the winner is... Marion Marschalek

    Take a look at her analysis:
    A mind is like a parachute. It doesnt work if it's not open.

  3. #3

    I hope they are aware of the "casting couch" that Halvar is going to arrange, before giving them a job in Zynamnics/Google... ahem!!

    Have Phun
    Blame Microsoft, get l337 !!

  4. #4
    Red wine, not vodka! ZaiRoN's Avatar
    Join Date
    Oct 2001
    Blog Entries
    Here is a little addition to the analysis made by Marion, her report is nice but I would like to add something about the way I used to automatically naming/resolving the imported functions.
    The idea is to change instructions like:
    .text:0040100B call dword ptr [ecx+220h]
    into something like:
    .text:0040100B call dword ptr [ecx+_API.malloc]

    I shared this reversing session with Kayaker, so credit for this post goes to him too.

    To perform this switch I have written an IDC script. There are two functions inside it, GetAPINames and ResolveAPINames. The first function is used to retrieve the name of all the hidden API while the other one will change the call instructions into a new readable version.
    The script stores all the information inside a structure named _API which is filled with all the API names. The structure is necessary and Iíll use it for some minor manual fix too.

    Thereís no trace of clear API names inside the disasm, everything is constructed at runtime inside call 402DB0, take a look at this piece of code (without unnecessary junk code lines):

    00403E42 C6 44 24 24 43 mov [esp+179CCh+var_179A8], 'C'
    00403E49 C6 44 24 25 72 mov [esp+179CCh+var_179A7], 'r'
    00403EC1 C6 44 24 26 65 mov [esp+179CCh+var_179A6], 'e'
    00403F14 C6 44 24 2F 61 mov [esp+179D4h+var_179A5], 'a'
    00403F19 C6 44 24 30 74 mov [esp+179D4h+var_179A4], 't'
    00403F1E C6 44 24 31 65 mov [esp+179D4h+var_179A3], 'e'
    00403F23 C6 44 24 32 54 mov [esp+179D4h+var_179A2], 'T'
    00403F28 C6 44 24 33 68 mov [esp+179D4h+var_179A1], 'h'
    00403F2D C6 44 24 34 72 mov [esp+179D4h+var_179A0], 'r'
    00403F32 C6 44 24 35 65 mov [esp+179D4h+var_1799F], 'e'
    00403F37 C6 44 24 36 61 mov [esp+179D4h+var_1799E], 'a'
    00403F3C C6 44 24 37 64 mov [esp+179D4h+var_1799D], 'd'
    00403F41 C6 44 24 38 00 mov [esp+179D4h+var_1799C], 0

    As you can see CreateThread string is obtained appending every single char. To create names the malware uses another similar way:

    00406EC9 C6 84 24 BC 0F 00 00 47 mov [esp+179CCh+var_16A10], 'G'
    00406ED1 C6 84 24 BD 0F 00 00 65 mov [esp+179CCh+var_16A0F], 'e'
    00406F4C C6 84 24 BE 0F 00 00 74 mov [esp+179CCh+var_16A0E], 't'
    00406FA5 C6 84 24 C7 0F 00 00 4D mov [esp+179D4h+var_16A0D], 'M'
    00406FAD C6 84 24 C8 0F 00 00 6F mov [esp+179D4h+var_16A0C], 'o'
    00406FB5 C6 84 24 C9 0F 00 00 64 mov [esp+179D4h+var_16A0B], 'd'
    00406FBD C6 84 24 CA 0F 00 00 75 mov [esp+179D4h+var_16A0A], 'u'
    00406FC5 C6 84 24 CB 0F 00 00 6C mov [esp+179D4h+var_16A09], 'l'
    00406FCD C6 84 24 CC 0F 00 00 65 mov [esp+179D4h+var_16A08], 'e'
    00406FD5 C6 84 24 CD 0F 00 00 46 mov [esp+179D4h+var_16A07], 'F'
    00406FDD C6 84 24 CE 0F 00 00 69 mov [esp+179D4h+var_16A06], 'i'
    00406FE5 C6 84 24 CF 0F 00 00 6C mov [esp+179D4h+var_16A05], 'l'
    00406FED C6 84 24 D0 0F 00 00 65 mov [esp+179D4h+var_16A04], 'e'
    00406FF5 C6 84 24 D1 0F 00 00 4E mov [esp+179D4h+var_16A03], 'N'
    00406FFD C6 84 24 D2 0F 00 00 61 mov [esp+179D4h+var_16A02], 'a'
    00407005 C6 84 24 D3 0F 00 00 6D mov [esp+179D4h+var_16A01], 'm'
    0040700D C6 84 24 D4 0F 00 00 65 mov [esp+179D4h+var_16A00], 'e'
    00407015 C6 84 24 D5 0F 00 00 45 mov [esp+179D4h+var_169FF], 'E'
    0040701D C6 84 24 D6 0F 00 00 78 mov [esp+179D4h+var_169FE], 'x'
    00407025 C6 84 24 D7 0F 00 00 41 mov [esp+179D4h+var_169FD], 'A'
    0040702D C6 84 24 D8 0F 00 00 00 mov [esp+179D4h+var_169FC], 0

    The way used to create GetModuleFileNameExA is pretty similar to the previous one but thereís a little difference, look at the opcodes. The mov instructions are similar but the ModR/M byte defines a distinct displacement.
    Is it possible to recognize and isolate all the instructions used to create all those strings? Well, itís not so hard because some bytes are fixed! The idea is to parse all the instructions inside 402DB0 trying to recognize those two special mov instructions:

    if (Byte(currAddress) == 0xC6) {
       if (Byte(currAddress+1) == 0x44) {
         if (Byte(currAddress+2) == 0x24) {
           if (Byte(currAddress+4) != 0x00) {
             // Get current char and append it to partial name
             szChar = sprintf("%c", Byte(currAddress+4));
             szAPI = sprintf("%s", szAPI + szChar);
           } else {
             if ((strstr(szAPI, ".DLL") == -1) && (strstr(szAPI, ".dll") == -1))
               // Add member to struct (no DLL name)
               AddStrucMember(id, szAPI, -1, FF_DATA, -1, 4);
             szAPI = sprintf("%s", ""); // reset for next string
    The instructions are checked byte by byte and the strings are created char by char. szChar is the current char to append to the partial string szAPI.
    Thereís a little problem with this parser, it constructs DLL names too. Iím not interested in DLL names, so a check over the formatted string is necessary:

    if ((strstr(szAPI, ".DLL") == -1) && (strstr(szAPI, ".dll") == -1))
    Now that Iím sure I donít have a DLL name I can insert it into the structure:

    AddStrucMember(id, szAPI, -1, FF_DATA, -1, 4);

    The use of the structure is fondamental for the script.
    Now that you know how to parse the first type of mov instruction you can easily change some checks over the fixed bytes and youíll retrieve names like GetModuleFileNameExA too:

    if (Byte(currAddress) == 0xC6) {
      if (Byte(currAddress+1) == 0x84) {
           if (Byte(currAddress+2) == 0x24) {
               if (Byte(currAddress+5) == 0x00) {
                   if (Byte(currAddress+6) == 0x00)     {
                       if (Byte(currAddress+7) != 0x00) {
    This part of the script works pretty fine but it has a little problem with few functions. To understand it here is an example with GetQueuedCompletionStatus:

    00405C2A mov [esp+179CCh+var_179A8], 'G'
    00405C31 mov [esp+179CCh+var_179A7], 'e'
    00405CA9 mov [esp+179CCh+var_179A6], 't'
    00405CF3 mov bl, 'Q'
    00405CFE mov [esp+179D4h+var_179A5], bl
    00405D02 mov [esp+179D4h+var_179A4], 'u'
    00405D07 mov [esp+179D4h+var_179A3], 'e'
    00405D0C mov [esp+179D4h+var_179A2], 'u'
    00405D11 mov [esp+179D4h+var_179A1], 'e'
    00405D16 mov [esp+179D4h+var_179A0], 'd'
    00405D1B mov [esp+179D4h+var_1799F], 'C'
    00405D20 mov [esp+179D4h+var_1799E], 'o'
    00405D25 mov [esp+179D4h+var_1799D], 'm'

    As you can see the letter ĎQí is obtained by a sequence of two instructions and my script is not able to catch it; it creates GetueuedCompletionStatus name. It has been proved that human brain is able to recognize word without few letters or with scrambled letters so I think I can pass over this minor problem!

    Ok, now that I have the API structure I need to use it for the resolution part. The function ResolveAPINames scans the entire disasmed code trying to fix the necessary calls. To identify the call you can use a simple strstr function, and to convert it you can use OpStroff (it converts operand to an offset in a structure):

    if(strstr(GetDisasm(ea), "call dword ptr") != -1) {
        OpStroff(ea, 0, GetStrucIdByName("_API"));
    The malware uses a nice addressing method and IDA is not able to parse the hidden API but the nature of the addressing method lets us to solve the problem with some lines of code only. Now you can understand why the structure is the core of the entire script.

    Manual fix
    The script is able to resolve 678 calls, but it fails to fix some special cases like this:

    00414E36 lea esi, [eax+1FCh]
    00414E3C call dword ptr [eax+_API.GetTickCount]
    00414E42 push eax
    00414E43 call dword ptr [esi]

    GetTickCount has been resolved but the next call not. Itís obvious that esi points to the API at offset 0x1FC. You can solve it manually because the structure contains it. Right click over 0x1FC and select the line ď[eax+_API.srand]ď. Now you know how to manually fix special cases too.
    Attached Files Attached Files
    A mind is like a parachute. It doesnt work if it's not open.

  5. #5
    Teach, Not Flame Kayaker's Avatar
    Join Date
    Oct 2000
    Blog Entries
    There was one trick I used with this malware that might be interesting to mention, though I certainly didn't get as far as any of the challenge submitters. It has to do with using the WinDbg RemoteCall feature in Skywing's SDbgExt debugger extension collection, to call API or arbitrary functions. This could probably be compared to the IDA AppCall feature.

    As can be read in the winning submission, the malware uses Events and other methods for thread synchronization. There were 3 named system Events which could be seen in Process Explorer or WinObj. One Event for example triggered creation of a temporary .dat file which was quickly deleted after use. While you could make a copy of the temporary file before it was deleted during tracing, I wanted to see if I could trigger that Event in other ways.

    The event in question:

    Being a named global system event I figured I could call it with an external application, so I made up a simple app with just an OpenEvent(szName) and a SetEvent call. This worked and I could make the malware create the temp file as often as I wished.

    Then I decided to try the SDbgExt RemoteCall feature, which Blabberer reminded me about, to do the same thing.

    >!load sdbgext
    >!remotecall   <address> <call-conv> [arguments]     Calls a function in the target process (from a new thread)
                                                        Valid calling conventions include stdcall(0), cdecl(1), fastcall(2)
                                                        Ansi and Unicode strings ("str", L"str") are automatically marshalled
    What we want to do is formulate the API
        BOOL WINAPI SetEvent(
          _In_  HANDLE hEvent
    First we need to find the handle of the Event:
    >!handle 0 f Event
    Handle 5f8
      Name             \BaseNamedObjects\{83D33F3A-9482-446f-ABFF-7B69D58C1634}
    Then simply call it:
    >!remotecall kernel32!SetEvent 0 5f8
    kernel32!SetEvent() will be run when execution is resumed
    0:001> g
    kernel32!SetEvent() returned 00000001
    and voila, the temporary .dat file is created!
    That was cool. Then I decided to try !remotecall on a procedure that wasn't executed during normal tracing, but I still wanted to step through it to see what it did.

    What we need to do is supply the correct arguments to the procedure. This particular one was a _thiscall procedure which passed a pointer to a newly created buffer of size 0x224 in ECX. Fortunately this was the only argument and was simply an empty buffer pointer.

    :00421F62                 push    224h            ; unsigned int
    :00421F67                 call    ??2@YAPAXI@Z    ; operator new(uint)
    :00421F6C                 pop     ecx
    :00421F7F                 mov     ecx, eax
    :00421F81                 call    sub_42742D
    The problem was how to recreate this 0x224 size buffer before doing a direct !remotecall on sub_42742D?

    At first I tried using the SdbgExt !heapalloc / !heapfree commands to allocate memory within the address space of the target. The exact results are lost in my notes, but it didn't really work.

    Then I decided to pass a pointer to the default system heap and let the procedure work with that instead. All I wanted was a throwaway buffer the proc could use, I wasn't too worried about it modifying the system heap for tracing purposes.

    The first step was to wait until the IAT had been created and all API's parsed at 40113D and break there. See Zairon's post for more about that.

    Then, set a break at the proc we want to examine and use !remotecall. Step into the proc and where it begins to make use of the buffer pointer passed in ECX, manually edit ECX to point to the system heap instead.

    > bp 42742d
    > !remotecall 42742d 0
    > g
    Windbg breaks at 42742d. At the 6th instruction down, ECX is used and is expected to be the 0x224 size buffer. We will use the process heap instead.
    !heap lists the heap allocations, I used the first one at 0x140000
    42743F mov esi, ecx // at this instruction edit ECX to be 140000
    And you can just step through the rest of the proc. It seemed to happily use the process heap as its buffer.

    Bottom line - !remotecall can be useful!!


  6. #6
    Super Moderator
    Join Date
    Dec 2004
    Blog Entries
    talking about ongoing challanges

    there seems to be one running at athcon ( )with few days still left

Similar Threads

  1. # MS VC - challenge for PE packers
    By nezumi-lab in forum Blogs Forum
    Replies: 3
    Last Post: January 14th, 2009, 12:26
  2. t206 challenge
    By ZaiRoN in forum Mini Project Area
    Replies: 2
    Last Post: September 14th, 2006, 21:22
  3. A little challenge (maybe?)
    By hobgoblin in forum Mini Project Area
    Replies: 2
    Last Post: December 24th, 2002, 16:16
  4. A challenge ?
    By Woodmann in forum RCE Cryptographics
    Replies: 10
    Last Post: November 11th, 2001, 13:55


Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts