关键字:IDM WinXP tls1.2 openssl SSL_connect
收到官方回复,IDM v6.38b22 修复了此问题。
Q1815349357Q
起因,由于部分网站升级https加密协议至tls1.2,而WinXP的schannel不支持tls1.2,使用windows SSPI提供https能力的软件,在XP下不能访问tls1.2的网站。
因此,thunder和IDM等下载软件,在后期版本里,都引入了openssl来支持https.
至笔者发文时,IDM的最新版本是v6.38b21,其在SSPI方式失败的时候,会尝试openssl来连接。
但是笔者发现,WinXP下IDM仍不能下载部分https链接,提示:
图例Url是:https://www.appinn.com/wp-content/uploads/Appinn-icon-32.jpg
wireshark抓包,与笔者调用openssl成功下载的demo对比发现:
下载失败的那种情况,SSL握手的时候,扩展字段里缺少了server_name.
对比IDM的SSL相关实现,发现它缺少了很重要的一句,SSL_set_tlsext_host_name(sslHandle, host);
// Connect the SSL struct to our connection
if (!SSL_set_fd (sslHandle, (int)socket))
ERR_print_errors_fp (stderr);
SSL_set_tlsext_host_name(sslHandle, host);
// Initiate SSL handshake
if (SSL_connect (sslHandle) != 1)
ERR_print_errors_fp (stderr);
于是需要给IDMan.exe里补丁上这一句:
1 查壳,无壳。
2 查导入表表,没发现libssl.dll.
3 windbg Ctrl+e IDMan.exe,sxe ld libssl.dll,返回至IDMan.exe领空,发现:
LoadLibraryW(libssl.dll);
GetProcAddress(h_libssl_dll, "SSL_connect");
SSL_set_tlsext_host_name宏实际是SSL_ctrl(),但是上面初始化ssl函数的函数里,没有初始化SSL_ctrl,给它补一个:
3.1 .rdata节末尾空白处,加一个字符串pch_SSL_ctrl"SSL_ctrl";
3.2 .data节末尾空白处加一个变量存SSL_ctrl地址;
3.3 在.text节末尾空白处加:
test bl, bl
jnz L_Fail
push offset pch_SSL_ctrl ; lpProcName
mov eax, h_libssl_dll
push eax ; hModule
call esi ; GetProcAddress
mov SSL_ctrl, eax
cmp eax, edi
jz L_Fail
jmp L_Success
3.4 在初始化ssl函数的函数里,原来初始化玩最后一个SSL函数,即SSL_read后,跳到L_SSL_ctrl,
push offset aSsl_read ; "SSL_read"
mov ecx, h_libssl_dll
push ecx ; hModule
call esi ; GetProcAddress
mov SSL_read, eax
cmp eax, edi
jnz L_SSL_ctrl
这样就找到了SSL_ctrl的地址。
4. bp libssl!SSL_connect,下载https://www.appinn.com/wp-content/uploads/Appinn-icon-32.jpg,断下,返回至IDMan.exe领空;
在SSL_connect之前,我们要想办法插入SSL_set_tlsext_host_name(sslHandle, host);
SSL_set_tlsext_host_name有两个参数,明显这里能直接拿到sslHandle,而host是Url中的www.appinn.com,猜测在此处函数里的this指针里,会有这个host的存在。
SSL_set_tlsext_host_name宏实际是SSL_ctrl。
bp ws2_32!gethostbyname, 笔者这里第五次命中时会进入openssl流程,返回至IDMan.exe领空,能看到:
v5 = *(_DWORD *)(this_ + 0x184);
if ( v5 )
v6 = *(_BYTE *)(v5 + 0x15C);
else
v6 = 0;
if ( v6 )
{
host = *(char **)(v5 + 0x140);
}
else
{
v9 = *(char ***)(this_ + 0xB4);
if ( !v9 )
v9 = *(char ***)(this_ + 0xB0);
host = *v9;
}
这里就拿到了host的位置,实际后面我们用到的是poi(poi(this_ + 0xB0))。
返回一开始断SSL_connect的地方,返回IDMan领空,把call SSL_connect改为:
mov ecx, [esi+3450h]
push ecx
nop
jmp L_SSL_ctrl_setServerName // 原来是call SSL_connect
在.text节末尾空白处加:
L_SSL_ctrl_setServerName proc near
mov eax, [esi+0B0h]
mov eax, [eax]
push eax ; _DWORD
push 0 ; _DWORD
push 37h ; _DWORD
push ecx ; _DWORD
call SSL_ctrl
add esp, 10h
call SSL_connect
jmp 原始call SSL_connect后一句
L_SSL_ctrl_setServerName endp
测试OK ~
PS:
1.汇编代码可以用VS 里 __asm{ xxx xxx}, 反编译后拿到。
2.jmp等的opcode自己构造,目标地址-原始地址-5,如果是负值用补码;
3.用IDA patch的,菜单Edit -> Patch program -> Change byte,完成后Apply patches to input file...
4.笔者爱用WinXP,有意见统一回复,你XX.
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-5-4 21:36
被囧囧编辑
,原因: 隐私