INTRODUCTION AND RELEVANT INFORMATION:
I am learning WinInet on my own. I have written (in my humble opinion) "typical" piece of code, that needs to perform cleanup in the end:
DWORD CSomeClass::MVCE4StackOverflow()
{
DWORD errorCode = ERROR_SUCCESS;
URL_COMPONENTS urlComp;
::ZeroMemory(&urlComp, sizeof(URL_COMPONENTS));
urlComp.dwStructSize = sizeof(URL_COMPONENTS);
urlComp.dwHostNameLength = -1;
urlComp.dwSchemeLength = -1;
urlComp.dwUrlPathLength = -1;
if (!::InternetCrackUrl(m_URL.c_str(), m_URL.length(), 0, &urlComp))
{
errorCode = ::GetLastError();
return errorCode;
}
HINTERNET hInternetSession = ::InternetOpen("WinInet",
INTERNET_OPEN_TYPE_DIRECT,
NULL, NULL, 0);
if (NULL == hInternetSession)
{
errorCode = ::GetLastError();
return errorCode;
}
std::string hostname(urlComp.dwHostNameLength, 0);
::memcpy(&hostname[0], urlComp.lpszHostName, urlComp.dwHostNameLength);
HINTERNET hHttpSession = ::InternetConnect(hInternetSession,
hostname.c_str(),
INTERNET_DEFAULT_HTTP_PORT, 0, 0,
INTERNET_SERVICE_HTTP, 0, NULL);
if (NULL == hHttpSession)
{
errorCode = ::GetLastError();
return errorCode;
}
HINTERNET hHttpRequest = ::HttpOpenRequest(hHttpSession, "POST",
urlComp.lpszUrlPath, 0, 0, 0,
INTERNET_FLAG_RELOAD, 0);
if (NULL == hHttpRequest)
{
errorCode = ::GetLastError();
return errorCode;
}
const char header[] = "Content-Type: application/x-www-form-urlencoded";
std::string data = "input=1234";
if (!::HttpSendRequest(hHttpRequest, header, strlen(header),
&data[0], data.length()))
{
errorCode = ::GetLastError();
return errorCode;
}
DWORD dwBytesRead = 0;
BOOL result = false;
char szBuffer[1025] = "";
char *temp = szBuffer;
const DWORD dwBytes2Read = sizeof(szBuffer) - 1;
do{
result = ::InternetReadFile(hHttpRequest, szBuffer, dwBytes2Read, &dwBytesRead);
if (FALSE == result)
{
errorCode = ::GetLastError();
}
temp += dwBytesRead;
} while (result && dwBytesRead > 0);
// need error handling for below 3
result = ::InternetCloseHandle(hHttpRequest);
result = ::InternetCloseHandle(hHttpSession);
result = ::InternetCloseHandle(hInternetSession);
return errorCode;
}
PROBLEM:
In the provided code example, I need to call InternetCloseHandle 3 times consecutively.
I do not know how to structure that part of code to perform proper error handling.
My idea would be to do do the following:
result = ::InternetCloseHandle(hHttpRequest);
if(result)
{
result = ::InternetCloseHandle(hHttpSession);
if (result)
{
result = ::InternetCloseHandle(hInternetSession);
if(!result) return ::GetLastError();
}
else return ::GetLastError();
}
else return ::GetLastError();
However, being new to WinInet, I am not sure if my solution is correct.
QUESTION:
Can you please instruct me on how to handle the scenario in the provided code example?
I understand that my question might be confusing, but please take into consideration that English is not my native. Please leave a comment seeking further clarifications.
UPDATE #1:
I have tried to apply RAII:
#include <Windows.h>
#include <iostream>
#include <WinInet.h>
#pragma comment(lib, "Wininet.lib")
class CInternetOpenRAII
{
HINTERNET hIntSession;
public:
CInternetOpenRAII(){}
HINTERNET get() const { return hIntSession; }
DWORD init()
{
hIntSession = ::InternetOpen("WinInet", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
return hIntSession ? ERROR_SUCCESS : ::GetLastError();
}
~CInternetOpenRAII()
{
if(hIntSession)
{
if(!::InternetCloseHandle(hIntSession))
{
std::cerr << "InternetOpen failed with GetLastErrorCode: " << ::GetLastError();
}
}
}
};
class CInternetConnectRAII
{
HINTERNET hHttpSession;
public:
CInternetConnectRAII() {}
HINTERNET get() const { return hHttpSession; }
DWORD init(const HINTERNET &hIntSession, const char *url)
{
hHttpSession = ::InternetConnect(hIntSession, url, INTERNET_DEFAULT_HTTP_PORT, 0, 0, INTERNET_SERVICE_HTTP, 0, NULL);
return hHttpSession ? ERROR_SUCCESS : ::GetLastError();
}
~CInternetConnectRAII()
{
if(hHttpSession)
{
if(!::InternetCloseHandle(hHttpSession))
{
std::cerr << "InternetConnect failed with GetLastErrorCode: " << ::GetLastError();
}
}
}
};
class CHttpOpenRequestRAII
{
HINTERNET hHttpRequest;
public:
CHttpOpenRequestRAII() {}
HINTERNET get() const { return hHttpRequest; }
DWORD init(const HINTERNET &hHttpSession, const char *request)
{
hHttpRequest = ::HttpOpenRequest(hHttpSession, "POST", request, 0, 0, 0, INTERNET_FLAG_RELOAD, 0);
return hHttpRequest ? ERROR_SUCCESS : ::GetLastError();
}
DWORD doRequest(const char *data, size_t dataLength, const char *header, size_t headerLength)
{
if (!::HttpSendRequest(hHttpRequest, header, headerLength, (void *)data, dataLength))
return ::GetLastError();
CHAR szBuffer[10] = "";
DWORD dwRead = 0;
const int dwBytes2Read = sizeof(szBuffer) - 1;
while (::InternetReadFile(hHttpRequest, szBuffer, dwBytes2Read, &dwRead) && dwRead)
{
std::cout << szBuffer;
}
return ERROR_SUCCESS;
}
~CHttpOpenRequestRAII()
{
if(hHttpRequest)
{
if(!::InternetCloseHandle(hHttpRequest))
{
std::cerr << "HttpOpenRequest failed with GetLastErrorCode: " << ::GetLastError();
}
}
}
};
int main()
{
DWORD error = ERROR_SUCCESS;
CInternetOpenRAII session;
error = session.init();
if(error) return error;
CInternetConnectRAII conn;
error = conn.init(session.get(), "www.test.com");
if(error) return error;
CHttpOpenRequestRAII req;
error = req.init(conn.get(), "/home/something");
if(error) return error;
error = req.doRequest("parameter=1234", strlen("parameter=1234"),
"Content-Type: application/x-www-form-urlencoded", strlen("Content-Type: application/x-www-form-urlencoded"));
if(error) return error;
return 0;
}
Now I do not know how to handle error in destructor. Can somebody please look at the code and provide some advice on that?
Related
I have created a simple OPC DA client for the C++ COM API in Qt5.
The client connects to the remote server, gets an OPCServer pointer, creates a new OPC group with an ItemMgt interface, and fails when I try to add items to the group.
The error message is: Incorrect function.
As far as I can see, the IUnknown:: QueryInterface works for this pItemMgt, but the ValidateItems, CreateEnumerator and AddItems calls results in the same Incorrect function error. The OPC server is a QMS220Simulator (Quadera).
Any idea what could be the problem?
This is my first attempt to write a DCOM client, so many, many thing could be wrong with this code.
The qms220.h file contains the CLSID for the QMS220Simulator.
The shortest code to reproduce the problem is this:
#include "opcda.h"
#include "qms220.h"
#include <QApplication>
#include <QDebug>
#include <comdef.h>
static void showStatus(const QString &message,HRESULT code);
IOPCServer *pOPCServer = nullptr;
IOPCItemMgt *pItemMgt = nullptr;
OPCHANDLE serverGroupHandle;
bool initializeCOM()
{
HRESULT hr = CoInitializeEx(nullptr,COINIT_APARTMENTTHREADED);
if (FAILED(hr)) {
showStatus("COM initialization failed!",hr);
return false;
}
hr = CoInitializeSecurity(
NULL, //security descriptor
-1, //COM authentication
NULL, //authentication services
NULL, //reserved
RPC_C_AUTHN_LEVEL_DEFAULT, //default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, //default impersonation
NULL, //authentication info
EOAC_NONE, //additional capabilities
NULL //reserved
);
if (hr == RPC_E_TOO_LATE) {
showStatus("RPC initalization is too late, ignoring...",hr);
} else {
if (FAILED(hr)) {
showStatus("CoInitializeSecurity",hr);
return false;
}
}
return true;
}
void deinitializeCOM()
{
CoUninitialize();
}
static const int INTERFACE_COUNT = 1;
bool connectToServer(const QString &address)
{
_bstr_t serverName = address.toStdString().c_str();
COSERVERINFO cs;
memset(&cs,0,sizeof(cs));
cs.pwszName = serverName;
MULTI_QI qi[INTERFACE_COUNT];
memset(qi,0,sizeof(qi));
qi[0].pIID = &IID_IOPCServer;
HRESULT hr = CoCreateInstanceEx(
CLSID_QMG220SIMDA,
NULL,
CLSCTX_SERVER,
&cs,
INTERFACE_COUNT,
qi
);
if (FAILED(hr)) {
showStatus("CoCreateInstanceEx",hr);
return false;
}
pOPCServer = (IOPCServer*)(qi[0].pItf);
return true;
}
void disconnectFromServer()
{
if (pOPCServer != nullptr) {
pOPCServer->Release();
pOPCServer = nullptr;
}
}
void showOPCStatus(const QString &message,HRESULT hr)
{
if (pOPCServer != nullptr) {
LPWSTR buffer = nullptr;
HRESULT hr2 = pOPCServer->GetErrorString(hr,LOCALE_SYSTEM_DEFAULT,&buffer);
if (hr2 != S_OK) {
qDebug() << message << QString(": HRESULT: 0x%1").arg(hr,8,16,QChar('0'));
} else {
qDebug() << message << QString(": ") << QString::fromWCharArray(buffer);
CoTaskMemFree(buffer);
}
} else {
qDebug() << message << QString(": HRESULT: 0x%1").arg(hr,8,16,QChar('0'));
}
}
static const LPCWSTR MIDGROUPNAME = L"mid";
bool createMIDGroup()
{
if (pOPCServer == nullptr) return false;
OPCHANDLE clientGroupHandle = 1;
DWORD revisedUpdateRate;
HRESULT hr = pOPCServer->AddGroup(
MIDGROUPNAME,
FALSE, //active
0, // requestedUpdateRate
clientGroupHandle,
NULL, //timebias
NULL, //percentDeadBand,
LOCALE_SYSTEM_DEFAULT, //lcid
&serverGroupHandle,
&revisedUpdateRate,
IID_IOPCItemMgt,
(LPUNKNOWN *)(&pItemMgt)
);
showOPCStatus("OPCServer::AddGroup",hr);
if (hr != S_OK) return false;
qDebug() << "The server group handle is: " << QString("0x%1").arg(serverGroupHandle,4,16);
qDebug() << "The revised update rate is: " << revisedUpdateRate;
#define ITEM_ID L"Hardware.Modules.Analyser.SI220.SimulationMode"
QString accessPath("");
QString itemId("Hardware.Modules.Analyser.SI220.SimulationMode");
wchar_t accessPathBuffer[1024];
wchar_t itemIdBuffer[1024];
accessPath.toWCharArray(accessPathBuffer);
itemId.toWCharArray(itemIdBuffer);
static const int ITEM_COUNT = 1;
OPCITEMDEF ItemArray[ITEM_COUNT] =
{{
/*szAccessPath*/ accessPathBuffer,
/*szItemID*/ itemIdBuffer,
/*bActive*/ FALSE,
/*hClient*/ 1,
/*dwBlobSize*/ 0,
/*pBlob*/ NULL,
/*vtRequestedDataType*/ VT_UI1,
/*wReserved*/0
}};
OPCITEMRESULT *itemResults = nullptr;
HRESULT *errors = nullptr;
hr = pItemMgt->AddItems(ITEM_COUNT,ItemArray,&itemResults,&errors);
bool failed = false;
if (hr != S_OK) {
failed = true;
}
showOPCStatus("createMidGroup/AddItems ",hr);
for(DWORD k=0;k<ITEM_COUNT;k++) {
showOPCStatus(QString("createMidGroup/AddItems[%1]").arg(k),errors[k]);
if (errors[k] != S_OK) {
failed = true;
}
CoTaskMemFree(itemResults[k].pBlob);
}
CoTaskMemFree(itemResults);
CoTaskMemFree(errors);
return !failed;
}
void removeMIDGroup()
{
if (pOPCServer != nullptr) {
if (pItemMgt != nullptr) {
pItemMgt->Release();
pItemMgt = nullptr;
}
HRESULT hr = pOPCServer->RemoveGroup(serverGroupHandle,false);
if (hr != S_OK) {
showStatus("deleteMIDGroup",hr);
}
}
}
int main(int argc, char *argv[])
{
Q_UNUSED(argc)
Q_UNUSED(argv)
if (!initializeCOM()) return -1;
if (connectToServer(QString("192.168.12.106"))) {
if (createMIDGroup()) {
removeMIDGroup();
}
disconnectFromServer();
}
deinitializeCOM();
return 0;
}
static void showStatus(const QString &message,HRESULT code)
{
_com_error error(code);
qDebug() << message + QString(": " ) + QString::fromWCharArray(error.ErrorMessage());
}
So, according to the Qt documentation: https://doc.qt.io/qt-5/qstring.html#toWCharArray
The toWCharArray creates a NOT NULL CHAR TERMINATED unicode string.
So a bit better usage would be:
int size = itemId.toWCharArray(itemIdBuffer);
itemIdBuffer[size] = L'\0';
And the same for accessPathBuffer.
Probably it's not a really good idea to send an unterminated group name to the OPC server.
The good thing is, that the CreateGroupEnumerator sends back the same unterminated group names as received.
So the problem is not COM nor OPC related, it's just not reading the fine documentation.
Hi i am trying to download something using WinInet with a costum user agent the issue is that i fails at HttpSendRequestA the error it return is 0
i dont know what this error means i have tried looking it up but nothing comes up
i get the error using std::to_string(GetLastError());
Here is the code bolow
std::string GetUrlData(const std::string& resource, const std::string& host)
{
std::string output = "";
output.clear();
HINTERNET hIntSession = InternetOpenA("token", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (!hIntSession) {
return "ERROR: Session Cannot Start";
}
HINTERNET hHttpSession = InternetConnectA(hIntSession, host.c_str(), 80, 0, 0, INTERNET_SERVICE_HTTP, 0, NULL);
if (!hHttpSession) {
InternetCloseHandle(hIntSession);
return "ERROR: Cannot Connect To Internet";
}
HINTERNET hHttpRequest = HttpOpenRequestA(hHttpSession, "GET", resource.c_str(), 0, 0, 0, INTERNET_FLAG_RELOAD, 0);
if (!hHttpRequest) {
InternetCloseHandle(hHttpSession);
InternetCloseHandle(hIntSession);
return "ERROR: Http Request Failed";
}
// const char* szHeaders = "User-Agent: Mozzila\r\n";
if (!HttpSendRequestA(hHttpRequest, NULL, 0, NULL, 0)) {
InternetCloseHandle(hHttpRequest);
InternetCloseHandle(hHttpSession);
InternetCloseHandle(hIntSession);
return "ERROR: Http Request Failed 2";
}
char szBuffer[1024];
DWORD dwRead = 0;
do {
if (!InternetReadFile(hHttpRequest, szBuffer, sizeof(szBuffer), &dwRead)) {
InternetCloseHandle(hHttpRequest);
InternetCloseHandle(hHttpSession);
InternetCloseHandle(hIntSession);
return "ERROR: Cannot Download";
}
if (dwRead == 0)
break;
output.append(szBuffer, dwRead);
}
while (true);
return output;
}
I call this fuction like so std::string
data = GetUrlData("http://example.com/", "http://example.com/");
The problem is that your host parameter should only be the hostname, not the full URL and the resource should only be the resource part on the host.
Like this:
std::string data = GetUrlData("/", "example.com");
A sidenote: You acquire up to three resources that need to be manually released and try to make sure to release them using InternetCloseHandle() but it soon becomes verbose and you risk forgetting one eventually. As it happens, if your GetUrlData() call succeeds, you don't release any of the resources.
Consider wrapping each resource in a std::unique_ptr to get it released automatically. Not only does it make it safer, it also makes it easier to read and maintain the code.
Example:
#include <Windows.h>
#include <wininet.h>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <type_traits>
// custom deleter for the resource
struct internet_deleter {
void operator()(HINTERNET hi) { InternetCloseHandle(hi); }
};
// the resource's wrapper type
using ihandle = std::unique_ptr<std::remove_pointer_t<HINTERNET>, internet_deleter>;
// functions for acquiring resources
auto RAII_InternetOpen() {
ihandle rv(InternetOpenA("token", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0));
if (rv) return rv;
throw std::runtime_error("InternetOpenA: " + std::to_string(GetLastError()));
}
auto RAII_InternetConnect(const ihandle& hIntSession, const std::string& host) {
ihandle rv(
InternetConnectA(hIntSession.get(), host.c_str(), 80, 0, 0,
INTERNET_SERVICE_HTTP, 0, NULL));
if (rv) return rv;
throw std::runtime_error("InternetConnectA: " + std::to_string(GetLastError()));
}
auto RAII_HttpOpenRequest(const ihandle& hHttpSession, const std::string& resource) {
ihandle rv(
HttpOpenRequestA(hHttpSession.get(), "GET", resource.c_str(), 0, 0, 0,
INTERNET_FLAG_RELOAD, 0));
if (rv) return rv;
throw std::runtime_error("HttpOpenRequestA: " + std::to_string(GetLastError()));
}
With the three functions above, the GetUrlData() function becomes simpler and will not leak resources:
std::string GetUrlData(const std::string& resource, const std::string& host)
{
std::string output;
auto hIntSession = RAII_InternetOpen();
auto hHttpSession = RAII_InternetConnect(hIntSession, host);
auto hHttpRequest = RAII_HttpOpenRequest(hHttpSession, resource);
if (!HttpSendRequestA(hHttpRequest.get(), NULL, 0, NULL, 0))
throw std::runtime_error("HttpSendRequestA: " +
std::to_string(GetLastError()));
char szBuffer[1024];
DWORD dwRead = 0;
do {
if (!InternetReadFile(hHttpRequest.get(), szBuffer, sizeof(szBuffer), &dwRead))
throw std::runtime_error("InternetReadFile: " +
std::to_string(GetLastError()));
if (dwRead == 0)
break;
output.append(szBuffer, dwRead);
} while (true);
return output;
}
int main() {
try {
std::cout << GetUrlData("/", "www.google.com");
}
catch (const std::exception& ex) {
std::cerr << "Exception: " << ex.what() << '\n';
}
}
I have a function to upload information to a byte array, but it ends with - if (!Httpsendrequest(httprequest, szHeaders, strlen(szhheaders), szRequest, strlen(szRequest)))
string GetUrlData(const string& url, LPCSTR host, string& output)
{
string request_data = "";
output = "";
HINTERNET hIntSession = InternetOpenA("token", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (!hIntSession)
{
return request_data;
}
HINTERNET hHttpSession = InternetConnectA(hIntSession, host, 80, 0, 0, INTERNET_SERVICE_HTTP, 0, NULL);
if (!hHttpSession)
{
return request_data;
}
HINTERNET hHttpRequest = HttpOpenRequestA(hHttpSession, "GET", url.c_str()
, 0, 0, 0, INTERNET_FLAG_RELOAD, 0);
if (!hHttpSession)
{
return request_data;
}
char* szHeaders = ("Content-Type: text/html\r\nUser-Agent: License");
char szRequest[1024] = { 0 };
if (!HttpSendRequestA(hHttpRequest, szHeaders, strlen(szHeaders), szRequest, strlen(szRequest)))
{
qDebug()<<"BLYA";
return request_data;
}
CHAR szBuffer[1024] = { 0 };
DWORD dwRead = 0;
while (InternetReadFile(hHttpRequest, szBuffer, sizeof(szBuffer) - 1, &dwRead) && dwRead)
{
request_data.append(szBuffer, dwRead);
}
InternetCloseHandle(hHttpRequest);
InternetCloseHandle(hHttpSession);
InternetCloseHandle(hIntSession);
output = request_data;
}
The failure might have something to do with the fact that szHeaders is missing \r\n on the User-Agent header. Every header you send ust be terminated with \r\n. And BTW, there is no reason to send a Content-Type header in a GET request, since there is no message body being sent to the server.
Also, your code has a bunch of memory leaks if anything goes wrong. You need to close every handle you successfully open.
Also, there is no point in having both a return value and an output parameter that return the same data. Pick one or the other. I would suggest using a bool return value and a string output parameter.
Try something more like this:
bool GetUrlData(const string& resource, const string& host)
{
output.clear();
bool result = false;
HINTERNET hIntSession = InternetOpenA("token", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (!hIntSession) {
qDebug() << "InternetOpen failed, error: " << GetLastError();
return false;
}
HINTERNET hHttpSession = InternetConnectA(hIntSession, host.c_str(), 80, 0, 0, INTERNET_SERVICE_HTTP, 0, NULL);
if (!hHttpSession) {
qDebug() << "InternetConnect failed, error: " << GetLastError();
InternetCloseHandle(hIntSession);
return false;
}
HINTERNET hHttpRequest = HttpOpenRequestA(hHttpSession, "GET", resource.c_str(), 0, 0, 0, INTERNET_FLAG_RELOAD, 0);
if (!hHttpRequest) {
qDebug() << "HttpOpenRequest failed, error: " << GetLastError();
InternetCloseHandle(hHttpSession);
InternetCloseHandle(hIntSession);
return false;
}
const char* szHeaders = "User-Agent: License\r\n";
if (!HttpSendRequestA(hHttpRequest, szHeaders, -1, NULL, 0)) {
qDebug() << "HttpSendRequest failed, error: " << GetLastError();
InternetCloseHandle(hHttpRequest);
InternetCloseHandle(hHttpSession);
InternetCloseHandle(hIntSession);
return false;
}
char szBuffer[1024];
DWORD dwRead = 0;
do {
if (!InternetReadFile(hHttpRequest, szBuffer, sizeof(szBuffer), &dwRead)) {
qDebug() << "InternetReadFile failed, error: " << GetLastError();
InternetCloseHandle(hHttpRequest);
InternetCloseHandle(hHttpSession);
InternetCloseHandle(hIntSession);
return false;
}
if (dwRead == 0)
break;
output.append(szBuffer, dwRead);
}
while (true);
return true;
}
I'm working in a small antirootkit and i need add a functionality that is:
Delete all files on directory of rootkit and in yours possible subdiretories.
So, firstly is necessary know all these directories and files, right?
To this, i have code below that already make half of this task. He enumerate all directories and files of a specific directory but not "see" subdirectories ( files and folders).
Eg:
Output:
Code:
#include <ntifs.h>
typedef unsigned int UINT;
NTSTATUS EnumFilesInDir()
{
HANDLE hFile = NULL;
UNICODE_STRING szFileName = { 0 };
OBJECT_ATTRIBUTES Oa = { 0 };
NTSTATUS ntStatus = 0;
IO_STATUS_BLOCK Iosb = { 0 };
UINT uSize = sizeof(FILE_BOTH_DIR_INFORMATION);
FILE_BOTH_DIR_INFORMATION *pfbInfo = NULL;
BOOLEAN bIsStarted = TRUE;
RtlInitUnicodeString(&szFileName, L"\\??\\C:\\MyDirectory");
InitializeObjectAttributes(&Oa, &szFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
ntStatus = ZwCreateFile(&hFile, GENERIC_READ | SYNCHRONIZE, &Oa, &Iosb, 0, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(ntStatus)) { return ntStatus; }
pfbInfo = ExAllocatePoolWithTag(PagedPool, uSize, '0000');
if (pfbInfo == NULL)
{
ZwClose(hFile); return STATUS_NO_MEMORY;
}
while (TRUE)
{
lbl_retry:
RtlZeroMemory(pfbInfo, uSize);
ntStatus = ZwQueryDirectoryFile(hFile, 0, NULL, NULL, &Iosb, pfbInfo, uSize, FileBothDirectoryInformation, FALSE, NULL, bIsStarted);
if (STATUS_BUFFER_OVERFLOW == ntStatus) {
ExFreePoolWithTag(pfbInfo, '000');
uSize = uSize * 2;
pfbInfo = ExAllocatePoolWithTag(PagedPool, uSize, '0000');
if (pfbInfo == NULL) { ZwClose(hFile); return STATUS_NO_MEMORY; }
goto lbl_retry;
}
else if (STATUS_NO_MORE_FILES == ntStatus)
{
ExFreePoolWithTag(pfbInfo, '000');
ZwClose(hFile); return STATUS_SUCCESS;
}
else if (STATUS_SUCCESS != ntStatus)
{
ExFreePoolWithTag(pfbInfo, '000');
ZwClose(hFile);
return ntStatus;
}
if (bIsStarted)
{
bIsStarted = FALSE;
}
while (TRUE)
{
WCHAR * szWellFormedFileName = ExAllocatePoolWithTag(PagedPool, (pfbInfo->FileNameLength + sizeof(WCHAR)), '0001');
if (szWellFormedFileName)
{
RtlZeroMemory(szWellFormedFileName, (pfbInfo->FileNameLength + sizeof(WCHAR)));
RtlCopyMemory(szWellFormedFileName, pfbInfo->FileName, pfbInfo->FileNameLength);
//KdPrint(("File name is: %S\n", szWellFormedFileName));
KdPrint((" %S\n", szWellFormedFileName));
ExFreePoolWithTag(szWellFormedFileName, '000');
}
if (pfbInfo->NextEntryOffset == 0) { break; }
pfbInfo += pfbInfo->NextEntryOffset;
}
}
ZwClose(hFile);
ExFreePoolWithTag(pfbInfo, '000');
return ntStatus;
}
So, how do this?
Thank in advance by any help or suggestion.
--------------------------------------------------------EDIT:--------------------------------------------------------------------
I found a possible solution, but i'm getting a BSOD in this line:
if ( (*pDir)->NextEntryOffset)
In KernelFindNextFile method.
Some suggestion?
Here is the code that i found:
#include <ntifs.h>
#include <stdlib.h>
HANDLE KernelCreateFile(IN PUNICODE_STRING pstrFile,IN BOOLEAN bIsDir)
{
HANDLE hFile = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
IO_STATUS_BLOCK StatusBlock = {0};
ULONG ulShareAccess = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
ULONG ulCreateOpt = FILE_SYNCHRONOUS_IO_NONALERT;
OBJECT_ATTRIBUTES objAttrib = {0};
ULONG ulAttributes = OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE;
InitializeObjectAttributes(&objAttrib,pstrFile,ulAttributes,NULL,NULL);
ulCreateOpt |= bIsDir?FILE_DIRECTORY_FILE:FILE_NON_DIRECTORY_FILE;
Status = ZwCreateFile(
&hFile,
GENERIC_ALL,
&objAttrib,
&StatusBlock,
0,
FILE_ATTRIBUTE_NORMAL,
ulShareAccess,
FILE_OPEN_IF,
ulCreateOpt,
NULL,
0);
if (!NT_SUCCESS(Status))
{
return (HANDLE)-1;
}
return hFile;
}
PFILE_BOTH_DIR_INFORMATION KernelFindFirstFile(IN HANDLE hFile,IN ULONG ulLen,OUT PFILE_BOTH_DIR_INFORMATION pDir)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
IO_STATUS_BLOCK StatusBlock = {0};
PFILE_BOTH_DIR_INFORMATION pFileList = (PFILE_BOTH_DIR_INFORMATION)ExAllocatePool(PagedPool,ulLen);
Status = ZwQueryDirectoryFile(
hFile,NULL,NULL,NULL,
&StatusBlock,
pDir,
ulLen,
FileBothDirectoryInformation,
TRUE,
NULL,
FALSE);
RtlCopyMemory(pFileList,pDir,ulLen);
Status = ZwQueryDirectoryFile(
hFile,NULL,NULL,NULL,
&StatusBlock,
pFileList,
ulLen,
FileBothDirectoryInformation,
FALSE,
NULL,
FALSE);
return pFileList;
}
NTSTATUS KernelFindNextFile(IN OUT PFILE_BOTH_DIR_INFORMATION* pDir)
{
if ( (*pDir)->NextEntryOffset)
{
(*pDir)=(PFILE_BOTH_DIR_INFORMATION)((UINT32)(*pDir)+(*pDir)->NextEntryOffset);
return STATUS_SUCCESS;
}
return STATUS_UNSUCCESSFUL;
}
void Traversal()
{
UNICODE_STRING ustrFolder = {0};
WCHAR szSymbol[0x512] = L"\\??\\";
UNICODE_STRING ustrPath = RTL_CONSTANT_STRING(L"C:\\MyDirectory");
HANDLE hFile = NULL;
SIZE_T nFileInfoSize = sizeof(FILE_BOTH_DIR_INFORMATION)+270*sizeof(WCHAR);
SIZE_T nSize = nFileInfoSize*0x256;
char strFileName[0x256] = {0};
PFILE_BOTH_DIR_INFORMATION pFileListBuf = NULL;
PFILE_BOTH_DIR_INFORMATION pFileList = NULL;
PFILE_BOTH_DIR_INFORMATION pFileDirInfo = (PFILE_BOTH_DIR_INFORMATION)ExAllocatePool(PagedPool,nSize);
wcscat_s(szSymbol,_countof(szSymbol),ustrPath.Buffer);
RtlInitUnicodeString(&ustrFolder,szSymbol);
hFile = KernelCreateFile(&ustrFolder,TRUE);
pFileList = pFileListBuf;
KernelFindFirstFile(hFile,nSize,pFileDirInfo);
if (pFileList)
{
RtlZeroMemory(strFileName,0x256);
RtlCopyMemory(strFileName,pFileDirInfo->FileName,pFileDirInfo->FileNameLength);
if (strcmp(strFileName,"..")!=0 || strcmp(strFileName,".")!=0)
{
if (pFileDirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
DbgPrint("[Directory]%S\n",strFileName);
}
else
{
DbgPrint("[File]%S\n",strFileName);
}
}
}
while (NT_SUCCESS(KernelFindNextFile(&pFileList)))
{
RtlZeroMemory(strFileName,0x256);
RtlCopyMemory(strFileName,pFileList->FileName,pFileList->FileNameLength);
if (strcmp(strFileName,"..")==0 || strcmp(strFileName,".")==0)
{
continue;
}
if (pFileList->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
DbgPrint("[Directory]%S\n",strFileName);
}
else
{
DbgPrint("[File]%S\n",strFileName);
}
}
RtlZeroMemory(strFileName,0x256);
RtlCopyMemory(strFileName,pFileListBuf->FileName,pFileListBuf->FileNameLength);
if (strcmp(strFileName,"..")!=0 || strcmp(strFileName,".")!=0)
{
if (pFileDirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
DbgPrint("[Directory]%S\n",strFileName);
}
else
{
DbgPrint("[File]%S\n",strFileName);
}
ExFreePool(pFileListBuf);
ExFreePool(pFileDirInfo);
}
}
BSOD:
FAULTING_SOURCE_LINE_NUMBER: 263
FAULTING_SOURCE_CODE:
259: }
260:
261: NTSTATUS KernelFindNextFile(IN OUT PFILE_BOTH_DIR_INFORMATION* pDir)
262: {
> 263: if ((*pDir)->NextEntryOffset)
264: {
265: (*pDir) = (PFILE_BOTH_DIR_INFORMATION)((UINT32)(*pDir) + (*pDir)->NextEntryOffset);
266: return STATUS_SUCCESS;
267: }
268:
ok, here code which tested and works. if somebody can not use it or got BSOD - probably problem not in code but in somebody skills
several notes - if you have previous mode kernel - use Nt* api (when exported) but not Zw* api. or Io* api. if you not understand why, or what is your previous mode - better even not try programming in kernel.
mandatory use FILE_OPEN_REPARSE_POINT option or even not try run this code if not understand what is this and why need use
for delete - open files with FILE_DELETE_ON_CLOSE option, for dump only - with FILE_DIRECTORY_FILE option instead.
code yourself used <= 0x1800 bytes of stack in x64 in deepest folders, like c:\Users - so this is ok for kernel, but always check stack space with IoGetRemainingStackSize
i will be not correct every comma in your code, if you can not do this yourself
#define ALLOCSIZE PAGE_SIZE
#ifdef _REAL_DELETE_
#define USE_DELETE_ON_CLOSE FILE_DELETE_ON_CLOSE
#define FILE_ACCESS FILE_GENERIC_READ|DELETE
#else
#define USE_DELETE_ON_CLOSE FILE_DIRECTORY_FILE
#define FILE_ACCESS FILE_GENERIC_READ
#endif
// int nLevel, PSTR prefix for debug only
void ntTraverse(POBJECT_ATTRIBUTES poa, ULONG FileAttributes, int nLevel, PSTR prefix)
{
if (IoGetRemainingStackSize() < PAGE_SIZE)
{
DbgPrint("no stack!\n");
return ;
}
if (!nLevel)
{
DbgPrint("!nLevel\n");
return ;
}
NTSTATUS status;
IO_STATUS_BLOCK iosb;
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName };
DbgPrint("%s[<%wZ>]\n", prefix, poa->ObjectName);
#ifdef _REAL_DELETE_
if (FileAttributes & FILE_ATTRIBUTE_READONLY)
{
if (0 <= NtOpenFile(&oa.RootDirectory, FILE_WRITE_ATTRIBUTES, poa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT|FILE_OPEN_REPARSE_POINT))
{
static FILE_BASIC_INFORMATION fbi = { {}, {}, {}, {}, FILE_ATTRIBUTE_NORMAL };
NtSetInformationFile(oa.RootDirectory, &iosb, &fbi, sizeof(fbi), FileBasicInformation);
NtClose(oa.RootDirectory);
}
}
#endif//_REAL_DELETE_
if (0 <= (status = NtOpenFile(&oa.RootDirectory, FILE_ACCESS, poa, &iosb, FILE_SHARE_VALID_FLAGS,
FILE_SYNCHRONOUS_IO_NONALERT|FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|USE_DELETE_ON_CLOSE)))
{
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (PVOID buffer = ExAllocatePool(PagedPool, ALLOCSIZE))
{
union {
PVOID pv;
PBYTE pb;
PFILE_DIRECTORY_INFORMATION DirInfo;
};
while (0 <= (status = NtQueryDirectoryFile(oa.RootDirectory, NULL, NULL, NULL, &iosb,
pv = buffer, ALLOCSIZE, FileDirectoryInformation, 0, NULL, FALSE)))
{
ULONG NextEntryOffset = 0;
do
{
pb += NextEntryOffset;
ObjectName.Buffer = DirInfo->FileName;
switch (ObjectName.Length = (USHORT)DirInfo->FileNameLength)
{
case 2*sizeof(WCHAR):
if (ObjectName.Buffer[1] != '.') break;
case sizeof(WCHAR):
if (ObjectName.Buffer[0] == '.') continue;
}
ObjectName.MaximumLength = ObjectName.Length;
#ifndef _REAL_DELETE_
if (DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
#endif
{
ntTraverse(&oa, DirInfo->FileAttributes, nLevel - 1, prefix - 1);
}
#ifndef _REAL_DELETE_
else
#endif
{
DbgPrint("%s%8I64u <%wZ>\n", prefix, DirInfo->EndOfFile.QuadPart, &ObjectName);
}
} while (NextEntryOffset = DirInfo->NextEntryOffset);
}
ExFreePool(buffer);
if (status == STATUS_NO_MORE_FILES)
{
status = STATUS_SUCCESS;
}
}
}
NtClose(oa.RootDirectory);
}
if (0 > status)
{
DbgPrint("---- %x %wZ\n", status, poa->ObjectName);
}
}
void ntTraverse()
{
char prefix[MAXUCHAR + 1];
memset(prefix, '\t', MAXUCHAR);
prefix[MAXUCHAR] = 0;
STATIC_OBJECT_ATTRIBUTES(oa, "\\??\\c:\\users");
//STATIC_OBJECT_ATTRIBUTES(oa, "\\systemroot");
ntTraverse(&oa, FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_READONLY, MAXUCHAR, prefix + MAXUCHAR);
}
I'm writing a win32 form application and drawing it with Direct2D. I have a few cross threaded functions to do animations on it and I'm doing web requests with WinHTTP. The issue is, when I use any WinHttp functions (even just opening an HINTERNET session), it will cause the thread not to terminate properly. After I run the 'login' process once, the program cannot exit calmly. I've posted the relevant code below:
//the login process
void __cdecl processloginasync(void* arg)
{
//getting text from textboxes, etc.
if(usernamestr.find(L'#') != wstring::npos && usernamestr.find(L".") != wstring::npos) {
swapdrawmode(1);
_beginthread(loadwheel,NULL,arg);
void* result = NULL;
unsigned sz = 0;
int rescode = web_request(L"appurl.mywebsite.com/func.php",ss.str().c_str(),result,sz);
//other code to handle the reply...
swapdrawmode(0);
}
else {
error_str = L"Invalid email address.";
err = TRUE;
}
if(err == TRUE) {
textopacity = 0;
animatemode = 0;
_beginthread(animatetext,NULL,arg);
}
//I realize I haven't called 'free' on result, I'll fix that.
}
//the web_request function
int web_request (const wchar_t* server, const wchar_t* object, void*& dest, unsigned& size)
{
vector<void*> retval;
vector<unsigned> szs;
HINTERNET hSess = NULL, hConn = NULL, hReq = NULL;
int res = 0;
DWORD dwDownloaded = 0;
DWORD dwSize = 0;
DWORD retcode = NULL;
short err = FALSE;
const wchar_t* accepted_types[] = {
L"image/*",
L"text/*",
NULL
};
hSess = WinHttpOpen(L"smartCARS2 Web/1.1",WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if(hSess)
hConn = WinHttpConnect(hSess,server,INTERNET_DEFAULT_HTTP_PORT, NULL);
else {
err = TRUE;
retcode = HTTP_OPEN_FAILED;
}
if(hConn)
hReq = WinHttpOpenRequest(hConn, NULL, object, NULL, WINHTTP_NO_REFERER,accepted_types,NULL);
else {
err = TRUE;
retcode = HTTP_CONN_FAILED;
}
if(hReq)
res = WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, NULL, WINHTTP_NO_REQUEST_DATA, NULL, NULL, NULL);
else {
err = TRUE;
retcode = HTTP_OPENREQ_FAILED;
}
if(res)
res = WinHttpReceiveResponse(hReq, NULL);
else {
err = TRUE;
retcode = HTTP_SEND_REQ_FAILED;
}
DWORD tsize = 0;
if(res) {
do {
dwSize = 0;
if(!WinHttpQueryDataAvailable(hReq, &dwSize)) {
retcode = HTTP_COULD_NOT_QUERY_SIZE;
err = TRUE;
break;
}
if(!dwSize)
break;
tsize += dwSize;
void* rets = malloc(dwSize + 1);
if(!rets) {
break;
}
if(!WinHttpReadData(hReq, (void*)rets, dwSize, &dwDownloaded)) {
retcode = HTTP_COULD_NOT_READ_DATA;
err = TRUE;
break;
}
if(!dwDownloaded) {
retcode = HTTP_COULD_NOT_DOWNLOAD;
err = TRUE;
break;
}
szs.push_back(dwSize);
retval.push_back(rets);
} while(dwSize > 0);
}
size = tsize;
unsigned int sz = retval.size();
dest = malloc(tsize);
tsize = 0;
for(unsigned i = 0; i < sz; i++) {
memcpy((BYTE*)dest + tsize,retval[i],szs[i]);
free(retval[i]);
tsize += szs[i];
}
if(hSess)
WinHttpCloseHandle(hSess);
if(hConn)
WinHttpCloseHandle(hConn);
if(hReq)
WinHttpCloseHandle(hReq);
if(err == TRUE)
return retcode;
return 0;
}
As far as I know, as soon as the main thread terminates, the others are not waited for. So the problem is probably in your main thread. You just need to attach a debugger if not already being debugged (Debug | Attach to process in VS) to a zombie process and press "Break all", then use "Threads" and "Call stack" windows to figure what's happening.