For the benefit of assembly language programmers working with file mapping (and also for the purpose of honing my personal skills), I converted my file mapping functions posted previously to assembly language, particularly in MASM.
I also converted (and managed to made few optimizations) the source code of Hex Dump utility to assembly language, this is by the way the example utility program I posted as an example for the file mapping functions.
The difference between what's compiled in C and what's assembled in MASM in term of executable size is quite big, DUMP.EXE in C is around the average of 26KB, while the DUMP.EXE assembled in MASM is just 3KB!
Anyway, most of the discussion regarding the file mapping functions and the HexDump utility has been covered in my previous post - so there's no much point in keeping you with lengthy explanation.
Here's the converted source code in MASM, and enjoy:
; --------------------- filemap.inc ---------------------
ifndef __FILEMAPPING__
__FILEMAPPING__ equ 1
; Prototypes
OpenAndMapFile proto :LPCSTR, :PHANDLE, :PHANDLE, :PHANDLE
CloseAndUnmapFile proto :PHANDLE, :PHANDLE, :PHANDLE
; Maximum length of filename
MAX_FNAME_LEN equ 256
; Constants
SUCCESS equ 0
ERROR_OPENFILE_FAILED equ 1
ERROR_CREATEMAP_FAILED equ 2
ERROR_MAPVIEW_FAILED equ 3
; OpenAndMapFile function settings
fm_desired_access equ GENERIC_READ
fm_share_mode equ FILE_SHARE_READ
fm_file_attributes equ FILE_ATTRIBUTE_NORMAL or \
FILE_ATTRIBUTE_ARCHIVE
fm_page_access equ PAGE_READONLY
fm_map_desired_access equ FILE_MAP_READ
;
; This function will try to open and memory map a specified file,
; and return all the handles for the opened file, created mapping
; object and the base of the memory mapped file. This function returns
; any of the status defined above for errors or success; Also, this
; function handles the closing of the handles for any errors that may
; happen during the process of memory mapping of the file.
;
.code
OpenAndMapFile proc lpszFilename:LPCSTR, \
_hFile:PHANDLE, \
_hFileMap:PHANDLE, \
_hFileBase:PHANDLE
local dwFileSize:DWORD
; Open the specified file
invoke CreateFile, \
lpszFilename, \
fm_desired_access, \
fm_share_mode, \
NULL, \
OPEN_EXISTING, \
fm_file_attributes, \
NULL
mov edx, _hFile
mov [edx], eax
.if eax != INVALID_HANDLE_VALUE
; Create FileMapping object for the file
push edx
invoke GetFileSize, [edx], NULL
mov dwFileSize, eax
pop edx
invoke CreateFileMapping, \
[edx], \
NULL, \
fm_page_access, \
0, \
dwFileSize, \
NULL
mov edx, _hFileMap
mov [edx], eax
.if eax != INVALID_HANDLE_VALUE
; Map the file to the process memory
invoke MapViewOfFile, \
[edx], \
fm_map_desired_access, \
0, \
0, \
0
mov edx, _hFileBase
mov [edx], eax
.if eax != INVALID_HANDLE_VALUE
mov eax, SUCCESS
ret
.else
mov edx, _hFileMap
invoke CloseHandle, [edx]
mov edx, _hFile
invoke CloseHandle, [edx]
mov eax, ERROR_MAPVIEW_FAILED
ret
.endif
.else
mov edx, _hFile
invoke CloseHandle, [edx]
mov eax, ERROR_CREATEMAP_FAILED
ret
.endif
.endif
mov eax, ERROR_OPENFILE_FAILED
ret
OpenAndMapFile endp
;
; This function will cloase all the handles opened and created by
; OpenAndMapFile function
;
CloseAndUnmapFile proc _hFile:PHANDLE, \
_hFileMap:PHANDLE, \
_hFileBase:PHANDLE
mov edx, _hFileBase
invoke UnmapViewOfFile, [edx]
mov edx, _hFileMap
invoke CloseHandle, [edx]
mov edx, _hFile
invoke CloseHandle, [edx]
ret
CloseAndUnmapFile endp
endif
; --------------------- dump.asm ---------------------
;
; HexDump utility written by Chris Vega.
;
;
; Copy both dump.asm and filemap.inc to bin directory of your
; MASM32 package, and execute these commands:
;
; ml /c /coff /Cp /nologo /I"..\include" dump.asm
; link /SUBSYSTEM:CONSOLE /OPT:NOREF dump.obj "..\lib\kernel32.lib"
; "..\lib\user32.lib" "..\lib\masm32.lib"
;
.386
.model flat, stdcall
include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
include masm32.inc
includelib masm32.lib
include filemap.inc
.data
filename db MAX_FNAME_LEN dup(0)
fname_cnt dd 0
linebytes db 17 dup(0)
; Strings
dump000 db "usage: DUMP [filename]", 0ah, 0dh, 0
dump001 db "DUMP has failed to open the specified file."
db 0ah, 0dh, 0
dump0x0 db "%08X: ", 0
dump0x1 db "%02X ", 0
dump0x2 db ": ", 0
dump0x3 db ": %s", 0ah, 0dh, 0
dump0x4 db ".. ", 0
.data?
result dd ?
hFile dd ?
hFileMap dd ?
hFileBase dd ?
buffer db 128 dup(?)
.code
start:
call ProcessCommandLine
cmp fname_cnt, 0
jnz @_proceed
; Print usage
invoke StdOut, addr dump000
jz @_exit
@_proceed:
; Open and map the specified file
invoke OpenAndMapFile, addr filename, \
addr hFile, addr hFileMap, addr hFileBase
mov result, eax
cmp eax, SUCCESS
jz @_00
; Failed to open the file? Print 'open error' and exit
invoke StdOut, offset dump001
ret
@_00:
; Get the file size and dump the file
invoke GetFileSize, hFile, NULL
push eax
push hFileBase
push hFileBase
call HexDump
invoke CloseAndUnmapFile, addr hFile, \
addr hFileMap, addr hFileBase
; Exit the program
@_exit:
invoke ExitProcess, 0
; Procedure to extract the filename from the command line.
; This procedure is done for that purpose only, and not designed
; to be reusable.
ProcessCommandLine proc uses esi edi ecx
mov edi, offset filename
mov byte ptr [edi], 0
xor ecx, ecx
invoke GetCommandLine
; Find the pointer to the first argument
mov esi, eax
@_@:
cmp byte ptr [esi], 32
je @_0
cmp byte ptr [esi], 0
je @_cx
inc esi
jmp @_@
@_0:
inc esi
cmp byte ptr [esi], 32
jne @__
cmp byte ptr [esi], 0
je @_cx
jmp @_0
@__:
; Copy the first argument to the filename buffer
cmp byte ptr [esi], '"'
je @_cq
; Filename is not quoted
@_c0:
lodsb
cmp al, 32
je @_c2
cmp al, 0
je @_c2
inc ecx
stosb
jmp @_c0
; Filename is quoted
@_cq:
inc esi
@_c1:
cmp byte ptr [esi], '"'
je @_c2
cmp byte ptr [esi], 0
je @_c2
lodsb
inc ecx
stosb
jmp @_c1
; Close the string
@_c2:
xor eax, eax
stosb
; Return to caller
mov fname_cnt, ecx
@_cx:
ret
ProcessCommandLine endp
; This is the main reusable hex dump procedure. Just specify the
; Base memory address, the memory address to dump and the
; count of bytes to dump, and everything will be done the the
; procedure, except that it doesn't check for read permission, so
; you have to manually capture that kind of error.
HexDump proc uses ebx edi esi base:DWORD, mem_ptr:DWORD, count:DWORD
mov byte ptr [linebytes + 16], 0
mov ebx, mem_ptr
mov edi, 0
@_loop_ebx_lt_count:
mov ecx, mem_ptr
add ecx, count
cmp ebx, ecx
jnb @_exit_hexdump
push ebx
cmp edi, 0
jnz @_get_a_byte
; Print the address
mov eax, ebx
sub eax, base
invoke wsprintf, addr buffer, addr dump0x0, eax
invoke StdOut, addr buffer
@_get_a_byte:
; Get a byte from pointer
xor eax, eax
mov al, byte ptr [ebx]
; Save it for later text printing, but remove all
; non printable characters by replacing them with
; a dot (.) character
cmp al, 255
jz @_non_printable
cmp al, 13
jbe @_non_printable
mov byte ptr [linebytes + edi], al
jmp @_print_the_byte
@_non_printable:
mov byte ptr [linebytes + edi], '.'
; Print the byte
@_print_the_byte:
invoke wsprintf, addr buffer, addr dump0x1, eax
invoke StdOut, addr buffer
inc edi
; Print 8th byte separator
cmp edi, 8
jne @_no_8th_byte_separator
invoke StdOut, addr dump0x2
@_no_8th_byte_separator:
; print end of line
cmp edi, 15
jne @_not_end_of_line
invoke wsprintf, addr buffer, addr dump0x3, addr linebytes
invoke StdOut, addr buffer
mov edi, 0
@_not_end_of_line:
; This is similar to the entire procedure, except this
; part is especially crafted as the last line scenario
; where the loop is expected to be terminated after
; this last line printing
cmp edi, 0
jbe @_not_last_line
mov ecx, base
add ecx, count
dec ecx
cmp ebx, ecx
jne @_not_last_line
; Is this the last line?
mov ebx, edi
inc ebx
@_loop_last_line:
cmp ebx, 16
jnb @_eloop_last_line
invoke StdOut, addr dump0x4
cmp ebx, 8
jne @_il_no8th
invoke StdOut, addr dump0x2
@_il_no8th:
inc ebx
jmp @_loop_last_line
@_eloop_last_line:
mov byte ptr [linebytes + edi], 0
invoke wsprintf, addr buffer, addr dump0x3, addr linebytes
invoke StdOut, addr buffer
jmp @_exit_hexdump
@_not_last_line:
pop ebx
inc ebx
jmp @_loop_ebx_lt_count
@_exit_hexdump:
ret
HexDump endp
end start