首页
社区
课程
招聘
[原创]劫持正在运行进程的EIP注入代码的方法
发表于: 2010-10-20 17:23 32938

[原创]劫持正在运行进程的EIP注入代码的方法

2010-10-20 17:23
32938

【标题】: 劫持正在运行进程的EIP注入代码的方法
【作者】: 火血狼(QQ:65845743)
【工具】: VC++2005, WINXP, WIN7
【声明】: 1.禁止用来做破坏;2.转载请告知作者.
-----------------------------------------------------------------------------
【灵感来源】
近日,在读<<Windows内核编程>>的时候,偶然发现,一个函数GetThreadContext,该函数可以使用户级的代码访问并操作指定线程的上下文:CONTEXT,通过这个CONTEXT里的一个字段EIP,我们可以得到CPU寄存器的当前值。当时就想,如果通过这个EIP允许修改,不就可以控制程序流程了吗?查了查资料,果然可以被另外用户态的进程修改,于是做了如下实验,实验目标:劫持EIP,执行自己代码,然后恢复EIP。

【第一步】修改另外进程的EIP寄存器
   SuspendThread(hThread);//这里先让线程挂起,避免EIP乱跑
   CONTEXT context;
   context.ContextFlags = CONTEXT_CONTROL;
   GetThreadContext(hThread, &context);
   DWORD dwEIP = context.Eip;
   context.ContextFlags = CONTEXT_CONTROL;
   //
   context.Eip = 0x000000; //这里随便设一个EIP值,导致目标进程崩溃
   SetThreadContext(hThread, &context);
   ResumeThread(hThread);

通过上面的代码实验,得出结论,EIP的设置是不受限制的。(其中hThread为目标进程的主线程句柄,至于如何得到,很多地方有例子,这里不再普及基础知识)

【第二步】构建合法的EIP值,引导目标进程EIP进入指定代码
在进行这一步的时候我遇到了以下几个问题:1.目标进程只能访问自身的虚拟内存地址;2.如何向内存中放入指定代码。
要解决第一个问题,就要用到
PVOID pCodeRemote = VirtualAllocEx(hProcess, NULL, (size_t)dwCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE));
这个代码将在目标进程的虚拟内存里申请一块儿大小为dwCodeSize的鲜活内存空间,并把内存起始指针返回。(并且页权限为可执行,可读写)
解决第二个问题,要用到汇编啦
写这样一个函数void __declspec(naked) __stdcall ASM_RemoteFunc(){
_asm{ int 3 }
}
然后把这个函数Copy到刚才的内存中,用到代码
WriteProcessMemory(hProcess, pCodeRemote, (PVOID)ASM_RemoteFunc, (size_t)dwCodeSize, NULL)
到这里,又有疑问了,怎么确定dwCodeSize呢?嗯,可以在函数末尾加个特殊值,然后查找到这个值,就可以确定函数的末尾地址了,嘿嘿,来试试
(naked修饰这里也不解释,请读者自行查资料)
void __declspec(naked) __stdcall ASM_RemoteFunc(){
_asm{ int 3; push 0x12345679 }
}

这样写搜索代码
void* find_ptr(void* mem, DWORD dwv)
{
void* ret_ptr;
__asm
{
  mov eax, mem
  jmp comp
diff: inc eax
comp: mov ebx, [eax]
  cmp ebx, dwv
  jnz diff
  mov ret_ptr, eax
}
return ret_ptr;
}
最后,函数大小可以通过下面代码来计算:
DWORD dwCodeStart = (DWORD)ASM_RemoteFunc; PVOID ptrCodeLocal = (PVOID)dwCodeStart; DWORD dwCodeEnd = (DWORD)find_ptr(ptrCodeLocal, PLACE_HOLDER_END) + 4; DWORD dwCodeSize = dwCodeEnd - dwCodeStart;
好了,第二部问题解决了,实验一下,果然,目标进程产生中断异常,说明执行了指定代码,但是最终程序还是会崩溃。如何能让程序不崩溃呢?

【第三步】寄存器和堆栈恢复
先分析一下程序为啥崩溃:因为我们改变EIP的时候,其代码有可能处于任何位置,执行完我们的代码后,并没有恢复原来的EIP指针,也没有保护好各个寄存器的值,目标进程会出现不可预计的现象。
如何恢复EIP呢,写过shellcode的人都知道,ret可以做到这一点,于是我们先push当前的EIP,然后,再结束的时候ret,就会返回到原来的地方执行EIP啦,于是这样写:
void __declspec(naked) __stdcall ASM_RemoteFunc(){
_asm{
      push 0x12345670
   ret
      push 0x12345679
}
}
呵呵,有人奇怪了,为啥用0x12345670而不用真正的EIP呢,因为这会儿我们无法得到,运行的时候才有。那怎么办呢?不用急,我们用找函数大小的方法找到0x12345670的地址,然后把目标进程的当前EIP,写入,不就行啦。
void * placeHolderEIP = find_ptr(ptrCodeLocal, 0x12345670);
memcpy((void *)placeHolderEIP, &dwEIP, 4);
运行,安静的通过,哈哈。
下面保护寄存器,并且调用一些有意思的代码:
#define PLACE_HOLDER_EIP 0x12345670
#define PLACE_HOLDER_ST1 0x12345671
#define PLACE_HOLDER_ST2 0x12345672
#define PLACE_HOLDER_FUN 0x12345678
#define PLACE_HOLDER_END 0x12345679
void __declspec(naked) __stdcall ASM_RemoteFunc(){
_asm{
      push PLACE_HOLDER_EIP;
      pushfd;
      pushad;
                  push MB_OK | MB_ICONINFORMATION
                  push PLACE_HOLDER_ST1;
                  push PLACE_HOLDER_ST2;
                  push NULL
      mov eax, PLACE_HOLDER_FUN;
      call eax;
      popad;
      popfd;
      ret;
      push PLACE_HOLDER_END
}
}

按照同样的内存查找的方法,把指定地方放入自己的值:
   HMODULE hModule = 0;
   if (!(hModule = LoadLibrary(_T("User32.dll")))) return false;
   DWORD funRemote = 0;
   if (!(funRemote = (DWORD)GetProcAddress(hModule, "MessageBoxA"))) return false;

   PVOID strRemote1 = NULL;
   if (!(strRemote1 = VirtualAllocEx(hProcess, NULL, (size_t)(strlen(strPam1) + 1), MEM_COMMIT, PAGE_READWRITE))) return false;
   PVOID strRemote2 = NULL;
   if (!(strRemote2 = VirtualAllocEx(hProcess, NULL, (size_t)(strlen(strPam2) + 1), MEM_COMMIT, PAGE_READWRITE))) return false;
   PVOID pCodeRemote = NULL;
   if (!(pCodeRemote = VirtualAllocEx(hProcess, NULL, (size_t)dwCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE))) return false;

   void * placeHolderEIP = find_ptr(ptrCodeLocal, PLACE_HOLDER_EIP);
   void * placeHolderST1 = find_ptr(ptrCodeLocal, PLACE_HOLDER_ST1);
   void * placeHolderST2 = find_ptr(ptrCodeLocal, PLACE_HOLDER_ST2);
   void * placeHolderFUN = find_ptr(ptrCodeLocal, PLACE_HOLDER_FUN);
   memcpy((void *)placeHolderEIP, &dwEIP, 4);
   memcpy((void *)placeHolderST1, &strRemote1, 4);
   memcpy((void *)placeHolderST2, &strRemote2, 4);
   memcpy((void *)placeHolderFUN, &funRemote, 4);

最后,把自己的函数复制到目标进程
  if (!WriteProcessMemory(hProcess, strRemote1, (LPCVOID)strPam1, strlen(strPam1), NULL)) return false;
  if (!WriteProcessMemory(hProcess, strRemote2, (LPCVOID)strPam2, strlen(strPam2), NULL)) return false;
  if (!WriteProcessMemory(hProcess, pCodeRemote, ptrCodeLocal, (size_t)dwCodeSize, NULL)) return false;
然后,ResumeThread(hThread),弹出messagebox,标题是strRemote1,内容为strRemote2指定的字符串值。
点ok后,目标进程安全无恙。大功告成!!

【结论】 EIP就是一切!!


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (35)
雪    币: 88
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
不错的方法。顶一个。
收藏了,谢谢。
2010-10-20 18:18
0
雪    币: 73
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wil
3
不够精巧。应该获取目标进程的esp,代码放到堆栈里面,执行完之后eip恢复起始位置,什么痕迹都找不到的
2010-10-20 19:25
0
雪    币: 94
活跃值: (475)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
这种方法在病毒中早就有人实现了
2010-10-21 08:18
0
雪    币: 145
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
方法不错,主要是写入目标程序的shellcode不好弄
2010-10-21 08:58
0
雪    币: 225
活跃值: (223)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
写入的函数还要涉及到代码重定位
2010-10-21 11:18
0
雪    币: 8221
活跃值: (2806)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
好帖子...学习了...谢谢分享
2010-10-24 10:46
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Nod
8
劫持eip意义不大,不过也可以借鉴一些思路。
2010-10-24 20:05
0
雪    币: 84
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
学习了……谢谢
2010-10-24 20:49
0
雪    币: 370
活跃值: (15)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
10
远程 线程 进程 注入 劫持EIP
2010-10-24 21:34
0
雪    币: 394
活跃值: (131)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
11
方法很好,经过尝试,成功插入进程运行程序,但是360把这种方法认为是远程注入。

要在进程中插入任意EXE程序的实例可以参考暗组darkst 5.0的服务端,解决了重定位的问题。

写入目标程序的shellcode最好是采用动态生成输入表API函数地址,采用自定位代替重定位的方法。
2010-10-24 23:04
0
雪    币: 808
活跃值: (10)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
12
老技术翻新。。。。。
提供思路也不错。
2010-10-25 09:29
0
雪    币: 8221
活跃值: (3892)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
重定位之类的都是小问题了
WriteProcessMemory 这个搞好才是关键
不然杀软嗷嗷叫了
2010-10-25 13:28
0
雪    币: 112
活跃值: (48)
能力值: ( LV9,RANK:320 )
在线值:
发帖
回帖
粉丝
14
这不就是  0x年前的 掏空进程嘛
2010-10-26 18:55
0
雪    币: 191
活跃值: (345)
能力值: ( LV9,RANK:450 )
在线值:
发帖
回帖
粉丝
15
调试器中的指定新EIP位置是这功能不?
2010-10-26 23:27
0
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
问一下
memcpy((void *)placeHolderEIP, &dwEIP, 4);
为什么我调试的时候错误:access violation
调试环境vc6.0+sp3
2010-11-9 22:31
0
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
哪个好人能提示一下
2010-11-10 09:09
0
雪    币: 478
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
关注。。。标记先。。
2010-11-10 09:41
0
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
在线等待中.......
2010-11-10 10:21
0
雪    币: 303
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
谢谢。好思路。
2010-11-10 15:53
0
雪    币: 359
活跃值: (41)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
21
这方法倒是用过,不过有一点一直都不太放心,寄存器还有浮点寄存器,MMX寄存器等等。虽然一般来说自己写的代码不会改变这些寄存器,但是调用的API会不会改变它们真没有一个准确的说法。而且貌似没有什么好方法对它们进行还原。。。
2010-11-10 19:18
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
值得参考!!呵呵!
2010-11-10 19:27
0
雪    币: 324
活跃值: (113)
能力值: ( LV15,RANK:280 )
在线值:
发帖
回帖
粉丝
23
通过这样的方法让目标进程执行代码,还不如直接CreateRemoteThread执行注入代码来的简单。更没有利用缓冲区溢出执行shellcode来的通用。
2010-11-11 13:16
0
雪    币: 261
活跃值: (83)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
24
针对正在运行的QQ程序写一个例子放上来吧
2010-11-11 14:48
0
雪    币: 1149
活跃值: (888)
能力值: ( LV13,RANK:260 )
在线值:
发帖
回帖
粉丝
25
好东西  提供一组思路。。但 确实 往进程或exe问文件写东西的时候 会被杀软 拼命的报告出来。。。
2010-11-21 21:30
0
游客
登录 | 注册 方可回帖
返回
//