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.
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.
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
You'll have to look at my post again, I edited it yet again hehe. Sorry.
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.
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
I'm missing your point I think. Why didn't it print them all?
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
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
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)
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.
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
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.
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.
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.
That aside it doesn't fix my problem. If you run and compile the program after entering your username it tells you the correct password (for testing purposes) but if you enter it, it says it's incorrect. Also it displays 00000000 for the volume serial number.
Nilrem,
When I am working on a project and it turns into a confused mess, I save the mess and start over with a clean sheet of paper, so to speak. It's not that I can't work with a confused mess, it's just that I prefer to apply a maximum amount of my increasingly limited brain power to the core task at hand, instead of wasting it unraveling a confused mess.
That is so true Michael. I recently made a simple data-hashing algorithm and had to start over from scratch - twice - to get my head out of the clouds. Assembler is very non-intuitive at first, I tried it once about 10 years ago and gave up after a few weeks of it [hell]. But then it was all Win16/dos; Win32 is much nicer to work with. Especially if you get accustomed to using Ramon's Easy Code. :bg
I'd code it exactly the same way. If you compile it, it just doesn't work properly. I do not understand why, good advice but not really applicable in my case (I'll rewrite it and optimise it when it actually works). Any help?