使用BDS2007的时候常常会出现闪屏结束后任务栏上的对应的按钮不消失的情况,白白占据了一个任务栏格子,唯一的办法就是重复启动它,运气好就不会出了。所以我决定解决掉它。
首先观察一下这个闪屏是怎么做才不会在任务栏显示的。
运行BDS,用Spy看下窗口样式,发现是WS_POPUP,且没有WS_EX_TOOLWINDOW。看起来是用隐藏父窗口的做法来实现的,如果隐藏父窗口失败,就可能会显示出来。我们就直接为他加上WS_EX_TOOLWINDOW样式,使它永远不显示就行了。开工
载入BDS.exe,bp SetWindowLongA。运行后观察堆栈,直到看到SplashScreen的窗口。断在这里:
代码:
2015ED8D E8 5645FEFF CALL vcl100.@Controls@TWinControl@GetHandle$qqrv 2015ED92 50 PUSH EAX 2015ED93 FF15 B4E11E20 CALL DWORD PTR DS:[@Forms@SetLayeredWindowAttributes] ; user32.SetLayeredWindowAttributes 2015ED99 EB 2C JMP SHORT vcl100.2015EDC7 2015ED9B 81E6 FFFFF7FF AND ESI,FFF7FFFF 2015EDA1 56 PUSH ESI 2015EDA2 6A EC PUSH -14 2015EDA4 8BC3 MOV EAX,EBX 2015EDA6 E8 3D45FEFF CALL vcl100.@Controls@TWinControl@GetHandle$qqrv 2015EDAB 50 PUSH EAX 2015EDAC E8 FB3DF8FF CALL <JMP.&user32.SetWindowLongA> <--这里 2015EDB1 68 85040000 PUSH 485 2015EDB6 6A 00 PUSH 0 2015EDB8 6A 00 PUSH 0 2015EDBA 8BC3 MOV EAX,EBX 2015EDBC E8 2745FEFF CALL vcl100.@Controls@TWinControl@GetHandle$qqrv 2015EDC1 50 PUSH EAX 2015EDC2 E8 CD3CF8FF CALL <JMP.&user32.RedrawWindow> 2015EDC7 5E POP ESI 2015EDC8 5B POP EBX 2015EDC9 C3 RETN
顺着堆栈往回找,可以找到bds实际调用的是vcl100.@Forms@TCustomForm@SetVisible$qqro,就是显示窗口。经过调试,这个地方只会处理闪屏窗口,正好可以拿来patch,我们在显示之前修改窗口的风格就可以了。vcl100.@Forms@TCustomForm@SetVisible$qqro>的参数是FormID之类的东西,不是实际可用的句柄,需要通过vcl100.@Controls@TWinControl@GetHandle$qqrv函数转换为实际窗口句柄,这个API已经被导入了,可以直接调用。不幸的是我们需要的SetWindowLongA却没有导入,直接动态载入好了,准备完成,可以动手了。
用topo添加一段空白空间到43CA2A,从原来调用vcl100.@Forms@TCustomForm@SetVisible$qqro跳转过去。
代码:
0040626B A1 2C404200 MOV EAX,DWORD PTR DS:[42402C] 00406270 - E9 B5670300 JMP bds.0043CA2A 00406275 A1 2C404200 MOV EAX,DWORD PTR DS:[42402C]
代码:
0043CA2A . 60 PUSHAD //保存寄存器 0043CA2B . 90 NOP 0043CA2C . E8 E359FCFF CALL <JMP.&vcl100.@Controls@TWinControl@GetHandle$qqrv> //获得实际窗口句柄。fastcall协定,不需要平衡堆栈 0043CA31 . 50 PUSH EAX 0043CA32 . 68 5ECA4300 PUSH bds.0043CA5E ; /pModule = "user32.dll" 0043CA37 . E8 D448FCFF CALL <JMP.&kernel32.GetModuleHandleA> ; \GetModuleHandleA 0043CA3C . 68 6ACA4300 PUSH bds.0043CA6A ; /ProcNameOrOrdinal = "SetWindowLongA" 0043CA41 . 50 PUSH EAX ; |hModule 0043CA42 . E8 3149FCFF CALL <JMP.&kernel32.GetProcAddress> ; \GetProcAddress 0043CA47 . 5A POP EDX 0043CA48 . 68 80000100 PUSH 10080 //偷懒直接赋值,规范些应该先GetWindowLongA,然后or 80后赋值。 0043CA4D . 6A EC PUSH -14 //GWL_EXSTYLE 0043CA4F . 52 PUSH EDX //窗口句柄 0043CA50 . FFD0 CALL EAX //SetWindowLongA 0043CA52 . 61 POPAD //恢复寄存器 0043CA53 . E8 E44FFCFF CALL <JMP.&vcl100.@Forms@TCustomForm@SetVisible$qqro> //原代码 0043CA58 .- E9 1898FCFF JMP bds.00406275 //返回 0043CA5D 00 DB 00 0043CA5E . 75 73 65 72 3>ASCII "user32.dll",0 0043CA69 . 00 ASCII 0 0043CA6A . 53 65 74 57 6>ASCII "SetWindowLongA",0