Dll in the machine codes
・ version for the press
In the article "appendix Windows by naked hands" was shown as with the aid of debug "by hand" gather simple Win32 exe- application with MessageBox'om. This time it is proposed analogously to create the simplest dll; this subject continuation of past article and at the same time necessary foundation for the articles of futures - since I am assembled to tell in them about the creation in the machine codes of components COM, and for them to manage without dll well is in any way cannot.
I assume that the reader attentively studied past material and he knows how now to summer to create PE- titles, tables of import and section of code and data OF J. It is assumed also that it is pig because of the creation and followed after this the general infection of enthusiasm by manualami from Intel no one it will comprise the special labor to be dismantled at hex'ax or at least even in the binary codes. Therefore entire attention let us concentrate on the more useful things - on however, what differs dll from usual exe- files. But differences in chapters two: appear the exported functions and, therefore, the table of export, and also, however it is lamentable, it is necessary for us to be investigated with tuning (relocations), since the load of our dll with our dear base address 10000000h is not completely guaranteed BY L.
This time for the variety let us make in our PE- file 4 sections - for the code, data, import with the export also of tuning. Let us appear also, in contrast to the past time, respect and will give to them the names: .code.data.rdata and.reloc respectively. They will be arranged in the memory on displacement 1000h, 2000h, 3000h, 4000h, and in file 200h, 400h, 600h and 800h respectively. Content itself will be the same, t.e. we export only the one function, whose entire work will consist in mapping MessageBox'a. In the data only two lines; we create file data.txt:
n data.bin
R of the cx
200
f 0 l 200 0
e 0 "Dll"
e 10 The "eksportirovannaya function"
m 0 l 200 100
w
q
I hope, it is understandable that in the file are recorded the commands for debug. This time we decided to somewhat automate process of J.
Thus, line with the title is located in our of the section of data along displacement 2000h, and the "dear" address in the memory will be respectively 10002000h. For the line with the communication of number there will be 2010h and 10002010h respectively.
But now - the code! It is necessary again to import MessageBoxA from User32.dll; this time IAT it will be arranged in its own section -.rdata - as usual, at the very beginning, t.e. on displacement 3000h; and the "dear" address will be 10003000h. There are no other imports. We fill the file code.txt:
n code.bin
R of the cx
200
f 0 l 200 0
a 0
; parameters MessageBox'.a
db 6a 0
db 68 0 20 0 10
db 68 10 20 0 10
db 6a 0
; call MessageBox
db ff 15 0 30 0 10
; the recovery
db c3
m 0 l 200 100
w
q
Am empty line cannot be removed! But that debug will be scolded, and you will obtain fuflo instead of the class binary block J.
However, as we - did not pass five minutes, but already polfayla comprised! On to nose new material; however. Let us look to the code and it is isolated the "boggy" places:
1000: 6a 00 68 00 | 20 00 10 68 | 10 20 00 10 | 6a 00 ff 15
1010: 00 30 00 10 | c3
I isolated to fatty the "dear" addresses, which fell into the composition of instructions. Mean system can throw far away our dll somewhere entirely into another place - here at that time our addresses will be covered, and the code will begin to push in any rubbish into the stack and will in addition send all this instead of our imported function with heaven knows what address. Specifically, these three 32-bit values must be disposed; and for this they must be indicated in the table of tuning.
Each tuning is represented altogether only by two bytes - 16-bit value, moreover 4 elder bits designate the type of tuning. For Win32 this practically always value of 3, which means that it is necessary "to repair" 32-bit address on the displacement indicated. But displacement they indicate the remained 12 bits. But, as you understand, this is sufficient only to the displacement in the limits of one page - 4 KB. So it there is - tuning are grouped into the blocks; for each tuned page there is its block, and in the beginning of the block of the first 4 bytes they contain the displacement of this page relative to the base address of load, and the following 4 bytes - size of block (together with the first 8 bytes). Remaining contents of block - the collection of tuning for this page (see fig.)
With load dll the system calculates the so-called delta - difference between the base address of load, indicated in PE- title, and the address, with which is actually loaded dll. Naturally, if dll is loaded according to its "dear" base, delta is equal to 0 and no tuning it is required. But if this not then, delta is added to each 32-bit value, for which there is a tuning. Only and delov.
In our case must be of 3 tuning; their displacement relative to the beginning of page - 3, 8 and 10h. Taking into account the type (3) we obtain numbers 3003h, 3008h and 3010h. Block must be vyrovnen on the 32-bit boundary; therefore into the end let us add filler from the "empty" tuning (for it 0 is their even type - naturally). As a result we obtain: RVA page - these are displacement by that tuned, t.e. code page - 1000h; the size of block - 8 bytes + 3 tuning on 2 bytes + 1 "empty" tuning (2 bytes) - in all 10h.
The section of tuning is ready! We collect file reloc.txt:
n reloc.bin
R of the cx
200
f 0 l 200 0
a 0
; RVA page
dw 1000 0
; the size of the block
dw 10 0
; the tuning
dw 3003
dw 3008
dw 3010
m 0 l 200 100
w
q
Export remains - since article read the specialists in the import, about it more not word. The main table, which unites everything else - the table of export - takes the following form:
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; //RVA from base of image
DWORD AddressOfNames; //RVA from base of image
DWORD AddressOfNameOrdinals; //RVA from base of image
The interrelations of different tables are shown in the following figure:
As is evident according to figure, given for the export are the predominantly different kind of displacement and indices. With the export on the ordinals are used only two tables: the basic table of export and the table of the exported addresses. The latter is simply the massif of the 32-bit displacement of the realizations of the corresponding functions relative to the base address of load. T.e. each eksportirovannoy function is had its index in this massif; after adding to this index t.n. base, we obtain the ordinal of this function.
If functions are exported on the names, the three additional tables are added: ordinals, the indicators of names and names themselves. Everything is understandable with the table of names: it contains the exported names. Moreover them it can be less than the exported addresses. "jointing" of names with the addresses of the corresponding functions achieves tables of ordinals and indicators of names; actually, this two tightly cmbined with each other of massif. Joining is accomplished due to the fact that data for one function are located at one and the same place (with the identical index) in both massifs: in the first massif - index for the table of the exported addresses, in the second - address of line with the name of the corresponding function. Moreover functions in these two massifs are located so that their names are regulated alphabetically (in the ascending order of their indices in these massifs). Note: not names themselves (it is possible even to scatter them throughout entire section), must be regulated; indicators on them in another table must be regulated. One should also remember that the indices in the table of ordinals are 16-bit, and the indicators of names - 32-bit.
It is time to undertake last section - '.rdata '. Let us first estimate "mock-up" (see fig.)
Now file rdata.txt:
n rdata.bin
R of the cx
200
f 0 l 200 0
a 0
; Import
; IAT
dw 3020 0 0 0
; Table of the search
dw 3020 0 0 0
; Name of the imported module
db "User32.dll "0
a 20
; Imported function with hint'om
db 0 0 "MessageBoxA" 0
a 30
; Table of the import:
; the displacement of the table of the search
dw 3008 0
; 2 empty fields
dw 0 0 0 0
; the displacement of name dll
dw 3010 0
; displacement IAT
dw 3000 0
a 60
; Export
; Table of the export:
; 3 empty fields
dw 0 0 0 0 0 0
; the displacement of name dll
dw 3094 0
; the base of the ordinals
dw 1 0
; the number of addresses
dw 1 0
; the number of names
dw 1 0
; the displacement of the address
dw 3088 0
; the displacement of the indicator of the name
dw 3090 0
; the displacement of the ordinal
Dw 308C 0
; (table) the addresses
dw 1000 0
; (table) the ordinals
dw 0 0
; (table) the indicators of the names
dw 30a0 0
; Name dll
db "Dll.dll "0
a a0
; Exported function
db "Function1" 0
m 0 l 200 100
w
q
You remember, empty lines cannot be touched! It is now necessary only slightly to touch up PE- title - file header.txt:
N Header.bin
R of the cx
200
f 0 l 200 0
e 0 "MZ '
E 3C 40
e 40 "PE '
e 44 4C 01
a 46
; Number of sections
db 04
a 54
; Size of the additional title
db e0 00
; Type of the file: to establish flag dll and flag, which resolves
; to load means with the base address, different from
; indicated in PE- title
Db 0E 21
; the "magic" value
Db 0B 01
a 74
; Base address of the load
db 00 00 00 10
; Levelling off of the sections
db 00 10 00 00
; Levelling off in the file
db 00 02 00 00
; Elder version Windows
db 04
a 88
; Elder version of the subsystem
db 04
a 90
; Size of the loaded file in the memory
db 00 50 00 00
; Size of all titles in the file
db 00 02
A 9C
; Subsystem
db 02 00
A A0
; Reserved size of the stack
db 00 00 10 00
; Chosen size of the stack
db 00 10 00 00
; Reserved size of the heap
db 00 00 10 00
; Chosen size of the heap
db 00 10 00 00
A B4
; Number of elements of the catalog of the displacement
db 10 00 00 00
;
; Catalog of the displacement:
; the displacement of the table of the export
dw 3060 0
; the size of data of the export
dw 4a 0
; the displacement of the table of the import
dw 3030 0
; the size of the table of the import
dw 28 0
; we pass 24 bytes (3 elements)
dw 0 0 0 0 0 0 0 0 0 0 0 0
; the displacement of the table of the tuning
dw 4000 0
; the size of the table of the tuning
dw 10
a 138
; Beginning of the table of the sections
;
; the name of the first section
db '.code ' 0 0 0
; the size of section in the memory
dw 200 0
; the shift of section relative to the address of the load
dw 1000 0
; the size of data of section in the file
dw 200 0
; the shift of the beginning of data of section in the file
dw 200 0
; We pass 12 bytes
dw 0 0 0 0 0 0
; the attributes of the first section
db 20 00 00 60
; the second section
db '.data ' 0 0 0
dw 200 0
dw 2000 0
dw 200 0
dw 400 0
dw 0 0 0 0 0 0
db 40 0 0 c0
; the third section
db '.rdata ' 0 0
dw 200 0
dw 3000 0
dw 200 0
dw 600 0
dw 0 0 0 0 0 0
db 40 0 0 40
; the fourth section
db '.reloc ' 0 0
dw 200 0
dw 4000 0
dw 200 0
dw 800 0
dw 0 0 0 0 0 0
db 40 0 0 42
m 0 l 200 100
w
q
Everything! We gather everything together in file make.bat:
@echo off
debug &.lt; header.txt &.gt; report.lst
debug &.lt; code.txt &.gt;&.gt; report.lst
debug &.lt; data.txt &.gt;&.gt; report.lst
debug &.lt; rdata.txt &.gt;&.gt; report.lst
debug &.lt; reloc.txt &.gt;&.gt; report.lst
copy /.b header.bin+.code.bin+.data.bin+.rdata.bin+.reloc.bin dll.dll
We start this file and - hurray! - we obtain our dll. It is without fail necessary to glance into the thoughtful created for you file of report - report.lst in order to nagging look there the errors, about which reports debug. Indeed you, of course, perfectly well know: method cut&.paste does not save from the quite debil'nykh errors!
Yes, this, of course, it is good; but indeed will be required even test exe- application, in order to verify the work of our dll? 4 not at all I doubt, that your level now will make it possible with the ease to independently create in debug this test programmku J.
It is agreeable, agreeably... I see the extended physiognomy of some. You renownedly took some pains themselves today (even if simply they read this to the end), and as bonusa I decided to grant "lazy" test application on MASM'e 32- ohm. Here is it:
.386
.model flat.stdchall
option casemap:none
include \.masme2\.inchlude\.shindoshs.inc
include \.masme2\.inchlude\.kernele2.inc
include \.masme2\.inchlude\.usere2.inc
includelib \.masme2\.lib\.kernele2.lib
includelib \.masme2\.lib\.usere2.lib
.data
hM1 dword 0
hM2 dword 0
app db "Test dll", 0
dll db "dll.dll", 0
dll2 db "dll2.dll", 0
fname db "Function1", 0
err1 db "LoadLibrary (dll1) failed", 0
err1_1 db "LoadLibrary (dll2) failed", 0
err2 db "GetProcAddress (first) failed", 0
err2_1 db "GetProcAddress (second) failed", 0
.code
start:
Invoke LoadLibrary.offset dll
.if eax==0
Invoke MessageBox, 0.offset err1.offset app.MB_.IccONERROR
ret
.endif
mov hM1.eakh
Invoke LoadLibrary.offset dll2
.if eax==0
Invoke MessageBox, 0.offset err1_1.offset app.MB_.IccONERROR
ret
.endif
mov hM2.eakh
Invoke GetProcAddress.yuMy.offset fname
.if eax==0
Invoke MessageBox, 0.offset err2.offset app.MB_.IccONERROR
ret
.endif
call eax
Invoke GetProcAddress.yuM2.offset fname
.if eax==0
Invoke MessageBox, 0.offset err2_1.offset app.MB_.IccONERROR
ret
.endif
call eax
Invoke FreeLibrary.yuMy
Invoke FreeLibrary.yuM2
ret
end start
It is necessary to copy that created by us dll.dll under the new name dll2.dll into the same catalog. Entire salt in the fact that we should verify that our tuning were correct, and system can with them work. But for this are required as the minimum two dll, which claim to one and the same place in the address space. The laziest method, of course, which only can be devised - this is simple to use the second renamed copy. But on happiness you can poeksperimentirovat' with the texts, at least to change concluded MessageBox'om communications and to create other dll with other names of module and exported function.