双进程保护及实现
来源:互联网 发布:知乎 人生目标 编辑:程序博客网 时间:2024/04/29 11:18
考前2个星期都一直在搞这个,然后考试周考的死去活来,全部忘光了。最近瞅了一下,发现远远没我想象的那么简单,双进程保护如果用的好的话,SMC+调试进程与被调试进程处理不同异常,的确能在很大程度上限制动态调试。 《加密与解密》上写的很简单,只是大体讲了一下思路,差不多步骤如下
1.加载或者附加一个正在运行的进程(可以用createprocess创建或者用debugactiveprocess附加)
2.获取被调试程序的信息(waitfordebugevent等待调试事件发生)
3.接受被调试进程发来的调试事件并处理然后我们在程序的开始,就可以做一些手脚,然后让调试进程,和被调试进程处理不同的事件,然后程序会沿着不同的逻辑进行,逆向起来就很头疼了。
下面我贴上以为看雪大牛之前发过的源码,然后自行填上了注释。
#define _WIN32_WINNT 0x0500#include "windows.h"int DebugMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow);void DecryptCode(HANDLE hProcess,DWORD begin,DWORD end);int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){ if(IsDebuggerPresent()) //区分调试进程与被调试进程,以执行不同代码,被调试的进程则调用DeBugMain { return DebugMain(hInstance,hPrevInstance,lpCmdLine,nCmdShow); } __try { __asm int 3 //断点异常想让调试进程处理 } __except(1) { __asm pop eax; //如果调试器不处理断点异常,这里会被执行 __asm pop esp;//这两句汇编是破坏了堆栈 } int div=0; __try { __asm int 3 //断点异常交给被调试进程处理 } __except(1) { div++; } div=1/div; //如果调试进程的异常处理模块未被执行,那么这里产生除0异常而使程序退出 __asm int 3; MessageBox(0,"这是一个简单的例子","TraceMe",0); return 0;}//加密messagebox函数,采用的是异或函数void DecryptCode(HANDLE hProcess,DWORD begin,DWORD end){ DWORD flOldProtect; BYTE ch[1]={0}; DWORD num=end-begin; VirtualProtectEx(hProcess, (LPVOID)begin,num,PAGE_EXECUTE_READWRITE,&flOldProtect); for(DWORD i=begin;i<end;i++) { ReadProcessMemory(hProcess,(LPCVOID)i,&ch,sizeof(ch),NULL) ; ch[0]^=0xDE; WriteProcessMemory(hProcess,(LPVOID)i,&ch,sizeof(ch),NULL); } VirtualProtectEx(hProcess,(LPVOID)begin,num,flOldProtect,NULL);}//调试进程主函数int DebugMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){ char filename[260]; GetModuleFileName(0,filename,260); //获取自身文件名 STARTUPINFO si={0}; GetStartupInfo(&si); //获取创建信息 PROCESS_INFORMATION pi={0}; if(!CreateProcess(filename,NULL,NULL,NULL,FALSE,DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS,NULL,NULL,&si,&pi)) //创建被调试进程 { return 0; } BOOL WhileDoFlag=TRUE; DEBUG_EVENT DBEvent ; DWORD dwState; CONTEXT Regs ; Regs.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; while (WhileDoFlag) { /*WaitForDebugEvent:用来等待被调试事件,如果成功,则返回等待调试事件发生的毫秒数,如果没有调试事件发生,则返回函数的调用者,如果 函数被定义为INFINITE,那么则一直等待的函数的发生*/ WaitForDebugEvent (&DBEvent, INFINITE); dwState = DBG_EXCEPTION_NOT_HANDLED ; switch (DBEvent.dwDebugEventCode) { case CREATE_PROCESS_DEBUG_EVENT: dwState = DBG_CONTINUE ; break; case EXIT_PROCESS_DEBUG_EVENT : WhileDoFlag=FALSE; break ; case EXCEPTION_DEBUG_EVENT: switch (DBEvent.u.Exception.ExceptionRecord.ExceptionCode) { case EXCEPTION_BREAKPOINT: //断点异常处理过程 { GetThreadContext(pi.hThread, &Regs) ; if(Regs.Eip==(DWORD)0x0040CC10) //地址值需纠正,处理上面第一个int 3指令地址+1 dwState = DBG_CONTINUE ; else if(Regs.Eip==(DWORD)0x0040CC20) //地址值需纠正,处理上面第二个int 3,指令地址+1 dwState = DBG_EXCEPTION_NOT_HANDLED ; else if(Regs.Eip==(DWORD)0x0040CC30) //地址值需纠正,对加密代码的地址修正 { DecryptCode(pi.hProcess,0x0040CC30,0x00401200); //地址值需纠正,对加密代码的地址修正 dwState = DBG_CONTINUE; } else dwState = DBG_CONTINUE ; break; } } break; } ContinueDebugEvent(pi.dwProcessId, pi.dwThreadId, dwState) ; } CloseHandle(pi.hProcess) ; CloseHandle(pi.hThread) ; return 0;}
我们回过头来细看这段源码,程序一旦被调试,就会调用debugmain这个函数 在这个函数中有两个至关重要的函数。
第一个是双进程保护的实现:CreateProcess,贴上msdn文档
BOOL CreateProcess(LPCTSTR lpApplicationName, LPTSTR lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes。LPSECURITY_ATTRIBUTES lpThreadAttributes,BOOL bInheritHandles,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCTSTR lpCurrentDirectory,LPSTARTUPINFO lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation)
然后百科中有函数的讲解:http://baike.baidu.com/view/697167.htm?fr=aladdin(鸟文好的看msdn最好了)
在这个函数的第6个参数dwCreationFlags此参数为指定 附加的用来控制优先类和进程的创建标志。
这里源码中设置为DEBUG_PROCESS,则代表着:调用进程将被当做一个调试程序,并且新进程会被当做被调试的进程,即父进程会调试子进程以及创建的所有进程。系统把被调试程序发生的所有调试事件通知给调试器。另外源码中与上了一个DEBUG_ONLY_THIS_PROCESS,表示为:创建的新进程为此调试程序的调试对象,此时子进程发生的所有特定事件都将通知父进程。反正个人理解相当于DEBUG_PROCESS将DEBUG_ONLY_THIS_PROCESS所能调试的进程做了推广。
创建完进程之后开始进入双进程保护的核心部分——调试循环。这里有点类似于windows下的消息循环,在一个死循环下等待调试事件,如果有调试事件发生则处理调试事件,并执行相应操作,同时告知父进程。
当有调试事件被告知父进程时,子进程的线程会被挂起,父进程会调用WaitForDebugEvent来等待事件,并返回一个DEBUG_EVENT
源码里采用了 CREATE_PROCESS_DEBUG_EVENT(进程被创建),EXIT_PROCESS_DEBUG_EVENT(退出调试进程中最后一个线程) ,EXCEPTION_DEBUG_EVENT(调试异常),这里又会对异常在做出具体的判断,从而修改部分汇编代码,利用的算是简单是SMC代码自修改技术,可见写源码的人心有多狠。
紧接着是双进程保护中第二个关键的函数,WaitForDebugEvent,用来等待被调试进程发生的调试事件。贴上函数定义:
BOOL WaitForDebugEvent(LPDEBUG_ENENT lpDebugEvent, DWORD dwMilliseconds)
lpDebugEvent :指向接收调试事件信息的DEBUG_ ENENT结构的指针
dwMilliseconds:指定用来等待调试事件发生的毫秒数,如果 这段时间内没有调试事件发生,函数将返回调用者;如果将该参数指定为INFINITE,函数将一直等待直到调试事件发生
如果函数成功,则返回非零值;如果失败,则返回零
在调试器调用WaitForDebugEvent返回后,得到事件通知,然后解析DEBUG_EVENT结构,并对事件进行响应,处理完成后调试器将会调用ContinueDebugEvent,并根据参数来通知调试目标执行相应操作。
与此函数对应着的是ContinueDebugEvent,用于调试器恢复先前犹豫调试事件挂起的线程。
BOOL ContinueDebugEvent(DWORD dwProcessId,DWORD dwThreadId, DWORD dwContinueStatus )
dwProcessId 为被调试进程的进程标识符
dwThreadId 为欲恢复线程的线程标识符
dwContinueStatus指定了该线程将以何种方式继续,包含两个定义值DBG_CONTINUE和DBG_EXCEPTION_NOT_HANDLED
如果函数成功,则返回非零值;如果失败,则返回零。
在这个源码中这理解这两个函数是逆向的关键。
一般的思路都是
while(Condition) {DEBUG_EVENT DebugEvent={0}; WaitForDebugEvent(&DebugEvent,INFINITE);//等待调试事件 ProcessEvenet(DebugEvent)//处理调试事件。 ContinueDebugEvent(DebugEvent.dwProcessId,DebugEvent.dwThreadId,Condition);//通知调试目标继续执行。}
这里也一样.
在源码中处理会给continuedebug的第三个参数赋为不同的值
DBG_CONTNUE表明该异常一杯妥善处理,
DBG_EXCEPTION_NOT_HANDLED则表明系统未被处理,会返回给操作系统,这个时候会返给调试器,就是我们开始的调试进程去处理。这就是双进程保护的精髓。
通过异常的处理与为处理来使程序走向不同的分支,从而来大大增加逆向的难度。
假如你此时进行黑盒测试的时候,如果并不清楚源码怎么写的,那么就很难搞清楚在不同的进程中的代码是怎样被执行的。
程序的大体流程应该就是这些了。然后可以对着这些先用编辑器(VS或者VC)进行调试,看看程序的走向。
具体的逆向过程下次再跟吧,码字好累。。。
- 双进程保护及实现
- Android双进程保护实现的思考及过程说明
- 实例分析双进程保护及其实现
- 业务进程监控及保护
- HOOK ZwTerminateProcess实现进程保护
- SSDT HOOK实现进程保护
- win7x64下实现进程保护
- VB双进程保护
- 双进程保护
- VC 双进程保护代码
- vc++实现内核级进程保护
- 驱动笔记:SSDT HOOK实现进程保护
- vc++实现内核级进程保护
- vc++实现内核级进程保护
- vc++实现内核级进程保护
- Ring3下实现进程保护,不用hook
- 利用Intel VT实现进程保护
- 进程保护
- OC 运行时
- Nginx 在Linux 配置高并发TCP连接
- 去掉火狐浏览器书签文件夹的箭头。
- 2.awesomeplayer结构分析
- android 横竖屏
- 双进程保护及实现
- 通过window系统定时任务备份数据库
- rfid理论基础知识要点
- Launch Screen在iOS7/8中的实现
- resin 的使用和配置详解
- 约瑟夫环问题
- Unix命令执行顺序
- XCODE6添加pch
- centos下安装谷歌浏览器