注意,本文章仅供学习参考,请勿用于违法犯罪,请务必尊重极域作者的著作权
上一篇文章发出后,笔者一直在思考怎样实现动态的设置屏幕广播的全屏与窗口化,就在最近,我突然发觉,可以另辟蹊径来解决这个问题,不用关注学生端本身,而是针对屏幕广播窗口
①遍历所有窗口,找到窗口名为 屏幕广播 的窗口,获取到它的句柄
②通过这个句柄调用SetWindowLong等win32方法来改变窗口
③解除极域设下的全局键盘钩子
①获取窗口句柄
首先是要根据广播窗口名称找到它的窗口句柄,然后再使用win32方法对其进行操作(这也是笔者很想吐槽windows api的地方,随便一个外部程序都能操作一个毫不相干的程序,还有WriteProcessMomery之类的方法,windows系统的不安全性完全是自找的)
切入正题,这里我们用EnumWindows方法来遍历所有的顶级窗口(全屏广播窗口自然也在其中),然后在他的回调函数中判断是否为想找到窗口,为了节约系统资源,我们把得到的句柄存储在全局变量里,当下次再调用时,先判断这个句柄指向的窗口是不是我们想操作的即可,当然,为了能处理其他极域的全屏窗口,我还搞到的其他功能,比如黑屏安静,共享屏幕等的窗口名,当遇到这些窗口时也会处理
BOOL isWindowAim(PWCHAR name)
{
return wcsstr(name, TEXT("屏幕广播")) || wcsstr(name, TEXT("BlackScreen Window")) || wcsstr(name, TEXT("共享屏幕"));
}
BOOL CALLBACK EnumBoardWindowsProc(HWND hWnd, LPARAM lParam)
{
TCHAR szWndowText[MAX_PATH] = { 0 };
GetWindowText(hWnd, szWndowText, MAX_PATH);
if (isWindowAim(szWndowText))
{
_SetBoardStatus(lParam, hWnd);//执行破解
return FALSE;
}
return TRUE;
}
VOID SetBoardStatus(BOOL bFullScreen)
{
TCHAR szWndowText[MAX_PATH] = { 0 };
GetWindowText(hBoardWindow, szWndowText, MAX_PATH);
if (!hBoardWindow || !isWindowAim(szWndowText))
EnumWindows(EnumBoardWindowsProc, bFullScreen);
_SetBoardStatus(bFullScreen, hBoardWindow);//执行破解
}
②进行窗口化(或全屏化)
这里我们选择调用SetWindowLong,当然,那些参数不是我们自己去设置的,我们只需另外写个程序,通过GetWindowLong获取到它在全屏时的long和窗口化时的long即可,结果如下
#define LONG_FULL_SCREEN -1778384896
#define LONG_WINDOW -1764818944
接着调用SetWindowLong即可,在小窗口切换至全屏的那一步,笔者发现如果点了右上角中间那个按钮再恢复全屏会出现如下的尴尬情况
所以我们在这里再给他发个消息WM_SYSCOMMAND,让它恢复至全屏,其中wParam写SC_RESTORE即可,最终代码如下
VOID _SetBoardStatus(BOOL bFullScreen, HWND hBoardWindow)
{
if (bFullScreen)
{
SendMessage(hBoardWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
SetWindowLong(hBoardWindow, GWL_STYLE, LONG_FULL_SCREEN);
}
else
{
SetWindowLong(hBoardWindow, GWL_STYLE, LONG_WINDOW);
}
}
③解全局钩子
小窗口化后还是不能稳定奔放,因为全屏广播时还设下了全局键盘钩子,这样会导致你使用不了键盘,我们用PCHunter就可以轻易发现
同时,教师端还有一个功能,就是控制,学生端进行的操作也和这个一样,设置了一个全局键盘钩子与一个全局鼠标钩子,如果我们能把它卸载掉,就是一箭双雕
但是实验证明,这个无法作用于Ctrl Shift键,所以我们可以把解除钩子和切换小窗口的快捷键设置成这两个
废话不多说,现在我们有两种思路,一种是卸载掉这两个钩子,就像在PCHunter里面右键卸载一样,还有一种就是自己设置钩子,然后在处理函数里直接返回,不调用极域的钩子
笔者查过资料后,发现第一种方法中的找它的钩子句柄太过麻烦(好像还涉及到了系统底层,如果理解有误还望指正),对比可知,第二种简单得多
于是我们先写处理函数
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
return FALSE;
}
另外,为了防止被老师使用线下手段发现钩子无效,所以我们在设置我们自己的钩子时会保存它们的句柄到全局变量中,并在设置前先卸载它们,这样程序中就只存在两个我们设置的钩子,而且还留有它们的句柄,到时候解除会非常方便
同时,这里还加了一个功能,可以实现每隔一段时间就设置一次钩子,达到永久解除钩子的效果
核心代码如下
DWORD WINAPI UnhookThreadProc(
_In_ LPVOID lpParameter
)
{
createdHookThread = TRUE;
while (1)
{
UnhookKeyboardAndMouseForSingle();
Sleep(SET_HOOK_TIME_WAIT_IN_MILLIONS);
}
}
VOID KeepUnhook()
{
if (!createdHookThread)
{
DWORD tid;
CreateThread(NULL, NULL, UnhookThreadProc, NULL, NULL, &tid);
}
}
VOID RehookKeyboardAndMouse()
{
if (hHookKeyboard != NULL)
UnhookWindowsHookEx(hHookKeyboard);
if (hHookMouse != NULL)
UnhookWindowsHookEx(hHookMouse);
}
BOOL UnhookKeyboardAndMouseForSingle()
{
RehookKeyboardAndMouse();
hHookKeyboard = SetWindowsHookEx(WH_KEYBOARD_LL, HookProc, NULL, NULL);
if (!hHookKeyboard)
return FALSE;
hHookMouse = SetWindowsHookEx(WH_MOUSE_LL, HookProc, NULL, NULL);
if (!hHookMouse)
return FALSE;
}
还可以进行一些骚操作,比如挂起学生端,挂起的函数在ntdll.dll里的,名叫ZwSuspendProcess
最后设置快捷键破解等代码就不贴出来了,另外,这种程序一般需要对入口进行一些伪装,以免老师一打开就发现,所以这就需要靠你们的想象力了
这里有一个参考思路,设置一个假的界面,伪装成计算器之类的,然后某个特定输入框内输入特定的数字才能进入,当然,伪装方式还有很多,这些就靠自己的想象力了吧,如果能想出一套完美的解决方案,那至少也算有在信息课上开小差的资本了(手动滑稽)
含有以上功能的静态库文件已上传至csdn,链接
https://download.csdn.net/download/powerful_green/10868426
最后还有一点,信息课终究是提升自己能力的课程,笔者比较反对使用网上别人写的破解程序(当然,没有任何贬低任何人的意思),因为那对自己而言没有任何帮助的
因此,笔者希望这篇文章只是给你们一个思路上的参考,希望你们能通过自己的能力去实现你们想实现的效果,正所谓青出于蓝而胜于蓝,有的时候你们或许并不比老师差.