DLL demonstration

DLL demonstration

Joined: April 5th, 2005, 9:24 pm

August 5th, 2017, 1:01 am #1

This is an asm version of the demonstration here:
http://www.network54.com/Forum/613583/m ... 81+edit%29

The asm DLL should work with the MinGW C++ EXE, and the MinGW C++ DLL should work with the asm EXE. You can test this by copying them into a new folder. That is, the MinGW C++ and asm modules should be interchangeable because their binary interfaces should be identical. The MSVC ones might not be interchangeable because of the manifests.

Anyway, this asm code has no reliance at all on the CRT.

I needed to add a stub DllMain, because otherwise the Rnd function was getting called once during image load, making the first result 888e7a instead of b49ec3.

Also, it took me a long time to figure out that I needed to clear the direction flag before calling WriteFile. Otherwise, it fails to print to the console with ERROR_INVALID_HANDLE. But it would still print to a file using redirection.

The call [__imp__functionname] instead of call functionname avoids calling a jump stub.

The write to _crlf is misaligned.

file sizes:

3,584 testrnddll.dll
1,536 testrndexe.exe

the DLL has an .idata section that I don't think it needs.

Later, I'll post versions in which the caller owns the seed in its .bss section. (This will let us get rid of the .data section in the DLL.)

Regards,
Michael

-------- testrnddll.asm --------
global _Rnd

section .text                  ; read-only code

_DllMain@12:
mov eax,1
ret 0xc                        ; to prevent Rnd from being mysteriously called once during startup.

_Rnd:
mov eax,0xfd43fd
mul dword [_seed]
add eax,0xc39ec3
and eax,0xffffff
mov [_seed],eax
ret

section .data                  ; read/write initialized data

_seed: dd 0x50000
--------

-------- testrndexe.asm --------
extern __imp__GetStdHandle
extern __imp__WriteFile
extern __imp__Rnd

STD_OUTPUT_HANDLE equ -11

section .text                  ; read-only code

_main:

mov word [_crlf],0xa0d

push STD_OUTPUT_HANDLE         ; nStdHandle
call [__imp__GetStdHandle]
mov [_stdout],eax

mov byte [_i],8
.outerloop:                    ; outer loop iterates 8 times, calling Rnd and printing result

call [__imp__Rnd]
mov edx,eax
mov edi,_crlf-1
std

.innerloop:                    ; inner loop converts result to hex numerals

mov al,dl
and al,0xf
cmp al,9
jbe .skip
add al,'a'-('9'+1)
.skip:
add al,'0'
stosb

shr edx,4
jnz .innerloop

cld                            ; otherwise WriteFile to console fails
push 0                         ; lpOverlapped
push _trash                    ; lpNumberOfBytesWritten
inc edi
mov eax,_endbuffer
sub eax,edi
push eax                       ; nNumberOfBytesToWrite
push edi                       ; lpBuffer
push dword [_stdout]           ; hFile
call [__imp__WriteFile]

dec byte [_i]
jnz .outerloop

xor eax,eax
ret                            ; to operating system, exiting process

section .bss                   ; read/write uninitiallized data

_stdout: resd 1
_trash: resd 1
_i: resb 1
_buffer: resb 8
_crlf: resb 2
_endbuffer:
--------

-------- mtestrnd.bat --------
nasm -f win32 -o testrnddll.o testrnddll.asm
\qb64\internal\c\c_compiler\bin\ld -s -shared --enable-auto-image-base -dy --nxcompat -o testrnddll.dll testrnddll.o
nasm -f win32 -o testrndexe.o testrndexe.asm
\qb64\internal\c\c_compiler\bin\ld -s -dy --nxcompat -o testrndexe.exe testrndexe.o testrnddll.dll %windir%\system32\kernel32.dll
--------

edit: 2017 aug 5
added "mov eax,1" to dllmain, and "xor eax,eax" to end of exe.
dllmain needs to return true. i guess eax just happened to be true, otherwise it should have failed to load.
Last edited by MCalkins on August 5th, 2017, 6:31 pm, edited 1 time in total.
Quote
Like
Share

Joined: April 5th, 2005, 9:24 pm

August 5th, 2017, 6:47 pm #2

Edited the previous posts to add return values to DllMain and mainCRTStartup.

I rearranged the variables in the EXE's .bss section.

In this version, the caller gives a pointer to the seed. The seed is in the EXE's .bss section.

This would allow the EXE to maintain multiple separate seeds/states if it wanted.

This lets us eliminate the .data section from the DLL. This reduces the DLL by 4KB of memory and 1KB of disk space (512B directly, and another 512B presumably by shortening the section list?). The DLL needs a .text section and .edata section. I don't think it should need an .idata section. I don't know about the .reloc section.

It makes the call to Rnd a little slower.

cdecl would be more efficient, because we could leave the pointer to the seed on the stack, and call Rnd repeatedly, rather than having to push it every time. (In C++ terms, we would say in the function declaration that the pointer is constant, so the caller knows the function isn't changing the pointer on the stack.) DLLs could of course use cdecl, but DLLs are generally stdcall. (msvcrt.dll is mostly cdecl, but most other DLLs are mostly stdcall.)

Obviously, these modules (with 1 in the filenames) are incompatible with the earlier ones (without 1 in the filenames). But they should be compatible with the C++ versions (with 1 in the filenames) which I will post later.

File sizes:

2,560 testrnddll1.dll
1,536 testrndexe1.exe

The DLL should be smaller, without the .idata and maybe without the .reloc.

Regards,
Michael

-------- testrnddll1.asm --------
global _Rnd

section .text                  ; read-only code

_DllMain@12:                   ; int __stdcall DllMain(void *, unsigned int, void *)
mov eax,1
ret 0xc

_Rnd:                          ; extern "C" __declspec(dllexport) unsigned int __stdcall Rnd(unsigned int *);
mov ecx,[esp+4]
mov eax,0xfd43fd
mul dword [ecx]
add eax,0xc39ec3
and eax,0xffffff
mov [ecx],eax
ret 4
--------

-------- testrndexe1.asm --------
extern __imp__GetStdHandle
extern __imp__WriteFile
extern __imp__Rnd

STD_OUTPUT_HANDLE equ -11

section .text                  ; read-only code

_main:                         ; int mainCRTStartup(void)

mov dword [_seed],0x50000
mov word [_crlf],0xa0d

push STD_OUTPUT_HANDLE         ; nStdHandle
call [__imp__GetStdHandle]
mov [_stdout],eax

mov byte [_i],8
.outerloop:                    ; outer loop iterates 8 times, calling Rnd and printing result

push _seed
call [__imp__Rnd]
mov edx,eax
mov edi,_crlf-1
std

.innerloop:                    ; inner loop converts result to hex numerals

mov al,dl
and al,0xf
cmp al,9
jbe .skip
add al,'a'-('9'+1)
.skip:
add al,'0'
stosb

shr edx,4
jnz .innerloop

cld                            ; otherwise WriteFile to console fails
push 0                         ; lpOverlapped
push _trash                    ; lpNumberOfBytesWritten
inc edi
mov eax,_endbuffer
sub eax,edi
push eax                       ; nNumberOfBytesToWrite
push edi                       ; lpBuffer
push dword [_stdout]           ; hFile
call [__imp__WriteFile]

dec byte [_i]
jnz .outerloop

xor eax,eax
ret                            ; from mainCRTStartup to operating system, exiting process.

section .bss                   ; read/write uninitiallized data

_buffer: resb 8
_crlf: resb 2
_endbuffer:
_i: resb 1
resb 1                         ; padding to align 4
_seed: resd 1
_stdout: resd 1
_trash: resd 1
--------

-------- mtestrnd1.bat --------
nasm -f win32 -o testrnddll1.o testrnddll1.asm
\qb64\internal\c\c_compiler\bin\ld -s -shared --enable-auto-image-base -dy --nxcompat -o testrnddll1.dll testrnddll1.o
nasm -f win32 -o testrndexe1.o testrndexe1.asm
\qb64\internal\c\c_compiler\bin\ld -s -dy --nxcompat -o testrndexe1.exe testrndexe1.o testrnddll1.dll %windir%\system32\kernel32.dll
--------
Last edited by MCalkins on August 5th, 2017, 6:52 pm, edited 1 time in total.
Quote
Like
Share

Joined: April 5th, 2005, 9:24 pm

September 17th, 2017, 3:40 am #3

---- testrnddll2.asm ----
global _rnd

section .text                  ; read-only code

_DllMain@12:                   ; int __stdcall DllMain(void *, unsigned int, void *)
mov eax,1
ret 0xc

_rnd:                          ; extern "C" __declspec(dllexport) unsigned int rnd(unsigned int * const);
mov ecx,[esp+4]
mov eax,0xfd43fd
mul dword [ecx]
add eax,0xc39ec3
and eax,0xffffff
mov [ecx],eax
ret
----

---- testrndexe2.asm ----
extern __imp__GetStdHandle
extern __imp__WriteFile
extern __imp__rnd

STD_OUTPUT_HANDLE equ -11

section .text                  ; read-only code

_main:                         ; int mainCRTStartup(void)

mov dword [_seed],0x50000
mov word [_crlf],0xa0d
push _seed

push STD_OUTPUT_HANDLE         ; nStdHandle
call [__imp__GetStdHandle]
mov [_stdout],eax

mov byte [_i],8
.outerloop:                    ; outer loop iterates 8 times, calling rnd and printing result

call [__imp__rnd]              ; rnd leaves the constant pointer to seed on the stack
mov edx,eax
mov edi,_crlf-1
std

.innerloop:                    ; inner loop converts result to hex numerals

mov al,dl
and al,0xf
cmp al,9
jbe .skip
add al,'a'-('9'+1)
.skip:
add al,'0'
stosb

shr edx,4
jnz .innerloop

cld                            ; otherwise WriteFile to console fails
push 0                         ; lpOverlapped
push _trash                    ; lpNumberOfBytesWritten
inc edi
mov eax,_endbuffer
sub eax,edi
push eax                       ; nNumberOfBytesToWrite
push edi                       ; lpBuffer
push dword [_stdout]           ; hFile
call [__imp__WriteFile]

dec byte [_i]
jnz .outerloop

pop eax                        ; remove rnd's parameter
xor eax,eax
ret                            ; from mainCRTStartup to operating system, exiting process.

section .bss                   ; read/write uninitiallized data

_buffer: resb 8
_crlf: resb 2
_endbuffer:
_i: resb 1
resb 1                         ; padding to align 4
_seed: resd 1
_stdout: resd 1
_trash: resd 1
----

---- mtestrndasm2.bat ----
nasm -f win32 -o testrnddll2.o testrnddll2.asm
\qb64\internal\c\c_compiler\bin\ld -s -shared --enable-auto-image-base -dy --nxcompat -o testrnddll2.dll testrnddll2.o
nasm -f win32 -o testrndexe2.o testrndexe2.asm
\qb64\internal\c\c_compiler\bin\ld -s -dy --nxcompat -o testrndexe2.exe testrndexe2.o testrnddll2.dll %windir%\system32\kernel32.dll
----

2,560 testrnddll2.dll
1,536 testrndexe2.exe

In these "2" marked files, the caller EXE owns the seed, and the DLL's cdecl function leaves the constant pointer on the stack. The EXE knows that the DLL has not modified it, so just leaves it alone through the multiple function calls. It is removed at the end, in preparation to return to the operating system. (To remove a bunch of parameters, you could add to ESP. But to remove 1, I used a pop.) The function's external name is "rnd", now lowercase because it's cdecl.

The "2" marked EXEs and DLLs from the asm and mingw versions should be interchangable with each other.

Regards,
Michael
Quote
Like
Share