以下是运行NOTEPAD.EXE,在01001EDC处OD中断结果:
01001ED6 |. 8B3D 48120001 MOV EDI,DWORD PTR DS:[<&USER32.CheckMenu>; |USER32.CheckMenuItem
01001EDC |. 50 PUSH EAX ; |hMenu
01001EDD |. FFD7 CALL EDI ; \CheckMenuItem可以通过OD的右上角查看到EAX的值。请问如何编写一个程序 然后读取01001EDC处EAX的值呢?
01001ED6 |. 8B3D 48120001 MOV EDI,DWORD PTR DS:[<&USER32.CheckMenu>; |USER32.CheckMenuItem
01001EDC |. 50 PUSH EAX ; |hMenu
01001EDD |. FFD7 CALL EDI ; \CheckMenuItem可以通过OD的右上角查看到EAX的值。请问如何编写一个程序 然后读取01001EDC处EAX的值呢?
不知道你有没有听说过OD,我们可以模仿他的思路下int 3软件断点,然后获得线程上下文就可以了。MSDN里面有大量的Debugging Functions,你可以去看下。下面是一段微型调试器的代码#include <windows.h>
#include <stdio.h>
#include <assert.h>int main()
{
DEBUG_EVENT d;
DWORD ;
BOOL initBP,r;
DWORD pid; initBP=0;
printf("set pid=? ");
scanf("%d",&pid);
r=DebugActiveProcess(pid); /* 开始调试 */
assert(r); printf("debugging..."); for (;;)
{
if (!WaitForDebugEvent(&d,INFINITE)) /* 等待调试事件 */
assert(0);
switch (d.dwDebugEventCode)
{
case CREATE_PROCESS_DEBUG_EVENT:
case EXIT_PROCESS_DEBUG_EVENT:
case CREATE_THREAD_DEBUG_EVENT:
case EXIT_THREAD_DEBUG_EVENT:
case LOAD_DLL_DEBUG_EVENT:
case UNLOAD_DLL_DEBUG_EVENT:
case OUTPUT_DEBUG_STRING_EVENT:
case RIP_EVENT:
=DBG_CONTINUE; /* 这些事件都不需要处理,让目标直接运行 */
break;
case EXCEPTION_DEBUG_EVENT:
if (initBP)
{
=DBG_EXCEPTION_NOT_HANDLED; /* 没处理就是没处理*/
}
else
{
initBP=1;
=DBG_CONTINUE; /* 初始断点要特别留意 */
}
break;
}
if (!ContinueDebugEvent(
d.dwProcessId,
d.dwThreadId,)) /* 继续执行目标程序 */
assert(0);
if (d.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT)
break;
}
printf("stopped\n"); return 0;
}
如上是直接附加到活动进程,如果我们学着OD,加载进程后,在OEP那里断下的话,用CreateProcess创建进程,标志位打上Debug标志即可,类似下面这样
STARTUPINFO st = {0};
PROCESS_INFORMATION pro = {0};
st.cb = sizeof(st);
CreateProcess(NULL, "d:\\test.exe ", NULL, NULL, TRUE,
DEBUG_ONLY_THIS_PROCESS,
NULL, NULL, &st, &pro); CloseHandle(pro.hThread);
CloseHandle(pro.hProcess);
DEBUG_EVENT dbe;
BOOL rc;
while(WaitForDebugEvent(&dbe, INFINITE))
{
if(dbe. dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
break;
ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId ,DBG_CONTINUE );
}
//有的代码我就略过了,在上课,不方便写很多软件断点(INT3):对应的异常是 EXCEPTION_BREAKPOINT,处理方式为恢复代码为原来字节,并将EIP减一,并设置单步以便进入单步后重新设置这个一般断点。那么现在要解决的问题就剩一个了,就是写入断点int3,OEP那边断下之后就要写,然后开始执行。这个可以用WriteProcess实现,当你初次加载之后,进程虽然断下来了,但是PE已经加载进了内存空间中。
根据偏移,把int 3对应的十六进制0xCC指令写入断下来之后,用GetThreadContext获得线程上下文就可以得到寄存器的值了这里有些注意点,看下面的代码
if (r->ExceptionCode==EXCEPTION_BREAKPOINT) /* 触发了断点中断 */
{ /* 根据中断地点查找断点记录 */
bp=find(ps->bps,MAX_BP,(DWORD)r->ExceptionAddress);
if (bp) /* 是调试器安置的断点 */
{
t=find(ps->ts,MAX_THRD,d.dwThreadId); /* 取线程上下文 */
memset(&ctx,0,sizeof(ctx));
ctx.ContextFlags=CONTEXT_FULL;
f=GetThreadContext(t->h,&ctx);
assert(f);
ctx.Eip--; /* 将指令指针值减1 */
ctx.EFlags|=TF_BIT; /* 打开CPU单步标记 */
f=SetThreadContext(t->h,&ctx); /* 向目标写入修改过的上下文 */
assert(f);
ps->write_back=bp; /* 标记该进程在单步结束后准备写回的断点 */
safe_write(ps->h,bp->addr,bp->c); /* 写入被断点覆盖的代码 */
on_bp(d.dwProcessId,d.dwThreadId,bp->addr); /* 用户处理 */
suspend_except(ps->ts,d.dwThreadId); /* 挂起除异常线程外的所有线程 */
=DBG_CONTINUE;
}
}
writeprocememory
直接看代码:
HANDLE processH=::OpenProcess(PROCESS_ALL_ACCESS,false,processid);
//processid 为进程PID
//获取PID方法很多,可以通过任务管理器查看...
//可以用枚举进程方式取得,或者获取窗口再获取进程ID等...
if(!processH)
{
return;//打开进程失败,返回
}
//读指定进程 内存数据
DWORD byread;
DWORD xuyao;
LPVOID pbase=(LPVOID)0x01001EDC;//把你的地址转换成通用指针
::ReadProcessMemory(processH,pbase,&xuyao,4,&byread);
//如果ReadProcessMemory成功,0x01001EDC的数据就保存在了变量 xuyao里了。
目标线程被中断后,再查看它的线程上下文(好象是叫这个名字)
WriteProcessMemory这些不行吗?
用WriteProcessMemory在目标进程01001EDC处插入INT3指令
程序收到调试事件后用GetThreadContext读取寄存器Context信息
这个地方应该是USER32.CheckMenuItem函数的参数