DLL 내보내기 함수를 보다 쉽게 동적으로 호출(VC++)

4376 단어
Submitted by 리마
당신은 이 글을 마음대로 전재할 수 있지만, 전재할 때 원본 링크와 저자를 명기해 주십시오. 감사합니다.
일반적으로 DLL 내보내기 함수를 동적으로 호출하는 방법은 다음과 같습니다.
  • typedef로 목표 함수에 함수 포인터 형식을 정의합니다.
  • GetProcAddress를 사용하여 함수 포인터를 가져옵니다.
  • 함수 바늘로 호출합니다.

  • 그러나 호출할 함수가 너무 많으면, 이 방법은 너무 많은 typedef, 너무 많은 GetProcAddress, 너무 많은 함수 바늘로 흐르는 것을 피하기 어렵다.본고에서 이러한 동적 호출을 더욱 간편하게 하는 통용적인 해결 방법을 제시할 것이다.먼저 우리 함수의 성명을 보아라.
    C++ 코드
    BOOL __cdecl DllCall(PCTSTR lpszDll, //       DLL      
               	     PCSTR lpszFunc, //          
                         int argc,       //            
                         PVOID pRet,     //            
                         ...);  //     
    

     
    MessageBoxA의 경우 다음을 사용합니다.
    C++ 코드
    	int ret;   
    	DllCall(_T("user32.dll"), "MessageBoxA", 4, &ret,   
    	        NULL, "Hello, World!", "Hello", MB_ICONINFORMATION | MB_YESNO);  
    

     
    매개 변수의 MessageBoxIndirectA를 바꾸면 다음과 같습니다.
    C++ 코드
    	MSGBOXPARAMSA param;   
    	ZeroMemory(¶m, sizeof(MSGBOXPARAMSA));   
    	param.cbSize = sizeof(MSGBOXPARAMSA);   
    	param.dwLanguageId = GetSystemDefaultLangID();   
    	param.dwStyle = MB_ICONINFORMATION;   
    	param.lpszCaption = "Hello";   
    	param.lpszText = "Hello, World";   
      
    	int ret;   
    	DllCall(_T("user32.dll"), "MessageBoxIndirectA", 1, &ret, ¶m);  
    

     
    구현의 원리는 어셈블리 코드를 동적으로 생성하는 것이다. 즉, 이와 같은 부분이다.
    C++ 코드
    	__declspec(naked) DWORD __cdecl DllCallProc(void)   
    	{   
    	     __asm   
    	     {   
    	         push argn   
    	         ...   
    	         push arg2   
             	push arg1   
             	call proc   
             	ret   
         	};   
    	}  
    

     
    다음은 DllCall의 코드를 보여 줍니다. 모든 변수 함수 (예: sprintf) 와 차이가 많지 않습니다.C++ 코드
     
    BOOL __cdecl DllCall(PCTSTR lpszDll,
                         PCSTR lpszFunc,
                         int argc,
                         PVOID pRet,
                         ...)   
    {   
        va_list arglist;   
        int ret;   
      
        va_start(arglist, pRet);   
        ret = vDllCall(lpszDll, lpszFunc, argc, pRet, arglist);   
        va_end(arglist);   
        return ret;   
    }

     
    가장 관건적인 것은 vDllCall의 코드입니다. 다음과 같습니다. C++ 코드
     
    #pragma pack(push, 1)   
    typedef struct {   
        BYTE op;   
        DWORD_PTR dwValue;   
    } OPCODE, *POPCODE;   
    #pragma pack(pop)   
      
    typedef DWORD (__cdecl * DLLCALL)(void);
     
    BOOL __cdecl CRfidNfc::vDllCall(PCTSTR lpszDll,
                                    PCSTR lpszFunc,   
                                    int argc,   
                                    PVOID pRet,   
                                    va_list arglist)   
    {   
        HMODULE hDll = LoadLibrary(lpszDll);   
        if (NULL == hDll)   
            return FALSE;   
         FARPROC proc = GetProcAddress(hDll, lpszFunc);   
        if (NULL == proc)   
            return FALSE;   
      
        HANDLE hHeap = GetProcessHeap();   
         POPCODE p = (POPCODE)HeapAlloc(hHeap, 0, sizeof(OPCODE) * (argc + 2));   
      
        int i;   
        for (i = argc - 1; i >= 0; --i)   
         {   
            // push arg[i]   
             p[i].op = 0x68;   
             p[i].dwValue = va_arg(arglist, DWORD_PTR);   
         }   
      
        // call proc   
         p[argc].op = 0xe8;   
         p[argc].dwValue = (INT_PTR)proc - (INT_PTR)&p[argc + 1];   
        // ret   
         p[argc + 1].op = 0xc3;   
         p[argc + 1].dwValue = 0x90909090; // nop nop nop nop   
      
         DLLCALL pfn = (DLLCALL)p;   
        DWORD ret = pfn();   
      
         HeapFree(hHeap, 0, p);   
         FreeLibrary(hDll);   
      
        if (NULL != pRet)   
             *(PDWORD)pRet = ret;   
        return TRUE;   
    }

     
    포인터 p는 DLLCALL 유형의 함수 포인터로 변환하여 동적으로 생성된 호출 코드입니다.마지막으로 네 가지 추가 설명이 필요합니다.
  • DllCall은 에만 해당stdcall에서 약속된 목표 함수를 호출합니다.
  • 이 vDllCall의 코드는 x86의 CPU에만 적용되며, WinCE 환경(예를 들어arm이나 mps의 CPU)에서 사용하면 동적 호출된 어셈블리 코드를 다시 작성해야 한다.
  • 그 중에서argc 매개 변수는 C 언어가 호출한 매개 변수의 개수가 아니라 실제 압축된 매개 변수의 개수를 가리킨다.예를 들어 API 함수인 WindowFromPoint는 함수 성명에 단지 하나의 매개 변수만 있지만 실제로는 POINT::x,POINT::::y를 각각 압축한다.이 경우,argc를 2로 설정해야 합니다.
  • DllCall의 반환값은 eax만 가져왔습니다. 만약 함수가 4바이트가 넘는 거대한 구조를 반환한다면 이 반환값은 당신이 원하는 것이 아닙니다.

  • http://www.titilima.cn/show-275-1.html

    좋은 웹페이지 즐겨찾기