2010年3月1日
摘要: 利用Winsock实现HTTP协议的GET请求,得到一个HTML文件信息。
阅读全文
2009年11月10日
最近搞一个协议,以实现隐蔽通道,其中需要使用CRC校验算法,由于数据位数比较少,最后决定使用位的CRC校验算法。
该算法主要就是实现一个模二运算,基本原理就是异或,移位。
模二运算的算法如下(C语言描述):
模二运算
// CrcMOD2函数///////////////////////////////////////////////////////
// 作用:模2运算
// 参数说明;
// m —— 被除数
// p —— 除数
// 返回:余数
unsigned long CrcMOD2(unsigned long m, unsigned long p)
{
int m_bits = 0, p_bits = 0, a = 1, bit = 0, flag = 0;
unsigned long m_temp = m, p_temp = p;
unsigned long remainder = 0, andData = 0x00000001;
unsigned long dividend, divisor;
// 得到被除数位数
while (m_temp)
{
m_temp /= 2;
++m_bits;
}
// 得到除数位数
while (p_temp)
{
p_temp /= 2;
++p_bits;
}
// 如果被除数位数小于除数,则直接返回被除数作为余数
if (m_bits < p_bits)
return m;
// 在此开始第一次模2运算
m_temp = m >> (m_bits - p_bits); // 被除数右移相应的位数
dividend = m_temp; // 得到第一次进行运算的被除数
divisor = p; // 得到除数
remainder = dividend ^ divisor; // 得到模2运算的结果作为余数
m_bits -= p_bits; // 第一次计算完成,减去已经计算过的位数
while (m_bits)
{
if (!flag)
{
dividend = (remainder << 1); // 上一次得到的余数作为这次被除数的高位
m_temp = m >> (m_bits - 1);
bit = andData & m_temp;
dividend |= bit; // 原被除数的某一位作为现在的被除数的最低位
}
else
{
dividend <<= 1;
m_temp = m >> (m_bits - 1);
bit = andData & m_temp;
dividend |= bit;
}
// 比较除数和被除数的最高位大小
if ((dividend & (andData << (p_bits - 1))) >= (divisor & (andData << (p_bits - 1))))
{
flag = 0;
remainder = dividend ^ divisor; // 得到模2运算的结果作为余数
}
else
{
// 需要借位
flag = 1;
}
--m_bits;
}
if (flag)
remainder = dividend;
return remainder;
}
CrcGetCode函数获原始数据和CRC生成多项式,利用模二运算得到新的数据。算法如下:
给数据加上校验码
// CrcGetCode函数///////////////////////////////////////////////////
// 作用:通过原始数据与CRC多项式,得到整个数据的编码
// 参数说明:
// originalData —— 原始数据
// crcPolynomial —— CRC生成多项式
// 返回:原始数据 + 校验码
int CrcGetCode(unsigned long originalData, unsigned long crcPolynomial)
{
unsigned long checkBits = 0, temp = crcPolynomial, m = originalData, fcs;
while (temp)
{
temp /= 2;
++checkBits; // 校验位个数加一
}
-- checkBits; // 得到校验位个数(比生成多项式少1位)
m <<= checkBits; // 得到模二运算的被除数
if (checkBits == 0)
return m;
fcs = CrcMOD2(m, crcPolynomial);
m |= fcs; // 加上FCS校验子
return m; // 返回整个编码
}
2009年8月14日
摘要: Windows上Ring3的HOOK导入表所用的类大同小异,本文改进了一个HOOK导入表的类,实现了HOOK延迟加载模块的API和FreeLibaray的HOOK的实现。欢迎大家批评指正!
阅读全文
2009年7月23日
转载自:http://www.winu.cn/space-14160-do-blog-id-8104.html
在许多关于视频的软件(如视频会议、可视电话等)开发中,都应用了视频捕获技术。微软为软件开发人员提供了一个专门用于视频捕获的VFW (Video for Windows) SDK。VFW SDK为在Windows系统中实现视频捕获提供了标准的接口,从而大大降低了程序的开发难度。由于VFW SDK只有VC和VB版,没有Delphi版,因此需要在Delphi中一一声明DLL中的各个函数和变量(可以参考MSDN中的VC的函数声明以及变量定义)。本文分3部分介绍如何利用VFW在Delphi中开发视频捕获程序。
VFW简介
VFW是微软公司1992年推出的关于数字视频的一个软件包,它能使应用程序通过数字化设备从传统的模拟视频源得到数字化的视频剪辑。VFW的一个关键思想是播放时不需要专用硬件,为了解决数字视频数据量大的问题,需要对数据进行压缩。它引进了一种叫AVI的文件标准,该标准未规定如何对视频进行捕获、压缩及播放,仅规定视频和音频该如何存储在硬盘上,以及在AVI文件中交替存储视频帧和与之相匹配的音频数据。VFW使程序员能通过发送消息或设置属性来捕获、播放和编辑视频剪辑。在Windows 9x系统中,当用户在安装VFW时,安装程序会自动地安装配置视频所需要的组件,如设备驱动程序、视频压缩程序等。
VFW主要由以下6个模块组成:
●AVICAP.DLL:包含执行视频捕获的函数,它给AVI文件的I/O处理和视频、音频设备驱动程序提供一个高级接口;
●MSVIDEO.DLL:包含一套特殊的DrawDib函数,用来处理屏幕上的视频操作;
●MCIAVI.DRV:包括对VFW的MCI命令解释器的驱动程序;
●AVIFILE.DLL:包含由标准多媒体I/O(mmio)函数提供的更高的命令,用来访问.AVI文件;
●压缩管理器(ICM):用于管理的视频压缩/解压缩的编译码器(Codec);
●音频压缩管理器ACM:提供与ICM相似的服务,适用于波形音频。
开发步骤
AVICap窗口类支持实时的视频流捕获和单帧捕获,并提供对视频源的控制。虽然MCI也提供数字视频服务(比如它为显示.AVI文件的视频提供了AVI VIDEO命令集),为视频叠加提供了Overlay命令集,但这些命令主要是基于文件的操作,它们不能满足实时地从视频缓存中取数据的要求, 对于使用没有视频叠加能力的捕获卡的PC机来说, 用MCI提供的命令集是无法捕获视频流的。而AVICap窗口类在捕获视频方面具有一定的优势,它能直接访问视频缓冲区,不需要生成中间文件,实时性很强,效率很高。而且,它还可将数字视频捕获到一个文件中。
1.创建“捕获窗”
在进行视频捕获之前必需要先创建一个“捕获窗”,并以它为基础进行所有的捕获及设置操作。“捕获窗”用AVICap窗口类的“CapCreateCaptureWindow”函数来创建,其窗口风格一般为WS_CHILD和WS_VISIBLE。
捕获窗类似于标准控件(如按钮、列表框等),并具有下列功能:
●将视频流和音频流捕获到一个AVI文件中;
●动态地同视频和音频输入器件连接或断开;
●以Overlay或Preview模式对输入的视频流进行实时显示;
●在捕获时,可指定所用的文件名并能将捕获文件的内容拷贝到另一个文件;
●设置捕获速率;
●显示控制视频源、视频格式、视频压缩的对话框;
●创建、保存或载入调色板;
●将图像和相关的调色板拷贝到剪贴板;
●将捕获的单帧图像保存为DIB格式的文件。
2.关联捕获窗和驱动程序
单独定义的一个捕获窗是不能工作的,它必需与一个设备相关联,这样才能取得视频信号。用函数CapDriverConnect可使一个捕获窗与一个设备驱动程序相关联。
3.设置视频设备的属性
通过设置TcaptureParms结构变量的各个成员变量,可以控制设备的采样频率、中断采样按键、状态行为等等。设置好TCaptureParms结构变量后,可以用函数CapCaptureSetSetup使设置生效。之后还可以用CapPreviewScale、CapPreviewRate来设置预览的比例与速度,也可以直接使用设备的默认值。
4.打开预览
利用函数CapOverlay选择是否采用叠加模式预览,这样占用系统资源小,并且视频显示速度快。然后用CapPreview启动预览功能,这时就可以在屏幕上看到来自摄像机的图像了。
通过以上4步就可以建立一个基本的视频捕获程序。但如果想自已处理从设备捕获到的视频数据,则要使用捕获窗回调函数来处理,比如一帧一帧地获得视频数据或以流的方式获得视频数据等等。
实例编程
下面以一个一帧一帧地从视频设备上捕获视频数据的Delphi程序为例,来说明每个函数的作用以及开发过程。
该程序的功能是可以在屏幕上显视捕获到的视频,并可以获得每一帧的图像数据。
新建一个工程,并将AVICAP32.PAS包含到USES中。
在Form1上放置一个TPanel控件,设Name为“gCapVideoArea”,该控件用于显示视频。再放置两个TButton控件,一个Name为“Openvideo”,另一个Name为“Closevideo”。
定义全局变量:
var
//定义捕获窗句柄
ghCapWnd: THandle;
//可以得到视频数据指针的结构变量,用于回调函数中
VideoStr: LPVIDEOHDR;
//用于设置设备属性的结构变量
CapParms: TCaptureParms;
在Name为“Openvideo”的TButton 的Click事件中写入以下代码:
procedure TForm1.OpenvideoClick(Sender: TObject);
begin
//使用Tpanel控件来创建捕获窗口
ghCapWnd := CapCreateCaptureWindow
( PChar(‘KruwoSoft'), //捕获窗口的名字
WS_CHILD or WS_VISIBLE,//窗口样式
0, //X坐标
0, //Y坐标
gCapVideoArea.Width, //窗口宽
gCapVideoArea.Height, //窗口高
gCapVideoArea.Handle, //窗口句柄
0); //一般为0
{为了能够捕获视频帧,要启动一个捕获帧回调函数VideoStreamCallBack。捕获一个视频流或当前设备状态时分别使用以下函数:
//捕获一个视频流
CapSetCallbackOnVideoStream;
//得到一个设备错误
CapSetCallbackonError;
//得到一个设备状态
CapSetCallbackOnStatus
}
//定义一个帧捕获回调函数
CapSetCallbackOnFrame (ghCapWnd,LongInt(@VideoStreamCallBack));
//将一个捕获窗口与一个设备驱程相关联,第二个参数是个序号,当系统中装有多个显视驱动程序时,其值分别依次为0到总个数
CapDriverConnect(ghCapWnd, 0);
//设置设备属性的结构变量
CapParms.dwRequestMicroSecPerFrame:=40000;
CapParms.fLimitEnabled := FALSE;
CapParms.fCaptureAudio := FALSE; // NO Audio
CapParms.fMCIControl := FALSE;
CapParms.fYield := TRUE;
CapParms.vKeyAbort := VK_ESCAPE;
CapParms.fAbortLeftMouse := FALSE;
CapParms.fAbortRightMouse := FALSE;
//使设置生效
CapCaptureSetSetup(ghCapWnd,LongInt(@CapParms),sizeof(TCAPTUREPARMS));
//设置预览时的比例
CapPreviewScale(ghCapWnd, 1);
//设置预览时的帧频率
CapPreviewRate(ghCapWnd,66);
//如果要捕获视频流,则要使用函数指定不生成文件。否则将会自动生成AVI文件
CapCaptureSequenceNoFile(ghCapWnd);
//指定是否使用叠加模式,使用为1,否则为0
CapOverlay(ghCapWnd, 1);
//打开预览
CapPreview(ghCapWnd, 1);
end;
在Name为“Closevideo”的TButton 的Click事件中写入以下代码:
procedure TForm1.ClosevideoClick(Sender: TObject);
begin
//停止捕获
capCaptureAbort(ghCapWnd);
//将捕获窗同驱动器断开
capDriverDisconnect(ghCapWnd);
end;
定义捕获帧回调函数:
function FrameCallBack(hWnd:HWND; lpVHdr:LongInt) :LongInt; stdcall;
var
DataPoint:^byte;
DibLen,RectWidth,RectHeight:integer;
begin
//转换从回调函数中得到的指针
VideoStr:=LPVIDEOHDR(lpVHdr);
//得到返回的数据大小
DibLen:=VideoStr^.dwBufferLength;
GetMem(DataPoint,64000);
//将帧数据COPY到一个内存中,注意DATAPOINT要先分配空间
CopyMemory(DataPoint,VideoStr^.lpData,Diblen);
//一些其他处理
……
end;
灵活地使用AVICap窗口类的回调函数可以满足各种不同的需求,但要注意从视频卡中捕获的视频数据的格式和图像的长宽要参考视频卡的参数。而且有些视频卡通过设置可支持多种的格式和图像长宽,所以在还原图像时要注意参考所用的视频卡的参数。
2009年6月13日
前一阶段重温了Windows中的HOOK,由此参考了《Windows via C/C++》中的示例程序——DIPS,但是我发现了一个有趣的问题。
默认情况下,链接器并不会将支持XP或Vista的manifest链接到程序上,因此,生成的应用程序运行时的控件风格是经典Windows样式,此时,DIPS小工具运行正常。
但是,当加上如下这段代码(适用于x86 CPU),问题就产生了。
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
这意味着链接器将会把新的XP或Vista的manifest链接到程序上,使应用程序具有XP或Vista的控件样式。这时,问题产生了。
这里我贴出程序的主函数代码:
int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR pszCmdLine, int) {
// Convert command-line character to uppercase.
CharUpperBuff(pszCmdLine, 1);
TCHAR cWhatToDo = pszCmdLine[0];
if ((cWhatToDo != TEXT('S')) && (cWhatToDo != TEXT('R'))) {
// An invalid command-line argument; prompt the user.
cWhatToDo = 0;
}
if (cWhatToDo == 0) {
// No command-line argument was used to tell us what to
// do; show usage dialog box and prompt the user.
switch (DialogBox(hInstExe, MAKEINTRESOURCE(IDD_DIPS), NULL, Dlg_Proc)) {
case IDC_SAVE:
cWhatToDo = TEXT('S');
break;
case IDC_RESTORE:
cWhatToDo = TEXT('R');
break;
}
}
if (cWhatToDo == 0) {
// The user doesn't want to do anything.
return(0);
}
// The Desktop ListView window is the grandchild of the ProgMan window.
HWND hWndLV = GetFirstChild(GetFirstChild(
FindWindow(TEXT("ProgMan"), NULL)));
chASSERT(IsWindow(hWndLV));
// Set hook that injects our DLL into the Explorer's address space. After
// setting the hook, the DIPS hidden modeless dialog box is created. We
// send messages to this window to tell it what we want it to do.
chVERIFY(SetDIPSHook(GetWindowThreadProcessId(hWndLV, NULL)));
// Wait for the DIPS server window to be created.
MSG msg;
GetMessage(&msg, NULL, 0, 0); // 请注意这里
// Find the handle of the hidden dialog box window.
HWND hWndDIPS = FindWindow(NULL, TEXT("Wintellect DIPS"));
// Make sure that the window was created.
chASSERT(IsWindow(hWndDIPS));
// Tell the DIPS window which ListView window to manipulate
// and whether the items should be saved or restored.
BOOL bSave = (cWhatToDo == TEXT('S'));
SendMessage(hWndDIPS, WM_APP, (WPARAM) hWndLV, bSave);
// Tell the DIPS window to destroy itself. Use SendMessage
// instead of PostMessage so that we know the window is
// destroyed before the hook is removed.
SendMessage(hWndDIPS, WM_CLOSE, 0, 0);
// Make sure that the window was destroyed.
chASSERT(!IsWindow(hWndDIPS));
// Unhook the DLL, removing the DIPS dialog box procedure
// from the Explorer's address space.
SetDIPSHook(0);
return(0);
}
看到上面代码中的GetMessage函数(加红色注释那行),该函数是在接收一个来自explorer.exe进程的消息,这个消息是在挂钩DLL注入之后,由挂钩过滤函数发送的。挂钩过滤函数代码如下:
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) {
static BOOL bFirstTime = TRUE;
if (bFirstTime) {
// The DLL just got injected.
bFirstTime = FALSE;
// Uncomment the line below to invoke the debugger
// on the process that just got the injected DLL.
// ForceDebugBreak();
// Create the DIPS Server window to handle the client request.
CreateDialog(g_hInstDll, MAKEINTRESOURCE(IDD_DIPS), NULL, Dlg_Proc);
// Tell the DIPS application that the server is up
// and ready to handle requests.
PostThreadMessage(g_dwThreadIdDIPS, WM_NULL, 0, 0);
}
return(CallNextHookEx(g_hHook, nCode, wParam, lParam));
}
明显地,这里发送了一个WM_NULL消息给DIPS进程,当使用经典样式的控件时一切安好,经调试得到的MSG结构中的各个字段为正确的值。但是加上了上面那行链接命令后,调试得到的MSG结构的字段压根就不是WM_NULL、0、0,而是一个数值为49211的消息,这样导致了DIPS主线程唤醒,随后的FindWindow可能会返回一个NULL,因为该消息并不是挂钩过滤函数的发送的消息。当然,如果在这里Sleep一下,可以得到正确的窗口句柄,我在GetMessage函数上加了一个do-while循环,结果也的确是这样,几次循环之后可以收到消息为WM_NULL的消息,且参数均为0。
但是我不明白为什么加上了一条链接命令会这样?不妨大家都试试看,我用的IDE是VS2005。
哪位高手可以来指导我一下呢?
2009年5月22日
前一阶段闲着无聊,用远程线程注入的方法把DLL注入到Explorer.exe进程实现音乐循环播放。
在DLL中的代码是这样的:
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, PVOID fImpLoad)
{
static HANDLE hThread;
static DWORD dwThreadId;
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
g_hInstDll = hInstDll;
hThread = BEGINTHREADEX(NULL, 0, ThreadProc, (PVOID)hInstDll, 0, &dwThreadId);
if (hThread == NULL)
{
return FALSE;
}
break;
case DLL_PROCESS_DETACH:
SendMessage(g_hWnd, WM_CLOSE, 0, 0);
WaitForSingleObject(hThread, INFINITE); // 这里存在死循环
CloseHandle(hThread); // 执行不到这里
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
default:
break;
}
return TRUE;
}
在DLL_PROCESS_ATTACH的时候创建了一个线程,这个线程中创建了一个隐藏的窗口,并利用MCI实现播放音乐,具体就不贴出了。然后在DLL_PROCESS_DETACH中停止和关闭设备。
但是在应用程序中,发现注入时无异常,在需要撤销DLL映射时出现了问题,程序出现了死循环,在等待远程线程执行FreeLibaray完毕时不返回了。
注意上面代码中DLL_PROCESS_DETACH的代码,SendMessage用来关闭窗口,窗口会在WM_DESTROY通知下调用PostQuitMessage(0)使得线程的消息循环退出,从而线程退出。但是WaitForSingleObject函数无法返回。
后来发现这里存在死循环,因为执行DllMain的线程是序列化的,必须等待一个线程执行完毕之后另一个线程才能执行。在SendMessage后,导致创建的线程即将退出,这时该线程会调用DllMain,并以DLL_THREAD_DETACH作为通知。可是调用SendMessage的线程调用DllMain时却还在等待即将退出的线程结束,DllMain还没有返回,因此存在了死循环。
因此,在DllMain中不适合调用WaitForSingleObject等函数来等待线程完毕。
以前都是在博客园的,发现博客园关注的是.Net技术,感觉和自己的兴趣与特长关系不大,因此申请了C++博客。
原博客园博客地址:
http://www.cnblogs.com/wz19860913/