News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

GetLogicalDrives trouble

Started by Nilrem, February 09, 2005, 08:07:26 PM

Previous topic - Next topic

Nilrem

invoke GetLogicalDrives
print [BYTE PTR [eax]]
Would anyone please like to explain why this will not work? I tried moving eax to a [var] but it wouldn't except that either. The above just crashes. Thanks once again guys.

Nilrem

#1
Ok so I had to use print uhex$(eax), which printed 00000001, but I was using the wrong api as I need the drive letter. Oops.
What I should have posted was:
LOCAL lpstring[128]:DWORD
LOCAL FindName[128]:DWORD
invoke GetLogicalDriveStrings, ADDR lpstring, ADDR FindName
mov [FindName], BYTE PTR [eax]

the mov instruction is invalid. What I want to do is get the first drive found and use it. I tried FirstFindVolume but that returned kernel.dll


Forget that, new modification, using a debugger when print rootpathname it shows ???
LOCAL lpstring[128]:DWORD
LOCAL FindNameSize[128]:DWORD
LOCAL FindName[128]:DWORD
invoke GetLogicalDriveStrings, ADDR FindNameSize, ADDR FindName
invoke StdOut, ADDR FindName
again:

    invoke GetVolumeInformation, [FindName],
                                ADDR VolumeNameBuffer,
                                nVolumeNameSize,
                                ADDR VolumeSerialNumber,
                                ADDR MaximumComponentLength,
                                ADDR FileSystemFlags,
                                ADDR FileSystemNameBuffer,
                                nFileSystemNameSize
  print chr$(13,10,"RootPathName: ")
    print [FindName]
    print chr$(13,10,"VolumeSerialNumber (hex): ")
    print uhex$(VolumeSerialNumber)
    mov lpstring, _input (13,10,"UserName: ")

Thanks.

pbrennick

Nilrem,
I was about to make that same suggestion to you.  The result that you say you got does not look right to me, though, as this is a bitwise result mask showing your drives.  On my machine I get 000001BD which looks more proper.

Paul

Nilrem

You'll have to look at my post again, I edited it yet again hehe. Sorry.

Nilrem

#4
I believe it's because it returns all my drives not just the first one, but when I do:
invoke StdOut, ADDR FindName it only prints A:\. I want to just get the first drive found and manipulate it from there. To do that I tried mov statements with getlogicaldrivestrings but to no avail.

Ediit:
Just had a lightbulb instance. I thought this would work (if I move +4 along then that should get rid of the other drives detected and leave just the first one found):
LOCAL lpstring[128]:DWORD
LOCAL FindNameSize[128]:DWORD
invoke GetLogicalDriveStrings, ADDR FindNameSize, ADDR FindName
;mov edi, OFFSET FindName
xor eax, eax
xor edi, edi
mov edi, OFFSET FindName
mov edi+4, eax
however as you can see by the last error producing line of code that I'm not sure how to achieve this.

Peterpan

Nilrem,

Just for an info, as you had find out, it only prints "A:\", while actually your machine had more drives. GetLogicalDriveStrings returned all available drives, delimited with null char (zero terminated). The last drive available is delimited with 2 null chars (zero terminated).

If your machine has A, C & D drives, the returned list will looks like this:
"A:\",0,"C:\",0,"D:\",0,0

Nilrem

I'm missing your point I think. Why didn't it print them all?

Peterpan

#7
Well, the null char (zero terminated) is actually use by Windows for string terminator. So it will only prints "A:\", because the list that returned by GetLogicalDriveStrings use null char as DELIMITER while Windows *think* it is string terminator. Am I making any sense here ???  :green

Update:

Oh okay. I think you can see it this way:

If you run this code:   print chr$( "RootPathName: " )
it will prints:  RootPathName:

but this one:  print chr$( "RootPa",0,"thName: " )
will print:  RootPa
because there is a null char in the middle, so Windows think it is the end of the string

Hope it makes sense now  :lol

MichaelW

To illustrate:

; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .486                       ; create 32 bit code
    .model flat, stdcall       ; 32 bit memory model
    option casemap :none       ; case sensitive
    include \masm32\include\windows.inc
    include \masm32\include\masm32.inc
    include \masm32\include\kernel32.inc
    includelib \masm32\lib\masm32.lib
    includelib \masm32\lib\kernel32.lib
    include \masm32\macros\macros.asm
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
        buffer db 128 dup(0)
    .code
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    mov   ebx, OFFSET buffer
    invoke GetLogicalDriveStrings, 128, ebx
  @@:
    print ebx               ; assume always at least 1 drive
    add   ebx, 4            ; skip past null delimiter
    cmp   BYTE PTR[ebx], 0  ; check for second null
    je    @F
    jmp   @B
  @@:
    mov   eax, input(13,10,13,10,"Press enter to exit...")
    exit
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start

eschew obfuscation

Nilrem

Thankyou Michael. Now with my code posted above can anyone please tell me why it displays nothing and crashes? A repeat of my code:

LOCAL lpstring[128]:DWORD
        LOCAL FindName[128]:DWORD
LOCAL FindNameSize[128]:DWORD
invoke GetLogicalDriveStrings, ADDR FindNameSize, ADDR FindName
again:

    invoke GetVolumeInformation, [FindName],
                                ADDR VolumeNameBuffer,
                                nVolumeNameSize,
                                ADDR VolumeSerialNumber,
                                ADDR MaximumComponentLength,
                                ADDR FileSystemFlags,
                                ADDR FileSystemNameBuffer,
                                nFileSystemNameSize
  print chr$(13,10,"RootPathName: ")
    print [FindName]
    print chr$(13,10,"VolumeSerialNumber (hex): ")
    print uhex$(VolumeSerialNumber)

MichaelW

You need to pass a valid address where the function expects an address, and a value where the function expects a value. For example:

BOOL GetVolumeInformation(
  LPCTSTR lpRootPathName,
  LPTSTR lpVolumeNameBuffer,
  DWORD nVolumeNameSize,
  LPDWORD lpVolumeSerialNumber,
  LPDWORD lpMaximumComponentLength,
  LPDWORD lpFileSystemFlags,
  LPTSTR lpFileSystemNameBuffer,
  DWORD nFileSystemNameSize
);

For the parameters with an "LP" prefix the function expects the parameter to be an address. For the
lpRootPathName  parameter the function expects the parameter to be the address of a null-terminated string that specifies the root directory of the volume to be described. Your code is passing [FindName], which for MASM is the same as FindName, which means you are passing the contents of the first dword of FindName. The function is trying to use this dword value as an address, and this is triggering an exception.

Other problems are that your two local strings are defined as dword arrays instead of byte arrays, and FindNameSize should be a single dword instead of an array.
eschew obfuscation

Nilrem

Thankyou Michael. I have made changes to my code, but still it is not working as expected, it gets A:\ as the first drive, but prints 00000000 as the volumeserialnumber, and lstrcmp is not working as expected still. Here is my modified and easier read code:
Thanks for your time again I really appreciate it.


    .data
    FileName db ".\ID.txt",0
    RootPathName dw 128 dup(0)
    VolumeNameBuffer        db 128 dup(0)
    nVolumeNameSize         dd 128
    VolumeSerialNumber      dd 0
    MaximumComponentLength  dd 0
    FileSystemFlags         dd 0
    FileSystemNameBuffer    db 128 dup(0)
    nFileSystemNameSize     dd 128
    DriveBufferSize dw 128(0)
    TempName db "C:\",0
    PasswordGen dd 128 dup(0)
    lpPasswordEntered dd 128 dup(0)
    lpUserNameEntered dd 128 dup(0)

ID PROC

LOCAL lpRootPathName[128]:BYTE
LOCAL lpstring[128]:DWORD
LOCAL FindName[128]:DWORD
LOCAL FindNameSize:DWORD
invoke GetLogicalDriveStrings, ADDR FindNameSize, ADDR lpRootPathName
again:

    invoke GetVolumeInformation, ADDR lpRootPathName,
                                ADDR VolumeNameBuffer,
                                nVolumeNameSize,
                                ADDR VolumeSerialNumber,
                                ADDR MaximumComponentLength,
                                ADDR FileSystemFlags,
                                ADDR FileSystemNameBuffer,
                                nFileSystemNameSize
  print chr$(13,10,"RootPathName: ")
    print ADDR lpRootPathName
    print chr$(13,10,"VolumeSerialNumber (hex): ")
    print uhex$(VolumeSerialNumber)
   
UserNameSequence:
    mov lpUserNameEntered, _input (13,10,"UserName: "); Store their username
    call UserNameLength; Make sure they entered something for the username
    je UserNameSequence; If they didn't enter anything ask them for their username again.
    call PasswordGeneration; Generate the password
    xor ebx, ebx; Get register ready to allow them 3 chances of retry

PasswordJump:
cmp ebx, 02h; Allow them three attempts to enter the correct password
je TooManyAttempts
mov lpPasswordEntered, _input ("Password: "); Store their password
invoke lstrcmp, [lpPasswordEntered], [lpUserNameEntered]; See if what they entered matches
test eax, eax; Result 0?
jnz @f; If not zero then badboy
print chr$(13,10,"That was correct")
ret
@@:
print chr$(13,10,"That was incorrect")
@@:
mov lpstring, _input(13,10,"Try Again? Y/N: "); Ask them to try again if they get it wrong

xor eax, eax; Get register ready
mov eax, [lpstring]; Move their choice to register
.if BYTE PTR [eax] == 'Y' || BYTE PTR [eax] == 'y' ; Y or y
inc ebx
jmp PasswordJump; Let them attempt the password
.elseif BYTE PTR [eax] == 'N' || BYTE PTR [eax] == 'n'
ret
.else
print chr$(13,10,"Invalid input",13,10)
jmp @b
.endif
ret

TooManyAttempts:
mov lpstring, _input(13,10,"You have had too many attempts, good bye.")
exit

ID endp

PasswordGeneration PROC

invoke lstrcat, ADDR lpUserNameEntered, ADDR VolumeSerialNumber; Put them together
print hex$(lpUserNameEntered); For testing purposes
ret

PasswordGeneration endp

UserNameLength PROC

invoke lstrlen, [lpUserNameEntered]
test eax, eax
ret

UserNameLength endp


MichaelW

I think the immediate problem is that you are not initializing lpRootPathName, so on my system the name being used is "A:\", and if there is no diskette in the drive (or the diskette has an invalid format) the volume serial number is not available. But I see multiple other problems in you code.

BOOL GetVolumeInformation(
  LPCTSTR lpRootPathName,
  LPTSTR lpVolumeNameBuffer,
  DWORD nVolumeNameSize,
  LPDWORD lpVolumeSerialNumber,
  LPDWORD lpMaximumComponentLength,
  LPDWORD lpFileSystemFlags,
  LPTSTR lpFileSystemNameBuffer,
  DWORD nFileSystemNameSize
);

In the above prototype, an "lp" prefix on the name indicates that the parameter is a pointer. If you followed this same naming convention in your code, then the pointer variables would have an "lp" prefix on the variable name, and the other variables would not. So in this local variable definition:

LOCAL lpRootPathName[128]:BYTE

You define a string variable, but the name indicates that it is a pointer variable. A name like "RootNamePath" would make more sense.

And in these variable definitions:

lpPasswordEntered  dd 128 dup(0)
lpUserNameEntered dd 128 dup(0)
...
LOCAL lpString[128]:DWORD

You use reasonable names and the correct data types for a pointer, but you allocate 128 DWORDs for each variable where you need only one to store the  pointer.

And there are probably other problems that I have not spotted.

A good piece of advice from the GoAsm manual, Some programming hints and tips, Code incrementally:
Quote
When writing new code as soon as you have completed a discrete part of it, test it in real time under all possible conditions and also if necessary run through it in single-step with the debugger. This way, if your program does something unexpected you can be reasonably sure the fault lies in the new code you have just written. If you leave the testing until you have written some other code, the fault will be more difficult to find.
eschew obfuscation

Mark Jones

Isn't EBX a "reserved" register, not supposed to be used in normal code? Without first PUSHing at the beginning of a proc and POPing at the end that is? i.e.,


MyProc proc
    PUSH EBX    ; EBX = 1, "save" it
.... code here changes EBX to say 0F11C000h, which could supposedly crash something later on ....
    POP EBX     ; EBX = 1 "restore" it
MyProc endp


The MASM helpfiles say a good prototyping trick is to use PUSHAD and POPAD at the beginning and end of any proc which uses registers other than EAX, ECX, and EDX. This way all the registers are restored at the end of the proc. Then later, once working, one could run the proc through a debugger and optimize the pushes and pops as needed.

Reading the help files which come with MASM can really help understanding a lot. Especially the "ASM Intro," essential reading that one is. Good luck.
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

MichaelW

AFAIK the EBX, ESI, and EDI registers need to be explicitly preserved only for code that is called by Windows (e.g. a window procedure or callback). For code that I know will not be called by Windows, even indirectly, I routinely use these registers without preserving them, and without apparent problems.
eschew obfuscation