An experiment of RE- engineering for Object Pascal programs.
Sample:dbexplorer 4.00 (c)Inprise Corp.1995-1998.
Published by +Tsehp March 2000
PART I -The PROBLEM
Once upon a time, my brothers DB programmers complained to me, that the life
is not so beautiful as it might be.) The point is that Dbexplorer (by the way ,it is
a tool for reversing databases logic)has some (or better to say has not) features ,
that degrade productivity of a programmer. If you do not know,the Program dbexplor.exe
is a standard tool for designing SQL databases in DELPHI and C++ BUILDER. The program
has several lacks of options and one of them is very annoying. Edit fields of the program
have no string and character numeration. However sql server often gives an error message,
which points to specific location:
General SQL error.
Token unknown - line 193, char 91
fromsfhM
but it is not so eazy to locate the error .The serious "STORED PROCEDURE" or
"TRIGGER" can take several pages of text.
Before I begin to describe REVERSE ENGINEERING and REENGINEERING of the program,
let me say some words to a reader , that did not see DELPHI and knows nothing about
principles of some Visual programming systems.
For preventing re-storing the same code , that can turn us to store megs of
useless information, Windows (and some other modern operating systems )uses,DYNAMIC LINK
LIBRARIES,or DLLs(Dynamic Load Library).Delphi uses it's own format of the libraries ,
called Packages.In fact they are DLLs,no doubt.
The packages are of interest to as with two reasons:
1) DBEXPLOR.exe -is just a shell for loading package dbx40.bpl
2) There are functions and classes methods address (and names) exported
from packages,including type info.( C++ mangled names)
And here is some information about calling conventions (convention about parameters
passing and stack correction). As it widely known DeLpHi use "fastcall" or "Register"
convention ,i.e. first 3 arguments passed through eax,edx,ecx respectively and others are
pushed to Stack left to right.
Example:
a:= The_FuncTioN(first,second,sird,fourth,fifth) ;
//compiled to
mov eax,[first]
mov edx,[second]
mov ecx,[sird]
push [fourth]
push [fifth]
call The_FuncTioN
mov [a],eax
To understand the following listings you need to note that in class methods, there is
a hidden first parameter Self (this in C++)- pointer to object instance,which owned the method.
So parameter numeration is right shifted by. As i can see it was implemented for making possible
de -referencing of object's instance's fields(data members).By the way in Delphi this->firstdword
is address of class VMT(virtual methods table).
Well, we are ready for rock and roll. What do we need to begin coding? we need to decide,
what we gonna do. Making the task done with traditional WINDOWS programming ,we 'd need to get
HWND of multiedit control , and make subclassing of the window OR intercept it's WndProc and
re-code it to make it do what we need .Following the ways drives us to a long process of code
analyzis and deep Thinking,and after that we can only constatate that the second way is impossible,
as the WndProc of the TMemo(BORLAND's encapsulated multiline edit)resides in a VCL40.bpl -a
placeholder for many standard windows controls and other components of VCL(VISUAL COMPONENTS
LIBRARY- DELPHI- C++ BUILDER), so the module is common for several others applications and we
can not modify it without affecting them.
The way of subclassing pushes us to reverse object mechanics to find TMemo.Handle-
hwnd of the object.
But there is another way :-to write additional code , wich repeats the code generated
by compiler ,weather we decided to write it , having the source of the program.
Let us make a model of the program,where Tmemo object'd tell us about cursor position
in it.
PART II -the point of no returning
Let us create a new Delphi Progect .I have used Delphi 4.0 , because of gesture, that
SQL explorer 4.00 was compiled with the version of compiler . Drop to the form a Tmemo,
Tbutton and a TStatusBar components. Let us code event handlers ONCLICK,ONKEYUP è ONKEYDOWN
to make them find cursor position and write it to StatusBar:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
StatusBar1: TStatusBar;
procedure Button1Click(Sender: TObject);
procedure coordpicker2(Sender: TObject);
private
procedure coordpicker(Sender: TObject; var Key: Word; Shift: TShiftState) ;
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure Tform1.coordpicker(Sender: TObject; var Key: Word; Shift: TShiftState) ;
begin
coordpicker2(sender);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
memo1.OnClick :=coordpicker2;
memo1.onkeydown:=coordpicker;
memo1.onkeyup :=coordpicker;
adding handlers
end;
procedure TForm1.coordpicker2(Sender: TObject);
var startt,endt:integer;//variables
begin
tcontrol(sender).perform(EM_GETSEL,dword(@startt),dword(@endt));
//number of symbol under the cursor->startt
endt:=tcontrol(sender ).perform(EM_LINEFROMCHAR,startt,0);
//number of line ->endt
startt :=startt- tcontrol(sender).perform(EM_LINEINDEX,endt,0);
//number of symbol from the begining of the line->startt
inc(startt);
inc (endt);
//WINDOWS returns 0 based values
statusbar1.SimpleText:=format('[%8d:%8d]',[endt,Start]);
// output to statusbar
end;
end.
Compile the project , setting "compile with packages " to "ON".
Load the file to Interactive DisAssembler (IDA 3.84), demo version does fit the goal
as the file is less than 64kb.
and we have the code:
(with some comments)
;CODE:00401810
; ??????????????? S U B R O U T I N E ???????????????????????????????????????
; Attributes: library function bp-based frame
;procedure Tform1.coordpicker(Sender: TObject; var Key: Word; Shift: TShiftState) ;
;begin
;coordpicker2(sender);
;end;
coordpicker proc near ; DATA XREF: _TForm1_Button1Click+1Co
; _TForm1_Button1Click+2Co
push ebp
mov ebp, esp
call _TForm1_coordpicker2
pop ebp
retn 4
coordpicker endp
; ??????????????? S U B R O U T I N E ???????????????????????????????????????
;procedure TForm1.Button1Click(Sender: TObject);
;begin
;memo1.OnClick :=coordpicker2;
;memo1.onkeydown:=coordpicker;
;memo1.onkeyup :=coordpicker;
;end;
_TForm1_Button1Click proc near ; DATA XREF: CODE:004017B5o
mov edx, [eax+2C4h]
mov [edx+108h], eax
mov dword ptr [edx+104h], offset _TForm1_coordpicker2
mov [edx+1B4h], eax
mov dword ptr [edx+1B0h], offset coordpicker
mov [edx+1C4h], eax
mov dword ptr [edx+1C0h], offset coordpicker
retn
_TForm1_Button1Click endp
; ???????????????????????????????????????????????????????????????????????????
align 4
; ??????????????? S U B R O U T I N E ???????????????????????????????????????
; Attributes: bp-based frame
_TForm1_coordpicker2 proc near ; CODE XREF: coordpicker+3p
; DATA XREF: CODE:004017C8o ...
procedure TForm1.coordpicker2(Sender: TObject);
var_1C = dword ptr -1Ch
var_18 = byte ptr -18h
var_14 = dword ptr -14h
var_10 = byte ptr -10h
var_C = dword ptr -0Ch
var_8 = dword ptr -8;var endt:integer;vars
var_4 = dword ptr -4;var startt:integer;//vars
push ebp
mov ebp, esp
add esp, 0FFFFFFE4h
push ebx
push esi
push edi
xor ecx, ecx
mov [ebp+var_C], ecx
mov ebx, edx
mov edi, eax
xor eax, eax
push ebp
push offset loc_0_401906
push dword ptr fs:[eax]
mov fs:[eax], esp
;exception handler
lea eax, [ebp+var_8]
push eax
lea ecx, [ebp+var_4]
mov edx, 0B0h
mov esi, ebx
mov eax, esi
call Controls::TControl::Perform(uint,int,int)
;tcontrol(sender).perform(EM_GETSEL,dword(@startt),dword(@endt));
push 0
mov ecx, [ebp+var_4]
mov edx, 0C9h
mov eax, esi
call Controls::TControl::Perform(uint,int,int)
mov [ebp+var_8], eax
;endt:=tcontrol(sender ).perform(EM_LINEFROMCHAR,startt,0);
push 0
mov ecx, [ebp+var_8]
mov edx, 0BBh
mov eax, esi
call Controls::TControl::Perform(uint,int,int)
sub [ebp+var_4], eax
;startt :=startt- tcontrol(sender).perform(EM_LINEINDEX,endt,0);
inc [ebp+var_4]
;inc(startt);;
inc [ebp+var_8]
;inc (endt);
lea eax, [ebp+var_C]
push eax
mov eax, [ebp+var_8]
mov [ebp+var_1C], eax
mov [ebp+var_18], 0
mov eax, [ebp+var_4]
mov [ebp+var_14], eax
mov [ebp+var_10], 0
lea edx, [ebp+var_1C]
mov ecx, 1
mov eax, offset _str___8d__8d_.Text
call Sysutils::Format(System::AnsiString,System::TVarRec *,int)
mov edx, [ebp+var_C]
mov eax, [edi+2CCh]
call Comctrls::TStatusBar::SetSimpleText(System::AnsiString)
;statusbar1.SimpleText:=format('[%8d:%8d]',[endt,Start]);
xor eax, eax
pop edx
pop ecx
pop ecx
mov fs:[eax], edx
push offset loc_0_40190D
loc_0_4018FD: ; CODE XREF: _TForm1_coordpicker2+B7j
;finally
lea eax, [ebp+var_C]
call System::`intcls'::LStrClr(System::AnsiString &)
;destroy the temporary string
retn
; ???????????????????????????????????????????????????????????????????????????
loc_0_401906: ; DATA XREF: _TForm1_coordpicker2+15o
jmp loc_0_401018
; ???????????????????????????????????????????????????????????????????????????
jmp short loc_0_4018FD
;Except
; ???????????????????????????????????????????????????????????????????????????
;
loc_0_40190D: ; DATA XREF: _TForm1_coordpicker2+A4o
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
retn
_TForm1_coordpicker2 endp
; ???????????????????????????????????????????????????????????????????????????
_str___8d__8d_ dd 0FFFFFFFFh ; _top ; DATA XREF: _TForm1_coordpicker2+84o
dd 9 ; Len
db '[%8d:%8d]',0 ; Text
align 4
Explore the listing .As you can see ,the talks of overbloated code from Delphi
compiler is a bit false, we can not see anything , that our program does not need,so
we can simply copy the code to dbx40.bpl body(making some changes, however).Note that
before using an object's method , register EAX is filled with object address. This address
is taken from some offset of the object , containing that one (a parent object).
mov eax, [edi+2CCh]
call Comctrls::TStatusBar::SetSimpleText(System::AnsiString)
The offsets is uncially defined by the compiler for every object in a program,
and we'll see how easy we can find them.
Note a very important detail , installing event handlers.
_TForm1_Button1Click proc near ; DATA XREF: CODE:004017B5o
mov edx, [eax+2C4h]; edx:=Memo1;
mov [edx+108h], eax; eax=form1
mov dword ptr [edx+104h], offset _TForm1_coordpicker2;OnClick
mov [edx+1B4h], eax
mov dword ptr [edx+1B0h], offset coordpicker ;OnKeyDown
mov [edx+1C4h], eax
mov dword ptr [edx+1C0h], offset coordpicker ;OnKeyUp
retn
_TForm1_Button1Click endp
The detail is - offsets to Events, and the fact , that event is actually
two dwords-,
struct EVENT
DWORD PROCADDRES
DWORD OBJECTADDRESS
ends
OBJECTADDRESS is an object reference(self,this), the value is transmitted to eax
before call to an object's event handler.
PART III -CULMINATION
So this time to look at the main module of SQL EXPLORER- dbx40.bpl
First,We need to find two objects of type Òìåìî and TStatusBar.
we'll do it easy and elegant .Open "Names" and find "SetSimpleText"
let us check the first reference...
mov eax, [ebp+var_C]
mov eax, [eax+54Ch]; ;EuRiCa!
mov edx, [ebp+var_18]
call Comctrls::TStatusBar::SetSimpleText
Further , scrolling the disassembler's listing i found this
(i am very lucky person:).
CODE:4104DA7F dw 500h
CODE:4104DA81 dw 0
CODE:4104DA83 dw 0Eh
CODE:4104DA85 db 9,'QueryGrid'
CODE:4104DA8F dw 504h
CODE:4104DA91 dw 0
CODE:4104DA93 dw 6
CODE:4104DA95 db 9,'QueryMemo'
CODE:4104DA9F dw 508h
CODE:4104DAA1 dw 0
CODE:4104DAA3 dw 2
CODE:4104DAA5 db 10,'QueryPanel'
CODE:4104DAB0 dw 50Ch
CODE:4104DAB2 dw 0
One need not to be a genius to understand what is what. offset 504h
from the beginning of main form object is the reference to QueryMemo - must be
a field where we edit SQL queries.
some lines after the text we can see an facts , that tell us:"You are on
the right way , boy" :
CODE:4104DBD8 dw 54Ch
CODE:4104DBDA dw 0
CODE:4104DBDC dw 11h
CODE:4104DBDE db 9,'StatusBar'
and some lnes above:
CODE:4104D22F dw 2E4h
CODE:4104D231 dw 0
CODE:4104D233 dw 6
CODE:4104D235 db 14,'DefinitionMemo'
CODE:4104D244 dw 2E8h
So, we have found all the components,let us find the place to
link our code to program , and of course the place for new code itself.
For soft embedding,let us extend FormCreate function, found in listing here:
dd offset _TDbExplorerForm_FormCreate;410510c4
db 10,'FormCreate'
insert there an address of empty space:41068ñ6f, and start
to copy code from another instance of IDA
CODE:41068C6F theplacefornewc: ; DATA XREF: CODE:4104DDAE?o
CODE:41068C6F nop
CODE:41068C70 push eax
CODE:41068C71 push edx
CODE:41068C72 mov edx, [eax+2E4h] ; sqlQuery
CODE:41068C78 call $+5 almost virus tech
CODE:41068C7D pop ebx
CODE:41068C7E sub ebx, 58C7Dh;in åâõ òåïåðü we have BASEADDRESS
CODE:41068C84 mov [edx+108h], eax
CODE:41068C8A lea esi, [ebx+58D00h]
CODE:41068C90 mov [edx+104h], esi
CODE:41068C96 mov [edx+1B4h], eax
CODE:41068C9C lea esi, [ebx+58DF6h]
CODE:41068CA2 mov [edx+1B0h], esi
CODE:41068CA8 mov [edx+1C4h], eax
CODE:41068CAE lea esi, [ebx+58DF6h]
CODE:41068CB4 mov [edx+1C0h], esi
CODE:41068CBA mov edx, [eax+504h] ; definition
CODE:41068CC0 mov [edx+108h], eax
CODE:41068CC6 lea esi, [ebx+58D00h]
CODE:41068CCC mov [edx+104h], esi
CODE:41068CD2 mov [edx+1B4h], eax
CODE:41068CD8 lea esi, [ebx+58DF6h]
CODE:41068CDE mov [edx+1B0h], esi
CODE:41068CE4 mov [edx+1C4h], eax
CODE:41068CEA lea esi, [ebx+58DF6h]
CODE:41068CF0 mov [edx+1C0h], esi
CODE:41068CF6 pop edx
CODE:41068CF7 pop eax
CODE:41068CF8 add ebx, 410C4h
CODE:41068CFE jmp ebx
CODE:41068D00
CODE:41068D00 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
CODE:41068D00
CODE:41068D00 ; Attributes: bp-based frame
CODE:41068D00
CODE:41068D00 clicks proc near ; CODE XREF: keys?p
CODE:41068D00
CODE:41068D00 var_1C = byte ptr -1Ch
CODE:41068D00 var_18 = byte ptr -18h
CODE:41068D00 var_14 = dword ptr -14h
CODE:41068D00 var_10 = byte ptr -10h
CODE:41068D00 dinARSTRT = dword ptr -0Ch
CODE:41068D00 start = dword ptr -8
CODE:41068D00 end = dword ptr -4
CODE:41068D00
CODE:41068D00 push ebp
CODE:41068D01 mov ebp, esp
CODE:41068D03 sub esp, 1Ch
CODE:41068D06 push ebx
CODE:41068D07 push esi
CODE:41068D08 push edi
CODE:41068D09 xor ecx, ecx
CODE:41068D0B mov [ebp+dinARSTRT], ecx
CODE:41068D11 mov ebx, edx
CODE:41068D13 mov edi, eax
CODE:41068D15 xor eax, eax
CODE:41068D17 test edi, edi; debug remains , always true
CODE:41068D19 jnz short loc_0_41068D23
CODE:41068D1B jmp loc_0_41068DEF;//eliminated try-except-finally
CODE:41068D1B ; -----------------------------------------------------------------------
CODE:41068D20 db 64h ; d
CODE:41068D21 db 89h ; ?
CODE:41068D22 db 20h ;
CODE:41068D23 ; -----------------------------------------------------------------------
CODE:41068D23
CODE:41068D23 loc_0_41068D23: ; CODE XREF: clicks+19?j
CODE:41068D23 lea eax, [ebp-8]
CODE:41068D29 push eax
CODE:41068D2A lea ecx, [ebp-4]
CODE:41068D30 mov edx, 0B0h
CODE:41068D35 mov esi, ebx
CODE:41068D37 mov eax, esi
CODE:41068D39 call Controls::TControl::Perform
CODE:41068D3E push 0
CODE:41068D40 mov ecx, [ebp-4]
CODE:41068D46 mov edx, 0C9h
CODE:41068D4B mov eax, esi
CODE:41068D4D call Controls::TControl::Perform
CODE:41068D52 mov [ebp-8], eax
CODE:41068D58 push 0
CODE:41068D5A mov ecx, [ebp-8]
CODE:41068D60 mov edx, 0BBh
CODE:41068D65 mov eax, esi
CODE:41068D67 call Controls::TControl::Perform
CODE:41068D6C sub [ebp+end], eax
CODE:41068D72 inc [ebp+end]
CODE:41068D78 inc [ebp+start]
CODE:41068D7E lea eax, [ebp+dinARSTRT]
CODE:41068D84 push eax
CODE:41068D85 mov eax, [ebp+start]
CODE:41068D8B mov dword ptr [ebp+var_1C], eax
CODE:41068D91 mov [ebp+var_18], 0
CODE:41068D98 mov edx, [ebp+end]
CODE:41068D9E mov [ebp+var_14], edx
CODE:41068DA4 mov [ebp+var_10], 0
CODE:41068DAB lea edx, [ebp+var_1C]
CODE:41068DB1 mov ecx, 1
CODE:41068DB6 call $+5
CODE:41068DBB pop eax
CODE:41068DBC sub eax, 58DBBh
CODE:41068DC1 add eax, 8E5A8h
CODE:41068DC6 call Sysutils::Format
CODE:41068DCB mov edx, [ebp+dinARSTRT]
CODE:41068DD1 mov eax, [edi+54Ch]
CODE:41068DD7 call Comctrls::TStatusBar::SetSimpleText
CODE:41068DDC xor eax, eax
CODE:41068DDE
CODE:41068DDE loc_0_41068DDE:
CODE:41068DDE jmp short loc_0_41068DE4;//the remains of try-except-finally
CODE:41068DDE ; -------------------------------------------------------------------
CODE:41068DE0 dd 10896459h
CODE:41068DE4 ; -------------------------------------------------------------------
CODE:41068DE4
CODE:41068DE4 loc_0_41068DE4: ; CODE XREF: clicks+DE?j
CODE:41068DE4 lea eax, [ebp+dinARSTRT]
CODE:41068DEA call System::`intcls'::LStrClr
CODE:41068DEF
CODE:41068DEF loc_0_41068DEF: ; CODE XREF: clicks+1B?j
CODE:41068DEF pop edi
CODE:41068DF0 pop esi
CODE:41068DF1 pop ebx
CODE:41068DF2 mov esp, ebp
CODE:41068DF4 pop ebp
CODE:41068DF5 retn
CODE:41068DF5 clicks endp
CODE:41068DF5
CODE:41068DF6
CODE:41068DF6 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
CODE:41068DF6
CODE:41068DF6
CODE:41068DF6 keys proc near
CODE:41068DF6 call clicks
CODE:41068DFB retn 4
CODE:41068DFB keys endp
CODE:41068DFB
CODE:41068DFB ; -----------------------------------------------------------------------
EPILOG
NOTES:ida not allow you to assemble functions calls with offsets , so
you must manually patch opcodes,write e8 and the offset of the function.
Using packages makes your program extremely easy to reverse engineer.
This is all. Look at those listings , compare them and it visible not so
hard to repeat all the story with another version of DBEXPLORER.
(c) Staier from Staier.cjb.net .1999
translated- march 2000