You are on page 1of 42

DISASSEMBLING VISUAL BASIC APPLICATIONS LAST UPDATED : 26 JULY 2006 INDEX I. DISCLAIMER AND NOTICE II.

READING THIS TUTORIAL III. INTRODUCTION IV. ASSUMPTIONS V. REQUIRED TOOLS VI. STRUCTURE OF A VB PROGRAM VII. OUR FIRST PROGRAM VIII. STRING COMPARISON IX. CONCLUSION X. IN THE NEXT UPDATE XI. REFERENCES DISCLAIMER AND NOTICE The information provided in this tutorial must not be used for Reverse Engineering any application.

THE TEXT HAS BEEN WRITTEN IN SUCH A WAY THAT THE READER CAN LEARN, AND NOT JUST GAIN INFORMATION WITHOUT KNOWING HOW STUFF WORKS. If the Reader still chooses to break Protection Mechamisms after reading this tutorial, he/she shall alone be responsible for the damages cause and not the Author. If you wish to post certain Sections of the Tutorial on a Website, you are free to do so provided you inform the author and publish the Selected Text from the Tutorial as it is without modification. The Author has not copied text or any other information directly from a Source. However, some information from some sources has been used to write this tutorial. These Sources have been mentioned in the References Section. You are permitted to continue reading the tutorial only if you agree to the text given above. II. READING THIS TUTORIAL [RTUT] Each Section in this Tutorial has a specific Topic Code enclosed in square brackets. This arrangement has been made so that you can jump to a specific topic simply by searching for the topic code from your Browser. At many places in the tutorial, I've explained a few things which are almost unnecessary to know when dealing with Visual BASIC programs but I've written them for those who are interested in Hacking (And I don't mean that 'hack-an-email-address' sort of a kid. The original meaning of Hacking has been ruined by pathetic people like them. Hacking in literal terms stands for 'curiousity'. The original meaning of a Hacker is: "A person who enjoys exploring the details of programmable systems, as opposed to most users, who prefer to learn only the minimum necessary." The extra details I've given in this tutorial are for those who want to be such ethical hackers. The Topic Code [XTRA] and [/XTRA] has been given for marking "extra-information" sections and you are free to skip such sections. Text within the [XTRA]..[/XTRA] blocks is given for extra information. You can search for the Extra Information using the Topic Code. III. INTRODUCTION [ITRO] As long as you know Assembly Language, it is easy to read disassembled listings of executable files written in C/C++ or PASCAL, especially if you are using IDA Pro as your disassembler. This is so because C and C++ Compilers generate (or at least try to) efficient code. Some Compilers like Borland C++ use simple instructions for complex operations(also remember that this is not always the case) which make it easier to study them. Implementation of Code Constructs such as loops, IF statements, Ternary IF statements, switch constructs etc. can be found very easily as each one is unique and distinct. However the same is not true for Applications written in Visual BASIC. VB Programs are said to be very slow and hence deliver poor performance. There is a reason for this. Visual BASIC programs unlike those written in other languages don't use Windows API Directly. Local functions present in VB Runtime Files are

called which call functions from the Windows API.Most of the Visual BASIC functions are present in MSVBM60.DLL (if you've got Runtime Files ver. 6.0). So to study VB programs, we must disassemble and analyze the MSVBM60.DLL file as well. Since VB programs use such a complex API Function call procedure, programs tend to run slower.(There are other reasons as to why VB programs run slower but I won't be covering it as it's off-topic.) It becomes difficult to analyze VB Programs as it uses functions which are not part of the Windows API and hence we are not acquainted with them. My primary aim in this Tutorial is to teach the reader how to understand disassembled listings of programs written in Visual BASIC. My secondary aim is to help you realise why Visual BASIC is not suitable for writing small,fast and efficient programs. Almost all authors of Visual BASIC books mention that Visual BASIC does not give you applications with good performance. This tutorial tells you why. The Tutorial will talk about executable files compiled in Visual BASIC in Native Code ONLY and not p-code. After reading this tutorial, you should be able to disassemble,debug and understand Visual Basic Applications. You may also be able to reverse engineer Protection Mechanisms written in Visual BASIC and that's where the next section comes in. IV. ASSUMPTIONS [ASPT] You are required to have a basic understanding of Visual BASIC,C,the Windows API and 80x86 Microprocessor Assembly Language. It would be advisable to have a copy of Intel's 80x86 Instruction Set Manual. Intel provides this manual free of charge. If you need this manual, contact me. This Instruction Set Reference is Volume 2 of Intel Architecture Software Developer's Manual. The Software Developer's Manual consists of 3 volumes: : Basic Architecture - Order Number 243190 : Instruction Set Reference - Order Number 243191 : System Programming Guide - Order Number 243192 You can provide these Order Numbers to get a copy of these manuals. For this tutorial only Volume 2 is required. V. REQUIRED TOOLS [RQRT] You will need the following tools to proceed with the Tutorial. * COMPILER : Visual Basic 6.0 * DISASSEMBLER : IDA Pro 4.x or higher * DEBUGGER : OllyDebug Ver. 1.09d or higher * WINDOWS API DOCUMENTATION * NuMeGa SmartCheck 6.x * VBDE version 0.85 by iorior * VBReformer VBDE is not required but it's always better to have it as it gives addresses of entry-points of most VB procedures. VBReformer is used to see the Property values of all objects in a Visual Basic Form. It even allows you to change the value of Object Properties such as Forms, Command Buttons etc. Import Libraries can also been seen. This Application is not required for this Tutorial but it's better to have it. NuMeGa SmartCheck is again not required but it is useful when we have no idea what a particular procedure of VB does. You can run a program from it like a Debugger and view its log files and find out which procedure is called and what operations are carried out etc. You can have API Documentation from MSDN or you can use the API Text Viewer Tool supplied with Visual Studio or browse MSDN Online (msdn.microsoft.com). Certain Applications like APIViewer will also do. I have given the names of the Tools that I have used. But you are free to use any disassembler and debugger as long as you are comfortable using it but I advice you to use the tools that I have used above. SoftIce is better than OllyDebug but the latter is good enough for VB Programs so it doesn't matter which one you use. Once you have the necessary knowledge and tools, you can proceed further. Let's begin. VI. STRUCTURE OF A VB PROGRAM [SVBP] When you open a VB Program from IDA, you'll end up with the following code. start: push offset dword_4012B4 call ThunRTMain ; ---------------------------------------------------------------------------

dd 0, 300000h, 400000h, 0, 0E9960000h, 82E6FCDFh, 939C4C23h dd 0EB969B2Fh, 73D5h, 0, 1, 34303230h, 72503033h, 63656A6Fh ; etc etc etc This doesn't make any sense does it? If you keep scrolling further you will see sections of code and data. Each Section has a meaning in VB Programs and you can see a general idea of a Visual BASIC program's Section Map below. 00401000: ... IAT (First Thunk ok apis) Next Section(NS): ... some data NS: ... transfer area (Jumps to imported functions) NS: ... lots of data NS: ... local transfer area (for internal event handlers) NS: ... other data NS: ... code NS: ... lots of data NS: ... .data Section Let us now start analysis from the entry point of the program. push offset RT_Struct call ThunRTMain It's C equivalent would have been: ThunRTMain(&RT_Struct); A function ThunRTMain is called which accepts one parameter. We'll soon find out that the parameter is a structure. Simply putting a step over command on the CALL statement results in the execution of the Application. Wierd Isn't it? For Pascal,C and C++ Programs there is always a start() function that takes all CommandLine Parameters,Gets ProcessThreads,Module Handles etc. We didn't see anything of the sort in a Visual BASIC Program. But actually, VB does have a start function. The start function code is placed in the ThunRTMain Function. Let's verify that by disassembling the MSVBM60.DLL and viewing the ThunRTMain Function. I've mentioned only a part of the ThunRTMain Function Code. ThunRTMain proc near arg_0 = dword ptr 8 mov esi, [ebp+arg_0] mov dword_7352F7DC, esi and [ebp+var_4], 0 lea eax, [ebp+StartupInfo] push eax ; lpStartupInfo lea eax, [ebp+StartupInfo] push eax ; lpStartupInfo call ds:GetStartupInfoA movzx eax, [ebp+StartupInfo.wShowWindow] mov dword_7352F7D8, eax push hModule push esi mov esi, offset dword_7352F470 mov ecx, esi call sub_7342DECD mov [ebp+var_1C], eax test eax, eax jl short loc_7342DEC5 push 0 ; lParam push 0 ; wParam push 1069h ; Msg call ds:GetCurrentThreadId push eax ; idThread call ds:PostThreadMessageA ; Other Code or [ebp+var_4], 0FFFFFFFFh push 0 ; uExitCode

call ds:ExitProcess jmp loc_734619B3 loc_7342DEC5: ; CODE XREF: ThunRTMain+60Rj push eax call sub_Free_Memory jmp short loc_7342DEB4 endp As you can see, it does call all the Functions that the start() function does in C and PASCAL programs. But what about CommandLine() Function from KERNEL32.DLL? MSVBM60.DLL does call that function as well but that function call is placed in deeply nested function calls. You can open the Imports Window to see the Imported Function and see the cross-reference to a procedure in MSVBM60.DLL The sub_Free_Memory procedure calls various API Functions but if you keep reading the procedure, you'll soon come across the HeapFree() Function which is imported from kernel32.dll. Now I guess you now know the purpose of the ThunRTMain Function. Let us now see what structure is passed to it. If we double-click on the RT_Struct offset, we reach an address containing certain values. It is a huge structure and each part needs to be seen one at a time. Explaining the Structure will take up a lot of time and since I want to focus on the Code Constructs of Visual BASIC, I won't explain the Structure Passed to ThunRTMain. All I can tell you is that the structure contains the PE (Portable Executable) Header Details. It is this header that is read by Resource Editors. I found a good source for understanding the structure that is passed to ThunRTMain and I suggest you read it if you are interested in knowing PE Header Details. The link to the Article is given in the References [REFR] Section. The article is titled "VISUAL BASIC REVERSED - A decompiling approach" and is written by Andrea Geddon. If the link is dead by the time you are reading this, you can contact me on my email address to get the article. VII. OUR FIRST PROGRAM [OFPR] Create a Form with a CommandButton. Click the CommandButton and add a simple Msgbox Code as shown below: Private Sub Command1_Click() Msgbox "Ssup" End Sub Open the Compiled EXE File with IDA Pro. Click the Strings Tab to find the "Ssup" String. Double-Click the String to find its cross-reference. Scroll up to the top of the procedure. You should see something like this: [Explanation is partly given by comments after an instruction.] Command1_Click proc near var_64 = dword ptr -64h var_5C = dword ptr -5Ch var_54 = dword ptr -54h var_4C = dword ptr -4Ch var_44 = dword ptr -44h var_3C = dword ptr -3Ch var_34 = dword ptr -34h var_2C = dword ptr -2Ch var_24 = dword ptr -24h var_14 = dword ptr -14h var_C = dword ptr -0Ch var_8 = dword ptr -8 ; Destructor Object var_4 = dword ptr -4 form_object = dword ptr 8 push ebp ; These two instructions mov ebp, esp ; open the Stack Frame. sub esp, 0Ch ; Allocates 12 bytes on stack push (offset exception_handler+1); Starts Exception Handler mov eax, large fs:0 push eax mov large fs:0, esp sub esp, 88h ; Allocates 136 bytes on stack push ebx push esi ; Saves Values of Registers push edi

; Loads the Destructor mov [ebp+var_C], esp mov [ebp+var_8], offset destructor ; Allocating Dynamic Resources mov eax, [ebp+form_object] mov ecx, eax and ecx, 1 mov [ebp+var_4], ecx and al, 0FEh push eax mov [ebp+form_object], eax mov edx, [eax] call dword ptr [edx+4] ; Calls MSVBM60.Zombie_AddRef mov ecx, 80020004h xor esi, esi mov [ebp+var_4C], ecx mov eax, 0Ah mov [ebp+var_3C], ecx mov [ebp+var_2C], ecx mov [ebp+var_34], esi mov [ebp+var_44], esi mov [ebp+var_54], esi mov [ebp+var_64], esi lea edx, [ebp+var_64] lea ecx, [ebp+var_24] mov [ebp+var_24], esi mov [ebp+var_54], eax mov [ebp+var_44], eax mov [ebp+var_34], eax mov [ebp+var_5C], offset aSsup ; "Ssup" mov [ebp+var_64], 8 call ds:__vbaVarDup lea eax, [ebp+var_54] lea ecx, [ebp+var_44] push eax lea edx, [ebp+var_34] push ecx push edx lea eax, [ebp+var_24] push esi push eax call ds:rtcMsgBox ; Calls the MsgBox Function lea ecx, [ebp+var_54] lea edx, [ebp+var_44] push ecx lea eax, [ebp+var_34] push edx lea ecx, [ebp+var_24] push eax push ecx push 4 call ds:__vbaFreeVarList add esp, 14h mov [ebp+var_4], esi push offset continue_after_jump jmp short fake_a_call_instr lea edx, [ebp+var_54] lea eax, [ebp+var_44] push edx lea ecx, [ebp+var_34] push eax lea edx, [ebp+var_24] push ecx push edx push 4 call ds:__vbaFreeVarList add esp, 14h retn ; --------------------------------------------------------------------------fake_a_call_instr:

retn ; --------------------------------------------------------------------------continue_after_jump: mov eax, [ebp+arg_0] push eax mov ecx, [eax] call dword ptr [ecx+8] ; Calls MSVBM60.Zombie_Release mov eax, [ebp+var_4] mov ecx, [ebp+var_14] pop edi pop esi mov large fs:0, ecx pop ebx mov esp, ebp pop ebp ; Closes Stack Frame retn 4 Command1_Click endp Simply by looking at the entire procedure you can't exactly figure out what the hell happens when the whole subroutine is executed. If you know Assembly well and have had the patience to read through the code, you should notice a few neat things in the code. [XTRA] Before I begin explaining the procedure, I want to teach you how to recognise a procedure in Visual BASIC. They can be called Procedure Signatures. 1) A Procedure has the open and close Stack Frame instructions. 2) The First Procedure in a VB Program is always preceded by 12 0xCC Bytes (which corresponds to the INT 3 Instruction) followed by 4 'T' bytes (0xE9) followed by 12 0xCC bytes. 3) Procedures other than the first are preceded by 10 NOP(0x90) Instructions. : 1) STACK FRAME: The Open/Close Stack Frame Instructions are even found in C/C++ and Pascal programs and hence can be termed as a universal method of determining procedures. However that is not always the case. --> Many compilers just JMP instructions to fake a Call Instruction. This Jump is at times a CALL to a procedure. IDA Pro does not detect such CALL 'emulating' instructions but OllyDebug does recognise such code patterns. --> Visual C++ allows the programmer to write naked functions. Naked functions mean that the compiler does not allocate space for its arguments nor does it include the stack open and close frame instructions. But since we are dealing with Visual BASIC, we can ignore the second case. You will see an example of the first case shortly. : 2) THE 0xCC BYTE The 0xCC Byte is used to Generate the INT 3 Exception, which is known as the "CALL TO INTERRUPT" Procedure. It is used by Debuggers such as OllyDebug and SoftIce to set software Breakpoints. Debuggers insert the 0xCC byte before the instruction which it wants to set a breakpoint on. As soon as the INT 3 Instruction is executed, Control is passed onto the Debuggers Exception Handler. Here is the description taken directly from Intel's Software Developers Manual Volume 2 : Instruction Set Reference. "The INT 3 instruction generates a special one byte opcode (CC) that is intended for calling the debug exception handler. (This one byte form is valuable because it can be used to replace the first byte of any instruction with a breakpoint, including other one byte instructions, without over-writing other code). To further support its function as a debug breakpoint, the interrupt generated with the CC opcode also differs from the regular software interrupts as follows: Interrupt redirection does not happen when in VME mode; the interrupt is handled by a protected-mode handler. The virtual-8086 mode IOPL checks do not occur. The interrupt is taken without faulting at any IOPL level." That's how debuggers work. That's also the concept of certain anti-debugging techniques. Since the 0xCC code is injected by Debuggers before an instruction, the CRC (Cyclic Redundancy Check) Value of the code also changes. Some Antidebugging techniques encrypt the program with a key which is the CRC value of the program. When a program is being debugged, its CRC value changes and with the result the program doesn't get decrypted. Such methods are effective in stopping amateur wannabe hackers from understanding their code but its not foolproof and an expert hacker can get past this technique with ease. So much for what '0xCC' is. But why is it placed before the First Procedure in

VB Programs? I've found no answer to that so far. This wastes a lot of space in a program. If you try to disassemble a Console Program written in Visual C++, you'll find many instructions which set parts of the stack to the '0xCC' value. You will also find 0xCC bytes scattered across the disassembled listing. If only Visual Studio was Open Source, we could have seen the code generation code and come up with an answer and improve the code generation code too. I hope you also realise why Open Source is slowly gaining momentum. 3) The 0x90 Byte Here is the description taken directly from Intel's Software Developers Manual Volume 2 : Instruction Set Reference. "Performs no operation. This instruction is a one-byte instruction that takes up space in the instruction stream but does not affect the machine context, except the EIP register. The NOP instruction is an alias mnemonic for the XCHG EAX, EAX instruction." This byte is injected into serial generation/checking procedures by amateur hackers where the protection mechanism is weak. This is known as bit-hacking. Sadly enough, bit-hacking STILL works for defeating plenty of today's Commercial Applications. Guess they never realised the importance for code-security. While writing programs in Assembly Language, if you use Forward Referencing in a few situations or use a wrong Jump Instruction to jump to certain addresses, chances are quite bright that the Assembler will fill in some bytes with the NOP instruction. As a result, having the presence of the 0x90 Instruction in your code is considered bad programming. But again, I see no reason why the 0x90 Byte is present in Visual BASIC. Removing such entries will reduce the executable size drastically. Programs like VBDE rely on such Procedure Signatures to identify where a procedure is present. [/XTRA] Let us start by analyzing the procedure in portions. First the Procedure opens the Stack Frame. Then it allocates 12 Bytes on the stack for the Destructor and other variables. ( We shall see the Destructor in detail after a short while. ) Then it allocates Dynamic Resources and calls the Zombie_AddRef Function. What does the Zombie_AddRef Function do? It Takes the Object Reference. In this function the parent object (in this case Form) is passed as a parameter and uses AddRef to increment reference count of the object (instantiation). Since COM objects are responsible for their lifetime, the resources they use are allocated until the reference count is 0, when it reaches 0 the objects enter zombie state & can be deallocated to free resources. Refer COM object management documentation for more detailed information. Right after the call of the Zombie_AddRef Function there are MOV instructions which assigns values to many variables. That follows a reference to the "Ssup" string followed by a call to the rtcMsgbox procedure. Why does it seem so wierd? Shouldn't it simply call the rtcMsgbox Function? Let us find out why in a little more interesting manner. Intuition tells us that no matter what the function does, it will end up calling the MessageBoxA or the MessageBoxW Function. So let's set a breakpoint on the MessageBoxA and MessageBoxW Functions. To do that, start OllyDebug and load the Executable file by pressing F3. After the program is loaded, press Alt+E to open the Executable Modules Window. Double click USER32.DLL to open the disassembled listing of the User32.dll file. From there press Ctrl+N to open the Imports/Exports Window. Then Scroll over till you see the MessageBoxA and MessageBoxW Functions. Click them one at a time and press F2 to set a breakpoint. Now press F9 to run the program. The Application should open. Click the CommandButton. Now instead of the Debugger halting at a breakpoint of MessageBox, the MessageBox comes up without any halt to the Debugger. Why does this happen? Does this mean that rtcMsgBox has a seperate copy of the MessageBox code within itself? Though it seems like a possible reason, it is unlikely to happen as Microsoft Developers built the Windows API so that they could be reused for performance. So that means that some API Function is called which displays the MessageBox. So let us try another experiment. In the same Imports/Exports Section of User32.dll we see 2 more MessageBox functions which are MessageBoxIndirectA and MessageBoxIndirectW. Let's try setting a breakpoint on both these Messages. After the breakpoint is set, press F9, and click the Command Button. This time, the Debugger halts at the MessageBoxIndirectA function. Interesting isn't it? All Visual BASIC Applications which use the Msgbox()

Function are actually calls to MessageBoxIndirectA and not MessageBox as thought. This is an important characteristic. So the Next time you set a breakpoint on the MessageBox function and the debugger halts at a breakpoint, you can be pretty sure that someone has used the MessageBox() API Directly by consulting the API Text Viewer for the VB Declaration. Let us now see the prototype of the MessageBoxIndirect() API Function. Private Declare Function MessageBoxIndirect Lib "user32" Alias "MessageBoxIndirectA" (lpMsgBoxParams As MSGBOXPARAMS) As Long Only One Parameter? So then how is the Message Body and Title passed to the Function? For that we'll need to see the declaration of the MSGBOXPARAMS Structure. Private Type MSGBOXPARAMS cbSize As Long hwndOwner As Long hInstance As Long lpszText As String lpszCaption As String dwStyle As Long lpszIcon As String dwContextHelpId As Long lpfnMsgBoxCallback As Long dwLanguageId As Long End Type This suggests that the required parameters are assigned to variables and the reference to that object is passed to that function. So That suggests that the many MOV instructions found before the rtcMsgbox call are used to initialise the MSGBOXPARAMS Structure. To confirm our doubt, let's compare the MOV instructions with the code found before the MessageBoxIndirect function is called. mov edx, [eax] mov [ebp+hWnd.lpszText], ecx mov ecx, [eax+8] mov eax, [eax+0Ch] push esi push ebx test ah, 40h mov [ebp+hWnd.hInstance], edi mov [ebp+hWnd.lpszIcon], edi mov [ebp+hWnd.lpfnMsgBoxCallback], edi mov [ebp+hWnd.cbSize], 28h mov [ebp+hWnd.hwndOwner], edx mov [ebp+hWnd.lpszCaption], ecx mov [ebp+hWnd.dwStyle], eax mov [ebp+hWnd.dwLanguageId], edi jz short loc_734A6133 mov [ebp+hWnd.lpfnMsgBoxCallback], offset sub_734A6098 loc_734A6133: mov esi, ds:MessageBoxIndirectA lea eax, [ebp+hWnd] push eax ; LPMSGBOXPARAMSA call esi ; MessageBoxIndirectA Interesting to see that....Isn't it? Next comes the __vbaFreeVarList Function. From its name we can see that it deallocates the address of a certain number of variables. This function actually does no work except call the __vbaFreeVar Function multiple number of times. Let us see how both functions work. __vbaFreeVar : Frees a Temporary Variable. __vbaFreeVar accepts only 1 Argument, which is the address of the variable to be deleted. This argument is ALWAYS passed through ECX. Uses the API Function __imp_SysFreeString()[Ordinal Number 6] from OLEAUT32.DLL that carries out the actual deallocation of a variable. __vbaFreeVarList : Frees Temporary Variables. Have a look at this Snippet: lea ecx, [ebp+var_54] ; Variable 1 stored in ecx lea edx, [ebp+var_44] ; Variable 2 => edx push ecx ; Variable 1 pushed lea eax, [ebp+var_34] ; Variable 3 => eax push edx ; Variable 2 pushed lea ecx, [ebp+var_24] ; Variable 4 => ecx push eax ; Variable 3 pushed

push ecx ; Variable 4 pushed push 4 ; Free 4 Temp. Variables call ds:__vbaFreeVarList The code is pretty easy to understand. This function frees temporary variables that are passed as arguments to it.Interestingly each memory location is 16 bytes wide. This is an interesting function as it can accept variable arguments. It's equivalent function call in C would be: __vbaFreeVarList(4,&var_24,&var_34,&var_44,&var_54); where its declaration would be: int __vbaFreeVarList(int NUMBER_OF_VARIABLES_TO_FREE,<addresses of the vars>...) { // CODE } If we analyse the code of __vbaFreeVarList, we actually find multiple calls of __vbaFreeVar. Have a look at the source code of __vbaFreeVarList. public __vbaFreeVarList __vbaFreeVarList proc near arg_0 = dword ptr 4 arg_4 = dword ptr 8 arg_8 = dword ptr 0Ch mov ecx, [esp+arg_4] ; Address of Variable to delete push esi ; Saves value of esi lea esi, [esp+4+arg_8] ; esi = Address of Next Variable call __vbaFreeVar ; function call mov eax, [esp+arg_4] ; eax gets value of num of ; variables to be freed cmp eax, 1 ; If eax<=1 then jbe short freed_all_vars ; jump to end of function. push edi ; Value of edi is saved on stack lea edi, [eax-1] ; edi=eax-1 ; THIS IS A WHILE LOOP : while(edi){ /*CODE*/ } loop_start: mov ecx, [esi] ; ecx=Address of Variable to Delete add esi, 4 ; esi = Address of Next Variable call __vbaFreeVar ; function call dec edi ; edi--; jnz short loop_start ; Jump to beginning of the loop if ; edi is not ZERO. ; Well Written Code.No need for a ; CMP Instruction as DEC ; Instruction affects the ZERO Flag pop edi ; Value of edi is restored. freed_all_vars: pop esi ; Value of esi is restored. retn ; Return to the calling function ENGINE:7352009D __vbaFreeVarList endp As you can see, __vbaFreeVarList uses a while loop to free each variable one by one using the __vbaFreeVar Function. Notice that the address of the variable to be freed is stored in ECX always. You can disassemble the __vbaFreeVar Function to confirm that. Now let us see what happens when after the MessageBox is shown. This is the most interesting part. After the MessageBox is displayed, a clean up code is executed that deallocates all the variables used in the entire procedure. Have a look at these statements in the Command1_Click() Code. push offset continue_after_jump jmp short fake_a_call_instr ; --------------------------------------------------------------------------fake_a_call_instr: retn ; --------------------------------------------------------------------------continue_after_jump: ; code This is the 'CALL-Simulation' instruction. If you recall, before a call function is executed, the processor pushes the location of the instructions which are supposed to receive control after execution of a function is over. VB instead of issuing a call instruction simulates it using the push, jmp and retn instructions. It is small sections of code like this that reduce Visual BASIC's efficiency and performance.

Let us still see why this is done. The CALL-Simulation Instruction calls the MSVBM60.Zombie_Release function. This is the destructor code. Doesn't that remind you of something? The instruction mov [ebp+var_8], offset destructor contains the offset of the destructor code. But is this so? Double-click on the 'offset destructor' text and you'll land up here. destructor dd 80005h, offset loc_401A7E, 0, offset loc_401A85, 102825FFh Hmm...this contains more offsets? By simply double-clicking the offsets you land up at the destructor code again. That's why the CALL simulation code is used so that the destructor code looks like its an inline function. If you're more curious, you can also double-click the 'exception_handler' text to see where that leads to. Well, after a long journey into the Command1_Click() Procedure, we're finally done analyzing it. From this point onwards, I shall explain only the important section of code rather than explain such intricate details once again. Let us proceed further. This Time let us create a Visual BASIC Application using only a module. We shall use the Main Subroutine. Use this code: Sub Main() MsgBox "Ssup" End Sub What you will realise that the Procedure code is an exact copy of the code we dealt with earlier. This means that Form Procedures and Module Procedures are treated alike. This also means that the Command Button code procedure had no chance of using any information of the Form Object. Let's take another example. VIII. STRING COMPARISON [STR1] Create a form without any controls. The code in the form module is as follows: Private Sub Form_Load() If "Sanchit" <> InputBox("ssup") Then MsgBox "wrong" Else MsgBox "Right" End If End Sub The resultant code is shown below (after stripping unimportant instructions) Form_Load proc near ; Variables and Arguments shown push ebp mov ebp, esp sub esp, 0Ch push (offset vba_exception_handler+1) mov eax, large fs:0 push eax mov large fs:0, esp sub esp, 0F0h push ebx push esi push edi mov [ebp+var_C], esp mov [ebp+var_8], offset destructor mov eax, [ebp+arg_0] mov ecx, eax and ecx, 1 mov [ebp+var_4], ecx and al, 0FEh push eax mov [ebp+arg_0], eax mov edx, [eax] call dword ptr [edx+4] ; Zombie_AddRef() xor eax, eax mov ebx, 80020004h mov edi, 0Ah ; code... lea edx, [ebp+var_98] lea ecx, [ebp+var_28] ; MOV [var],register Instructions

mov [ebp+var_90], offset aSsup ; "ssup" mov [ebp+var_98], 8 call ds:__vbaVarDup lea eax, [ebp+var_88] push offset aSanchit ; "Sanchit" lea ecx, [ebp+var_78] push eax lea edx, [ebp+var_68] push ecx lea eax, [ebp+var_58] push edx lea ecx, [ebp+var_48] push eax lea edx, [ebp+var_38] push ecx lea eax, [ebp+var_28] push edx push eax call ds:rtcInputBox mov edx, eax lea ecx, [ebp+var_18] call ds:__vbaStrMove push eax call ds:__vbaStrCmp mov esi, eax lea ecx, [ebp+var_18] neg esi sbb esi, esi neg esi neg esi call ds:__vbaFreeStr lea ecx, [ebp+var_88] lea edx, [ebp+var_78] push ecx lea eax, [ebp+var_68] push edx lea ecx, [ebp+var_58] push eax lea edx, [ebp+var_48] push ecx push edx lea eax, [ebp+var_38] lea ecx, [ebp+var_28] push eax push ecx push 7 call ds:__vbaFreeVarList add esp, 20h mov [ebp+var_50], ebx test si, si mov [ebp+var_58], edi mov [ebp+var_40], ebx mov [ebp+var_48], edi mov [ebp+var_30], ebx mov [ebp+var_38], edi jz short jump_if_right lea edx, [ebp+var_98] lea ecx, [ebp+var_28] mov [ebp+var_90], offset aWrong ; "wrong" mov [ebp+var_98], 8 call ds:__vbaVarDup lea edx, [ebp+var_58] lea eax, [ebp+var_48] push edx lea ecx, [ebp+var_38] push eax push ecx lea edx, [ebp+var_28] push 0 push edx

call ds:rtcMsgBox lea eax, [ebp+var_58] lea ecx, [ebp+var_48] push eax lea edx, [ebp+var_38] push ecx lea eax, [ebp+var_28] push edx push eax jmp short free_up_resources ; --------------------------------------------------------------------------jump_if_right: lea edx, [ebp+var_98] lea ecx, [ebp+var_28] mov [ebp+var_90], offset aRight ; "Right" mov [ebp+var_98], 8 call ds:__vbaVarDup lea ecx, [ebp+var_58] lea edx, [ebp+var_48] push ecx lea eax, [ebp+var_38] push edx push eax lea ecx, [ebp+var_28] push 0 push ecx call ds:rtcMsgBox lea edx, [ebp+var_58] lea eax, [ebp+var_48] push edx lea ecx, [ebp+var_38] push eax lea edx, [ebp+var_28] push ecx push edx free_up_resources: push 4 call ds:__vbaFreeVarList add esp, 14h mov [ebp+var_4], 0 push offset call_emulate jmp short jump_as_a_call ; --------------------------------------------------------------------------lea ecx, [ebp+var_18] call ds:__vbaFreeStr lea eax, [ebp+var_88] lea ecx, [ebp+var_78] push eax lea edx, [ebp+var_68] push ecx lea eax, [ebp+var_58] push edx lea ecx, [ebp+var_48] push eax lea edx, [ebp+var_38] push ecx lea eax, [ebp+var_28] push edx push eax push 7 call ds:__vbaFreeVarList add esp, 20h retn ; --------------------------------------------------------------------------jump_as_a_call: retn ; --------------------------------------------------------------------------call_emulate: mov eax, [ebp+arg_0] push eax

mov ecx, [eax] call dword ptr [ecx+8] ; Zombie_Release() mov eax, [ebp+var_4] mov ecx, [ebp+var_14] pop edi pop esi mov large fs:0, ecx pop ebx mov esp, ebp pop ebp retn 4 Form_Load endp Let us first see the Prototype of the InputBox Function Function InputBox(Prompt As String, Title As String, Default as String , _ xpos As Long , ypos As Long, helpfile As String ,context As Long ) _ As String If we disassemble the MSVBM60.DLL File and scroll over to the rtcInputBox function, we can confirm this from the prototype that IDA provides us. Most of the code is irrelevant to us but the important chunk of code of the function (which took me quite some time to find) is given below: push edi ; Context Pushed push ebx ; HelpFile Pushed push [ebp+arg_8] ; ypos Pushed push [ebp+arg_C] ; xpos Pushed push eax ; Default pushed push ecx ; Title Pushed push edx ; Prompt pushed call sub_7349DC68 ; Function that causes Inputbox ; At this point EAX contains the entered string push [ebp+arg_4] ; BSTR mov edi, eax call esi ; __imp_SysFreeString push [ebp+arg_0] ; BSTR call esi ; __imp_SysFreeString push [ebp+var_4] ; BSTR call esi ; __imp_SysFreeString push [ebp+var_1C] ; BSTR call esi ; __imp_SysFreeString mov eax, edi ; Sets EAX to the Return Value pop edi pop esi pop ebx leave ; Close Stack Frame retn 1Ch ; Returns from here In the first portion of the code, the push instructions push all the seven parameters to the function that creates the InputBox Dialogbox. I know that with the current example that I'm disassembling, it's not quite possible to believe that all the push instructions stand for what I've mentioned. So what you can do is disassemble the following code given below and set a breakpoint on the PUSH instructions in the MSVBM60.DLL File using OllyDebug or SoftIce. str1 = InputBox("This is prompt","This is Title","Default-Val",10,20, _ "c:\help.hlp",1) With this you can actually verify the contents of the push instruction to confirm what I've written. Now, after the call instruction you can see a PUSH instruction pushing the contents of a variable. This is a parameter for the SysFreeString Function. Next is a MOV instruction transferring the contents of the EAX register into EDI. EAX at this point of time contains the address of the String that we filled in the Text Box. This is done to save the value of EAX. Then the SysFreeString Function is called. This function takes 1 argument which is the string that needs to be deallocated. This function does not return any value after execution. Did you notice something stupid? If the SysFreeString Function does not return any value, then why were the contents of EAX saved in EDI? And why is there a need to have a MOV EAX , EDI instruction? What a waste of precious bytes. This is deploring code generated by Visual C++ 6.0 Compiler (which was used to write the MSVBM60.DLL File) and since Visual BASIC programs use this routine, it results in slow, sluggish programs.

And this is just one function...imagine what would happen if we analyzed all of them? Anyway, the original contents of the registers are restored, the stack frame is closed (with LEAVE) and the function returns after adding 0x1C bytes to ESP. Now let's have a look at this code portion. call ds:rtcInputBox mov edx, eax lea ecx, [ebp+var_18] call ds:__vbaStrMove push eax call ds:__vbaStrCmp mov esi, eax lea ecx, [ebp+var_18] neg esi sbb esi, esi neg esi neg esi call ds:__vbaFreeStr After the call of rtcInputBox, EAX contains the entered string in the Text Box of the InputBox dialog. The address of this string is moved to EDX. Is this done to save the contents of the String for the compare function? As obvious as it may seem, it is really not like that. Let us see. The __vbaStrMove function moves a String from one place in memory to another place. On Analysis of the function code it is found that this function accepts two arguments and returns one value as shown below: PARAMETERS: EDX : Source String ECX : Destination RETURNS : Source String. Sets EAX to EDI (which holds value of EDX) Now we see that by setting the value of EAX to EDX, we are setting the entered string as a SOURCE string to be copied into another location. This is neat. Now the Source String is pushed again and the __vbaStrCmp Function is called. This on first glance looks wierd again. It seems that the StrCmp Function accepts only one argument. Then what does it compare it with? If you scroll above you will find a push instruction that pushes the address of the string "Sanchit" ( push offset aSanchit ; "Sanchit" ) Such cases remind us that unlike code generated by Pascal and C/C++ compilers, Visual BASIC functions can have it's arguments pushed anywhere and not right before the function call. Keep this thing in mind when you set out to disassemble your own Visual BASIC programs. Now after the Comparing function returns its value via the EAX register, it is copied to ESI. Then the LEA instruction is used to load the address of a variable in the ECX register. Now since we are aware of VB's tricks we know that this is a parameter to the __vbaFreeStr function. You should notice that the same variable which held the value of the entered string is now being passed to this function to deallocate it as its not required after the comparison has been done. Now let's talk about this code fragment: neg esi sbb esi, esi neg esi neg esi call ds:__vbaFreeStr The NEG statement's actual use is to change the sign of a number for example, from 3 to -3. But this one has an indirect use. This Instruction affects many flags. But the one it is meant for is the Carry Flag (CF) which is used in the next SBB Instruction.If ESI is equal to 0 then CF is reset to 0 and otherwise is set to 1. Now the SBB Instruction stands for Subtraction with Borrow. In this case it can be translated to this: ESI = ESI - (ESI + CF); This is where the previous NEG instruction comes into picture. The next two NEG instructions are of no use. You can take this as another example to show why VB code is slow. Following this is the __vbaFreeStr Function which deallocates space for a string variable. Now you might wonder that if comparison has been performed, then why isn't there any JUMP instruction? If you keep reading the code you will find the TEST and JUMP Instructions after

the seven variables used have been cleared. These instructions are found together in C/C++ and Pascal programs but in Visual BASIC this isn't always the case. That's why Visual BASIC programs take up more time for analysis compared to C and Pascal programs. I'm not discussing the rest of the code as it has been covered in the previous section. Now let's discuss a Hack-tip. This type of String Comparision protection is used in many popular Shareware Applications. Removing such protection isn't much of a job and I'll discuss a cleaner way to hack this protection. Remember the two useless NEG instructions before the __vbaFreeStr Function call? Since these instructions are of no practical use, we can replace these two bytes with XOR ESI, ESI which consumes only 2 bytes. That way the TEST instruction would always result in ZERO making control jump always to the Msgbox("Right") code. A lot of Hackers use such 'useless' code to implement such hacking techniques. IX. CONCLUSION [END1] This isn't exactly the end of the tutorial. I'd rather say that it is the end for now. This topic is vast and I'm trying to include explanation and analysis of all Visual BASIC functions. If I plan to release this tutorial with all functions inclusive, it's going to take a lot of time. So i've decided to post this tutorial first and keep updating it every 15 to 20 days. You can check the LAST UPDATED Section in the beginning of the Tutorial to see how recently this tutorial has been updated. Keep checking for updated versions every month. I hope you've enjoyed my tutorial as I've put in a lot of hard work and time on writing this. Since I haven't come across any Books or Articles on this subject, I don't quite know what exactly is expected from my tutorial. I would appreciate it if you could email me suggestions and comments on this tutorial. I may not be able to reply to every email, but I do read each one of them. Thanks. X. IN THE NEXT UPDATE [NXTU] In the next update of this tutorial, I plan to give an explanation of which function is called when a certain Visual BASIC function is called. I then analyze the function, it's parameters and return value, methods to improve the function and much more. XI. REFERENCES [REFR] -> MSDN (msdn.microsoft.com) -> "VISUAL BASIC REVERSED - A decompiling approach" by Andrea Geddon.

VISUAL BASIC REVERSED - A decompiling approach


Author: AndreaGeddon

Abstract
Keywords: Reverse Code Engineering, Visual Basic, VB, Decompiling Frameworks are getting more and more popular today, Visual Basic is one of them. Personally i hate frameworks, and also most reversers do. So, why this tutorial? We can consider both the light and the dark side of the problem: frameworks usually put a lot of code in the compiled programs, so it becomes hard to nd the way among all that jungle. But they also use sets of pre-built objects, so theese objects are always the same and can be recognized, helping the reverser to understand the code itself. In a VB PE you have a lot of information inside the exe, so you can easily extract all the information you need about all components of the program. To analyze a VB application I used this program that was written by a friend of mine (thank you _d31m0s_!). Its a sort of name/serial crackme, but we are not interested in serial shing, we are interested in how it works and how the vb knows how to build the app itself. I asked my friend to write it adding some event handling (colors, on over, etc) and a simple algorithm to check serial. He also wrote the proggy using more source les and making various subs (some null sub too). We also have the source of all, but we will check them later. Lets make some introduction now!

INTRODUCTION

Before VB5 the VB programs were not truly traduced in assembler, they were coded in Pseudo Code (hehe you all remember those hating pcode exe!), and the VB virtual machine had the task of interpreting the pseudo code and execute it. Those programs were linked to vbrun100, vbrun200, vbrun300, vbrun400 dlls (depending on the version); well thing are a little dierent because there were variations between 16bit or 32bit modules (pcode were mostrly 16bit apps), but this is not what we are looking for. Today we have version 5 and 6 of VB, they use MSVBVM50.dll and MSVBVM60.dll, and now VB exes are really compiled and traduced in asm. As you all know you cant use usual breakpoints like GetWindowTextA when debugging VB programs, infact you should use the apis exported from the VBVM dll (e.g. for a serial i would use __vbaStrCmp, or rtcMsgBox), if you want to use theese apis in softice you just have to load the VB dll (in winice.dat or via symbol loader). This will help you debug VB applications.

ANALISYS

Lets start disassembling the proggy. I am using Ida, i advice you to use it too, but you can use other disassemblers if you want. First of all lets have a general look, so we can have a general idea. You can easily see the following: 00401000 ... 004010F0 ... 004011A5 ... 0040130E ... 004023EC ... 0040242C ... 00402E44 ... 00403D06 ... other data code other data local transfer area (for internal event handlers) lots of data transfer area (declspec(dllimport) style) some data IAT (First Thunk ok apis)

Ok we have a general idea of the mapping of the program. Notice that all read only data is in the .text section, the data before and after the code contains names of imported functions (IT original rst thunks), however now we begin analisys. We start from entry point. What we see is: 00401310 00401315 push call offset RT_MainStruct ThunRTMain

if you debug this, you execute the call and the program runs. The analisys here is very simple: the entry point simply consists of: ThunRTMain(&RT_MainStruct);

this function is the main function of the VBVM (the Thunder Runtime Engine) and has one parameter, the pointer to a complex structure that describes all the application. It is clear that all the data, all the callbacks and so on are described by this structure, so we can extract a lot of information. We just have to check this big structure! Lets go! 00401970 00401974 00401975 00401980 00401984 00401988 0040198C 00401990 00401994 00401998 0040199C 004019A0 004019A4 004019A8 004019AC 004019B0 004019B4 004019B8 004019BC 004019C0 004019C4 004019C8 004019CC 004019D0 004019D4 004019D8 004019DC 004019E0 004019E4 RT_MainStruct db VB5! db db dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd ;signature

aVb6it_dll

1Ch ; #VB6IT.DLL,0 ;italian language 0 2Ah 0 0 0A0000h 410h 409h 0 offset ProjectStruct 30F016h 0FFFFFF00h 8 1 2 0E9h offset DialogsStruct offset ExternStruct offset ProjectInfo 78h 7Eh 84h 85h 0 0 0 0

the rst eld is the signature of the struct itself. Note that the program has been written with VB 6.0, but the signature is VB5!, probably they didnt change it for cross compatibility? Or just forgot it? Who knows! However we can see various infos, the VB6IT.dll that should be the module to load the language (the app has been written with italian version of vb). What should really abtract your attention are the four pointers (ProjectStruct, DialogsStruct, ExternStruct, ProjectInfo). I gave them a name because i know their function, you should see just four addresse in the dasm. However we will analyse theese structs to nd infos we need.

0040131C ProjectInfo dd 0 ; DATA XREF: 004019C4o 00401320 dd 30h 00401324 dd 40h 00401328 dd 0 0040132C dd 0EACF9A13h 00401330 dd 4F93898Bh 00401334 dd 0DF0C5493h 00401338 dd 159AAEEDh 0040133C dd 0 00401340 dd 10000h 00401344 a020430progetto db 0,0,020430Progetto1,0 00401356 aC000 db -C000- 0040135C dd 0 ; here we can see various numbers, in particular there is the project name as last eld. If we go and search in the source of the app, we can see in the .vbp the following line

Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#D:\WINNT\System32\STDOLE2.TLB#OLE Automatio so we can think all the data bytes represent the type of project, modules contained etc etc. This isnt very useful, so we go on. We go back to main struct and see another sub structure: 004018F8 004018FC ExternStruct dd 6 ;flag ;import data

dd offset InsideImport

it is a bit harder to gure out what this structure is. This handles other imported functions, they can be inside or outside virtual machine module. The ag indicates the type of import (6 = inside, 7 = outside). If you look at InsideImport you will nd: 00402B64 00402B68 InsideImport dd offset Descr dd offset Thunk

Descr will point to four dwords that seem to be the same in all vb apps. Thunk will contain a pointer to an area where addresses (of some code of the virtual machine) are stored. I analysed an other vb app, its ExternStruct is:

//snippet from another vb app 00401A5C ExternStruct dd 7 ;outside 00401A60 dd offset ImportData 00401A64 dd 6 ;inside 00401A68 dd offset InsideImport //end snippet from another vb app

as you can see in that app there was an external function, infact the app used ShellExecute to open the internet browser and link to a site. The ImportData is as follows: //snippet from another vb app 00402DA4 ImportData dd offset aShell32 ;modulename 00402DA8 dd offset aShellexecutea ;apiname 00402DAC align 8 00402DB0 dd offset IAT_Data //end snippet from another vb app where you can see you have the pointers to the name of the module and the name of the api to import. The IAT_Data elds points to thunking data (hInstance of module and api address). This data is also used from the primitive DLL_Import of VB, used to thunk to outside apis. Back to our app, we check the DialogsStruct pointer: 00401A00 00401A04 00401A08 00401A0C 00401A10 00401A14 00401A18 00401A1C 00401A20 00401A24 00401A28 00401A2C 00401A30 00401A34 00401A38 00401A3C 00401A40 00401A44 00401A48 00401A4C 00401A50 00401A54 00401A58 00401A5C 00401A60 00401A64 00401A68 00401A6C 00401A70 00401A74 00401A78 00401A7C 00401A80 00401A84 00401A88 DialogsStruct[0] dd 50h ;sizeof struct

dd 356022C6h dd 400E3F28h dd 495240B8h dd 0C9491BB6h dd 0 dd 0 dd 0 dd 0 dd 0 dd 310h dd 0 dd 0 dd 0 dd 0 dd 0 dd 596h dd 0 dd offset MainDialog dd 4Ch DialogsStruct[1] dd 50h ;sizeofstruct dd 78CBBB9Fh dd 401EB563h dd 0DB80B296h dd 7EFFD31Ah dd 0 dd 0 dd 0 dd 0 dd 3 dd 100h dd 0 dd 0 dd 0 dd 0 5

00401A8C 00401A90 00401A94 00401A98 00401A9C

dd dd dd dd dd

0 1A3h 0 offset 9Ch

AboutDialog

we have an array of dialog descriptors. Each descriptor has various data values, in particular has in the 19th eld a pointer to a struct were the resource info are stored. Theese structure have variable size, because it depends on the data contained by the resources. Lets go and see MainDialog; i will not paste all the data but only the important things. 004013C2 004013CC aForm1 aLeimcrackme db 5,0,Form1,0 db 0Bh,0,Leimcrackme,0

004013E2 004016FA 004016FE 00401702 00401706

IconData

db 6,3,0,0,6Ch,74h,0,0,0FEh... dw 2Dh ; clientleft dw 14Ah ; clienttop dw 1248h ; clientwidth dw 0B7Ch ; clientheight

00401715 00401731

aText2 aText1

db 5,0,Text2,0 db 5,0,Text1,0 db 8,0,Command2,0 db 7,0,&Check!,0

0040174D aCommand2 0040175A aCheck

00401777 aCommand1 db 8,0,Command1,0 00401784 aAbout db 6,0,&About,0 004017A2 aLabel2 db 6,0,Label2,0 004017AD aSerial db 7,0,Serial:,0 004017D1 aLabel2_0 db 6,0,Label2,0 004017DC aName db 5,0,Name:,0 004017FC aLabel1 db 6,0,Label1,0 00401807 aWhoeverTriesTh db ,0,Whoever tries... you can see all the components of the crackme, their data, etc etc. IconData is the raw data of the icon of the main dialog.

If we look at the source we have: Begin VB.Form Form1 BorderStyle = 3 Fixed Dialog Caption = "Leimcrackme" ClientHeight = 2940 ClientLeft = 45 ClientTop = 330 ClientWidth = 4680 Icon = "andre.frx":0000 LinkTopic = "Form1" you can see the icon data is encoded in the .frx le, which usually have big data. So the Icon eld links to andre.frx le and 0000 is the oset of the starting data. Infact also the last label is linked as follows: Caption = $"andre.frx":030A infact in the frx le after the raw icon data there is at oset 30Ah the string of that label. Of course in the compiled app all the infos (.frm and .frx) are built in the dialog sutrcture. In the same way you can see the infos about the second dialog (AboutDialog). Now we go and check the most important structure: 00401AA0 00401AA4 00401AA8 00401AAC 00401AB0 00401AB4 00401AB8 00401ABC 00401AC0 00401AC4 00401CD4 00401CD8 ProjectStruct dd 1F4h ;signature?

dd offset Tree dd 0 dd offset StartOfCode dd offset EdnOfCode dd 1238h dd offset DataVar1 dd offset vbaExceptHandler dd offset StartOfData dd 84h dup(0) dd offset ExternStruct dd 1

Lets see the elds: there is a pointer to the exetern imports, we already covered this structure. There are StartOfCode and EndOfCode vars, they indicate where the executable code starts and where it ends. The code is delimited by two signatures (E9E9E9E9h starting, 9E9E9E9E ending) and some 0xCC padding. There is also a eld that is the pointer to the base per-thread exception handler, that is __vbaExceptHandler (but of course the code will install others handerls). There are the pointers to StartOfData and another pointer that points to StartOfData + 8, it seems theese values are common for all applications. In the 84h dup(0) space you can probably nd infos about path of the project etc, usually you see here some unicode strings, nothing interesting. The remaining eld, Tree, is a descriptor of code modules as they were in the source (and as they are oranized in the compiled exe).

So we are going to check this struct; it is a bit complex, so pay attention: 00402434 00402438 0040243C 00402440 00402444 00402448 0040244C 00402450 00402454 00402458 0040245C 00402460 00402464 00402468 0040246C 00402470 00402474 00402478 0040247C 00402480 00402484 Tree dd 0 dd offset VB_Func dd offset TreeData dd 0FFFFFFFFh dd 0 dd offset UnkVar1 dd 1F1CB8D4h dd 42793AE6h dd 51A97A97h dd 41E1033h dd 4000Ah dd 40004h dd offset ModulesList ;ptr to ModulesList[0] dd 0 dd 0 dd 0 dd offset aProgetto1_0 ; "Progetto1" dd 409h dd 410h dd 0 dd 2

VB_Func is a pointer to a location that is lled at runtime with some address of the vbvm, there are other elds such as the project name and an unknown var, but the interesting things are TreeData and ModuleList. Lets see TreeData: 00402DE0 00402DE4 00402DE8 00402DEC 00402DF0 00402DF4 00402DF8 00402DFC 00402E00 00402E04 00402E08 00402E0C 00402E10 00402E14 00402E18 00402E1C 00402E20 00402E24 00402E28 00402E2C 00402E30 00402E34 TreeData dd 0 dd offset Tree ;back pointer dd 0FFFFFFFFh dd 0 dd offset FormList dd 0 dd 0 dd 0 dd 0FFFFFFFFh dd 0 dd offset ProjectInfo2 dd offset RawData1 dd offset R_UnkVar1 dd offset ProjectInfo2 dd offset RawData2 dd offset R_UnkVar2 dd offset ProjectInfo2 dd offset RawData3 dd offset R_UnkVar3 dd offset ProjectInfo2 dd offset RawData4 dd offset R_UnkVar4 8

00402E38 00402E3C 00402E40

dd 0 dd offset dd offset

RawData5 R_UnkVar5

what we got here? A back pointer to tree struct, some raw data and ather infos about the project (you can see at projectInfo2 some data and usually you will nd the string C:\Programmi\Microsoft Visual Studio\VB98\VB6.OLB), the important pointer is FormList. Lets see: 00402D18 FormList dd offset Form[0] ;form1 00402D1C dd 0FFFFFFFFh ;module1 00402D20 dd 0FFFFFFFFh ;module2 00402D24 dd offset Form[1] ;form2 infact if we see the .vbw file we find: Form1 = Module1 Module2 Form2 = 186, 224, 986, 702, , 207, 76, 652, 524, C = 236, 214, 1036, 662, = 166, 196, 966, 644, 100, 283, 900, 731, , 173, 118, 973, 566, C

so the FormList simply points to a list of tied dialogs to the forms/modules of the project. Note that modules correspond to .bas les, forms to .frm les (which include .frx for raw resources). Lets go on: 00402D60 Form[0] dd 0 00402D64 00402D68 00402D6C 00402D70 00402D74 00402D78 00402D7C 00402D80 00402D84 00402D88 00402D8C 00402D90 00402D94 00402D98 00402D9C dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd offset FormDescriptor[0] 0FFFFFFFFh 0 0 0 offset FlagList 0 offset UnkData offset UnkData offset UnkData 0 0 0 58h 4

i will not paste Form[1] because its identical to Form[0]. We see some pointers to some data (mostly they point to null values), and then a pointer to FormDescriptor. Here we stop for now, we have to go back to check Tree.ModulesList structure (from there we will reach again FormDescriptor structs): 00402488 0040248C 00402490 00402494 00402498 0040249C 004024A0 004024A4 004024A8 004024AC 004024B0 004024B4 004024B8 004024BC 004024C0 004024C4 004024C8 004024CC 004024D0 004024D4 004024D8 004024DC 004024E0 004024E4 004024E8 004024EC 004024F0 004024F4 004024F8 004024FC 00402500 00402504 00402508 0040250C 00402510 00402514 00402518 0040251C 00402520 00402524 00402528 0040252C 00402530 00402534 00402538 0040253C 00402540 00402544 ModulesList[0] dd offset FormDescriptor[0] dd 0FFFFFFFFh dd offset Flags_0 dd 0 dd 0 dd 0 dd offset aForm1_0 ; "Form1" dd 5 dd offset OptionalData1 dd 0FFFFh dd 18083h ;Flags_1 dd 0 ModulesList[1] dd offset ModuleDescriptor[0] dd 0FFFFFFFFh dd offset Flags_1 dd 0 dd offset unk_modvar1 dd 0 dd offset aModule1 ; "Module1" dd 7 dd 0 dd 0FFFFh dd 18001h dd 0 ModulesList[2] dd offset ModuleDescriptor[1] dd 0FFFFFFFFh dd offset Flags_1 dd 0 dd offset unk_modvar2 dd 0 dd offset aModule2 ; "Module2" dd 3 dd 0 dd 0FFFFh dd 18001h dd 0 ModulesList[3] dd offset FormDescriptor[1] dd 0FFFFFFFFh dd offset Flags_2 dd 0 dd 0 dd 0 dd offset aForm2 ; "Form2" dd 2 dd offset OptionalData2 dd 0FFFFh dd 18083h dd 0 10

voil. We can easily see names and order of the forms/modules of the project. Note that the rst eld is a descriptor for the Module/Form, they are dierent descriptors, so they have a dierent structure. We can go and see ModuleDescriptors: 00401938 ModuleDescriptor[0] dd 10001h 0040193C dd offset Tree ;back pointer 00401940 dd 0 00401944 dd 0FFFFFFFFh 00401948 dd 0FFFFFFFFh 0040194C dd 0 00401950 dd offset ModulesList[1] ;back pointer 00401954 dd offset MD0_UnkVar 00401958 dd 0 0040195C dd 7D63150h 00401960 dd 0 00401964 dd 0 00401968 dd 0 0040196C dd offset RT_MainStruct ; ptr to following address nothing of interest here. The other module descriptor is like this so i wont paste it. Lets see form descriptors: 00401F48 FormDescriptor[0] dd 1 00401F4C dd offset Tree ;back pointer 00401F50 dd 0 00401F54 dd offset Form[0] ;back pointer 00401F58 dd 0FFFFFFFFh 00401F5C dd 0 00401F60 dd offset ModulesList ;back pointer 00401F64 dd offset DataVar1 00401F68 dd 0 00401F6C dd 7D98E18h 00401F70 dd 0 00401F74 dd 0 00401F78 dd 0 00401F7C dd offset FD0_Raw1 00401F80 dd 1 00401F84 dd offset FD0_Raw2 00401F88 dd 0 00401F8C dd offset FD0_Raw1 00401F90 dd 1 00401F94 dd offset FD0_ControlsList 00401F98 dd 0 00401F9C dd offset FD_Raw3 00401FA0 dd 7 ;number of controls in list 00401FA4 dd offset FD0_ControlsList 00401FA8 dd 1B70005h 00401FAC dd 6C0068h 00401FB0 dd offset FD0_Dispatcher 00401FB4 dd offset FD0_UnkVar1 00401FB8 dd 0 00401FBC dd 1324FCh 11

we see some unknown vars and some back pointers, what we care of is FD0_COntrolsList and FD0_Dispatcher. We see there are 7 controls in the list, so lets see the list: 00401FC8 00401FCC 00401FD0 00401FD4 00401FD8 00401FDC 00401FE0 00401FE4 00401FE8 00401FEC 00401FF0 00401FF4 00401FF8 00401FFC 00402000 00402004 00402008 0040200C 00402010 00402014 etc... FD0_ControlsList[0] dd 180040h ;control type dd 34h ;ID1 dd offset RawData1 dd 30005h ;ID2 dd 0 dd 0 dd offset LocalDispatcher[0] dd 7DC1BF0h dd offset aText2_0 ; "Text2" dd 30005h FD0_ControlsList[1] dd 180040h ;control type dd 38h ;ID1 dd offset RawData1 dd 30004h ;ID2 dd 0 dd 0 dd offset LocalDispatcher[1] dd 7DC1BF0h dd offset aText1_0 ; "Text1" dd 30004h

we have all the components of the dialog, which is fundamental in event tracking. You can see names for the controls, some strange values (ID1 and ID2, semms like they are internally used as resource id) and LocalDispatcher. This is the point of all! LocalDispatcher infact points to a structure that contains all event handlers of the controls! Lets see LocalDispatcher[0]: 004020F4 LocalDispatcher[0] dd 0 004020F8 dd offset FD0_ControlsList[0] 004020FC dd offset FormDescriptor[0] 00402100 dd offset i_EVENT_SINK_QueryInterface 00402104 dd offset i_EVENT_SINK_AddRef 00402108 dd offset i_EVENT_SINK_Release 0040210C dd 18h dup(0) the rst eld is always zero. The second is a backpointer to the parent structure in the controls list, the third is a backpointer to parent FormDescriptor structure. After we nd some basic handlers which are present in all controls, then we have no other handlers. This is the dispatcher of a label control, so it has no other handlers!

12

So we can choose another control in the list: 00402040 ControlsList[3] dd 110040h 00402044 dd 40h 00402048 dd offset RawData3 0040204C dd 30002h 00402050 dd 0 00402054 dd 0 00402058 dd offset LocalDispatcher[3] 0040205C dd 7DC1C10h 00402060 dd offset aCommand1_0 ; "Command1" 00402064 dd 30002h as before we go and see the LocalDispatcher[3]: 00402278 LocalDispatcher[3] dd 0 0040227C dd offset ControlsList_3_ 00402280 dd offset FormDescriptor_0_ 00402284 dd offset i_EVENT_SINK_QueryInterface 00402288 dd offset i_EVENT_SINK_AddRef 0040228C dd offset i_EVENT_SINK_Release 00402290 dd offset onClickAbout 00402294 dd 10h dup(0)

voil, what we were searching for! We have the onClickAbout pointer, that points to: 004023FD onClickAbout: 004023FD sub dword ptr [esp+4], 3Fh 00402405 onClickAbout1: 00402405 jmp onClickAboutRoutine that is a transfer area (internal function dispatcher) for local event handlers. So now we know what routine is executed when About button is pressed. The problem is, how did I know that it is a onClick handler? It could be a onOver, onMove etc etc? To answer this question lets see the Label1 dispatcher, we know that it has a onOver handler, we can see it at runtime (and we know it is label1 from line 004017FC remember?)

13

00402330 LocalDispatcher[5] dd 0 00402334 dd offset ControlsList[5] 00402338 dd offset FormDescriptor[0] 0040233C dd offset i_EVENT_SINK_QueryInterface 00402340 dd offset i_EVENT_SINK_AddRef 00402344 dd offset i_EVENT_SINK_Release 00402348 dd 0 0040234C dd 0 00402350 dd 0 00402354 dd 0 00402358 dd 0 0040235C dd 0 00402360 dd 0 00402364 dd 0 00402368 dd 0 0040236C dd offset onOverLabel1 00402370 dd 8 dup(0) we see the common handler and backpointers, then far away the onOver handler pointer. So my idea is that every eld of the LocalDispatch structure is a pointer to a given event handler (00402348 would be onClick etc), in addition the LocalDIspatch structure seems to be not exactly the same for all control types, so if you want to map all controls handlers you should write an app which uses all possible handlers and see where they are placed in this struct. Now we can go back to FormDescriptor[0] and check the last pointer, that was FD0Dispatcher, it points to 004020E0 004020E4 004020E8 004020EC 004020F0 FD0_Dispatcher dd offset dd offset dd offset dd offset dd offset onClickAboutPre1 onClickAbout1 onClickCheck1 OnOverForm1 onOverLabel11

a simple list to all import transfers addresses (they point directly to the jumps, the pointers in local dispatchers instead point to previous line!). As for this form, we can go in ModulesList[3] and check FormDescriptor[1], we will nd the controls list and associated event handlers.

14

In particular, in this form there is a moving button, if we check the LocalDispatcher of this button we see: 00401E70 FD1_LocalDispatcher[1] dd 0 00401E74 dd offset FD1_ControlsList[1] 00401E78 dd offset FormDescriptor_1_ 00401E7C dd offset i_EVENT_SINK_QueryInterface 00401E80 dd offset i_EVENT_SINK_AddRef 00401E84 dd offset i_EVENT_SINK_Release 00401E88 dd offset onClickOk 00401E8C dd 0 00401E90 dd 0 00401E94 dd 0 00401E98 dd 0 00401E9C dd 0 00401EA0 dd 0 00401EA4 dd 0 00401EA8 dd 0 00401EAC dd offset onOverOk 00401EB0 dd 7 dup(0) the other controls in the list just have default handlers. We have mapped all the resource and relative event handlers just by examining data structures (and using a bit of zen!). Now we can work on the code. Lets say we want to nd a correct serial for a given name. We know that the routine called when the Check button is pressed is at address 00402FD0, so we start from there: -standard function initialization 00402FD0 00402FD1 00402FD3 00402FD6 00402FDB 00402FE1 00402FE2 00402FE9 00402FEC 00402FED 00402FEE push ebp mov ebp, esp sub esp, 0Ch ;allocate private stackframe

push offset vbaExceptHandler ;installing default seh mov eax, large fs:0 push eax mov large fs:0, esp sub esp, 74h push ebx ;save registers area push esi push edi

-loading destructors 00402FEF mov [ebp-0Ch], esp 00402FF2 mov dword ptr [pDestruct], offset -allocating dynamic resource 00402FF9 mov esi, [ebp+8] 00402FFC mov eax, esi 00402FFE and eax, 1 00403001 mov [ebp-4], eax

Destructors_2

15

00403004 00403007 00403008 0040300B 0040300D

and esi, 0FFFFFFFEh push esi mov [ebp+8], esi mov ecx, [esi] call dword ptr [ecx+4] ;

Zombie_AddRef

the parent object (the form) is passed as parameter (ebp+8, sorry i forgot to resolve ebp based frame function in ida!) and the COM tecnology uses AddRef to increment reference count of the object (instantiation). For those of you that dont know this, COM objects are responsible for their lifetime, the resources they use are allocated until the reference count is 0, when it reaches 0 the objects enter zombie state and can be deallocated to free resources (well, things are a little more complex, see COM object management documentation to know more on this topic). -background color changing 00403010 00403012 00403015 0040301A 0040301F 00403022 00403023 00403026 00403029 0040302C 0040302F 00403035 00403038 0040303B 0040303E 0040303F 00403042 00403048 0040304B 0040304E 00403051 00403053 00403054 00403057 0040305D 00403063 00403069 0040306B 0040306D 00403073 00403075 00403078 0040307E 0040307F 00403081 00403083 00403089 xor eax, eax lea edx, [ebp-24h] mov ebx, 80020004h mov edi, 0Ah mov [ebp-24h], eax ;zero vars push edx mov [ebp-34h], eax mov [ebp-44h], eax mov [ebp-1Ch], ebx mov [ebp-24h], edi call ds:rtcRandomNext ;get random fp values fstp dword ptr [COlor_R] lea eax, [ebp-34h] mov [ebp-2Ch], ebx push eax mov [ebp-34h], edi call ds:rtcRandomNext fstp dword ptr [Color_G] lea ecx, [ebp-44h] mov [ebp-3Ch], ebx mov ebx, [esi] push ecx mov [ebp-44h], edi call ds:rtcRandomNext ;color_B not stored, used directly eax fmul ds:_0255 ;multiply each rand fpu * 255 mov edi, ds:__vbaR8IntI2 fnstsw ax test al, 0Dh jnz loc_40312F ;fpexception call edi ; __vbaR8IntI2 ;cast from _fpu real 8 bytes_ to _integer 2 bytes_ fld dword ptr [ebp-7Ch] fmul ds:_0255 push eax fnstsw ax test al, 0Dh jnz loc_40312F ;fpexception call edi ; __vbaR8IntI2 16

0040308B 0040308E 00403094 00403095 00403097 00403099 0040309F 004030A1 004030A2 004030A8 004030A9 004030AA 004030AD 004030AF 004030B1 004030B3 004030B5 004030BA 004030BB 004030BC 004030C2 004030C5 004030C8 004030C9 004030CC 004030CD 004030CE 004030D0

fld dword ptr [ebp-78h] fmul ds:_0255 push eax fnstsw ax test al, 0Dh jnz loc_40312F call edi ; __vbaR8IntI2 push eax call ds:rtcRgb push eax push esi call dword ptr [ebx+64h] test eax, eax fnclex jge short loc_4030C2 push push push push call

;fpexception

;rgb resulting from previous calculus ;form object instance ; MSVBVM_UnkFunc2 (Set back form color)

;taken

64h offset dword_402590 esi eax ds:__vbaHresultCheckObj

lea edx, [ebp-44h] lea eax, [ebp-34h] push edx lea ecx, [ebp-24h] push eax push ecx push 3 ;number of objects call ds:__vbaFreeVarList ;free vars used in previous rtcRandomNext

-name/serial check 004030D6 add esp, 10h 004030D9 call VoidCheck 004030DE test ax, ax 004030E1 jnz short loc_4030E8 004030E3 call SerialValidation 004030E8 mov dword ptr [ebp-4], 0 004030EF Destr_2_0: 004030EF wait 004030F0 push offset loc_403110 004030F5 jmp short EndRoutine

17

SerialValidation is the routine that checks the serial, so we will check it (see later). VoidCheck is the routine that checks if text elds are void, if so display the error message then exit. -destructors and ending stuff 004030F7 004030F7 004030FA 004030FD 004030FE 00403101 00403102 00403103 00403105 0040310B 0040310E 0040310F 0040310F 00403110 00403113 00403114 00403116 00403119 0040311C 0040311F 00403120 00403121 00403128 00403129 0040312B 0040312C 0040312F Destr_2_1: ;called in case of error lea edx, [ebp-44h] lea eax, [ebp-34h] push edx lea ecx, [ebp-24h] push eax push ecx push 3 ;number of vars to free call ds:__vbaFreeVarList add esp, 10h retn EndRoutine retn ;goes to 00403110 mov eax, [ebp+8] ;ptr to form object push eax mov edx, [eax] call dword ptr [edx+8] ;Zombie_Release (decrease reference count mov eax, [ebp-4] ;for form object) mov ecx, [ebp-14h] pop edi ;save registers area pop esi mov large fs:0, ecx ;restore exception handler pop ebx mov esp, ebp ;delete private stackframe pop ebp retn 4 jmp loc_4011AC ; __vbaFPException

are you beginning to feel the VB framework? Its really easy as you can see, the structure of the code is always the same.

18

Again lets see all the code, so you can understand how easy is code analisys: SerialValidation 004032C0 004032C1 004032C3 004032C6 004032CB 004032D1 004032D2 004032D9 004032DF 004032E0 004032E1 push ebp ;allocate private stackframe mov ebp, esp sub esp, 8 push offset vbaExceptHandler ;allocate exception handler mov eax, large fs:0 push eax mov large fs:0, esp sub esp, 158h push ebx ;registers save area push esi push edi

-destructors allocation and initialization 004032E2 mov [ebp+var_8], esp 004032E5 mov [ebp+var_4], offset Destructors_5 004032EC mov eax, Form1Instance 004032F1 xor edi, edi 004032F3 cmp eax, edi ;is form1 instanced? 004032F5 mov [ebp+var_20], edi ;zero vars 004032F8 mov [ebp+var_30], edi 004032FB mov [ebp+var_40], edi 004032FE mov [ebp+var_50], edi 00403301 mov [ebp+var_54], edi 00403304 mov [ebp+var_58], edi 00403307 mov [ebp+var_5C], edi 0040330A mov [ebp+var_6C], edi 0040330D mov [ebp+var_7C], edi 00403310 mov [ebp+var_8C], edi 00403316 mov [ebp+var_9C], edi 0040331C mov [ebp+var_AC], edi 00403322 mov [ebp+var_BC], edi 00403328 mov [ebp+var_CC], edi 0040332E mov [ebp+var_DC], edi 00403334 mov [ebp+var_EC], edi 0040333A mov [ebp+var_FC], edi 00403340 mov [ebp+var_10C], edi 00403346 mov [ebp+var_11C], edi 0040334C mov [ebp+var_13C], edi 00403352 mov [ebp+var_14C], edi 00403358 mov [ebp+var_15C], edi 0040335E jnz short loc_403375 ;jump if form1 istanced 00403360 push offset Form1Instance 00403365 push offset FormDescriptor_0_ 0040336A call ds:__vbaNew2 ;if not instanced the form wuold 00403370 mov eax, Form1Instance ;have been created here -copy vars from form instance to local vars 00403375 mov ecx, [eax] 00403377 push eax ;form object instance 19

00403378 0040337E 00403384 00403389 0040338C 0040338F 00403392 00403395 00403397 0040339C 0040339E 004033A0 004033A5 004033AA 004033B0 004033B5 004033B7 004033B8 004033BE 004033C1 004033C4 004033C7 004033CA

call dword ptr [ecx+308h] ;MSVBVM_UnkFunc (some sort of addref) mov esi, ds:__vbaVarMove mov ebx, 9 lea edx, [ebp+var_6C] lea ecx, [ebp+Text1] ;note, text1 object, not string! mov [ebp+var_64], eax ;(parameter in ecx, fastcall style) mov [ebp+var_6C], ebx call esi ; __vbaVarMove ;copy text1 in local var mov eax, Form1Instance cmp eax, edi jnz short loc_4033B5 ;avoid allocation of form if it exists push offset Form1Instance push offset FormDescriptor_0_ call ds:__vbaNew2 mov eax, Form1Instance mov edx, [eax] push eax call dword ptr [edx+30Ch] ; MSVBVM_UnkFunc (as above) lea edx, [ebp+var_6C] lea ecx, [ebp+Text2] ;note, text2 object, not string! mov [ebp+var_64], eax ;(parameter in ecx, fastcall style) mov [ebp+var_6C], ebx call esi ; __vbaVarMove ;copy text2 in local var

as you can see COM tecnology strikes again: in the form object there is the list of controls, each one with his own data (text controls have a simple string). So what does the code do? It simply copies the data from those object in local variables (Text1 and Text2), it uses __vbaVarMove to copy data. Attention: it does not copy only the string, but the whole text object! The parameter is passed via ecx register (fastcall convention), and is a pointer to the memory space that receives object data. So if you want to know the data of the object (the text in this case), after __vbaVarMove go to data pointed by Text1 (ebp-50h), and see the third dword: it is a pointer to the unicode string for name inserted. So the structure should be as follows: RT_Text_Object: +00 SizeOf(RT_Text_Object) +04 Method1 +08 TextPointer +0C etc (other values or methods) ok, we now know that the proggy has copied locally the data of text objects, we also know the addresses of this objects, so we can easily track all movements on theese strings.

20

-check length of name 004033CC 004033D1 004033D4 004033DA 004033E0 004033E6 004033E9 004033EA 004033EF 004033F0 004033F1 004033F7 004033FD 00403403 00403409 0040340F 00403410 00403416 00403419 0040341A 0040341B mov eax, 1 lea ecx, [ebp+Text1] mov [ebp+var_104], eax mov [ebp+var_F4], eax lea eax, [ebp+var_10C] lea edx, [ebp+var_6C] push eax mov esi, 2 push ecx ;object (Text1) push edx ;var object that receives length mov [ebp+var_10C], esi mov [ebp+var_FC], esi mov [ebp+var_114], edi mov [ebp+var_11C], esi call ds:__vbaLenVar ;get length of text1 (name) push eax ;var object for target value of subtraction lea eax, [ebp+var_FC] lea ecx, [ebp+var_7C] push eax ;var object for value to subtract (1) push ecx ;var object for result of subtraction call ds:__vbaVarSub

some easy function here, the rst function gets the Text1 and gets its length. As before keep in mind that the pointers points to OBJECTS, so vbaLenVar does no return the length of the string, but the object that contains the length. The object is as before: RT_Var_Object: +00 SizeOf(RT_Text_Object) +04 Method1 +08 Data (length of string)

+0C etc (other values or methods) so you must look the third dword at the memory pointed by the result pointer of vbaLenVar. Same thing for all other __vba***. So here it gets length of string and decrements it by one.

21

-main for() cycle 00403421 00403422 00403428 0040342E 0040342F 00403435 00403436 00403439 0040343A push eax lea edx, lea eax, push edx lea ecx, push eax lea edx, push ecx push edx ;(strlen(Text1)-1) object [ebp+var_11C] [ebp+var_15C] [ebp+var_14C] [counter] ;counter

0040343B call ds:__vbaVarForInit ;prepare for() cycle 00403441 mov edi, ds:__vbaVarMul 00403447 mov ebx, ds:__vbaVarAdd

-loop here 0040344D test eax, eax ;is cycle finished? (true = loop, false = exit) 0040344F jz EndCheck ;then check ends 00403455 mov eax, Form1Instance 0040345A test eax, eax 0040345C jnz short loc_403473 ;is form1 instanced? 0040345E push offset Form1Instance ;create instance if not 00403463 push offset FormDescriptor[0] 00403468 call ds:__vbaNew2 0040346E mov eax, Form1Instance 00403473 mov ecx, [eax] ;use current instance 00403475 push eax ;form1 instance 00403476 call dword ptr [ecx+308h] ;MSVBVM_UnkFunc 0040347C lea edx, [ebp+var_5C] 0040347F push eax 00403480 push edx 00403481 call ds:__vbaObjSet ;set object var to [ebp+var_5C] ptr 00403487 mov eax, 1 0040348C lea ecx, [counter] 0040348F mov [ebp+var_F4], eax ;set vars to true 00403495 mov [ebp+var_A4], eax 0040349B mov [ebp+var_114], eax 004034A1 mov eax, [ebp+var_5C] 004034A4 mov [ebp+var_94], eax 004034AA lea eax, [ebp+var_AC] 004034B0 push eax ;value to add (1) 004034B1 lea edx, [ebp+var_11C] 004034B7 push ecx ;target value of addiction (counter) 004034B8 lea eax, [ebp+var_8C] 004034BE push edx ;value to add 004034BF push eax ;target 004034C0 mov [ebp+var_104], 64h 004034CA mov [ebp+var_10C], esi 004034D0 mov [ebp+var_FC], esi 004034D6 mov [ebp+var_AC], esi

22

004034DC 004034E2 004034E9 004034F3 004034F5 004034F6 004034FC 00403502 00403503 00403509 0040350A 0040350B 00403511 00403517 0040351A 0040351B 0040351C 00403522 00403523 00403529 0040352A

mov [ebp+var_11C], esi mov [ebp+var_5C], 0 mov [ebp+var_9C], 9 call ebx ; __vbaVarAdd push eax call ds:__vbaI4Var lea ecx, [ebp+var_9C] push eax lea edx, [ebp+var_BC] push ecx push edx call ds:rtcMidCharVar lea eax, [ebp+var_BC] lea ecx, [ebp+var_54] push eax push ecx call ds:__vbaStrVarVal push eax call ds:rtcAnsiValueBstr push eax call ds:__vbaStrI2

;result object ;convert result object to int_4_bytes ;position ;length ;target string (name) ;extract one char from name at counter+1 position

;extracted char object ;name string ;extracted char value ;get ansi value from extracted value ;ansi string of value of mid ;convert it to int_2_bytes

this code increments the for() counter and gets the char at counter+1 position with mid function. Then it obtains the numeric value of the extracted char, then at the end it converts it into a unicode string representing that char in decimal number. Example: at iteration x it extracts Name[x+1] char, lets assume it is an A (0x41). Then it gets the numeric value (0x41) and then the unicode string 65 (dec for 0x41), unicode means the string will be (0x36 0x00 0x35 0x00).

00403530 00403532 00403535 0040353B 0040353C

mov edx, eax lea ecx, [ebp+var_58] ;ptr to unicode int2bytes call ds:__vbaStrMove ;move unicode decimal number in ebp+var48 push eax ;unicode decimal number call ds:rtcR8ValFromBstr ;convert unicode decimal number string ;in floating point value ;convert previous fp number in int4bytes

00403542 call ds:__vbaFpI4

23

here we have the numeric value of the extracted char in eax 00403548 00403549 0040354E 00403558 0040355A 00403560 00403563 00403569 0040356C 0040356D 0040356E 00403574 00403575 00403578 00403579 0040357A 0040357C 0040357F 00403580 00403581 00403583 00403584 0040358A 00403590 00403591 00403592 cdq mov ecx, 0Ah ;divisor mov [ebp+var_13C], 3 idiv ecx ;int4bytes % 10 lea eax, [ebp+var_10C] ;trash result lea ecx, [counter] ;trash divisor mov [rest], edx ;save rest of division lea edx, [ebp+var_40] push edx ;result string push eax ;value 100 (for multiplication) lea edx, [ebp+var_FC] push ecx ;target (counter) lea eax, [total] push edx ;value to add (1) push eax ;target (result) call ebx ; __vbaVarAdd ;increment counter lea ecx, [ebp+var_7C] push eax push ecx call edi ; __vbaVarMul ;100 * (counter+1) push eax lea edx, [ebp+var_13C] lea eax, [ebp+var_CC] push edx push eax call edi ; __vbaVarMul ;(100*(counter+1)) * previous calculus ;on extracted char 00403594 0040359A 0040359B 0040359C 004035A2 004035A8 004035A9 004035AA lea ecx, [ebp+var_DC] push eax push ecx call ds:__vbaVarInt lea edx, [ebp+var_EC] push eax push edx call ds:__vbaVarCat

;cast to int the result of all calculus ;int numeric value ;string that gets total ;cat unicode string of int numeric value ;to unicode string of total

004035B0 004035B2 004035B5 004035BB 004035BE 004035C1 004035C2 004035C3 004035C4 004035CA 004035CD 004035D0

mov edx, eax lea ecx, [ebp+var_40] call ds:__vbaVarMove lea eax, [ebp+var_58] lea ecx, [ebp+var_54] push eax push ecx push esi call ds:__vbaFreeStrList add esp, 0Ch lea ecx, [ebp+var_5C] call ds:__vbaFreeObj

;copy string of total to [ebp+var_40]

;esi = 2 (number of vars) ;free temp vars

;free temp object 24

004035D6 004035DC 004035E2 004035E3 004035E9 004035EA 004035F0 004035F1 004035F4 004035F5 004035F6 004035F8 004035FE 00403601 00403607 0040360D 00403610 00403611 00403612 00403613 00403619

lea edx, [ebp+var_BC] lea eax, [ebp+var_AC] push edx lea ecx, [ebp+var_8C] push eax lea edx, [ebp+var_9C] push ecx lea eax, [total] push edx push eax push 5 call ds:__vbaFreeVarList add esp, 18h lea ecx, [ebp+var_15C] lea edx, [ebp+var_14C] lea eax, [counter] push ecx push edx push eax call ds:__vbaVarForNext jmp loc_40344D

;temp total

;free 5 temp vars

;maximum value of counter ;incremental step ;actual counter ;if actual counter < maximum then return true ;else return false

this is the loop for serial calculus 0040361E 0040361E 00403623 00403628 0040362D 00403632 00403637 0040363C 0040363F 00403642 00403643 00403644 0040364A 0040364D 0040364F 00403654 00403655 0040365A 0040365C 0040365C 0040365C 00403661 00403662 00403667 00403669 00403669 00403669 0040366C EndCheck: ; CODE XREF: Check+18Fj call nullsub_1 ;null calls call nullsub_1 ;I asked _d31m0s_ to add them call nullsub_1 ;just to check some things call nullsub_1 call nullsub_1 call nullsub_1 lea ecx, [ebp+var_40] lea edx, [ebp+var_30] push ecx ;calculated serial (text object) push edx ;inserted serial (text object) call ds:__vbaVarTstEq ;compare the two string objects test ax, ax ;true = equal, false = different jz short Error ;if strings are different then error message call OkMessage ;else ok message wait ;useless waste... push offset loc_4036FE jmp short Ending Error: call ErrorMessage wait push offset loc_4036FE jmp short Ending Destructor: lea eax, [ebp-58h] lea ecx, [ebp-54h] ;called in case of error

25

0040366F 00403670 00403671 00403673 00403679 0040367C 0040367F 00403685 0040368B 00403691 00403692 00403698 00403699 0040369F 004036A0 004036A6 004036A7 004036AD 004036AE 004036B4 004036B5 004036B8 004036B9 004036BC 004036BD 004036BE 004036C0 004036C6 004036C9 004036CA 004036CA 004036D0 004036D6 004036D7 004036D8 004036DA 004036E0 004036E6 004036E9 004036EC 004036EE 004036F1 004036F3 004036F6 004036F8 004036FB 004036FD 004036FE 00403701 00403702 00403703 0040370A

push eax push ecx push 2 call ds:__vbaFreeStrList add esp, 0Ch lea ecx, [ebp-5Ch] call ds:__vbaFreeObj lea edx, [ebp-0ECh] lea eax, [ebp-0DCh] push edx lea ecx, [ebp-0CCh] push eax lea edx, [ebp-0BCh] push ecx lea eax, [ebp-0ACh] push edx lea ecx, [ebp-9Ch] push eax lea edx, [ebp-8Ch] push ecx lea eax, [ebp-7Ch] push edx lea ecx, [ebp-6Ch] push eax push ecx push 9 call ds:__vbaFreeVarList add esp, 28h retn Ending: lea edx, [ebp+var_15C] ;free vars lea eax, [ebp+var_14C] push edx push eax push 2 call ds:__vbaFreeVarList mov esi, ds:__vbaFreeVar add esp, 0Ch lea ecx, [counter] call esi ; __vbaFreeVar lea ecx, [ebp+var_30] call esi ; __vbaFreeVar lea ecx, [ebp+var_40] call esi ; __vbaFreeVar lea ecx, [ebp+var_50] call esi ; __vbaFreeVar retn ;return to 004036FE mov ecx, [ebp-10h] pop edi ;save register area pop esi mov large fs:0, ecx ;restore seh handler pop ebx

26

0040370B mov esp, ebp 0040370D pop ebp 0040370E retn

;delete private stackframe

ok, now we discovered how the serial is built, in particular if you see the __vbaVarTestEq function, the two parameters passed are two string objects, one for serial inserted and one for the correct serial, so you can do serial shing here without calculating. The algorithm we reversed is: nser = nser & Int(100 * (i + 1) * (Val(Asc(Mid(Form1.Text1, i + 1, 1))) Mod 10)) so debugging vb applications is really easy, just keep in mind that functions use Objects instead of direct values. Once you know this, COM jungle will not be a problem! Serial for "AndreaGeddon" is 50000160050042007008000011000, find your own!

SUMMARY

I hope that at the end of this tutorial you will have learned how to debug visual basic applications. As you can see it is really easy, you do not even need SmartCheck. The exe itself is full of precious infos, you can easily nd all event handlers. Then you just have to analyse the code to understand what the program does. Remember that COM tecnology is object based, so when you see functions and you analyse their parameters and return values, you know that you are considering OBJECTS, not the values directly. The values you search (strings, numbers, etc) will be encoded in the object. We encountered a lot of rtc* and __vba* apis here, they always use objects, infact when there is an addition you see __vbaVarAdd instead of a simple add asm instruction, this is because the function adds two number objects! Naturally the code uses also asm instructions for direct value arithmetics, this happens when there is some casting such as __vbaI4Var function etc etc. I wrote this tute because my intent is vb full decompiling, now that you know the structure of the compiled exes you can understand that decompiling is possible, and is relatively easy. Hope I will write a proggy about this one day! GREETS AND THANKS Thanks to _d31m0s_ who wrote the vb app for this tutorial (i will kill him for lame messages in it!), greets to all RET friends and great reversers! Greets to all UIC members an to all #crack-it people, see you all guys! GoodBye [AndreaGeddon] andreageddon@hotmail.com www.andreageddon.8m.com [RET] [UIC] www.reteam.org www.quequero.org my mail my lame italian site RETs great site italian university of cracking

27

You might also like