PWDGEN is a password generator I wrote in x86 Assembly using MASM32. I decided to write this program to learn more about assembly programming. It has been a long time since I have programmed in assembly and I have always missed it.
I decided to write a password generator after hearing a well-known security expert discussing personal password policies on his podcast.
The idea is to construct an algorithm you can easily remember and use it to generate passwords for various accounts you have on the internet. For example, if you need a password for a web-site at
www.somewebsite.com, you might apply your secret password algorithm to the seed value, "somewebsite" to obtain a password you don't have to remember. You simply have to remember the algorithm you used to generate it and you can use the same algorithm over and over.
I really like the idea of a personal password policy and I have my very own which I use regularly. It is different than the one implemented in PWDGEN.
The algorithm implemented in PWDGEN yields an eight character password. It is;
(Cap3rd + last + 2nd + count%4 + count%3 + 1st + !||$(odd or even) + CapCountAlphaPointer)
1st Character - Capatilize the 3rd character of the seed value if it is an alphabetic character, otherwise just use the third character.
2nd character - Take the last character, if it is alphabetic, change it to lower-case, if its not alphabetic, just use the last character.
3rd character - Take the 2nd character, if it is alphabetic, change it to lower-case, if its not alphabetic, just use the 2nd character.
4th character - Create a numeric character by using a modulus operation on the length of the seed value, divide the seed value by four and use the remainder as the 4th character.
5th character - Create a numeric character by using a modulus operation on the length of the seed value, divide the seed value by three and use the remainder as the 4th character.
6th character - Take the 1st character, if it is alphabetic, change it to lower-case, if its not alphabetic, just use the 1st character.
7th character - Select a special character by determining if the length of the seed value is odd or even, for odd use "!", for even use "$".
8th character - Use the length of the seed value to select a letter from the alphabet and capitalize it. Start counting from "A".
So if we use "somewebsite" as our seed value, we get;
Meo32s!K
Now, I know its probably a bad idea to to actually document your secret algorithm in an assembly language application and I'm sure the security expert I mentioned earlier would cringe at the thought. Anyone capable of dis-assembling the program could determine the algorithm and use it to break your passwords if they got their hands on it.
However, it was a fun exercise and I'm on my way to doing some more assembly programming. I was thinking about adding functionality to let a user personalize the algorithm.
I will post the source in the next post...
reconmax
Ok. Here are the files. Any pointers would be appreciated...
makefile
pwdgen.rc
pwdgen.asm
thanks,
reconmax
----------------START MAKEFILE-----------------------
NAME=PWDGEN
$(NAME).exe: $(NAME).obj $(NAME).res
Link /SUBSYSTEM:WINDOWS /VERSION:4.0 /LIBPATH:c:\masm32\lib /DEBUG $(NAME).obj $(NAME).res
$(NAME).res: $(NAME).rc
rc $(NAME).rc
$(NAME).obj: $(NAME).asm
ml /c /coff /Cp /Zi /Zf /I"c:\masm32\include" $(NAME).asm
-----------------END MAKEFILE--------------------------
-----------------START PWDGEN.RC--------------------------
#define IDD_DIALOG1 101
#define IDC_GRP1 1001
#define IDC_EDT1 1002
#define IDC_EDT2 1003
#define IDC_BTN1 1004
IDD_DIALOG1 DIALOGEX 5,5,216,78
CAPTION " Password Generator"
FONT 8,"MS Sans Serif",0,0
STYLE 0x10CB0800
EXSTYLE 0x00000088
BEGIN
CONTROL "Enter Seed Value",IDC_GRP1,"Button",0x50000007,1,0,213,74,0x00000000
CONTROL "",IDC_EDT1,"Edit",0x50010000,12,15,192,11,0x00020000
CONTROL "",IDC_EDT2,"Edit",0x50010000,12,57,192,11,0x00020000
CONTROL "GO",IDC_BTN1,"Button",0x50010000,85,34,44,12,0x00000000
END
-----------------END PWDGEN.RC--------------------------
-----------------START PWDGEN.ASM--------------------------
-----------------END PWDGEN.ASM--------------------------
.386
.model flat, stdcall ;32 bit memory model
option casemap :none ;case sensitive
include windows.inc
include kernel32.inc
include user32.inc
include Comctl32.inc
include shell32.inc
includelib kernel32.lib
includelib user32.lib
includelib Comctl32.lib
includelib shell32.lib
DlgProc PROTO :HWND,:UINT,:WPARAM,:LPARAM
ToLower PROTO :BYTE
ToUpper PROTO :BYTE
GetMod PROTO :BYTE,:BYTE
IsEven PROTO :BYTE
.const
IDD_DIALOG1 equ 101
;#########################################################################
.data?
hInstance dd ?
;#########################################################################
.data
AppName db "PWDGEN",0
sdlen BYTE 0
.data?
sdbuffer db 55 dup(?)
password db 55 dup(?)
.const
IDC_EDT1 equ 1002
IDC_EDT2 equ 1003
IDC_BTN1 equ 1004
.code
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke InitCommonControls
invoke DialogBoxParam,hInstance,IDD_DIALOG1,NULL,addr DlgProc,NULL
invoke ExitProcess,0
;########################################################################
DlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
mov eax,uMsg
.if eax==WM_INITDIALOG
.elseif eax==WM_COMMAND
mov eax,wParam
.IF ax==IDC_BTN1
invoke GetDlgItemText,hWin,IDC_EDT1,ADDR sdbuffer,512
first:
;Get the third character in sdbuffer
;check to see if it is a lower case alphabetic character
;If it is, Capitalize it
;If it is not a lower case letter, just leave it as is
;Place it in the first position of password
mov ebx, offset sdbuffer
xor eax, eax
mov ah, byte ptr [ebx+2] ;move the 3rd char to ah
invoke ToUpper, ah
;place the character in ah into the first position of password
mov ecx, offset password
mov byte ptr [ecx], ah
second:
;Find the last character in sdbuffer and
;Place it in the 2nd position of password
mov esi, ebx
next_byte:
cmp byte ptr [esi], 0
je found_the_end
inc esi
inc sdlen
jmp next_byte
found_the_end:
dec esi
;now ebx points to beginning,
;and esi points to the end of string.
;and sdlen recorded the length of sdbuffer
;Place last char the 2nd position of pwdbuffer
mov ecx, offset password
mov ah, byte ptr [esi]
invoke ToLower,ah
mov byte ptr [ecx+1], ah
third:
;find the 2nd character in sdbuffer and
;place it in the 3rd porition password
mov ebx, offset sdbuffer
xor eax, eax
mov ah, byte ptr [ebx+1] ;move the 2nd char to ah
invoke ToLower,ah
mov ecx, offset password
mov byte ptr [ecx+2], ah ;move the char in ah to the 3rd position
fourth:
;The 4th character of password will be calculated
;by taking the modulus value of the sdbuffer length
;divided by the value 4, i.e. sdlen%4 or
;the remainder of sdlength/4
mov al, 4
invoke GetMod, [sdlen], al
;mov sdlen,0 ;don't re-initialize sdlen, will be used again
mov ecx, offset password
add ah, 30h ;convert the value in ah to a numeric character
mov byte ptr [ecx+3], ah ;move the character in ah to the 4th position
fifth:
;The 5th character of password will be calculated
;by taking the modulus value of the sdbuffer length
;divided by the value 3, i.e. sdlen%4 or
;the remainder of sdlength/3
mov al, 3
invoke GetMod, [sdlen], al
mov ecx, offset password
add ah, 30h ;convert the value in ah to a numeric character
mov byte ptr [ecx+4], ah ;move the character in ah to the 4th position
sixth:
;find the 1st character in sdbuffer and
;place it in the 6th porition password
mov ebx, offset sdbuffer
xor eax, eax
mov ah, byte ptr [ebx] ;move the 1st char to ah
invoke ToLower,ah
mov ecx, offset password
mov byte ptr [ecx+5], ah ;move the char in ah to t41Hhe 6th position
seventh:
;Next Learn About Odd/Even, Look at least significant bit (LSB)
;Divide the length of the seed by 2, if the remainder is zero
;it must be even,
;if the length is an even number use '$' else use '!'
invoke IsEven, [sdlen]
cmp ah,1 ;If ISEven==true
jne oddnum
;it must be even select '$'
mov ecx, offset password
mov ah, '$'
mov byte ptr [ecx+6], ah ;move the character in ah to the 7th position
jmp eighth
oddnum:
;it muist be odd select '!'
mov ecx, offset password
mov ah, '!'
mov byte ptr [ecx+6], ah ;move the character in ah to the 7th position
eighth:
;Use the length of the sed value sdbuffer as a pointer index
;to a letter in the alphabet starting with A=1, B=2, C=3, etc...
;Capitalize the letter and place it into the 8th position
mov ah, byte ptr [sdlen]
add ah, 40h
mov ecx, offset password
mov byte ptr [ecx+7], ah
mov sdlen,0 ;re-initialize sdlen
;(Cap3rd + last + 2nd + count%4 + count%3 + 1st + !||$(odd or even) + CapCountAlphaPointer)
;Place the constructed password in IDC_EDT2
invoke SendDlgItemMessage, hWin, IDC_EDT2, WM_SETTEXT, 0, ADDR password
.ENDIF
.elseif eax==WM_CLOSE
invoke EndDialog,hWin,0
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
DlgProc endp
ToLower proc char:BYTE
mov ah, char
chk_uprcse_ltr:
cmp ah, 41h
jl not_uprcse_ltr
cmp ah, 5Ah
jg not_uprcse_ltr
is_uprcse_ltr:
add ah, 20h ;lower case letter
not_uprcse_ltr:
nop
ret
ToLower endp
ToUpper proc char:BYTE
mov ah, char
chk_lwrcse_ltr:
cmp ah, 61h
jl not_lwrcse_ltr
cmp ah, 7Ah
jg not_lwrcse_ltr
is_lwrcse_ltr:
sub ah, 20h ;upper case letter
not_lwrcse_ltr:
nop
ret
ToUpper endp
GetMod proc dvdnd:BYTE, dvsr:BYTE
xor eax, eax
xor ebx,ebx
mov al, dvdnd
mov bl, dvsr
div bl
ret
GetMod endp
IsEven proc dvdnd:BYTE
xor eax, eax
xor ebx,ebx
mov al, dvdnd
mov bl, 2
div bl
;Remainder is in ah
cmp ah, 0
je evennum
mov ah, 0
jmp LeaveIsEven
evennum:
mov ah,1 ;Number is even so return true
LeaveIsEven:
ret
IsEven endp
end start
Your GetMod procedure is returning the quotient instead of the remainder. DIV returns the quotient in AL/AX/EAX and the remainder in DL/DX/EDX.
Bit 0 is set for odd numbers and clear for even numbers. With just two logical operations you can produce a result of 1 for even and 0 for odd.
Quote from: MichaelW on April 07, 2007, 04:18:06 AM
Your GetMod procedure is returning the quotient instead of the remainder. DIV returns the quotient in AL/AX/EAX and the remainder in DL/DX/EDX.
Bit 0 is set for odd numbers and clear for even numbers. With just two logical operations you can produce a result of 1 for even and 0 for odd.
Thanks. I'll check it out.
For the GetMod proc, doesn't the DIV op return its results based on the size of the data it is executed on? Since in this case, the dividend and the divisor are both BYTES (8-bits), aren't the return values confined to the AX register (quotient in AL and remainder in AH)?
Sorry, I'm only half awake. Yes, for a byte division the quotient is returned in AL and the remainder in AH.
Wow! I just found masm32.lib. I see that I could have used it to do some of the work like;
(szLen) get the length of the sdbuffer
(szLower & szUpper) convert case
(InString) isolate individual characters in sdbuffer
reconamx
Quote from: reconmax on April 07, 2007, 04:45:13 AM
Quote from: MichaelW on April 07, 2007, 04:18:06 AM
Your GetMod procedure is returning the quotient instead of the remainder. DIV returns the quotient in AL/AX/EAX and the remainder in DL/DX/EDX.
Bit 0 is set for odd numbers and clear for even numbers. With just two logical operations you can produce a result of 1 for even and 0 for odd.
Thanks. I'll check it out.
For the GetMod proc, doesn't the DIV op return its results based on the size of the data it is executed on? Since in this case, the dividend and the divisor are both BYTES (8-bits), aren't the return values confined to the AX register (quotient in AL and remainder in AH)?
What about the odd/even thing you are describing above? Are you saying I could determine the odd/even nature of a number in a better/easier way? Are you saying that some flag bits are set or something? Which flags and which operation would cause it? Sorry, but I don't understand what two logical ops to perform or how to observe the resulting bit you mention...
And thanks for you help...
reconmax
The bit values, starting with bit 7, are 128, 64, 32, 16, 8, 4, 2, and 1. Since all of the other bit values are even, bit 0 alone determines whether the number is odd or even. One method would be to isolate bit 0 by ANDing the value with 1, and then XOR the result with 1.
1 XOR 1 = 0
0 XOR 1 = 1