存档

‘技术心得’ 分类的存档
232 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);
}

阅读全文…

技术心得

184 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系统下驱动签名指南
设备和驱动程序安装

技术心得

816 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?

技术心得 ,

440 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

阅读全文…

技术心得 ,

730 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

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

阅读全文…

技术心得 ,

396 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 来实现, 这里就不做详细的介绍了.

技术心得

504 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.

阅读全文…

技术心得 , , ,

1,087 views

在 Windows下用 Visual studio 编译 OpenSSL (VC6 VS2003-VS2010)

2010年12月11日

我下载的 openssl 的源代码包是 openssl-1.0.0c.tar.gz, 版本 1.0.0c.

1. 安装 ActivePerl, 因为初始化的时候, 需要用到 perl 解释器

2. 使用 VS2005 下的 Visual Studio 2005 Command Prompt 进入控制台模式 (这个模式会自动设置各种环境变量)

3. 解压缩 openssl 源代码包, 进入 openssl 的根目录, 如 c:\openssl-1.0.0c

4. perl configure VC-WIN32
必须在 c:\openssl-1.0.0c 目录下执行该命令, 否则找不到 Configure 文件, 这时就必须指定完整的 Configure 文件路径.

5. ms\do_ms
在 c:\openssl-1.0.0c 目录下执行 ms\do_ms 命令

6. 在正式编译之前,
(1). 找到 c:\openssl-1.0.0c\ms\ntdll.mak 文件, 用记事本打开, 搜索 “/MD” 字符串, 替换成 “/MT”, 这将导致静态链接 C 运行时, 省却你要在目标机器上安装 VC 再发行包等等让人抓狂的事.

(2) 找到 c:\openssl-1.0.0c\e_os.h 文件, 用记事本打开, 找到

#  if !defined(OPENSSL_USE_IPV6)
#    if defined(AF_INET6) && !defined(OPENSSL_SYS_BEOS_BONE) && !defined(NETWARE_CLIB)
#      define OPENSSL_USE_IPV6 1
#    else
#      define OPENSSL_USE_IPV6 0
#    endif
#  endif

替换成

#if !defined(_MSC_VER) || (defined(_MSC_VER) && _MSC_VER > 1200 )
#  if !defined(OPENSSL_USE_IPV6)
#    if defined(AF_INET6) && !defined(OPENSSL_SYS_BEOS_BONE) && !defined(NETWARE_CLIB)
#      define OPENSSL_USE_IPV6 1
#    else
#      define OPENSSL_USE_IPV6 0
#    endif
#  endif
#else
#  if !defined(OPENSSL_USE_IPV6)
#      define OPENSSL_USE_IPV6 0
#  endif
#endif

这一步的目的是, 在 VC6 下编译时, 避免使用 IPv6 特性, 因为 VC6 不支持.

(3). 找到 c:\openssl-1.0.0c\crypto\mem.c 文件, 在文件靠近顶部包含文件语句的下面, 添加如下代码

#ifndef OPENSSL_CPUID_OBJ
extern unsigned char cleanse_ctr;
#endif

7. 编译动态链接库版本
nmake -f ms\ntdll.mak

8. 编译静态链接库版本
nmake -f ms\nt.mak

完成编译后, 输出的动态链接库版本文件在 out32dll 里面, 包括应用程序的可执行文件, lib 文件和 dll 文件;
静态链接库版本文件在 out32 里面, 包括应用程序的可执行文件和 lib 文件.

技术心得, 日常琐碎

975 views

ATL Enumerator coding

2010年10月1日

template <typename EnumType, typename CollType>
HRESULT CreateSTLEnumerator(IUnknown ** ppUnk,
	IUnknown * pUnkForRelease,
	CollType & collection)
{
	HRESULT hr = E_POINTER;
	do
	{
		if (ppUnk == NULL) { break; }
		*ppUnk = NULL;

		CComObject<EnumType>* pEnum = NULL;
		HRESULT hr = CComObject<EnumType>::CreateInstance(&pEnum);

		if (FAILED(hr)) { break; }

		{
			pEnum->AddRef();
			hr = pEnum->Init(pUnkForRelease, collection);
			if (SUCCEEDED(hr)) {
				hr = pEnum->QueryInterface(ppUnk);
			}
			pEnum->Release();
		}
	} while (FALSE);
	return hr;
} // CreateSTLEnumerator

template<typename T>
class _CopyInterfaceFromCComPtr
{
public:
	typedef T * LPINTERFACE;

	static HRESULT copy(OUT T ** p1, IN CComPtr<T> const * p2)
	{
		ATLASSERT(p1 != NULL && p2 != NULL);
		return p2->QueryInterface(p1);
	}
	static void init(IN OUT T * * ) {}
	static void destroy(IN OUT T * * p)
	{
		if (*p) { (*p)->Release(); }
	}
};

使用方法:

STDMETHODIMP CFsFolder::EnumChildren(IEnumFsItem **ppChildren)
{
	//typedef _CopyInterface<IFsItem> _CopyFsItemInterface;
	typedef _CopyInterfaceFromCComPtr<IFsItem> _CopyFsItemInterface;

	typedef CComEnumOnSTL <
		IEnumFsItem,
		&__uuidof(IEnumFsItem),
		_CopyFsItemInterface::LPINTERFACE,
		_CopyFsItemInterface,
		std::vector< CComPtr<IFsItem> >
	> FsItemEnum;

	HRESULT hr = E_FAIL;
	do
	{
		CComPtr<IUnknown> spOut;
		hr = CreateSTLEnumerator<FsItemEnum,
                            std::vector< CComPtr<IFsItem> > > (
        			&spOut, GetUnknown(), m_vecChildren);
		if (SUCCEEDED(hr))
		{
			hr = spOut->QueryInterface(ppChildren);
		}
	} while (FALSE);
	return hr;
}

技术心得

835 views

获取文件句柄的基本信息

2010年8月5日

有时候我们想得到文件句柄的一些信息, 比如这个句柄的引用计数, 属性等等的. 我就写了下面的函数.

声明的头文件:

typedef LONG NTSTATUS;

typedef struct _OBJECT_BASIC_INFORMATION
{
	ULONG Attributes;
	ACCESS_MASK GrantedAccess;
	ULONG HandleCount;
	ULONG PointerCount;
	ULONG PagedPoolCharge;
	ULONG NonPagedPoolCharge;
	ULONG Reserved[3];
	ULONG NameInfoSize;
	ULONG TypeInfoSize;
	ULONG SecurityDescriptorSize;
	LARGE_INTEGER CreationTime;
} OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION;

NTSTATUS WINAPI _GetHandleInformation
	(
	HANDLE ProcessHandle,
	HANDLE Handle,
	POBJECT_BASIC_INFORMATION BasicInformation
	);

阅读全文…

技术心得 , , ,