找回密码
 注册
搜索
热搜: 回贴

控制其他程序内部函数(汇编源代码),函数,系统底层

2010-1-22 18:44| 发布者: admin| 查看: 79| 评论: 0|原作者: 段誉


控制其他程序内部函数(汇编源代码),函数,系统底层
2008年06月23日 星期一 下午 01:37
首先声明本文为菜鸟级,高手就不要看了,呵呵。关于如何控制其它程序内部函数的问题,我在网上找了很久也没有找到比较全面的技术资料和文档。于是我开始翻看《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.加入文本 (查看字节集 (数据封包))

.如果真结束





.判断循环尾 ()



返回 ()







其实程序很简单,就是创建一个通信管道,然后监听,当有连接后,开始循环读取管道内数据,当数据长度不为零时就把数据输出到一个编辑框里。

也可能因为我是菜鸟,所以只能用这种方式控制其它进程内的函数,如果哪位大侠有更好的更简单的控制办法,请告诉我好么?


最新评论

QQ|小黑屋|最新主题|手机版|微赢网络技术论坛 ( 苏ICP备08020429号 )

GMT+8, 2024-9-30 01:27 , Processed in 0.230107 second(s), 12 queries , Gzip On, MemCache On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

返回顶部