GCC在系统DLL调用期间编译Win32程序崩溃

时间:2013-12-17 15:29:58

标签: c winapi pointers gcc dll

我尝试使用Microsoft SDK库从IcmpSendEcho 加载iphlpapi.dll而不是。我在Windows 7 x64上使用GCC 4.8.1(win32)。我这样试过:

  1. 创建必需的结构并定义常量
  2. 获取DLL函数的指针
  3. 调用DLL函数
  4. 我汇总了一个展示我问题的小例子:

    #include <windows.h>
    #include <stdio.h>
    #include <stdint.h>
    
    typedef struct {
      union {
        struct {
          u_char s_b1,s_b2,s_b3,s_b4;
        } S_un_b;
        struct {
          u_short s_w1,s_w2;
        } S_un_w;
        u_long S_addr;
      } S_un;
    } IPAddr;
    
    
    #define IP_FLAG_REVERSE 0x01
    #define IP_FLAG_DF      0x02
    
    typedef struct {
      UCHAR  Ttl;
      UCHAR  Tos;
      UCHAR  Flags;
      UCHAR  OptionsSize;
      PUCHAR OptionsData;
    } IP_OPTION_INFORMATION;
    
    
    typedef struct {
      IPAddr                       Address;
      ULONG                        Status;
      ULONG                        RoundTripTime;
      USHORT                       DataSize;
      USHORT                       Reserved;
      PVOID                        Data;
      IP_OPTION_INFORMATION        Options;
    } ICMP_ECHO_REPLY;
    
    
    typedef HANDLE (*IcmpCreateFile_t)();
    
    typedef BOOL (*IcmpCloseHandle_t)(
        HANDLE IcmpHandle // _In_  HANDLE IcmpHandle
    );
    
    typedef DWORD (*IcmpSendEcho_t)(
        HANDLE IcmpHandle,  // _In_      HANDLE IcmpHandle,
        IPAddr dst,         // _In_      IPAddr DestinationAddress,
        void * RequestData, // _In_      LPVOID RequestData,
        WORD RequestSize,   // _In_      WORD RequestSize,
        IP_OPTION_INFORMATION * RequestOptions, // _In_opt_  PIP_OPTION_INFORMATION RequestOptions,
        void * ReplyBuffer, // _Out_     LPVOID ReplyBuffer,
        DWORD ReplySize,    // _In_      DWORD ReplySize,
        DWORD Timeout       // _In_      DWORD Timeout
    );
    
    
    HINSTANCE hIPHLPAPI = NULL;
    IcmpCreateFile_t  IcmpCreateFile  = NULL;
    IcmpCloseHandle_t IcmpCloseHandle = NULL;
    IcmpSendEcho_t    IcmpSendEcho    = NULL;
    
    HANDLE hIcmpFile;
    
    int icmp_init() {
        hIPHLPAPI = LoadLibrary("iphlpapi.dll");
        if(hIPHLPAPI == NULL) return 0;
    
        IcmpCreateFile = (IcmpCreateFile_t) GetProcAddress(hIPHLPAPI, "IcmpCreateFile");
        if(IcmpCreateFile == NULL) return 0;
    
        IcmpCloseHandle = (IcmpCloseHandle_t) GetProcAddress(hIPHLPAPI, "IcmpCloseHandle");
        if(IcmpCloseHandle == NULL) return 0;
    
        IcmpSendEcho = (IcmpSendEcho_t) GetProcAddress(hIPHLPAPI, "IcmpSendEcho");
        if(IcmpSendEcho == NULL) return 0;
    
        hIcmpFile = IcmpCreateFile();
        if(!hIcmpFile) return 0;
    
        return 1;
    }
    
    int icmp_ping(uint32_t ip) {
        // Echo request data
        int dataSize = 32;
        void * data = malloc(dataSize);
    
        // Reply buffer
        int replySize = sizeof(ICMP_ECHO_REPLY) + 128;
        void * replyBuffer = malloc(replySize);
        if(replyBuffer == NULL) return 0;
    
        // Send echo request and wait for reply
        IPAddr dst;
        dst.S_un.S_addr = ip;
        printf("Calling IcmpSendEcho\n");
        DWORD dwRetVal = IcmpSendEcho(hIcmpFile, dst, data, dataSize, NULL, replyBuffer, replySize, 1000);
        if(dwRetVal == 0) return 0;
    
        ICMP_ECHO_REPLY *echoreply = (ICMP_ECHO_REPLY *) replyBuffer;
        printf("Status = %ld\n", echoreply->Status);
    
        free(replyBuffer);
    
        return 0;
    }
    
    int icmp_free() {
        FreeLibrary(hIPHLPAPI);
        return 1;
    }
    
    int main(int argc, char *argv[]) {
        if(icmp_init()) {
            int pret = icmp_ping(2915201282 /* google.com */);
            printf("icmp_ping returned %d\n", pret);
            icmp_free();
        }
    }
    

    我使用gcc -o icmp_test.exe icmpt_test.c编译此示例。它打印“Calling IcmpSendEcho”并崩溃(见下文)。

    这不是“真正的”代码,但它以同样的方式失败。对IcmpCreateFile的调用成功,但调用IcmpSendEcho会使程序崩溃并使Windows显示“...已停止工作”。

    我认为问题可能与错误的指针有关,但我找不到问题。

1 个答案:

答案 0 :(得分:4)

使用GetProcAddress同步链接库时,需要确保匹配签名和调用约定。虽然签名通常很容易纠正,但很容易忘记考虑calling convention

Windows中的系统模块始终使用__stdcall,这也可以通过WINAPI预处理器宏获得。要修复代码以便编译器为函数调用生成适当的指令,您需要更新以下函数指针类型以包含适当的调用约定:

typedef HANDLE (WINAPI* IcmpCreateFile_t)();

typedef BOOL (WINAPI* IcmpCloseHandle_t)(
    HANDLE IcmpHandle // _In_  HANDLE IcmpHandle
);

typedef DWORD (WINAPI* IcmpSendEcho_t)(
    HANDLE IcmpHandle,  // _In_      HANDLE IcmpHandle,
    IPAddr dst,         // _In_      IPAddr DestinationAddress,
    void * RequestData, // _In_      LPVOID RequestData,
    WORD RequestSize,   // _In_      WORD RequestSize,
    IP_OPTION_INFORMATION * RequestOptions, // _In_opt_  PIP_OPTION_INFORMATION
    RequestOptions,
    void * ReplyBuffer, // _Out_     LPVOID ReplyBuffer,
    DWORD ReplySize,    // _In_      DWORD ReplySize,
    DWORD Timeout       // _In_      DWORD Timeout
);