xixuedafa123 发表于 2020-4-16 17:36

[已解决](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:35

本帖最后由 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:20

本帖最后由 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 :
就是在这个跳转,每次都执行不到替换的这两句。

我的爱是你 发表于 2020-4-16 17:56

不会汇编看的脑壳疼。
等3小时没人回复的话建议看雪发个贴。

xixuedafa123 发表于 2020-4-16 18:06

本帖最后由 xixuedafa123 于 2020-4-22 08:55 编辑

汇编我也看的痛苦,所以我想改成C,那个播放器有个VMP的壳(本来就有),就可以用来实验inline hook。

olhoscn 发表于 2020-4-16 18:28

必须顶上去,下载了分析试试。感谢

JuncoJet 发表于 2020-4-16 19:12

直接shellcode就行,定义个数组

xixuedafa123 发表于 2020-4-16 19:56

本帖最后由 xixuedafa123 于 2020-4-16 20:44 编辑

我也想过把内联的汇编直接转shellcode,我还没试,我还在考虑通用易懂的代码。不然以后永远要转换。
shellcode的方法我有空也试下,其实真正要转换的就是hookcode的部分,64位的话,好像只能用shellcode了。

xixuedafa123 发表于 2020-4-17 11:49

本帖最后由 xixuedafa123 于 2020-4-17 12:31 编辑

JuncoJet 发表于 2020-4-16 19:12
直接shellcode就行,定义个数组
上面的代码我重新更新了,可断在关键处,我也试过shellcode,发现shellcode就是有一句jmp address1jmp这句是动态的,不知道是不是vmp壳的影响。
不用shellcode的话编译器会给我加些push和pop的代码,不知道是不是这个的影响

JuncoJet 发表于 2020-4-17 13:46

xixuedafa123 发表于 2020-4-17 11:49
上面的代码我重新更新了,可断在关键处,我也试过shellcode,发现shellcode就是有一句jmp address1jmp这 ...

没看出动态的,0x0040B50B+6而已

JuncoJet 发表于 2020-4-17 15:26

xixuedafa123 发表于 2020-4-17 15:20
我明白了,我每次抄的OD里面的机器码,就是汇编代码前面那个,用C32ASM里面的就不会变,不过shellcode也 ...

你理解源码就行了
jnz的话看前面的判断
rep的话从esi指针(hwnd)拷贝到edi指针(edx)
长度ecx,也就是0x1E个长度
页: [1] 2
查看完整版本: [已解决](inline hook后jmp)汇编转C的问题