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



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

  1. 创建所需的结构并定义常量
  2. 获取指向DLL函数的指针
  3. 调用DLL函数

我举了一个小例子来说明我的问题:

#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 IcmpSendEchon");
    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 = %ldn", 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 %dn", pret);
        icmp_free();
    }
}

我使用gcc -o icmp_test.exe icmpt_test.c编译了这个例子。它打印"调用IcmpSendEcho"并崩溃(见下文)。

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

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

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

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
);

最新更新