libcurl在Windows服务中崩溃 [英] libcurl crashes in a Windows service

查看:107
本文介绍了libcurl在Windows服务中崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的服务从某些进程接收数据,对其进行解析,然后将HTTP帖子发送到我的PHP服务器.

My service receives data from some processes, parses it, and sends an HTTP post to my PHP server.

当我开始编写代码时,它是一个普通的64位程序.完成后,我将其转换为服务,但是当该服务尝试发送数据时,会发生一些崩溃.

When I started writing the code, it was an ordinary 64-bit program. After I finished, I converted it to a service, but some crashes happen when the service tries to send the data.

原因尚不清楚,因为我在服务的其他位置使用了libcurl却没有问题.

The reason isn't clear, as I use libcurl in other places in the service without problems.

我的接收器是这样的:

while (true)
{
    memset(pipe_buffer, 0, 10000);
    cres = ReadFile(pipe, pipe_buffer, 10000, &read, 0);
    ofile << "[*] got a packet with length : " << read << endl;
    if (read > 0 && cres) {
        ofile << "[*] " << pipe_buffer << endl;

        // send the request
        string payload;
        payload += "data=";
        payload += pipe_buffer;
        ofile << "[*] sending post : " << url << "?" << payload<< endl;
        CURL *curl;
        curl = curl_easy_init();
        if (!curl) {
            ofile << "[!] curl failed to init" << endl;
            return 0;
        }
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // crashes start here
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload.c_str());
        curl_easy_perform(curl);
        curl_easy_cleanup(curl);
    }
    else {
         // the client may dissconnect , wait for it to connect again
         DisconnectNamedPipe(pipe); ConnectNamedPipe(pipe, 0);
    }
}

我每次都会遇到非常不同且奇怪的错误.

I'm getting very different and strange errors every time.

它们大多数来自libcurl调用的RtlFreeHeap(),以及来自curl_easy_perform()使用的某些WSA函数的整数除以零.

Most of them come from RtlFreeHeap() that libcurl calls, and integer divided by zero from some WSA functions that curl_easy_perform() uses.

curl_easy_setopt()开始,崩溃可能发生在任何libcurl函数中.

The crash may occur in any of the libcurl functions, starting from curl_easy_setopt().

相同的代码在普通程序中可以正常工作.

The same code works without problems in an ordinary program.

挖掘后,此函数会导致损坏 以前的curl使用之所以不会发生崩溃的原因是,除了此之后,我不使用此函数,然后我创建了一个与使用此函数的线程并行工作的接收器线程,普通程序也没有由于此功能仅适用于服务而崩溃(不在本地系统中运行的程序可以使用EnumWindows) 我认为Poco net不会崩溃,因为它基于c ++而不是c并使用new/delete分配和释放内存,但是崩溃是由于curl和其他函数从_malloc_base和类似的c分配函数开始

EDIT : after digging this is the function causes corruption the reason why the crash didn't happen for previous curl usage is that I don't use this function except after this , then I create a receiver thread that works in parallel with a thread that uses this function , also the ordinary program didn't crash as this function was only for the service (a program not running in local system can use EnumWindows) I think Poco net didn't crash as it's based on c++ not c and uses new/delete to allocate and free the memory but the crash coming from curl and other functions start from _malloc_base and similar c allocation functions

功能代码:

wstring GetCommandLineRemote(DWORD id) {
  PROCESS_BASIC_INFORMATION pbInfo = { 0 };
  HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, 0, id);
  if (!hProc || hProc == INVALID_HANDLE_VALUE) return wstring(L"");
  auto status = fNtQueryInformationProcess(hProc, ProcessBasicInformation, &pbInfo, sizeof(pbInfo), NULL);
  if (!NT_SUCCESS(status)) { CloseHandle(hProc); return wstring(L""); }
  BPEB bbeb = { 0 };
  BOOL result;
  result = ReadProcessMemory(hProc, (void*)pbInfo.PebBaseAddress, &bbeb, sizeof(BPEB), 0);
  if (!result) { CloseHandle(hProc); return wstring(L""); }
  BRTL_USER_PROCESS_PARAMETERS parameters = { 0 };
  result = ReadProcessMemory(hProc, (void*)((uintptr_t)bbeb.ProcessParameters), &parameters, sizeof(BRTL_USER_PROCESS_PARAMETERS), 0);
  if (!result) { CloseHandle(hProc); return wstring(L""); }
  UNICODE_STRING CommandLine = { 0 };
  CommandLine.Length = parameters.CommandLine.Length;
  CommandLine.MaximumLength = parameters.CommandLine.MaximumLength;
  CommandLine.Buffer = new WCHAR[CommandLine.MaximumLength];
  result = ReadProcessMemory(hProc, (void*)parameters.CommandLine.Buffer, CommandLine.Buffer, parameters.MaximumLength, 0);
  if (!result) { CloseHandle(hProc); return wstring(L""); }
  CloseHandle(hProc);
  wstring wCommandLine = CommandLine.Buffer;
  delete CommandLine.Buffer;
  return wCommandLine;
}

我正在使用此功能通过辅助进程的开始命令行来区分我的助手进程的实例

I'm using this function to distinguish the instances of my helper process by the command line that the process started with

所以查找实例的机制如下:

so the mechanism to find the instance looks like this :

vector<DWORD> enum_ids(wstring proc_name) {
  HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  vector<DWORD> ids;
  PROCESSENTRY32W entry = { 0 };
  entry.dwSize = sizeof(entry);
  if (!Process32FirstW(snap, &entry)) return ids;
  do {
      wstring p_name = entry.szExeFile;
      auto check_pos = p_name.find(proc_name);
      if (check_pos != wstring::npos) {
        ofile << "[*] found process instance with id : " << entry.th32ProcessID << endl;
        ids.push_back(entry.th32ProcessID);
      }
  } while (Process32NextW(snap, &entry));
  return ids;
}


DWORD find_process(wstring proc_name,wstring unique) {
  DWORD process_id = 0;
  auto ids = enum_ids(proc_name);
  for (auto id : ids) {
      wstring wCommandLine = GetCommandLineRemote(id);
      auto check_pos = wCommandLine.find(unique);
      if (check_pos != wstring::npos) {
          process_id = id;
          break;
      }
      //if (id == 83004) { process_id = id; break; } 83004 is example , if I used this instead of the above comparison code no errors occur so I assumed the errors come from GetCommandLineRemote 
  }
  return process_id;
}

然后在服务主线程中:

CreateThread(0, 0, recieve, 0, 0, 0);
CreateThread(0, 0, FindParticularInstance, (char*)"ch", 0, 0);

现在找到错误源之后,此函数将如何处理所有这些错误以及如何防止此错误发生?

now after finding the errors source , how this function do all these errors and how to prevent it from this ?

结构的定义(来自NiroSoft和流程黑客):

the definitions of the structures (from NiroSoft and process hacker) :

 typedef struct _CURDIR
{
  UNICODE_STRING DosPath;
  PVOID Handle;
} CURDIR, *PCURDIR;

typedef struct _RTL_DRIVE_LETTER_CURDIR
{
  WORD Flags;
  WORD Length;
  ULONG TimeStamp;
  STRING DosPath;
} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;

typedef struct _BRTL_USER_PROCESS_PARAMETERS
{
  ULONG MaximumLength;
  ULONG Length;
  ULONG Flags;
  ULONG DebugFlags;
  PVOID ConsoleHandle;
  ULONG ConsoleFlags;
  PVOID StandardInput;
  PVOID StandardOutput;
  PVOID StandardError;
  CURDIR CurrentDirectory;
  UNICODE_STRING DllPath;
  UNICODE_STRING ImagePathName;
  UNICODE_STRING CommandLine;
  PVOID Environment;
  ULONG StartingX;
  ULONG StartingY;
  ULONG CountX;
  ULONG CountY;
  ULONG CountCharsX;
  ULONG CountCharsY;
  ULONG FillAttribute;
  ULONG WindowFlags;
  ULONG ShowWindowFlags;
  UNICODE_STRING WindowTitle;
  UNICODE_STRING DesktopInfo;
  UNICODE_STRING ShellInfo;
  UNICODE_STRING RuntimeData;
  RTL_DRIVE_LETTER_CURDIR CurrentDirectores[32];
  ULONG EnvironmentSize;
} BRTL_USER_PROCESS_PARAMETERS, *PBRTL_USER_PROCESS_PARAMETERS;

typedef struct _BPEB
{
  UCHAR InheritedAddressSpace;
  UCHAR ReadImageFileExecOptions;
  UCHAR BeingDebugged;
  UCHAR BitField;
  ULONG ImageUsesLargePages : 1;
  ULONG IsProtectedProcess : 1;
  ULONG IsLegacyProcess : 1;
  ULONG IsImageDynamicallyRelocated : 1;
  ULONG SpareBits : 4;
  PVOID Mutant;
  PVOID ImageBaseAddress;
  PPEB_LDR_DATA Ldr;
  PBRTL_USER_PROCESS_PARAMETERS ProcessParameters;
  PVOID SubSystemData;
  PVOID ProcessHeap;
  PRTL_CRITICAL_SECTION FastPebLock;
  PVOID AtlThunkSListPtr;
  PVOID IFEOKey;
  ULONG CrossProcessFlags;
  ULONG ProcessInJob : 1;
  ULONG ProcessInitializing : 1;
  ULONG ReservedBits0 : 30;
  union
  {
      PVOID KernelCallbackTable;
      PVOID UserSharedInfoPtr;
  };
  ULONG SystemReserved[1];
  ULONG SpareUlong;
} BPEB, *PBPEB;

推荐答案

GetCommandLineRemote()中,分配CommandLine.Buffer时,您分配过多. MaximumLength以字节表示,WCHAR的大小为2个字节.您使用new[]分配的内存比实际需要多2倍,但这是可以的,因为您读取的字节数不超过分配的数.您应该将MaximumLength除以sizeof(WCHAR),以避免在使用new WCHAR[]时出现过度分配的情况:

In GetCommandLineRemote(), when you allocate CommandLine.Buffer, you are over-allocating. MaximumLength is expressed in bytes, and WCHAR is 2 bytes in size. Your use of new[] is allocating 2x more memory than you really need, but that is OK since you are not reading more bytes than you allocate. You should divide MaximumLength by sizeof(WCHAR) to avoid over-allocating when using new WCHAR[]:

CommandLine.Buffer = new WCHAR[(CommandLine.MaximumLength / sizeof(WCHAR)) + 1];

但是,不能保证您读取的UNICODE_STRING数据以null结尾,但是您在构造最终的wstring时将其视为是空的.您应该考虑Length(也以字节表示),而不要依赖空终止符:

However, the UNICODE_STRING data you read is not guaranteed to be null terminated, but you are treating it as if it were when constructing the final wstring. You should take the Length into account (which is also expressed in bytes) instead of relying on a null terminator:

wstring wCommandLine(CommandLine.Buffer, CommandLine.Length / sizeof(WCHAR));

更重要的是,您使用delete而不是delete[]释放了CommandLine.Buffer,因此从那时起,您的代码具有未定义的行为.用delete释放用new分配的内存.用delete[]释放用new[]分配的内存.您不能互换它们.

More importantly, you are freeing CommandLine.Buffer with delete instead of delete[], so your code has undefined behavior from that point onward. Memory allocated with new is freed with delete. Memory allocated with new[] is freed with delete[]. You cannot interchange them.

另外,在enum_ids()中,您正在泄漏CreateToolhelp32Snapshot()返回的HANDLE.完成使用后,需要使用CloseHandle()将其关闭.

Also, on a side note, in enum_ids(), you are leaking the HANDLE returned by CreateToolhelp32Snapshot(). You need to close it with CloseHandle() when you are done using it.

这篇关于libcurl在Windows服务中崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆