Visual Basic - VB40032.DLL comparison code
(Visual Basic 4.0 how-to additions to Razzia's writings and Softice BPX problems)

by sth

(21 October 1997)


Courtesy of reverser's page of reverse engineering

Well, a welcome visual basic addition to Razzia's great tutorial. Now you can plunge straight into the VB40032.dll and reverse its standard functions... The part of this essay about common bpr breakpointing problems with softice is also quite interesting. Enjoy it!

I.SoftICE BPX problems
II.Visual Basic 4.0 how-to additions to Razzia's writings

First I want to say: THANK YOU to Razzia for his
fantastic writing about Visual Basic.
I just want to add a few lines to it but before this I
would like to mention that all the commands here were
and should be executed under Soft Ice (for Windows95)
-> the most/extremely powerful software I've ever met!

Part 1. About having problems with BPR (Break Point in Range) command
I think that every one has had problems with it. According
to the SICE documentation it should mark a region for
R/W/RW/T/TW checking. But sometimes I found that there
is an action in the region, but the BPR does not work.
Why? :--(
After a few tests I finally understand what the BPR
selector:offset sometimes causes the problem. Here is
the hole story:
	1.You stop the running program with Ctrl+D.
	2.You find that the data you are interested in is between 
          123f:00000001 and 123f:00000005.
	3.You put 'BPR 123f:00000001 123f:00000005 R'.
	4.You run the program further with F5.
	5.->The program is running ....
	6.->There is a read from within the range ...
	7.->The program is running ..
	8.The program finishes and you are damn sure that there WAS 
          a reading!
	9.You begin hating SICE (and Windows95, personally) !

Now let's take a closer look at point 6. The reading
was made but ... from within another selector:offset.
This is quite normal. According to SICE documentation
the BPR command selects a region and this region
follows the data and code wherever it goes. Nice!
But then why it does not want to follow this noughty data?
Let's try to display the data from within the original
selector:offset... Nothing?! ERROR?!
We receive a message telling us that the selector does
not exists! Ha! Here is the key from the tent! The
selector is no longer used and SICE couldn't continue
checking for the region!

Now let's quickly solve the problem:--)
	1. You stop the running program with Ctrl+D.
	2. You find that the data you are interested in is again
           between 123f:00000001 and 123f:00000005.
	2a.You do PAGE 123f:00000001. You receive that the
           Liner Address of this data is, for example 80812301.
	3. You put 'BPR 030:80812301 030:80812305 R'.
	4. You run the program further with F5.
	5.->The program is running ....
	6.->There is a read from within the range ... AND IT STOPS, 
            because selector 030 always exist!
	9. You begin loving again Windows95 (especially the SICE, 
           personally) !

Part 2. Visual Basic 4.0 - 16 bit version
As Razzia already wrote there are only few places
where VB4 compares. But in the previous writing there
is not a word about VB40016.DLL. I find exactly where
it compares and here is the code:

: 8BF8                MOV     DI,AX
: 8EC2                MOV     ES,DX
: 1E                  PUSH    DS
: C5760E              LDS     SI,[BP+0E]
: 33C0                XOR     AX,AX
: F3A6                REPZ CMPSB        ; #0  here the strings in ds:si
: 7405                JZ      2667      ; and es:di are compared
: 1BC0                SBB     AX,AX
: 1DFFFF              SBB     AX,FFFF

And now - let's try it:
Name	: RADIUS TACACS SERVER 3.5
Where 	: http://ns.sblc.af.mil/ras/radtac35.exe
Size	: RADTAC.EXE = 377 376 bytes (main executable
module)
Protection	: uses VB40016.DLL and a MD5UTIL.DLL
(Borland C++ RTL)

Let's try with some test data first:
Registration name	: sth
Registration number	: 1997
Registration checksum	: 1234567890123456

This is a program for Internet Service Providers. It
takes care about all the modem PPP logins through
TCP/IP and it's very useful.

The whole task is to stay tuned to point #0. First the
program checks every digit from the CheckSum if it is
space (0x20) or dash. After checking all of them it
checks if the checksum is correct :--). So the correct
one (in my case ) is:
Registration checksum	: d2976c2d50c3abc8


Part 3. Visual Basic 4.0 - 32 bit version
Visual Basic 4.0 - 32 bit version uses wide char format
to all its string operations. According to Razzia's
writing most of the programs uses 'MultiByteToWideChar'
function before comparing stings. In most cases it is
true!
But I met a program which uses a little bit different
way to check the registration code.

Name	: Shiva AccessManager 2.0 - evaluation copy
Where 	: http://www.shiva.com/remote/radius/sam20.exe
Size	: RADTAC.EXE = 1 116 160 bytes (main executable
module)
Protection	: uses VB40032.DLL and a
UNET32.DLL(Microsoft Visual C++ RTL)

The requested information is:
Registration name	: Test
Product Code	: 1212
Serial Number	: 3434
ID Address of a computer	: 200.200.100.201
Number of users	: 5000
Registration code	: 1234567890123456 <-not the correct one!

The program reads all the input information you entered
and converts it to Wide Char format. Then the
UNET32.DLL calculates the requested 'Registration
code'. This is actually a check sum and it is based on
all the user entered fields except the 'Registration code' 
entry. The program writes it to Wide Char format. 
Finally it compares the requested code with the
one you entered in such way that if the code it
requests is:
	0x12 0x34 0x56 .... Then you have to enter: 123456...

If you put a breakpoint in 'MultiByteToWideChar'
function then you will not find the final comparison 
string because it is done by 'WideCharToMultiByte'
conversion.

It was written about the function 'MultiByteToWideChar'
(#1a) that it converts strings to  Wide Char format.
The next function 'WideCharToMultiByte' (#1b) is the
opposite. Here I gave a little more information:

KERNEL32!MultiByteToWideChar
: 2bd2                sub     edx,edx			<- #1a : 68c7e2f9bf push bff9e2c7 : 64ff32 push dword ptr fs:[edx] : 648922 mov fs:[edx],esp : 8b4c2414 mov ecx,[esp+14] : 8a01 mov al,[ecx] : 648f02 pop dword ptr fs:[edx] : 83c404 add esp,04 : e9644c0000 jmp bff7c8d8 KERNEL32!WideCharToMultiByte : 2bd2 sub edx,edx <- #1b : 68dde2f9bf push bff9e2dd : 64ff32 push dword ptr fs:[edx] : 648922 mov fs:[edx],esp : 8b4c2414 mov ecx,[esp+14] : 668b01 mov ax,[ecx] : 8b4c2424 mov ecx,[esp+24] : e302 jecxz bff77c90 : 8a01 mov al,[ecx] : 8b4c2428 mov ecx,[esp+28] : e302 jecxz bff77c98 : 8b01 mov eax,[ecx] : 648f02 pop dword ptr fs:[edx] : 83c404 add esp,04 : e93b320000 jmp bff7aede If you put a break point: BPX MultiByteToWideChar (#1a) and/or BPX WideCharToMultiByte (#1b) and the program you are running stops there then you'll have in Double Word (DW) format the following information: 1. In ss:esp you have the length of the SOURCE string; 2. In ss:esp+4 you have the RESULT's string offset (where the result will be put); 3. In ss:esp+c you have the SOURCE's string offset (where the source string is). Then if you type AFTER the break point: 'dd ss:esp+c' and then 'db ds:xxxxxxxx' (where xxxxxxxx is the value which resides in 'ss:esp+c') you'll see the source string. KERNEL32!CompareStringA : 2bd2 sub edx,edx #1c : 68c7e2f9bf push bff9e2c7 : 64ff32 push dword ptr fs:[edx] : 648922 mov fs:[edx],esp : 8b4c2414 mov ecx,[esp+14] If you put a break point: BPX CompareStringA (#1c) and the program you are running stops there then you'll have in Double Word (DW) format the following information: 1. In ss:esp+c you have the SOURCE's string offset (where the source string is). 2. In ss:esp+10 you have the SOURCE's length. 3. In ss:esp+14 you have the TARGET's string offset (where the target string is). 4. In ss:esp+18 you have the TARGET's length. Then if you type AFTER the break point: 'dd ss:esp+c' and then 'db ds:xxxxxxxx' (where xxxxxxxx is the value which resides in 'ss:esp+c') you'll see the source string. Then if you type AFTER the break point: 'dd ss:esp+18' and then 'db ds:xxxxxxxx' (where xxxxxxxx is the value which resides in 'ss:esp+18') you'll see the target string. If you recognise that the source string is the 'Registration code' you entered then go and see the Target string and length and THIS IS YOUR REGISTRATION CODE! ;-) At the beginning the program compares in 'CompareStringA' short strings (1-3 bytes long), where the offset is usually the same. Suddenly there is a comparison, where the target length is 0x08. This is the Registration Code! Just wait there for it! Finally, here is the part form VB40032.DLL where the comparison is made:

: 6a00                push    00
: 6a00                push    00
: 56                  push    esi                  #2a - length
: 57                  push    edi                  #2b - result's offset
: ff7514              push    dword ptr [ebp+14]
: ff7510              push    dword ptr [ebp+10]   #2c - source's offset
: 6a00                push    00
: 6a00                push    00
: ff1530c27b0f        call    [KERNEL32!WideCharToMultiByte]  #2x
: 8bf0                mov     esi,eax
: 6a00                push    00
: 6a00                push    00
: ff75fc              push    dword ptr [ebp-04]    #3a - length
: 53                  push    ebx	#3b - result's offset
: ff751c              push    dword ptr [ebp+1c]
: ff7518              push    dword ptr [ebp+18]    #3c - target's offset
: 6a00                push    00
: 6a00                push    00
: ff1530c27b0f        call    [KERNEL32!WideCharToMultiByte]  #3x
: 85f6                test    esi,esi
: 0f8427010000        jz      0f789121
: 85c0                test    eax,eax
: 0f841f010000        jz      0f789121
: 50                  push    eax                    #4a - length.target
: 53                  push    ebx                    #4b - target's offset
: 56                  push    esi                    #4c - length.source
: 57                  push    edi                    #4d - source's offset
: ff750c              push    dword ptr [ebp+0c]	
: ff7508              push    dword ptr [ebp+08]	
: ff15f8c17b0f        call    [KERNEL32!CompareStringA]	#4x

As +ORC says: That's (nice music for us! Let's have) a
(deep look at these) pretty data!
#2 - the first string ( #2x - executes the first conversion )
#3 - the second string ( #3x - executes the second conversion )
#4x - the program compares them! That's why it does not use 
'MultiByteToWideChar' but only 'WideCharToMultiByte' translation!

Special thanks to Razzia & reverser+

By sth

(c) sth 1997. All rights reversed
You are deep inside reverser's page of reverse engineering, choose your way out:

redBack to the +HCU Visual basic project
redhomepage redlinks redanonymity +ORC redstudents' essays redacademy database
redtools redcocktails redantismut CGI-scripts redsearch_forms redmail_fravia
redIs reverse engineering legal?