I'm trying to enumerate subkeys of a specific registry key.
Please check the following code.


This procedure is the entrypoint, it opens a known registry key and then hands the key's pathname and handle to another proc:


Registry_EnumerateShares proc pInputPath
local hkey:DWORD
local cunter
mov cunter,0
invoke RegOpenKeyEx, HKEY_CURRENT_USER, pInputPath, NULL, KEY_READ,addr hkey
.if eax==ERROR_SUCCESS
invoke Registry_RecurseSubKeys, hkey, pInputPath
mov cunter,eax
invoke RegCloseKey,hkey
.endif
return cunter
Registry_EnumerateShares endp



This is the workhorse procedure, called once by the entrylevel code and then iterative:



Registry_RecurseSubKeys proc hkey, pInputPath
local currentKey:DWORD
local currentValue:DWORD
local buf[256]:BYTE
local szSubKeyPath[256]:BYTE
local bufsize
local cunter
local hsubkey

mov currentKey,0
mov cunter,0
$Message "Registry_RecurseSubKey Entering SubKey %s",pInputPath

;--------------------------
@@:
mov bufsize,255
invoke RegEnumKey, hkey, currentKey, addr buf, bufsize
.if eax==ERROR_SUCCESS
;The current key has one or more SubKeys...
;We must recurse each SubKey...

invoke lstrcpy, addr szSubKeyPath, pInputPath ;Construct a Path to the new SubKey
invoke lstrcat, addr szSubKeyPath, CTEXT("\")
invoke lstrcat, addr szSubKeyPath, addr buf
invoke RegOpenKeyEx, HKEY_CURRENT_USER, addr szSubKeyPath, NULL, KEY_READ,addr hsubkey
.if eax==ERROR_SUCCESS
invoke Registry_RecurseSubKeys, hsubkey, addr szSubKeyPath
add cunter,eax
inc currentKey
jmp @B
.else
$Message "Error in RegOpenKeyEx %lu",eax
.endif
;--------------------------

.elseif eax==ERROR_NO_MORE_ITEMS
;This is a leaf key - it may contain values we seek

.else
$Message "Error in RegEnumKey: %lu",eax
.endif
return cunter
Registry_RecurseSubKeys endp



The problem is stack corruption which I only noticed because of masm silly optimisations.
I have pseudocode which looks something like this:


invoke Registry_EnumerateShares, CTEXT("OpenSlather\Shares")
invoke wsprintf, addr blah, CTEXT("%lu keys discovered"),eax


I am seeing the "keys discovered" text during final iteration of subkeys.
This means that masm is shoving the formatting string on the stack prior to calling the enumerator proc, regardless of my sourcecode's logical order of operations. Anyone have something to say about this?
It's frustrating !!
Posted on 2005-02-08 22:55:56 by Homer
post the CTEXT Macro so we can see whats going on.
But basically yeh.. i wouldnt use it.. or recode it so it works.. what else can you do?
Posted on 2005-02-09 13:51:29 by pwn
I think it's not a problem in CTEXT. Please give the code of Registry_EnumerateShares, too . Btw, why do you use HKEY_CURRENT_USER, when you already have the "hkey" variable (that I think you have to use now) ? I think if you use the hkey, the lstrcpy/cat will drop out :) . You won't have more than 10 handles open at once, so you needn't care about it. But don't forget to close those keys when you've finished enumeration :-D
Posted on 2005-02-09 14:18:19 by Ultrano
Here's my current code, modified a little.. I see what you mean about HKEY in the iterator - but since I don't open subkeys based on the current key, it isn't an issue - the way I was closing handle for error condition was a bigger issue.. hey, if you feel inclined to write a cleaner version of this, I'll be happy to replace this crap.. it's got a job to do. As long as it does what its meant to, I'm happy as a pig in the proverbial.



;====================
;This procedure walks the Shares registry "tree", looking for "leaves".
;We only store file-specific data in the "leaves" of the tree.
;It enumerates subkeys of the currently open key.
;For each subkey discovered, the procedure iterates (calls itself).
;====================
Registry_RecurseSubKeys proc hkey, pInputPath
;local dwtype:DWORD
local currentKey:DWORD
local currentValue:DWORD
local buf[256]:BYTE
local szSubKeyPath[256]:BYTE
local bufsize
local cunter
local hsubkey
local valsize, valtype, valbuffsize
local valname[256]:BYTE
local valbuffer[256]:BYTE
local dwHigh
local dwLow
local masterhash:MD5HASH
local pmap
local numbytes
local validity
local numpieces

mov currentKey,0
mov cunter,0
; $Message "Registry_RecurseSubKey Entering SubKey %s",pInputPath

;--------------------------
@@:
mov bufsize,255
invoke RegEnumKey, hkey, currentKey, addr buf, bufsize
.if eax==ERROR_SUCCESS
;The current key has one or more SubKeys...
;We must recurse each SubKey...

invoke lstrcpy, addr szSubKeyPath, pInputPath ;Construct a Path to the new SubKey
invoke lstrcat, addr szSubKeyPath, CTEXT("\")
invoke lstrcat, addr szSubKeyPath, addr buf
invoke RegOpenKeyEx, HKEY_CURRENT_USER, addr szSubKeyPath, NULL, KEY_READ,addr hsubkey
.if eax==ERROR_SUCCESS ;Key will be closed when ERROR_NO_MORE_ITEMS is encountered
invoke Registry_RecurseSubKeys, hsubkey, addr szSubKeyPath
invoke RegCloseKey, hsubkey
add cunter,eax
inc currentKey
jmp @B
.else
$Message "Error in RegOpenKeyEx %lu",eax
.endif
;--------------------------

.elseif eax==ERROR_NO_MORE_ITEMS ;end of the line, baby - no more CHILDREN
; add pInputPath, 28 ;Skip the RegistryKey part of the pathname
; $Message "Leaf %s", pInputPath
mov currentValue,0

morevals:
mov bufsize,255
mov valbuffsize,255
mov valsize,255
mov valtype,REG_DWORD
invoke RegEnumValue, hkey, currentValue, addr valname, addr valsize,NULL,addr valtype,addr valbuffer,addr valbuffsize

mov valbuffsize,255
invoke RegQueryValueEx, hkey, CTEXT("IsValid"),NULL,addr valtype, addr validity, addr valbuffsize
.if eax==ERROR_SUCCESS
.if validity==1
Message "Valid Registry Entry"
mov valbuffsize,255
invoke RegQueryValueEx, hkey, CTEXT("SizeHigh"),NULL,addr valtype,addr dwHigh, addr valbuffsize
.if eax==ERROR_SUCCESS
; $Message "SizeHigh %lu", dwHigh
mov valbuffsize,255
invoke RegQueryValueEx, hkey, CTEXT("SizeLow"),NULL,addr valtype,addr dwLow, addr valbuffsize
.if eax==ERROR_SUCCESS
; $Message "SizeLow %lu", dwLow
mov valbuffsize,255
invoke RegQueryValueEx, hkey, CTEXT("MasterHash"),NULL,addr valtype,addr masterhash, addr valbuffsize
.if eax==ERROR_SUCCESS
mov valbuffsize,255
invoke RegQueryValueEx, hkey, CTEXT("NumBytes"),NULL,addr valtype,addr numbytes, addr valbuffsize
.if eax==ERROR_SUCCESS
$Message "NumBytes %lu", numbytes
mov pmap, malloc (numbytes)
mov valbuffsize,255
invoke RegQueryValueEx, hkey, CTEXT("PieceMap"),NULL,addr valtype,pmap, addr valbuffsize
.if eax==ERROR_SUCCESS
Message "Got PieceMap"
mov valbuffsize,255
invoke RegQueryValueEx, hkey, CTEXT("NumPieces"),NULL,addr valtype,addr numpieces, addr valbuffsize
.if eax==ERROR_SUCCESS
; $Message "NumPieces %lu", numpieces
mov eax,numpieces
mov pmap, malloc (sizeof MD5HASH)
Message "Fetching PieceHashes"

xor ecx,ecx
mov edi,pmap
.while ecx<numpieces
push ecx
push edi
invoke wsprintf,addr buf, CTEXT("%lu"),ecx
pop edi
push edi
mov valbuffsize,255
invoke RegQueryValueEx, hkey, addr buf,NULL,addr valtype,edi, addr valbuffsize
pop edi
add edi,sizeof MD5HASH
pop ecx
inc ecx
.endw
Message "should be calling Shares_AddNewFilePath"
; invoke Shares_AddNewFilePath,pInputPath,addr masterhash,pmap
inc cunter
.endif
.endif
free pmap
.endif
.endif
.endif
.endif

.else
Message "Invalid Registry Entry"
.endif

.elseif eax==ERROR_NO_MORE_ITEMS

; .else
; $Message "RegQueryValue Error %lu",eax
.endif


.else
$Message "Error in RegEnumKey: %lu",eax
invoke RegCloseKey, hsubkey
.endif
invoke RegCloseKey, hkey
return cunter
Registry_RecurseSubKeys endp

;===================
;ENTRYPOINT FOR REGISTRYKEY ENUMERATION
;This procedure should be called once during startup to load existing Shares from the registry.
;This procedure Opens a Registry Key from a given KeyPath string,
;then it hands the open key and the pathstring to Registry_RecurseKeys.
;====================
Registry_EnumerateShares proc pInputPath
local hkey:DWORD
local cunter
mov cunter,0
invoke RegOpenKeyEx, HKEY_CURRENT_USER, pInputPath, NULL, KEY_READ,addr hkey
.if eax==ERROR_SUCCESS
invoke Registry_RecurseSubKeys, hkey, pInputPath
mov cunter,eax
.endif
return cunter
Registry_EnumerateShares endp

;==========================
Posted on 2005-02-10 06:39:29 by Homer
i see what you mean about that code being a piece of crap evilhomer...
yeh someone should definetely give it a fixup..
oh well best of luck. -nt-
Posted on 2005-02-10 09:20:31 by pwn
The iterator procedure now opens subkeys via the currently open key's handle ..



;--------------------------
@@:
mov bufsize,255
invoke RegEnumKey, hkey, currentKey, addr buf, bufsize
.if eax==ERROR_SUCCESS
;The current key has one or more SubKeys...
;We must recurse each SubKey...
invoke RegOpenKeyEx, hkey, addr buf, NULL, KEY_READ,addr hsubkey
.if eax==ERROR_SUCCESS ;Key will be closed when ERROR_NO_MORE_ITEMS is encountered
invoke Registry_RecurseSubKeys, hsubkey, addr szSubKeyPath
invoke RegCloseKey, hsubkey
add cunter,eax
inc currentKey
jmp @B
.else
$Message "Error in RegOpenKeyEx %lu",eax
.endif
;--------------------------


Does that look better to you? :)
Posted on 2005-02-10 22:06:01 by Homer
Bug in regkey enumerator finally resolved.
It was hard to spot due to the multithreaded nature of the application, which made it appear that the bug was elsewhere..
Has anyone ever noticed that RegQueryValueEx operates from within a thread created by ntdll? I had read the documentation regarding cached WRITES to the registry and the keyflush thingy, but I had no idea that READS operated the same way ...
Here's the final version of the procedure :)
Damn, that little bug took me three days to find because I was looking in the WRONG PLACE :oops:




;========================================================
;This procedure walks the Shares registry "tree", looking for "leaves".
;We only store file-specific data in the "leaves" of the tree.
;It enumerates subkeys of the currently open key.
;For each subkey discovered, the procedure iterates (calls itself).
;========================================================
Registry_RecurseSubKeys proc hkey, pInputPath
;local dwtype:DWORD
local currentKey:DWORD
local currentValue:DWORD
local buf[256]:BYTE
local szSubKeyPath[256]:BYTE
local bufsize
local cunter
local hsubkey
local valsize, valtype, valbuffsize
local valname[256]:BYTE
local valbuffer[256]:BYTE
local dwHigh
local dwLow
local masterhash:MD5HASH
local pmap ;<-- pointer to buffer for binary piecemap
local phasharray ;<-- pointer to buffer for N piecehashes
local numbytes
local validity
local numpieces

mov currentKey,0
mov cunter,0
; $Message "Registry_RecurseSubKey Entering SubKey %s",pInputPath

;--------------------------
@@:
mov bufsize,255
invoke RegEnumKey, hkey, currentKey, addr buf, bufsize
.if eax==ERROR_SUCCESS
;The current key has one or more SubKeys...
;We must recurse each SubKey...
invoke RegOpenKeyEx, hkey, addr buf, NULL, KEY_READ,addr hsubkey
.if eax==ERROR_SUCCESS ;Key will be closed when ERROR_NO_MORE_ITEMS is encountered
invoke Registry_RecurseSubKeys, hsubkey, addr szSubKeyPath
invoke RegCloseKey, hsubkey
inc currentKey
jmp @B
.else
$Message "Error in RegOpenKeyEx %lu",eax
.endif
;--------------------------

.elseif eax==ERROR_NO_MORE_ITEMS ;end of the line, baby - no more CHILDREN
; add pInputPath, 28 ;Skip the RegistryKey part of the pathname
; $Message "Leaf %s", pInputPath
mov currentValue,0

morevals:
mov bufsize,255
mov valbuffsize,255
mov valsize,255
mov valtype,REG_DWORD
invoke RegEnumValue, hkey, currentValue, addr valname, addr valsize,NULL,addr valtype,addr valbuffer,addr valbuffsize

mov valbuffsize,255
invoke RegQueryValueEx, hkey, CTEXT("IsValid"),NULL,addr valtype, addr validity, addr valbuffsize
.if eax==ERROR_SUCCESS
.if validity==1
mov valbuffsize,255
invoke RegQueryValueEx, hkey, CTEXT("SizeHigh"),NULL,addr valtype,addr dwHigh, addr valbuffsize
.if eax==ERROR_SUCCESS
mov valbuffsize,255
invoke RegQueryValueEx, hkey, CTEXT("SizeLow"),NULL,addr valtype,addr dwLow, addr valbuffsize
.if eax==ERROR_SUCCESS
mov valbuffsize,255
invoke RegQueryValueEx, hkey, CTEXT("MasterHash"),NULL,addr valtype,addr masterhash, addr valbuffsize
.if eax==ERROR_SUCCESS
mov valbuffsize,255
invoke RegQueryValueEx, hkey, CTEXT("NumBytes"),NULL,addr valtype,addr numbytes, addr valbuffsize
.if eax==ERROR_SUCCESS
mov pmap, malloc (numbytes)
mov valbuffsize,255
invoke RegQueryValueEx, hkey, CTEXT("PieceMap"),NULL,addr valtype, pmap, addr valbuffsize
.if eax==ERROR_SUCCESS
Message "Got PieceMap"
mov valbuffsize,255
invoke RegQueryValueEx, hkey, CTEXT("NumPieces"),NULL,addr valtype,addr numpieces, addr valbuffsize
.if eax==ERROR_SUCCESS
mov eax,sizeof MD5HASH
mul numpieces
mov phasharray, malloc (eax)
xor ecx,ecx
mov edi,phasharray
.while ecx<numpieces
push ecx
push edi
mov buf[0],0
invoke wsprintf,addr buf, CTEXT("%lu"),ecx ;<--Pieces are numerically named (0,1,2,etc)
pop edi
push edi
mov valbuffsize,255
invoke RegQueryValueEx, hkey, addr buf,NULL,addr valtype,edi, addr valbuffsize
pop edi
add edi,sizeof MD5HASH
pop ecx
inc ecx
.endw
Message "Call to Shares_AddNewFilePath is currently DISABLED"
; invoke Shares_AddNewFilePath,pInputPath,addr masterhash,pmap
inc cunter
.endif
.endif
free pmap
.endif
.endif
.endif
.endif

.else
Message "Invalid Registry Entry"
.endif

.elseif eax==ERROR_NO_MORE_ITEMS

; .else
; $Message "RegQueryValue Error %lu",eax
.endif


.else
$Message "Error in RegEnumKey: %lu",eax
invoke RegCloseKey, hsubkey
.endif
invoke RegCloseKey, hkey
return cunter
Registry_RecurseSubKeys endp

;============================================================================
;ENTRYPOINT FOR REGISTRYKEY ENUMERATION
;This procedure should be called once during startup to load existing Shares from the registry.
;This procedure Opens a Registry Key from a given KeyPath string,
;then it hands the open key and the pathstring to Registry_RecurseKeys.
;============================================================================
Registry_EnumerateShares proc pInputPath
local hkey:DWORD
local cunter
mov cunter,0
invoke RegOpenKeyEx, HKEY_CURRENT_USER, pInputPath, NULL, KEY_READ,addr hkey
.if eax==ERROR_SUCCESS
invoke Registry_RecurseSubKeys, hkey, pInputPath
mov cunter,eax
.endif
return cunter
Registry_EnumerateShares endp

;============================================================================
Posted on 2005-02-10 22:29:48 by Homer