home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Reverse Code Engineering RCE CD +sandman 2000
/
ReverseCodeEngineeringRceCdsandman2000.iso
/
RCE
/
LordLucifer
/
win32asm
/
tutorials
/
com_1.txt
< prev
next >
Wrap
Text File
|
2000-05-25
|
13KB
|
389 lines
COM in ASM
------------------------------------------------------------------------------
This article will discuss how to use COM interfaces in your assembly language
programs. It will not discuss what COM is and how it is used, but rather how
it can be used when programming in assembler. It will discuss only how to
use existing interfaces, and not how to actually implement new ones; this will
be shown in a future atricle.
About COM
------------------------------------------------------------------------------
Here is a brief introduction to the basics behind COM.
A COM object is one in which access to an object's data is achieved
exclusively through one or more sets of related functions. These function
sets are called interfaces, and the functions of an interface are called
methods. COM requires that the only way to gain access to the methods of an
interface is through a pointer to the interface.
An interface is actually a contract that consists of a group of related
function prototypes whose usage is defined but whose implementation
is not. An interface definition specifies the interface's member functions,
called methods, their return types, the number and types of their parameters,
and what they must do. There is no implementation associated with an
interface. An interface implementation is the code a programmer supplies to
carry out the actions specified in an interface definition.
An instance of an interface implementation is actually a pointer to an array
of pointers to methods (a function table that refers to an implementation of
all of the methods specified in the interface). Any code that has a pointer
through which it can access the array can call the methods in that interface.
Using a COM object assembly language
-------------------------------------------------------------------------------
Access to a COM object occurs through a pointer. This pointer points to a
table of function pointers in memory, called a virtual function table, or
vtable in short. This vtable contains the addresses of each of the objects
methods. To call a method, you indirectly call it through this pointer table.
Here is an example of a C++ interface, and how its methods are called:
interface IInterface
{
HRESULT QueryInterface( REFIID iid, void ** ppvObject );
ULONG AddRef();
ULONG Release();
Function1( INT param1, INT param2);
Function2( INT param1 );
}
// calling the Function1 method
pObject->Function1( 0, 0);
Now here is how the same functionality can be implemented using assembly
language:
; defining the interface
; each of these values are offsets in the vtable
QueryInterface equ 0h
AddRef equ 4h
Release equ 8h
Function1 equ 0Ch
Function2 equ 10h
; calling the Function1 method in asm
; the method is called by obtaining the address of the objects
; vtable and then calling the function addressed by the proper
; offset in the table
push param2
push param1
mov eax, pObject
push eax
mov eax, [eax]
call [eax + Function1]
You can see this is somewhat different than calling a function normally.
Here, pObject points to the Interface's vTable. At the Function1(0Ch) offset
in this table is a pointer to the actual function we wish to call.
Using HRESULT's
-------------------------------------------------------------------------------
The return value of OLE APIs and methods is an HRESULT. This is not a handle
to anything, but is merely a 32-bit value with several fields encoded in the
value. The parts of an HRESULT are shown below.
HRESULTs are 32 bit values layed out as follows:
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+-+-+-+-+-+---------------------+-------------------------------+
|S|R|C|N|r| Facility | Code |
+-+-+-+-+-+---------------------+-------------------------------+
S - Severity Bit
Used to indicate success or failure
0 - Success
1 - Fail
By noting that this bit is actually the sign bit of the 32-bit value,
checking success/failure is simply performed by checking its sign:
call ComFunction ; call the function
test eax,eax ; now check its return value
js error ; jump if signed (meaning error returned)
; success, so continue
R - reserved portion of the facility code, corresponds to NT's
second severity bit.
C - reserved portion of the facility code, corresponds to NT's
C field.
N - reserved portion of the facility code. Used to indicate a
mapped NT status value.
r - reserved portion of the facility code. Reserved for internal
use. Used to indicate HRESULT values that are not status
values, but are instead message ids for display strings.
Facility - is the facility code
FACILITY_WINDOWS = 8
FACILITY_STORAGE = 3
FACILITY_RPC = 1
FACILITY_WIN32 = 7
FACILITY_CONTROL = 10
FACILITY_NULL = 0
FACILITY_ITF = 4
FACILITY_DISPATCH = 2
To retreive the Facility,
call ComFunction ; call the function
shr eax, 16 ; shift the HRESULT to the right by 16 bits
and eax, 1FFFh ; mask the bits, so only the facility remains
; eax now contains the HRESULT's Facility code
Code - is the facility's status code
To get the Facility's status code,
call ComFunction ; call the function
and eax, 0000FFFFh ; mask out the upper 16 bits
; eax now contains the HRESULT's Facility's status code
Using COM with MASM
------------------------------------------------------------------------------
If you use MASM to assemble your programs, you can use some of its
capabilities to make calling COM functions very easy. Using invoke, you can
make COM calls look almost as clean as regular calls, plus you can add type
checking to each function.
Defining the interface:
IInterface_Function1Proto typedef proto :DWORD
IInterface_Function2Proto typedef proto :DWORD, :DWORD
IInterface_Function1 typedef ptr IInterface_Function1Proto
IInterface_Function2 typedef ptr IInterface_Function2Proto
IInterface struct DWORD
QueryInterface IUnknown_QueryInterface ?
AddRef IUnknown_AddRef ?
Release IUnknown_Release ?
Function1 IInterface_Function1 ?
Function2 Interface_Function2 ?
IInterface ends
Using the interface to call COM functions:
mov eax, pObject
mov eax, [eax]
invoke (IInterface [eax]).Function1, 0, 0
As you can see, the syntax may seem a bit strange, but it allows for a simple
method using the function name itself instead of offsets, as well as type
checking.
A Sample program written using COM
------------------------------------------------------------------------------
Here is some sample source code which uses COM written in straight assembly
language, so it should be compatable with any assembler you prefer with only
minor changes necessary.
This program uses the Windows Shell Interfaces to show the contents of the
Desktop folder in a window. The program is not complete, but shows how the
COM library is initialized, de-initialized, and used. I also shows how the
shell library is used to get folders and obcets, and how to perform
actions on them.
.386
.model flat, stdcall
include windows.inc ; include the standard windows header
include shlobj.inc ; this include file contains the shell namespace
; definitions and constants
;----------------------------------------------------------
.data
wMsg MSG <?>
g_hInstance dd ?
g_pShellMalloc dd ?
pshf dd ? ; shell folder object
peidl dd ? ; enum id list object
lvi LV_ITEM <?>
iCount dd ?
strret STRRET <?>
shfi SHFILEINFO <?>
...
;----------------------------------------------------------
.code
; Entry Point
start:
push 0h
call GetModuleHandle
mov g_hInstance,eax
call InitCommonControls
; initialize the Component Object Model(COM) library
; this function must be called before any COM functions are called
push 0
call CoInitialize
test eax,eax ; error when the MSB = 1
; (MSB = the sign bit)
js exit ; js = jump if signed
; Get the Shells IMalloc object pointer, and save it to a global variable
push offset g_pShellMalloc
call SHGetMalloc
cmp eax, E_FAIL
jz shutdown
; here we would set up the windows, list view, message loop, and so on....
; we would also call the FillListView procedure...
; ....
; Cleanup
; Release IMalloc Object pointer
mov eax, g_pShellMalloc
push eax
mov eax, [eax]
call [eax + Release] ; g_pShellMalloc->Release();
shutdown:
; close the COM library
call CoUninitialize
exit:
push wMsg.wParam
call ExitProcess
; Program Terminates Here
;----------------------------------------------------------
FillListView proc
; get the desktop shell folder, saved to pshf
push offset pshf
call SHGetDesktopFolder
; get the objects of the desktop folder using the EnumObjects method of
; the desktop's shell folder object
push offset peidl
push SHCONTF_NONFOLDERS
push 0
mov eax, pshf
push eax
mov eax, [eax]
call [eax + EnumObjects]
xor ebx, ebx ;use ebx for the counter
; now loop through the enum id list
idlist_loop:
; Get next id list item
push 0
push offset pidl
push 1
mov eax, peidl
push eax
mov eax, [eax]
call [eax + Next]
test eax,eax
jnz idlist_endloop
mov lvi.imask, LVIF_TEXT or LVIF_IMAGE
mov lvi.iItem, ebx
; Get the item's name by using the GetDisplayNameOf method
push offset strret
push SHGDN_NORMAL
push offset pidl
mov eax, pshf
push eax
mov eax, [eax]
call [eax + GetDisplayNameOf]
; GetDisplayNameOf returns the name in 1 of 3 forms, so get the correct
; form and act accordingly
cmp strret.uType, STRRET_CSTR
je strret_cstr
cmp strret.uType, STRRET_OFFSET
je strret_offset
strret_olestr:
; here you could use WideCharToMultiByte to get the string,
; I have left it out because I am lazy
jmp strret_end
strret_cstr:
lea eax, strret.cStr
jmp strret_end
strret_offset:
mov eax, pidl
add eax, strret.uOffset
strret_end:
mov lvi.pszText, eax
; Get the items icon
push SHGFI_PIDL or SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_ICON
push sizeof SHFILEINFO
push offset shfi
push 0
push pidl
call SHGetFileInfo
mov eax, shfi.iIcon
mov lvi.iImage, eax
; now add item to the list
push offset lvi
push 0
push LVM_INSERTITEM
push hWndListView
call SendMessage
; increment counter ebx and repeat the loop
inc ebx, ebx
jmp idlist_loop
idlist_endloop:
; now free the enum id list
; Remember all allocated objects must be released...
mov eax, peidl
push eax
mov eax,[eax]
call [eax + Release]
; free the desktop shell folder object
mov eax, pshf
push eax
mov eax,[eax]
call [eax + Release]
ret
FillListView endp
END start
Conclusiom
-------------------------------------------------------------------------------
Well, that is about it for using COM with assembly language. Hopefully, my
next article will go into how to define your own interfaces. As you can
see, using COM is not difficult at all, and with it you can add a very
powerful capability to your assembly language programs.