存档

作者存档
606 views

Anti-Screen Capture(Prevent Screen Captures)截屏与反截屏

2011年12月3日

1.数字图片使用类似于动画的方式显示,每次显示的是数字的一部分,当动态显示的时候人眼是可以分辨出具体数字的。但是截图的话就只能截取一部分,参考:
http://cups.cs.cmu.edu/soups/2007/posters/p147_lim.pdf

2.屏蔽系统按键:Print Screen 和 Alt + Print Screen,主要原理是注册热键的方式,参考:
http://www.vckbase.com/document/viewdoc/?id=1566

代码: Handling Hotkeys
MainFrame.h

#include "FolderFrame.h"
#include "resource.h"

////////////////
// Typical MFC Main frame window, override to disable PrintScreen.
//
class CMainFrame : public CFrameWnd {
protected:
...
   afx_msg int  OnCreate(LPCREATESTRUCT lpCreateStruct);

   // disable PrintScreen
   afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
   afx_msg LRESULT OnHotKey(WPARAM wp, LPARAM lp);
   afx_msg void OnDestroy();

   DECLARE_MESSAGE_MAP()
};

MainFrame.cpp

#include "StdAfx.h"
#include "MainFrm.h"
#include "View.h"

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
...
   // disable PrintScreen:
   ON_WM_CREATE()
   ON_WM_DESTROY()
   ON_WM_ACTIVATE()
   ON_MESSAGE(WM_HOTKEY, OnHotKey)
END_MESSAGE_MAP()
...
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
...
   RegisterHotKey(m_hWnd, IDHOT_SNAPDESKTOP, 0, VK_SNAPSHOT);
   return 0;
}

void CMainFrame::OnDestroy()
{
   UnregisterHotKey(m_hWnd, IDHOT_SNAPDESKTOP);
}

//////////////////
// Handle hotkey: should be PrintScreen or Alt-PrintScreen.
// Do nothing (bypass Windows screen capture)
//
LRESULT CMainFrame::OnHotKey(WPARAM wp, LPARAM)
{
   UNREFERENCED_PARAMETER(wp);
   return 0; // ignore
}

//////////////////
// When window is activated/deactivated, disable/enable Alt-PrintScreen.
// (IDHOT_SNAPWINDOW)
//
void CMainFrame::OnActivate(UINT nState, CWnd* pWndOther,
   BOOL bMinimized)
{
   CFrameWnd::OnActivate(nState, pWndOther, bMinimized);
   if (nState)
      RegisterHotKey(m_hWnd, IDHOT_SNAPWINDOW, MOD_ALT, VK_SNAPSHOT);
   else
      UnregisterHotKey(m_hWnd, IDHOT_SNAPWINDOW);
}

阅读全文…

技术心得

525 views

64 位 Windows 操作系统手工为驱动程序添加数字签名

2011年11月30日

MS 自从 Vista 开始在 64 位系统加载内核驱动要强制签名, 而普通开发者也许不愿或没有能力购买昂贵的 CA 数字证书.

那么解决方案就是:自己伪造签名。

过程:

  1. 打开系统的 testsigning 模式, 使得非权威 CA 发放的签名可以使用.
    bcdedit /set testsigning on
  2. 做一个签名证书出来.
    MakeCert -pe -ss PrivateCertStore -n CN=tinybrowser.net(test) tinybrowser.cer
  3. 把证书加进本机信任根 CA 中去.
    CertMgr -add tinybrowser.cer -s -r localMachine root
  4. 给驱动签名.

    Signtool sign /v /s PrivateCertStore /n tinybrowser.net(test) /t http://timestamp.verisign.com/scripts/timestamp.dll mydriver.sys

说明:

  1. mydriver.sys就是所要签名的驱动文件名称了, 这个驱动是 64 位的.
  2. MakeCert.exe, CertMgr.exe 和 Singtool.exe 在基本系统中不附带, 不过Plateform SDK, .netFramework SDK, Visual Studio里面都有, 拣自己方便弄得安一个就是.
  3. 命令中 tinybrowser.net, tinybrowser 都可以替换成你自己喜欢的, 不过反正是 fake 的, 你自己爱写什么写什么, 如果要签其他驱动, 把 mydriver.sys 换成你要的文件名就是了.
  4. 要 reboot.
  5. testsigning 打开后据说会影响到 DRM, 会使有 DRM 的玩意儿失效, 不过我等贫民还没看见什么东西用 DRM.
  6. 喜欢学习的好同学想知道原理自己去啃文档:

    Signing Drivers during Development and Test (Windows Vista and Later)

感想:

  1. 打上 testsigning 开关后重起系统屏幕四个角会出来 Test mode, 上面中间会出来版本号和 build. 看起来好像很酷, 不过还好不是 Safe Mode.
  2. 如果我自己能这么做绕过 MS 的签名机制, 病毒不是也能嘛. 那强制签名和安驱动时跳个可以被屏蔽掉的警告框也没什么区别了嘛! 噢, 对了, 所以才给你打个 Test mode 的标, 别怪我没提醒. :D
  3. 有没有给病毒或木马发证书的 CA?

延伸阅读:
64位Windows Vista 和Windows 7系统下驱动签名指南
设备和驱动程序安装

技术心得

999 views

为 QT 程序添加拖放文件到桌面的功能

2011年6月11日

QT 的例子程序 $(QTDIR)\examples\draganddrop\delayedencoding 宣称实现了这个功能, 其实没有, 根本不工作.
正确的方法如下所示:

#include <QtGui>

class Label : public QLabel
{
protected:
    void mouseMoveEvent(QMouseEvent* event)
    {
        QTemporaryFile file;
        if (file.open())
        {
            QUrl url = QUrl::fromLocalFile(file.fileName());
            file.write(text().toUtf8());
            file.close();

            QMimeData* mimeData = new QMimeData;
            mimeData->setUrls(QList<QUrl>() << url);

            QDrag* drag = new QDrag(this);
            drag->setMimeData(mimeData);
            drag->exec(Qt::CopyAction);
        }
    }
};

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    Label label;
    label.setText("foo bar");
    label.show();
    return app.exec();
}

关键点就是, 先创建一临时文件, 然后把数据写入. 最后

mimeData->setUrls(QList() << url);

将这个临时文件的路径写入 mimeData, 这样才能真正实现文件的拖放.

修改过的 delayedencoding 例子工程在这里. delayedencoding

现在, 最大的问题也是唯一的问题是,当文件很大的时候, 鼠标一按下去, 就会卡死主线程, 直到文件复制到本地临时目录后,才会恢复正常. 这期间还不能松开鼠标左键, 否则前功尽弃. 目前没找到更好的解决方案.

其他资讯:
Qt 智能指针学习
Count with me: how many smart pointer classes does Qt have?

技术心得 ,

633 views

Beginning with wxWidgets

2011年5月5日

1. 下载最近的稳定版 Current Stable Release: 2.8.12

http://sourceforge.net/projects/wxwindows/files/2.8.12

解压到 E:\wxWidgets-2.8.12\

2. 编译, 用 VS 2010 命令行.

e:
cd E:\wxWidgets-2.8.12

nmake -f makefile.vc BUILD=release UNICODE=0 RUNTIME_LIBS=static
nmake -f makefile.vc BUILD=release UNICODE=1 RUNTIME_LIBS=static
nmake -f makefile.vc BUILD=debug UNICODE=0 RUNTIME_LIBS=static
nmake -f makefile.vc BUILD=debug UNICODE=1 RUNTIME_LIBS=static

3. 设置 vs 2010 的路径

include : E:\wxWidgets-2.8.12\include
E:\wxWidgets-2.8.12\include\msvc

lib : E:\wxWidgets-2.8.12\lib\vc_lib

4. 例子

wxHello.zip

阅读全文…

技术心得 ,

955 views

用 VS 2010 玩转 QT

2011年4月27日

工作所需, 用 QT 作为开发环境, 入入门, 简单的介绍一些玩法.

1. 下载 QT 源代码包, 到其官方网站 http://qt.nokia.com/downloads 去找, 目前的版本是 4.7.2, Windows 版本的下载地址是
【Qt libraries 4.7.2 for Windows (VS 2008, 218 MB)】
源代码包的下载地址是
http://get.qt.nokia.com/qt/source/qt-everywhere-opensource-src-4.7.2.zip

2. 将下载下来的源码包解开, 放到一个目录内, 我的是 E:\works\qt-everywhere-opensource-src-4.7.2

3. 编译 QT 源码库, 跟着 http://doc.qt.nokia.com/4.7/install-win.html 页面的导引, 开始编译; 下面是个简介.

(1). 将环境变量 QTDIR 加入系统, 值为 E:\works\qt-everywhere-opensource-src-4.7.2

(2). 将 Path 环境变量追加一个(如果 path 环境变量不存在, 则创建之), 从如

…\WindowsPowerShell\v1.0\

改成如

…\WindowsPowerShell\v1.0\;%QTDIR%\bin;%QTDIR%\lib

注意要加一个分号, 追加的信息和原有信息之间不能有空格.

(3). 运行 VS2010 的命令行 (开始 -> 所有程序 -> Microsoft Visual Studio 2010 -> Visual Studio Tools -> Visual Studio 命令提示(2010) );
在这个 DOS 窗口内运行如下命令

e:
cd E:\works\qt-everywhere-opensource-src-4.7.2
configure -opensource
nmake

对于 configure 命令, 如果带上参数 -static 将会编译出静态版本的 QT 库(但这里还是带有 VC 运行时, 如果要想彻底不带运行时, 必须手工修改 %QTDIR%\mkspecs\win32-msvc2010\qmake.conf 文件, 将 QMAKE_CFLAGS_RELEASEQMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO 标记的 -MD 值改成 -MT, 将 QMAKE_CFLAGS_DEBUG 标记的 -MDd 改成 -MTd ), 但这里没有带, 所以将是 DLL 版本的;

此外, configure 命令执行过程中, 会有一个询问, “Do you accept the terms of the license?”, 当然我们要回答 Y(es) 了, 否则啥也别搞.

最后, nmake 命令的执行要经过漫长的等待, 我这里是大概 4 个小时, 终于编译出了 VS 2010 的 QT 二进制文件, 包括 debug 版本和 release 版本, 均位于 %QTDIR%\lib 文件夹.

这里啰嗦一句, 如果你想重新完全生成编译配置, 先使用命令 nmake distclean 来清除配置, 然后再运行 configure 和 nmake 命令, 这里的 configure 命令可以更精细一些, 如 configure -opensource -platform win32-msvc 等等参数带着.

4. 将 VS 2010 用到的各种 include, lib, bin 路径告诉给 VS 2010, 包括:
(1).包含文件路径:

$(QTDIR)\include

(2).库文件路径:

$(QTDIR)\lib

(3).可执行文件路径:

$(QTDIR)\lib
$(QTDIR)\bin

具体怎么设就不废话了, 看图更直观.

阅读全文…

技术心得 ,

568 views

Windows 驱动之间的通信

2011年4月24日

本文主要介绍 windows NT 驱动之间利用 CTL_CODE 如何通信. 一般来讲, 用户态的应用程序通过调用 CreateFile 和 DeviceIOControl 就可以和内核态驱动之间进行通信, 如果是内核态的一个驱动程序想和另外一个驱动程序进行类似操作又如何实现呢, 接下来会做详细的说明.

主要的思路是首先得到另外一个驱动的 handle, 再构建一个 IRP, 赋予必要的参数, 再把这个 IRP 发送给另外一个驱动, 等待另外一个驱动处理完毕后获取返回的数据.

具体代码如下:

PIRP SMBIrp;
PIO_STACK_LOCATION      irpStack;

RtlInitUnicodeString(&usDeviceToFilter, L"\Device\AdvSMBus");
status = IoGetDeviceObjectPointer(&usDeviceToFilter,
    FILE_WRITE_DATA,&SMBusObject,&g_LM87Data.SmbHc);
if( !NT_SUCCESS(status) )
{
    KdPrint(("LM87: Can't get SMBus host! "));
}
SMBIrp = IoAllocateIrp (g_LM87Data.SmbHc->StackSize, FALSE);
if(!SMBIrp)
{
    KdPrint(("LM87: Allocate irp failed! "));
}
irpStack = IoGetNextIrpStackLocation(SMBIrp);
g_LM87Data.SMBusHostData.Address = LM87_SMBUS_ADDRESS;
g_LM87Data.SMBusHostData.Offset = LM87_INTER_TEMP_REGISTER;
g_LM87Data.SMBusHostData.Data = 0x0;
irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
irpStack->Parameters.DeviceIoControl.IoControlCode      = IOCTL_SMBUS_READ_BYTE;
irpStack->Parameters.DeviceIoControl.InputBufferLength = sizeof(SMBUS_HOST);
irpStack->Parameters.DeviceIoControl.OutputBufferLength = sizeof(SMBUS_HOST);
SMBIrp->AssociatedIrp.SystemBuffer = &g_LM87Data.SMBusHostData;
IoSetCompletionRoutine (SMBIrp, LM87RequestComplete, &g_LM87Data.SyncEvent, TRUE, TRUE, TRUE);
IoCallDriver (g_LM87Data.SmbHc, SMBIrp);
KeWaitForSingleObject(&g_LM87Data.SyncEvent, Executive, KernelMode, FALSE, NULL);
KdPrint(("LM87: query result is [%X] ",g_LM87Data.SMBusHostData.Data));
result[1] = g_LM87Data.SMBusHostData.Data * 10; //compatible with DiagAnywhere
IoFreeIrp(SMBIrp);

在上面的这段代码中, 我们先用 IoGetDeviceObjectPointer 得到另外一个驱动的 handle, 然后利用 IoAllocateIrp 生成一个 IRP, 在赋完必要的参数之后通过 IoCallDriver 发送给另外一个驱动, 这里需要注意的是在 IoCallDriver 之前有 IoSetCompletionRoution 的调用, 当另外一个驱动处理完这个 IRP 之后进入它所设定的 Roution 之中, 通过一个 Event 我们就知道 IRP 已经处理完毕, 我们可以获取想要的数据了.

NTSTATUS
LM87RequestComplete (
     IN PDEVICE_OBJECT       DeviceObject,
     IN PIRP                 Irp,
     IN PVOID                Context
     )
{
    PKEVENT         Event;
    Event = (PKEVENT) Context;
    KeSetEvent (Event, IO_NO_INCREMENT, FALSE);
    return STATUS_MORE_PROCESSING_REQUIRED;
}

这样就实现了在内核态的一个驱动程序和另外一个驱动程序的通信.

另外还有一种实现的方式是通过 WorKItem 和 IoBuildDeviceIoControlRequest 来实现, 这里就不做详细的介绍了.

技术心得

1,687 views

扩展内核函数 PsSetCreateProcessNotifyRoutine 等的蹲坑数量

2011年1月22日

Windows 内核提供的 PsSetCreateProcessNotifyRoutine 等函数能够容纳的回调函数的数量是有限的,目前仅区区 8 个 “蹲位”, 被占用满了以后, 再调用 PsSetCreateProcessNotifyRoutine 等函数将会失败.

因此, 我编写了一个驱动, 用以扩展 PsSetCreateProcessNotifyRoutine 等函数能够容纳回调函数的数量.

经过扩展后, 能够容纳的回调函数是无限的, 这个驱动有五个导出函数, 其使用方法与原始函数完全相同. 列表如下.

扩展后的函数名 原始函数名
Ext_PsSetCreateProcessNotifyRoutine PsSetCreateProcessNotifyRoutine
Ext_PsSetCreateThreadNotifyRoutine PsSetCreateThreadNotifyRoutine
Ext_PsRemoveCreateThreadNotifyRoutine PsRemoveCreateThreadNotifyRoutine
Ext_PsSetLoadImageNotifyRoutine PsSetLoadImageNotifyRoutine
Ext_PsRemoveLoadImageNotifyRoutine PsRemoveLoadImageNotifyRoutine

本驱动的启动方式建议为 boot 方式, 当其他驱动使用了本驱动的服务时, 不要卸载本驱动; 另外, 本驱动在 windows 2000 系统下不能卸载, 只能通过删除服务然后重启计算机的方式卸载本驱动.

本地下载 knlcbext.zip

项目主页 https://winutilities.svn.sourceforge.net/svnroot/winutilities/knlcbext/

内核编程

647 views

How To Run Office Macros Using Automation From Visual C++ .NET

2011年1月14日

orignal Article

With Microsoft Office Automation, you can open a document or create a new document that contains a Visual Basic for Applications (VBA) macro and execute the macro at run time. This article demonstrates how to call Office macros from a Visual C++ .NET Automation client.

阅读全文…

技术心得 , , ,

682 views

Windows 系统路径环境变量

2011年1月2日
环境变量名称 实际路径
%SystemDrive% 操作系统所在的分区号。如 C:
%SystemRoot% 操作系统根目录。如 C:\WINDOWS
%windir% 操作系统根目录。如 C:\WINDOWS
%ALLUSERSPROFILE% 相当于 C:\Documents and Settings\All Users
%APPDATA% 相当于 C:\Documents and Settings\用户目录\Application Data
%ProgramFiles% 相当于 C:\Program Files
%CommonProgramFiles% 相当于 C:\Program Files\Common Files
%HOMEDRIVE% 操作系统所在的分区号。如:C:
%HOMEPATH% 相当于 C:\Documents and Settings\用户目录
%USERPROFILE% 相当于 C:\Documents and Settings\用户目录
%HOMEDRIVE% C:\ 当前启动的系统的所在分区
%system% C:\WINDOWS\SYSTEM32
%ALLUSERSPROFILE% 列出所有用户Profile文件位置。
%APPDATA% 列出应用程序数据的默认存放位置。
%CD% 列出当前目录。
%CLIENTNAME% 列出联接到终端服务会话时客户端的NETBIOS名。
%CMDCMDLINE% 列出启动当前cmd.exe所使用的命令行。
%CMDEXTVERSION% 命令出当前命令处理程序扩展版本号。
%COMPUTERNAME% 列出了计算机名。
%COMSPEC% 列出了可执行命令外壳(命令处理程序)的路径。
%DATE% 列出当前日期。
%ERRORLEVEL% 列出了最近使用的命令的错误代码。
%HOMEPATH% 列出用户主目录的完整路径。
%HOMESHARE% 列出用户共享主目录的网络路径。
%LOGONSEVER% 列出有效的当前登录会话的域名控制器名。
%NUMBER_OF_PROCESSORS% 列出了计算机安装的处理器数。
%OS% 列出操作系统的名字。(Windows XP 和 Windows 2000 列为 Windows_NT.)
%Path% 列出了可执行文件的搜索路径
%PATHEXT% 列出操作系统认为可被执行的文件扩展名。
%PROCESSOR_ARCHITECTURE% 列出了处理器的芯片架构。
%PROCESSOR_IDENTFIER% 列出了处理器的描述。
%PROCESSOR_LEVEL% 列出了计算机的处理器的型号。
%PROCESSOR_REVISION% 列出了处理器的修订号。
%PROMPT% 列出了当前命令解释器的命令提示设置。
%RANDOM% 列出界于0 和 32767之间的随机十进制数。
%SESSIONNAME% 列出连接到终端服务会话时的连接和会话名。
%TEMP% and %TMP% 列出了当前登录的用户可用应用程序的默认临时目录。
%TIME% 列出当前时间。
%USERDOMAIN% 列出了包含用户帐号的域的名字。
%USERNAME% 列出当前登录的用户的名字。
%USERPROFILE% 列出当前用户Profile文件位置。
%HOMEDRIVE% 返回连接到用户主目录的本地工作站驱动器号。基于主目录值而设置。用户主目录是在“本地用户和组”中指定的。
%HOMEPATH% 返回用户主目录的完整路径。基于主目录值而设置。用户主目录是在“本地用户和组”中指定的。
%HOMESHARE% 返回用户的共享主目录的网络路径。基于主目录值而设置。用户主目录是在“本地用户和组”中指定的。
%LOGONSERVER% 返回验证当前登录会话的域控制器的名称。
%NUMBER_OF_PROCESSORS% 指定安装在计算机上的处理器的数目。
%OS% 返回操作系统名称。Windows 2000 显示其操作系统为 Windows_NT。
%PATH% 指定可执行文件的搜索路径。
%PATHEXT% 返回操作系统认为可执行的文件扩展名的列表。
%PROCESSOR_ARCHITECTURE% 返回处理器的芯片体系结构。值:x86 或 IA64(基于 Itanium)。
%PROCESSOR_IDENTFIER% 返回处理器说明。
%PROCESSOR_LEVEL% 返回计算机上安装的处理器的型号。
%PROCESSOR_REVISION% 返回处理器的版本号。
%PROMPT% 返回当前解释程序的命令提示符设置。由 Cmd.exe 生成。
%RANDOM% 返回 0 到 32767 之间的任意十进制数字。由 Cmd.exe 生成。
%SYSTEMDRIVE% 返回包含 Windows server operating system 根目录(即系统根目录)的驱动器。
%SYSTEMROOT% 返回 Windows server operating system 根目录的位置。
%TEMP% 和 %TMP% 返回对当前登录用户可用的应用程序所使用的默认临时目录。有些应用程序需要 TEMP,而其他应用程序则需要 TMP。
%TIME% 返回当前时间。使用与 time /t 命令相同的格式。由 Cmd.exe 生成。有关 time 命令的详细信息,请参阅 Time。
%USERDOMAIN% 返回包含用户帐户的域的名称。
%USERNAME% 返回当前登录的用户的名称。
%USERPROFILE% 返回当前用户的配置文件的位置。
%WINDIR% 返回操作系统目录的位置。

例如:%windir%\drives 的实际路径就是 C:\WINDOWS\drives 目录。
所谓变量,就是指一个在不同环境中会有相对不同的值的、但在所有环境中都有相同约定的含义的量。
这些变量,可以在开始菜单-运行中输入,如输入%SystemRoot% ,系统会直接打开 C:\WINDOWS 目录。
如果你当前的系统是装在D盘的2000的话,上边这个变量的执行结果就是打开 D:\WINNT 目录了。

日常琐碎

1,086 views

从盘符得到设备名称(QueryDosDevice)

2010年12月14日
#include <string.h>
#include <Windows.h>
#include <tchar.h>
#include <stdio.h> 

void _tmain()
{
    TCHAR drv = 0;
    TCHAR cDiskSymbol[] = _T("C:"); 

    for(drv=_T('C'); drv <= _T('Z'); drv++)
    {
        cDiskSymbol[0] = drv;
        if (GetDriveType(cDiskSymbol)==DRIVE_CDROM ||
            GetDriveType(cDiskSymbol)==DRIVE_FIXED ||
            GetDriveType(cDiskSymbol)==DRIVE_REMOVABLE ||
            GetDriveType(cDiskSymbol)==DRIVE_REMOTE)
        {
            TCHAR szBuf[MAX_PATH] = { 0 };
            QueryDosDevice(cDiskSymbol, szBuf, MAX_PATH);
            _tprintf(_T("==== %s === %s  ===\n"), cDiskSymbol, szBuf);
        }
    }
}

延伸阅读:
Displaying Volume Paths
QueryDosDevice Function
Local File Systems

日常琐碎