修改钩子函数的out参数 [英] Modifying an out parameter of a hooked function
问题描述
我想挂钩EnumProcesses(),作为第一个练习,从它填充的数组中删除一些进程ID。
这是一个out参数所以我不得不让函数执行,然后做我的脏东西。
基本思路,使用可以注入进程的DLL:
- hook EnumProcesses()(做了这个)
- 复制第一个(_Out_ DWORD * pProcessIds)和3rd(_Out_ DWORD * pBytesReturned)参数的地址(做了这个,但我存储了它们在一个全局变量中)
- 保存当前的返回地址(在[ebp + 4])(做了这个,但也在一个全局变量中)
- 改变回报地址,所以函数将在我的一个函数中返回
- 将控制权交还给EnumProcesses()
现在不是回到它被调用的地方,而是转到一个我的朋友们,它们将:
- 将eax的返回值保存到局部变量中(只是为了安全)
- 遍历数组进程ID,删除我要删除的内容
- 将返回值放回eax中
- 推送堆栈上的返回值,返回
这个按预期工作,有一个我不喜欢的小缺点:当我的原始程序(称为EnumProcesses的人)存在时,我得到:运行时检查失败#2 - 堆栈周围变量'allProcesses'已损坏,allProcesses是用作我的EnumProcesses调用的第一个参数的变量。
结果数组是预期的数组,但我不喜欢我的堆栈损坏。而且我也想知道我搞砸了哪里,
这里我保存了参数地址并更改了返回地址:
< pre lang =c ++> __ declspec ( naked )
void
__ stdcall
InterceptEnumProc()
{
DWORD_PTR retAdr;
FARPROC变化;
HMODULE hModule;
FARPROC oldFuncAdr;
LPBYTE pJmpAdr;
__ asm
{
// 重做我们用钩子写的叮咬
sub edi , edi
push ebp
mov ebp , esp
sub esp ,_ _ LOCAL_SIZE
mov eax ,[ ebp + 4 ] // 返回地址
mov g_returnAdrEnumProc, eax
mov eax ,[ ebp + 8 ] // * pProcessIds
mov g_pProcIds, eax
mov eax ,[ ebp + 16 ] // * pBytesReturned
mov g_pBytedReturned, eax
// 保存寄存器
push esi
推送 ebx
};
change =(FARPROC)ChangeEnumProc; // 更改输出
retAdr =(DWORD_PTR)更改;
hModule = NULL;
oldFuncAdr = NULL;
hModule = LoadLibrary(TEXT( Psapi.dll));
// 错误检查此处省略了可能的LoadLibrary故障
oldFuncAdr = GetProcAddress( hModule,
EnumProcesses);
// 错误检查此处省略了可能的GetProcAddress
pJmpAdr =(LPBYTE)(DWORD_PTR)oldFuncAdr + 5 ;
__ asm
{
pop ebx
pop esi
mov eax ,retAdr // 新的返回地址
mov [ ebp + 4 ], eax
mov esp , ebp
jmp pJmpAdr
};
}
我在这里更改结果:
__ declspec ( naked )
void
__ stdcall
ChangeEnumProc()
{
DWORD rValue;
DWORD bytesReturned;
DWORD procsReturned;
DWORD i;
DWORD currentProcId;
DWORD poz;
/ * 这就是问题:
__asm
{
mov rValue,eax //保存返回值
};
* /
__ asm
{
// 创建一个stacj框架
push ebp
mov ebp , esp
sub esp ,_ _ LOCAL_SIZE
mov rValue, eax
push esi
push ebx
};
bytesReturned = *(DWORD *)g_pBytedReturned;
procsReturned = bytesReturned / sizeof(DWORD);
currentProcId = GetCurrentProcessId(); // 我只是从结果中隐藏当前流程
for (i = 0 ; i< procsReturned; i ++)
{
DWORD procId;
procId = * g_pProcIds;
if (procId == currentProcId)
{
poz = i;
break ;
}
g_pProcIds ++;
}
g_pProcIds ++;
for (i = poz + 1 ; i< procsReturned; i ++ )
{
*(g_pProcIds - 1 )= * g_pProcIds;
g_pProcIds ++;
}
// 同时,更改调用者应该的进程ID数量查找
bytesReturned = bytesReturned - sizeof (DWORD);
*(DWORD *)g_pBytedReturned = bytesReturned;
__ asm
{
pop ebx
pop esi
mov esp , ebp
pop ebp
mov eax ,rValue
jmp g_returnAdrEnumProc // 返回来电者
};
}
全局变量:
PDWORD g_pProcIds;
PDWORD g_pBytedReturned;
DWORD_PTR g_returnAdrEnumProc;
EnumProcesses致电:
DWORD allProcesses [ 1024 ];
DWORD bytesReturned = 0 ;
if (!EnumProcesses(allProcesses,
sizeof (allProcesses) ),
& bytesReturned))
{
// 等等
}
挂钩是基本内联挂钩,而不是写入前5个字节API。
当我操纵这些指针时,我认为我做了一些错误的事情。我利用了EnumProcesses需要第一个和第三个参数指针的事实,但我不确定整个方法有多正确。
作为一个方面 - 注意:我正在考虑松开全局变量,并在ChangeEnumProc的开头添加一些空格(用NOP填充它),然后用我需要的数据写入该空间。我也想对这个想法有一些反馈(好像过度杀人,但我从不喜欢全局变量)。
编辑:发现问题有点一步一步地跑。在为局部变量保留堆栈空间之前,我正在为局部变量赋值。编辑上面的代码来说明它。我仍然愿意接受反馈和建议。
I want to hook EnumProcesses() and, as a first exercise, remove some process IDs from the array it populates.
This is an out parameter so I had to let the function to execute, and then to do my dirty thing.
Basic idea, using a DLL that can be injected into a process:
- hook EnumProcesses() (did this)
- copy the address of the first (_Out_ DWORD *pProcessIds) and 3rd (_Out_ DWORD *pBytesReturned) parameters (did this, but I store them in a global variable)
- save current return address (at [ebp + 4]) (did this, but also in a global variable)
- change the return address so the function will return in one of my functions
- give control back to EnumProcesses()
Now instead of going back to where it was called, it goes to one of my friends, which will:
- save the return value from eax into a local variable (just to be safe)
- iterate through the array of process IDs, removing what I want to remove
- put back the return value in eax
- push the return value on the stack, return
This works as intended with one little drawback which I don't like: when my original program (the one who called EnumProcesses) exists I get: "Run-Time Check Failure #2 - Stack around the variable 'allProcesses' was corrupted", allProcesses is the variable used as the first parameter for my EnumProcesses call.
The resulting array is the expected one, but I don't like having my stack corrupted. And I also want to know where I messed up,
Here I save the out parameters addresses and I change the return address:
__declspec(naked)
void
__stdcall
InterceptEnumProc()
{
DWORD_PTR retAdr;
FARPROC change;
HMODULE hModule;
FARPROC oldFuncAdr;
LPBYTE pJmpAdr;
__asm
{
// redo the bites we over written with our hook
sub edi, edi
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
mov eax, [ebp + 4] // return address
mov g_returnAdrEnumProc, eax
mov eax, [ebp + 8] // *pProcessIds
mov g_pProcIds, eax
mov eax, [ebp + 16] // *pBytesReturned
mov g_pBytedReturned, eax
// save registers
push esi
push ebx
};
change = (FARPROC)ChangeEnumProc; // change the output
retAdr = (DWORD_PTR)change;
hModule = NULL;
oldFuncAdr = NULL;
hModule = LoadLibrary(TEXT("Psapi.dll"));
// error checks for possible LoadLibrary failure omitted here
oldFuncAdr = GetProcAddress(hModule,
"EnumProcesses");
// error checks for possible GetProcAddress omitted here
pJmpAdr = (LPBYTE)(DWORD_PTR)oldFuncAdr + 5;
__asm
{
pop ebx
pop esi
mov eax, retAdr // new return address
mov [ebp + 4], eax
mov esp, ebp
jmp pJmpAdr
};
}
And here I change the result:
__declspec(naked)
void
__stdcall
ChangeEnumProc()
{
DWORD rValue;
DWORD bytesReturned;
DWORD procsReturned;
DWORD i;
DWORD currentProcId;
DWORD poz;
/* this was the problem:
__asm
{
mov rValue, eax // save return value
};
*/
__asm
{
// create a stacj frame
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
mov rValue, eax
push esi
push ebx
};
bytesReturned = *(DWORD* )g_pBytedReturned;
procsReturned = bytesReturned/sizeof(DWORD);
currentProcId = GetCurrentProcessId(); // I just hide the current process from the results
for(i = 0; i < procsReturned; i++)
{
DWORD procId;
procId = *g_pProcIds;
if(procId == currentProcId)
{
poz = i;
break;
}
g_pProcIds++;
}
g_pProcIds++;
for(i = poz + 1; i < procsReturned; i++)
{
*(g_pProcIds - 1) = *g_pProcIds;
g_pProcIds++;
}
// also, change the number of process IDs the caller should look for
bytesReturned = bytesReturned - sizeof(DWORD);
*(DWORD* )g_pBytedReturned = bytesReturned;
__asm
{
pop ebx
pop esi
mov esp, ebp
pop ebp
mov eax, rValue
jmp g_returnAdrEnumProc // go back to the caller
};
}
The global variables:
PDWORD g_pProcIds;
PDWORD g_pBytedReturned;
DWORD_PTR g_returnAdrEnumProc;
EnumProcesses call:
DWORD allProcesses[1024];
DWORD bytesReturned = 0;
if(!EnumProcesses(allProcesses,
sizeof(allProcesses),
&bytesReturned))
{
// etc, etc
}
The hooking is basic inline hooking, over writing the first 5 bytes of the API.
I think I do some wrong things when I manipulate those pointers. I took advantage of the fact that EnumProcesses needs pointers for the first and 3rd argument, but I'm not sure how correct is the whole approach.
As a side-note: I was thinking to loose the global variables, and let some space at the beginning of ChangeEnumProc (fill it wit NOPs) and then over write that space with the data I need. I will also like some feedback on this idea too (it seems like over kill, but I never liked global variables).
EDIT: found the problem with a bit of step by step running. I was assigning a value to a local variable before I reserved space on the stack for my local variables. Edited the code above to illustrate it. I'm still open to feedback and advises.
这篇关于修改钩子函数的out参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!