当前位置:文档之家› OD反调试大全

OD反调试大全

OD反调试大全
OD反调试大全

调试技巧总结-原理和实现

-------------------------------------------------------------------------------------------------------

2008.8.7 shellwolf

一、前言

前段学习反调试和vc,写了antidebug-tester,经常会收到message希望交流或索要实现代码,我都没有回复。其实代码已经在编程版提供了1个版本,另其多是vc内嵌asm写的,对cracker而言,只要反下就知道了。我想代码其实意义不是很大,重要的是理解和运用。做个简单的总结,说明下实现原理和实现方法。也算回复了那些给我发Message的朋友。

部分代码和参考资料来源:

1、<<脱壳的艺术>> hawking

2、<> Angeljyt

3、https://www.doczj.com/doc/e614672613.html,

4、<<软件加密技术内幕>> 看雪学院

5、<> Peter Ferrie

我将反调试技巧按行为分为两大类,一类为检测,另一类为攻击,每类中按操作对象又分了五个小类:

1、通用调试器包括所有调试器的通用检测方法

2、特定调试器包括OD、IDA等调试器,也包括相关插件,也包括虚拟环境

3、断点包括内存断点、普通断点、硬件断点检测

4、单步和跟踪主要针对单步跟踪调试

5、补丁包括文件补丁和内存补丁

反调试函数前缀

检测攻击

通用调试器 FD_ AD_

特定调试器 FS_ AS_

断点 FB_ AB_

单步和跟踪 FT_ AT_

补丁 FP_ AP_

声明:

1、本文多数都是摘录和翻译,我只是重新组合并翻译,不会有人告侵权吧。里面多是按自己的理解来说明,可能有理解错误,或有更好的实现方法,希望大家帮忙指出错误。

2、我并没有总结完全,上面的部分分类目前还只有很少的函数甚至空白,等待大家和我一起来完善和补充。我坚信如果有扎实的基础知识,丰富的想像力,灵活的运用,就会创造出更多的属于自己的反调试。而最强的反调试,通常都是自己创造的,而不是来自别人的代码。

二、查找-通用调试器(FD_)

函数列表如下,后面会依次说明,需事先说明的是,这些反调试手段多数已家喻户晓,目前有效的不多,多数已可以通过OD的插件顺利通过,如果你想验证它们的有效性,请关闭OD 的所有反反调试插件:

bool FD_IsDebuggerPresent();

bool FD_PEB_BeingDebuggedFlag();

bool FD_PEB_NtGlobalFlags();

bool FD_Heap_HeapFlags();

bool FD_Heap_ForceFlags();

bool FD_Heap_Tail();

bool FD_CheckRemoteDebuggerPresent();

bool FD_NtQueryInfoProc_DbgPort();

bool FD_NtQueryInfoProc_DbgObjHandle();

bool FD_NtQueryInfoProc_DbgFlags();

bool FD_NtQueryInfoProc_SysKrlDbgInfo();

bool FD_SeDebugPrivilege();

bool FD_Parent_Process();

bool FD_DebugObject_NtQueryObject();

bool FD_Find_Debugger_Window();

bool FD_Find_Debugger_Process();

bool FD_Find_Device_Driver();

bool FD_Exception_Closehandle();

bool FD_Exception_Int3();

bool FD_Exception_Popf();

bool FD_OutputDebugString();

bool FD_TEB_check_in_Vista();

bool FD_check_StartupInfo();

bool FD_Parent_Process1();

bool FD_Exception_Instruction_count();

bool FD_INT_2d();

2.1 FD_IsDebuggerPresent()

对调试器来说,IsDebuggerPresent是臭名昭著的恶意函数。不多说了,它是个检测调试的api函数。实现更简单,只要调用IsDebuggerPresent就可以了。在调用它之前,可以加如下代码,以用来检测是否在函数头有普通断点,或是否被钩挂。

//check softbreak

if(*(BYTE*)Func_addr==0xcc)

return true;

//check hook

if(*(BYTE*)Func_addr!=0x64)

return true;

2.2 FD_PEB_BeingDebuggedFlag

我们知道,如果程序处于调试器中,那么在PEB结构中有个beingDegug标志会被设置,直接读取它就可判断是否在调试器中。实际上IsDebuggerPresent就是这么干的。

__asm

{

mov eax, fs:[30h] ;EAX = TEB.ProcessEnvironmentBlock

inc eax

inc eax

mov eax, [eax]

and eax,0x000000ff ;AL = PEB.BeingDebugged

test eax, eax

jne rt_label

jmp rf_label

}

2.3 FD_PEB_NtGlobalFlags

PEB中还有其它FLAG表明了调试器的存在,如NtGlobalFlags。它位于PEB环境中偏移为0x68的位置,默认情况下该值为0,在win2k和其后的windows平台下,如果在调试中,它会被设置为一个特定的值。使用该标志来判断是否被调试并不可靠(如在winnt中),但这种方法却也很常用。这个标志由下面几个标志组成:

***_HEAP_ENABLE_TAIL_CHECK (0x10)

***_HEAP_ENABLE_FREE_CHECK (0x20)

***_HEAP_VALIDATE_PARAMETERS (0x40)

检测NtGlobalFlags的方法如下,这个方法在ExeCryptor中使用过。

__asm

{

mov eax, fs:[30h]

mov eax, [eax+68h]

and eax, 0x70

test eax, eax

jne rt_label

jmp rf_label

}

2.4 FD_Heap_HeapFlags()

同样,调试器也会在堆中留下痕迹,你可以使用kernel32_GetProcessHeap()函数,如果你不希望使用api函数(以免暴露),则可以直接在PEB中寻找。同样的,使用HeapFlags和后面提到的ForceFlags来检测调试器也不是非常可靠,但却很常用。

这个域由一组标志组成,正常情况下,该值应为2。

__asm

{

mov eax, fs:[30h]

mov eax, [eax+18h] ;PEB.ProcessHeap

mov eax, [eax+0ch] ;PEB.ProcessHeap.Flags

cmp eax, 2

jne rt_label

jmp rf_label

}

2.5 FD_Heap_ForceFlags

进程堆里另外一个标志,ForceFlags,它也由一组标志组成,正常情况下,该值应为0。__asm

{

mov eax, fs:[30h]

mov eax, [eax+18h] ;PEB.ProcessHeap

mov eax, [eax+10h] ;PEB.ProcessHeap.ForceFlags

test eax, eax

jne rt_label

jmp rf_label

}

2.6 FD_Heap_Tail

如果处于调试中,堆尾部也会留下痕迹。标志HEAP_TAIL_CHECKING_ENABLED 将会在分配的堆块尾部生成两个0xABABABAB。如果需要额外的字节来填充堆尾,HEAP_FREE_CHECKING_ENABLED标志则会生成0xFEEEFEEE。

据说Themida使用过这个反调试

__asm

{

mov eax, buff

;get unused_bytes

movzx ecx, byte ptr [eax-2]

movzx edx, word ptr [eax-8] ;size

sub eax, ecx

lea edi, [edx*8+eax]

mov al, 0abh

mov cl, 8

repe sca**

je rt_label

jmp rf_label

}

2.7 FD_CheckRemoteDebuggerPresent

CheckRemoteDebuggerPresent是另一个检测调试的api,只是可惜它似乎只能在winxp sp1版本以后使用。它主要是用来查询一个在winnt时就有的一个数值,其内部会调用NtQueryInformationProcess(),我是这样实现的:

FARPROC Func_addr ;

HMODULE hModule = GetModuleHandle("kernel32.dll");

if (hModule==INVALID_HANDLE_VALUE)

return false;

(FARPROC&) Func_addr =GetProcAddress(hModule, "CheckRemoteDebuggerPresent");

if (Func_addr != NULL)

{

__asm

{

push eax;

push esp;

push 0xffffffff;

call Func_addr;

test eax,eax;

je rf_label;

pop eax;

test eax,eax

je rf_label;

jmp rt_label;

}

}

2.8 FD_NtQueryInfoProc_DbgPort

使用ntdll_NtQueryInformationProcess()来查询ProcessDebugPort可以用来检测反调试。如果进程被调试,其返回值应为0xffffffff。

下面的代码应该是从pediy里copy过来的,时间太长,不记得是哪位兄弟的代码了。HMODULE hModule = GetModuleHandle("ntdll.dll");

ZW_QUERY_INFORMATION_PROCESS ZwQueryInformationProcess; ZwQueryInformationProcess = (ZW_QUERY_INFORMATION_PROCESS)GetProcAddress(hModule, "ZwQueryInformationProcess");

if (ZwQueryInformationProcess == NULL)

return false;

PROCESS_DEBUG_PORT_INFO ProcessInfo;

if (STATUS_SUCCESS != ZwQueryInformationProcess(GetCurrentProcess( ), ProcessDebugPort, &ProcessInfo, sizeof(ProcessInfo), NULL))

return false;

else

if(ProcessInfo.DebugPort)

return true;

else

return false;

2.9 FD_NtQueryInfoProc_DbgObjHandle

在winxp中引入了"debug object".当一个调试活动开始,一个"debug object"被创建,同也相应产生了一个句柄。使用为公开的ProcessDebugObjectHandle类,可以查询这个句柄的数值。

代码可能还是从pediy里复制的,不记得了。

HMODULE hModule = GetModuleHandle("ntdll.dll");

ZW_QUERY_INFORMATION_PROCESS ZwQueryInformationProcess; ZwQueryInformationProcess = (ZW_QUERY_INFORMATION_PROCESS)GetProcAddress(hModule, "ZwQueryInformationProcess");

if (ZwQueryInformationProcess == NULL)

return false;

_PROCESS_DEBUG_OBJECTHANDLE_INFO ProcessInfo;

if (STATUS_SUCCESS != ZwQueryInformationProcess(GetCurrentProcess( ), (PROCESS_INFO_CLASS)0x0000001e, &ProcessInfo, sizeof(ProcessInfo), NULL))

return false;

else

if(ProcessInfo.ObjectHandle)

return true;

else

return false;

2.10 FD_NtQueryInfoProc_DbgFlags();

同样的未公开的ProcessDebugFlags类,当调试器存在时,它会返回false。

HMODULE hModule = GetModuleHandle("ntdll.dll");

ZW_QUERY_INFORMATION_PROCESS ZwQueryInformationProcess; ZwQueryInformationProcess = (ZW_QUERY_INFORMATION_PROCESS)GetProcAddress(hModule, "ZwQueryInformationProcess");

if (ZwQueryInformationProcess == NULL)

return false;

_PROCESS_DEBUG_FLAGS_INFO ProcessInfo;

if (STATUS_SUCCESS != ZwQueryInformationProcess(GetCurrentProcess( ), (PROCESS_INFO_CLASS)0x0000001f, &ProcessInfo, sizeof(ProcessInfo), NULL))

return false;

else

if(ProcessInfo.Debugflags)

return false;

else

return true;

2.11 FD_NtQueryInfoProc_SysKrlDbgInfo()

这个方法估计对大家用处不大,SystemKernelDebuggerInformation类同样可以用来识别调试器,只是可惜在windows下无效,据称可以用在reactOS中。

HMODULE hModule = GetModuleHandle("ntdll.dll");

ZW_QUERY_SYSTEM_INFORMATION ZwQuerySystemInformation; ZwQuerySystemInformation = (ZW_QUERY_SYSTEM_INFORMATION)GetProcAddress(hModule, "ZwQuerySystemInformation");

if (ZwQuerySystemInformation == NULL)

return false;

SYSTEM_KERNEL_DEBUGGER_INFORMATION Info;

if (STATUS_SUCCESS == ZwQuerySystemInformation(SystemKernelDebuggerInformation, &Info, sizeof(Info), NULL))

{

if (Info.DebuggerEnabled)

{

if (Info.DebuggerNotPresent)

return false;

else

return true;

}

else

return false;

}

else

return true;

2.12 FD_SeDebugPrivilege()

当一个进程获得SeDebugPrivilege,它就获得了对CSRSS.EXE的完全控制,这种特权也会被子进程继承,也就是说一个被调试的程序如果获得了CSRSS.EXE的进程ID,它就可以使用openprocess操作CSRSS.EXE。获得其进程ID有很多中方法,如Process32Next,或NtQuerySystemInformation,在winxp下可以使用CsrGetProcessId。

hTmp=OpenProcess(PROCESS_ALL_ACCESS,false,PID_csrss);

if(hTmp!=NULL)

{

CloseHandle(hProcessSnap );

return true;

}

2.13 FD_Parent_Process()

通常我们都直接在windows界面下运行应用程序,这样的结果就是它的父进程为"explorer.exe",这个反调试就是检测应用程序的父进程是否为"explorer.exe",如不是则判定为处于调试器中,这也不是百分百可靠,因为有的时候你的程序是在命令行提示符下运行的。

Yoda使用了这个反调试,它使用Process32Next检测父进程,目前很多插件已经通过使Process32Next始终返回false来越过这个反调试(比如HideOD)。不过可以对代码做些简单的修正来处理这个反反调试。

2.14 FD_DebugObject_NtQueryObject();

如前面所描述的,当一个调试活动开始,一个"debug object"被创建,同也相应产生了一个句柄。我们可以查询这个调试对象列表,并检查调试对象的数量,以实现调试器的检测。HMODULE hModule = GetModuleHandle("ntdll.dll");

PNtQueryObject NtQueryObject;

NtQueryObject = (PNtQueryObject)GetProcAddress(hModule,"NtQueryObject");

if(NtQueryObject==NULL)

return false;

unsigned char szdbgobj[25]=

"\x44\x00\x65\x00\x62\x00\x75\x00\x67\x00\x4f\x00\x62\x00\x6a\x00\x65\x00\x63\x 00\x74\x00\x00\x00";

unsigned char *psz=&szdbgobj[0]; __asm

{

xor ebx,ebx;

push ebx;

push esp;

push ebx;

push ebx;

push 3;

push ebx;

Call dword ptr [NtQueryObject]; pop edi;

push 4;

push 1000h;

push edi;

push ebx;

call dword ptr [VirtualAlloc]; push ebx;

push edi;

push eax;

push 3;

push ebx;

xchg esi,eax;

Call dword ptr [NtQueryObject]; lodsd;

xchg ecx,eax;

lable1: lodsd;

movzx edx,ax;

lodsd;

xchg esi,eax;

cmp edx,16h;

jne label2;

xchg ecx,edx;

mov edi,psz;

repe cmp**;

xchg ecx,edx;

jne label2;

cmp dword ptr [eax],edx

jne rt_label;

lable2: add esi,edx

and esi,-4;

lodsd

loop label1;

}

rt_label:

return true;

2.15 FD_Find_Debugger_Window();

通过列举运行的应用程序的窗口,并于常用调试相关工具比对的方法,应该很常用了,就不多说了。这个也是个可以自行增加项目的函数,你可以将一些常用的调试工具归入其中,比如OD,IDA,WindBG,SoftICE等,你也可以添加任何你需要的,比如"Import REConstructor v1.6 FINAL (C) 2001-2003 MackT/uCF","Registry Monitor - Sysinternals: https://www.doczj.com/doc/e614672613.html,"等等。

//ollyice

hWnd=CWnd::FindWindow(_T("1212121"),NULL);

if (hWnd!=NULL)

return true;

//ollydbg v1.1

hWnd=CWnd::FindWindow(_T("icu_dbg"),NULL);

if (hWnd!=NULL)

return true;

//ollyice pe--diy

hWnd=CWnd::FindWindow(_T("pe--diy"),NULL);

if (hWnd!=NULL)

return true;

//ollydbg ?-°?

hWnd=CWnd::FindWindow(_T("ollydbg"),NULL);

if (hWnd!=NULL)

return true;

//ollydbg ?-°?

hWnd=CWnd::FindWindow(_T("odbydyk"),NULL);

if (hWnd!=NULL)

return true;

//windbg

hWnd=CWnd::FindWindow(_T("WinDbgFrameClass"),NULL);

if (hWnd!=NULL)

return true;

//dede3.50

hWnd=CWnd::FindWindow(_T("TDeDeMainForm"),NULL);

if (hWnd!=NULL)

return true;

//IDA5.20

hWnd=CWnd::FindWindow(_T("TIdaWindow"),NULL);

if (hWnd!=NULL)

return true;

//others

hWnd=CWnd::FindWindow(_T("TESTDBG"),NULL);

if (hWnd!=NULL)

hWnd=CWnd::FindWindow(_T("kk1"),NULL);

if (hWnd!=NULL)

return true;

hWnd=CWnd::FindWindow(_T("Eew75"),NULL);

if (hWnd!=NULL)

return true;

hWnd=CWnd::FindWindow(_T("Shadow"),NULL);

if (hWnd!=NULL)

return true;

//PEiD v0.94

hWnd=CWnd::FindWindow(NULL,"PEiD v0.94");

if (hWnd!=NULL)

return true;

//RegMON

hWnd=CWnd::FindWindow(NULL,"Registry Monitor - Sysinternals: https://www.doczj.com/doc/e614672613.html,");

if (hWnd!=NULL)

return true;

//File Monitor

hWnd=CWnd::FindWindow(NULL,"File Monitor - Sysinternals: https://www.doczj.com/doc/e614672613.html,"); if (hWnd!=NULL)

return true;

//Import Rec v1.6

hWnd=CWnd::FindWindow(NULL,"Import REConstructor v1.6 FINAL (C) 2001-2003 MackT/uCF");

if (hWnd!=NULL)

return true;

return false;

2.16 FD_Find_Debugger_Process();

与上面的方法类似,区别是这个反调试用通过查询进程名字与已知的常用调试器应用程序名字进行比对,以确定是否有调试器处于运行状态。

if(strcmp(pe32.szExeFile,"OLLYICE.EXE")==0)

return true;

if(strcmp(pe32.szExeFile,"IDAG.EXE")==0)

return true;

if(strcmp(pe32.szExeFile,"OLLYDBG.EXE")==0)

return true;

if(strcmp(pe32.szExeFile,"PEID.EXE")==0)

return true;

if(strcmp(pe32.szExeFile,"SOFTICE.EXE")==0)

return true;

if(strcmp(pe32.szExeFile,"LORDPE.EXE")==0)

if(strcmp(pe32.szExeFile,"IMPORTREC.EXE")==0)

return true;

if(strcmp(pe32.szExeFile,"W32DSM89.EXE")==0)

return true;

if(strcmp(pe32.szExeFile,"WINDBG.EXE")==0)

return true;

2.17 FD_Find_Device_Driver()

调试工具通常会使用内核驱动,因此如果尝试是否可以打开一些调试器所用到的设备,就可判断是否存在调试器。常用的设备名称如下:

\\.\SICE (SoftICE)

\\.\SIWVID(SoftICE)

\\.\NTICE (SoftICE)

\\.\REGVXG(RegMON)

\\.\REGVXD(RegMON)

\\.\REGSYS(RegMON)

\\.\REGSYS(RegMON)

\\.\FILEVXG(FileMON)

\\.\FILEM(FileMON)

\\.\TRW(TRW2000)

2.18 FD_Exception_Closehandle()

如果给CloseHandle()函数一个无效句柄作为输入参数,在无调试器时,将会返回一个错误代码,而有调试器存在时,将会触发一个EXCEPTION_INVALID_HANDLE (0xc0000008)的异常。__try

{

CloseHandle(HANDLE(0x00001234));

return false;

}

__except(1)

{

return true;

}

2.19 FD_Exception_Int3()

通过Int3产生异常中断的反调试比较经典。当INT3 被执行到时, 如果程序未被调试, 将会异常处理器程序继续执行。而INT3指令常被调试器用于设置软件断点,int 3会导致调试器误认为这是一个自己的断点,从而不会进入异常处理程序。

__asm

{

push offset exception_handler; set exception handler

push dword ptr fs:[0h]

mov dword ptr fs:[0h],esp

xor eax,eax;reset EAX invoke int3

int 3h

pop dword ptr fs:[0h];restore exception handler

add esp,4

test eax,eax; check the flag

je rt_label

jmp rf_label

exception_handler:

mov eax,dword ptr [esp+0xc];EAX = ContextRecord

mov dword ptr [eax+0xb0],0xffffffff;set flag (ContextRecord.EAX)

inc dword ptr [eax+0xb8];set ContextRecord.EIP

xor eax,eax

retn

rt_label:

xor eax,eax

inc eax

mov esp,ebp

pop ebp

retn

rf_label:

xor eax,eax

mov esp,ebp

pop ebp

retn

}

2.20 FD_Exception_Popf()

我们都知道标志寄存器中的陷阱标志,当该标志被设置时,将产生一个单步异常。在程序中动态设置这给标志,如果处于调试器中,该异常将会被调试器捕获。

可通过下面的代码设置标志寄存器。

pushf

mov dword ptr [esp], 0x100

popf

2.21 FD_OutputDebugString()

在有调试器存在和没有调试器存在时,OutputDebugString函数表现会有所不同。最明显的不同是,如果有调试器存在,其后的GetLastError()的返回值为零。OutputDebugString("");

tmpD=GetLastError();

if(tmpD==0)

return true;

return false;

2.22 FD_TEB_check_in_Vista();

这是从windows anti-debug reference里拷贝出来的,据说是适用于vista系统下检测调试器。我没有vista所以也没有测试。有条件的可以试下,有问题帮忙反馈给我。多谢。//vista

__asm

{

push offset exception_handler; set exception handler

push dword ptr fs:[0h]

mov dword ptr fs:[0h],esp

xor eax,eax;reset EAX invoke int3

int 3h

pop dword ptr fs:[0h];restore exception handler

add esp,4

mov eax, fs:[18h] ; teb

add eax, 0BFCh

mov ebx, [eax] ; pointer to a unicode string

test ebx, ebx ; (ntdll.dll, gdi32.dll,...)

je rf_label

jmp rt_label

exception_handler:

mov eax,dword ptr [esp+0xc];EAX = ContextRecord

inc dword ptr [eax+0xb8];set ContextRecord.EIP

xor eax,eax

retn

}

2.23 FD_check_StartupInfo();

这是从pediy上拷贝来的。Window创建进程的时候会把STARTUPINFO结构中的值设为0,而通过调试器创建进程的时候会忽略这个结构中的值,也就是结构中的值不为0,所以可以利用这个来判断是否在调试程序。

STARTUPINFO si;

ZeroMemory( &si, sizeof(si) );

si.cb = sizeof(si);

GetStartupInfo(&si);

if ( (si.dwX != 0) || (si.dwY !=0)

|| (si.dwXCountChars != 0) || (si.dwYCountChars !=0 )

|| (si.dwFillAttribute != 0) || (si.dwXSize != 0)

|| (si.dwYSize != 0) )

return true;

else

return false;

2.24 FD_Parent_Process1()

与前面的FD_Parent_Process原理一样,唯一不同的是使用ZwQueryInformationProcess 检测父进程,而没有使用Process32Next,这有一个好处是可以绕过OD的HideOD插件。

2.25 FD_Exception_Instruction_count()

好像《软件加解密技术》中有提到这个反调试。

通过注册一个异常句柄,在特定地址设置一些硬件断点,当通过这些地址时都会触发EXCEPTION_SINGLE_STEP (0x80000004)的异常,在异常处理程序中,将会调整指令指针到一条新指令,然后恢复运行。可以通过进入进程context结构来设置这些断点,有些调试器不能处理那些不是自己设置的硬件断点,从而导致一些指令将会被漏掉计数,这就形成了一个反调试。

__asm

{

xor eax,eax;

cdq;

push e_handler;

push dword ptr fs:[eax];

mov fs:[eax],esp;

int 3;

hwbp1: nop

hwbp2: nop

hwbp3: nop

hwbp4: nop

div edx

nop

pop dword ptr fs:[0]

add esp,4

cmp al,4;

jne rt_label;

jmp rf_label;

e_handler:

xor eax,eax;

;ExceptionRecord

mov ecx,dword ptr[esp+0x04]

;Contextrecord

mov edx,dword ptr[esp+0x0c]

;ContextEIP

inc byte ptr[edx+0xb8];

;ExceptionCode

mov ecx,dword ptr[ecx];

;1.EXCEPTION_INT_DIVIDE_BY_ZERO

cmp ecx,0xc0000094;

jne Ex_next2;

;Context_eip

inc byte ptr[edx+0xb8];

mov dword ptr[edx+0x04],eax;dr0

mov dword ptr[edx+0x08],eax;dr1

mov dword ptr[edx+0x0c],eax;dr2

mov dword ptr[edx+0x10],eax;dr3

mov dword ptr[edx+0x14],eax;dr6

mov dword ptr[edx+0x18],eax;dr7

ret

;2.EXCEPTION_BREAKPOINT

Ex_next2:

cmp ecx,0x80000003;

jne Ex_next3;

mov dword ptr[edx+0x04],offset hwbp1;dr0

mov dword ptr[edx+0x08],offset hwbp2;dr1

mov dword ptr[edx+0x0c],offset hwbp3;dr2

mov dword ptr[edx+0x10],offset hwbp4;dr3

mov dword ptr[edx+0x18],0x155;dr7

ret

;3.EXCEPTION_SINGLE_STEP

Ex_next3:

cmp ecx,0x80000004

jne rt_label

;CONTEXT_Eax

inc byte ptr[edx+0xb0]

ret

}

2.26 FD_INT_2d()

在windows anti-debug reference中指出,如果程序未被调试这个中断将会生产一个断点异常. 被调试并且未使用跟踪标志执行这个指令, 将不会有异常产生程序正常执行. 如果被调试并且指令被跟踪, 尾随的字节将被跳过并且执行继续. 因此, 使用 INT 2Dh 能作为一个强有力的反调试和反跟踪机制。

__try

{

__asm

{

int 2dh

inc eax;any opcode of singlebyte.

;or u can put some junkcode,"0xc8"..."0xc2"..."0xe8"..."0xe9"

}

return true;

}

__except(1)

{

return false;

}

三、检测-专用调试器(FS_)

这一部分是我比较喜欢的,但内容还不是很丰富,比如:

1、针对SoftIce的检测方法有很多,但由于我从没使用过Softice,也没有条件去测试,所以没有给出太多,有兴趣的可以自己查阅资料进行补充,针对softice网上资料较多,或查阅《软件加解密技术》。

2、同样,这里也没有给出windbg等等其它调试器的检测方法。

3、而针对Odplugin,也只给了几种HideOD的检测。事实上,目前OD的使用者通常都使用众多的强大插件,当OD的反调试越来越普遍时,自己设计几款常用的OD插件的反调试,将会是非常有效的反调试手段。

4、对VME的检测也只给出了两种,如想丰富这一部分可以参考Peter Ferrie的一篇anti-vme的文章(https://www.doczj.com/doc/e614672613.html,/showthread.php?t=68411)。里面有非常多的anti-vme方法。

针对专用调试器的函数列表如下:

//find specific debugger

bool FS_OD_Exception_GuardPages();

bool FS_OD_Int3_Pushfd();

bool FS_SI_UnhandledExceptionFilter();

bool FS_ODP_Process32NextW();

bool FS_ODP_OutputDebugStringA();

bool FS_ODP_OpenProcess();

bool FS_ODP_CheckRemoteDebuggerPresent();

bool FS_ODP_ZwSetInformationThread();

bool FS_SI_Exception_Int1();

bool IsInsideVMWare_();

bool FV_VMWare_VMX();

bool FV_VPC_Exception();

int FV_VME_RedPill();//0:none,1:vmvare;2:vpc;3:others

3.1 FS_OD_Exception_GuardPages

“保护页异常”是一个简单的反调试技巧。当应用程序尝试执行保护页内的代码时,将会产生一个EXCEPTION_GUARD_PAGE(0x80000001)异常,但如果存在调试器,调试器有可能接收这个异常,并允许该程序继续运行,事实上,在OD中就是这样处理的,OD使用保护页来实现内存断点。

最开始实现时忘记了free申请的空间,多谢sessiondiy提醒。

SYSTEM_INFO sSysInfo;

LPVOID lpvBase;

BYTE * lptmpB;

GetSystemInfo(&sSysInfo);

DWORD dwPageSize=sSysInfo.dwPageSize;

DWORD flOldProtect;

DWORD dwErrorcode;

lpvBase=VirtualAlloc(NULL,dwPageSize,MEM_COMMIT,PAGE_READWRITE);

if(lpvBase==NULL)

return false;

lptmpB=(BYTE *)lpvBase;

*lptmpB=0xc3;//retn

VirtualProtect(lpvBase,dwPageSize,PAGE_EXECUTE_READ | PAGE_GUARD,&flOldProtect);

__try

{

__asm call dword ptr[lpvBase];

VirtualFree(lpvBase,0,MEM_RELEASE);

return true;

}

__except(1)

{

VirtualFree(lpvBase,0,MEM_RELEASE);

return false;

}

3.2 FS_OD_Int3_Pushfd

这是个最近比较牛X的反调试,据称是vmp1.64里发现的,好像ttprotect里面也有使用,我没有验证。Pediy里有帖子详细讨论,我是看到gkend的分析,才搞懂一些。下面摘自gkend 分析

int3,pushfd和int3,popfd一样的效果。只要修改int3后面的popfd为其他值,OD都能通过。老掉牙的技术又重新被用了。SEH异常机制的运用而已。

原理:在SEH异常处理中设置了硬件断点DR0=EIP+2,并把EIP的值加2,那么应该在

int3,popfd后面的指令执行时会产生单步异常。但是OD遇到前面是popfd/pushfd时,OD 会自动在popfd后一指令处设置硬件断点,而VMP的seh异常处理会判断是否已经设置硬件断点,如果已经有硬件断点就不产生单步异常,所以不能正常执行。

https://www.doczj.com/doc/e614672613.html,/showthread.php?t=67737

大家也可以仔细研究下OD下的pushfd,popfd等指令,相信利用它们可以构造很多反调试,下面是我实现的一个,不过现在看起来有点没看懂,不知当时为什么用了两个int3。

__asm

{

push offset e_handler; set exception handler

push dword ptr fs:[0h]

mov dword ptr fs:[0h],esp

xor eax,eax;reset EAX invoke int3

int 3h

pushfd

nop

nop

nop

nop

pop dword ptr fs:[0h];restore exception handler

add esp,4

test eax,eax; check the flag

je rf_label

jmp rt_label

e_handler:

push offset e_handler1; set exception handler

push dword ptr fs:[0h]

mov dword ptr fs:[0h],esp

xor eax,eax;reset EAX invoke int3

int 3h

nop

pop dword ptr fs:[0h];restore exception handler

add esp,4

;EAX = ContextRecord

mov ebx,eax;dr0=>ebx

mov eax,dword ptr [esp+0xc]

;set ContextRecord.EIP

inc dword ptr [eax+0xb8];

mov dword ptr [eax+0xb0],ebx;dr0=>eax

xor eax,eax

retn

e_handler1:

;EAX = ContextRecord

mov eax,dword ptr [esp+0xc]

;set ContextRecord.EIP

inc dword ptr [eax+0xb8];

mov ebx,dword ptr[eax+0x04]

mov dword ptr [eax+0xb0],ebx;dr0=>eax

xor eax,eax

retn

rt_label:

xor eax,eax

inc eax

mov esp,ebp

pop ebp

retn

rf_label:

xor eax,eax

mov esp,ebp

pop ebp

retn

}

3.3 FS_SI_UnhandledExceptionFilter

这个针对SoftIce的反调试很简单,好像是SoftIce会修改UnhandledExceptionFilter这个函数的第一个字节为CC。因此判断这个字节是否为cc,就是一种检查softice的简便方法。

FARPROC Uaddr ;

BYTE tmpB = 0;

(FARPROC&) Uaddr =GetProcAddress ( GetModuleHandle("kernel32.dll"),"UnhandledExceptionFilter");

tmpB = *((BYTE*)Uaddr); // 取UnhandledExceptionFilter函数第一字节

tmpB=tmpB^0x55;

if(tmpB ==0x99) // 如该字节为CC,则SoftICE己加载

return true;

else

return false;

3.4 FS_ODP_Process32NextW

当我在调试FD_parentprocess时,感觉总是怪怪的,使用OD时运行Process32NextW总是返回失败,搞了一个晚上,才搞懂原来是OD的插件HideOD在作怪。当HideOD的Process32NextW的选项被选中时,它会更改Process32NextW的返回值,使其始终返回false,这主要是HideOD针对FD_parentprocess这个反调试的一个反反调试。但也正是这一点暴露的它的存在。

int OSVersion;

FARPROC Func_addr;

WORD tmpW;

//1.Process32Next

HMODULE hModule = GetModuleHandle("kernel32.dll");

(FARPROC&) Func_addr =GetProcAddress ( hModule,"Process32NextW");

if (Func_addr != NULL)

{

tmpW=*(WORD*)Func_addr;

OSVersion=myGetOSVersion();

switch(OSVersion)

{

case OS_winxp:

if(tmpW!=0xFF8B)//maybe option of Process32Next is selected.

return true;

break;

default:

if(tmpW==0xC033)

return true;

break;

}

}

但上面的代码并不完美,因为有跨平台问题,所以要先获得当前操作系统版本。目前只在win2k和winxp下进行了测试。

3.5 FS_ODP_OutputDebugStringA

同样,HIDEOD的OutputDebugStringA选项,也对OutputDebugStringA这个api做了处理,具体修改内容我记不得了,大家可以自己比对一下。我的代码如下:

int OSVersion;

FARPROC Func_addr;

WORD tmpW;

//2.OutputDebugStringA

HMODULE hModule = GetModuleHandle("kernel32.dll");

(FARPROC&) Func_addr =GetProcAddress ( hModule,"OutputDebugStringA");

if (Func_addr != NULL)

{

tmpW=*(WORD*)Func_addr;

OSVersion=myGetOSVersion();

switch(OSVersion)

{

case OS_winxp:

if(tmpW!=0x3468)//maybe option of OutputDebugStringAt is selected.

return true;

break;

如何高效使用GDB断点

在gdb中,断点通常有三种形式 断点(BreakPoint): 在代码的指定位置中断,这个是我们用得最多的一种。设置断点的命令是break,它通常有如下方式: 可以通过info breakpoints [n]命令查看当前断点信息。此外,还有如下几个配套的常用命令: 观察点(WatchPoint): 在变量读、写或变化时中断,这类方式常用来定位bug。

捕捉点(CatchPoint): 捕捉点用来补捉程序运行时的一些事件。如:载入共享库(动态链接库)、C++的异常等。通常也是用来定位bug。 捕捉点的命令格式是:catch ,event可以是下面的内容 自动删除。 捕捉点信息的查看方式和代码断点的命令是一样的,这里就不多介绍了。 在特定线程中中断 你可以定义你的断点是否在所有的线程上,或是在某个特定的线程。GDB很容易帮你完成这一工作。

break thread break thread if ... linespec指定了断点设置在的源程序的行号。threadno指定了线程的ID,注意,这个ID是GDB分配的,你可以通过"info threads"命令来查看正在运行程序中的线程信息。如果你不指定thread 则表示你的断点设在所有线程上面。你还可以为某线程指定断点条件。如: (gdb) break frik.c:13 thread 28 if bartab > lim 当你的程序被GDB停住时,所有的运行线程都会被停住。这方便你你查看运行程序的总体情况。而在你恢复程序运行时,所有的线程也会被恢复运行。那怕是主进程在被单步调试时。 在特定条件下中断 条件断点的一种特殊场景是在断点命中指定次数后停下来。事实上每个断点都有一个 ignore count, 他是一个正整数。通常情况下它的值为0,所以看不出来它的存在。但是如果它是一个非0值, 那么它将在每次命中后都将 count 减 1,直到它为 0. ignore bnum count 恢复程序运行和单步调试 在gdb中,和调试步进相关的命令主要有如下几条: 参考资料

OD教程文库

OD教程文库.txt曾经拥有的不要忘记;不能得到的更要珍惜;属于自己的不要放弃;已经失去的留作回忆。OllyDbg完全教程 一,什么是 OllyDbg? OllyDbg 是一种具有可视化界面的 32 位汇编-分析调试器。它的特别之处在于可以在没有源代码时解决问题,并且可以处理其它编译器无法解决的难题。 Version 1.10 是最终的发布版本。这个工程已经停止,我不再继续支持这个软件了。但不用担心:全新打造的 OllyDbg 2.00 不久就会面世! 运行环境: OllyDbg 可以以在任何采用奔腾处理器的 Windows 95、98、ME、NT 或是 XP(未经完全测试)操作系统中工作,但我们强烈建议您采用300-MHz以上的奔腾处理器以达到最佳效果。还有,OllyDbg 是极占内存的,因此如果您需要使用诸如追踪调试[Trace]之类的扩展功能话,建议您最好使用128MB以上的内存。 支持的处理器: OllyDbg 支持所有 80x86、奔腾、MMX、3DNOW!、Athlon 扩展指令集、SSE 指令集以及相关的数据格式,但是不支持SSE2指令集。 配置:有多达百余个(天呀!)选项用来设置 OllyDbg 的外观和运行。 数据格式: OllyDbg 的数据窗口能够显示的所有数据格式:HEX、ASCII、UNICODE、 16/32位有/无符号/HEX整数、32/64/80位浮点数、地址、反汇编(MASM、IDEAL或是HLA)、PE文件头或线程数据块。 帮助:此文件中包含了关于理解和使用 OllyDbg 的必要的信息。如果您还有 Windows API 帮助文件的话(由于版权的问题 win32.hlp 没有包括在内),您可以将它挂在 OllyDbg 中,这样就可以快速获得系统函数的相关帮助。 启动:您可以采用命令行的形式指定可执行文件、也可以从菜单中选择,或直接拖放到OllyDbg中,或者重新启动上一个被调试程序,或是挂接[Attach]一个正在运行的程序。OllyDbg支持即时调试。OllyDbg根本不需要安装,可直接在软盘中运行! 调试DLLs:您可以利用OllyDbg调试标准动态链接库 (DLLs)。OllyDbg 会自动运行一个可执行程序。这个程序会加载链接库,并允许您调用链接库的输出函数。 源码级调试: OllyDbg 可以识别所有 Borland 和 Microsoft 格式的调试信息。这些信息包括源代码、函数名、标签、全局变量、静态变量。有限度的支持动态(栈)变量和结构。 代码高亮: OllyDbg 的反汇编器可以高亮不同类型的指令(如:跳转、条件跳转、入栈、出栈、调用、返回、特殊的或是无效的指令)和不同的操作数(常规[general]、 FPU/SSE、段/系统寄存器、在栈或内存中的操作数,常量)。您可以定制个性化高亮方案。 线程: OllyDbg 可以调试多线程程序。因此您可以在多个线程之间转换,挂起、恢复、终止线程或是改变线程优先级。并且线程窗口将会显示每个线程的错误(就像调用 GETLASTERROR

experiment 用OD下消息断点, 捕获按钮操作.

experiment 用OD下消息断点, 捕获按钮操作. 实验目的: 想直接拦截按钮操作的消息处理, 分析按钮操作的逻辑. 实验程序: <<使用OllyDbg从零开始Cracking 第一章翻译>> 中附带的一个CrackMe. 使用OD加载目标程序, 当程序运行起来后, 暂停OD 经过实验, 点击菜单会触发主窗体WM_COMMAND消息. 如果直接捕获弹出的注册窗口的WM_COMMAND, 捕获不到. 准备捕获主窗体的WM_COMMAND消息, 找到菜单生成注册窗体的处理. 找到注册窗体的创建函数传入的注册窗口处理过程,从而找到注册窗体的所有消息处理实现逻辑.点击菜单属于 WM_COMMAND, 按照消息名称排序, 好找一些.下完消息断电后, 确认一下是否消息断点已下.F9, 让程序跑起来. 点击注册菜单. 程序被断在WM_COMMAND消息断点处.[cpp] view plaincopy00401128 > $ C8 000000 ENTER 0,0 ; 主窗体消息断点WM_COMMAND 0040112C . 56 PUSH ESI 0040112D . 57 PUSH EDI 0040112E . 53 PUSH EBX

0040112F . 837D 0C 02 CMP DWORD PTR SS:[EBP+C],2 00401133 . 74 5E JE SHORT CRACKME.00401193 00401135 . 817D 0C 040200>CMP DWORD PTR SS:[EBP+C],204 0040113C . 74 65 JE SHORT CRACKME.004011A3 F8往下走,找到创建注册窗体的代码. [cpp] view plaincopy00401209 > 6A 00 PUSH 0 ; /lParam = NULL 0040120B . 68 53124000 PUSH CRACKME.00401253 ; |DlgProc = CRACKME.00401253 00401210 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner 00401213 . 68 15214000 PUSH CRACKME.00402115 ; |pTemplate = "DLG_REGIS" 00401218 . FF35 CA204000 PUSH DWORD PTR DS:[4020CA] ; |hInst = 00400000 0040121E . E8 7D020000 CALL <JMP.&USER32.DialogBoxParamA> ; \DialogBoxParamA CRACKME.00401253 是注册窗体的消息处理过程. 转到那看看.[cpp] view plaincopy00401253

OD菜鸟的福音(经典讲解CALL的找法)

详细教程 目录 第一章概述 (1) 第二章组件 (5) 一、一般原理[General prnciples] (5) 二、反汇编器[Disassembler] (8) 三、分析器[Analysis] (9) 四、Object扫描器[Object scanner] (12) 五、Implib扫描器[Implib scanner] (12) 第三章 OllyDbg的使用 (13) 一、如何开始调试[How to start debugging session] (13) 二、CPU 窗口[CPU window] (14) 三、断点[Breakpoints] (14) 四、数据窗口[Dump] (15) 五、可执行模块窗口[Executable modules window] (16) 六、内存映射窗口[Memory map window] (17) 七、监视与监察器[Watches and inspectors] (19) 八、线程[Threads] (19) 九、调用栈[Call stack] (20) 十、调用树[Call tree] (21) 十一、选项[Options] (21) 十二、搜索[Search] (22) 十三、自解压文件[Self—extracting (SFX) files] (22) 十四、单步执行与自动执行[Step—by—step execution and animation] (23) 十五、Hit跟踪[Hit trace] (23) 十六、Run 跟踪[Run trace] (24) 十七、快捷键 (26) 十八、插件[Plugins] (29) 十九、技巧提示[Tips and tricks] (29) 第四章其他功能 (30) 一、调试独立的DLL[Debugging of stand—alone DLLs] (30) 二、解码提示[Decoding hints] (32) 三、表达式赋值[Evaluation of expressions] (32) 四、自定义函数描述[Custom function descriptions] (34)

od破解调试秘籍断点

拦截窗口: bp CreateWindow 创建窗口 bp CreateWindowEx(A) 创建窗口 bp ShowWindow 显示窗口 bp UpdateWindow 更新窗口 bp GetWindowText(A) 获取窗口文本 拦截消息框: bp MessageBox(A) 创建消息框 bp MessageBoxExA 创建消息框 bp MessageBoxIndirect(A) 创建定制消息框 拦截警告声: bp MessageBeep 发出系统警告声(如果没有声卡就直接驱动系统喇叭发声) 拦截对话框: bp DialogBox 创建模态对话框 bp DialogBoxParam(A) 创建模态对话框 bp DialogBoxIndirect 创建模态对话框 bp DialogBoxIndirectParam(A) 创建模态对话框 bp CreateDialog 创建非模态对话框 bp CreateDialogParam(A) 创建非模态对话框 bp CreateDialogIndirect 创建非模态对话框 bp CreateDialogIndirectParam(A) 创建非模态对话框 bp GetDlgItemText(A) 获取对话框文本 bp GetDlgItemInt 获取对话框整数值 拦截剪贴板: bp GetClipboardData 获取剪贴板数据 拦截注册表: bp RegOpenKey(A) 打开子健 bp RegOpenKeyEx 打开子健 bp RegQueryValue(A) 查找子健 bp RegQueryValueEx 查找子健 bp RegSetValue(A) 设置子健 bp RegSetValueEx(A) 设置子健 功能限制拦截断点: bp EnableMenuItem 禁止或允许菜单项 bp EnableWindow 禁止或允许窗口 拦截时间:

OD破解常用方法

[分享] OD破解常用方法[复制链接] 一、概论 壳出于程序作者想对程序资源压缩、注册保护的目的,把壳分为压缩壳和加密壳两种 顾名思义,压缩壳只是为了减小程序体积对资源进行压缩,加密壳是程序输入表等等进行加密保护。当然加密壳的保护能力要强得多! 二、常见脱壳方法 预备知识 1.PUSHAD (压栈)代表程序的入口点, 2.POPAD (出栈)代表程序的出口点,与PUSHAD想对应,一般找到这个OEP就在附近 3.OEP:程序的入口点,软件加壳就是隐藏了OEP(或者用了假的OEP/FOEP),只要我们找到程序真正的OEP,就可以立刻脱壳。 方法一:单步跟踪法 1.用OD载入,点“不分析代码!” 2.单步向下跟踪F8,实现向下的跳。也就是说向上的跳不让其实现!(通过F4) 3.遇到程序往回跳的(包括循环),我们在下一句代码处按F4(或者右健单击代码,选择断点——>运行到所选) 4.绿色线条表示跳转没实现,不用理会,红色线条表示跳转已经实现! 5.如果刚载入程序,在附近就有一个CALL的,我们就F7跟进去,不然程序很容易跑飞,这样很快就能到程序的OEP 6.在跟踪的时候,如果运行到某个CALL程序就运行的,就在这个CALL中F7进入 7.一般有很大的跳转(大跨段),比如jmp XXXXXX 或者JE XXXXXX 或者有RETN的一般很快就会到程序的OEP。 Btw:在有些壳无法向下跟踪的时候,我们可以在附近找到没有实现的大跳转,右键-->“跟随”,然后F2下断,Shift+F9运行停在“跟随”的位置,再取消断点,继续F8单步跟踪。一般情况下可以轻松到达OEP! 方法二:ESP定律法 ESP定理脱壳(ESP在OD的寄存器中,我们只要在命令行下ESP的硬件访问断点,就会一下来到程序的OEP了!) 1.开始就点F8,注意观察OD右上角的寄存器中ESP有没突现(变成红色)。(这只是一般情况下,更确切的说我们选择的ESP值是关键句之后的第一个ESP值) 2.在命令行下:dd XXXXXXXX(指在当前代码中的ESP地址,或者是hr XXXXXXXX),按回车! 3.选中下断的地址,断点--->硬件访--->WORD断点。 4.按一下F9运行程序,直接来到了跳转处,按下F8,到达程序OEP。

TRANSCAD使用技巧(od表、id转换、合并分区)

TC使用技巧 目录 1、转换id:选中质心连接点id (1) 2、连接数据 (2) 3、调整主题图 (3) 4、TransCAD中建立交通小区的两种方法 (6) 5、TransCAD直接导入OD矩阵的方法 (7) 6、tc分配结果另存为dbf文件可以用excel直接打开 (10) 7、小区合并大区 (10) 1、转换ID:选中质心连接点ID 转换出效果如下 1

2、连接数据 先将一组dataview另存为bin格式,然后用tc打开bin ,点击或是dataview中的jion,如下图: 点ok出如下结果: 2

选中index实验填充小区实际编号,,6、矩阵对角线清零 3、调整主题图 点弹出以下对话框,点styles 3

质心输出 1、在学习TransCAD之前,先来认识几个文件格式: (1)dbd:保存点、线、面层的数据;(2)map:地图文件,编辑完dbd后,生成map;(3)bin:表格数据,存储一般表格形式的数据,编辑完bin后,若想保存,出来dvw格式的文件,表示普通数据表的外观;(4)mtx:矩阵文件,一般用来表示OD数据之类的,若想创建矩阵文件,需打开创建好的bin文件或者map文件,因为这是基于bin、map创建的。 4

2、使用该软件之前,最好进行一些设置:edit-preferences,包括地图精度,存放位置之类的,改成中国的习惯 3、创建面类型文件:new-geographic file-area geographic file,然后进行设置面类型文件的字段 4、点击“铅笔+方格”快捷菜单,可进行面的绘制之类的操作,“i”可进行面(小区)的信息扩展 5、如果修改面的类型,可点击“类似三层图层”的那个快捷菜单 6、修改字段的话,可点击dadaview-modify table,前提条件是:表格数据并没有和面文件join,joinzai dataview菜单下 7、创建表格文件时,基本上是在“edit”菜单下进行操作,增加记录,删除记录,等 8、建立矩阵文件的前提是打开创建好的bin文件或者地图文件。将外部的OD数据导入到建好的矩阵文件中,直行matrix-inport,填写好对应的字段即可。当然也可从矩阵文件中进行数据的导出,方便进行编辑 9、制作期望线:直行tools-geographic analysis-desire lines,将字段一一对应,就制作好了期望线,期望线直观的显示小区之间出行分布量的大小。宽度代表了量的大小。如果修改期望线的线条之类的,可直接点击“有三个星星”的快捷菜单。 交通需求预测建模即传统的“四阶段法”---出行产生,出行分布,方式划分,交通分配。 1)建设项目交通需求预测主要包括目标年背景交通量预测和拟建项目新增交通量预测两方面。目标年交通量即为背景交通量与拟建项目新增交通量之和。 5

使用OllyDbg从零开始Cracking 第三十二章-OEP寻踪

第三十二章-OEP寻踪 在上一章中我们提到了OEP(Original EntryPoint)的概念,也就是应用程序原本要执行的第一行代码,OEP %99的情况位于第一个区段中(本章中有一个例子,OEP就不在第一个区段,我是特意举的这个例子,嘿嘿)。 我们知道当到达OEP后,各个区段在内存中的分布跟原始程序很接近,这个时候我们就可以尝试将其转储到(dump)文件中,完成程序的重建工作(PS:脱壳)。 通常脱壳的基本步骤如下: 1:寻找OEP 2:转储(PS:传说中的dump) 3:修复IAT(修复导入表) 4:检查目标程序是否存在AntiDump等阻止程序被转储的保护措施,并尝试修复这些问题。 以上是脱壳的经典步骤,可能具体到不同的壳的话会有细微的差别。本章我们主要介绍定位OEP的方法。 很多时候我们遇到的壳会想方设法的隐藏原程序的OEP,要定位OEP的话就需要我们尝试各种各样的方法了。 首先我们来看看上一章CRACKME UPX,然后再来看其他的壳。 1)搜索JMP或者CALL指令的机器码(即一步直达法,只适用于少数壳,包括UPX,ASPACK壳) 对于一些简单的壳可以用这种方式来定位OEP,但是对于像AsProtect这类强壳(PS:AsProtect在04年算是强壳了,嘿嘿)就不适用了,我们可以直接搜索长跳转JMP(0E9)或者CALL(0E8)这类长转移的机器码,一般情况下(理想情况)壳在解密完原程序各个区段以后,需要一个长JMP或者CALL跳转到原程序代码段中的OEP处开始执行原程序代码。 下面我们将上一章中加了UPX壳的那个CrueHead的CrackMe加载到OD中: 这里我们按CTRL+B组合键搜索一下JMP的机器码E9,看看有没有这样一个JMP跳转到原程序的代码段。

OD使用方法

壳出于程序作者想对程序资源压缩、注册保护的目的,把壳分为压缩壳和加密壳两种 UPX ASPCAK TELOCK PELITE NSPACK ... ARMADILLO ASPROTECT ACPROTECT EPE SVKP ... 顾名思义,压缩壳只是为了减小程序体积对资源进行压缩,加密壳是程序输入表等等进行加密保护。当然加密壳的保护能力要强得多! 二、常见脱壳方法 预备知识 1.PUSHAD (压栈)代表程序的入口点, 2.POPAD (出栈)代表程序的出口点,与PUSHAD想对应,一般找到这个OEP就在附近 3.OEP:程序的入口点,软件加壳就是隐藏了OEP(或者用了假的OEP/FOEP),只要我们找到程序真正的OEP,就可以立刻脱壳。 方法一:单步跟踪法 1.用OD载入,点“不分析代码!” 2.单步向下跟踪F8,实现向下的跳。也就是说向上的跳不让其实现!(通过F4) 3.遇到程序往回跳的(包括循环),我们在下一句代码处按F4(或者右健单击代码,选择断点——>运行到所选) 4.绿色线条表示跳转没实现,不用理会,红色线条表示跳转已经实现! 5.如果刚载入程序,在附近就有一个CALL的,我们就F7跟进去,不然程序很容易跑飞,这样很快就能到程序的OEP 6.在跟踪的时候,如果运行到某个CALL程序就运行的,就在这个CALL中F7进入 7.一般有很大的跳转(大跨段),比如jmp XXXXXX 或者JE XXXXXX 或者有RETN的一般很快就会到程序的OEP。 Btw:在有些壳无法向下跟踪的时候,我们可以在附近找到没有实现的大跳转,右键-->“跟随”,然后F2下断,Shift+F9运行停在“跟随”的位置,再取消断点,继续F8单步跟踪。一般情况下可以轻松到达OEP! 方法二:ESP定律法 ESP定理脱壳(ESP在OD的寄存器中,我们只要在命令行下ESP的硬件访问断点,就会一下来到程序的OEP了!) 1.开始就点F8,注意观察OD右上角的寄存器中ESP有没突现(变成红色)。(这只是一般情况下,更确切的说我们选择的ESP值是关键句之后的第一个ESP值) 2.在命令行下:dd XXXXXXXX(指在当前代码中的ESP地址,或者是hr XXXXXXXX),按回车! 3.选中下断的地址,断点--->硬件访--->WORD断点。 4.按一下F9运行程序,直接来到了跳转处,按下F8,到达程序OEP。 方法三:内存镜像法 1:用OD打开软件! 2:点击选项——调试选项——异常,把里面的忽略全部√上!CTRL+F2重载下程序! 3:按ALT+M,打开内存镜象,找到程序的第一个.rsrc.按F2下断点,然后按SHIFT+F9运行到断点,接着再按ALT+M,打开内存镜象,找到程序的第一个.rsrc.上面的.CODE(也就是00401000处),按F2下断点!然后按SHIFT+F9(或者是在没异常情况下按F9),直接到达程序OEP! 方法四:一步到达OEP 1.开始按Ctrl+F,输入:popad(只适合少数壳,包括UPX,ASPACK壳),然后按下F2,F9运

中断的应用中对断点的保护

【016】中断的应用中对断点的保护 [51] 点击数:430 发布日期:2006-8-22 20:37:00 【收藏】【评论】【打印】【编程爱好者论坛】【关闭】 实验参考: 笨笨工作室-实验八、按键控制跑马灯(中断)。(查看) 实验板: FB51A。(查看) 实验目的: <1> 掌握堆栈在中断程序中的作用。 <2> 掌握让程序保护现场的方法。 实验现象: 二极管作左右跑马灯,当按下外部按键 K1 时, 8 个二极管全部闪烁 5 次后从 K1 按下之前的位置继续作跑马灯。 在应用中断时,若主程序在正常运行的过程中响应了中断就要转而去执行中断服务程序。那如何在执行完中断服务程序之后完全恢复原来的主程序呢?这就需要在执行中断服务程序之前将相关的状态保护起来,在中断完成后再将这些状态恢复,从而继续执行主程序。 51单片机中允许我们从内部 RAM 中指定一个空间专门保存这些断点处的状态,这个空间就是堆栈。并且还专门给了我们一个 8 位的堆栈指针,让我们用它来开辟堆栈空间。例如我们给堆栈指针赋值: mov sp, #70h ,就表示我们把内部数据 RAM 的地址为 70H 开始的单元设为堆栈。 MCS-51的片内存储器(RAM)共有256字节,高128字节是特殊功能寄存器,地址范围80H~FFH。这一部分可看作系统资源,不能随便利用。而剩下的低12 8字节区分如下: ──┬────────────┐ 7FHㄧ用户RAM区ㄧ ㄧ(数据缓冲区、堆栈区)ㄧ 30Hㄧ 80 byte ㄧ ──┼────────────┤ 2FHㄧ可位寻址区ㄧ 20Hㄧ 16 byte ㄧ ──┼────────────┤ 1FHㄧ第3组工作寄存器区ㄧ 18Hㄧ 8 byte ㄧ ──┼────────────┤ 17Hㄧ第2组工作寄存器区ㄧ 10Hㄧ 8 byte ㄧ ──┼────────────┤ 0FHㄧ第1组工作寄存器区ㄧ

OD里SHIFT+F4条件断点用法篇

致菜鸟: OD里SHIFT+F4条件断点用法篇 发帖人:pjb 时间: 2005-08-24 20:25 原文链接:https://www.doczj.com/doc/e614672613.html,/showthread.php?threadid=16494 高手就不必看了~,若有错误的地方请指正! 先说一点ALT+L 看记录~ 在某条语句上按了SHIFT+F4后出现了个对话框, 1)第一行是"条件": 要输入的肯定是你感兴趣的条件啦,比如说下面的俩句 0041150 push eax 0041151 call [TranslateMessage] 你在0041150处下条件记录中断,假如这时候你要是想知道ESI等不等于0,那就可以设置条件ESI==0 或ESI!=0 反正是关于ESI的条件了而不是只能利用这行的EAX. 当然一样可以在0041150处下断追消息,比如说你想知道按了鼠标移动消息,那么就设置MSG==WM_ MOUSEMOVE 或是MSG==0200(移动的数字代码). 有人该问了,消息记录好象应该在下一行有函数的下断才正确吧? 其实不一定非要在传递消息的那行0041151下断的,但在那行下断的好处是可以记录到函数参数,这在下面会讲到~

"条件"这的书写是按照MASM32汇编的形式书写的,比如== != > < 2)"说明"="表达" "解码表达式的值" "说明"就是个注释啦,添不添无所谓了,除非你下的记录中断比较多,这样在记录里能看的明白~ "表达"其实就是我们记录的核心啦,程序在我们第1小条里的"条件"为真的时候,就会记录我们在"表达"里填的内容,比如说ESI==0的时候,我们记录[eax+4]的值 反正是你感兴趣的内容啦,若是想记录消息就添MSG 啦,很多时候我们只添了"条件"而没有添"表达",程序中断后就会显示"未知的函数或标识符!",这就是原因了. 至于下面的"解码表达式的值"你可以随便选了,他不过是把记录后的数据又分析了一下, 比如说我们记录了[eax+4]的值是201 ,那么当你选" 信息代码(WM_XXX)"的时候那么你在记录(ALT+L看记录)里看到的就是被分析成了201 WM_LBUTTONDOWN 当你选"布尔数值",那么记录里数值201后面就会跟个TRUE了,其实就是OD对这个数据按照我们选择的类型又进行了一次分析而已~~ 3)暂停程序=> 永不条件满足时永远 这三个选择就是条件为真记录表达的时候程序暂不暂停程序,那就看你调试的需要了~ 4)记录表达式的值=> 永不条件满足时永远 呵呵,要是选"永不",那么我们设立记录"表达"还有啥意义啊, 若是选"永远",那么条件似乎不就没意义了,因为记录的时候已经不管条件了,只要运行到这就记录表达.不过好象也可以利用的,自己去想想啦..我不说. 一般都是选"条件满足"啦. 5)记录函数参数=> 永不条件满足时永远 这个要是你下在没函数的那行,那么这行就变灰了~~ 记录函数的参数很有意义啊,直接可以看到很多信息,我们比如说在有函数的那行下条件记录."条件"是EAX==0 "表达"是[EAX+ 4],然后在这选择记录函数参数在"条件满足时";当然你若只想记录运行到此处的函数参数,那么就空着" 条件"和"表达"吧,同时3)和4)的选择也要变为"永不"了...也许你在记录了啥也看不到,因为运行到这行可能EAX总不=0 ,那么好换换EAX>0 ,好象看了到一堆,然后还有P MSG=XXXX Hw=XXXX,这就是运行到这里这个函数的参数啦~~ 我们也可以记录CREA TFILEA 函数,那么就知道都打开什么文件了~~

OllyDBG常用API断点非常实用

OllyDbg常用断点 以前收集的资料,不知道作者是谁了。很抱歉! --liuyunagang OD常用断点 常用断点(OD中) 拦截窗口: bp CreateWindow 创建窗口 bp CreateWindowEx(A) 创建窗口 bp ShowWindow 显示窗口 bp UpdateWindow 更新窗口 bp GetWindowText(A) 获取窗口文本 拦截消息框: bp MessageBox(A) 创建消息框 bp MessageBoxExA 创建消息框 bp MessageBoxIndirect(A) 创建定制消息框 bp IsDialogMessageW 拦截警告声: bp MessageBeep 发出系统警告声(如果没有声卡就直接驱动系统喇叭发声) 拦截对话框: bp DialogBox 创建模态对话框 bp DialogBoxParam(A) 创建模态对话框 bp DialogBoxIndirect 创建模态对话框 bp DialogBoxIndirectParam(A) 创建模态对话框 bp CreateDialog 创建非模态对话框 bp CreateDialogParam(A) 创建非模态对话框 bp CreateDialogIndirect 创建非模态对话框 bp CreateDialogIndirectParam(A) 创建非模态对话框 bp GetDlgItemText(A) 获取对话框文本 作用是得指定输入框输入字符串 bp GetDlgItemInt 获取对话框整数值 拦截剪贴板:

bp GetClipboardData 获取剪贴板数据 拦截注册表: bp RegOpenKey(A) 打开子健 bp RegOpenKeyEx 打开子健 bp RegQueryValue(A) 查找子健 bp RegQueryValueEx 查找子健 bp RegSetValue(A) 设置子健 bp RegSetValueEx(A) 设置子健 功能限制拦截断点: bp EnableMenuItem 禁止或允许菜单项 bp EnableWindow 禁止或允许窗口 拦截时间: bp GetLocalTime 获取本地时间 bp GetSystemTime 获取系统时间 bp GetFileTime 获取文件时间 bp GetTickCount 获得自系统成功启动以来所经历的毫秒数 bp GetCurrentTime 获取当前时间(16位) bp SetTimer 创建定时器 bp TimerProc 定时器超时回调函数 GetDlgItemInt 得指定输入框整数值 GetDlgItemText 得指定输入框输入字符串 GetDlgItemTextA 得指定输入框输入字符串 拦截文件: bp CreateFileA 创建或打开文件 (32位) bp OpenFile 打开文件 (32位) bp ReadFile 读文件 (32位) bp WriteFile 写文件 (32位) GetModuleFileNameA GetFileSize Setfilepointer fileopen FindFirstFileA ReadFile 拦截驱动器: bp GetDriveTypeA 获取磁盘驱动器类型 bp GetLogicalDrives 获取逻辑驱动器符号 bp GetLogicalDriveStringsA 获取当前所有逻辑驱动器的根驱动器路径 ★★VB程序专用断点★★

OD设置断点函数

常用断点 拦截窗口: bp CreateWindow 创建窗口 bp CreateWindowEx(A) 创建窗口 bp ShowWindow 显示窗口 bp UpdateWindow 更新窗口 bp GetWindowText(A) 获取窗口文本 bp ShellExecuteA获取网站转向地址 拦截消息框: bp MessageBox(A) 创建消息框 bp MessageBoxExA 创建消息框 bp MessageBoxIndirect(A) 创建定制消息框 bp IsDialogMessageW 拦截警告声: bp MessageBeep 发出系统警告声(如果没有声卡就直接驱动系统喇叭发声) 拦截对话框: bp DialogBox 创建模态对话框 bp DialogBoxParam(A) 创建模态对话框 bp DialogBoxIndirect 创建模态对话框 bp DialogBoxIndirectParam(A) 创建模态对话框 bp CreateDialog 创建非模态对话框 bp CreateDialogParam(A) 创建非模态对话框 bp CreateDialogIndirect 创建非模态对话框 bp CreateDialogIndirectParam(A) 创建非模态对话框 bp GetDlgItemText(A) 获取对话框文本 bp GetDlgItemInt 获取对话框整数值 拦截剪贴板: bp GetClipboardData 获取剪贴板数据 拦截注册表: bp RegOpenKey(A) 打开子健 bp RegOpenKeyEx 打开子健 bp RegQueryValue(A) 查找子健 bp RegQueryValueEx 查找子健 bp RegSetValue(A) 设置子健 bp RegSetValueEx(A) 设置子健 功能限制拦截断点: bp EnableMenuItem 禁止或允许菜单项 bp EnableWindow 禁止或允许窗口

OD管理中的“六定”操作

OD管理中的“六定”操作 定责、定岗、定编、定额、定员、定薪(以下简称“六定”),对于企业而言,设计出企业发展需求的组织系统,首先要理解人力资源管理系统的基础依据。很多HR承担了这项任务,却还没有系统学习定岗定编定责定额定员定薪的理论和实践,更不具备组织设计能力,导致HR苦于事务操作,却没有任何建树。 概念 定责定责是指在明确组织目标,对组织目标进行设定、分解,并进行系统的岗位分析的基础上,对部门职能和岗位职责进行分解和设计,达到各部门与各岗位职责明晰、高效分工与协作,最终制作出部门职责说明书、岗位职责说明书的过程。 定岗合理、顺畅、高效的组织结构是企业快速有序运行的基础,其中岗位是企业组织结构中最基本的功能单位。定岗就是在生产组织合理设计以及劳动组织科学化的基础上,从空间上和时间上科学地界定各个工作岗位的分工与协作关系,并明确地规定各个岗位的职责范围、人员的素质要求、工作程序和任务总量。因事设岗是岗位设置的基本原则。(因人设岗是最低级的错误) 定编广义的定编是指国家机关、企事业单位、社会团体及其他工作单位中,各类组织机构的设置以及人员数量定额、结构和职务的配置。编制包括机构编制和人员编制两部分,这里研究的是对工作组织中各类岗位的数量、职务的分配,以及人员的数量及其结构所作的统一规定的人员编制。 定编就是在定责、定岗的基础上,对各种职能部门和业务机构的合理布局和设置的过程。定编为企业制订生产经营计划和人事调配提供了依据,有利于企业不断优化组织结构,提高劳动效率。 定额定额是在规范的劳动组织,合理地使用材料、机械、设备的条件下,预先规定完成单位合格产品所消耗的资源数量的标准,它反映的是在一定时期的社会生产力水平的高低。在企业中实行劳动定额的人员约占全体员工的40%~50%左右,企业可以工时定额等数据为依据,核定出这些有定额人员的定员人数。 定员定员是在一定生产技术组织的条件下,为保证企业生产经营活动正常进行,按照工作任务所需的一定素质要求,对企业配备各类人员所预先规定的限额。

Ollydbg常用断点函数

Ollydbg常用断点函数 作者:lvvl文章来源:https://www.doczj.com/doc/e614672613.html,点击数:1739更新时间:2007-5-21 1、限制程序功能函数 EnableMenuItem 允许、禁止或变灰指定的菜单条目 EnableWindow 允许或禁止鼠标和键盘控制指定窗口和条目(禁止时菜单变灰) 2、对话框函数 CreateDialog 从资源模板建立一非模态对话窗 CreateDialogParam 从资源模板建立一非模态对话窗 CreateDialogIndirect 从内存模板建立一非模态对话窗CreateDialogIndirectParam 从内存模板建立一非模态对话窗 DialogBox 从资源模板建立一模态对话窗 DialogBoxParam 从资源模板建立一模态对话窗 DialogBoxIndirect 从内存模板建立一模态对话窗 DialogBoxIndirectParam 从内存模板建立一模态对话窗 EndDialog 结束一模态对话窗 MessageBox 显示一信息对话框 MessageBoxEx 显示一信息对话框 MessageBoxIndirect 显示一定制信息对话框 GetDlgItemInt 得指定输入框整数值 GetDlgItemText 得指定输入框输入字符串 GetDlgItemTextA 得指定输入框输入字符串 Hmemcpy 内存复制(非应用程序直接调用)

3、磁盘处理函数1273?GAMEHK所有--admin?11326 GetDiskFreeSpaceA 获取与一个磁盘的组织有关的信息,以及了解剩余空间的容量GetDiskFreeSpaceExA 获取与一个磁盘的组织以及剩余空间容量有关的信息 GetDriveTypeA 判断一个磁盘驱动器的类型 GetLogicalDrives 判断系统中存在哪些逻辑驱动器字母 GetFullPathNameA 获取指定文件的详细路径 GetVolumeInformationA 获取与一个磁盘卷有关的信息 GetWindowsDirectoryA 获取Windows目录的完整路径名 GetSystemDirectoryA 取得Windows系统目录(即System目录)的完整路径名 4、文件处理函数 CreateFileA 打开和创建文件、管道、邮槽、通信服务、设备以及控制台 OpenFile 这个函数能执行大量不同的文件操作 ReadFile 从文件中读出数据 ReadFileEx 与ReadFile相似,只是它只能用于异步读操作,并包含了一个完整的回调WriteFile 将数据写入一个文件 WriteFileEx 与WriteFile类似,只是它只能用于异步写操作,并包括了一个完整的回调SetFilePointer 在一个文件中设置当前的读写位置 SetEndOfFile 针对一个打开的文件,将当前文件位置设为文件末尾 CloseHandle 关闭一个内核对象。其中包括文件、文件映射、进程、线程、安全和同步对象等 _lcreat 创建一个文件 _lopen 以二进制模式打开指定的文件 _lread 将文件中的数据读入内存缓冲区

OD使用说明书

一、下边是几个通用的按钮 1、:重新运行程序(重复上次运行的程序,快捷键:ctrl+F2)。 2、:关闭当前正在运行的程序。(快捷键:Alt+F2) 3、:运行程序,(快捷键:F9) 4、:暂停执行,(快捷键:F12) 5、:单步步入,意思就是:当遇到call时,就进入call的内部(快捷键:F7) 6、:单步步过,意思就是:当遇到call时,就跳过call得内部,直接运行完call,然后执行下条语句。(快捷键:F8) 7、:跟踪步入,意思跟F9基本一样,步过就是遇到call就进入,这个按钮几乎不用(快捷键:ctrl+f11) 8、:跟踪步过,同上,区别是遇到call就跳过了,同样也几乎不用(快捷键:ctrl+f12) 9、:执行到返回,意思是执行某段代码后返回执行前的位置,(是在设置断点后才用的,)(快捷键:ctrl+f9) 10、:是跳转到某一内存地址,在弹出的对话框里输入地址,然后就跳到你想要看的地址,但程序没有被执行,只是用来查看的功能。 二、后便的蓝色按钮(功能不是很经常用) 1、:是用来查看记录的,(程序被载入后,调用的系统DLL和一些函数) 2、:是用来显示模块窗口,告诉我们有多少个模块,和模块在机器的位置 3、:是用来显示内存窗口的,一般是在脱壳的时候用到的内存脱壳方法,在这里设断点, 4、:是用来显示线程的, 5、:是用来显示句柄的, 6、:是用来显示当前程序运行的位置的,点击后就会跑到程序当前的位置,(功能就是:当你查看其他模块的代码,然后点一下,就会跑回原来的地方, ) 前边的黑色就代表了程序已经运行到0100645C了,每次点击就会跑到当前运行的位置。这个按钮主要为了提供用户方便,快速的回到当前位置。 7、:这个是用来查看当前有多少个断点,在这里也可以取消断点。(这个按钮和C经常会用到) 三、在反汇编窗口的右键功能 经常用到的功能有(一级目录):二进制、汇编、断点、数据窗口跟随、查找、复制到可执行文件、ultra string reference. 1、二进制:主要用来复制语句的,同时也可以清空选中的语句(即在2级目录里的NOP) 2、汇编:主要用来修改语句的 3、断点:主要用来设置断点,一般情况都是在比较有用的信息上设断点,(如:发现某个 关键的ascii值时,一般都是在后边的注释里,能够看到的) 4、数据窗口跟随:一般情况都是在2级目录里选第二个,然后你就会发现在数据窗口发现 你想要的数据了。 5、查找:主要是用来查找字符串,和你想要的命令、还有二进制数通常都查找这3个 6、复制到可执行文件:是用来保存功能的,如果你修改了很多地方,那么你就选择(所有

将有时间限制的软件通通拿下

将有时间限制的软件通通拿下 一般软件加入时间限制时都是在主要功能函数或程序开始初始化运行时加入时间判断,在安装或者在每次运行时进行时间标志更新,比如程序安装时把当前时间标志秘密地放在注册表或某个文件中,程序开始执行时就先启动时间读取代码,获得当前时间,再与标记的时间进行比较,如果过期则显示过期提示,如果时间被调回则警告等。由于软件设置时间标志及读取时间都要调用系统的API函数,这往往使得Cracker很容易找到关键代码处,所以很多软件除了加入时间判断代码外,还会加入功能限制,或者是时间限制与Demo版本结合。 破除方法简述 击破时间限制的方法有很多,这里罗列一些: 1. 使用出错提示信息定位,程序在判断软件是否使用过期后,一般都会据判断标志执行不同的代码,如在有效期则继续执行,如过期则出错提示,我们通过反汇编代码,找到出错信息,再据调用的代码就可以定位关键的代码了。这一般在静态分析使用。 2. 使用API断点。程序过期后,一般会弹出消息框(Nag窗口),发出警告信息,提醒使用者注册软件。所以可以在动态调试器中用API断点法定位Nag窗口代码,再据Nag调用前的代码找出判断时间的关键处。 3. 监视法。使用注册表监控软件或文件监控软件,看软件从那里调用隐藏的时间标志,一般来说对一些加壳软件可以用这个方法定位它的时间标志,只要把它的时间标志删除就又可以在试用期内免费使用软件了。 另外还有一些通过逆向注册过程,找出注册码等方法,以上方法各有优缺点,下面通过两个实例进一步解说。 击破时间限制的实例分析 实例1 解除LeapFtp v2.7.5.610(英文版)限制 这个软件有60天的试用时间,过期的话会弹出如图1所示的NAG窗口。 图1 这次我们用信息提示法入手定位关键时间判断代码。先用PEID查看一下看看软件是用什么语言编写的或者有没有加壳,如图2所示,可以看到软件是由Dephi编写。 图2 这里可以用Dede载入程序进行分析,但我们通过信息提示法就可以定位,并不需要使用其它工具。 现在开始用OD载入程序,在CPU窗口,右击,在弹出菜单中选择“Search for/all referenced text strings”,按图3所示进行字符串查找。 图3 在弹出的字符串窗口中,会找到如图4所示的提示信息。 图4 这正是过期窗口的信息内容。好了,双击“of your 30 day……”那行字符串,OD就会自动定位到调用这段字符串的代码,如图5所示。

相关主题
文本预览
相关文档 最新文档