How toCreate your own MASM Import Libraries
This short essay is about the mechanics of creating
import libraries for use with MASM. I assume you already know something
about import libraries, ie. you know what an import library is and so on.
I will focus on the technique used to generate your own custom MASM import
libraries.
MASM Import Library Format
MASM and Visual C++ can use the same import libraries
which is very handy. Microsoft import libraries use a variation of COFF
file format which is different from the OMF format used by TASM. That is
the reason TASM cannot use MASM's import libs and vice versa. I won't go
into detail about the details of Microsoft import library format. Suffice
to say that every Microsoft import lib contains information about functions
in some DLLs. That information includes the function names and the overall
size of parameters passed to functions. If you examine kernel32.lib with
a hex editor, you will find entries with this format:
_ExitProcess@4
_CreateProcessA@40
The function names are "decorated" with a leading underscore.
The number following @ is the overall size of parameters of that function,
in bytes. ExitProcess takes only one dword parameter, so that number is
4.
Why includes the information about the size of
parameters? That information is used by MASM to check if the correct number
and size of parameters are passed to a function. You get this feature when
you call that function with invoke keyword. If you just push parameters
on the stack and execute the function with call, you won't get this
MASM check.
It's this extra boon which makes it nearly impossible
to create MASM import libs directly from DLLs because DLLs don't contain
explicit information about the size of parameters passed to their own functions.
Creating MASM Import Libs from DLLs
If you are willing to push parameters on the stack
and execute functions with call, you can create import lib from
any DLL for use with MASM like this:
-
use dumpbin.exe which comes with Visual C++ to dump
the names of the export functions in the DLL.
Dumpbin /EXPORTS blah.dll > output.txt
-
After you got the list of function names, create a
module definition file (.def) from them. For example, if the function contains
only one function, GetSomeLine, type the following lines into a text file:
LIBRARY blah
EXPORTS
GetSomeLine
Save it as blah.def
-
Run lib.exe to create the import lib from the module
definition file, like this:
lib /DEF:blah.def
That's it. You'll get blah.lib which you can use with
MASM so long as you don't use invoke with the functions in the import
lib.
Creating MASM Import Libs for Use with invoke
I, for one, am reluctant to use the above approach.
invoke
is a nice function call wrapper. It's one of the reasons I prefer MASM
to TASM. But as I pointed out earlier, it's next to impossible to create
a 100% working MASM import lib from any DLL. You can't use the above method
to create an MASM import lib which can be used with invoke. For
example, you may think that if you modify the function names in .def file
to include "@xx", the import lib should be ok. Trust me. It won't work.
The easy way to create an "invokable" import lib
is to use MASM itself. If you have coded a DLL, you will observe that you
also get an import lib for that DLL as well. And that import lib is fully
invokable! Our strategy is as follow:
-
Obtain the function names and the overall sizes of
parameters
-
Create a DLL source code that includes all those functions
with the correct number and size of arguments.
-
Create a module definition file that describes the
corresponding functions in the asm source code.
-
Assemble the asm source code as a DLL project.
That's it. You'll obtain a fully functional MASM import
lib. The above steps deserve more explanation
Obtain the function names and overall sizes of parameters
This is the most difficult part of the process. If
you have only the DLL, you're in for a tiresome adventure. Below are the
methods I can think of, none of them works 100%.
-
Use Interactive Disassembler (IDA) to disassemble the
DLL. With this wonderful tool, you can obtain the rough size of parameters
expected by the functions. The information is not perfect, however. IDA
is an awesome disassembler but sometimes, only humans can decide which
is which. You will have to peruse and check the whole disassembling listing.
-
Observe the value of the stack pointer before and after
calls to all the functions in the DLL. The method is:
-
Obtain the address of the functions with GetProcAddress
-
Call each function without passing any parameter on
the stack. Note the value of esp before the call.
-
When the function returns, compare the value of esp
after the call with the one before the call. The rationale is: with stdcall
parameter passing convention, the function is the one who takes care of
the stack balancing. The difference of the esp values is the size of parameters
expected by that function.
Alas, this method is not bullet-proof. It can fail
under these circumstances:
-
If the functions in the DLL use other parameter passing
convention other than stdcall or pascal.
-
If the functions fail to clean up the stack, such as
when exceptions occur.
-
If the functions are designed to do something dangerous
such as formatting the hard disk (God forbids!)
-
Study the existing programs that use the DLL. You can
debug/disassemble those programs to see the number and size of parameters
they pass to functions in the DLL. However, if there are some functions
in the DLL which are never used in any program, you're left with the above
two methods.
Create a DLL asm source code which contains all those
functions
After you obtain the function names and their parameter
sizes, the rest is easy. You just create a DLL asm skeleton and then create
functions with the same names as those in the DLL in the file. For example,
if the DLL has only one function, GetSomeLine, which takes 16 bytes of
parameters. In the asm file, you type the following lines:
.386
.model flat,stdcall
.code
GetSomeLine proc param1:DWORD,
param2:DWORD, param3:DWORD, param4:DWORD
GetSomeline endp
end
What's this? You may ask. A procedure with no instruction
inside? An import lib doesn't contain any info on what the functions are
supposed to do. Its sole purpose is to provide information about the function
names and their parameters. So we don't need to put any instruction into
the dummy procedure. We will discard the useless DLL generated by the assembling
process anyway. All we want is to put the info about the function names
and the size of parameters into the asm source code so that MASM can generate
the working import lib. The size of each parameter is NOT essential. For
your information, currently MASM always regards each parameter as a DWORD
no matter which size specifier you use. For example, we can use:
.386
.model flat,stdcall
.code
GetSomeLine proc param1:BYTE,
param2:BYTE, param3:BYTE, param4:BYTE
GetSomeline endp
end
And MASM will happily create _GetSomeLine@16 entry
in the import lib.
Create a matching module definition file
This is a simple process. You need this file so that
MASM can generate the DLL and import lib. A module defintion file template
is like this:
LIBRARY <The
name of the DLL>
EXPORTS
<The names of the functions>
You just fill the name of the DLL which will be used
as the name of the import lib as well. And then put the list of the function
names below the EXPORTS line, each name on its own line. Save the file
and you got a working module defintion file.
Assemble the asm source code as a DLL project
The last step is the simplest one. Just use ml.exe
and link.exe.
ml /c /coff /Cp blah.asm
link /DLL /NOENTRY /def:blah.def /subsystem:windows
blah.obj
And you'll get an invokable import lib.
An Example
The dry explanation above may not be clear enough.
I'm a firm believer in learning by doing. So I provide an
example that demonstrates the above process. The example files are:
-
The asm source code that contains all functions in
kernel32.dll (the documented ones anyway)
-
The matching module definition file
-
A batch file which you can use to build the import
lib
Assembling the example will give you a kernel32.lib
which you can use in place of the one provided by Microsoft.
More Tools
If you want to add/remove functions to/from a certain import lib, you can
use two simpleminded tools I coded. For example, if you want to add undocumented
functions to kernel32.lib, you'll find these tools useful.
Lib2Def
It extracts function names and the corresponding sizes of parameters from
any MASM/VC++ import lib. Just run it and it will process ALL import libraries
that are in the same folder. The output files have .icz extensions. The
content looks like this:
_ExitProcess@4
_ExitThread@4
_ExitVDM@8
_ExpandEnvironmentStringsA@12
_ExpandEnvironmentStringsW@12 @12
If you want to add a function, you just insert a new line and put a leading
underscore, followed by the function name and the combined size of its
parameters. If the function is exported by ordinal, there will be another
@xx following the name. The "xx" being the ordinal number. Note that this
simple tool doesn't check for duplicate function names. And there can be
duplicate names in some import libs.
MLib
This is the tool that accepts the output files of Lib2Def and creates import
libs from them. It will process ALL files with .icz extension. For your
information, it parses the lines in .icz files and creates .asm and .def
from them. Then it calls ml.exe and link.exe to generate the import lib.
The .obj, .asm, .exp and .dll are deleted afterwards so only .lib is left.
If this tool fails to generate .lib file, please check if there are some
duplicate function names in the .icz file: it's the most common cause of
failure.
Download these two tools
[Iczelion's Win32 Assembly
Homepage]