vb6 PCode NOP

Author: David Zimmer
Date: 08.29.19 - 10:02pm

I have not yet seen a true one byte NOP instruction for vb6 pcode. Now I have not yet googled for this, but just saw some discussion saying they couldnt find one and I had a fun thought how we can easily MAKE one.

Going back to the source in our previous post consider the following disasm
401D88 Form1.Form_Load:
401D88    F5 04000000           LitI4 0x4
401D8D    F5 03000000           LitI4 0x3
401D92    F5 02000000           LitI4 0x2
401D97    F5 01000000           LitI4 0x1
401D9C    05 0000               ImpAdLdRf Module1.Proc_401DD8
401D9F    0A 01001400           ImpAdCallFPR4 user32.CallWindowProcA
401DA4    3C                    GetLastError  
401DA5    FCC8                  End           <-- nop
401DA7    13                    ExitProcHresult 
This is a perfect example to try, how can we easily eliminate the end call? In this situation we could easily move the ExitProc up but that wont work in very many situations. I want a true one byte nop that has no consequences and can be used anywhere, and I know exactly how to do it easily.

In the opcode handler tables there are plenty of slots which are unused. They are all filled with an invalid opcode handler that just raises an exception just in case they are somehow hit.
ENGINE:66106D14 BE 4F 10 66  _tblByteDisp    dd offset _lblEX_Bos     
ENGINE:66106D18 23 C4 10 66                  dd offset InvalidExCode 

ENGINE:6610C423 InvalidExCode  
ENGINE:6610C423     push    33h
ENGINE:6610C425     call    _EbRaiseExceptionCode
Ok perfect opcode 01 is easy to remember and is a free slot vb will never use. Now we need to find the right block of asm we can re-use?
ENGINE:66105049  _lblEX_StAry                                                                    
ENGINE:66105049     call    ___vbaAryMove@8  
ENGINE:6610504E     xor     eax, eax
ENGINE:66105050     mov     al, [esi]
ENGINE:66105052     inc     esi
ENGINE:66105053     jmp     ds:_tblByteDisp[eax*4]  
Do you see what I see? Got lucky and found this on literally the second try when i scrolled down a few lines from my first instinct of looking at opcode 00 Bos* (see end for details on Bos)

Yeah, all we have to do is change 66106D18 to point to 6610504E and opcode 01 goes from invalid to a perfect one byte nop. (Thats table 5, index 1 for those keeping track)
ENGINE:66108114 tableLead4      dd offset _lblEX_FnStrComp3Var
ENGINE:66108118                 dd offset _lblEX_StAry  
You could patch your runtime on disk or with an injection dll. You could probably even use a sdb shim to patch it but never tried. Anyway since my debugger stuffs a dll into the target process and I am already dynamically locating and patching the opcode tables, this one is a gimmie for use while debugging in vbdec. Going to include it as a little bonus for sure.

Tested in manually in the debugger and worked perfect. Just a fun little find, smallest of tweaks really.

LargeBos: What the heck I am on a roll, you see this instruction everywhere but what does it do? Name makes no sense. Even knowing what it does name still makes no sense.

It takes two bytes off the stream as a word which are cur function start + delta to calculate a new address in the current function.

This address is stored in the vb runtime house keeping area of the stack at ebp-14h. This sets where to resume execution at the asm which is beginning of the next source line for routines which use On Error Resume Next.

I have renamed it ErrNext in my disasm routines.

Final fun fact, there is no On Error Resume Next opcode. They use OnError with the handler address set to 0xFFFF (or -1 to us humans)

Edit: So I did some googling and I found that people did find a natural 2 byte NOP in the form of:
ENGINE:661051E9 _lblEX_CI2UI1          
ENGINE:661051E9                 xor     eax, eax
ENGINE:661051EB                 mov     al, [esi]
ENGINE:661051ED                 inc     esi
ENGINE:661051EE                 jmp     ds:_tblByteDisp[eax*4] 
This is table 2 index 14, so would require a FC14 as referenced in Eternal Bliss VBCrackMe

Comments: (1)

On 08.30.19 - 1:31pm Dave wrote:
Reading up on some old articles on pcode I ran across John Chamberlins Microsofts P-Code Implementation where he has a answer for why there are so many duplicate opcodes:

Altogether there are 775 op-code handlers in VB6. This is possible because many of the 1351 opcodes map to the same handler. The main reason for the overlap is that there are actually two engines: the Visual Basic for Applications engine (in VBA6.DLL) which operates in the design environment and the run-time virtual machine engine (in MSVBVM60.DLL) that is used by executables. Many instructions have one op-code for VBA and a different parallel one for the run-time engine. The two engines have the same functionality with some slight differences mostly attributable to the special needs of the design environment like stepping through code that dont apply to run-time.
Actually after reading more of Johns work, Bos does make sense. He was seeing it generated it always for the IDE in memory pcode, while in compiled versions it only shows up when On Error Resume next is used. he called it beginning of line or by another name would be Beginning of Statement. Makes sense..

Leave Comment:
Email: (not shown)
Message: (Required)
Math Question: 59 + 46 = ? followed by the letter: V 

About Me
More Blogs
Main Site
Posts: (All)
2022 ( 2 )
2021 ( 4 )
2020 ( 8 )
2019 (12)
     Yara WorkBench
     vbdec dbg updates
     vb6 PCode NOP
     vb6 API and call backs
     how pcode works Pt1
     Reversing PCode Args
     VB6 PCode Disassembly
     VB6 PCode Debugger
     UConnect Disable Cell Modem
2017 (5)
     IDA python over IPC
     dns wildcard blocking
     64bit IDA Plugins
     anterior lines
     misc news/updates
2016 ( 4 )
2015 ( 6 )
2014 ( 5 )
2013 ( 9 )
2012 ( 13 )
2011 ( 19 )
2010 ( 11 )
2009 ( 1 )