Link to home
Start Free TrialLog in
Avatar of bboitano
bboitano

asked on

Search for partial matches to registry keys and delete them

Hello,

I am looking for some C code to do the following please :

Search through HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist branch of the registry looking for all sub-keys that contain a specific string and then delete them.

Many thanks





Avatar of pgnatyuk
pgnatyuk
Flag of Israel image

Something like that:
 

#include <Windows.h>
#include <wchar.h>
 
static const LPCWSTR s_szKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist";
static const LPCWSTR s_szSearch = L"to_be_deleted";
 
int main()
{
	HKEY hKey = NULL;
	DWORD nIndex = 0;
	DWORD nSubKey = 1024;
	WCHAR szSubKey[1024] = { 0 };
	LONG nRet = RegOpenKeyEx(HKEY_CURRENT_USER, s_szKey, 
		0, KEY_READ | KEY_WRITE, &hKey);
 
	if (hKey == NULL)
		return 0;
 
	while (RegEnumKeyEx(hKey, nIndex, szSubKey, &nSubKey, NULL,
		NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
	{
		wprintf(L"%d. %s\n", nIndex, szSubKey);
		if (wcsstr(szSubKey, s_szSearch) != 0)
		{
			RegDeleteKey(hKey, szSubKey);
		}
		nIndex++;
	}
 
	RegCloseKey(hKey);
	return 0;
}

Open in new window

Avatar of bboitano
bboitano

ASKER

Hi pgnatyu,

Thank you so much for your reply.

I have tried it but it doesn't appear to work. I get no output (though I don't know if I should) from the line :

                wprintf(L"%d. %s\n", nIndex, szSubKey);

I suspect (though my C coding is non-existant, I'm just hoping to tack this on to some other code that I have) it is because it only checks the HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist section and not any subsections below it.

This is probably more to do with my poor explanation, for which I apologise - on the machine I am on at the moment, I have these keys I would like to search :

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{5E6AB780-7743-11CF-A12B-00AA004AE837}
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{5E6AB780-7743-11CF-A12B-00AA004AE837}\Count
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{75048700-EF1F-11D0-9888-006097DEACF9}
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{75048700-EF1F-11D0-9888-006097DEACF9}\Count

I would like to search each of these - the problem is that the values between the {} change per machine so I would just like to search every value in every key under the HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\ key and if one of them contains a substring, it is to be deleted.

So it should work like this :

From HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist we create an index of keys to be searched. In our case we would get :

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{5E6AB780-7743-11CF-A12B-00AA004AE837}
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{5E6AB780-7743-11CF-A12B-00AA004AE837}\Count
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{75048700-EF1F-11D0-9888-006097DEACF9}
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{75048700-EF1F-11D0-9888-006097DEACF9}\Count

Search each of these keys for the our 'to_be_deleted" value.

So if we imagine that under HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{5E6AB780-7743-11CF-A12B-00AA004AE837}\Count we find :

HRZR_EHACNGU:P:\\Cebtenz Svyrf\\Abgrcnq++\\abgrcnq++.rkr=hex:39,00,00,00,06,\
HRZR_EHACNGU:P:\\Cebtenz Svyrf\\Fnaqobkvr\\Fgneg.rkr=hex:36,00,00,00,08,00,\
HRZR_EHACNGU:P:\\Cebtenz Svyrf\\Gbgny Havafgnyy 5\\Gh.rkr=hex:37,00,00,00,0d,\
HRZR_EHACNGU:P:\\Cebtenz Svyrf\\GharHc Hgvyvgvrf 2009\\QvfxQbpgbe.rkr=hex:1c,\
HRZR_EHACNGU:P:\\Cebtenz Svyrf\\GharHc Hgvyvgvrf 2009\\Vagrtengbe.rkr=hex:2c,\

If our "to_be_deleted" value ==  "GharHc Hgvyvgvrf 2009" then the bottom 2 lines only should be deleted.

Is that any clearer?

Thanks again - and sorry if my initial poor explanation has meant you have wasted any time.
RegEnumKeyEx allows to enumarate the keys in the opened hKey. You need to open this hKey with correct flags allowing to enumerate (and delete if you are going to delete).
The code I posted is a simple Win32 console application. The project supports Unicode character set.
This line has to print the subkeys in your code too:
wprintf(L"%d. %s\n", nIndex, szSubKey);
So you will be sure that the enumerator works fine.
Then you can continue in the way that is more convenient for you. For example, save the subkey names in an array (vector), then in another loop to analyze them and delete.  
If you are going to delete a key, I'm afraid, you need to delete all values first. I just don't remember exactly. You can test yourself or ask a related question here - will be glad to help.
>This line has to print the subkeys in your code too:
>wprintf(L"%d. %s\n", nIndex, szSubKey);
>So you will be sure that the enumerator works fine.


So I should be getting output when I run it from the console? I don't get anything at the moment.


>If you are going to delete a key, I'm afraid, you need to delete all values first. I just don't remember >exactly. You can test yourself or ask a related question here - will be glad to help.


Sorry, at some point I have obviously confused keys and values. I want to search a key, delete values containing the substring.

I hoped my example would have clarified things - I obviously need much more coffee this morning!


>Then you can continue in the way that is more convenient for you. For example, save the subkey >names in an array (vector), then in another loop to analyze them and delete.  

This is obviously way beyond my skills - as mentioned before, I can barely code a 'hello world' :)

I have increased the points value for the question as it would appear that my poor explanation has complicated this much further than necessary. If you could see your way clear to finalising the code for me, I would be very grateful.
I thought it might be me being the cause of the problems so I tried it under a different IDE.

Under LCC - I get no errors when attempting to compile your code.

Using Dev-Cpp I get :

main2.c:16: warning: passing arg 2 of `RegOpenKeyExA' from incompatible pointer type
main2.c:22: warning: passing arg 3 of `RegEnumKeyExA' from incompatible pointer type
main2.c:27: warning: passing arg 2 of `RegDeleteKeyA' from incompatible pointer type

Perhaps, if you decide to complete the code, you could give me an idiots guide to compiling it too? Whilst my C coding is non-existant, as is my C experience, if I had a working executable, I would have no problem adapting the assembly to my needs.
My code is from a Unicode project, so the functions you mentioned should be with W in the end like RegOpenKeyExW. You can try to modify it. Or I will change this app to work with multi-byte character set. It is not a big deal.
The project should be a console application or you need a function for another Win32 project?
So we have a predefined registry key. We need to enumerate all subkeys under this key. For each subkey we need to enumerate all values and delete the string values that contains a predefined string.
Or everything is easier and we do not need to enumerate the subkeys?
In the code I posted, you can see, that in the beginning, I open the key (RegOpenKeyEx). Then I enumerate the subkeys (RegEnumSubKeyEx).
So, if you say, we will need to open each subkey (with RegOpenKeyEx) and enumerate all values (RegEnumValue). If value contains a predefined string, it should be deleted.
Do I understand the task correctly?
Looking at the compiled executable under a debugger shows that the Dev-Cpp output does indeed fail at the first RegOpenKeyExA.

Patching the executable to use RegOpenKeyExW instead, does produce some output - but only the first enumerated subkey.

It is obviously something that I am doing at my end - sorry about that. As I mentioned before, if you could possibly include some simple usage intructions as well, it would be most helpful.

Kindest regards
http://www.gammadyne.com/registry.htm
Check this link - it explains what is the key and what is the value
Simulpost nearly!

>My code is from a Unicode project, so the functions you mentioned should be with W in the end like >RegOpenKeyExW. You can try to modify it. Or I will change this app to work with multi-byte character >set. It is not a big deal.

Whichever is easier for you. It might be easier if I explained the purpose of the project : we have had a malware incident and it stored information under the user assist key and also in another null-terminated or 'hidden' registry key.

I have adapted the reghide source from sysinternals (http://technet.microsoft.com/en-us/sysinternals/bb897446.aspx) so that I can delete the hidden key but now I want to add your code to that code so I can do both tasks simultaneously to clean the registry correctly.

>The project should be a console application or you need a function for another Win32 project?

It is not important - if you can add the removal of a hidden registry key to your code, all the better for me - at least that way I know it is good code, not smacked together by a monkey code like I have now :) I do appreciate though that it is outside of the scope of my initial question and would not like you to think I am trying for a '2 for 1'!

>So we have a predefined registry key. We need to enumerate all subkeys under this key. For each >subkey we need to enumerate all values and delete the string values that contains a predefined string.

Precisely

>Or everything is easier and we do not need to enumerate the subkeys?

Unfortunately, on each machine, the names of the subkeys differ. The only constant we have found at the moment is a substring in the entries that were created.

>So, if you say, we will need to open each subkey (with RegOpenKeyEx) and enumerate all values >(RegEnumValue). If value contains a predefined string, it should be deleted.

>Do I understand the task correctly?

And you have explained it much better than I.

The final project should.

1. Search for and remove a single, predifined null terminated key (though this bit you don't have to do)
2. Search every value of every key under HKCU/.../UserAssist/ for a substring and delete that value if it exists.

Simples!

Thank you for your patience - I know I am not upto speed with C and it must be painful for you!
Here are few programs like the one I posted with a short explanation:
http://www.forteach.net/Programming/win32/35351.html
And that's all we need to finish the task. Please clarify my questions and let me know if you need a code sample.
Ok. I read your post.
In a few minutes I will give a sample function with comments - I don't want to delete a lot from my registry. :)
On SysInternals I see only the executable without source code.
So the sample should work in Unicode? Have you compiled the code I posted or not?
 
Hi

http://rapidshare.com/files/312908375/reghide.zip.html <- complete with source.

I think it will have to work in Unicode to be compatible with the routines needed to delete a null terminated key.

I have compiled the initial code you gave me but have yet to get any output. I will try tinkering with some compilation options now as I suspect I have them set to ANSI not Unicode.
D:\Dev-Cpp\Enum>enum2

0. {5E6AB780-7743-11CF-A12B-00AA004AE837}
1. {5E6AB780-7743-11CF-A12B-00AA004AE837}

D:\Dev-Cpp\Enum>

This is the output I get when I add a W to the appropriate RegEnum/Open/Delete calls.

Though the 2 keys should be :

{5E6AB780-7743-11CF-A12B-00AA004AE837} and {75048700-EF1F-11D0-9888-006097DEACF9}



Please check this app. It will not delete anything. It will just print all subkeys. To be sure that the enumeration of subkeys works on your side too.

#include <Windows.h>
#include <wchar.h>
 
static const LPCWSTR s_szKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist";
 
BOOL PrintSubKeys(LPCWSTR lpszKey);
 
int main()
{
	PrintSubKeys(s_szKey);
	return 0;
}
 
BOOL PrintSubKeys(LPCWSTR lpszKey)
{
	HKEY hKey = NULL;
	DWORD nIndex = 0;
	DWORD nSubKey = 1024;
	WCHAR szSubKey[1024] = { 0 };
	LONG nRet = RegOpenKeyEx(HKEY_CURRENT_USER, s_szKey, 
		0, KEY_READ | KEY_WRITE, &hKey);
 
	if (hKey == NULL)
		return 0;
 
	while (RegEnumKeyEx(hKey, nIndex, szSubKey, &nSubKey, NULL,
		NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
	{
		wprintf(L"%d. %s\n", nIndex, szSubKey);
		nIndex++;
		ZeroMemory(szSubKey, 1024 * sizeof(WCHAR));
		nSubKey = 1024;
	}
 
	RegCloseKey(hKey);
	return TRUE;
}

Open in new window


D:\Dev-Cpp>project1
0. {5E6AB780-7743-11CF-A12B-00AA004AE837}
1. {75048700-EF1F-11D0-9888-006097DEACF9}

D:\Dev-Cpp>

Perfect!
Though it doesn't enumerate the subkeys of {5E6AB780-7743-11CF-A12B-00AA004AE837} or {75048700-EF1F-11D0-9888-006097DEACF9} - I didn't think it was meant to yet.

Please excuse me if I am wrong.
Now you will see the values (their names) under the subkeys
#include <Windows.h>
#include <wchar.h>
 
static const LPCWSTR s_szKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist";
 
BOOL PrintKey(LPCWSTR lpszKey);
 
int main()
{
	PrintKey(s_szKey);
	return 0;
}
 
BOOL PrintKey(LPCWSTR lpszKey)
{
	HKEY hKey = NULL;
	DWORD nIndex = 0;
	HKEY hSubKey = NULL;
	DWORD nSubKey = 1024;
	WCHAR szSubKey[1024] = { 0 };
	DWORD nCnt = 0;
	DWORD nData = 1024;
	BYTE pData[1024] = { 0 };
	WCHAR szValue[1024] = { 0 };
	DWORD nValue = 1024;
	LONG nRet = RegOpenKeyEx(HKEY_CURRENT_USER, s_szKey, 
		0, KEY_READ | KEY_WRITE, &hKey);
 
	if (hKey == NULL)
		return 0;
 
	while (RegEnumKeyEx(hKey, nIndex, szSubKey, &nSubKey, NULL,
		NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
	{
		wprintf(L"%d. %s\n", nIndex, szSubKey);
 
		nRet = RegOpenKeyEx(hKey, szSubKey, 0, KEY_READ | KEY_WRITE, &hSubKey);
		if (nRet == ERROR_SUCCESS)
		{
			nCnt = 0;
			do 
			{
				nRet = RegEnumValue(hSubKey, nCnt, szValue, &nValue, NULL, NULL, pData, &nData);
				if (nRet == ERROR_SUCCESS)
				{
					wprintf(L"\t %d. name = %s\n", nCnt, szValue);
					ZeroMemory(szValue, 1024 * sizeof(WCHAR));
					ZeroMemory(pData, 1024);
					nData = 1024;
					nValue = 1024;
				}
				nCnt++;
			} while(nRet == ERROR_SUCCESS);
			RegCloseKey(hSubKey);
		}
 
		nIndex++;
		ZeroMemory(szSubKey, 1024 * sizeof(WCHAR));
		nSubKey = 1024;
	}
 
	RegCloseKey(hKey);
	return TRUE;
}

Open in new window

I forgot, the values that we need to delete, they have a known substring in their name? or these are the registry strings and the known substring is the value?
like
First = 5326173265_to_be_deleted_hsdjsakjd
Or it is
First_to_be_deleted_somethingelse = does_not_matter_what
 
Output (I am still manually adding a W to the end of the RegEnum/Open functions)


D:\Dev-Cpp>project1
0. {5E6AB780-7743-11CF-A12B-00AA004AE837}
         0. name = Version
1. {75048700-EF1F-11D0-9888-006097DEACF9}
         0. name = Version

D:\Dev-Cpp>

The registry string contains the substring - the values are different each time.

Thank you
Please check if this is what you need.
Add 'W' again.

#include <Windows.h>
#include <wchar.h>
 
static const LPCWSTR s_szKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist";
static const LPCWSTR s_szToBeDeleted = L"to_delete";
 
BOOL DeleteValues(LPCWSTR lpszKey, LPCWSTR lpszSubString);
 
int main()
{
	DeleteValues(s_szKey, s_szToBeDeleted);
	return 0;
}
 
BOOL DeleteValues(LPCWSTR lpszKey, LPCWSTR lpszSubString)
{
	HKEY hKey = NULL;
	DWORD nIndex = 0;
	HKEY hSubKey = NULL;
	DWORD nSubKey = 1024;
	WCHAR szSubKey[1024] = { 0 };
	DWORD nCnt = 0;
	WCHAR szValue[1024] = { 0 };
	DWORD nValue = 1024;
	LONG nRet = RegOpenKeyEx(HKEY_CURRENT_USER, s_szKey, 
		0, KEY_READ | KEY_WRITE, &hKey);
 
	if (hKey == NULL)
		return 0;
 
	while (RegEnumKeyEx(hKey, nIndex, szSubKey, &nSubKey, NULL,
		NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
	{
		wprintf(L"%d. %s\n", nIndex, szSubKey);
 
		nRet = RegOpenKeyEx(hKey, szSubKey, 0, KEY_READ | KEY_WRITE, &hSubKey);
		if (nRet == ERROR_SUCCESS)
		{
			nCnt = 0;
			do 
			{
				nRet = RegEnumValue(hSubKey, nCnt, szValue, &nValue, NULL, NULL, NULL, NULL);
				if (nRet == ERROR_SUCCESS)
				{
					wprintf(L"\t %d. name = %s\n", nCnt, szValue);
					if (wcsstr(szValue, s_szToBeDeleted) != 0)
					{
						RegDeleteValue(hSubKey, szValue);
						wprintf(L"\t %d. name = %s. Deleted.\n", nCnt, szValue);
					}
					ZeroMemory(szValue, 1024 * sizeof(WCHAR));
					nValue = 1024;
				}
				nCnt++;
			} while(nRet == ERROR_SUCCESS);
			RegCloseKey(hSubKey);
		}
 
		nIndex++;
		ZeroMemory(szSubKey, 1024 * sizeof(WCHAR));
		nSubKey = 1024;
	}
 
	RegCloseKey(hKey);
	return TRUE;
}

Open in new window

I created a test string
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{75048700-EF1F-11D0-9888-006097DEACF9}\Count\to_delete

When running the code, this is the output I get :


D:\Dev-Cpp>project1
0. {5E6AB780-7743-11CF-A12B-00AA004AE837}
         0. name = Version
1. {75048700-EF1F-11D0-9888-006097DEACF9}
         0. name = Version

D:\Dev-Cpp>

It doesn't appear to enumrating the subkeys correctly at this stage, and it does not appear to find the string 'to_delete" which, if I understand your code correctly, it should.



:)
Because you create the key.
 

add-value.jpg
show me a screenshot from with your registry and what exactly should be deleted there
I am getting, honestly, embarassed by my constant confusion of terms.

I created a string? under the key.

Output


D:\Dev-Cpp>project1
0. {5E6AB780-7743-11CF-A12B-00AA004AE837}
         0. name = Version
1. {75048700-EF1F-11D0-9888-006097DEACF9}
         0. name = Version

D:\Dev-Cpp>

Registry
39289838.png
So we need to delete just this "to_delete" string or everything under the key Count or what?
Any string that has a name that contains "to_delete".

So if there was another string with the name "please_can_I_finally_stop_confusing_strings_and_keys_before_pgnatyuk_goes_mad_to_delete", we should delete that too.

Ok. in the code I posted we need to add one more level. Just cut&paste. Or I will add a function.
I thought the strings that we need to delete are under this {0d...} key.
If it helps - the 3 strings highlighted  in this image should be deleted
11661353.png
>I thought the strings that we need to delete are under this {0d...} key.

No sir - there are never strings (or shouldn't be) under the {} key. They are always under {}/Count

Apart from Version sorry.
Ahh. So I don't need to make a recursive functin? So the strings are always one level dwon from the key with {}?
 
YEs - once we have the {} values, we need to search {}/Count for string names containing "to_delete"
Anyway, I have finished. I hope so.
This attached app, will look for the "to_delete" string in all subkeys from the main one that is defined in the beginning.
Please check. Probably, I do not have such heavy tree in the registry like you.

#include <Windows.h>
#include <wchar.h>
 
static const LPCWSTR s_szKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist";
static const LPCWSTR s_szToBeDeleted = L"to_delete";
 
BOOL CheckKey(LPCWSTR lpszKey, LPCWSTR lpszSubString);
BOOL DeleteValues(HKEY hKey, LPCWSTR lpszSubString);
 
int main()
{
	CheckKey(s_szKey, s_szToBeDeleted);
	return 0;
}
 
BOOL CheckKey(LPCWSTR lpszKey, LPCWSTR lpszSubString)
{
	HKEY hKey = NULL;
	DWORD nIndex = 0;
 
	DWORD nSubKey = 1024;
	WCHAR szSubKey[1024] = { 0 };
 
	LONG nRet = RegOpenKeyEx(HKEY_CURRENT_USER, lpszKey, 
		0, KEY_READ | KEY_WRITE, &hKey);
 
	if (hKey == NULL)
		return 0;
 
	DeleteValues(hKey, lpszSubString);
 
	while (RegEnumKeyEx(hKey, nIndex, szSubKey, &nSubKey, NULL,
		NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
	{
		wprintf(L"%d. %s\n", nIndex, szSubKey);
		CheckKey(szSubKey, lpszSubString);
 
		nIndex++;
		ZeroMemory(szSubKey, 1024 * sizeof(WCHAR));
		nSubKey = 1024;
	}
 
	RegCloseKey(hKey);
	return TRUE;
}
 
BOOL DeleteValues(HKEY hKey, LPCWSTR lpszSubString)
{
	DWORD nCnt = 0;
	WCHAR szValue[1024] = { 0 };
	DWORD nValue = 1024;
	LONG nRet = 0;
 
	do 
	{
		nRet = RegEnumValue(hKey, nCnt, szValue, &nValue, NULL, NULL, NULL, NULL);
		if (nRet == ERROR_SUCCESS)
		{
			wprintf(L"\t %d. name = %s\n", nCnt, szValue);
			if (wcsstr(szValue, s_szToBeDeleted) != 0)
			{
				RegDeleteValue(hKey, szValue);
				wprintf(L"\t %d. name = %s. Deleted.\n", nCnt, szValue);
			}
			ZeroMemory(szValue, 1024 * sizeof(WCHAR));
			nValue = 1024;
		}
		nCnt++;
	} while(nRet == ERROR_SUCCESS);
 
	return TRUE;
}

Open in new window

:(

This time I only get :

D:\Dev-Cpp>project1
0. {5E6AB780-7743-11CF-A12B-00AA004AE837}
1. {75048700-EF1F-11D0-9888-006097DEACF9}

D:\Dev-Cpp>

When I run it.
yes. just a moment
ASKER CERTIFIED SOLUTION
Avatar of pgnatyuk
pgnatyuk
Flag of Israel image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Perfect
Thank you very much for your help - works perfectly.
You are welcome