如何防止屏幕截图

10

我有一个需求,希望我正在开发的应用程序可以防止用户轻易地截取屏幕内容。

我已经说明了完全防止此类情况发生是不可行的,但我正在寻找一些方法来增加一些障碍。

我正在使用C#/.NET 2.0和WinForms。


5
lol. 客户的要求总是那么好笑 ;) - bruno conde
5
请允许我为您翻译:在用户的键盘上插入螺丝刀并按下打印屏幕按钮。:) 我开玩笑,我开玩笑! - scottm
2
这是一个愚蠢的请求 - 直接说不可能,任何“障碍”都是浪费时间。 - DJ.
2
请雇佣一群威风凛凛的人站在用户身后,手持棒球棒。这样可以防止他们对您的应用程序进行任何不良操作。 - Greg
这是完全可能的,而且并不太复杂。请检查我的答案。 - scottm
显示剩余2条评论
15个回答

23

你做不到。

最好的方法是在叠加层上呈现硬件加速设备,类似于视频播放器所做的那样。基本上,你需要将整个窗口涂成蓝色,并将图形渲染到显卡上,内部显卡会用图形替换蓝色。这样做的缺点是你必须放弃使用winforms控件,且我不知道有任何一种简单的方式在.NET中实现这一点。我认为,如果你使用DirectShow.NET,他们的示例之一是将你自己的图形放入流中。

即使你做了所有这些工作,仍然可能会被截图。只需用数码相机拍摄屏幕即可。


2
数字相机加1分。用户可能在阁楼上有一台旧的拍立得相机。如果用户使用玩具画板捕捉屏幕,则获得额外奖励分数 :-) - Leonel
这会防止我从模拟另一个程序的机器上截屏吗? - Diego Castro
我猜你是指虚拟机。我不知道它们是如何实现的。虽然这种方法可能有效,但我怀疑它是否可行。此外,似乎Windows 7渲染模式意味着这已不再可能。 - FryGuy
至少下面的答案提供了相对清晰的方法来使屏幕截图更加困难。 - Ben Bryant

9

来自这里:

A. Windows使用注册热键实现Print Screen。Windows使用预定义的热键IDHOT_SNAPDESKTOP和IDHOT_SNAPWINDOW来处理Print Screen。它们对应于Print Screen(捕获整个屏幕)和Alt+Print Screen(只捕获活动窗口)。要禁用这些功能,您只需注册热键即可,这会导致Windows在用户按下任一热键时向您的应用程序发送WM_HOTKEY消息。您可以忽略此消息以绕过默认的屏幕捕获行为。一个好的实现位置是在您的mainframe类中。


1
不会起作用。大多数屏幕截图工具都定义了自己的热键。 - Rinat Abdullin
它会停止内置的打印屏幕功能吗?那样就足够了吗? - Greg
这基本上是我的想法。禁用打印屏幕将消除大部分屏幕截图。如果他们真的想要捕捉屏幕,他们可以轻松地使用数码相机来获取它。 - z -
2
我用 slimKEYS 进行了测试,将 PrintScreen 映射到其他功能(如打开记事本),测试成功。这意味着使用 PrintScreen vkey 代码调用 RegisterHotkey 将对您起作用。我还测试过 Alt-PrintScreen。您需要同时注册两者,但这不会阻止其他应用程序进行捕获... - Martin Plante
有人能提供这个的代码示例吗?我正在尝试做这个,但我不太清楚该怎么做。 - David Brunelle

2

就此而言,这是可能的。以下是一些代码:

这将是一个由您创建的dll,然后从您的应用程序调用HookKeyboard方法。我已经测试过它,它有效。当然,如果有人用相机拍照,它无法帮助,但是,重点已经说明了。NYAH!

    namespace KeyboardHook
    {
        public class Hooker
        {

            [StructLayout(LayoutKind.Sequential)]
            public struct KBDLLHOOKSTRUCT
            {
                public int vkCode;
                public int scanCode;
                public int flags;
                public int time

;
            public int extraInfo;
        }

        public delegate int HookProc(int nCode, int wParam, IntPtr ptrKBDLLHOOKSTRUCT);


        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern IntPtr SetWindowsHookEx(int idHook, HookProc callBack, IntPtr hMod, int threadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern int CallNextHookEx(IntPtr hhk, int nCode, int wParam, IntPtr lParam);

        private static IntPtr kbh_Handle;
        private static HookProc kbh_HookProc;

        private const int VK_SNAPSHOT = 0x2C;
        private const int WM_KEYDOWN = 0x0100;
        private const int WM_SYSKEYDOWN = 0x0104;
        private const int WH_KEYBOARD_LL = 13;

        private static int LowLevelKeyboardProc(int nCode, int wParam, IntPtr lParam)
        {
            if (nCode < 0)
            {
                CallNextHookEx(kbh_Handle, nCode, wParam, lParam);
                return 0;
            }

            if (wParam == WM_KEYDOWN)
            {
                IntPtr kbdll = lParam;
                KBDLLHOOKSTRUCT kbdllstruct = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(kbdll, typeof(KBDLLHOOKSTRUCT));

                if (kbdllstruct.vkCode == VK_SNAPSHOT)
                    return -1;

            }

            return CallNextHookEx(kbh_Handle, nCode, wParam, lParam);
        }

        public static void HookKeyboard()
        {
            try
            {
                kbh_HookProc = LowLevelKeyboardProc;

                kbh_Handle = SetWindowsHookEx(WH_KEYBOARD_LL, kbh_HookProc, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);

                if (kbh_Handle != IntPtr.Zero)
                    System.Diagnostics.Debug.WriteLine(String.Format("It worked! HookHandle: {0}", kbh_Handle));
                else
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(String.Format("ERROR: {0}", ex.Message));
            }
        }
    }
}

2
当然,这对于任何100万个屏幕截图应用程序(包括Windows 7内置的应用程序)都没有任何作用,并且很可能会被偏执的杀毒软件标记为键盘记录器。所以这很好。 - bobince
我正在寻找一些方法来引入一些障碍到这个过程中。这将是一个障碍,并需要安装额外的软件。 - scottm
我有一个要求,就是我正在开发的应用程序要防止用户轻易地捕捉屏幕内容。 - FryGuy
10
等一下,你刚刚把你的班级命名为“妓女”吗? - Mizipzor

2
您可以尝试使用msipc.dll中提供的IpcProtectWindow。
[DllImport("msipc.dll", SetLastError = false, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
internal static extern int IpcProtectWindow([In] IntPtr hwnd);

从微软下载SDK

调用上述函数并提供您想要保护的表单的句柄。(Form.Handle属性)


1

在这里,你需要关注两种情况。一种是当你的窗口/应用程序有焦点时,另一种是没有焦点时。

当它没有焦点时,你不能做太多事情,即如果用户点击你的应用程序并进入桌面,键盘输入不会发送到你的应用程序,因此你永远看不到它们。在这种情况下,当你的应用程序失去焦点时,你可以将其最小化到托盘中(或者,也许,在表单上放置一个“空白”面板,以防止用户看到任何东西,这也将防止打印屏幕有用)。

在另一种情况下,当你有焦点时,捕获按键并检查它们。如果Alt键和PrintScreen键同时按下,则重置该值,以便不会发生打印屏幕。(想想看,那可能行不通。我需要测试一下才能确定。)


我已经给这个点踩了,因为信息是误导性的——尽管您在应用程序线程或其窗口失去焦点时不会接收到定向键事件方面是正确的,但记录在案的WinAPI过程RegisterHotKey允许调用者捕获键而不受焦点的影响——即“系统范围内”,这应该是OP的主要关注点。换句话说,无论窗口是否失去焦点,在这里都没有什么实际意义,当然也不是OP应该担心的事情。 - Armen Michaeli

1
你可以研究一下电影播放器是如何工作的。我相信它们会直接渲染到硬件表面(通过DirectX)。我怀疑你需要这样做。

1

这并没有真正回答问题,但请记住存在捕获屏幕的工具,并且简单的相机会破坏一切。

我的意思是好吧你“必须”,但我会(但我还年轻,还是学生,所以我不太清楚可以说什么)回答这很愚蠢。


1

看看这个新技术 - sivizion.com,它们完全防止了屏幕截图 - 没有绕过它的方法。如果有人能想出如何破解它,请在此处发布,我无法破解。我认为他们还许可他们的技术,不确定,去看看吧。


有趣,但真是个麻烦事。 - Matthew Whited

0

有一些应用程序可以捕获OpenGL和DirectX应用程序的屏幕!(取决于它们是否用于录制游戏电影) 附注:Windows Aero是DirectX

http://www.fraps.com/ 我认为那就是这个应用程序


0

您可以使用视觉密码学和利用视网膜持续时间(详见此文章以获取详细信息,以及此bit.ly/vcrypto以获取网络演示)使任何随意的屏幕截图无效。

其思想是在两个或多个随机噪声图像之间高频交替,这些图像将通过视觉持续时间结合起来显示内容。屏幕截图只会抓取一个图像,其中包含无意义的随机噪声。

这样做的代价是闪烁和诱发用户头痛,可以被拍摄屏幕的相机或了解Photoshop的非随意用户所击败,但将击败任何一种随意屏幕截图或帧捕获。

在学术意义上,可能偶尔有用!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接