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

[原创]初探双进程保护

2009-8-5 18:06
44468

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
__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;
}
。。。。。。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
支持
分享
赞赏记录
参与人
雪币
留言
时间
Youlor
为你点赞~
2024-2-25 00:35
伟叔叔
为你点赞~
2024-1-4 04:35
QinBeast
为你点赞~
2023-12-9 00:20
shinratensei
为你点赞~
2023-10-11 04:24
PLEBFE
为你点赞~
2023-10-10 00:19
心游尘世外
为你点赞~
2023-9-28 00:07
飘零丶
为你点赞~
2023-9-20 00:36
最新回复 (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
活跃值: (82)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
7
学习之
2009-8-6 00:08
0
雪    币: 191
活跃值: (351)
能力值: ( LV9,RANK:450 )
在线值:
发帖
回帖
粉丝
8
学习了,标记一下。
2009-8-6 01:01
0
雪    币: 7327
活跃值: (3813)
能力值: (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
雪    币: 7327
活跃值: (3813)
能力值: (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结合的简单测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
///////////////// 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
雪    币: 5949
活跃值: (4289)
能力值: ( 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
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册