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.htm
dll2ansi.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