知其然,知其所以然,希望大家觉得有用,大家可以用在自己程序中查看自己的程序是否被调试..同时为了更好的了解一些游戏无法用OD调试的原因
1.程序窗口句柄检测
原理:用FindWindow函数查找具有相同窗口类名和标题的窗口,如果找到就说明有OD在运行
//********************************************
//通过查找窗口类名来实现检测OllyDBG
//********************************************
function AntiLoader():Boolean;
const
OllyName=’OLLYDBG’;
var
Hwnd:Thandle;
begin
Hwnd:=FindWindow(OllyName,nil);
if Hwnd<>0 then
Result:=True
else
Result:=False;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
if AntiLoader then
MessageBox(Handle,’找到调试器!’,'提示’,MB_OK+MB_ICONINFORMATION)
else
MessageBox(Handle,’未找到调试器!’,'提示’,MB_OK+MB_ICONINFORMATION)
end;
2.用线程环境块检测
原理:用ring3级下的调试器对可执行程序进行调试时,调试器会把被调试的可执行程序作为一个子线程进行跟踪.这时被调试的可执行程序的PEB结构偏移0×02处的BeingDebugged的值为1,如果可执行程序未被调试,则值为0,所以可以利用这个值来检测程序是否被ring3级下的调试器调试
//***************************************
//使用PEB结构检测OllyDBG
//***************************************
function AntiLoader():Boolean; //检测调试器;
var
YInt,NInt:Integer;
begin
asm
mov eax,fs:[$30]
//获取PEB偏移2h处BeingDebugged的值
movzx eax,byte ptr[eax+$2]
or al,al
jz @No
jnz @Yes
@No:
mov NInt,1
@Yes:
Mov YInt,1
end;
if YInt=1 then
Result:=True;
if NInt=1 then
Result:=False;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
if AntiLoader then
MessageBox(Handle,’发现调试器!’,'提示’,MB_OK+MB_ICONINFORMATION)
else
MessageBox(Handle,’未发现调试器!’,'提示’,MB_OK+MB_ICONINFORMATION);
end;
3.用API函数IsDebuggerPresent检测
原理:操作系统将调试对象设置为在特殊环境中运行,而kernel32.dll中的API函数IsDebuggerPresent的功能是用于判断进程是否处于调试环境中,这样就可以利用这个API函数来查看进程是否在调试器中执行
//****************************************
//利用IsDebuggerPresent函数检测OllyDBG
//****************************************
function AntiLoader():Boolean;
var
isDebuggerPresent: function:Boolean;
Addr: THandle;
begin
Addr := LoadLibrary(‘kernel32.dll’);
isDebuggerPresent := GetProcAddress(Addr, ‘IsDebuggerPresent’);
if isDebuggerPresent then
Result:=True
else
Result:=False;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
if AntiLoader then
MessageBox(Handle,’发现调试器!’,'提示’,MB_OK+MB_ICONINFORMATION)
else
MessageBox(Handle,’未发现提示器!’,'提示’,MB_OK+MB_ICONINFORMATION);
end;
4.检查程序的父进程
原理:Windows操作系统下的GUI可执行程序的父进程都是explorer.exe(CUI可执行程序的父进程是CMD.exe,系统服务的父进程是Service.exe,在实际使用的时候需要根据自己的程序类型来选择父进程实现反跟踪),而正被调试器OD调试的程序的父进程是调试器的执行程序ollydbg.exe而不是别的.所以可以利用检查父进程是否为explorer.exe的方法来检测OD.
//***************************************************
//检查父进程来检测OllyDBG
//***************************************************
function AntiLoader():Boolean;
const
ParentName=’\EXPLORER.EXE’;
var
hSnap,hProcess:THandle;
szBuffer:array[0..MAX_PATH] of char;
FileName:array[0..MAX_PATH] of char;
Process32:PROCESSENTRY32;
LoopFlag:BOOL;
begin
////得到所有进程的列表快照
hSnap:=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if hSnap=INVALID_HANDLE_VALUE then
begin
Result:=False;
Exit;
end;
Process32.dwSize:=sizeof(PROCESSENTRY32);
//查找进程
LoopFlag:=Process32First(hSnap,Process32);
if LoopFlag=False then
begin
CloseHandle(hSnap);
Result:=False;
Exit;
end;
while Integer(LoopFlag)<>0 do
begin
if Process32.th32ProcessID=GetCurrentProcessId() then
begin
hProcess:=OpenProcess(PROCESS_ALL_ACCESS,FALSE,Process32.th32ParentProcessID);
if hProcess<>0 then
begin
if GetModuleFileNameEx(hProcess,0,FileName,MAX_PATH)<>0 then
begin
//取得系统目录
GetWindowsDirectory(szBuffer,MAX_PATH);
//合并系统目录和\EXPLORER.EXE
StrCat(szBuffer,ParentName);
//转换成大写以后比较当前调试程序的进程是否为父进程
if UpperCase(String(FileName))<>UpperCase(String(szBuffer)) then
Result:=True
else
Result:=False;
end;
end
else
Result:=False;
end;
LoopFlag:=Process32Next(hSnap,Process32);
end;
CloseHandle(hSnap);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
if AntiLoader then
MessageBox(Handle,’发现调试器!’,'提示’,MB_OK+MB_ICONINFORMATION)
else
MessageBox(Handle,’未发现调试器!’,'提示’,MB_OK+MB_ICONINFORMATION)
end;
5.检查STARTUPINFO结构
原理:Windows操作系统中的explorer.exe创建进程的时候会把STARTUPINFO结构中的值设为0,而非explorer.exe创建进程的时候会忽略这个结构中的值,也就是结构中的值不为0,所以可以利用这个来判断OD是否在调试程序.
/************************************************
//通过检测STARTUPINFO结构来检测OllyDbg
//************************************************
function AntiLoader():Boolean;
var
Info:STARTUPINFO;
begin
GetStartupInfo(Info);
if (Info.dwX<>0) or (Info.dwY<>0) or (Info.dwXCountChars<>0) or (Info.dwYCountChars<>0) or
(Info.dwFillAttribute<>0) or (Info.dwXSize<>0) or (Info.dwYSize<>0) then
Result:=True
else
Result:=False;
end;
procedure TMainFrm.FormCreate(Sender: TObject);
begin
if AntiLoader then
MessageBox(Handle,’发现调试器!’,'提示’,MB_OK)
else
MessageBox(Handle,’未发现调试器!’,'提示’,MB_OK);
end;
posted @
2012-12-04 17:16 寻步 阅读(649) |
评论 (0) |
编辑 收藏
分析他的Anti方式,对付进程检测/父进程检测用HideToolz对付IsDebuggerPresent什么的尝试Olly Advanced,对付TMD195的Anti尝试HanOlly对付TMD191,PeP,EC尝试PhantOm其他一些尝试HideOD,HideDebugger辅助
posted @
2012-12-04 10:12 寻步 阅读(649) |
评论 (0) |
编辑 收藏
strText.GetLength();
返回的是字符个数(不包括结尾的空字符),
要视乎你编译是UNICODE还是MBCS(多字符集)。CString 本身就是TCHAR的封装,所以你定义了UNICODE那么他就占两个字节,否则就是一个字节。
我通常使用下面代码来取得字符串的字节数:
int nBytes = (csSomeCString.GetLength() + 1) * sizeof(TCHAR);
注意:这字节数的长度还包括文件结束符.
关于getlength()获取字符串的长度问题 下面几种是不一样的
只是一些实验,每个人都可以自己做。尽管简单,但是不做实验还真是无法知道,从而实际应用的时候因此出错也说不定哦。现把我的实验结果罗列在此,仅供其他网友参考:
在MBCS设置下:
1. 以'\0'为结尾。故_T("ab\0cd\n")长度为2,_T("abcd\n")长度为5。
2. 一个英文字母占1字节,一个其他文字占2字节,故_T("abcd汉字")长度为8。_T("セβΔ尒ab汉字cd")长度为16。
UNICODE设置下:
1. 每个字符占2字节,但是GetLength返回的是纯字符数,故_T("セβΔ尒ab汉字cd")长度为10。
2. '\0'仍然是结尾,所以_T("セβ\0Δ尒ab汉字cd")的长度为2。
另外W2A是把字符串从UNICODE转化到MBCS而不是ASCII,下面的代码可以证明:
CString str=_T("セβΔ尒ab汉字cd");
USES_CONVERSION;
char *pValueTemp=W2A((LPCTSTR)str);
nLength=strlen(pValueTemp);
此时的nLength为16,说明pValueTemp为MBCS串。
posted @
2012-12-03 17:54 寻步 阅读(3751) |
评论 (0) |
编辑 收藏
摘要:对VC++编程中如何访问MFC框架下各类的成员函数进行详细介绍,包括基于Doc/View architecture和Dialog base结构。给Doc/View下访问应用程序类、框架类、文档类、视类的快速方法,对基于Dialog下如何访问对话框成员函数进行了详细阐述。介绍了回调函数中访问类成员函数的方法。
Visual C++是微软公司C++语言开发环境,它成功地将C++“可视化”为Visual C++,Microsoft基本类库为Visual C++的核心,是学好、用好Visual C++的关键。
MFC中主要包括Document/View architecture support和Dialog based两种情况,文中给出了两种情况下类间访问的详细分析。
1.
2.Dialog based结构
新建工程MyProject2,需要在自己新添加的X.cpp文件中访问对话框程序中的成员函数(X.cpp文件中的类是CX),可以采用传递指针的方法。
2.1 利用构造函数传递指针变量
在新添加的X.h文件中,添加构造函数
CX(CMyProject2Dlg *pDlg);
在相应的X.cpp文件中添加实现函数:
CX::CX(CMyProject2Dlg *pDlg)
{
m_pDlg = pDlg;
}
此处m_pDlg是在X.h中定义的类成员变量:
CMyProject2Dlg *m_pDlg;
这样通过在CMyProject2Dlg中定义的CX的指针,就可以在X.cpp中实现访问CMyProject2Dlg中的构造函数,具体操作如下:
在CMyProject2Dlg.h中添加
CX *m_pX;
在CMyProject2Dlg.cpp中添加
m_pX = new CX(this);
则实现了传递对话框指针给m_pDlg,这样就可以通过m_pDlg实现对CMyProject2Dlg中成员函数的访问了。
2.2 直接在对话框类中实现访问
采用直接在对话框创建时,在对话框显示之前初始化对话框的时候传递指针:
BOOL CMyProject2Dlg::OnInnitDialog()
{
m_pX->m_pDlg = this;
}
第二种方法较第一种方法简单,在实际应用中比较常用。
访问应用程序类成员函数,可直接应用程序类全局变量theApp,方法类似Doc/View结构下的实现。
3.回调函数中访问函数
回调函数是系统自动调用的函数,相当于该函数由系统来管理,它类似于消息处理函数,由系统调用,但其中的处理过程,需要
写代码来实现。程序中常常需要实现回调,在开发视频应用程序过程中用到了回调函数以实现视频数据的导出,当设置回调后,
只要设备处于运行状态,每一场/帧数据达到时回调函数被调用。这样就可以在回调函数中进行数据的处理。
回调函数就相当于一个中断处理函数,由系统在符合设定的条件时自动调用。为此需要做3件事:
1)声明:
2)定义:
3)设置触发条件。
下面以笔者所做的视频图像数据导出程序为例。
回调函数声明:
void WINAPI CALLBACKFUNCT (PVOID pData,IMAGEINFO pImageInfo,PVOID pUserData,ULONG Index);
视频当前图像数据指针pData,用户传递给回调函数的上下文数据pUserData,
回调函数的定义:
void WINAPI CALLBACKFUNCT (PVOID pData,IMAGEINFO pImageInfo,PVOID pUserData,ULONG Index)
{
//视频数据的处理
}
设置触发条件,也就是在函数中把回调函数名称作为一个参数,以便于系统调用。
回调函数只能声明为类的静态成员函数或全局函数,而不能声明为类的普通成员函数,因为若声明为类的普通成员函数,则系
统在调用回调函数时需要先生成类的对象,以便调用类的成员函数,然而运行时代码根本不知道如何去产生一个类的对象。
将类名定义为CCamera,下面讨论如何在回调函数中调用类的成员函数进行数据的处理,回调函数中访问类的成员函数,此时
可以设置回调函数为全局函数,定义添加如下:
CALLBACKFUNCT (PVOID pData,IMAGEINFO pImageInfo,PVOID pUserData,ULONG Index)
{
CCamera *pThis = (CCamera *)pUserData;
}
这样就可以在回调函数中采用pThis访问CCamera类中的成员函数(注意此处把类的this指针作为传递的上下文数据)。
也可以采用下列形式,首先定义一个全局变量g_pCamera,具体实现如下:
CCamera *g_pCamera = NULL;
g_pCamera = this;
这样就可以用g_pCamera实现对类的成员函数的访问。
如果回调函数被定义成类中的静态成员函数:
static void WINAPI CALLBACKFUNCT (PVOID pData,IMAGEINFO pImageInfo,PVOID pUserData,ULONG Index);
则在静态回调函数中调用类的成员函数与上述全局回调函数实现类似,但在定义一全局变量g_pCamera时需要定义一个静态的
全局变量,具体实现如下:
static CCamera *g_pCamera = NULL;
g_pCamera = this;
这样,就可以李利用静态全局变量g_pCamera访问类中的成员函数。
4.结语
给出VC++中关于MFC各类间成员函数的访问方法和回调函数中访问其他函数的技巧,希望对大家编程方面,特别是视频应用方面
的开发有所帮助。
posted @
2012-12-02 01:13 寻步 阅读(888) |
评论 (0) |
编辑 收藏
1.定义消息值,在想添加消息文件中添加代码:
#define WM_MY_MESSAGE(WM_USER + 101)
Microsoft推荐自定义消息值至少为WM_USER + 100。
2.实现消息处理函数。该函数使用WPARAM和LPARAM参数并返回LPESULT。
LPESULT CMyclass :: OnMyMessage(WPARAM wParam, LPARAM lParam)
{
//TODO:
```
return 0;
}
3.在类头文件的AFX_MSG中说明消息处理函数:
class Myclass
{...
//一般消息映射函数
protected:
// {{AFX_MSG
afx_msg void OnTimer(UINT nIDEvent);
...
afx_msg LRESULT OnMyMessage(WPARAM wParam,LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
}
4.在用户类的消息块中,使用ON_MESSAGE宏指令将消息映射到消息处理函数中。
BEGIN_MESSAGE_MAP
//{{AFX_MSG_MAP
...
ON_WM_TIMER()
...
ON_MESSAGE(WM_MY_MESSAGE,OnMyMessage)
//}}AFX_MSG_MAP()
5.按照上面的方式我们已经自定义了消息,并为该消息实现了简单的处理过程,
这里就通过发送消息获得自定义消息的响应。在要触发自定义消息地方添加代码:
::SendMessage(GetSafeHwnd(),WM_MY_MESSAGE,0L,0L);
posted @
2012-11-21 23:51 寻步 阅读(177) |
评论 (0) |
编辑 收藏
C++提供了两种字符串的表示:C风格的字符串和标准C++引入的string类类型。一般我们建议使用string类,但实际上在许多程序的情形中,我们有必要理解和使用老式的C风格字符串。
C风格的字符串起源于C语言,并在C++中继续得到支持(实际上,在标准C++之前,除了第三方字符串库类之外,它是唯一一种被支持的字符串)
字符串被存储在一个字符数组中,一般通过一个char*类型的指针来操纵它。标准C库为操纵C风格的字符串提供了一组函数。例如:
int strlen(const char*);//返回字符串的长度
int strcmp(const char*, const char*);//比较两个字符串是否相等
char* strcpy(char*, const char*);//把第二个字符串拷贝到第一个字符串中
(标准C库为标准的C++的一部分被包含在其中。)为使用这些函数。我们必须包含相关的C头文件,
#include <cstring>
指向C风格字符串的字符指针总是指向一个相关联的字符数组。即使当我们写一个字符串常量时,如:
const char *st = "The expense of spirit\n";
系统在内部也把字符串常量存储在一个字符串数组中。然后,st指向该数组的第一个元素。一般地我们用指针的算术运行来遍历C风格的字符串,每次指针增加1,直到到达终止空字符为止。例如:
while ( *st++){...}
char*类型的指针被解除引用,并且测试指向的字符是true还是false。true值是除了空字符外的任意字符。++是增加运算符,它使指针对指向数组中的下一个字符。
C++标准库提供了字符串类抽象的一个公共实现。
你希望字符串类有哪些操作呢?最小的基本行为集合出什么构成呢?
1.支持用字符序列或第二个字符串对象来初始化一个字符串对象。C风格的字符串不支持用另外一个字符串初始化一个字符串。
2.支持字符串之间的拷贝。C风格字符串通过使用库函数strcpy()来实现。
3.支持读写访问单个字符。对于C风格字符串,单个字符访问由下标操作符或直接解除指针引用来实现。
4.支持两个字符串的相等比较。对于C风格字符串,字符串比较通过库函数strcmp()来实现。
5.支持两个字符串的连接:把一个字符串接到另一个字符串上,或将两个字符串组合起来形成第三个字符串。对于C风格的字符串,连接由库函数strcat()来实现。把两个字符串连接起来形成第三个字符串的实现是,用strcpy()把一个字符串拷贝到一个新实例中,然后用strcat()把另一个字符串连接到新的实例上。
6.支持对字符串长度的查询。对于C风格字符串,字符串长度由库函数strlen()返回。
7.支持字符串是否为空的判断。对于C风格字符串,通过下面两部条件测试来完成。
char *str = 0;
//...
if(!str ||!*str)
return;
标准C++提供了支持这些操作的string类。
让我们来看string类型怎样支持这些操作。
要使用string类型,必须先包含相关的头文件:
#include<string>
...
posted @
2012-11-14 00:55 寻步 阅读(246) |
评论 (0) |
编辑 收藏
例如:const int bufSize = 512;//缓存区大小
定义bufSize是一个常量,并将其初始化为512.在程序中任何改变这个值的企图都将导致编译错误。因此它被称为是只读(read-only)。例如:
if(bufSize = 0) ...
因为常量在定义后就不能被修改,所以它必须被初始化。未初始化的常量定义将导致编译错误。const double pi;//错误:未初始化的常量
一旦一个常量被定义了,我们就不能改变与const对象相关联的值。
在实际的程序中,指向const的指针常被用作函数的形式参数。它作为一个约定来保证:被传递给函数的实际对象在函数中不会被修改。例如:
int strcmp( const char *str1, const char *str2);
posted @
2012-11-14 00:45 寻步 阅读(112) |
评论 (0) |
编辑 收藏
指针持有另一个对象的地址,使我们能够间接地操作这个对象。
指针的典型用法是构建一个链接的数据结构,例如数(tree)和链表(list),并管理在程序执行过程中动态分配的对象,以及作为函数参数类型,主要用来传递数组或大型的类对象。
每个指针都有一个相关的类型。不同数据类型的指针之间的区别不是在指针的表示上,也不在指针所持有的值(地址)上--对所有类型的指针这两方面都是相同的。不同之处在于指针所指的对象的类型上。指针的类型可以指示编译器怎样解释特定地址上内存的内容,以及该内存区域应该跨越多少内存单元。
指针可以让它的地址值增加或减少一个整数值。这类指针操作,被称为指针的算数运算。这种操作初看上去并不直观,我们总认为是数据对象的加法,而不是离散的十进制数值的加法。指针加2意味着给指针持有的地址值增加了该类型两个对象的长度。例如,假设一个char是一个字节,一个int是4个字节,double是8个字节,那么指针加2是给其持有的地址值增加2、8、还是16,完全取决于指针的类型是char、int还是double。
实际上,只有指针指向数组元素时,我们才能保证较好地运用指针的算数运算。我们不能保证三个整数变量连续存储在内存中。因此,lp+2,也可能不产生一个有效的地址,这取决于在该位置上实际存储的是什么。指针算数运算的典型用法是遍历一个数组。
posted @
2012-11-12 23:13 寻步 阅读(186) |
评论 (0) |
编辑 收藏
楼主,你好:
iterator是C++标准库(STL)中的迭代器~~~
比如你建一个链表(要记得#include <list>
#include <iostream>)
list<char> A;
再list<char>::iterator it,这样,就可以对链表进行遍历了~
其实,你可以把它理解成类似指针的东西~当然,只是用处差不多,使用方式和声明方式可是完全不同的喔~~
PS:再给你一个简单的小程序段,可以说明iterator的用处~
#include <list>
#include <iostream>
using namespace std;
void main(void)
{
int a[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
list<int> name(a,a+10);
list<int>::iterator it;
for (it = name.begin(); it != name.end(); it++)
{
cout << *it << endl;
}
}
希望能帮上你~~:) 还有不明白的可以去我的HI问我~
posted @
2012-11-01 15:38 寻步 阅读(202) |
评论 (0) |
编辑 收藏
背景色:
字体和大小:
效果:
posted @
2012-10-24 00:59 寻步 阅读(234) |
评论 (0) |
编辑 收藏