zoukankan      html  css  js  c++  java
  • thunk (转)

    thunk 在网络词典上解释为:形实转换程序或替换程序。那么到底如何转换?如何替换呢?

    其实可以把 thunk 理解为一小段代码,但这段代码并不是静态编译在程序的代码段中的,而是在程序运行过程中自动生成的一段代码,然后让程序在合适的时机去执行这段代码。

    下面是一个替换函数参数的 thunk 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #include <windows.h>
    #include <iostream>
     
    // 定义一个函数指针。原型和 API 函数 MessageBoxA 函数的原型完全一样
    typedef int (WINAPI *TEST)(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);
     
    // 按字节对齐,因为此结构中保存的是一段机器码
    #pragma pack(push,1)
    // 此 thunk 代码的功能是用新的字符串的地址替换原先的字符串的地址
    // 并跳转到 MessageBoxA 函数处执行
    struct Thunk
    {
        DWORD   m_mov;         
        DWORD   m_pStr;        
        BYTE    m_jmp;         
        DWORD   m_relproc;     
     
        // 初始化后 thunk 代码执行以下功能
        // 1. 把地址 [esp + 0x08] 处的内容替换为参数 pStr 的值
        // 2. 跳转到 MessageBoxA 函数
        BOOL Init(char* pStr) 
        {  
            m_mov = 0x082444C7;  // mov dword ptr [esp+0x8] esp+8处是函数参数 lpText
            m_pStr = PtrToUlong(pStr);  
            m_jmp = 0xe9;   // jmp
            m_relproc = DWORD((INT_PTR)::MessageBoxA - ((INT_PTR)this+sizeof(Thunk)));  
     
            // 不调用此函数程序也能正常执行,但最好是调用一下
            ::FlushInstructionCache(::GetCurrentProcess(), this, sizeof(Thunk));  
            return TRUE;  
        }  
    };
    #pragma pack(pop)
     
    CHAR love[] = "I love you!";
     
    int main()
    {
        // 在堆栈中生成 thunk 代码
        Thunk thunk;
        thunk.Init(love);
     
        TEST MyMessageBox = (TEST)&thunk;
     
        // 注意 "I hate you!" 会被 thunk 替换为 "I love you!"
        // 此时并不会弹出"I hate you!"而是弹出"I love you!"
        MyMessageBox(NULL,"I hate you!",NULL,0);
    }

    要彻底理解以上代码的实现原理,必须对汇编语言有所了解。

    首先看第47行代码。MyMessageBox 是一个函数指针,此行代码在编译后会产生汇编代码把四个参数从右向左(stdcall) push 到堆栈中然后再跳转到 MyMessageBox 所指向的内存位置处继续执行。在把四个参数 push 到堆栈后,内存地址[ESP + 8](ESP为栈顶指针) 处保存的就是字符串"I hate you!"的地址。而 MyMessageBox 所指向的就是我们的 thunk 代码。

    跳转到 thunk 代码处后,首先用字符串"I love you!"的地址替换掉了堆栈中保存的"I hate you!"的地址,然后跳转到 MessageBoxA 函数中执行。注意这里执行函数 MessageBoxA 的代码使用的是“跳转”而不是“调用”。在 MessageBoxA 函数中有 ret 指令,当执行到此指令后程序将返回到第 48 行代码处继续往下执行。

    再看第26行代码。此行代码是计算要跳转到的地址相对于当前地址的偏移量,格式是:目标地址 - EIP 中的当前地址。目标地址当然就是函数 MessageBoxA 的地址,而EIP中当前的地址是紧跟结构变量 thunk 之后的那个字节的地址,即:(INT_PTR)this+sizeof(Thunk)。

  • 相关阅读:
    Dedecms5.7修改文章,不改变发布时间的方法
    dedecms列表页如何调用栏目关键词和描述
    DEDE内容页调用栏目的SEO标题、描述、关键字的方法
    织梦安装过后出现"...www/include/templets/default/index.htm Not Found!"
    DEDE无简略标题时显示完整标题
    Dede调用简略标题_简略标题标签(短标题)
    修改dede提示信息
    交叉栏目实现织梦首页分页
    织梦添加和调用自定义字段的方法
    织梦列表页和内容页调用缩略图的方法
  • 原文地址:https://www.cnblogs.com/winkyao/p/2497615.html
Copyright © 2011-2022 走看看