[已解决](inline hook后jmp)汇编转C的问题
本帖最后由 xixuedafa123 于 2020-4-22 08:56 编辑已解决,核心问题在于char hwnd[] = "123456789";
这样定义就是个ascii字符串,而汇编里面是unicode。这才是问题的关键。
我把C++代码留给以后需要的人。
汇编代码功能就是替换一个老版播放器里面的码,运行结果是正常的,
我想用C++重写下, 但是替换出来,结果是错误的。
下面是我写的C++代码,vs2019通过
/////////////////////////////////////////////////////////////////
// 第13章Hook技术 《加密与解密(第四版)》 //
// //
// Author: achillis(黑月教主) //
// Blog: http://www.cnblogs.com/achillis/ //
// QQ : 344132161 //
// Email : achillis@126.com //
// 转载请保留作者信息 //
// (c)看雪学院 www.kanxue.com 2000-2018 //
/////////////////////////////////////////////////////////////////
//程序功能:对user32.dll导出的MessageBoxA进行Inline Hook
//本程序中实现了一个简单的Hook引擎,可以通过更改下面的宏定义实现不同的InlineHook
//对MessageBoxA,可选值为2,5,7
#include "pch.h"
#define HOOKCODELEN 5
#include <windows.h>
#include <stdio.h>
#include <CONIO.H>
#include <assert.h>
#include <tchar.h>
#include <imagehlp.h>
#pragma comment(lib,"imagehlp.lib")
//定义如下结构,保存一次InlineHook所需要的信息
typedef struct _HOOK_DATA {
char szApiName; //待Hook的API名字
char szModuleName; //待Hook的API所属模块的名字
intHookCodeLen; //Hook长度
BYTE oldEntry; //保存Hook位置的原始指令
BYTE newEntry; //保存要写入Hook位置的新指令
BYTE HotPatchCode;//用于HotPatch式Hook
ULONG_PTR HookPoint; //待HOOK的位置
ULONG_PTR JmpBackAddr; //回跳到原函数中的地址
ULONG_PTR pfnTrampolineFun; //调用原始函数的通道
ULONG_PTR pfnDetourFun; //HOOK过滤函数
}HOOK_DATA, * PHOOK_DATA;
#define HOOKLEN (7) //要改写的指令的长度
HOOK_DATA MsgBoxHookData;
ULONG_PTR SkipJmpAddress(ULONG_PTR uAddress);
LPVOID GetAddress(char*, char*);
void makehookentry(PVOID HookPoint);
LPVOID DetourVirtualAllocEx(
HANDLE hProcess,// process within which to allocate memory
LPVOID lpAddress, // desired starting address of allocation
DWORD dwSize, // size, in bytes, of region to allocate
DWORD flAllocationType,// type of allocation
DWORD flProtect // type of access protection
);
//int WINAPI TrampolineMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
ULONG TrampolineRegCloseKey(HKEY hKey);
BOOL Inline_InstallHook(void);
BOOL Inline_UnInstallHook();
BOOL InstallCodeHook(PHOOK_DATA pHookData);
BOOL UninstallCodeHook(PHOOK_DATA pHookData);
char hwnd[] = "123456789";
DWORD address1 = 需要的地址;
DWORD address1jmp;
DWORD param;
DWORD edxbak;
DWORD ecxbak;
char buf, bak;
VOID ShowMsgBox(char* szMsg)
{
MessageBoxA(NULL, szMsg, "Test", MB_OK);
}
//读寄存器的函数
void __fastcall hookprint(DWORD ecx, DWORD edx, DWORD param) {
//DWORD* d = (DWORD*)((DWORD)¶m - 4);
if (edx) {
//(*(DWORD*)(edx - 4) == '\xE') {
//if (*(DWORD*)(edx + 0x12) == '\x2D') {
char *pstr= (char*)(&(*(DWORD*)(edx)));
sprintf(&buf, pstr);
sprintf(buf, " %08X %s", *(DWORD*)(edx),&buf);
OutputDebugString(buf);
//}
//}
}
*(DWORD*)((DWORD)¶m + 4) = address1jmp;
}
__declspec(naked)
VOID hookcode()
{
__asm
{
pushad
pushfd
cmp edx, 00H
jeeaxit
cmp dword ptr ds : , 0EH
jnzeaxit
cmp byte ptr ds : , 2DH
jnzeaxit
///////////检测//////////////
mov param, 00H
mov edxbak, edx
mov ecxbak, ecx
push param
push edx
push ecx
call hookprint
pop ecx
pop edx
mov edx, edxbak
mov ecx, ecxbak
/////////////////////////////
mov ecx, 01Eh
mov esi, offset hwnd
mov edi, edx
rep movs byte ptr es : , byte ptr ds :
///////////检测//////////////
mov param, 00H
mov edxbak, edx
mov ecxbak, ecx
push param
push edx
push ecx
call hookprint
pop ecx
pop edx
mov edx, edxbak
mov ecx, ecxbak
/////////////////////////////
eaxit : popfd
popad
mov eax, dword ptr
test edi, edi
push address1jmp
retn
}
}
DWORD dwLOldProtect;
MEMORY_BASIC_INFORMATION LMBI = { 0 };
__declspec(naked)
void hookjmp()
{
VirtualQuery((LPVOID)address1, &LMBI, sizeof(MEMORY_BASIC_INFORMATION));
VirtualProtect((LPVOID)address1, 18, PAGE_EXECUTE_READWRITE, &dwLOldProtect);
__asm
{
pushad
mov eax, address1
add eax, 6
mov address1jmp, eax
mov eax, address1
mov edx, hookcode
sub edx, eax// 因为JMP参数是函数地址的相对偏移,所以这里先减去老函数入口地址,再减5
sub edx, 5 // JMP 指令本身占5个字节
mov byte ptr, 233// 0xE9 是JMP指令的机器码,后面是4个字节的相对地址偏移
mov dword ptr, edx
popad
ret
}
}
//************************************
// Method: FakeMessageBox
// FullName:FakeMessageBox
// Purpose: 取代原始MessageBoxA的功能,HOOK后所有对MessageBoxA的调用将实际调用本函数
// Author: achillis
// Returns: int WINAPI
// Parameter: HWND hWnd
// Parameter: LPCTSTR lpText
// Parameter: LPCTSTR lpCaption
// Parameter: UINT uType
//************************************
//注意函数的定义和原始函数一定要一样,尤其是调用约定,否则函数返回后将出错
//int WINAPI My_MessageBoxA(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
ULONG My_RegCloseKey(HKEY hKey)
{
ULONG ret;
__asm
{
pushad
call hookjmp
popad
}
ret=TrampolineRegCloseKey(hKey);
return ret;
}
BOOL Inline_InstallHook()
{
//准备Hook
ZeroMemory(&MsgBoxHookData, sizeof(HOOK_DATA));
strcpy(MsgBoxHookData.szApiName, "RegCloseKey");
strcpy(MsgBoxHookData.szModuleName, "advapi32.dll");
MsgBoxHookData.HookPoint = (ULONG_PTR)GetAddress(MsgBoxHookData.szModuleName, MsgBoxHookData.szApiName);//HOOK的地址
//MsgBoxHookData.pfnTrampolineFun = (ULONG_PTR)TrampolineMessageBox;//调用原始函数的通道
MsgBoxHookData.pfnTrampolineFun = (ULONG_PTR)TrampolineRegCloseKey;
//MsgBoxHookData.pfnDetourFun = (ULONG_PTR)My_MessageBoxA;//DetourFun
MsgBoxHookData.pfnDetourFun = (ULONG_PTR)My_RegCloseKey;//DetourFun
MsgBoxHookData.HookCodeLen = HOOKCODELEN;
return InstallCodeHook(&MsgBoxHookData);
}
BOOL Inline_UnInstallHook()
{
return UninstallCodeHook(&MsgBoxHookData);
}
/*
VirtualAllocEx的代码开头:
7C809B12 >6A 10 push 10
7C809B14 68 609B807C push kernel32.7C809B60
7C809B19 E8 B889FFFF call kernel32.7C8024D6
*/
//当需要调用原始的MessageBox时,直接调用此函数即可,参数完全相同
//int WINAPI TrampolineMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
__declspec(naked)
ULONG TrampolineRegCloseKey(HKEY hKey)
{
//几个NOP就几行
__asm
{
nop
//由于我们写入的Hook指令破坏了原函数开头的几条指令,所以将原来的指令复制到这里,在这里执行原函数的前几条指令
mov edx, hKey
mov esi, dword ptr
push esi
jmp MsgBoxHookData.JmpBackAddr //跳到Hook代码之后的地方,绕过自己安装的HOOK
}
}
//获取指定模块中指定API的地址
LPVOID GetAddress(char* dllname, char* funname)
{
HMODULE hMod = 0;
if (hMod = GetModuleHandle(dllname))
{
return GetProcAddress(hMod, funname);
}
else
{
hMod = LoadLibrary(dllname);
return GetProcAddress(hMod, funname);
}
}
ULONG_PTR SkipJmpAddress(ULONG_PTR uAddress)
{
ULONG_PTR TrueAddress = 0;
PBYTE pFn = (PBYTE)uAddress;
if (memcmp(pFn, "\xFF\x25", 2) == 0)
{
TrueAddress = *(ULONG_PTR*)(pFn + 2);
return TrueAddress;
}
if (pFn == 0xE9)
{
TrueAddress = (ULONG_PTR)pFn + *(ULONG_PTR*)(pFn + 1) + 5;
return TrueAddress;
}
if (pFn == 0xEB)
{
TrueAddress = (ULONG_PTR)pFn + pFn + 2;
return TrueAddress;
}
return (ULONG_PTR)uAddress;
}
/*
这里计算一下要填充的指令
根据要HOOK的长度不同及现场保护的不同要求,可以采用不同的HOOK指令
通常远跳转最少要5字节,下面是常用的三种情况:
5字节 jmp xxxxxxxx(0xE9) 大部分函数开头是这样的
77D5050B >8BFF mov edi,edi
77D5050D 55 push ebp
77D5050E 8BEC mov ebp,esp
77D50510 833D 1C04D777 00 cmp dword ptr ds:,0
6字节 push xxxxxxxx/ret 使用情况较少
7字节 mov eax,xxxxxxxx/jmp eax 常用于有SEH的函数开头,如IsBadReadPtr
7565D015 >6A 0C push 0C
7565D017 68 78D06575 push kernel32.7565D078
7565D01C E8 9F45FDFF call kernel32.756315C0
当要改变的指令长度大于以上时,可以使用nop来填充
*/
void InitHookEntry(PHOOK_DATA pHookData)
{
if (pHookData == NULL
|| pHookData->pfnDetourFun == NULL
|| pHookData->HookPoint == NULL
|| pHookData->HookCodeLen == 0)
{
return;
}
switch (pHookData->HookCodeLen)
{
case 2://Hot Patch
{
/*
77D507E5 >-/E9 66086B88 jmp InlineHo.00401050
77D507EA > $^\EB F9 jmp short USER32.77D507E5
*/
pHookData->newEntry = 0xEB; //Jmp -5
pHookData->newEntry = 0xF9;
pHookData->HotPatchCode = 0xE9; //Jmp
*(ULONG*)(pHookData->HotPatchCode + 1) = (ULONG)pHookData->pfnDetourFun - ((ULONG)pHookData->HookPoint - 5) - 5;//0xE9 式jmp的计算
}
break;
case 5:
{
pHookData->newEntry = 0xE9; //Jmp
//计算跳转偏移并写入
*(ULONG*)(pHookData->newEntry + 1) = (ULONG)pHookData->pfnDetourFun - (ULONG)pHookData->HookPoint - 5;//0xE9 式jmp的计算
}
break;
case 6:
{
/*
0040E9D1 > 68 44332211 push 11223344
0040E9D6 C3 retn
*/
memcpy(pHookData->newEntry, "\x68\x44\x33\x22\x11\xC3", 6);
*(ULONG*)(pHookData->newEntry + 1) = (ULONG)pHookData->pfnDetourFun;
}
break;
case 7:
/*
7C809B12 >B8 44332211 mov eax,11223344
7C809B17 FFE0 jmp eax
*/
{
memcpy(pHookData->newEntry, "\xB8\x44\x33\x22\x11\xFF\xE0", 7);
*(ULONG*)(pHookData->newEntry + 1) = (ULONG)pHookData->pfnDetourFun;
}
break;
default:
break;
}
}
BOOL InstallCodeHook(PHOOK_DATA pHookData)
{
DWORD dwBytesReturned = 0;
DWORD dwOldProtect;
PBYTE pfnHead = NULL;
PBYTE pAddrToWrite = NULL;
BOOL bResult = FALSE;
MEMORY_BASIC_INFORMATION MBI = { 0 };
if (pHookData == NULL
|| pHookData->HookPoint == 0
|| pHookData->pfnDetourFun == NULL
|| pHookData->pfnTrampolineFun == NULL)
{
return FALSE;
}
pHookData->pfnTrampolineFun = SkipJmpAddress(pHookData->pfnTrampolineFun);
pHookData->HookPoint = SkipJmpAddress(pHookData->HookPoint); //如果函数开头是跳转,那么将其跳过
pHookData->JmpBackAddr = pHookData->HookPoint + pHookData->HookCodeLen;
pfnHead = (PBYTE)pHookData->HookPoint;
//printf("Address To HOOK=0x%08X\n", pfnHead);
//检查是否被Hook过
if (memcmp(pfnHead, "\x8B\xFF\x55\x8B\xEC", 5) == 0 //push ebp,mov ebp,esp
|| (pfnHead == 0x6A && pfnHead == 0x68)) //push sehhandler
{
//未Hook过才能继续
InitHookEntry(pHookData);//填充Inline Hook代码
}
else
{
//printf("Addr 0x%X already hooked.\n");
}
//读取原函数代码
memcpy(pHookData->oldEntry, (void*)pHookData->HookPoint, 8);
//如果不是HotPatch Hook,需要把原函数的指令替换掉Trampoline中的nop
if (pHookData->HookCodeLen != 2)
{
pAddrToWrite = (PBYTE)pHookData->pfnTrampolineFun;
if (VirtualQuery(pAddrToWrite, &MBI, sizeof(MEMORY_BASIC_INFORMATION))
&& VirtualProtect(MBI.BaseAddress, pHookData->HookCodeLen, PAGE_EXECUTE_READWRITE, &dwOldProtect))
{
memcpy(pAddrToWrite, pHookData->oldEntry, pHookData->HookCodeLen);
VirtualProtect(MBI.BaseAddress, pHookData->HookCodeLen, dwOldProtect, &dwOldProtect);
}
}
//下面开始Hook
if (pHookData->HookCodeLen == 2)
{
pAddrToWrite = (PBYTE)pHookData->HookPoint - 5;
}
else
{
pAddrToWrite = (PBYTE)pHookData->HookPoint;
}
if (VirtualQuery(pAddrToWrite, &MBI, sizeof(MEMORY_BASIC_INFORMATION))
&& VirtualProtect(MBI.BaseAddress, pHookData->HookCodeLen, PAGE_EXECUTE_READWRITE, &dwOldProtect))//实际上这里会修改整页的属性
{
if (pHookData->HookCodeLen == 2)
{
//先填充jmp指令
memcpy(pAddrToWrite, pHookData->HotPatchCode, 5);
pAddrToWrite += 5;
}
memcpy(pAddrToWrite, pHookData->newEntry, pHookData->HookCodeLen);
VirtualProtect(MBI.BaseAddress, pHookData->HookCodeLen, dwOldProtect, &dwOldProtect);
}
return bResult;
}
BOOL UninstallCodeHook(PHOOK_DATA pHookData)
{
DWORD dwBytesReturned = 0;
DWORD dwOldProtect;
BOOL bResult = FALSE;
MEMORY_BASIC_INFORMATION MBI = { 0 };
PBYTE pAddrToWrite = NULL;
if (pHookData == NULL
|| pHookData->HookPoint == 0
|| pHookData->oldEntry == 0)
{
return FALSE;
}
pAddrToWrite = (PBYTE)pHookData->HookPoint;
if (VirtualQuery(pAddrToWrite, &MBI, sizeof(MEMORY_BASIC_INFORMATION))
&& VirtualProtect(MBI.BaseAddress, pHookData->HookCodeLen, PAGE_EXECUTE_READWRITE, &dwOldProtect))
{
memcpy(pAddrToWrite, pHookData->oldEntry, pHookData->HookCodeLen);
bResult = TRUE;
VirtualProtect(MBI.BaseAddress, pHookData->HookCodeLen, dwOldProtect, &dwOldProtect);
}
return bResult;
}
int CDECL MessageBoxPrintf(TCHAR* szCaption, TCHAR* szFormat, ...)
{
TCHAR szBuffer;
va_list pArgList;
va_start(pArgList, szFormat);
_vsntprintf_s(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), sizeof(szBuffer) / sizeof(TCHAR), szFormat, pArgList);
va_end(pArgList);
return MessageBox(NULL, szBuffer, szCaption, 0);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 入口函数
BOOL APIENTRY DllMain(HANDLE hModule,
DWORDul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
Inline_InstallHook();
break;
}
case DLL_PROCESS_DETACH:
Inline_UnInstallHook();
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
本帖最后由 xixuedafa123 于 2020-4-22 08:38 编辑
JuncoJet 发表于 2020-4-17 16:23
给你写个例子吧
void __fastcall HOOK(DWORD ecx,DWORD edx,DWORD param){
if ...
解决了,谢谢版主大人的调试器。很有用。最后有个小的疑问
//读寄存器的函数
void __fastcall hookprint(DWORD ecx, DWORD edx, DWORD param) {
if (edx) {
char *pstr= (char*)(&(*(DWORD*)(edx)));
sprintf(&buf, pstr);
sprintf(buf, " %08X %s", *(DWORD*)(edx),&buf);
OutputDebugString(buf);
}
*(DWORD*)((DWORD)¶m + 4) = address1jmp;
}
这段代码我感觉自己写的还是有点问题,而且我最后是汇编调用的,这个代码
*(DWORD*)((DWORD)¶m+4)=address1jmp;不知道如何使用,还希望大佬指点。
上面这段代码论坛用代码格式发布,第一句话就跑代码里面了。 本帖最后由 xixuedafa123 于 2020-4-17 15:26 编辑
JuncoJet 发表于 2020-4-17 13:46
没看出动态的,0x0040B50B+6而已
我明白了,我每次抄的OD里面的机器码,就是汇编代码前面那个,用C32ASM里面的就不会变,不过shellcode也不行
OD的时候可以执行到shellcode(hookcode函数),但是替换的地方就跑不到,就是下面的部分
jnzeaxit
mov ecx, 01Eh
mov esi, offset hwnd
mov edi, edx
rep movs byte ptr es : , byte ptr ds :
就是在这个跳转,每次都执行不到替换的这两句。 不会汇编看的脑壳疼。
等3小时没人回复的话建议看雪发个贴。 本帖最后由 xixuedafa123 于 2020-4-22 08:55 编辑
汇编我也看的痛苦,所以我想改成C,那个播放器有个VMP的壳(本来就有),就可以用来实验inline hook。 必须顶上去,下载了分析试试。感谢 直接shellcode就行,定义个数组 本帖最后由 xixuedafa123 于 2020-4-16 20:44 编辑
我也想过把内联的汇编直接转shellcode,我还没试,我还在考虑通用易懂的代码。不然以后永远要转换。
shellcode的方法我有空也试下,其实真正要转换的就是hookcode的部分,64位的话,好像只能用shellcode了。 本帖最后由 xixuedafa123 于 2020-4-17 12:31 编辑
JuncoJet 发表于 2020-4-16 19:12
直接shellcode就行,定义个数组
上面的代码我重新更新了,可断在关键处,我也试过shellcode,发现shellcode就是有一句jmp address1jmp这句是动态的,不知道是不是vmp壳的影响。
不用shellcode的话编译器会给我加些push和pop的代码,不知道是不是这个的影响 xixuedafa123 发表于 2020-4-17 11:49
上面的代码我重新更新了,可断在关键处,我也试过shellcode,发现shellcode就是有一句jmp address1jmp这 ...
没看出动态的,0x0040B50B+6而已 xixuedafa123 发表于 2020-4-17 15:20
我明白了,我每次抄的OD里面的机器码,就是汇编代码前面那个,用C32ASM里面的就不会变,不过shellcode也 ...
你理解源码就行了
jnz的话看前面的判断
rep的话从esi指针(hwnd)拷贝到edi指针(edx)
长度ecx,也就是0x1E个长度
页:
[1]
2