IDM 6.40.11.2 弹窗的解决思路
前言
在IDM官方下载了IDM的30天试用版。
装好后,找了一个和谐工具。运行和谐工具后,看IDM关于那里,已经是全功能版本。
美中不足的是,IDM运行一段时间,就会弹出neg窗口,说文件被修改,最好是去官网下载原版的提示。
我这的弹框如下:
文本提示信息如下:
---------------------------
IDM is corrupt
---------------------------
The main IDM executive file is damaged. It's possible that it was infected with a virus.
Please download the latest version of IDM from our web site, and install it over your current version. Just run downloaded IDM installer, and it will replace the damaged files. Do not worry, because all downloads and IDM settings will not be affected
---------------------------
确定
---------------------------
虽然是不影响使用,但是不断的要关掉neg窗口会让人疯掉的,弄不好要得强迫症:(
查看IDMan.exe的属性,看到版本为 6.40.11.2
在网上找了一下,好像没有太好的方法去掉neg窗口。
有些方法是针对旧版的,针对这个新版不好使。
实验环境
win10 21H2 + IDA7.7.220118
确定弹窗的所有者
这个neg弹窗通过spy++看,是IDM主程序弹出的,并不是调用的外部exe.
开始调试
那就只能通过无源码调试,学习研究,撸清弹窗的逻辑。
用IDA载入IDMan.exe,看调试信息,可以知道IDM6.40.11是用vs2008中的MFC写的程序。
看到IDM目录下并没有MFCDLL, 知道IDM是静态使用MFC库的。
尝试用IDAx86将IDMan.exe带着跑起来,看看有没有机会将neg窗口搞掉。
IDM并没有做明显的反调试处理,用IDA带着IDM跑起来,功能都是正常的。作者比较温和。
IDM弹出neg窗口是随机的,一天反正要弹那么几次。弹框后,按确定键,还会去IDM官网下载页面. 次数多了,真是不耐其烦啊…
还不如作者不给老百姓用,这样还省心些。
作者为的就是让和谐版的用户心烦,然后买原版省心。
关键是作者忽略了像我这种喜欢DIY的用户,这咋弄?
等了一下午,终于等到IDM弹neg窗口了, 美滋滋。
点击neg窗口的确定按钮,打开了网页,去了IDM站点。url如下:
https://www.internetdownloadmanager.com/download3.html?lng=chn2
在串参考中查找 ?lng=
找到了几个,唯一和这个url符合的字符串如下:
1
2
|
.rdata:00CF7164 aSLngS_0 db '%s?lng=%s',0 ; DATA XREF: sub_AF6E90+D51↑o
.rdata:00CF7164 ; sub_AFA1A0+D51↑o ...
|
查找 aSLngS_0 的调用者就能找到拼url的地方,这个地方和弹窗是一个逻辑流。
调用点一共有3处
debug1
1
|
.text:00AF7BE1 push offset aSLngS_0 ; "%s?lng=%s"
|
debug2
1
|
.text:00AFAEF1 push offset aSLngS_0 ; "%s?lng=%s"
|
debug3
1
|
.text:00C40B0C push offset aSLngS_0 ; "%s?lng=%s"
|
主dlg虚表位置
由debug1查找调用点,逐级回溯查找调用点,找到了主Dlg的虚表。
1
2
3
4
5
6
7
8
9
10
|
.rdata:00CF6719 align 10h
.rdata:00CF6720 dd offset ??_R4CDownloaderDlg@@6B@ ; const CDownloaderDlg::`RTTI Complete Object Locator'
.rdata:00CF6724 ; const CDownloaderDlg::`vftable'
.rdata:00CF6724 ??_7CDownloaderDlg@@6B@ dd offset sub_C689EF
.rdata:00CF6724 ; DATA XREF: sub_ADD890+40↑o
.rdata:00CF6724 ; CRecordset::~CRecordset(void)+30↑o
.rdata:00CF6728 dd offset sub_AE0980
.rdata:00CF672C dd offset nullsub_1
.rdata:00CF6730 dd offset unknown_libname_51 ; MFC 3.1-14.0 32bit
.rdata:00CF6734 dd offset ?OnFinalRelease@CWnd@@UAEXXZ ; CWnd::OnFinalRelease(void)
|
根据自己写MFC程序的经验,就在这些虚函数中找,就能看到判断程序损坏的逻辑。
看了一圈,没看到逻辑。我只看了顶层函数,子函数调用没看。应该是在那些子函数中。
换个方法调试 - 在这3处启动网页的地方下断点
这3处下断点(F2), 然后用IDA带着IDM跑起来。
等了3个小时,IDM终于弹框了。点击确认,IDA下的断点断住了。
1
2
3
4
5
6
7
8
9
|
.text:0050AEEC push ecx ; ArgList
.text:0050AEED lea edx, [esp+1F8h+var_1D0]
.text:0050AEF1 push offset aSLngS_0 ; "%s?lng=%s" // break here
.text:0050AEF6 push edx ; int
.text:0050AEF7 call sub_492F80
.text:0050AEFC add esp, 1Ch
.text:0050AEFF mov byte ptr [esp+1E4h+var_4], bl
.text:0050AF06 lea ecx, [esp+1E4h+var_1CC] ; void *
.text:0050AF0A call sub_492570
|
因为程序是动态装载的,首地址的段不一样。比较地址后面的4个字节,可以知道是debug2处的url.
如果查看代码到了非当前IP处,先回到当前IP处的代码。
选择当前线程
在栈窗口跳到ESP
跟随ESP的反汇编。
这时,就回到了断住的第一现场。如果自己F7单步过,当前现场就在断点下面2,3行。
打开函数调用链窗口
可以看到当前被断住的代码的调用链。
可知,当前代码在sub_50A1A0中。
最近调用过的一个函数是sub_491980
复制保存当前的调用链为文本,用于分析。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
.text:0050AD76 call sub_491980
.text:0050AE08 call esi ; GetDesktopWindow
.text:0050AE0B call sub_4FDB50
.text:0050AE2B call sub_4911F0
.text:0050AE43 call sub_4930C0
.text:0050AE8D call sub_4921B0
.text:0050AEA1 call _wcsrchr
.text:0050AEC1 call _wcsrchr
.text:0050AED6 call sub_492A70
.text:0050AEF7 call sub_492F80
.text:0050AF0A call sub_492570
.text:0050AF1B call sub_4911F0
.text:0050AF25 call esi ; GetDesktopWindow
.text:0050AF28 call sub_53BEB0
.text:0050AF42 call ds:SetTimer
.text:0050AF54 call sub_492570
.text:0050AF68 call sub_4911F0
.text:0050AF8B call @__security_check_cookie@4; __security_check_cookie(x)
.text:006CEBAE call @__security_check_cookie@4; __security_check_cookie(x)
.text:006CEBBB call @__security_check_cookie@4; __security_check_cookie(x)
|
回到当前IP处的代码,用图形方式查看代码实现,逻辑看的清楚。
同一逻辑的代码都用在一个图形框里面,跳转看的清楚。
鼠标左键拖动 + 鼠标中键滚动,向上看代码逻辑。咋走到这里来的?
再看看这个函数的图形调用链。
现在就可以去查fn_msg_proc_sub_B07090中,调用sub_50A1A0的调用点。
顺着调用链看代码时,发现疑似弹窗实现。
.text:0050AE0B call sub_4FDB50
.text:004FDC1E call ds:MessageBoxW
在 MessageBoxW调用处下断点看看。
1
2
|
.idata:006F972C ; int (__stdcall *MessageBoxW)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
.idata:006F972C MessageBoxW dd offset user32_MessageBoxW
|
现在有3个url字符串拼串的断点,剩下断点都是MessageBoxW的断点,已经跑起来了。等弹框。
等到弹框断点断住
晚上本本没关,等到了早上10点钟左右。IDA被弹窗操作断下来了。
等了12+小时,终于等到弹框断点被断住。作者可能不知道我这么有耐心陪他玩。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
.text:004FDC16
.text:004FDC16 loc_4FDC16:
.text:004FDC16 mov edx, [esp+34h+uType]
.text:004FDC1A push edx ; uType
.text:004FDC1B push eax ; lpCaption
.text:004FDC1C push ebp ; lpText
.text:004FDC1D push edi ; hWnd
.text:004FDC1E call ds:MessageBoxW // break here
.text:004FDC24 mov esi, eax
.text:004FDC26 mov byte ptr [esp+34h+var_4], bl
.text:004FDC2A lea ecx, [esp+34h+var_20] ; void *
.text:004FDC2E call sub_492570
.text:004FDC33 jmp short loc_4FDC55
|
这个断点在 .text:004FDB50 sub_4FDB50 proc near 函数中
偏移 = 4FDC1E - 4FDB50 = 206 = 0xCE
按x键时,可以看到这个断点为
1
|
p sub_4FDB50+CE call ds:MessageBoxW
|
栈窗口内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
|
// 栈是向上生长的
// 每多调用一个函数,地址就减少
// 当断下时,栈地址 = 063DF674
// 代码为
.text:004FDC16 mov edx, [esp+34h+uType]
.text:004FDC1A push edx ; uType
.text:004FDC1B push eax ; lpCaption
.text:004FDC1C push ebp ; lpText
.text:004FDC1D push edi ; hWnd
.text:004FDC1E call ds:MessageBoxW // 栈地址 = 063DF674
// 此时,按F7步入MessageBoxW,栈地址就变为063DF670,栈地址减小了
063DF670 004FDC24 neg_msgbox_sub_4FDB50+D4
063DF674 00010010 // 断下时的栈顶
063DF678 0394E340 debug080:0394E340
063DF67C 03949690 debug080:03949690
063DF680 00041030
063DF684 BB18CB49
063DF688 00000035
063DF68C 76B89F90 USER32:user32_GetDesktopWindow
063DF690 00000000
063DF694 00000C8A
063DF698 0394AF70 debug080:0394AF70
063DF69C 03949690 debug080:03949690
063DF6A0 063D0000
063DF6A4 0000000F
063DF6A8 BB18CB59
063DF6AC 063DF8A4 debug286:063DF8A4
063DF6B0 006E09A0 sub_5CF400:SEH_46DB50
063DF6B4 00000001
063DF6B8 0050AE10 sub_50A1A0+C70
063DF6BC 00010010
063DF6C0 0394E340 debug080:0394E340
063DF6C4 063DF700 debug286:063DF700
063DF6C8 00041030
063DF6CC BB18CB11
063DF6D0 011E7470 debug011:011E7470
063DF6D4 0393BBE0 debug080:0393BBE0
063DF6D8 063DF934 debug286:063DF934
063DF6DC 00000C50
063DF6E0 FFFFFFFE
063DF6E4 012B0000 debug030:012B0000
063DF6E8 4014006A
063DF6EC 00000000
063DF6F0 772A0000 ntdll:772A0000
063DF6F4 0394E340 debug080:0394E340
063DF6F8 00000000
063DF6FC 00000157
063DF700 204D4449
063DF704 63207369
063DF708 7572726F Crypt32:crypt32_I_CryptInstallOssGlobal+546F
063DF70C A2007470
063DF710 70747468
063DF714 772F2F3A ntdll:ntdll_RtlGetThreadPreferredUILanguages+18A
063DF718 692E7777
063DF71C 7265746E
063DF720 6474656E
063DF724 6C6E776F
063DF728 6D64616F
063DF72C 67616E61 dcomp:67616E61
063DF730 632E7265
063DF734 642F6D6F
063DF738 6C6E776F
063DF73C 3264616F
063DF740 6D74682E
063DF744 0000006C
063DF748 20656854
063DF74C 6E69616D
063DF750 4D444920
063DF754 65786520
063DF758 69747563
063DF75C 66206576
063DF760 20656C69
063DF764 64207369
063DF768 67616D61 dcomp:67616D61
063DF76C 202E6465
063DF770 73277449 mswsock:73277449
063DF774 736F7020
063DF778 6C626973
063DF77C 68742065 COMCTL32:comctl32_Ordinal234+BEE5
063DF780 69207461
063DF784 61772074
063DF788 6E692073
063DF78C 74636566 windows.storage:74636566
063DF790 77206465 COMDLG32:77206465
063DF794 20687469
063DF798 69762061
063DF79C 2E737572
063DF7A0 0A0D0A0D
063DF7A4 61656C50
063DF7A8 64206573
063DF7AC 6C6E776F
063DF7B0 2064616F
063DF7B4 20656874
063DF7B8 6574616C
063DF7BC 76207473 SETUPAPI:76207473
063DF7C0 69737265
063DF7C4 6F206E6F
063DF7C8 44492066
063DF7CC 7266204D
063DF7D0 6F206D6F
063DF7D4 77207275 COMDLG32:77207275
063DF7D8 73206265 RASAPI32:73206265
063DF7DC 2C657469
063DF7E0 646E6120
063DF7E4 736E6920
063DF7E8 6C6C6174
063DF7EC 20746920
063DF7F0 7265766F
063DF7F4 756F7920 Crypt32:crypt32_RegCreateHKCUKeyExU+8F20
063DF7F8 75632072 KERNEL32:kernel32_WakeConditionVariable+1E8C
063DF7FC 6E657272
063DF800 65762074
063DF804 6F697372
063DF808 4A202E6E
063DF80C 20747375
063DF810 206E7572
063DF814 6E776F64
063DF818 64616F6C
063DF81C 49206465
063DF820 69204D44
063DF824 6174736E
063DF828 72656C6C
063DF82C 6E61202C
063DF830 74692064 windows.storage:74692064
063DF834 6C697720
063DF838 6572206C
063DF83C 63616C70 schannel:schannel_SpLsaModeInitialize+12420
063DF840 68742065 COMCTL32:comctl32_Ordinal234+BEE5
063DF844 61642065
063DF848 6567616D
063DF84C 69662064
063DF850 2E73656C
063DF854 206F4420
063DF858 20746F6E
063DF85C 72726F77
063DF860 62202C79
063DF864 75616365 KERNEL32:kernel32_Wow64Transition+4331
063DF868 61206573
063DF86C 64206C6C
063DF870 6C6E776F
063DF874 7364616F
063DF878 646E6120
063DF87C 4D444920
063DF880 74657320 windows.storage:74657320
063DF884 676E6974 dcomp:dcomp_DllGetClassObject+31B04
063DF888 69772073
063DF88C 6E206C6C
063DF890 6220746F
063DF894 66612065
063DF898 74636566 windows.storage:74636566
063DF89C 01006465
063DF8A0 BB18CB21
063DF8A4 063DF928 debug286:063DF928
063DF8A8 006CEB9C sub_506E90:SEH_47A1A0
063DF8AC 00000000
063DF8B0 00682640 _AfxThreadEntry(void *)+DA
063DF8B4 00000000
063DF8B8 BB18C4F5
063DF8BC 006AD07B _threadstartex(x)
063DF8C0 0394E930 debug080:0394E930
063DF8C4 0394E930 debug080:0394E930
063DF8C8 00000000
063DF8CC 0078458C .rdata:const CWnd::`vftable'
063DF8D0 00000001
063DF8D4 00000000
063DF8D8 00000000
063DF8DC 00000000
063DF8E0 00000001
063DF8E4 00000000
063DF8E8 012C57D8 debug030:012C57D8
063DF8EC 00000000
063DF8F0 D378BE00
063DF8F4 00000000
063DF8F8 00000000
063DF8FC 007844FC .rdata:const CWnd::XAccessible::`vftable'
063DF900 00784570 .rdata:const CWnd::XAccessibleServer::`vftable'
063DF904 00000000
063DF908 00000000
063DF90C 00000000
063DF910 00000000
063DF914 00000000
063DF918 00000000
063DF91C 00000000
063DF920 0393BBE0 debug080:0393BBE0
063DF924 063DF8B8 debug286:063DF8B8
063DF928 063DF95C debug286:063DF95C
063DF92C 006EAD0D _AfxThreadEntry(void *):loc_6EAD0D
063DF930 00000000
063DF934 063DF96C debug286:063DF96C
063DF938 006AD055 __callthreadstartex+1B
063DF93C 011E7470 debug011:011E7470
063DF940 BB18C4AD
063DF944 006AD07B _threadstartex(x)
063DF948 0394E930 debug080:0394E930
063DF94C 0394E930 debug080:0394E930
063DF950 063DF940 debug286:063DF940
063DF954 063DF940 debug286:063DF940
063DF958 063DF9D4 debug286:063DF9D4
063DF95C 063DF9D4 debug286:063DF9D4
063DF960 006A69B0 SEH_6337D0
063DF964 BD59D039
063DF968 00000000
063DF96C 063DF978 debug286:063DF978
063DF970 006AD0FD .text:006AD0FD
063DF974 006AD07B _threadstartex(x)
063DF978 063DF988 debug286:063DF988
063DF97C 755AFA29 KERNEL32:kernel32_BaseThreadInitThunk+19
063DF980 0394E930 debug080:0394E930
063DF984 755AFA10 KERNEL32:kernel32_BaseThreadInitThunk
063DF988 063DF9E4 debug286:063DF9E4
063DF98C 77307A7E ntdll:ntdll_RtlGetAppContainerNamedObjectPath+11E
063DF990 0394E930 debug080:0394E930
063DF994 D378BF44
063DF998 00000000
063DF99C 00000000
063DF9A0 0394E930 debug080:0394E930
063DF9A4 00000000
063DF9A8 00000000
063DF9AC 00000000
063DF9B0 00000000
063DF9B4 00000000
063DF9B8 00000000
063DF9BC 00000000
063DF9C0 00000000
063DF9C4 00000000
063DF9C8 00000000
063DF9CC 063DF994 debug286:063DF994
063DF9D0 00000000
063DF9D4 063DF9EC debug286:063DF9EC
063DF9D8 7731AD20 ntdll:ntdll_wcstombs+70
063DF9DC A27F8F80
063DF9E0 00000000
063DF9E4 063DF9F4 debug286:063DF9F4
063DF9E8 77307A4E ntdll:ntdll_RtlGetAppContainerNamedObjectPath+EE
063DF9EC FFFFFFFF
063DF9F0 77328A28 ntdll:ntdll_RtlCaptureContext+E8
063DF9F4 00000000
063DF9F8 00000000
063DF9FC 006AD07B _threadstartex(x)
063DFA00 0394E930 debug080:0394E930
|
弹窗后,F7/F8 单步,回到了 sub_50A1A0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
unsigned int __cdecl fn_show_neg_wnd_sub_50A1A0(void *a1)
{
// 这个函数功能
// 给弹框内容赋值,一个一个字节的赋值,翻译字符串成宽字符串。让调试者在字符串表中,找不到现成的字符串。
// 然后弹框
// ...
v14 = v7;
DesktopWindow = GetDesktopWindow();
neg_msgbox_sub_4FDB50(DesktopWindow, v14, v24, 0x41030u);
if ( a1 )
{
v27 = -1;
fn_delete_sub_4911F0();
return 1;
}
else
{
sub_4930C0(ArgList);
...
v15 = v16;
v13 = GetDesktopWindow();
sub_53BEB0(v13, v15); // 这里疑似打开下载网页
if ( hWnd )
SetTimer(hWnd, 0x26u, 0x36EE80u, 0); // 这个定时器(0x26)应该就是neg窗口检测的定时器。
// 0x36EE80u = 3600000 = 3600秒 = 60分钟 = 1小时
// 这个定时器一个小时的,而且这个定时器1小时时间到后,也不是每次都弹框。
// 可以考虑在1小时定时器中,不干活。
LOBYTE(v27) = 0;
fn_free_sub_492570(&v16);
v27 = -1;
fn_delete_sub_4911F0();
return 0;
}
|
单步出来,到MFC线程函数操作中了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
if ( v6 )
{
v7 = v6(v1[13]); // 执行 fn_show_neg_wnd_sub_50A1A0
}
else
{
v8 = (*(int (__thiscall **)(_DWORD *))(*v1 + 80))(v1) == 0;
v9 = *v1;
if ( v8 )
v7 = (*(int (__thiscall **)(_DWORD *))(v9 + 104))(v1);
else
v7 = (*(int (__thiscall **)(_DWORD *))(v9 + 84))(v1);
}
v10 = v7;
CWnd::Detach((CWnd *)v11);
AfxEndThread(v10, 1);
}
|
通过单步,已经知道IDM是用26号定时器(触发时间1个小时)来弹neg窗口和neg URL.
neg线程函数为 fn_show_neg_wnd_sub_50A1A0
停掉IDA调试,回分析状态。
在函数列表中过滤出 fn_show_neg_wnd_sub_50A1A0
查找 fn_show_neg_wnd_sub_50A1A0的调用点
可以看到只有一处,双击过去瞧瞧。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
.text:0051D68F
.text:0051D68F loc_51D68F: ; CODE XREF: fn_msg_proc_sub_B07090+2A1A↑j
.text:0051D68F ; DATA XREF: .text:jpt_519AAA↓o
.text:0051D68F push 0 ; jumptable 00519AAA case 5355
.text:0051D691 push 0 ; unsigned int
.text:0051D693 push 0 ; StackSize
.text:0051D695 push 0 ; int
.text:0051D697 push 0 ; void *
.text:0051D699 push offset fn_show_neg_wnd_sub_50A1A0 ; unsigned int (__cdecl *)(void *)
.text:0051D69E call ?AfxBeginThread@@YGPAVCWinThread@@P6AIPAX@Z0HIKPAU_SECURITY_ATTRIBUTES@@@Z ; AfxBeginThread(uint (*)(void *),void *,int,uint,ulong,_SECURITY_ATTRIBUTES *)
.text:0051D6A3 mov eax, 1
.text:0051D6A8 wait
.text:0051D6A9 jmp loc_51F405
|
看到了不? 这里起了一个neg线程。
往上拉,看看这个起neg线程的函数的整体含义。
这是一个消息处理函数啊
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
int __thiscall fn_msg_proc_sub_B07090(int this, UINT wParam, LPARAM a3)
{
// CWnd::OnCommand 处理
LPARAM v4; // ecx
// ...
// 消息ID = 0x14EB
case 0x14EBu:
AfxBeginThread(fn_show_neg_wnd_sub_50A1A0, 0, 0, 0, 0, 0); // 这里启动neg线程
return 1;
case 0x14ECu:
if ( sub_4E6AE0(v384, v385) )
return 1;
v247 = *(WPARAM **)(this + 492);
if ( !v247 )
return 1;
LABEL_862:
sub_4BDBF0(0x111u, *v247, 0);
return 1;
default:
return CWnd::OnCommand((CWnd *)this, wParam, lParam);
}
// ...
LABEL_862:
sub_4BDBF0(0x111u, *v247, 0);
return 1;
default:
return CWnd::OnCommand((CWnd *)this, wParam, lParam);
}
}
if ( wParam != 10239 )
return CWnd::OnCommand((CWnd *)this, wParam, lParam);
sub_4FB3F0(1);
return 1;
}
|
已经看到 // 消息ID = 0x14EB 引起建立neg线程
用图形模式看
1
2
3
4
5
6
7
8
9
10
11
12
|
loc_51D68F: ; jumptable 00519AAA case 5355
; 0x14EB is 5355
push 0
push 0 ; unsigned int
push 0 ; StackSize
push 0 ; int
push 0 ; void *
push offset fn_show_neg_wnd_sub_50A1A0 ; unsigned int (__cdecl *)(void *)
call ?AfxBeginThread@@YGPAVCWinThread@@P6AIPAX@Z0HIKPAU_SECURITY_ATTRIBUTES@@@Z ; AfxBeginThread(uint (*)(void *),void *,int,uint,ulong,_SECURITY_ATTRIBUTES *)
mov eax, 1
wait
jmp loc_51F405
|
看看谁发的消息(ID = 0x14EB or 5355)。
在 fn_msg_proc_sub_B07090 看了,只是消息处理。
消息是外部投递进来的。
一般投递消息使用postmessage, 特别是这种neg窗口的消息,咱自己写程序,也不可能用sendmessage.
查看postmessage的调用点,找到 (ID = 0x14EB or 5355)的消息投递点。
1
2
3
4
5
6
|
.idata:006F97B0 ; CODE XREF: sub_498BE0+A↑p
.idata:006F97B0 ; sub_4B4630+88↑p ...
.idata:006F97B4 ; BOOL (__stdcall *PostMessageA)(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
.idata:006F97B4 extrn PostMessageA:dword
.idata:006F97B4 ; CODE XREF: sub_498320+160↑p
.idata:006F97B4 ; sub_4AE410+249↑p ...
|
查看 PostMessageA 的所有调用。
随便去一个postmessage处,看看代码格式。
1
2
3
4
5
6
|
.text:0049846F push eax ; lParam
.text:00498470 push 14DFh ; wParam
.text:00498475 push 111h ; Msg // 这是消息ID
.text:0049847A mov eax, hWnd
.text:0049847F push eax ; hWnd
.text:00498480 call ds:PostMessageA
|
先找一下文本 push 14EBh
不好找。
将IDA中的反汇编,存成要给.asm, 再来找.
在asm中查找 14EBh,好多都不是。
还得在IDA中找找PostMessageA, 找到一处,就看看上面的MSG ID 是不是 14EBh
这样找也不行啊,好多ID是用参数传进来的。并不是一个立即数。
找找settimer, ID = 0x26
找到一个包装过的设置定时器的函数fn_setimer_sub_4B4B20,在这个函数中的SetTimer处下断点等着。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
.text:004B4B20 fn_setimer_sub_4B4B20 proc near ; CODE XREF: sub_4F5860+17D↓p
.text:004B4B20 ; sub_4F5860+19C↓p ...
.text:004B4B20
.text:004B4B20 nIDEvent= dword ptr 4
.text:004B4B20 uElapse= dword ptr 8
.text:004B4B20 lpTimerFunc= dword ptr 0Ch
.text:004B4B20
.text:004B4B20 mov eax, [esp+lpTimerFunc]
.text:004B4B24 mov edx, [esp+uElapse]
.text:004B4B28 mov ecx, [ecx+20h]
.text:004B4B2B push eax ; lpTimerFunc
.text:004B4B2C mov eax, [esp+4+nIDEvent]
.text:004B4B30 push edx ; uElapse
.text:004B4B31 push eax ; nIDEvent
.text:004B4B32 push ecx ; hWnd
.text:004B4B33 call dword ptr ds:SetTimer // F2 here
.text:004B4B39 retn 0Ch
.text:004B4B39 fn_setimer_sub_4B4B20 endp
|
不好使
查找 push 26h 下面是settimer的,找到下面几处。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
loc_507C10: ; CODE XREF: sub_506E90+CBC↑j
mov eax, [esp+1E4h+var_1D0]
push eax
call esi ; GetDesktopWindow
push eax
call fn_show_dl_url_sub_53BEB0
mov eax, hWnd
add esp, 8
cmp eax, ebp
jz short loc_507C38
push ebp ; lpTimerFunc
push 36EE80h ; uElapse
push 26h ; '&' ; nIDEvent
push eax ; hWnd
call ds:SetTimer
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
loc_50AF20: ; CODE XREF: fn_show_neg_wnd_sub_50A1A0+CBC↑j
mov eax, [esp+1E4h+var_1D0]
push eax
call esi ; GetDesktopWindow
push eax
call fn_show_dl_url_sub_53BEB0
mov eax, hWnd
add esp, 8
cmp eax, ebp
jz short loc_50AF48
push ebp ; lpTimerFunc
push 36EE80h ; uElapse
push 26h ; '&' ; nIDEvent
push eax ; hWnd
call ds:SetTimer
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
; ---------------------------------------------------------------------------
loc_5105F3: ; CODE XREF: sub_50FC00+4F↑j
; DATA XREF: .text:jpt_50FC4F↓o
; try { ; jumptable 0050FC4F case 38
mov [ebp+64h+var_68], 14h
wait
push 26h ; '&' ; uIDEvent
mov edx, [esi+20h]
push edx ; hWnd
call ds:KillTimer
push 0 ; lParam
push 14BFh ; wParam
push 111h ; Msg
mov eax, [esi+20h]
push eax ; hWnd
call ds:PostMessageA
wait
; } // starts at 5105F3
mov [ebp+64h+var_68], 0FFFFFFFFh
|
IDA中跳到一个地址或label,按 G 键,在编辑框中输入label名称或地址就行。
不好查,这些断点都不是很快能触发的。
非要从根上找到判断文件被修改的实现也不是不行,不值当,没那么多时间陪作者玩。
我们要的是解决问题(去掉neg弹窗)。
那只能先暴力一点点,将找到的起neg线程的地方nop掉。管他呢,将bug修复了再说。
找到的起neg线程的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
loc_51D68F: ; jumptable 00519AAA case 5355
; 0x14EB is 5355
push 0
push 0 ; unsigned int
push 0 ; StackSize
push 0 ; int
push 0 ; void *
push offset fn_show_neg_wnd_sub_50A1A0 ; unsigned int (__cdecl *)(void *)
call ?AfxBeginThread@@YGPAVCWinThread@@P6AIPAX@Z0HIKPAU_SECURITY_ATTRIBUTES@@@Z ; AfxBeginThread(uint (*)(void *),void *,int,uint,ulong,_SECURITY_ATTRIBUTES *)
mov eax, 1
wait
jmp loc_51F405
|
给这段代码打补丁,前9句都换成nop.
打补丁时,要注意堆栈平衡。否则打过补丁的程序跑起来,过了打补丁的地方就崩了。
打过补丁的代码如下:
保存工程
将原始文件复制一份,改名保存. e.g. test_org.exe
保存补丁
可以看到打补丁成功。
退出IDA, 单独直接运行修改过的同名程序。
开始跑起来,连续跑1天(24小时),看看会出啥问题不?
neg线程前面单步调试过,除了起neg窗口和开网页去官网下载页,没看到有啥其他功能。
按理说,只是去掉了neg线程的启动,应该没啥不良反应。
等着看疗效。
测试
启动了安装好的程序建立的快捷方式,程序跑起来了。
重新下载了一个下载好的文件,可以重新下载,非常好,没有不良反应。
前几天测试,刚装好,是1,2个小时就会出一次neg框。
这2天,是一天会出个几次neg框。弹窗节奏是越来越慢了,不知道作者是不是害怕了? 还是针对调试者做了特殊处理?(看到程序中有检测调试器的操作,逻辑没细看)
现在将程序直接跑起来(2022_0505_1600),下载都正常。准备跑12+个小时,看看还出neg窗口不?
现在直接将打过补丁的IDM运行起来了,已经过去了3个小时,期间也下载了新远程文件,功能正常。
…
现在已经过了24小时了(2022_0506_1717), IDM一直开着,期间下载了几次几百MB的远程文件,功能正常。
感觉是搞定了这个bug,先这样。
END
原版作者做了一些处理,用于隐藏neg窗口的启动流程。
不过用neg窗口这种东西,惹毛了用户,被盯上了,就难看了:) 还不如直接不给用。
如果给用,但是不断骚扰使用者,用户不是愤怒(谁要是老撩拨自己,肯定不爽啊)就是好奇啊。
用户有2种:
一种是觉得这软件真好用,离不开啊…,非要死皮赖脸的用,过了一段时间,被neg窗口整疯了,直接跪了,掏钱买原版。另外一种是喜欢DIY的用户,打死都没零钱买原版,这咋整啊?
有动手能力的用户会尝试去调试bug, 这是免不了的。调试个程序,谁还不会啊? 多大点事…
最后能不能将bug搞定,看实力也看运气,啥不会我现学行不? 只要bug是可以重现的,总有解决的那一天,调试者有的是时间和思路。
总的来说,只要有需求,调试者(逆向工程师)比作者(纯正向工程师)更有耐心。
作者要考虑的多,必有疏漏。这是一定的。不是有个成语"百密一疏"么?
调试者只是针对一个bug,可以撒着欢的调试。
补充 - 2022_0518_1602
现在已经用改过的IDM 6.40.11.2 很久了. 效果非常好, 没有副作用.
看来bug修正思路是正确的.
补充 - 2022_0530_1638
有的同学非要下载链接, 还不是一个人这样要求…
鄙人承诺: 此博客文章没有下载链接.
我们都不要做违法的事情, 这是底线啊.
IDM 6.40.11.2 的这个弹窗bug, 修正的思路和实现细节已经描述的很清楚. 仅供有兴趣的同学在DIY时参考.
只是抛砖引玉, 说不定能引发您想出更好的思路.
对DIY没兴趣的同学, 只能坐等网上大神们放出新的和谐包了. 请不要在此博客再留言要下载链接.
补充 - 2022_1021_2149
今天有同学留言, 问具体咋解决的.
看到这样的留言, 整的我好无语. 这文章不是将思路和实现细节都讲清楚了么? 言无不尽啊, 不知道再补充些什么才好.
如果看了这篇文章还不知道怎么用DIY的方法来解决IDM弹窗的bug, 那说明您需要补充一些逆向方面的知识.
基本这篇文章对您现阶段没用, 大家不在一个频道.
今天这么留言的同学, 后来告诉我, 已经找到第三方的工具(e.g. 火绒, 360), 将IDM弹窗拦截了, 不用自己来DIY.
这就对了么!
每个人遇到问题, 总是要找到适合自己现阶段的方法. 别人的解决方案仅供参考. 如果找到的资料对自己没用, 就继续找资料, 做实验.
不断折腾, 总有能正常使用的那一个高光时刻.
这个补充说明, 不仅仅针对这个具体bug, 其他事情也是一样的, 总是要自己不断折腾, 才能找到适合自己现阶段的方法. 这世界上, 现成的事情是很少的, 也有些事情是钱买不来的, 需要自己动手才有吃的