Vb6 Runtime ForLoop Disasm


Author: David Zimmer
Date: 03.28.20 - 10:35am



If you want to see how lovingly and skillfully the vb6 runtime handlers were created consider how the For loop was coded.

The excerpt below shows the code it uses when the byte type is used as the loop counter.

The entire block of code you see at the bottom was written in pure assembly. The functionality of the different parts of it flow together and can also be used independently of one another. This one block handles 3 opcode entry points: ForUI1, ForStepUI1, and NextStepUI1.

For example we see that the ForUI1 handler used to initialize a loop when no specific step value was given, simply adds the default step value of 1 before falling through to the "parent" ForStepUI1 opcode handler.

The lblNextStepUI1Cmp is a reusable block that ForStepUI1 drops down into but which is also reused by the _lblEX_NextStepUI1 handler which can be called directly on its own.

It is a very elegant design loving crafted by an artisan.

For reference I have also included some vb6 and its related pcode disassembly so you can see how the args are passed on the stack and encoded into the opcode bytestream. There is also a video demo showing it live in action.


Vb6 Source:
------------
Dim b As Byte
For b = &H22 To &H26 Step 3
	If b = 4 Then Exit For
Next
	
	
Generated PCode:
------------
401614    F4 22                 LitI2_Byte 34         'push 0x22 - start count
401616    FC0D                  CUI1I2                'bounds check in byte range
401618    04 5AFF               FLdRfVar var_A6       'push addr or var_a6 onto stack
40161B    F4 26                 LitI2_Byte 38         'push 0x26 - end count
40161D    FC0D                  CUI1I2
40161F    F4 03                 LitI2_Byte 3          'push step increment 
401621    FC0D                  CUI1I2
401623    FE6A 56FF2D00         ForStepUI1 var_AA loc_401641 'bytestream args: control struct, end of loop addr
401629    FCE0 5AFF             	FLdUI1 var_A6            'this end of loop addr only used if loop condition false on start
40162D    FC14                  	CI2UI1
40162F    F4 04                 	LitI2_Byte 4
401631    C6                    	EqI2
401632    1C 2400               	BranchF loc_401638
401635    1E 2D00               	Branch loc_401641
401638    04 5AFF               	FLdRfVar var_A6
40163B    FE80 56FF1500         NextStepUI1 var_AA loc_401629 'byte stream args: control struct, first loop instr addr 


VB6 Runtime ForStepUI1 disasm ------------------------------- ENGINE:66109E3D _lblEX_ForUI1 ENGINE:66109E3D push 1 ; stuff handcoded step increment of 1 on stack ENGINE:66109E3F mov edi, edi ; do nothing ENGINE:66109E41 _lblEX_ForStepUI1 ENGINE:66109E41 movsx edi, word ptr [esi] ; move 2 bytes from bytestream into edi (control struc var rel offset) ENGINE:66109E44 pop ebx ; load step increment off stack ENGINE:66109E45 mov [edi+ebp], bl ; save step increment into first byte of loop control struct ENGINE:66109E48 pop ebx ; load the end loop val from stack ENGINE:66109E49 mov [edi+ebp+1], bl ; save to loop control struct +1 ENGINE:66109E4D pop ebx ; load address of loop counter variable ENGINE:66109E4E pop ecx ; load the loop start index from stack ENGINE:66109E4F movzx eax, word ptr [esi+2] ; second bytestream argument = end of loop rel address ENGINE:66109E53 add esi, 4 ; increment opcode pointer past bytestream args ENGINE:66109E56 add eax, [ebp-58h] ; add curFunc start to get absolute end of loop address ENGINE:66109E59 lblNextStepUI1Cmp ENGINE:66109E59 mov [ebx], cl ; save current counter to variable ENGINE:66109E5B cmp cl, [edi+ebp+1] ; compare counter to end value in control structure ENGINE:66109E5F ja short loopOver ; is loop over now? ENGINE:66109E61 xor eax, eax ; zero out all bits in eax ENGINE:66109E63 mov al, [esi] ; move next opcode from opcode stream into al ENGINE:66109E65 inc esi ; increment opcode stream index 1 to next ENGINE:66109E66 jmp ds:_tblByteDisp[eax*4] ; jump to the opcode handler for opcodeX ENGINE:66109E6D _lblEX_NextStepUI1 ENGINE:66109E6D movsx edi, word ptr [esi] ; load next 2 bytes from stream (control struct var addr) ENGINE:66109E70 pop ebx ; address of counter variable ENGINE:66109E71 mov cl, [ebx] ; cl = current counter val ENGINE:66109E73 add cl, [edi+ebp] ; add step increment from control struct ENGINE:66109E76 jb lblOverflow_0 ENGINE:66109E7C movzx eax, word ptr [esi+2] ; load loop start rel offset from byte stream ENGINE:66109E80 add esi, 4 ; move opcode index pointer past our bytestream args ENGINE:66109E83 add eax, [ebp-58h] ; add rel offset to cur func start ENGINE:66109E86 xchg eax, esi ; esi = after for loop opcode, eax = for loop start address ENGINE:66109E87 jmp short lblNextStepUI1Cmp ; save incremented counter to variable ENGINE:66109E89 loopOver: ENGINE:66109E89 mov esi, eax ; move saved end of loop abs address into opcode pointer ENGINE:66109E8B xor eax, eax ; clear all bits in eax ENGINE:66109E8D mov al, [esi] ; move next opcode from opcode bytestream into AL ENGINE:66109E8F inc esi ; increment opcode pointer ENGINE:66109E90 jmp ds:_tblByteDisp[eax*4] ; jump to next opcode handler using opcode value as index





Comments: (0)

 
Leave Comment:
Name:
Email: (not shown)
Message: (Required)
Math Question: 31 + 40 = ? followed by the letter: F 



Twitter
RSS
About Me
More Blogs
Main Site
Posts: (All)
2020 (7)
     Using VB6 Obj files from C
     Reusing Pcode Functions
     Vb6 PCode Internals
     Vb6 Runtime ForLoop Disasm
     VB6 Pcode - For Loops
     Yara Corrupt Imports
     Yara Undefined values
2019 ( 12 )
2017 ( 5 )
2016 ( 4 )
2015 ( 6 )
2014 ( 5 )
2013 ( 9 )
2012 ( 13 )
2011 ( 19 )
2010 ( 11 )
2009 ( 1 )