• 如果有个库函数接受的是无参数的回调函数怎么办?


    问题:

    如果某个第三方库的某个函数需要接收用户传过来的回调函数,该回掉函数通常可能需要获取用户自己的某些参数。

    一般做法是第三方库提供这样的回掉方式

    void LibRegisterFtn(void(*f)(void *), void *param)

    用户通过void指针来传入自己感兴趣的参数

    比如例子

    class UserClass
    
    {
    
        UserClass()
        {
    
         LibRegisterFtn(
        }
    
        static void callback(void *p)
        {
            UserClass *pThis = (UserClass*)p;
            pThis->xxxx();
        }
    
    };

    但如果该第三方库写的比较恶心,没有提供这个二外的参数怎么办呢?

    比如这个

    void LibRegisterFtn(void(*f)())

    针对这种情况,大家常见的做法是定义一个全局变量,通过这个全局变量将参数传入到回掉函数里面

    UserClass g_me;
    
    void callback()
    {
        g_me.xxxx();
    }

    这种做法的恶心之处在于多了一个全局变量,如果在多线程情况下,每个线程注册不同的参数到回掉函数就需要定义多个全局变量。每个线程接受相应的变量

    大家都知道,全局变量很不鼓励的一种做法

    现在,俺介绍一种新的方法,可以把任意局部变量作为参数传入到该回掉函数。其实这种做法是参考了wtl窗口消息会掉的做法。具体来说就是动态生成一段回掉函数,从而该回掉函数可以通过offset一个固定的偏移来获取到需要的参数

    废话不多说,代码如下

    #include <stdio.h>
    #include <windows.h>
    #include <assert.h>
    #include <functional>
    
    typedef void(*F)();
    
    void LibFtn(F callback)
    {
        callback();
    }
    
    class MyStruct
    {
    public:
        void init(std::function<void()>    f)
        {
            f_ = f;
            init(this, callback);
        }
    
        F get()
        {
            F f = (F)(dummy + 8);
            return f;
        }
    
    private:
        char dummy[0x20];
        std::function<void()>    f_;
    
        typedef  void(_stdcall *_myCallbackSpecial)(void *n);
    
        
    
        static void _stdcall callback(void *n)
        {
            MyStruct *pThis = (MyStruct*)n;
            pThis->f_();
        }
    
        void init(void *p, _myCallbackSpecial fCallback)
        {
            char dummy2[0x20] = { 0x90, 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0xe8 , 0x00 , 0x00 , 0x00 , 0x00 , 0x58 , 0x83 , 0xe8
                , 0x0d, 0x50, 0x83, 0xc0, 0x04, 0x8b, 0x00, 0xff, 0xd0, 0xc3, 0x83, 0xc4, 0x04, 0x3b, 0xec, 0xe8 };
            memcpy(dummy, dummy2, sizeof(dummy2));
    
            DWORD lOldProtect;
            BOOL bRet = VirtualProtect(dummy, sizeof(dummy), PAGE_EXECUTE_READWRITE, &lOldProtect);
            assert(bRet);
            FlushInstructionCache(GetCurrentProcess(), dummy, sizeof(dummy));
    
            memcpy(dummy, p, 4);
            memcpy(dummy + 4, &fCallback, 4);
        }
    };
    
    void main()
    {
        MyStruct ms;
    
        int i = 10;
        ms.init([=]() {
            printf("Hello %d
    ", i);
        });
    
        LibFtn(ms.get());
    }

    该u代码关键就是那堆字符串,其assembly对应的代码如下

     
    1 005EF99C  call        005EF9A1  
    2 005EF9A1  pop         eax  
    3 005EF9A2  sub         eax,0Dh  
    4 005EF9A5  push        eax  
    5 005EF9A6  add         eax,4  
    6 005EF9A9  mov         eax,dword ptr [eax]  
    7 005EF9AB  call        eax  
    8 005EF9AD  ret  

    第一,2行用来获取eip地址,第3,4行将参数push进堆栈,第5,6行调用用户指定的回掉函数

  • 相关阅读:
    speedtest测速网站测速节点添加流程
    Mac 系统更新怎么忽略
    【木马免杀思路】msf木马免杀 python转exe(一) 截止2021年8月8日通杀360,火绒,微步
    启动docker desktop for mac时,会自动打开IntelliJ IDEA
    【漏洞复现系列】Apache Solr SSRF文件读取漏洞(附0day POC)
    【维持权限】schtasks命令
    【cowrie蜜罐系列2】cowrie蜜罐配置代理成为高交互蜜罐(避免踩坑)
    【cowrie蜜罐系列1】cowrie蜜罐部署并配置mysql
    【漏洞复现系列】ThinkPHP 5 远程命令执行
    【漏洞复现系列】WebLogic XMLDecoder反序列化漏洞(CVE-2017-10271)
  • 原文地址:https://www.cnblogs.com/cutepig/p/6536644.html
Copyright © 2020-2023  润新知