首页
社区
课程
招聘
[原创]初探双进程保护
发表于: 2009-8-5 18:06 44190

[原创]初探双进程保护

2009-8-5 18:06
44190

写一篇关于双进程保护的心得,因为我也是新手兼菜鸟,所以写的比较简单。
windows下一个ring3进程只能对应一个调试器,那么我们就可以预先给自己的应用程序加个调试器,以防止别人跟踪调试。
1.双进程实现:
程序一开始,先判断是否为调试状态,由于刚启动默认为非调试状态,那这样就作为调试器进程运行,作为调试进程运行后,会用自身程序文件再创建一个被调试进程。被调试进程判断出自己处于调试状态,就会沿着不同于调试进程的软件逻辑运行。被调试进程是我们主要运行的进程,而调试进程是用来保护被调试进程的。好像很啰嗦,还是看代码,代码比较简单:

#include "windows.h"

int DebugMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow);

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
  if(!IsDebuggerPresent()) //区分调试进程与被调试进程,以执行不同的代码。
  {
    return DebugMain(hInstance,hPrevInstance,lpCmdLine,nCmdShow); 
  }

  __asm int 3;
  MessageBox(0,"这是一个简单的例子","TraceMe",0);

  return 0;
}

int DebugMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) //调试进程主函数
{  
  char filename[MAX_PATH];
  GetModuleFileName(0,filename,MAX_PATH); //获取自身文件名
  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;

  while (WhileDoFlag) 
  {
    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:
          {
            dwState = DBG_CONTINUE ;
            break;
          }
        }
        break;
    }    
    ContinueDebugEvent(pi.dwProcessId, pi.dwThreadId, dwState) ;
  }

  CloseHandle(pi.hProcess) ;
  CloseHandle(pi.hThread)  ;
  return 0;
}
__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异常,接着就exit了。  

。。。。。。

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
    dwState = DBG_CONTINUE ;
  break;
}
。。。。。。
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);
}
。。。。。。

  else if(Regs.Eip==(DWORD)0x0040CC30) //地址值需纠正
  {
    DecryptCode(pi.hProcess,0x0040CC30,0x0041200); //地址值需纠正
    dwState = DBG_CONTINUE;
  }
  else
    dwState = DBG_CONTINUE ;
。。。。。。
0040109C   .  CC            int3
。。。。。。

004010C6   .  CC            int3

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (34)
雪    币: 230
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
虽然不是很明白,但是还是谢谢楼主
2009-8-5 18:55
0
雪    币: 1450
活跃值: (35)
能力值: (RANK:680 )
在线值:
发帖
回帖
粉丝
3
标题不大合适.

简单的Debug API应用.

更经典的例子见riijj版主的一个CM.
2009-8-5 19:18
0
雪    币: 97
活跃值: (30)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
4
标题是有点不合适,双进程保护本是指用来保护进程不死的,我这个标题会混淆概念,呵呵。

基本上大家都看过《加密与解密3》15.3.8 双进程保护,我这个概念跟它差不多,反正《加密与解密3》这么出名,就先将就一下了。
2009-8-5 20:23
0
雪    币: 97
活跃值: (30)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
5
2009-8-5 20:50
0
雪    币: 264
活跃值: (11)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
6
双进程 很好的资料 仔细拜读..
2009-8-6 00:02
0
雪    币: 62
活跃值: (72)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
7
学习之
2009-8-6 00:08
0
雪    币: 191
活跃值: (345)
能力值: ( LV9,RANK:450 )
在线值:
发帖
回帖
粉丝
8
学习了,标记一下。
2009-8-6 01:01
0
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
9
运行一遍,dump一下,代码就全出来了
2009-8-6 09:57
0
雪    币: 97
活跃值: (30)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
10
厉害!

得在被调试进程中多加几个断点,然后由调试进程把被调试进程运行过的代码一阵乱改.
2009-8-6 10:41
0
雪    币: 264
活跃值: (11)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
11
运行完之后断点 抹掉代码
2009-8-6 14:05
0
雪    币: 219
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
我是来学习的!
2009-8-6 14:15
0
雪    币: 474
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
谢谢楼主
学习了
2009-8-6 14:16
0
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
14
父进程是可以调试的,不是说抹就抹的
2009-8-7 14:39
0
雪    币: 202
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
我是初学者
看来对这方面要好好的研究下了
谢谢楼主提供的思路
2009-8-12 16:52
0
雪    币: 199
活跃值: (72)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
16
太难的东西只能看看
2009-8-12 17:02
0
雪    币: 264
活跃值: (11)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
17
相当于在调试一个调试器??那怎么办呢?类似于safedisc创建调试进程?
2009-8-12 18:10
0
雪    币: 97
活跃值: (30)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
18
贴一点关于双进程反跟踪与vm结合的简单测试代码:

///////////////// vm.h /////////////////

typedef struct _tagVMCONTEXT
{
	DWORD eax;
	DWORD ebx;
	DWORD ecx;
	DWORD edx;
	DWORD edi;
	DWORD esi;
	DWORD ebp;
	DWORD *esp;
	DWORD eflags;
	BYTE arg[5]; //操作数
	DWORD stack[4096];  //堆栈
} VMCONTEXT;

VMCONTEXT vmcontext;
typedef int (* VMHANDLER)(void);

__declspec(naked) int VMStart()
{
	__asm
	{
		push eax
		pop [vmcontext.eax]
		push ebx
		pop [vmcontext.ebx]
		push ecx
		pop [vmcontext.ecx]
		push edx
		pop [vmcontext.edx]
		push edi
		pop [vmcontext.edi]
		push esi
		pop [vmcontext.esi]
		push ebp
		pop [vmcontext.ebp]
		pushfd
		pop [vmcontext.eflags]
		push eax
		mov eax,offset vmcontext.stack
		add eax,0x1000*4-4
		push eax
		pop [vmcontext.esp]
		pop eax
		mov eax,1     //返回值为1
		ret
	}
}

__declspec(naked) int VMExit()
{
	__asm
	{
		push [vmcontext.eax]
		pop eax
		push [vmcontext.ebx]
		pop ebx
		push [vmcontext.ecx]
		pop ecx
		push [vmcontext.edx]
		pop edx
		push [vmcontext.edi]
		pop edi
		push [vmcontext.esi]
		pop esi
		push [vmcontext.ebp]
		pop ebp
		push [vmcontext.eflags]
		popfd
		mov eax,1   //返回值为1
		ret
	}
}


int VPushImm32()
{
	DWORD imm=*((DWORD *)vmcontext.arg);
	vmcontext.esp--;
	*vmcontext.esp=imm;
	return 5;  //返回值 = 1 + 用到的操作数个数,下同。
}

int VCallImm32()
{
	DWORD imm =*((DWORD *)vmcontext.arg);
	_asm
	{
		mov esi, esp
		mov esp, [vmcontext.esp]
		call imm
		push eax
		pop [vmcontext.eax]
		mov esp, esi
	}
	return 5;
}

int VMovReg32Mem32()
{
	BYTE index=vmcontext.arg[0];
	DWORD *addr =*(DWORD **)(&vmcontext.arg[1]);
	((DWORD *)&vmcontext)[index]=*addr;
	return 6;
}

int VCmpReg32Imm32()
{
	BYTE index=vmcontext.arg[0];
	DWORD reg=((DWORD *)&vmcontext)[index];
	DWORD imm=*((DWORD*)(&vmcontext.arg[1]));
	if(reg==imm)
	{
		vmcontext.eflags = vmcontext.eflags | 0x40;  //ZF标志置1
	}
	else
	{
		vmcontext.eflags = vmcontext.eflags & 0xFFFFFFBF; //ZF标志置0
	}
	return 6;
}

int VJnz()
{
	int iRtn;
	if(vmcontext.eflags & 0x40)
	{
		iRtn = 5;
	}
	else
	{
		iRtn = *((int *)vmcontext.arg);
	}
	return iRtn;
}

int VJmp()
{
	int iRtn=*((int *)vmcontext.arg);
	return iRtn;
}

int VNop()
{
	return 1;
}

int VPop()
{
	vmcontext.esp++;
	return 1;
}

VMHANDLER VMHandlerTable[]= //删减了许多Handler,这里相当的不完整
{
	(VMHANDLER)NULL, //00,无对应Handler
	(VMHANDLER)VMStart, //01
	(VMHANDLER)VMExit,  //02

	(VMHANDLER)VNop, //03
	(VMHANDLER)VMovReg32Mem32, //04
	(VMHANDLER)VCmpReg32Imm32, //05

	(VMHANDLER)VPushImm32, //06
	(VMHANDLER)VCallImm32, //07
	(VMHANDLER)VPop, //08

	(VMHANDLER)VJnz,//09
	(VMHANDLER)VJmp, //0A
};

///////////////// vm.cpp /////////////////

#define _WIN32_WINNT 0x0500
#include "windows.h"
#include "iostream.h"
#include "vm.h"

int DebugMain(int argc, char* argv[]);

DWORD key;
char w[]="wrong";
char r[]="right";

void ShowMessage(char *s)
{
	cout<<s;
}


int main(int argc, char* argv[])
{
	if(!IsDebuggerPresent())
	{
		return DebugMain(argc,argv); 
	}
	cin>>key;
	__asm int 3;
	return 0;
}

int DebugMain(int argc, char* argv[])
{
	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;

	BYTE vm_code[20][6]=
	{
		0x00,0x00,0x01,0x03,0x03,0x03, //VMStart
		0x04,0x00,0x00,0x00,0x00,0x00, //VMovReg32Mem32
		0x05,0x00,0x78,0x56,0x34,0x12, //VCmpReg32Imm32
		0x09,0x12,0x00,0x00,0x00,0x03, //VJnz
		0x06,0x00,0x00,0x00,0x00,0x03, //VPushImm32 
		0x0A,0x0C,0x00,0x00,0x00,0x03, //VJmp
		0x06,0x00,0x00,0x00,0x00,0x03, //VPushImm32 
		0x07,0x00,0x00,0x00,0x00,0x03, //VCallImm32
		0x08,0x03,0x03,0x03,0x03,0x03, //VPop	
		0x02,0x00,0x00,0x00,0x00,0x00, //VMExit
	}; 
	BYTE *vm_eip=&vm_code[0][0];

	*((DWORD *)(&vm_code[1][2]))=(DWORD)&key;
	*((DWORD *)(&vm_code[4][1]))=(DWORD)r;
	*((DWORD *)(&vm_code[6][1]))=(DWORD)w;
	*((DWORD *)(&vm_code[7][1]))=(DWORD)ShowMessage;

	while (WhileDoFlag) 
	{
		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: //下面相当于VM的调度器
					{
						GetThreadContext(pi.hThread, &Regs);
						if(*vm_eip!=NULL)
						{
							vm_eip+=Regs.Eax;
						}
						else
						{
							vm_eip+=1;
						}
						if(*vm_eip!=NULL)
						{
							DWORD addrCC=(--Regs.Eip); //int 3指令地址
							Regs.Esp-=4;
							WriteProcessMemory(pi.hProcess,(LPVOID)Regs.Esp,&addrCC,sizeof(addrCC),NULL); //将int 3指令地址放入堆栈。
							Regs.Eip=(DWORD)VMHandlerTable[*vm_eip]; //修改eip为handler地址
							WriteProcessMemory(pi.hProcess,(LPVOID)vmcontext.arg,vm_eip+1,5,NULL); //传入参数
							SetThreadContext(pi.hThread,&Regs);
						}
						dwState = DBG_CONTINUE ;
						break;
					}
				}
				break;
		}		
		ContinueDebugEvent(pi.dwProcessId, pi.dwThreadId, dwState) ;
	}

	CloseHandle(pi.hProcess) ;
	CloseHandle(pi.hThread)  ;
	return 0;
}

实际上就是将VM的Handler留在子进程中执行,而将VM的调度器和伪代码放在父进程的执行代码中,在父进程的控制下,实现vm在双进程之间的"来回切换"(表达不是很确切),从而增加逆向跟踪的难度。我贴出来的只是一个简单的测试代码,还缺少很多很多东西,感兴趣的朋友可以去实现一个完整的双进程vm模型。

ps:参考了Bughoho版主的虚拟机源代码。
2009-8-15 20:31
0
雪    币: 5318
活跃值: (3714)
能力值: ( LV13,RANK:283 )
在线值:
发帖
回帖
粉丝
19
向楼主学习.
2009-8-15 21:49
0
雪    币: 1450
活跃值: (35)
能力值: (RANK:680 )
在线值:
发帖
回帖
粉丝
20
18L的思路不错, good  

若要更WS的话,  可以设计成3进程, 1和2 负责两套不同VM指令系统. 3中放第二套VM指令系统的Handlers.
2009-8-15 22:19
0
雪    币: 97
活跃值: (30)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
21
这样也太。。。
2009-8-15 23:03
0
雪    币: 290
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
看id 看成debugman了。
2009-8-16 06:53
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
看 不 懂    呵呵
2009-8-18 16:46
0
雪    币: 203
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
下载下来看看
2009-8-22 10:44
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
学习了,谢谢楼主
2009-8-22 13:26
0
游客
登录 | 注册 方可回帖
返回
//