首页
社区
课程
招聘
控制其他程序内部函数(汇编源代码)[原创]
发表于: 2006-6-3 09:57 8850

控制其他程序内部函数(汇编源代码)[原创]

2006-6-3 09:57
8850

首先声明本文为菜鸟级,高手就不要看了,呵呵。关于如何控制其它程序内部函数的问题,我在网上找了很久也没有找到比较全面的技术资料和文档。于是我开始翻看《WINDOWS核心编程》及《WINDOWS网络编程》两本资料,并且进行了实际测试。
    首先说下大体思路,如果要控制一个进程的内部函数,我认为功能上大体可分为两个部分。第一部分需要控制的就是函数的参数,能够取出函数的参数;第二部分就是能够根据自己的需要调用这个函数。今天我想先根据我的经验给大家介绍下第一部分的内容,有说的不对的地方欢迎大家指正。
    要控制一个进程,方式有很多,比如HOOK等技术,但是我个人认为那些技术不够灵活,在功能上也不够全面,也可能是我对HOOK的理解和应用不够深入吧。我采取的方法是利用创建远程线程的办法,插入一个DLL到目标进程,然后利用DLL内部代码捕获指定函数的指定参数,并且传送到我们自己的一个进程。具体分为以下几部进行的:
1.首先建立一个EXE文件用于插入DLL到目标进程。
2.DLL被成功插入后,立即执行其初始化代码。
3.捕获函数参数。
4.利用管道通信技术,把参数值传回我们自己的程序。
以下我分步具体讲解实施细节。
1.首先建立一个EXE文件用于插入DLL到目标进程。
这个过程在《WINDOWS核心编程》里面介绍的比较详细,不过里面是C语言代码,我是利用汇编语言实现的。首先我们需要找到目标进程的窗口,然后取得进程的句柄,然后以我们需要的方式打开进程,然后在目标进程内创建一端内存,用来存放我们的DLL名称字符串,然后再获得Kernel32.dll的LoadLibraryA函数的内存入口地址,然后利用这个函数装载我们的DLL到目标进程,然后利用CreateRemoteThread函数在目标进程中创建一个线程,用来执行DLL的初始化代码。
汇编源代码如下:
               .386

               .model flat,stdcall

               option casemap:none

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

include        windows.inc

include        user32.inc

includelib     user32.lib

include        kernel32.inc

includelib     kernel32.lib

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

               .data

hGameHandle    dd     ?    ;进程句柄
hGameClass  dd  ?  ;游戏窗口类名
dwProcessID  dd  ?

szBuffer       db     256 dup (?)
dwMemAdd  dd  ?  ;创建的内存空间的地址
dwSizeDllName  dd  ?  ;欲插入的DLL文件名长度
dwLoadLibraryW  dd  ?   ;LoadLibraryW函数地址
dwRemoteThreadID  dd  ?  ;创建的远程线程的ID
dwExitCode  dd  ?

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

               .const

szCaption      db     'SendMessage',0

szStart        db     'Press OK to start SendMessage, param: %d',0

szReturn       db     'SendMessage returned!',0

szGameClass  db  'FSOnline Class',0

szText         db     'Text send to other windows',0

szNotFound     db     'Receive Message Window not found!',0
szError    db  '无法打开进程',0
szSucceed  db  '打开进程成功',0
szMemError  db  '申请内存失败',0
szMemSucceed  db  '申请内存成功',0
szExit    db  '线程退出,0
szFileName  db  'c:\\mylib.dll',0
szKernel32  db  'Kernel32.dll',0
szLoadLibraryW  db  'LoadLibraryA',0

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

               .code

start:

              invoke  FindWindow,addr szGameClass,NULL    ;查找目标进程窗口
              mov  hGameClass,eax
        invoke GetWindowThreadProcessId,hGameClass,addr dwProcessID  ;取得目标进程ID
               .if    dwProcessID
                     

                      invoke  wsprintf,addr szBuffer,addr szStart,dwProcessID

                      invoke  MessageBox,NULL,offset szBuffer,\

                              offset szCaption,MB_OK
                              
                     
                     
                      invoke OpenProcess,PROCESS_QUERY_INFORMATION or PROCESS_CREATE_THREAD or \  ;以指定权限打开目标进程
                          PROCESS_VM_OPERATION or PROCESS_VM_WRITE,FALSE, dwProcessID
                ; Required by Alpha// For CreateRemoteThread// For VirtualAllocEx/VirtualFreeEx// For WriteProcessMemory  

             .if eax==NULL
               invoke  MessageBox,NULL,addr szError,addr szCaption,MB_OK
               invoke ExitProcess,NULL  
             .else
               mov hGameHandle,eax
               invoke  MessageBox,NULL,addr szSucceed,addr szCaption,MB_OK
             .endif  
               
             mov eax,sizeof szFileName
             mov dwSizeDllName,eax
             invoke VirtualAllocEx,hGameHandle, NULL, dwSizeDllName, MEM_COMMIT,PAGE_READWRITE ;创建内存地址空间,用来传递DLL名称字符串
            
             .if eax==NULL
               invoke  MessageBox,NULL,addr szMemError,addr szCaption,MB_OK
               invoke ExitProcess,NULL  
             .else
               mov dwMemAdd,eax
               invoke  MessageBox,NULL,addr szMemSucceed,addr szCaption,MB_OK
             .endif  
            
      invoke WriteProcessMemory,hGameHandle, dwMemAdd,addr szFileName,\
               dwSizeDllName, NULL  ;目标DLL名称写入进程内存
             invoke GetModuleHandle,addr szKernel32
             invoke GetProcAddress,eax,offset szLoadLibraryW
           
      mov dwLoadLibraryW,eax
      
      invoke CreateRemoteThread,hGameHandle, NULL, 0,dwLoadLibraryW, dwMemAdd, 0, NULL   ;创建线程
      mov dwRemoteThreadID,eax
      
      invoke WaitForSingleObject,dwRemoteThreadID, INFINITE ;等待线程结束并释放资源
      invoke  MessageBox,NULL,addr szExit,addr szCaption,MB_OK
      invoke GetExitCodeThread,dwRemoteThreadID, dwExitCode
      invoke CloseHandle,dwRemoteThreadID

      
      

               .else

                      invoke  MessageBox,NULL,offset szNotFound,\

                      offset  szCaption,MB_OK

               .endif

               invoke ExitProcess,NULL

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

               end    start

我这个人比较懒,很多出错代码没有写,还有些用于调试的代码没有删除,呵呵。
下面我们再来看看插入到目标进程内的DLL代码,这段代码的初始化代码在远程线程建立的时候即开始运行。我目前要控制的函数是目标进程的WSASend函数,不要以为这种方法只能控制API,等你看完你就明白,只要是目标进程使用的函数,不管是内部的还是外部的,都可以控制。我们的目标是WSASend函数的第2个参数,因为这个参数记录着要发送数据包的长度和数据包的内存地址,我们先来看下用OD打开目标进程后,它调用WSASend函数的过程。
005609D2    6A 00           push    0
005609D4    8906            mov     [esi], eax
005609D6    03E9            add     ebp, ecx
005609D8    6A 00           push    0
005609DA    8D4424 18       lea     eax, [esp+18]
005609DE    6A 00           push    0
005609E0    896E 04         mov     [esi+4], ebp
005609E3    2BF9            sub     edi, ecx
005609E5    8B8B FC010000   mov     ecx, [ebx+1FC]
005609EB    50              push    eax
005609EC    6A 01           push    1
005609EE    56              push    esi
005609EF    51              push    ecx
005609F0    897C24 30       mov     [esp+30], edi
005609F4    FF15 5CC75700   call    [57C75C]                         ; WS2_32.WSASend

可以看到我们要获得的函数的参数在ESI中保存,那么我们插入DLL的初始化代码就把
005609EB    50              push    eax
这一条指令改为跳转到我们DLL的获取参数的代码的地址处,为什么选从这里开始呢,因为在这里之前,ESI已经被付值,并且JMP指令占用的是5字节内存,正好到
005609F0    897C24 30       mov     [esp+30], edi
这里,我们的代码取出ESI的数值后,在JMP到005609F0,即可不影响这个进程的正常工作。好了,看下我们的DLL代码:
    .386
    .model flat, stdcall
    option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include    windows.inc
include    user32.inc
includelib  user32.lib
include    kernel32.inc
includelib  kernel32.lib
includelib  ws2_32.lib
include    ws2_32.inc

PATCH_POSITION  equ     005609ebh
WSASendBuf  struct
  dwSize  dd  ?
  dwAddr  dd  ?
WSASendBuf ends
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
hModel  dd  ?      ;本模块入口地址
dwAddress  dd  ?    ;跳转回去的目标地址
lpBuffers  dd  ?    ;数据包地址

dwProcAddr  dd  ?    ;跳转函数的入口地址
   
hPipe    dd  ?    ;管道句柄
dwSize    dd  ?    ;已经发送数据的长度、字节
dwMemAdd  dd  ?    ;发送数据的地址
dwPackSize  dd  ?    ;需要发送数据的长度、字节
lpBufRecv  dd  ?    ;接收管道数据包地址
szBuffer  db  1024 dump (?)  ;数据处理空间
hSocket    dd  ?    ;SOCKET句柄
dwWsasend  WSASendBuf  ?
dwSizePipe  dd  ?    ;模拟发送的数据长度
  .const
szText  db  '载入成功',0
szProc  db  '_lanjie',0
szModel  db  'mylib.dll',0
szPipeName  db  '\\.\Pipe\masm',0

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .code

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DllEntry  proc  _hInstance,_dwReason,_dwReserved
               
                mov     eax,_dwReason

                .if     eax ==  DLL_PROCESS_ATTACH

                        invoke  MessageBox,NULL,addr szText,addr szModel,MB_OKCANCEL
                                                
                        invoke  GetModuleHandle,addr szModel  ;获取我们DLL模块的入口地址
                        mov hModel,eax
                        invoke  GetProcAddress,hModel,addr szProc
                        mov dwProcAddr,eax
                        
                        mov  edx,005609ebh    ;获取我们需要跳转到我们代码相关函数的地址
                        mov al,0e9h
                        mov byte ptr [edx],al
                        sub dwProcAddr,005609ebh
                        sub dwProcAddr,5
                        mov eax,dwProcAddr
                        mov dword ptr [edx+1],eax
                        
                        mov dwAddress,005609f0h
;修改005609EB 地方的指令为跳转指令                        
                        
invoke WaitNamedPipe,addr szPipeName,500h    ;连接我们管道通信的服务器,并获得管道句柄

invoke CreateFile,addr szPipeName,GENERIC_READ or GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
mov hPipe,eax
                             

                .elseif eax ==  DLL_THREAD_ATTACH

                    ;释放库使用的资源

                .elseif eax ==  DLL_THREAD_DETACH

                   ;为新的线程分配资源

                .elseif eax ==  DLL_PROCESS_DETACH

                    ;为线程释放资源

                .endif

                ret

DllEntry  Endp

_lanjie  proc    ;我们处理函数参数的代码
  
push eax               ;因为跳转指令覆盖掉了这4条指令,所以在这里我们需要把这四条指令加入
push 1
push esi
push ecx
mov lpBuffers,esi     ;取出我们感兴趣的参数到一个我们的变量里
mov hSocket,ecx

mov eax,lpBuffers  ;对此参数进行相应处理,这里是取出发送封包的长度
mov eax,[eax]
mov dwPackSize,eax

mov eax,lpBuffers  ;这里取出发送封包的地址
mov eax,[eax+4h]
mov dwMemAdd,eax

invoke WriteFile,hPipe,dwMemAdd,dwPackSize,offset dwSize,NULL
;这里把我们取得的封包数据,通过上面已经取得的管道句柄发送给我们的管道服务器
jmp [dwAddress]
;跳回005609F0处继续进程运行

  retn

_lanjie endp

End     DllEntry
下面就是这个DLL插入到目标进程后,目标进程调用WSASend处的代码
005609D2    6A 00           push    0
005609D4    8906            mov     [esi], eax
005609D6    03E9            add     ebp, ecx
005609D8    6A 00           push    0
005609DA    8D4424 18       lea     eax, [esp+18]
005609DE    6A 00           push    0
005609E0    896E 04         mov     [esi+4], ebp
005609E3    2BF9            sub     edi, ecx
005609E5    8B8B FC010000   mov     ecx, [ebx+1FC]
005609EB  - E9 C3064404     jmp     mylib._lanjie
005609F0    897C24 30       mov     [esp+30], edi
005609F4    FF15 5CC75700   call    [57C75C]                         ; WS2_32.WSASend

怎么样,看到了吧
005609EB  - E9 C3064404     jmp     mylib._lanjie
调到了我们的代码处。

然后再来看下我们建立的用于接收参数信息的进程管道通信的服务器端,这一端我使用的是E语言编写的代码,E语言操作简单,不用写那么多API即可,呵呵
.版本 2
.版本 2
.支持库 EThread
.支持库 EInterProcess

.程序集 窗口程序集1

.子程序 __启动窗口_创建完毕

.子程序 _按钮1_被单击

启动线程 (&读取封包, )

.子程序 读取封包
.局部变量 管道句柄, 整数型
.局部变量 数据封包, 字节集
.局部变量 数值, 整数型
.局部变量 连接, 逻辑型

管道句柄 = 创建命名管道 (“masm”)
连接 = 监听命名管道 (管道句柄)

.判断循环首 (读命名管道 (管道句柄, 数据封包))
    数值 = 取字节集长度 (数据封包)
    .如果真 (数值 > 0)
        编辑框1.加入文本 (查看字节集 (数据封包))
    .如果真结束

.判断循环尾 ()

返回 ()

其实程序很简单,就是创建一个通信管道,然后监听,当有连接后,开始循环读取管道内数据,当数据长度不为零时就把数据输出到一个编辑框里。
也可能因为我是菜鸟,所以只能用这种方式控制其它进程内的函数,如果哪位大侠有更好的更简单的控制办法,请告诉我好么?

由 回心转意 于 2006-06-03 09:52 最后编辑


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (10)
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lcw
2
先顶一下哈~!!
2006-6-3 13:40
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
关注第2部分
2006-6-3 17:45
0
雪    币: 2384
活跃值: (766)
能力值: (RANK:410 )
在线值:
发帖
回帖
粉丝
4
等你的第二部分。
2006-6-3 20:48
0
雪    币: 235
活跃值: (41)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
5
顶一个!
对你的赞美有如滔滔江水!呵呵
2006-6-3 21:04
0
雪    币: 206
活跃值: (10)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
6
明天开始调试第二部分,调试好后,马上贴上来
2006-6-3 22:03
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
7
写的不错,思路的比较全!!
用兴趣的话,等这篇结束后可以考虑去领个任务做做
2006-6-3 22:31
0
雪    币: 214
活跃值: (70)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
8
精采啊..不过用易语言那部分就看得别扭...
2006-6-4 17:17
0
雪    币: 161
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
API HOOK的另一种形式
2006-6-6 07:27
0
雪    币: 248
活跃值: (1096)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
一头雾水。
能否给一个工作的实例,体验一番?
2006-6-25 09:31
0
雪    币: 200
活跃值: (24)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
汇编的东西有些难懂。。。。
2006-6-27 18:26
0
游客
登录 | 注册 方可回帖
返回
//