I'm playing around in C++ with WinHTTP.
I made an API in C# which I'm trying to call from C++, for this I'm using WinHTTP. The API is hosted on a local server with SSL active, the root certificate is also trusted.
I made a sample application to go through the following in order:
WinHttpOpen with WINHTTP_FLAG_ASYNC
WinHttpConnect with INTERNET_DEFAULT_HTTPS_PORT
WinHttpOpenRequest with WINHTTP_FLAG_SECURE
WinHttpSetStatusCallback to set callback function
WinHttpSendRequest
WinHttpReceiveResponse
WinHttpQueryDataAvailable
WinHttpReadData
WinHttpCloseHandle on all open handles
Here is the full source:
#include <windows.h>
#include <winhttp.h>
#include <iostream>
#pragma comment(lib, "winhttp")
LPWSTR GetHeaders(HINTERNET hHttp)
{
LPVOID lpOutBuffer = NULL;
DWORD dwSize = 0;
retry:
if (!WinHttpQueryHeaders(hHttp, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, (LPVOID)lpOutBuffer, &dwSize, WINHTTP_NO_HEADER_INDEX))
{
if (GetLastError() == ERROR_WINHTTP_HEADER_NOT_FOUND)
{
std::cout << "ERROR_HTTP_HEADER_NOT_FOUND" << std::endl;
return NULL;
}
else
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
//std::cout << "ERROR_INSUFFICIENT_BUFFER" << std::endl;
lpOutBuffer = new wchar_t[dwSize];
goto retry;
}
else
{
std::cout << GetLastError() << std::endl;
if (lpOutBuffer)
{
delete[] lpOutBuffer;
}
return NULL;
}
}
}
return (LPWSTR)lpOutBuffer;
}
VOID CALLBACK HookCallback(
HINTERNET hInternet,
DWORD_PTR dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength)
{
std::cout << "Status: " << dwInternetStatus << std::endl;
if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR)
{
WINHTTP_ASYNC_RESULT *pAR = (WINHTTP_ASYNC_RESULT*)lpvStatusInformation;
std::cout << "Error: " << pAR->dwError << std::endl;
}
else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE)
{
char *buffer = new char[dwStatusInformationLength+1];
memcpy(buffer, lpvStatusInformation, dwStatusInformationLength);
buffer[dwStatusInformationLength] = '\0';
std::cout << buffer << std::endl;
}
else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE)
{
BOOL result = WinHttpQueryDataAvailable(hInternet, NULL);
if (!result)
{
std::cout << GetLastError() << std::endl;
}
LPWSTR pwszHeaders = GetHeaders(hInternet);
char* headers = new char[wcslen(pwszHeaders) + 1];
wsprintfA(headers, "%S", pwszHeaders);
std::cout << headers << std::endl;
}
else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE)
{
DWORD *bytesAvailable = (DWORD *)lpvStatusInformation;
std::cout << *bytesAvailable << std::endl;
LPVOID buffer = (LPVOID)new char *[*bytesAvailable];
WinHttpReadData(hInternet, buffer, *bytesAvailable, NULL);
}
};
int main()
{
BOOL async = TRUE;
BOOL bResults = FALSE;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
// Use WinHttpOpen to obtain a session handle.
hSession = WinHttpOpen(L"WinHTTP/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
async ? WINHTTP_FLAG_ASYNC : 0);
// Specify an HTTP server.
if (hSession)
hConnect = WinHttpConnect(hSession, L"api.local", INTERNET_DEFAULT_HTTPS_PORT, 0);
// Create an HTTP Request handle.
if (hConnect)
hRequest = WinHttpOpenRequest(hConnect, L"GET",
L"/config",
NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_SECURE);
if (async) {
WinHttpSetStatusCallback(hRequest, (WINHTTP_STATUS_CALLBACK)&HookCallback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0);
}
// Send a Request.
if (hRequest)
bResults = WinHttpSendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0, WINHTTP_NO_REQUEST_DATA, 0,
0, 0);
// Report any errors.
if (!bResults)
std::cout << GetLastError() << std::endl;
bResults = WinHttpReceiveResponse(hRequest, NULL);
// Report any errors.
if (!bResults)
std::cout << GetLastError() << std::endl;
if (!async)
{
DWORD bytesAvailable;
bResults = WinHttpQueryDataAvailable(hRequest, &bytesAvailable);
// Report any errors.
if (!bResults)
std::cout << GetLastError() << std::endl;
std::cout << bytesAvailable << std::endl;
LPVOID buffer = (LPVOID)new char *[bytesAvailable];
bResults = WinHttpReadData(hRequest, buffer, bytesAvailable, NULL);
// Report any errors.
if (!bResults)
std::cout << GetLastError() << std::endl;
std::cout << (char *)buffer << std::endl;
// Close any open handles.
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);
}
while (true)
{
Sleep(1000);
}
}
Whenever I call WinHttpSendRequest, my API is hit and a response is sent back. However, right before it should call the callback with WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, it is triggered with an WINHTTP_CALLBACK_STATUS_REQUEST_ERROR pointing out a ERROR_INTERNET_OPERATION_CANCELLED. The error description says this usually occurs if the handle is closed prematurely. If I hook the CloseHandle with some assembly magic, I can see its indeed called right before the error.
Question: What might be potential causes that it's generating this error at this particular point in the chain? How can I get more information that this when my API seemingly returns its data just fine.
I would have to add that the API is a "proxy" to another API. As such it does it's own WebRequest internally for every request that comes in, then writes that result to the response. The weird thing is however, if I cache the result of the internal WebRequest and return that immediately the next time I try it, without ever initiating the WebRequest, the C++ proceeds as expected. As soon as I call the internal WebRequest, but still return the cached value (even though it's identical to what I'm requesting), C++ fails.
That said, I have excluded the possibility of timeouts being the issue, because the response happens with or without caching within 30ms.
Also, when I disable ASYNC, I get to read my response just fine regardless of what happens on the API side.
I'm really pulling my hairs over this!
Related
I have a C# .NET Framework application that comes along with a C# DLL that has COM interfaces. The COM interfaces are published by registering them in the Windows Registry.
When starting, the C# application starts a C++ application and establishes a communication with it (using NamedPipes). Then, the C++ application calls a function from one of the registered COM interfaces.
I have added a new function to the existing interface, re-compiled all projects, registered the updated DLL in the Windows Registry and when I everything as before, it works totally fine. However, when I use the new function my C++ application crashes - and I can't figure out why.
There is no error in the Windows Event Log and I know that the crash happens when I call "_intf->FunctionV2". Calling "_intf->Function" works fine. I added a try-catch-block around it, but I have not caught anything.
Any ideas?
try
{
_comClientAdapter->AddLog(L"Namespace::Function - pre", TRUE);
result = _intf->FunctionV2(kontext, pCollection, TRUE);
// result = _intf->Function(kontext, pCollection);
_comClientAdapter->AddLog(L"Namespace::Function - post", TRUE);
}
catch (const std::overflow_error& e)
{
std::wstringstream wss;
wss << L"Namespace::Function - overflow_error - " << e.what();
_comClientAdapter->AddLog(wss.str().c_str(), TRUE);
}
catch (const std::runtime_error& e)
{
std::wstringstream wss;
wss << L"Namespace::Function - runtime_error - " << e.what();
_comClientAdapter->AddLog(wss.str().c_str(), TRUE);
}
catch (const std::exception& e)
{
std::wstringstream wss;
wss << L"Namespace::Function - exception - " << e.what();
_comClientAdapter->AddLog(wss.str().c_str(), TRUE);
}
catch (std::string s)
{
std::wstringstream wss;
wss << L"Namespace::Function - exception - " << s.c_str();
_comClientAdapter->AddLog(wss.str().c_str(), TRUE);
}
catch (std::wstring ws)
{
std::wstringstream wss;
wss << L"Namespace::Function - exception - " << ws.c_str();
_comClientAdapter->AddLog(wss.str().c_str(), TRUE);
}
catch (int i)
{
std::wstringstream wss;
wss << L"Namespace::Function - exception - " << i;
_comClientAdapter->AddLog(wss.str().c_str(), TRUE);
}
catch (long l)
{
std::wstringstream wss;
wss << L"Namespace::Function - exception - " << l;
_comClientAdapter->AddLog(wss.str().c_str(), TRUE);
}
catch (unsigned long ul)
{
std::wstringstream wss;
wss << L"Namespace::Function - exception - " << ul;
_comClientAdapter->AddLog(wss.str().c_str(), TRUE);
}
catch (...)
{
_comClientAdapter->AddLog(L"Namespace::Function - exception - UNKNOWN", TRUE);
IErrorInfo* pError;
unsigned long result = GetErrorInfo(0, &pError);
std::wstringstream wss;
wss << L"Namespace::Function - exception - COM Error Result = ";
if (SUCCEEDED(result) && pError)
{
// Build an error message from the COM error object
BSTR bstrSource = NULL;
if (SUCCEEDED(pError->GetSource(&bstrSource)) && bstrSource)
{
_bstr_t src(bstrSource, false);
wss << static_cast<const char*>(src) << " : ";
}
BSTR bstrDesc = NULL;
if (SUCCEEDED(pError->GetDescription(&bstrDesc)) && bstrDesc)
{
wss << " - ";
_bstr_t desc(bstrDesc, false);
wss << static_cast<const char*>(desc);
}
}
else
{
wss << L"GetErrorInfo failed";
}
_comClientAdapter->AddLog(wss.str().c_str(), TRUE);
}
I am trying to load a C++ DLL in a C# program, however the variables from C++ when loaded in C# are corrupt (I think)
This is how the output should look like (this is C++)
//cout << "Loading config, please wait..." << endl;
LoadConfig();
Sleep(2000);
clear();
cout << "Your current settings are: " << endl << endl;
cout << "Resolution: " << screen_width << "x" << screen_height << endl;
cout << "Sensitivty: " << sensitivity << endl;
cout << "Smoothing: " << smoothing << endl;
cout << "FOV: " << fov << endl;
cout << "Mode: " << mode << endl;
cout << "Color: " << color << endl;
cout << "MB4: " << mb4 << endl;
cout << "MB5: " << mb5 << endl;
cout << "lClick: " << lClick << endl;
cout << "Hold : " << hold << endl;
Sleep(10000);
And this is how it actually looks like.
Resolution: 1920x1080
Sensitivty: 1
Smoothing: 1e-07
FOV: 100Your current settings are:
Mode:
1Resolution:
1920x1080Color: PURPLE
Sensitivty: 1
MB4: Smoothing: 1e-071
FOV: 100
MB5:
0
Mode: 1lClick: 0
Hold : 1Color: PURPLE
MB4: 1
MB5: 0
lClick: 0
Hold : 1
The program '[288] GynLockLoader.exe' has exited with code -1073741510 (0xc000013a).
why does this happen? the variables in C++ are correct, I am loading them thru a config loader LoadConfig();
LoadConfig() (don't mind the messy code, I don't use C++ a lot)
void LoadConfig() {
ifstream cFile("config.txt");
if (cFile.is_open())
{
string line;
while (getline(cFile, line))
{
line.erase(remove_if(line.begin(), line.end(), isspace),
line.end());
if (line.empty() || line[0] == '#')
{
continue;
}
auto delimiterPos = line.find("=");
string name = line.substr(0, delimiterPos);
string value = line.substr(delimiterPos + 1);
if (name == "resX") {
screen_width = stoi(value);
}
else if (name == "resY") {
screen_height = stoi(value);
}
else if (name == "sensitivity") {
sensitivity = stod(value);
}
else if (name == "smoothing") {
smoothing = stod(value);
}
else if (name == "fov") {
fov = stoi(value);
}
else if (name == "mode") {
mode = stoi(value);
}
else if (name == "color") {
color = value;
}
else if (name == "mb4") {
if (value == "1") {
mb4 = true;
}
else {
mb4 = false;
}
}
else if (name == "mb5") {
if (value == "1") {
mb5 = true;
}
else {
mb5 = false;
}
}
else if (name == "lClick") {
if (value == "1") {
lClick = true;
}
else {
lClick = false;
}
}
else if (name == "hold") {
if (value == "1") {
hold = true;
}
else {
hold = false;
}
}
}
}
else
{
cerr << "Couldn't open config file for reading.\n";
}
}
I call extern C in dllmain.h so I can use main() in C# by calling DllImport
dllmain.h
extern "C"
__declspec(dllexport)
int
main(void);
And this is how I call main() from C#
C# main();
static class Program
{
[DllImport("GynLock.dll")]
public static extern void main();
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
//Application.EnableVisualStyles();
//Application.SetCompatibleTextRenderingDefault(false);
//Application.Run(new Main());
main();
}
}
Everything loads normally, I just don't understand why my output looks so weird and corrupt (?)
Can somebody help clarify this for me, maybe I am missing something basic here...
c++ force std::cout flush (print to screen)
add (std::)flush to the end of each of it. As You mix it with c# there could be variety of reasons. If You have github project - tell me, I would try to help. Cheers.
cout << "Your current settings are: " << endl << endl << flush;
I'm working on a project which required a dll file for another program written in c# to use (I'm not very familiar with the usage of C++/C#). After finishing the development(?) of the dll file, I started its testing process(run the function call from dll in C# for many times to ensure it works fine). I've asked the way to pass data from C++ to C# here, and the problem is, the program will stop without any error message(I've put try catch in my program) after calling it over 2 times.
I've seen a saying that if there's a memory corruption, the program will stop at a very normal line(e.g. std::cout << ...). I think my situation is similar to that saying...
And here's my code structure...
//dll.hpp
extern "C" LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat*& res, int& img_count, int& mat_type_size, double params[2]);
extern "C" LIB_API void clear_mem(cv::Mat* res);
//dll.cpp
LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat*& res, int& img_count, int& mat_type_size, double params[2])
{
try
{
img_count = 8;
mat_type_size = sizeof(cv::Mat);
res = new cv::Mat[img_count];
cv::Mat& img1 = res[0];
cv::Mat& img2 = res[1];
cv::Mat& img3 = res[2];
cv::Mat& img4 = res[3];
cv::Mat& img5 = res[4];
cv::Mat& img6 = res[5];
cv::Mat& img7 = res[6];
cv::Mat& img8 = res[7];
//some process to update img1~img8
std::cout << "res->" << res << std::endl;
std::cout << "===== finish inference process ===== >> " << (std::clock() - t_inf1) / (double)CLOCKS_PER_SEC << " s" << std::endl;
}
catch (const std::runtime_error& re)
{
std::cerr << "*** Runtime error: " << re.what() << std::endl;
return;
}
catch (const std::exception& ex)
{
std::cerr << "*** Error occurred: " << ex.what() << std::endl;
return;
}
catch (...)
{
std::cerr << "*** Unknown failure occurred... Possible memory corruption" << std::endl;
return;
}
}
LIB_API void clear_mem(cv::Mat* res)
{
try
{
std::cout << ">> In 'clear_mem'...." << std::endl;
std::cout << "res in clear_mem->" << res << std::endl;
delete[] res;
std::cout << ">> finish deleting res...." << std::endl;
}
catch (const std::runtime_error& re)
{
std::cerr << "*** Runtime error: " << re.what() << std::endl;
return;
}
catch (const std::exception& ex)
{
std::cerr << "*** Error occurred: " << ex.what() << std::endl;
return;
}
catch (...)
{
std::cerr << "*** Unknown failure occurred... Possible memory corruption" << std::endl;
return;
}
}
//test.cs
namespace Test_Unet_Console
{
class Program
{
[DllImport(#"D:\Coco\Code\C_plus_plus\unet_cpp_dll\x64\Release\unet_cpp_dll.dll")]
private static extern void inference(byte[] img, long data_len, out IntPtr images, ref int img_count, out int mat_type_size,
[In, MarshalAs(UnmanagedType.LPArray, SizeConst = 3)]double[] param);
[DllImport(#"D:\Coco\Code\C_plus_plus\unet_cpp_dll\x64\Release\unet_cpp_dll.dll")]
private static extern void init_inference();
[DllImport(#"D:\Coco\Code\C_plus_plus\unet_cpp_dll\x64\Release\unet_cpp_dll.dll")]
private static extern void clear_mem(IntPtr images);
static void Cppdll_inf(Bitmap image, out List<Mat> output_pic, double[] param, out IntPtr imgPtrs)
{
Console.WriteLine("before fmt");
ImageFormat fmt = new ImageFormat(image.RawFormat.Guid);
Console.WriteLine("before imageCodecInfo");
var imageCodecInfo = ImageCodecInfo.GetImageEncoders().FirstOrDefault(codec => codec.FormatID == image.RawFormat.Guid);
//this is for situations, where the image is not read from disk, and is stored in the memory(e.g. image comes from a camera or snapshot)
if (imageCodecInfo == null)
{
fmt = ImageFormat.Jpeg;
Console.WriteLine("C# - imageCodecInfo is null");
}
Console.WriteLine("before MemoryStream");
using (MemoryStream ms = new MemoryStream())
{
try
{
Console.WriteLine("C# - before image.Save");
image.Save(ms, fmt);
Console.WriteLine("C# - finish image.Save");
byte[] image_byte_array = ms.ToArray();
Console.WriteLine("C# - finish reading pic");
int imgCount = 0;
inference(image_byte_array, ms.Length, out var imgPtrs, ref imgCount, out var matTypeSize, param);
output_pic = new List<Mat>();
for (int i = 0; i < imgCount; i++)
{
output_pic.Add(new Mat(IntPtr.Add(imgPtrs, i * matTypeSize)));
}
Console.WriteLine("finish getting value from imgPtrs.....");
}
catch (Exception ex)
{
throw ex;
}
}
}
// I've tried to add this in my code, but it will cause some error(I didn't record it...)
static void clear()
{
Console.WriteLine("start gc collect");
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("finish gc collect in clear");
}
static void show_result(List<Mat> pic_list)
{
for (int i = 0; i < pic_list.Count; i++)
{
Console.WriteLine(i);
Cv2.ImShow("test", pic_list[i]);
Cv2.WaitKey(500);
}
Cv2.DestroyAllWindows();
Console.WriteLine("finish showing pic");
}
static void Main()
{
Bitmap image1 = new Bitmap("C:\\Users\\Coco\\Desktop\\(3).png");
double[] param = { 0.7, 20 }; //{ VI_threshold, area_threshold }
Console.WriteLine(">>> Initializing from C# =======");
init_inference(); // >> initialization of models
Console.WriteLine(">>> Finish initializing from C# ======");
for (int i = 0; i < 3; i++)
{
Console.WriteLine(i);
List<Mat> result;
IntPtr imgPtrs;
Cppdll_inf(image1, out result, param, out imgPtrs); // inference of picture
Console.WriteLine(">>> Finish image1 C# ======");
show_result(result); // showing pic to check if result is correct
clear_mem(imgPtrs); // clean the memory send from dll(?
result.Clear();
Console.WriteLine(">>> Finish result.Clear() ======");
Console.WriteLine("================================");
}
}
}
}
I've tried not to return the result from dll to C#, it can run about 100 times(the maximum I've tested). So I think the problem might be the process when passing data from dll to C#...
By the way, here's my memory usage when running the program...
I've searched many info on the internet, but I still have no idea how to fix this problem, so any advise or help is really appreciated!
(Thanks in advance for finish reading my messy question description! )
It's been quite long not recording the answer of this question...
There are two ways to handle this(in my experience):
Adding a function with doing delete[] cv::Mat* in your cpp, call this function every time after you received a Mat* from dll
Using IDisposable Class in C#'s calling with DLL function
(Imagine like it kills itself after every run with this syntax)
(This is Chinese material, but I think it's pretty clear with its
example code)
I am attempting to write a DLL Injector in a C# WPF Application. I have written a DLL Injector before in Windows Forms, however I wanted to write a new program from scratch. I have written DLL Injectors before many times in many other languages (using the traditional VirtualAllocEx / WriteProcessMemory / CreateRemoteThread method), however I ran into some peculiar problems. My DLL Injector said that it was succeeding, yet my testing program showed no changes. I did some testing with Marshal.GetLastWin32Error() and got ERROR_FILE_NOT_FOUND as a result for VirtualAllocEx, WriteProcessMemory and CreateRemoteThread. I then created an Injector in C++ to test if C# was the problem. When I tested the C++ DLL Injector, every method threw the ERROR_NO_MORE_FILES error. Finally, I downloaded my old DLL Injector and found that it no longer works.
I have no idea what the problem is at all.
C# Injector Code:
IntPtr handle = OpenProcess(ProcessAccessFlags.All, false, SelectedProcess.Id);
Console.WriteLine("OpenProcess: " + new Win32Exception(Marshal.GetLastWin32Error()).Message); // This does not fail.
foreach (string files in DLLFiles.Items) // Get the files from the ListView control
{
// Allocate memory
IntPtr address = VirtualAllocEx(handle, IntPtr.Zero, (uint)(files.Length + 1), AllocationType.Commit, MemoryProtection.ExecuteReadWrite);
Console.WriteLine("VirtualAllocEx: " + new Win32Exception(Marshal.GetLastWin32Error()).Message);
// Write in memory
if (address != IntPtr.Zero)
{
bool success = WriteProcessMemory(handle, address, Encoding.ASCII.GetBytes(files), files.Length + 1, out IntPtr lpNumberOfBytesWritten);
Console.WriteLine("WriteProcessMemory: " + new Win32Exception(Marshal.GetLastWin32Error()).Message);
if (success)
{
IntPtr module = LoadLibrary("kernel32.dll"); // Get the module
Console.WriteLine("LoadLibrary: " + new Win32Exception(Marshal.GetLastWin32Error()).Message);
IntPtr LoadLib = GetProcAddress(module, "LoadLibraryA"); // Get the address of the function
Console.WriteLine("GetProcAddress: " + new Win32Exception(Marshal.GetLastWin32Error()).Message);
// Create a remote thread in the target process
IntPtr thread_handle = CreateRemoteThread(handle, IntPtr.Zero, 0, LoadLib, address, 0, out IntPtr lpThreadId);
Console.WriteLine("CreateRemoteThread: " + new Win32Exception(Marshal.GetLastWin32Error()).Message);
if (thread_handle == IntPtr.Zero)
{
Console.Write("[");
Console.ForegroundColor = ConsoleColor.Red;
Console.Write(files);
Console.ResetColor();
Console.WriteLine("] Injection Failed: " + new Win32Exception(Marshal.GetLastWin32Error()).Message);
}
else
{
Console.Write("[");
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write(files);
Console.ResetColor();
Console.WriteLine("] Injected Successfully.");
}
}
}
}
After running it, the Console said that the injection succeeded yet the error output still kept displaying:
The system cannot find the file specified.
C++ Injector Code:
#include <windows.h>
#include <iostream>
#include <string>
int main() {
DWORD pid = 25860; // Hardcoded for testing purposes.
std::string str;
HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); // Get the process handle
std::cout << handle << std::endl;
std::cout << "Error: " << GetLastError() << std::endl;
LPVOID addr = VirtualAllocEx(handle, NULL, sizeof("C:\\Users\\BenHerebefore\\source\\repos\\InjectionTest\\InjectionTest\\InjectionTest.dll") + 1, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
std::cout << addr << std::endl;
std::cout << "Error: " << GetLastError() << std::endl;
BOOL success = WriteProcessMemory(handle, addr, "C:\\Users\\BenHerebefore\\source\\repos\\InjectionTest\\InjectionTest\\InjectionTest.dll", sizeof("C:\\Users\\BenHerebefore\\source\\repos\\InjectionTest\\InjectionTest\\InjectionTest.dll") + 1, NULL);
std::cout << success << std::endl;
std::cout << "Error: " << GetLastError() << std::endl;
HMODULE module = LoadLibrary("kernel32");
std::cout << module << std::endl;
std::cout << "Error: " << GetLastError() << std::endl;
FARPROC proc = GetProcAddress(module, "LoadLibraryA");
std::cout << proc << std::endl;
std::cout << "Error: " << GetLastError() << std::endl;
HANDLE test = CreateRemoteThread(handle, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(LoadLibrary("kernel32"), "LoadLibrary"), addr, 0, NULL);
std::cout << test << std::endl;
std::cout << "Error: " << GetLastError() << std::endl;
std::getline(std::cin, str);
}
The C++ injector displays Error: 18 after every line. The result for CreateRemoteThread returns 0 and the message is Error: 5. I tried running the program as Administrator but it still didn't work.
I have absolutely no idea what the problem is.
According to Nina's answer, I was compiling it incorrectly. Thanks, Nina!
Is your injector/dll the same architecture as the process you are injecting into? For example, if you are injecting into a 64bit process, your dll and your injector should be compiled as x64. Likewsie if it's x86 or WOW64 it should be compiled accordingly.
Quser.exe allows a client to see user sessions on a remote RDP server. For example,
C:\>quser /server:MyRDPserver
USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
userA 3 Disc 1+20:03 08/07/2014 12:36
userB 4 Disc 1+22:28 08/07/2014 10:38
I would like to build this functionality into a C++ or C# program. Yes, I could just spawn quser.exe and parse the output, but is there an Win32 API or .Net framework class that can give me the same information? Specifically:
User Name
Connection State
Logon time
I've found that using WMI (Win32_LoggedOnUser) to find the same information is unreliable, as it often lists stale connections. I've also tried the psloggedon approach of enumerating subkeys of HKEY_USERS and looking for the Volatile Environment key, but this also suffers from the same problem.
I'm going to answer my own question.
First of all, you need to make sure that permissions are set correctly on the target machine. This entails setting HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\AllowRemoteRPC to 1. A powershell script to do this is:
# Get the service account credential
$cred = Get-Credential my_admin_account
$Target_Computers = #("Computer_1","Computer_2")
# open the remote registry
[long]$HIVE_HKLM = 2147483650
foreach($c in $Target_Computers)
{
$StdRegProv = Get-WmiObject -List -Namespace root\default -ComputerName $c -Credential $cred | where { $_.Name -eq "StdRegProv" }
$StdRegProv.SetDWORDValue($HIVE_HKLM, "SYSTEM\CurrentControlSet\Control\Terminal Server", "AllowRemoteRPC", 1)
}
As Xearinox said, for C++ you can use the WTSxxx functions in the Win32 API. Assuming your computers are not XP, here is some C++ code:
#include <string>
#include <iostream>
#include <iomanip>
#include <windows.h>
#include <WtsApi32.h>
using namespace std;
const unsigned num_connection_states = 10;
const wchar_t* connection_state_list[num_connection_states] = {
L"Active",
L"Connected",
L"ConnectQuery",
L"Shadow",
L"Disc",
L"Idle",
L"Listen",
L"Reset",
L"Down",
L"Init" };
int print_error(DWORD err)
{
// format the message
LPTSTR* ppBuffer = nullptr;
DWORD retval = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, err, 0, reinterpret_cast<LPTSTR>(ppBuffer), 0, nullptr);
// print out
wcerr << "Error: *ppBuffer" << endl;
return 1;
}
wstring format_time(const LARGE_INTEGER& time)
{
// convert to a local Win32 file time
FILETIME ft = { time.LowPart, time.HighPart };
FileTimeToLocalFileTime( &ft, &ft );
// convert to a system time
SYSTEMTIME st;
FileTimeToSystemTime( &ft, &st );
wchar_t local_date[255], local_time[255];
GetDateFormat( LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, local_date, sizeof(local_date)/sizeof(wchar_t) );
GetTimeFormat( LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, local_time, sizeof(local_time)/sizeof(wchar_t) );
wstring result = local_date;
result.append(L" ");
result.append(local_time);
return result;
}
const _int64 SECOND = 10000000;
const _int64 MINUTE = 60*SECOND;
const _int64 HOUR = 60*MINUTE;
const _int64 DAY = 24*HOUR;
wstring format_timespan(const LARGE_INTEGER& timespan)
{
// convert to a local Win32 file time
FILETIME ft = { timespan.LowPart, timespan.HighPart };
FileTimeToLocalFileTime( &ft, &ft );
// convert to a system time
SYSTEMTIME st;
FileTimeToSystemTime( &ft, &st );
wchar_t local_time[255];
int daydiff = floor(
GetTimeFormat( LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, local_time, sizeof(local_time)/sizeof(wchar_t) );
wstring result = local_date;
result.append(L" ");
result.append(local_time);
return result;
}
int wmain(int argc, wchar_t* argv[])
{
// check args
if(argc > 2)
{
wcout << "Usage: " << argv[0] << " [server_name]\n";
return 1;
}
// server name
bool current_server = true;
wstring server_name = L".";
if(argc == 2)
{
server_name = argv[1];
current_server = false;
}
// open the server
HANDLE hServer;
if(current_server)
hServer = WTS_CURRENT_SERVER_HANDLE;
else
hServer = WTSOpenServer(const_cast<LPWSTR>(server_name.c_str()));
// enumerate through the sessions
DWORD Count = 0;
WTS_SESSION_INFO* pSessionInfo = nullptr;
BOOL success = WTSEnumerateSessions(hServer, 0, 1, &pSessionInfo, &Count);
if(success == 0)
return false;
// write the headers
wcout << " " << left << setw(24) << "USERNAME";
wcout << setw(19) << "SESSIONNAME";
wcout << "ID ";
wcout << setw(9) << "STATE";
wcout << "IDLE TIME LOGON TIME";
// loop through each session
for(unsigned long s=0; s<Count; s++)
{
LPTSTR pBuffer = nullptr;
DWORD BytesReturned = 0;
wcout << "\n " << left;
// try getting all info at once
WTSINFO* info = nullptr;
success = WTSQuerySessionInformation(hServer, pSessionInfo[s].SessionId, WTSSessionInfo, reinterpret_cast<LPTSTR*>(&info), &BytesReturned);
bool have_wtsinfo = true;
if(!success)
{
// see why failed
DWORD err = GetLastError();
if(err == ERROR_NOT_SUPPORTED)
have_wtsinfo = false;
else
return print_error(err);
}
// print user name
wstring user_name;
if(have_wtsinfo)
user_name = info->UserName;
else
{
success = WTSQuerySessionInformation(hServer, pSessionInfo[s].SessionId, WTSUserName, &pBuffer, &BytesReturned);
if(!success)
continue;
user_name = pBuffer;
WTSFreeMemory(pBuffer);
}
wcout << setw(24) << user_name;
// print session name
wstring session_name;
if(have_wtsinfo)
session_name = info->WinStationName;
else
{
success = WTSQuerySessionInformation(hServer, pSessionInfo[s].SessionId, WTSWinStationName, &pBuffer, &BytesReturned);
if(!success)
continue;
session_name = pBuffer;
WTSFreeMemory(pBuffer);
}
wcout << setw(19) << session_name;
// print session ID
wcout << right << setw(2) << pSessionInfo[s].SessionId;
// print connection state
WTS_CONNECTSTATE_CLASS connect_state;
if(have_wtsinfo)
connect_state = info->State;
else
{
success = WTSQuerySessionInformation(hServer, pSessionInfo[s].SessionId, WTSConnectState, &pBuffer, &BytesReturned);
if(!success)
continue;
connect_state = *reinterpret_cast<WTS_CONNECTSTATE_CLASS*>(pBuffer);
WTSFreeMemory(pBuffer);
}
if(connect_state>=num_connection_states)
continue;
wcout << " " << left << setw(8) << connection_state_list[connect_state];
// get idle time
LARGE_INTEGER idle = info->CurrentTime;
idle.QuadPart -= info->LogonTime.QuadPart;
// print logon time - not supported
if(info->LogonTime.QuadPart!=0)
{
wcout << format_time(info->LogonTime);
}
// clean up
WTSFreeMemory(info);
}
// clean up
WTSFreeMemory(pSessionInfo);
if(!current_server)
WTSCloseServer(hServer);
}
For C#, the easiest way is to use the Cassia library, which is basically a C# wrapper around the same API functions.
You can call a Win32 API to create a process and pass the "quser /server:MyRDPserver" as parameters,I usually do like this:
PROCESS_INFORMATION process_info;
STARTUPINFOA startup_info;
string cmdline2;
char error_msg[1024];
memset(&process_info, 0, sizeof(process_info));
memset(&startup_info, 0, sizeof(startup_info));
startup_info.cb = sizeof(startup_info);
argc = argarray.size();
for(int i = 0; i < argc; i++) {
cmdline2 += argarray.at(i);
if(i != (argc - 1)) cmdline2 += " ";
}
string command = suCmdLineRemoveQuotations(argarray.at(0));
retval = CreateProcessA(command.c_str(), (LPSTR)cmdline2.c_str(), NULL, NULL, TRUE,
0, NULL, NULL, &startup_info, &process_info);
if (!retval) {
windows_error_string(error_msg, sizeof(error_msg));
error = error_msg;
return false;
}
WaitForSingleObject(process_info.hProcess, msecs);
if(GetExitCodeProcess(process_info.hProcess, &status)) {
// status maybe is STILL_ACTIVE, in that case, the process is killed
if(status == STILL_ACTIVE) {
TerminateProcess(process_info.hProcess, 1);
}
ecode = status;
}
return true;
when the process startup, you can redirect the output.If you use Qt,the problem become simple,you can use QProcess to implement.