DLL2ANSI is convertor which reads exported functions from dynamically-linked library (*.dll
) and writes names of those exported amphibious functions,
which have both ANSI and WIDE variant (their names are suffixed with A and W).
Output text can be redirected to a source file winansi
which is required by macro
WinAPI.
%SystemRoot%\system32\directory.
euroasm.exe dll2ansi.htmdll2ansi.exe %SystemRoot%\system32\kernel32.dll >> ..\maclib\winansi.asm or
FOR %a in (*32.dll) DO dll2ansi %a >> winansi.asm
The header defines program format, reads the command-line parameter and loads
the processed DLL to memory with macro FileLoad
. Inspection of DLL format will use definition of PECOFF headers, whose structures
are borrowed from EuroAssembler source files pf*.htm
included to this program.
All used WinAPI invocations are in the default library kernel32.dll
, no import library is linked.
This sample program also demonstrates the feature EUROASM AUTOSEGMENT=ON
. No explicit section switching is used here.
EUROASM Unicode=off, AutoAlign=on, AutoSegment=on
dll2ansi PROGRAM Format=PE, Width=32, Subsystem=Con, Entry=Main:
INCLUDEHEAD1 wins.htm, winscon.htm, winapi.htm, winf32.htm, \
sort32.htm, string32.htm, cpuext32.htm, \
..\easource\pfcoff.htm, ..\easource\pfpe.htm, ..\easource\pfmz.htm
Main:PROC ; Program entry.
GetArg 1 ; Return the first argument in ESI,ECX.
JC Help: ; If syntax error on command line.
JECXZ Help: ; If no argument was provided.
StripQuotes ESI,ECX ; Get rid of quotes, if they were used.
FileAssign DllFile,ESI,Size=ECX
FileLoad DllFile ; Load the file contents into heap memory.
JNC AnalyzeDLL: ; Continue with DLL loaded at ESI,EAX.
StdOutput ="Error reading file ", DllFile.Name, Eol=Yes
TerminateProgram Errorlevel=8
Help:StdOutput HelpText
TerminateProgram Errorlevel=12
ENDP Main:
DllFile DS FILE ; This program uses WinFILE layer.
HelpText: D "Program: dll2ansi.exe",13,10
D "Function:Enumerate function names exported by the DLL with ANSI+WIDE variant.",13,10
D "Version: %^Date",13,10
D "Format: Windows 32-bit console application.",13,10
D "Licence: Public domain by vitsoft.",13,10
D "Input: DLL file name specified on the command line.",13,10
D "Output: List of exported ANSI+WIDE function names.",13,10
D "Example: dll2ansi %%SystemRoot%%\system32\kernel32.dll >> winansi.asm",13,10,0
DLL file contents will be inspected. This procedure will recognize COFF section which accomodates export directory (usually it is [.edata] or [.text]).
NumberOfSections D DWORD ; Copy of the field NumberOfSection in COFF file header.
AnalyzeDLL: PROC
LEA EDX,[ESI+EAX] ; DLL contents is now pointed to with ESI..EDX.
CMPW [ESI+PFMZ_DOS_HEADER.e_magic],'MZ'
JNE Wrong: ; If the file is not valid DLL.
ADD ESI,[ESI+PFMZ_DOS_HEADER.e_lfanew] ; Skip DOS header.
CMP ESI,EDX ; Check for a premature end.
JNB Wrong: ; If DLL format is damaged.
LODSD ; Load PE signature and advance ESI to the COFF file header.
CMP EAX,'PE'
JNE Wrong:
MOVZX EAX,[ESI+PFCOFF_FILE_HEADER.Machine]
MOVZX EBX,[ESI+PFCOFF_FILE_HEADER.SizeOfOptionalHeader]
MOVZX ECX,[ESI+PFCOFF_FILE_HEADER.NumberOfSections]
ADD ESI,SIZE# PFCOFF_FILE_HEADER; ESI now points to the optional header.
JECXZ NoExport:
MOV [NumberOfSections],ECX
; Recognize the library width.
CMP AX,pfcoffFILE_MACHINE_IA64
JE .10:
CMP AX,pfcoffFILE_MACHINE_AMD64
JE .10:
; DLL is 32bit.
MOV EDI,[ESI+PFPE_OPTIONAL_HEADER32.DataDirectory + 8*pfpeDIRECTORY_ENTRY_EXPORT + 0]
MOV ECX,[ESI+PFPE_OPTIONAL_HEADER32.DataDirectory + 8*pfpeDIRECTORY_ENTRY_EXPORT + 4]
JMP .20:
.10: ; DLL is 64bit.
MOV EDI,[ESI+PFPE_OPTIONAL_HEADER64.DataDirectory + 8*pfpeDIRECTORY_ENTRY_EXPORT + 0]
MOV ECX,[ESI+PFPE_OPTIONAL_HEADER64.DataDirectory + 8*pfpeDIRECTORY_ENTRY_EXPORT + 4]
.20: ; EDI is RVA of the export data directory, ECX is its size.
; ESI points to optional header, EBX is its size.
ADD ESI,EBX ; ESI is now the first section header.
.30: MOV EAX,[ESI+PFCOFF_SECTION_HEADER.VirtualAddress]
CMP EDI,EAX
JB .40:
ADD EAX,[ESI+PFCOFF_SECTION_HEADER.SizeOfRawData]
CMP EDI,EAX
JB CollectNames: ; If export data directory is in the section specified by header ESI.
.40: ADD ESI,SIZE# PFCOFF_SECTION_HEADER ; Otherwise try the next COFF section.
DEC [NumberOfSections]
JZ NoExport: ; If no COFF section is left.
JMP .30:
NoExport:StdOutput ="No ANSI+WIDE function is exported from ", DllFile.Name, Eol=Yes
TerminateProgram Errorlevel=4
Wrong:StdOutput DllFile.Name, =" is not in valid DLL format.", Eol=Yes
TerminateProgram Errorlevel=8
ENDP AnalyzeDLL:
This procedure will find Name Pointer Table (NPT) which contains pointers to all symbols
exported by the DLL. Then it sorts the table by size+name, so names which differ only
in the last suffix ~A and ~W are kept together.
Sorting of pointers takes place in the DLL's image loaded in memory.
RVA2PTR DD 0 ; Difference between RVA and pointer to memory where is the DLL loaded.
CollectNames: PROC
MOV EBX,[DllFile.BufPtr] ; Start of file contents loaded in memory (FA=0).
ADD EBX,[ESI+PFCOFF_SECTION_HEADER.PointerToRawData] ; Add FA of section data.
SUB EBX,[ESI+PFCOFF_SECTION_HEADER.VirtualAddress] ; Subtract RVA of section data.
MOV [RVA2PTR],EBX ; Remember the difference.
ADD EDI,EBX ; EDI is now pointer to Export Directory Table (EDT).
MOV ECX,[EDI+PFPE_EXPORT_DIRECTORY.NumberOfNames]
MOV ESI,[EDI+PFPE_EXPORT_DIRECTORY.AddressOfNames]
ADD ESI,EBX ; ESI is now pointer to Name Pointer Table (NPT) in memory.
; Sort NPT (array of ECX DWORD pointers to ASCIIZ names) pointed to by ESI.
ShellSort ESI,ECX,4,BySize ; Macro from the library sort32.htm.
BySize: PROC1 ; Callback to compare ASCIIZ names pointed to by ESI and EDI.
PUSH ECX
MOV EAX,[EDI]
ADD EAX,[RVA2PTR]
GetLength$ EAX ; Return the size of ASCIIZ string in ECX.
MOV EDX,ECX
MOV EAX,[ESI]
ADD EAX,[RVA2PTR]
GetLength$ EAX
CMP ECX,EDX ; Compare string sizes.
JB .Ordered:
JA .Swapped:
PUSH ESI,EDI ; Equal size of both string. Compare them.
MOV ESI,[ESI]
MOV EDI,[EDI]
ADD ESI,[RVA2PTR]
ADD EDI,[RVA2PTR]
REPE:CMPSB
POP EDI,ESI
JB .Ordered:
.Swapped:LODSD ; Entered with CF=0, fn names are in wrong order.
MOV EDX,[EDI]
STOSD
MOV [ESI-4],EDX ; Swap pointers pointed to by ESI,EDI.
.Ordered:CMC ; Entered with CF=1, the order of fn names complies.
POP ECX
RET
ENDP1 BySize:
ENDP CollectNames ; Continue with SelectNames:.
Sorted names of all exports are examinated for the presence of ANSI and WIDE variant.
Pointers to compliant names will be stored to a dynamically allocated table EXPtab.
Table EXPtab is then sorted again, alphabetically.
NPTtop DD 0 ; Pointer beyond the last DWORD in NPT.
EXPtab DD 0 ; EXP table contains DWORD pointers to amphibious ASCIIZ fn names.
EXPptr DD 0 ; Pointer to the next free DWORD in EXPtab.
SelectNames:: PROC
SAL ECX,2 ; Size of NPT in bytes.
LEA EAX,[ESI+ECX] ; Pointer to the top of NPT.
MOV [NPTtop],EAX
WinAPI GlobalAlloc,GMEM_FIXED,ECX ; Allocate the room for pointers to selected names.
MOV [EXPtab],EAX
MOV [EXPptr],EAX
TEST EAX
JZ Wrong: ; If unexpected allocation error.
SUB ESI,4 ; Loop through the sorted NPT and search for termination with ~A and ~W.
.10: ADD ESI,4 ; Search for the next amphibious fn name.
MOV EAX,[NPTtop]
SUB EAX,4
CMP ESI,EAX
JNB .70: ; If no more names.
MOV EAX,[ESI]
MOV EDI,[ESI+4]
ADD EAX,EBX
ADD EDI,EBX ; EAX and EDI now point to the two neibourghing names.
GetLength$ EAX
MOV EDX,ECX
GetLength$ EDI
CMP ECX,EDX
JNE .10: ; If they have different lengths, search further.
DEC ECX ; Let [EAX+ECX] and [EDI+ECX] point to their last characters.
CMPB [EAX+ECX],'A'
JNE .10:
CMPB [EDI+ECX],'W'
JNE .10:
MOV EDX,EAX ; Save pointer to possible amphbious fn name.
XCHG EAX,ESI ; ESI,EDI now point to same-length names terminated with A and W.
REPE:CMPSB
XCHG ESI,EAX
JNE .10: ; If they are different, search further.
MOV EDI,[EXPptr]
MOV EAX,EDX ; EDX is now pointer to amphibious ASCIIZ function name.
STOSD ; Store name pointed to with EDX to EXPtab.
MOV [EXPptr],EDI
ADD ESI,4 ; Skip the stored pair.
JMP .10: ; Continue searching.
.70: ; All amphibious names are selected now to EXPtab.
MOV ESI,[EXPtab]
MOV ECX,[EXPptr]
SUB ECX,ESI
SAR ECX,2 ; ECX is now the number of names.
ShellSort ESI,ECX,4,ByName
ByName:PROC1 ; Callback to compare ASCIIZ names pointed to by ESI and EDI.
PUSH ECX
MOV EAX,[EDI]
GetLength$ EAX
MOV EDX,ECX
MOV EAX,[ESI]
GetLength$ EAX
CMP ECX,EDX
JLE .Short:
MOV ECX,EDX
.Short:DEC ECX ; Compare only in the size of the shorter name.
JS .Ordered:
PUSH ESI,EDI
MOV ESI,[ESI]
MOV EDI,[EDI]
REPE:CMPSB
POP EDI,ESI
JB .Ordered:
LODSD ; Entered with CF=0.
MOV EDX,[EDI]
STOSD ; Swap name pointers.
MOV [ESI-4],EDX
.Ordered:CMC ; Entered with CF=1.
POP ECX
RET
ENDP1 ByName:
ENDP SelectNames: ; Continue with PrintNames:.
This procedure writes the list of exported names from EPTtab to the standard output formated to lines. Then it terminates the program.
Line D 128*BYTE ; Room where the printed line is compiled.
LinePtr D D ; Pointer to the next free position in Line.
Number D 12*BYTE ; Room for the ASCIIZ number of amphibious functions exported by this DLL.
Wrap EQU 78 ; Line size where it wraps.
PrintNames:: PROC
MOV EAX,Line
MOV [LinePtr],EAX ; Start with empty line.
; The first output line is a comment.
MOV EAX,ECX ; Number of amphibious functions.
MOV EDI,Number
StoD EDI
SUB EAX,EAX
STOSB
LEA EDX,[DllFile.Name] ; EDX now points to ASCIIZ DLL path.
ADD EDX,[DllFile.NameOffs] ; EDX now points to ASCIIZ DLL name without path.
StdOutput =B" \ ",Number,=B" ANSI+WIDE functions exported by ",EDX,=B"
:",Eol=Yes
; Print export names in a loop, wrap when line size approaches Wrap.
MOV ESI,[EXPtab]
.10: CMP ESI,[EXPptr]
JAE .90: ; If the last fn has been written.
LODSD ; Load pointer to ASCIIZ fn name and advance ESI to the next name.
GetLength$ EAX
DEC ECX ; Get rid of the last character A or W.
MOV EDI,[LinePtr]
LEA EDX,[EDI+ECX]
SUB EDX,Line
CMP EDX,Wrap
JB .20: ; Skip to .20: if Wrap would not be overrun yet.
CALL PrintLine: ; Terminate Line contents with line-continuation \ and CR+LF.
MOV EDI,[LinePtr]
.20: XCHG EAX,ESI
REP:MOVSB
XCHG ESI,EAX
MOV AL,',' ; Terminate each name with comma ,.
STOSB
MOV [LinePtr],EDI
JMP .10:
.90: CALL PrintLine: ; Finish the last line.
; The final cleaning.
WinAPI GlobalFree,[EXPtab]
FileClose [DllFile]
TerminateProgram Errorlevel=0
ENDP PrintNames:
This procedure will pad the unused room in Line with spaces, complete the line with a line-continuation character \+CR+LF and write the Line on the standard output.
PrintLine: PROC
PUSHAD
MOV ECX,Wrap ; Calculate the number of spaces to pad the line.
MOV EDX,Line
MOV EDI,[LinePtr]
ADD ECX,EDX
SUB ECX,EDI
JNA .50:
MOV AL,' '
REP:STOSB ; Pad with spaces.
.50: MOV AL,'\' ; Continuation character is right-justified.
STOSB
SUB EDI,EDX ; Printed line size.
StdOutput EDX,Size=EDI,Eol=Yes
MOV [LinePtr],EDX ; Reinitializate the Line.
POPAD
RET
ENDP PrintLine:
ENDPROGRAM dll2ansi