La InlineHook
esencia llamada es agregar o modificar la instrucción del ensamblador de la función del programa (por ejemplo, jmp、call
instrucciones) para implementar la lógica del programa que provoca un salto, es decir, la Shellcode
inyección. Al ejecutar la función de destino, el EIP
código fuente del programa , después de un salto, para evitar afectar la función normal del programa original.
Cabe señalar que cuando el programa comienza a cargarse y ejecutarse en el sistema operativo, se han configurado los atributos de sus diferentes segmentos. Por ejemplo, el segmento de código es legible y ejecutable pero no escribible, mientras que el segmento de datos solo permite lectura y escritura pero no ejecución, etc. La necesidad de API
funcionar VirtualProtect
para leer y escribir las propiedades del segmento de código ejecutable de destino está configurada para lograr el segmento de código de proceso de modificación, o el comando de escritura será rechazado, causando errores anormales de lectura y escritura.
El código de prueba es el siguiente:
#include <stdio.h>
#include <windows.h>
BYTE code[20]; //存储原硬编码
LPSTR lpszParam; //函数参数
DWORD dwRet; //指向函数返回地址
DWORD codeLength; //覆盖目标代码段数据长度
DWORD dwdstFuncAddr;
DWORD dwsrcFuncAddr;
struct Reg
{
DWORD EAX;
DWORD ECX;
DWORD EDX;
DWORD EBX;
DWORD ESP;
DWORD EBP;
DWORD ESI;
DWORD EDI;
DWORD EFL;
};
Reg reg;
VOID __declspec(naked)InlineHook()
{
__asm
{
pushad
pushfd
mov reg.EAX,eax
mov reg.ECX,ecx
mov reg.EDX,edx
mov reg.EBX,ebx
mov eax,[esp+0x10]
mov reg.ESP,eax
mov reg.EBP,ebp
mov reg.ESI,esi
mov reg.EDI,edi
mov eax,[esp]
mov reg.EFL,eax
mov eax,[esp+4*10]
mov lpszParam,eax
}
//打印寄存器的值
printf("eax: %x\n",reg.EAX);
printf("ecx: %x\n",reg.ECX);
printf("edx: %x\n",reg.EDX);
printf("ebx: %x\n",reg.EBX);
printf("esp: %x\n",reg.ESP);
printf("ebp: %x\n",reg.EBP);
printf("esi: %x\n",reg.ESI);
printf("edi: %x\n",reg.EDI);
printf("elf: %x\n",reg.EFL);
//打印函数参数
printf("函数参数: %s\n", lpszParam);
//恢复代码
memcpy((LPVOID)dwsrcFuncAddr, &code, codeLength);
__asm
{
popfd
popad
jmp dwRet
}
}
BOOL SetInLineHook(LPVOID lpdstFuncAddr, LPVOID lpsrcFuncAddr, DWORD codeLen)
{
//地址转换, 将jmp SayHello指令地址转化为真正的函数地址
dwdstFuncAddr = *(DWORD*)((DWORD)lpdstFuncAddr+1) + (DWORD)lpdstFuncAddr + 5;
dwsrcFuncAddr = *(DWORD*)((DWORD)lpsrcFuncAddr+1) + (DWORD)lpsrcFuncAddr + 5;
//将原代码的硬编码存储在code数组中
codeLength = codeLen;
memcpy(&code, (LPVOID)dwsrcFuncAddr, codeLength);
//返回地址
dwRet = dwsrcFuncAddr;
//计算jmp的硬编码
BYTE shellCode[5] = {
0xe9, 0x00, 0x00, 0x00, 0x00
};
DWORD jmpAddr = dwdstFuncAddr - (dwsrcFuncAddr + codeLength);
memcpy((LPVOID)((DWORD)&shellCode+1), &jmpAddr, 4);
//修改页面属性
DWORD flOldProtect;
DWORD ret = VirtualProtectEx(GetCurrentProcess(), (LPVOID)dwdstFuncAddr, codeLength, PAGE_EXECUTE_READWRITE, &flOldProtect);
if(!ret)
{
printf("页面属性设置失败!\n");
return FALSE;
}
//将shellCode写入代码中
memset((LPVOID)dwsrcFuncAddr, 0x90, codeLength);
memcpy((LPVOID)dwsrcFuncAddr, &shellCode, 5);
return TRUE;
}
VOID SayHello(LPSTR lpszName)
{
printf("\nhaha,%s!\n", lpszName);
}
int main()
{
//初始化code数组
memset(code, 0, 20);
//设置InLineHook
SetInLineHook(InlineHook, SayHello, 5);
SayHello("walker");
return 0;
}
Las representaciones son las siguientes: