站长中国
设为首页 | 站长论坛

站长论坛 站长下载
您所在的位置: 站长中国 > 站长学院 > 安全技术 > 其他相关 >  正文

Win32教程29-Win32调试API 第二部分
  2007年10月15日03:57:46  评论(0条) 字体:[ ]
相关热点:

我们继续Win32调试API的话题。在本章中,我们将要学习如何修改被调试程序。

理论:

   在前面一章中,我们学会了如何装载被调试的进程以及如何处理进程中发生的事件。为了有实际用途,我们的程序应具有修改被调试程序的能力。有好几个API函数用于这一目的。

ReadProcessMemory该函数允许你去读指定的进程的内存。函数原型如下:
ReadProcessMemory proto hProcess:DWORD, lpBaseAddress:DWORD, lpBuffer:DWORD, nSize:DWORD, lpNumberOfBytesRead:DWORD

hProcess 待读进程的句柄.
lpBaseAddress 目标进程中待读内存起始地址。例如,如果你想要读目标 进程中从地址401000h开始的4个字节,该参数值应置为401000h。
lpBuffer 接收缓冲区地址
nSize 想要读的字节数。
lpNumberOfBytesRead 记录实际读取的字节数的变量地址。如果对这个值 不关心,填入NULL即可。

WriteProcessMemory 是对应于ReadProcessMemory的函数,通过它 可以写目标进程的内存。其参数和ReadProcessMemory 相同。

   理解接下去的两个函数需要一些进程上下文的有关背景知识。在象Windows这样的 多任务操作系统中,同一时间里可能运行着几个程序。Windows分配给每个线程一个 时间片,当时间片结束后,Windows将冻结当前线程并切换到下一具有最高优先级的 线程。在切换之前,Windows将保存当前进程的寄存器的 内容,这样当在该线程再 次恢复运行时,Windows可以恢复最近一次线程运行的*环境*。保存的寄存器内容总 称为进程上下文。
   现在回到我们的主题。当一个调试事件发生时,Windows暂停被调试进程,并保存其 进程上下文。由于进程被暂停运行,我们可以确信其进程上下文内容将保持不变。 可以用GetThreadContext来获取进程上下文内容,并且也可以用GetThreadContext 来修改进程上下文内容。
   这两个函数威力非凡。有了他们,对被调试进程你就具有象VxD的能力: 如改变其寄 存器内容,而在被调试程序恢复运行前,这些值将会写回寄存器中。在进程上下文中 所做的任何改动,将都会反映到被调试程序中。想象一下: 甚至可以改变eip寄存器 的内容,这样你可以让程序运行到你想要的任何地方! 在正常情况下是不可能做到这 一点的。

GetThreadContext proto hThread:DWORD, lpContext:DWORD

hThread 你想要获得上下文的线程句柄
lpContext 函数成功返回时用来保存上下文内容的结构指针。

SetThreadContext 参数相同。让我们来看看上下文的结构:

CONTEXT STRUCT

ContextFlags dd ?
;----------------------------------------------------------------------------------------------------------
;当ContextFlags包含CONTEXT_DEBUG_REGISTERS,返回本部分
;-----------------------------------------------------------------------------------------------------------
iDr0 dd ?
iDr1 dd ?
iDr2 dd ?
iDr3 dd ?
iDr6 dd ?
iDr7 dd ?

;----------------------------------------------------------------------------------------------------------
;当ContextFlags包含CONTEXT_FLOATING_POINT,返回本部分
;-----------------------------------------------------------------------------------------------------------

FloatSave FLOATING_SAVE_AREA <>

;----------------------------------------------------------------------------------------------------------
;当ContextFlags包含CONTEXT_SEGMENTS,返回本部分
;-----------------------------------------------------------------------------------------------------------
regGs dd ?
regFs dd ?
regEs dd ?
regDs dd ?

;----------------------------------------------------------------------------------------------------------
;当ContextFlags包含CONTEXT_INTEGER,返回本部分
;-----------------------------------------------------------------------------------------------------------
regEdi dd ?
regEsi dd ?
regEbx dd ?
regEdx dd ?
regEcx dd ?
regEax dd ?

;----------------------------------------------------------------------------------------------------------
;当ContextFlags包含CONTEXT_CONTROL,返回本部分
;-----------------------------------------------------------------------------------------------------------
regEbp dd ?
regEip dd ?
regCs dd ?
regFlag dd ?
regEsp dd ?
regSs dd ?

;----------------------------------------------------------------------------------------------------------
;当ContextFlags包含CONTEXT_EXTENDED_REGISTERS,返回本部分
;-----------------------------------------------------------------------------------------------------------
ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup(?) CONTEXT ENDS

   可以看出,该结构中的成员是对实际处理器的寄存器的模仿。在使用该结构之前 要在ContextFlags 中指定哪些寄存器组用来读写。如要访问所有的寄存器, 你可以置ContextFlags 为CONTEXT_FULL 。或者只访问regEbp, regEip, regCs, regFlag, regEsp 或 regSs, 应置ContextFlags 为 CONTEXT_CONTROL 。

   在使用结构CONTEXT 时还应记住: 它必须是双字对齐的,否则在NT下将得 到奇怪的结果。可以在定义前加上"align dword"。例如:

align dword
MyContext CONTEXT <>

例:

   第一个例子演示DebugActiveProcess的使用。首先,需要在Windows显示在屏幕上以前运行一个待调试程序win.exe,该程序将处于无限循环运行状态中。然后你运行例子程序,它将把自己与win.exe连接起来,并且修改win.exe的代码,这样win.exe将退出无限循环状态而显示自己的窗口。

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib
includelib \masm32\lib\user32.lib

.data
AppName db "Win32 Debug Example no.2",0
ClassName db "SimpleWinClass",0
SearchFail db "Cannot find the target process",0
TargetPatched db "Target patched!",0
buffer dw 9090h

.data?
DBEvent DEBUG_EVENT <>
ProcessId dd ?
ThreadId dd ?
align dword
context CONTEXT <>

.code
start:
invoke FindWindow, addr ClassName, NULL
.if eax!=NULL
invoke GetWindowThreadProcessId, eax, addr ProcessId
mov ThreadId, eax
invoke DebugActiveProcess, ProcessId
.while TRUE
invoke WaitForDebugEvent, addr DBEvent, INFINITE
.break .if DBEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT
.if DBEvent.dwDebugEventCode==CREATE_PROCESS_DEBUG_EVENT
mov context.ContextFlags, CONTEXT_CONTROL
invoke GetThreadContext,DBEvent.u.CreateProcessInfo.hThread, addr context
invoke WriteProcessMemory, DBEvent.u.CreateProcessInfo.hProcess, context.regEip ,addr buffer, 2, NULL
invoke MessageBox, 0, addr TargetPatched, addr AppName, MB_OK+MB_ICONINFORMATION
.elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT
.if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT
invoke ContinueDebugEvent, DBEvent.dwProcessId,DBEvent.dwThreadId, DBG_CONTINUE
.continue
.endif
.endif
invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
.endw
.else
invoke MessageBox, 0, addr SearchFail, addr AppName,MB_OK+MB_ICONERROR .endif
invoke ExitProcess, 0
end start

;--------------------------------------------------------------------
; The partial source code of win.asm, our debuggee. It's actually
; the simple window example in tutorial 2 with an infinite loop inserted
; just before it enters the message loop.
;----------------------------------------------------------------------

......
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInst,NULL
mov hwnd,eax
jmp $ <---- Here's our infinite loop. It assembles to EB FE
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.while TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.break .if (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.endw
mov eax,msg.wParam
ret
WinMain endp


分析:

invoke FindWindow, addr ClassName, NULL

   我们的程序需要用DebugActiveProcess将自己绑定到被调试程序,这需要知道被调试程序的进程Id。用GetWindowThreadProcessId 可以得到该Id,该函数需要窗口句柄作为参数,因此首先需要知道窗口句柄。
   用FindWindow, 我们先指定窗口类的名称,返回的是该类创建的窗口句柄。如 果返回NULL,则表明当前没有该类的窗口。

.if eax!=NULL
    invoke GetWindowThreadProcessId, eax, addr ProcessId
    mov ThreadId, eax
    invoke DebugActiveProcess, ProcessId

   得到进程Id后,我们调用DebugActiveProcess。这样就进入等待调试事件的循环中。

    .if DBEvent.dwDebugEventCode==CREATE_PROCESS_DEBUG_EVENT
    mov context.ContextFlags, CONTEXT_CONTROL
    invoke GetThreadContext,DBEvent.u.CreateProcessInfo.hThread, addr context

当得到 CREATE_PROCESS_DEBUG_INFO, 这意味着被调试进程已经被暂停运行了。 我们就可以对该进程动手术了。本例中,我们将用NOPs ( 90h 90h)覆盖被调试进程中的无 限循环指令(0EBh 0FEh) 。
首先,需要得到该指令的地址。由于在我们的程序绑定到被调试程序时,被调试程序已经 处于循环语句中了,eip总是指向该指令。我们所要做的是得到eip的值。我们将使用 GetThreadContext来达到此目的。将上下文结构成员中ContextFlags设置 为CONTEXT_CONTROL ,这样告诉GetThreadContext我们需要它去填充上下 文结构的成员中的"控制"寄存器。

    invoke WriteProcessMemory, DBEvent.u.CreateProcessInfo.hProcess, context.regEip ,addr buffer, 2, NULL

得到eip的值以后,可以调用WriteProcessMemory来用NOPs覆盖"jmp $" 指令,这样将使被调试程序退出无限循环。在向用户显示了信息之后,调用ContinueDebugEvent 来恢复被调试程序的运行。由于指令"jmp $"已被Nops覆盖,被调试程序将继续 显示窗口,并进入消息循环。证据是我们在屏幕上观察到了次窗口。

另一个例子与此稍有不同,它是将被调试程序从无限循环中中断。

.......
.......
.if DBEvent.dwDebugEventCode==CREATE_PROCESS_DEBUG_EVENT
    mov context.ContextFlags, CONTEXT_CONTROL
    invoke GetThreadContext,DBEvent.u.CreateProcessInfo.hThread, addr context
    add context.regEip,2
    invoke SetThreadContext,DBEvent.u.CreateProcessInfo.hThread, addr context
    invoke MessageBox, 0, addr LoopSkipped, addr AppName, MB_OK+MB_ICONINFORMATION
.......
.......

这里仍调用GetThreadContext来获取eip值,但没有去覆盖"jmp $" 指令,而是将 regEip加2,从而"跳过"该指令。结果是当被调试程序 重新获得控制权时,将恢复执行在"jmp $"后的指令。

现在你可以体会到Get/SetThreadContext的威力了。你也可以修改其他寄存器映象,这些值将直接反映到被调试程序中。甚至你可以把int 3h指令插入到被调试进程中。产生断点。


责任编辑:

收藏本文 打印 打印本文  推荐本文 告诉好友 投稿 投稿邮箱
    评论加载中…

站长排行

学院

新闻

专栏

盈利

[揭密网络黄链]中国留学生买凶专破日本
JSP语法(6)
超强弹出窗口代码,什么都挡不住
FLASH视觉特效实例之地震效果
贴吧发帖机使用教程(绝对原创)
关于数据分页(转自www.codeproject.co
ASP实现文件直接下载
Photoshop制作光感超酷效果水晶球
 遍历ASP.NET页面控件
永远的后门[经典]+查不出的后门
淘宝网卖家公然叫卖“艳照门”照片集
驳《百度Hi面世对腾讯有利》
Google绿色专家质疑黑色背景网页节省资
国内各IT企业办公环境揭秘(多图)
阿里妈妈广告卖主全攻略
站长创业源动力 主流站长站赏析
推荐阅读:80年小子的创业道理
Discuz!6.0猛将出击 最强论坛程序酷炫
我的网络,我的团队:专访李文明
百度新闻频道改版十天 流量止跌反弹翻
ECSHOP模板制作参考文档
悬挂阿里妈妈会否被百度惩罚
阿里妈妈是否是中小站长的救世主?
最强网店ECShop发新版 众多酷炫功能给
ECSHOP模板下载
土豆网,优酷网,爆米花等视频网站采集
DedeCms模板安装/制作概述
网上商店系统巅峰对决 ECShop vs ShopE
艰难的走在创业的路上 第一天
编程中国全站采集规则
性福联盟 一个不尊重站长的联盟
大脚:日赚100元—揭露最新firefox欺骗
大脚:垃圾站超级赚钱法之二—突破“站
大脚:垃圾站超级赚钱法之——前言
迅雷联盟、快车联盟收入对比
经理人必看的十个管理网站
Google Adsense的秘密 第二版
西联汇款兑付城市查询
不用SEO取得成功的10个步骤
关于做GOOGLE的五条经验
站长学院  网页设计 建站教程 图形图象 网络编程

Photoshop CS3
Photoshop CS3
不用Photoshop
不用Photoshop

DIV+CSS的开发方式 听听另外的
虚拟主机建站动易里快速生成的
VBScript特效代码 满屏幕乱跑
牛气!一个菜鸟站长的超强网站
创建、维护一个个人博客的“投
让网站流量稳步飙升的秘籍
网站推广的基本思想

新闻线索

如果你有站长界人事变动、重组并购、变革技术出现,以及产品投诉等重要新闻线索,请告诉我们,我们会给予特别关注。
0631-3653338
站长中国编辑部
站长中国24小时新闻热线: 13256307008