当我们安装一款软件的时候,这款软件的一些快捷方式可能被设置到开始菜单的“常用应用”区域。但是,如果是“卸载”快捷方式被“钉”到该区域,就会造成非常不好的体验。毕竟把“卸载”接口暴露得如此醒目,如同把该款软件的地狱大门暴露出来。(转载请指明出于breaksoftware的csdn博客)
如下图,python就将卸载程序放到了“常用区域”。从而会导致windows会将该快捷方式放到“常用应用”区域。
一种解决方案就是,不将“卸载”快捷方式放到开始菜单的“普通区域”。很多应用都是使用这种方式。
另一种就是如何将该项从“常用应用”区域删除。本文就是讲解这套方案的研究和分析。
首先,我们使用RegSnap抓取一次注册表快照。然后在”常用应用“区域删除notepad++的启动快捷方式,再生成一次快照。对比两次快照。我们可以得出如下结果:
Deleted keys HKEY_USERS\S-1-5-21-3689171631-189274284-2341753515-176562\Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs\70 HKEY_USERS\S-1-5-21-3689171631-189274284-2341753515-176562\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\Count\{0139Q44R-6NSR-49S2-8690-3QNSPNR6SSO8}\Abgrcnq++\Abgrcnq++.yax |
---|
从对比图中,我们可以看到一个叫Abgrcnq++.yax被删除了。这个是个非常有意思的名字,我们可以看出来,这个名字明显是被加密的,但是加号和点号没有被加密。我又观察了其他的键(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\Count下)
从后缀rkr可以想象,这个可能是exe的加密结果。后来我做了下计算,发现字符间存在13的差值。后来才知道这个就是所谓的ROT13置换转换码。该算法的详细介绍可以见
http://baike.baidu.com/link?url=a3UL0bMbmzzINfomfkCgTHyUOQDwBk83WkEjcgH6gZdvproZg7OTcXkt6G3oLLFpZnBXwXWhFWeGhqXBN8Tuhq
后来,我又做了一个实验——删除了HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\Count的一个键,该键对应的快捷方式在开始菜单的”常用应用“区域。发现,该键被删除后,该快捷方式就消失了。
以下是测试代码:
// RegExplorerUserAssist.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <windows.h>
#include <string>
/*
Registry value names are limited to 32,767 bytes.
The ANSI version of this function treats this parameter as a SHORT value.
Therefore, if you specify a value greater than 32,767 bytes, there is an overflow and the function may return ERROR_MORE_DATA.
*/
#define MAX_VALUE_NAME 16383
// http://baike.baidu.com/link?url=a3UL0bMbmzzINfomfkCgTHyUOQDwBk83WkEjcgH6gZdvproZg7OTcXkt6G3oLLFpZnBXwXWhFWeGhqXBN8Tuhq
std::wstring ConvertData( LPCWSTR lpBuffer, DWORD dwLength ) {
std::wstring wstrValueName;
for ( DWORD dwIndex = 0; dwIndex < dwLength; dwIndex++ ) {
WCHAR wchSingle = *( lpBuffer + dwIndex );
unsigned char ucsa = LOBYTE(WCHAR('a'));
unsigned char ucsz = LOBYTE(WCHAR('z'));
unsigned char ucbA = LOBYTE(WCHAR('A'));
unsigned char ucbZ = LOBYTE(WCHAR('Z'));
if ( ( WCHAR('a') <= wchSingle && WCHAR('z') >= wchSingle ) || ( WCHAR('A') <= wchSingle && WCHAR('Z') >= wchSingle ) ) {
unsigned char uc = LOBYTE(wchSingle);
if ( ucsa <= uc && ucsz >= uc ) {
uc = ( ( uc + 13 ) > ucsz ) ? uc - 13 : uc + 13;
}
else if ( ucbA <= uc && ucbZ >= uc ) {
uc = ( ( uc + 13 ) > ucbZ ) ? uc - 13 : uc + 13;
}
wchSingle = WCHAR(MAKELONG(uc,0));
}
wstrValueName.append( &wchSingle, 1 );
}
#ifdef DEBUG
std::wstring wstrOutput = wstrValueName;
wstrOutput.append( WCHAR('\n'), 1);
OutputDebugStringW(wstrOutput.c_str());
#endif
return wstrValueName;
}
BOOL DeleteValue(HKEY hKey, const std::wstring& wstrSubString)
{
WCHAR wchValueName[MAX_VALUE_NAME] = {0};
LPWSTR lpValueNameBuffer = wchValueName;
DWORD dwValueNameBufferLength = ARRAYSIZE(wchValueName);
BOOL bSuc = FALSE;
do {
DWORD dwValuesCount = 0; // number of values for key
DWORD dwMaxValueNameLen = 0; // longest value name
// Get the class name and the value count.
LONG lretCode = RegQueryInfoKey( hKey,
NULL, NULL, NULL, NULL, NULL, NULL, &dwValuesCount, &dwMaxValueNameLen, NULL, NULL, NULL );
if ( ERROR_SUCCESS != lretCode ) {
break;
}
// Enumerate the key values.
if ( 0 != dwValuesCount) {
for ( DWORD dwIndex = 0; dwIndex < dwValuesCount; dwIndex++ ) {
DWORD dwValueNameInlineLength = dwValueNameBufferLength;
wmemset( lpValueNameBuffer, 0, dwValueNameInlineLength);
lretCode = RegEnumValue(hKey, dwIndex,
lpValueNameBuffer, &dwValueNameInlineLength, NULL, NULL, NULL, NULL);
if ( ERROR_NO_MORE_ITEMS == lretCode ) {
// Suc
bSuc = TRUE;
break;
}
if ( ERROR_MORE_DATA == lretCode ) {
if ( wchValueName != lpValueNameBuffer ) {
if ( NULL != lpValueNameBuffer ) {
delete [] lpValueNameBuffer;
lpValueNameBuffer = NULL;
}
}
lpValueNameBuffer = new WCHAR[dwValueNameInlineLength];
dwValueNameBufferLength = dwValueNameInlineLength;
dwIndex--;
}
if ( ERROR_SUCCESS == lretCode ) {
std::wstring wstrValueName = ConvertData( lpValueNameBuffer, dwValueNameInlineLength );
if ( std::wstring::npos == wstrValueName.find( wstrSubString.c_str() ) ) {
continue;
}
else {
bSuc = RegDeleteValue( hKey, wstrValueName.c_str() );
break;
}
}
}
}
} while (0);
if ( lpValueNameBuffer != wchValueName ) {
delete [] lpValueNameBuffer;
lpValueNameBuffer = NULL;
}
return bSuc;
}
int _tmain(int argc, _TCHAR* argv[])
{
HKEY hTestKey;
if( RegOpenKeyEx( HKEY_CURRENT_USER,
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count"),
0,
KEY_READ,
&hTestKey) == ERROR_SUCCESS
)
{
DeleteValue(hTestKey, L"\\360安全中心\\360安全卫士\\360安全卫士.lnk");
}
return 0;
}
有一点需要说明——XP下不是该注册表路径。如果想将方案移植到XP上,使用相同的方法应该可以得出注册表路径。