*本文原创作者:浪子_三少,属Freebuf原创奖励计划,未经许可禁止转载
在win7时我们只需修改注册表就能设置默认浏览器,但是win8、win10下不能直接修改的因为同样的注册表项,win8、win10多了一个校验hash值。本文就以当下最流行的操作系统win10为例分析下,我们在设置默认浏览器一般会 操作:设置—默认应用—web浏览器,或者在使用chrome设置默认浏览器时都会弹出如下的对话框:
而国内的一些浏览器会绕过这个对话框设置默认浏览器,这个设置对话框就是systemsetting.exe进程来进行的。
下面我们就分析下systemsetting.exe是如何设置默认浏览器的。
一:首先我们要了解windows的默认浏览器设置在注册表里:
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice
项目:Progid 是浏览器的指向的chrome, hash是设置的hash校验值,只有校验值对了才会弹出指定的浏览器,如果随意修改这个注册表就会失效的,所以最关键是这个hash值。接下来我们开始分析过程,使用windbg我们挂接到systemsetting.exe
我们在注册表函数下断点: bp RegSetValueExW后,操作设置默认浏览器,这时windbg会自动停在RegSetValueExW的函数入口点,说明systemsetting.exe 达到了设置注册表的地方了,然后我们查看堆栈,输入命令kb:
发现函数调用过程由systemsetting.exe进入了shell32.dll的空间,看到几个关键函数名:
000002b8`4dc10000 :KERNELBASE!RegSetValueExW
00030018`000307e1 : SHELL32!_RegSetKeyValue+0×60
00007ff9`28b3c398 : SHELL32!SHRegSetString+0×39
000002b8`536394ac : SHELL32!SetProgIdAndHash+0x1fb
00000000`00000002 : SHELL32!_SetUserChoiceAndHash+0×63
000002b8`536394ac : SHELL32!UserAssocSet+0xf6
00007ff9`28842e00 : SHELL32!CAssocHandler::MakeDefaultPriv+0x16d
看到这个函数SetProgIdAndHash 我们大概就能知道这个的函数的作用了就是计算并且设置那个注册表了。
使用IDA 打开 shell32.dll,我们可以具体分析了:
在继续往下分析:
分别经过调用了IsBrowserExtension、SHGetValueW,前面都是一些条件判断与一些信息的获取最后经过了并且进入UerAssocSet函数
在继续进入分析:
_SetUserChoiceAndHash(ushort const *,ushort const *,bool)
然后继续进入
SetProgIdAndHash(HKEY__ *,ushort const *,ushort const *,ushort const *,ushort const *,bool)
下面我们就具体分析下这个函数,最开始经过第一个函数:call ?_GetAssociationPath@@YAJPEBG_N1PEAGI@Z
第一个参数r9, lea ,r9, [rbp+1E0h+SubKey] 这是从外部传进来的,这个具体干嘛的呢?
第二个参数是r8b
第三个参数dl,都是字节传入的应该属于bool类型
具体这些将干什么我们可以分析函数获取:
该函数就是根据不同传入类型拼接不同的路径,到这里我们显然知道了这个传进来的第一个参数就是注册表里的”ProgId”,返回原函数继续分析然后返回。
打开那个注册表项目后,获取当前时间GetSystemTime,接着根据传进来的浏览器类型字符串计算一个HashAssociation字符hash值
最后写入注册表UserChoice===hash,UserChoice == progid项目里,整个过程结束。这以上是大体设置过程的分析,下面我们就开始利用这些设置过程,看看是否能够直接使用接口是设置默认浏览器。
那我们从最开始的接口是CAssocHandler::MakeDefaultPriv(unsigned char );返回开始重新具体的分析,在此函数调用前下断点:
参数1是class指针rcx,参数2是 字节类型当前值为1。
进入MakeDefaultPrive函数
IsBrowserExtension ()函数判断当前是否是设置浏览器
查看[rcx +38h]
继续查看这个红框的地址内存
是L”https”,unicode字符串。
接下来在调用UserAssocSet函数时,传入了三个参数
第一个参数就是[rdi + 38h] 即:L”https”
第二个参数是:1
第三个参数是:[rdi+50h] ,查看此内存
即L”ChromeHtml”的unicode编码。
那我们就可以假设了,我们是否能直接调用这个函数去调用设置呢,即UserAssocSet(L”http”,1,”ChromeHtml”);理论上是可以,但实际上这个函数不导出只在微软符号表里有,不太好调用,也就是说有点麻烦,写出来的代码会不是很简单,有没有更简单的方法呢,我们继续分析,我们可以看看是否也有其他导出的接口或者类调用了该函数。
鼠标点在函数UserAssocSet,按键盘”X”:
除了当前的CAssocHandler类还有一个比较可疑的函数_SetAssociation,仔细分析他的代码与刚才的MakeDefaultPrive函数类似:
我们再继续看谁调用了SetAssociation函数:
这里是不是看到很眼熟的函数:
CApplicationAssociationRegistration::_SetAppAsDefault(),google chrome浏览器就是调用的这个com类函数设置默认浏览器的,还有一个函数
SetProgIdAsDefault@?QIApplicationAssociationRegistrationInternal@@CApplicationAssociationRegistration,一个是CApplicationAssociationRegistration类,一个QIApplicationAssociationRegistrationInternal类,两个函数只有一个地方区别
我们看下IApplicationAssociationRegistrationInternal的结构定义怎样的:
发现CApplicationAssociationRegistration继承于IApplicationAssociationRegistrationInternal这个类,也就是说IApplicationAssociationRegistrationInternal,也是一个com接口,从结构看他也是继承于IUnknow:
我们可以获取其接口Id定义:
这就是intenal的id:_GUID_1c5c9d10_1225_4c97_8c51_98e1b6f0d4e0
从上我们大致能够知道其定义了:
MIDL_INTERFACE("1c5c9d10-1225-4c97-8c51-98e1b6f0d4e0")
IApplicationAssociationRegistrationInternalWin10: public IUnknown{
public:
virtual HRESULT STDMETHODCALLTYPE ClearUserAssociations(void) = 0;
virtual HRESULT STDMETHODCALLTYPE SetProgIdAsDefault(
/* [string][in] */ __RPC__in_string LPCWSTR pszAppRegistryName,
/* [string][in] */ __RPC__in_string LPCWSTR pszSet,
/* [in] */ ASSOCIATIONTYPE atSetType) = 0;
virtual HRESULT STDMETHODCALLTYPE SetAppAsDefault(
/* [string][in] */ __RPC__in_string LPCWSTR pszAppRegistryName,
/* [string][in] */ __RPC__in_string LPCWSTR pszSet,
/* [in] */ ASSOCIATIONTYPE atSetType) = 0;
// Only use this method. We can not gurantee other method is valid.
virtual HRESULT STDMETHODCALLTYPE SetAppAsDefaultAll(
/* [string][in] */ __RPC__in_string LPCWSTR pszAppRegistryName) = 0;
virtual HRESULT STDMETHODCALLTYPE QueryAppIsDefault(
/* [string][in] */ __RPC__in_string LPCWSTR pszQuery,
/* [in] */ ASSOCIATIONTYPE atQueryType,
/* [in] */ ASSOCIATIONLEVEL alQueryLevel,
/* [string][in] */ __RPC__in_string LPCWSTR pszAppRegistryName,
/* [out] */ __RPC__out BOOL *pfDefault) = 0;
virtual HRESULT STDMETHODCALLTYPE QueryAppIsDefaultAll(
/* [in] */ ASSOCIATIONLEVEL alQueryLevel,
/* [string][in] */ __RPC__in_string LPCWSTR pszAppRegistryName,
/* [out] */ __RPC__out BOOL *pfDefault) = 0;
virtual HRESULT STDMETHODCALLTYPE QueryCurrentDefault(
/* [string][in] */ __RPC__in_string LPCWSTR pszQuery,
/* [in] */ ASSOCIATIONTYPE atQueryType,
/* [in] */ ASSOCIATIONLEVEL alQueryLevel,
/* [string][out] */ __RPC__deref_out_opt_string LPWSTR *ppszAssociation) = 0;
virtual HRESULT STDMETHODCALLTYPE GetDefaultBrowserInfo(
ASSOCIATIONTYPE atQueryType,
LPWSTR * ppszAssociation) = 0;
virtual HRESULT STDMETHODCALLTYPE RestoreDefaultBrowserContractRegistration(void) = 0;
virtual HRESULT STDMETHODCALLTYPE IsBrowserAssociation(LPCWSTR, int *) = 0;
virtual HRESULT STDMETHODCALLTYPE ExportUserAssociations(LPCWSTR) = 0;
virtual HRESULT STDMETHODCALLTYPE ApplyUserAssociations(LPCWSTR) = 0;
virtual HRESULT STDMETHODCALLTYPE UpdateProtocolCapabilityCache(LPCWSTR, int) = 0;
};
我们要设置默认浏览器只需调用SetAppAsDefault这个函数,第一个是参数是pszAppRegistryName,第二个参数”https” 或者 “https”,第三个参数SetType,这个在微软sdk中有定义AT_URLPROTOCOL = 1,关键是第一个参数如何填写,在之前我们区分函数不同时函数内部函数:
_GetManifestedAppAssociation(a3, a4, AL_EFFECTIVE, a2, (unsigned __int16 **)&pv);
这个函数的作用就是判断第一个参数,我们进去看下:
v7 = _GetAppPathKey(a4, a3, &hKey); 传进去第一个参数的值,然后返回hKey,我们大概能想到这个一定是打开了什么注册表,再次进去
打开的是HKEY_CURRENT_USER或者HKEY_LOCAL_MACHINE
嗯?是不是发现了什么?对,L”SOFTWARE\\RegisteredApplications”这个注册表路径,我们去看看注册表信息
L“Google Chrome”这就是他要打开的路径,也就是上述函数的第一个参数。
下面我们可以具体写代码了,并且验证下该想法:
HRESULT SetAppAsDefault(const std::wstring& app_name){
IApplicationAssociationRegistration* pAAR;
HRESULT hr = CoCreateInstance(
CLSID_ApplicationAssociationRegistration,
NULL,
CLSCTX_INPROC,
__uuidof (IApplicationAssociationRegistration),
(void**)&pAAR);
if (SUCCEEDED(hr)) {
IApplicationAssociationRegistrationInternal *Paari = NULL;
HRESULT hr = pAAR->QueryInterface(
__uuidof(IApplicationAssociationRegistrationInternal),
(void**)&pAARI);
if (SUCCEEDED(hr)) {
hr = pAARI->SetAppAsDefault(
L"Goole Chrome",
L"http",
AT_URLPROTOCOL);
hr = pAARI->SetAppAsDefault(
L"Goole Chrome",
L"https",
AT_URLPROTOCOL);
pAARI->Release();
}
}
return hr;
}
我们可以看下运行结果:
运行中也不会弹出systemsetting.exe,直接就设置了,当然如果你再继续更加深入地话就去分析hash算法,这个不是今天的讨论的范围,留着读者们去分析,win8的分析过程与此一样,至此本文结束。