[C#] 窗体穿透效果、禁用任务管理器、键盘钩子

2015/12/19 Csharp

也许你碰巧需要写个恶搞程序来整整室友呢?

先说说要的效果.让别人的电脑出现灰蒙蒙的样子,但是又能使用鼠标键盘且还算正常的使用电脑,却又不能关掉这个程序.
1.jpg

没错.就是这么的贱.

使用 VS2015+Windows10
分成几块来做

  1. 达到灰蒙蒙的界面效果且鼠标键盘能用.
  2. 让用户不能关掉程序.
  3. 留个可以偷偷关掉程序的后门.
  4. 或许可以放别人电脑上设置开机自启动(小心被砍)

首先窗口虽然灰色,但是期待的效果应该是鼠标键盘都可用.但是任务栏上不能看到这个正在运行的程序,这点很容易办到.

1. 灰色界面效果

1.关键API:SetWindowLongGetWindowLong

SetWindowLong函数,该函数用于为指定的窗口设置信息.

声明如下:

[DllImport("user32", EntryPoint = "SetWindowLong")]  
private static extern uint SetWindowLong(IntPtr hwnd, int nIndex, uint dwNewLong);  

参数说明

  • hwnd: IntPtr,为其取得消息的窗口的句柄.
  • nIndex: int类型,准备取回的信息,其常数说明自行Google.
  • dwNewLong: uint类型,由nIndex制定的窗口信息的新值.
  • 返回值: uint类型,制定数据的前一个值.

GetWindowLong函数,和上面一个相反,用于从指定窗口的结构中取得信息.且

语法如下:

[DllImport("user32", EntryPoint = "GetWindowLong")]  
private static extern uint GetWindowLong(IntPtr hwnd, int nIndex);  

参数说明参考上一个函数.

2.窗体部分.

  • 新建窗口,FromBorderStyle属性设置为None. (无边框)
  • 添加NotifyIcon控件,设置提示信息.

2.任务栏取消显示

取消在任务栏显示很容易办到.只需要设置窗体的ShowInTaskbar 属性为false即可
另外,我们还需要让窗体始终在其他窗体之上. 需设置窗体的 TopMost为true.

第一步的代码写在了源码的窗体类中,加上第二部的代码,在窗体初始化时只需做如下初始化.
方法写在了主窗体的类中.在窗体初始化时,直接调用即可.

private void Frm_Main_Load(object sender, EventArgs e)  
{  
    this.ShowInTaskbar = false;//窗体不出现在Windows任务栏中  
    CanPenetrate();  
    this.TopMost = true;//使窗体始终在其它窗体之上  
}  

步骤1的几个方法源码:

private void CanPenetrate()  
{  
    uint intExTemp = GetWindowLong(this.Handle, GWL_EXSTYLE);  
    uint oldGWLEx = SetWindowLong(this.Handle, GWL_EXSTYLE, WS_EX_TRANSPARENT | WS_EX_LAYERED);  
}  
#endregion  

#region 设置颜色和透明度的状态  
/// <summary>  
/// 设置颜色和透明度的状态  
/// </summary>  
private void SetEstate(Form Frm, object sender)  
{  
    Var_genre = ((ToolStripMenuItem)sender).Name;  
    string Tem_Str = Var_genre;  
    if (Var_genre.IndexOf('_') >= 0)  
    {  
        Var_genre = Tem_Str.Substring(0, Tem_Str.IndexOf('_'));  
    }  

    switch (Var_genre)  
    {  
        case "ToolColor":  
            {  
                Color Tem_Color=Color.Gainsboro;  
                switch (Convert.ToInt32(((ToolStripMenuItem)sender).Tag.ToString()))  
                {  
                    case 1: Tem_Color = Color.Gainsboro; break;  
                    case 2: Tem_Color = Color.DarkOrchid; break;  
                    case 3: Tem_Color = Color.RoyalBlue; break;  
                    case 4: Tem_Color = Color.Gold; break;  
                    case 5: Tem_Color = Color.LightGreen; break;  
                }  
                Frm.BackColor = Tem_Color;  
                break;  
            }  
        case "ToolClarity":  
            {  
                double Tem_Double = 0.0;  
                switch (Convert.ToInt32(((ToolStripMenuItem)sender).Tag.ToString()))  
                {  
                    case 1: Tem_Double = 0.1; break;  
                    case 2: Tem_Double = 0.2; break;  
                    case 3: Tem_Double = 0.3; break;  
                    case 4: Tem_Double = 0.4; break;  
                    case 5: Tem_Double = 0.5; break;  
                    case 6: Tem_Double = 0.6; break;  
                    case 7: Tem_Double = 0.7; break;  
                    case 8: Tem_Double = 0.8; break;  
                    case 9: Tem_Double = 0.9; break;  

                }  
                Frm.Opacity = Tem_Double;  
                break;  
            }  
        case "ToolAcquiescence":  
            {  
                Frm.BackColor = Color.Gainsboro;  
                Frm.Opacity = 0.6;  
                break;  
            }  
        case "ToolClose":  
            {  
                Close();  
                break;  
            }  

    }  
}  
#endregion  

private void ToolColor_Glass_Click(object sender, EventArgs e)  
{  
    SetEstate(this, sender);  
}  

上面三部分代码加起来的效果:
2.jpg

有个很明显的问题任务栏是看不到程序了,但是任务管理器可以直接结束掉。。。所以继续看。

3.为了不让用户关掉程序,需要禁用任务管理器。

传统的方法是检测任务管理器的进程taskmgr。隔一掉段时间检查一遍,发现就强制关掉,造成任务管理器不能用的假象。但是指标不治本。查阅了资料找到了另外一种方法,修改注册表HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System中的DisableTaskmgr键值。键值为1时,任务管理器不可用。

private void forbidTaskManager(int arg)  
{  
    RegistryKey currentUser = Registry.CurrentUser;  
    RegistryKey system = currentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Policies\System", true);  
    if (system == null)  
    {  
        system = currentUser.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Policies\System");  
    }  
    system.SetValue("DisableTaskmgr", arg, RegistryValueKind.DWord);  
    currentUser.Close();  
}  

直接在Frm_Main_Load中调用forbidTaskManager方法即可.传入1禁用任务管理器.

到这一步请先别急着测试.因为修改注册表需要首先给程序获取管理员权限.用管理员权限运行EXE文件之后.你怎么关掉呢?别坑了自己.所以,得留个后门.请继续看.

4.全局监控按键,为程序留个关闭的小后门.

关于C#全局键盘的监听请参考: C#全局键盘监听(Hook)
这里我不再多赘述.

如何使用:
将文章中的KeyboardHook类放在源码项目中.
然后安装键盘钩子.将下面三行代码 写在Frm_Main_Load

k_hook = new KeyboardHook();  
k_hook.KeyDownEvent += new KeyEventHandler(hook_KeyDown);//钩住键按下  
k_hook.Start();//安装键盘钩子  

并在主窗体中添加下面的函数.参考文章有说明.

private void hook_KeyDown(object sender, KeyEventArgs e)  
{  
    //判断按下的键(Alt + A)  
    if (e.KeyValue == (int)Keys.A && (int)Control.ModifierKeys == (int)Keys.Alt)  
    {  
        //System.Windows.Forms.MessageBox.Show("按下了指定快捷键组合");  

        forbidTaskManager(0);//设置任务管理器为可用.  
        Application.Exit();//关闭程序  
    }  
}   上面的代码是 不管何时当监听到按下(Alt + A)时,设置任务管理器可用,退出程序.  

这时可以以管理员身份运行exe文件了.

运行程序后,界面变成灰色,鼠标键盘可用,且任务栏没有程序名,任务管理器不可用便无法关闭这个程序.
3.jpg

按下alt+A后恢复正常.
4.jpg

5.拓展

只是心血来潮看到一本书时弄的这个小程序,怎么用呢?复制到室友电脑上并且设置开机启动,然后…

设置开机启动请参考:设置开机启动的方法

其实还有很多不足的地方,比如说管理员权限的问题,包括win7以后的版本,增加了这个权限机制,修改注册表的时候,需要管理员权限.如何让程序自身取得权限是个问题,毕竟手动设置还要修改属性多点几下鼠标.

另外,整整室友只是说说而已,我并没有那么做也没有这个必要,只是学习C#啦 : )