I am working on this to learn how to call DLLs that are not Windows-standard. I am using Steve Gibson's Perfect Paper Passwords (PPP) library from http://www.grc.com/ppp/pppdll.htm. On that page are the functions and the arguments they take. I made my own protos from this documentation because there is no include file that comes with the dll, only the dll and a library. The problem is, when I try to put this through the linker, I get 2 unresolved external LNK2001 errors (For GenerateRandomSequenceKey and RetrievePasscodes), even though I have the library (in the correct directory) and the protos included. What am I doing wrong?
Also, is it possible to send the output of input directly to a variable instead of with mov, lea, mov? (It will probably assemble to that anyway, but it would make the code clearer.)
include \masm32\include\masm32rt.inc
includelib ppp.lib ;Steve Gibson's PPP Lib
;There is no .inc file with the lib, so I made my own protos. (Based on http://www.grc.com/ppp/pppdll.htm)
GenerateRandomSequenceKey proto c :DWORD
RetrievePasscodes proto c :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
.data?
passcodechars db ?
firstpasscodeno db ?
passcodecount db ?
sequencekey db ?
passcodelength db ?
.const
characterset db "!#%+23456789:=?@ABCDEFGHJKLMNPRSTUVWXYZabcdefghijkmnopqrstuvwxyz",0
AppName db "PPP DLL Test",0
.code
start:
invoke AllocConsole
SetConsoleCaption addr AppName
print "Prepare to enter parameters...",13,10
mov edi, input("Enter First Passcode Number: ")
lea eax, firstpasscodeno
mov [eax],edi
mov edi, input("Enter Passcode Count: ")
lea eax, passcodecount
mov [eax], edi
mov edi, input("Enter Passcode Length: ")
lea eax, passcodelength
mov [eax], edi
invoke GenerateRandomSequenceKey,addr sequencekey
invoke RetrievePasscodes,addr passcodechars,addr firstpasscodeno,addr passcodecount,addr sequencekey,addr characterset,addr passcodelength
print addr passcodechars
inkey "Operation Finished. Press a key to exit..."
invoke FreeConsole
invoke ExitProcess,0
end start
I notice from the site that:
QuoteIt follows 'C' naming conventions and PASCAL calling conventions.
I'm not to clear on how to declare and call with PASCAL calling conventions but it's definitely something you should take into account.
MASM Programmer's Guide : Mixed-Language Programming
http://webster.cs.ucr.edu/Page_TechDocs/MASMDoc/ProgrammersGuide/Chap_12.htm
QuoteMASM supports several different conventions. The assembler uses C convention when you specify a language type (langtype) of C, and Pascal convention for language types PASCAL, BASIC, or FORTRAN. To the assembler, the keywords BASIC, PASCAL, and FORTRAN are synonymous. MASM also supports the SYSCALL and STDCALL conventions, which mix elements of the C and Pascal conventions.
"C" naming convention is like this: MyFunction, with every first letter capitalized. PASCAL calling convention seems to be parameters are pushed by the caller but the stack is cleaned up by the callee. It is unclear, however, weather the parameters are pushed left-to-right or right-to-left. In the declaration RetrievePasscodes proto c :DWORD,.... the"c" should take care of that, but I'm not sure.
Edit: On the site, he says parameters are passed right-to-left, which is what I am doing in the code. Since he specifies PASCAL calling, should I change my proto directive?
PPP.DLL appears to be a standard DLL that uses STDCALL, and PPP.LIB is an import library. I'm not sure why he chose to confuse the user with the mixed description. Including the import library with the includelib directive causes the linker to include information in the EXE that the system will use to perform Load-Time Dynamic Linking (http://msdn.microsoft.com/en-us/library/ms684184(VS.85).aspx).
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
include \masm32\include\masm32rt.inc
includelib ppp.lib
GenerateRandomSequenceKey proto :DWORD
ConvertAsciiTo128BitDecimal proto :DWORD,:DWORD
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
sequenceKeyBuffer db 32 dup(0)
binaryBuffer db 16 dup(0)
asciiString db "012345678901234567890123456789",0
.code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
REPEAT 2
invoke GenerateRandomSequenceKey, ADDR sequenceKeyBuffer
print uhex$(DWORD PTR sequenceKeyBuffer),13,10
print uhex$(DWORD PTR sequenceKeyBuffer+4),13,10
print uhex$(DWORD PTR sequenceKeyBuffer+8),13,10
print uhex$(DWORD PTR sequenceKeyBuffer+12),13,10
print uhex$(DWORD PTR sequenceKeyBuffer+16),13,10
print uhex$(DWORD PTR sequenceKeyBuffer+20),13,10
print uhex$(DWORD PTR sequenceKeyBuffer+24),13,10
print uhex$(DWORD PTR sequenceKeyBuffer+28),13,10,13,10
ENDM
invoke ConvertAsciiTo128BitDecimal, ADDR binaryBuffer, ADDR asciiString
print str$(eax),13,10
print uhex$(DWORD PTR binaryBuffer),13,10
print uhex$(DWORD PTR binaryBuffer+4),13,10
print uhex$(DWORD PTR binaryBuffer+8),13,10
print uhex$(DWORD PTR binaryBuffer+12),13,10,13,10
inkey "Press any key to exit..."
exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
Note that the import library must be present at build time, and the DLL at run time.
Interesting.
EDIT: No, he doesn't supply source code.
Best regards,
Astro.
Thanks MichaelW. Now GetSequenceKey works.
But RetrievePasscodes doesn't. Every time execution proceeds there, it crashes. I thought it might have been because I was passing strings, not integers, so I used code I found at http://www.masm32.com/board/index.php?topic=11024.0. Based on the information on the Implementation page I linked to earlier and MichaelW's code, I defined all the parameters that are passed to RetrievePasscodes to be 16 bytes and the sequenceKey to be 32 bytes. Still it crashes. Any suggestions?
Edit:When I pass values directly (invoke RetrievePasscodes,addr passCodeChars,0,10,addr sequenceKey,addr characterSet,2), it works. So how do I take an input string and make it an integer? Clearly my method is not working.
include \masm32\include\masm32rt.inc
includelib ppp.lib ;Steve's PPP Lib
GenerateRandomSequenceKey proto :DWORD
RetrievePasscodes proto :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
.data?
sequenceKey db 32 dup(?)
firstPassCodeNo db 16 dup(?)
passCodeCount db 16 dup(?)
passCodeLength db 16 dup(?)
passCodeChars db ?
.const
characterSet db "!#%+23456789:=?@ABCDEFGHJKLMNPRSTUVWXYZabcdefghijkmnopqrstuvwxyz",0 ;this is PPP's default character set
AppName db " PPP!",0
message1 db "Enter First Passcode Number: ",0
message2 db "Enter Passcode Count: ",0
message3 db "Enter Passcode Length: ",0
.code
start:
invoke AllocConsole
SetConsoleCaption addr AppName
print "Welcome to PPP!",13,10,"Prepare to enter parameters...",13,10
invoke StdOut,addr message1
invoke StdIn,addr firstPassCodeNo,128
mov BYTE PTR [firstPassCodeNo+eax-2],0 ;This is supposed to change the string
invoke atodw,addr firstPassCodeNo ;to an integer. I don't know if it works.
invoke StdOut,addr message2
invoke StdIn,addr passCodeCount,128
mov BYTE PTR [passCodeCount+eax-2],0
invoke atodw,addr passCodeCount
invoke StdOut,addr message3
invoke StdIn,addr passCodeLength,128
mov BYTE PTR [passCodeLength+eax-2],0
invoke atodw,addr passCodeLength
invoke GenerateRandomSequenceKey,addr sequenceKey
; ¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤
; Begin Debug Section
; ¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤
;print sequenceKey in DWORD-length blocks
print "sequenceKey",13,10
print uhex$(DWORD PTR sequenceKey),13,10
print uhex$(DWORD PTR sequenceKey+4),13,10
print uhex$(DWORD PTR sequenceKey+8),13,10
print uhex$(DWORD PTR sequenceKey+12),13,10
print uhex$(DWORD PTR sequenceKey+16),13,10
print uhex$(DWORD PTR sequenceKey+20),13,10
print uhex$(DWORD PTR sequenceKey+24),13,10
print uhex$(DWORD PTR sequenceKey+28),13,10,13,10
;print other parameters
print "Parameters",13,10
print "firstPassCodeNo",13,10
print uhex$(DWORD PTR firstPassCodeNo)
print uhex$(DWORD PTR firstPassCodeNo+4)
print uhex$(DWORD PTR firstPassCodeNo+8)
print uhex$(DWORD PTR firstPassCodeNo+12),13,10
print "passCodeCount",13,10
print uhex$(DWORD PTR passCodeCount)
print uhex$(DWORD PTR passCodeCount+4)
print uhex$(DWORD PTR passCodeCount+8)
print uhex$(DWORD PTR passCodeCount+12),13,10
print "passCodeLength",13,10
print uhex$(DWORD PTR passCodeLength)
print uhex$(DWORD PTR passCodeLength+4)
print uhex$(DWORD PTR passCodeLength+8)
print uhex$(DWORD PTR passCodeLength+12),13,10
inkey
; ¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤
; End Debug Section
; ¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤=÷=¤
invoke RetrievePasscodes,addr passCodeChars,addr firstPassCodeNo,addr passCodeCount,addr sequenceKey,addr characterSet,addr passCodeLength
print addr passCodeChars
inkey
invoke FreeConsole
invoke ExitProcess,0
end start
The atodw procedure appears to be broken. For MASM32 v10 here is a demo of three workable alternatives, all of which can correctly convert input values from -2147483648 to 4294967295.
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
buff db 30 dup(0)
.code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
invoke StdIn, ADDR buff, 30
print ADDR buff,13,10
invoke atol, ADDR buff
push eax
print str$(eax),9,"atol as signed",13,10
pop eax
print ustr$(eax),9,"atol as unsigned",13,10
invoke crt_atol, ADDR buff
push eax
print str$(eax),9,"crt_atol as signed",13,10
pop eax
print ustr$(eax),9,"crt_atol as unsigned",13,10
invoke crt_atoi, ADDR buff
push eax
print str$(eax),9,"crt_atoi as signed",13,10
pop eax
print ustr$(eax),9,"crt_atoi as unsigned",13,10
inkey "Press any key to exit..."
exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
here's a atodw that works with negatives aswell as positive, is from the thread http://www.masm32.com/board/index.php?topic=11275.0
a2dwLIB proc uses ecx edi edx esi String:DWORD
;----------------------------------------
; Convert decimal string into dword value
; return value in eax
;----------------------------------------
xor ecx, ecx
mov edi, String
invoke lstrlen, String
.while eax != 0
xor edx, edx
mov dl, byte ptr [edi]
sub dl, "0" ; subtrack each digit with "0" to convert it to hex value
mov esi, eax
dec esi
push eax
mov eax, edx
push ebx
mov ebx, 10
.while esi > 0
mul ebx
dec esi
.endw
pop ebx
add ecx, eax
pop eax
inc edi
dec eax
.endw
mov eax, ecx
ret
a2dwLIB endp
a2dwLIB appears the code from the MASM32 library a2dw procedure, which is also broken.