I am new to windows programming and have written a small utility with mingw which will unrar a package. The code is as provided below
Descrition:
When the below program is run, the results are as follows
XPSP2 32 bit and Windows 7
Untar Operation : Success
CreateProcess return code : Non Zero (Success)
exit Code : 0 (Success)
XP2SP3 32 bit
Untar Operation : Success
CreateProcess return code : Non Zero (Success)
exit Code : 3221225477
Problem Statement
I am not sure why in XP2SP3 patch only, the winRar operation provides the exit code as Huge positive value. Do you find any problem in the below code? Please help in this regard.
int main()
{
string ProgramName = "C:\\Program Files\\WinRAR\\WinRAR.exe";
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInfo;
memset(&StartupInfo, 0, sizeof(STARTUPINFO));
memset(&ProcessInfo, 0, sizeof(PROCESS_INFORMATION)
if (CreateProcess((LPCTSTR)ProgramName.c_str(),(LPCTSTR)"WinRAR.exe x -y -ibck d:\\abc.tar d:\\"),NULL,
NULL,
FALSE,
NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&StartupInfo,
&ProcessInfo) == 0)
{
string tmpStr("Error executing");
tmpStr += ProgramName;
cout<<"StmtDesigner"<<tmpStr<<"CreateProcess failed"<<endl;
}
else
{
string tmpStr("Succes executing");
tmpStr += ProgramName;
cout<<"StmtDesigner"<<tmpStr<<"CreateProcess Success"<<endl;
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
DWORD exitCode=0;
if (GetExitCodeProcess(ProcessInfo.hProcess, &exitCode))
{
string tmpStr("GetExitCodeProcess");
tmpStr += ProgramName;
cout<<tmpStr<<"WinRAR.exe x -y -ibc<<endl;
}
}
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
getch();
return 0;
}
PS : WinRar 3.8 version trail Mode is used for above testing.
That huge positive number, in hexadecimal, is 0xC0000005. It's a common Windows error, which means "Access Violation". Why exactly are you getting it really depends on what winrar is trying to do, but the problem might be with access rights to files. I suggest you give it a try with ProcMon watching your program's file activity. If accessing one of the files is denied, you'll see it in the log.
Related
The 2 trojans:
Wacatac.G!ml
Persistence.G!ml
Here's the code that I believe is causing the issue. The purpose is to create/modify a registry key to make the program run on startup:
void SoftwareDlg::SetSURegValue() {
string regSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\\");
string regValueName = "Software";
string regValue = "D:\\Users\\Skew\\Documents\\repos\\Software\\Debug\\Software.exe"
try
{
size_t bufferSize = 0xFFF;
auto cbData = static_cast<DWORD>(regValue.size() * sizeof(char));
HKEY hKey;
DWORD position;
auto rc = RegCreateKeyEx(HKEY_CURRENT_USER, regSubKey.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &position);
if (position == REG_OPENED_EXISTING_KEY) {
DEBUG_PRINT("Key already exists & has been opened.")
}
else if (position == REG_CREATED_NEW_KEY) {
DEBUG_PRINT("Created new key.")
}
else {
DEBUG_PRINT("ERROR: Key does not exist, and a new key was not created.")
}
if (rc == ERROR_SUCCESS) {
auto rc = RegSetValueEx(hKey, regValueName.c_str(), 0, REG_SZ, (BYTE*)regValue.data(), cbData);
if (rc != ERROR_SUCCESS)
{
throw std::runtime_error("Windows system error code: " + to_string(rc));
}
}
else {
DEBUG_PRINT("Error opening key.\n")
}
}
catch (std::exception& e)
{
DEBUG_PRINT(e.what())
}
}
In my attempts to solve this issue I began testing different scenarios of creating/modifying the key, but my results became inconclusive when I realized that Windows Defender had seemingly stopped logging each run as "new threats" and seemed to log them together as a single "permeant threat" I guess? Not really sure.
With that said, Windows Defender did not seem to log the threat when I would initially create the key or when I would open it and assign it the same value, but did appear to log the threat when I would move the program to a new directory(and the program would attempt to change the value of the "Software" registry value to the new EXE location).
That's left me with several questions:
Does my program mimic the behavior of the 2 trojans through some coding mistake?
Or do I have some latent, opportunistic piece of malware on my machine that's just been waiting to take advantage?
Is deleting the existing value necessary before attempting to change it? The behavior of RegCreateKeyEx leads me to believe this is not the case.
Is writing to the registry without elevated permissions a no-no? If so... why does my machine let me do it?
Am I doing some incorrect type conversion in the RegSetValueEx() function?
If #4 is the case, I guess I'm just really surprised that I was notified by Windows Defender and not Visual Studio or a UAC prompt.
Edit 1: No engines on VirusTotal.com detected the file as malware.
In Windows 10 version 1607, processes can now opt in to long path awareness using a manifest attribute (https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx#maxpath)
How can I programmatically check if the calling process is long path aware? Note that checking the OS version or the value of the registry key alone is insufficient due to the case where the Windows version is >= 1607, long paths are disabled system wide, and the process is not manifested for long paths.
Despite the documentation says that long path names could be enabled for both Win32 and UWP applications, it is broken for UWP. API KernelBase!BasepIsProcessLongPathAwareByManifest uses SxS API to obtain values from manifest, and this API is not functional for UWP.
The problem could be solved by manually setting proper bit in PEB:
NtCurrentTeb()->ProcessEnvironmentBlock->IsLongPathAwareProcess = 1;
definition of TEB could be copied from winternl.h, IsLongPathAwareProcess bit is the most significant bit of 4th byte, i.e this can be rewritten as
((unsigned char*)NtCurrentTeb()->ProcessEnvironmentBlock)[3] |= 0x80;
ntdll (in win10 1607) export next API BOOLEAN NTAPI RtlAreLongPathsEnabled(); - so you can call this. it return TRUE if LongPaths Enabled
here code spinet - if RtlAreLongPathsEnabled returned false - STATUS_NAME_TOO_LONG (c0000106) is returned
system need convert Win32 path to NT path before use it in any file functions, which call kernel. this is done by calling RtlDosPathNameTo*NtPathName* . this functions, if see that path exceeds MAX_PATH (~) - called RtlAreLongPathsEnabled() and continue work only if function return TRUE. in case false - STATUS_NAME_TOO_LONG returned.
code of RtlAreLongPathsEnabled is simply - when first time called - it check registry (and only registry) and save result. not looking for manifest at all. here exactly code of function:
BOOLEAN RtlAreLongPathsEnabled()
{
static BOOLEAN init;
static BOOLEAN elp;
if (!init)
{
init = true;
HANDLE hKey;
KEY_VALUE_PARTIAL_INFORMATION kvpi;
STATIC_OBJECT_ATTRIBUTES(FileSystemRegKeyName, "\\registry\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\FileSystem");
if (0 <= ZwOpenKey(&hKey, KEY_READ, &FileSystemRegKeyName))
{
STATIC_UNICODE_STRING(LongPathRegKeyValue, "LongPathsEnabled");
if (0 <= ZwQueryValueKey(hKey, &LongPathRegKeyValue, KeyValuePartialInformation, &kvpi, sizeof(kvpi), &kvpi.TitleIndex) &&
kvpi.Type == REG_DWORD && kvpi.DataLength == sizeof(DWORD))
{
elp = *(DWORD*)kvpi.Data != 0;
}
ZwClose(hKey);
}
}
return elp;
}
so my conclusion - in current build long path behavior dependent only from registry settings and absolute not depended from application manifest, despite MSDN.
for down votes - for me simply interesting - are somebody from you build test app (with and without manifest) and test this yourself , or you can only read documentation ?
for those who find it difficult, or too lazy to write the code yourself. you can test with this code:
BOOL CreateFolder(LPCWSTR lpPathName)
{
return CreateDirectoryW(lpPathName, 0) || GetLastError() == ERROR_ALREADY_EXISTS;
}
void LPT()
{
WCHAR name[128], path[0x8000], *c;
if (!SHGetFolderPath(0, CSIDL_PROFILE , 0, 0, path))
{
*name = '\\';
__stosw((PUSHORT)name + 1, '3', RTL_NUMBER_OF(name) - 2);
name[RTL_NUMBER_OF(name) - 1] = 0;
c = path + wcslen(path);
int n = 4;
do
{
memcpy(c, name, sizeof(name));
c += RTL_NUMBER_OF(name) - 1;
if (!CreateFolder(path))
{
break;
}
} while (--n);
if (!n)
{
wcscpy(c, L"\\1.txt");
HANDLE hFile = CreateFileW(path, FILE_GENERIC_WRITE, FILE_SHARE_VALID_FLAGS, 0, OPEN_ALWAYS, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
return ;
}
}
}
GetLastError();
}
and test it with <ws2:longPathAware>true</ws2:longPathAware> in manifest and LongPathsEnabled==0 in registry. it fail ? and then test it without manifest but with LongPathsEnabled==1 in registry. worked ?
if so i test on windows 10. version 1607. build 14393.0
on win10 1709 implementation changed: now RtlAreLongPathsEnabled is very simply:
BOOLEAN RtlAreLongPathsEnabled()
{
return NtCurrentTeb()->ProcessEnvironmentBlock->IsLongPathAwareProcess;
}
in previous build was:
EDIT 3
OK, so it seems like this might not be an Installer issue after all. When I make a simple batch file:
exit /b 12
and call it as
cmd /c test.cmd
echo %ERRORLEVEL%
I get "12" on Windows Server 2003 R2, but "0" on XP. I thought I had tested this simple test case many times before but apparently not.
So, I've changed the tags and title but I'm leaving the other information here as there's actually a lot of useful stuff here that is not directly related to this issue.
Thoughts?
Original below
I have a custom action written in VBScript that in turn is calling a Windows batch file (the custom action is essentially allowing the user to execute something at install time they can also run later by running the batch file - it's a convenience). The function is below:
Function MainFunction
strCustomActionData = Session.Property("CustomActionData")
strSplit = Split(strCustomActionData, ";")
strInstallDir = strSplit(0)
strPostCopyAction = strSplit(1)
strScriptLocation = strInstallDir & "\MigrationMasterProcess.cmd"
strFullCommand = """" & strScriptLocation & """ " & strPostCopyAction
Set objShell = CreateObject("WScript.Shell")
Dim objExec
Set objExec = objShell.Exec(strFullCommand)
intReturnCode = objExec.ExitCode
Set objExec = Nothing
Set objShell = Nothing
WriteMessage "Return value: " & intReturnCode
' cf. http://msdn.microsoft.com/en-us/library/windows/desktop/aa371254(v=vs.85).aspx
If (intReturnCode = 0) Then
MainFunction = 1
Else
MainFunction = 3
End If
End Function
When I run the same kind of code outside of a custom action, and the batch file returns an error code (via EXIT /B), the return value is correctly captured in intReturnCode. However, from the custom action, the exit code seems to be "lost" - I always get a 0 back (I can see this in the installer log from the WriteMessage call). It doesn't matter if I use Exec or Run on the shell, I still get back a 0. The script writes its own return code out before returning it (I can see this in the stdout stream from Exec) so I know it's not actually 0. I need that return code to properly report an error back to the installer.
Ideas?
For the record this is Windows Installer 3.0 on Windows XP SP3. The installer is in Wise so I don't have a WiX snippet or I would include it, but this is the function being called.
Also this is somewhat stripped - I've left out comments and other calls to WriteMessage as well as that function. And yes psuedo-Hungarian is evil blah blah blah.
Edit: Here is the C version of the code. It's giving the same exact issue:
#include <Windows.h>
#include <msi.h>
#include <msiquery.h>
#include <stdio.h>
#include <stdlib.h>
#include "LaunchChildProcess.h"
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) {
return TRUE;
}
UINT __stdcall RunMigrationAction(MSIHANDLE hModule) {
UINT uiStat;
DWORD dwPropertySize = MAX_PATH * 2;
TCHAR szValueBuf[MAX_PATH * 2]; // arbitrary but we know the strings won't be near that long
TCHAR *szInstallDir, *szPostCopyAction;
TCHAR *szNextToken;
TCHAR szScriptLocation[MAX_PATH * 2];
TCHAR szParameters[MAX_PATH * 2];
INT iReturnValue;
LogTaggedString(hModule, TEXT("Action Status"), TEXT("Starting"));
uiStat = MsiGetProperty(hModule, TEXT("CustomActionData"), szValueBuf, &dwPropertySize);
if (ERROR_SUCCESS != uiStat) {
LogTaggedString(hModule, TEXT("Startup"), TEXT("Failed to get custom action data"));
return ERROR_INSTALL_FAILURE;
}
LogTaggedString(hModule, TEXT("Properties given"), szValueBuf);
LogTaggedInteger(hModule, TEXT("Property length"), dwPropertySize);
if (0 == dwPropertySize) {
return ERROR_INSTALL_FAILURE;
}
LogTaggedString(hModule, TEXT("Properties given"), szValueBuf);
szInstallDir = wcstok_s(szValueBuf, TEXT(";"), &szNextToken);
szPostCopyAction = wcstok_s(NULL, TEXT(";"), &szNextToken);
LogTaggedString(hModule, TEXT("Install dir"), szInstallDir);
LogTaggedString(hModule, TEXT("Post-copy action"), szPostCopyAction);
wcscpy_s(szScriptLocation, MAX_PATH * 2, szInstallDir);
wcscat_s(szScriptLocation, MAX_PATH * 2, TEXT("\\MigrationMasterProcess.cmd"));
LogTaggedString(hModule, TEXT("Script location"), szScriptLocation);
wcscpy_s(szParameters, MAX_PATH * 2, TEXT(" /C "));
wcscat_s(szParameters, MAX_PATH * 2, szScriptLocation);
wcscat_s(szParameters, MAX_PATH * 2, TEXT(" "));
wcscat_s(szParameters, MAX_PATH * 2, szPostCopyAction);
LogTaggedString(hModule, TEXT("Parameters to cmd.exe"), szParameters);
iReturnValue = ExecuteProcess(TEXT("cmd.exe"), szParameters);
LogTaggedInteger(hModule, TEXT("Return value from command"), iReturnValue);
LogTaggedString(hModule, TEXT("Action Status"), TEXT("Finished"));
return (0 == iReturnValue) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
}
void LogTaggedInteger(MSIHANDLE hInstall, TCHAR* szTag, INT iValue) {
TCHAR szValue[15];
_itow_s(iValue, szValue, 15, 10);
LogTaggedString(hInstall, szTag, szValue);
}
void LogTaggedString(MSIHANDLE hInstall, TCHAR* szTag, TCHAR* szMessage) {
MSIHANDLE hRecord;
UINT uiStat;
//TCHAR szFullMessage[4096];
//wcscpy_s(szFullMessage, 4096, TEXT("--------------- "));
//wcscat_s(szFullMessage, 4096, szTag);
//wcscat_s(szFullMessage, 4096, TEXT(": "));
//wcscat_s(szFullMessage, 4096, szMessage);
hRecord = MsiCreateRecord(3);
uiStat = MsiRecordSetString(hRecord, 0, TEXT("--------- [1]: [2]"));
uiStat = MsiRecordSetString(hRecord, 1, szTag);
uiStat = MsiRecordSetString(hRecord, 2, szMessage);
uiStat = MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), hRecord);
MsiCloseHandle(hRecord);
return;
}
int MsiMessageBox(MSIHANDLE hInstall, TCHAR* szString, DWORD dwDlgFlags) {
PMSIHANDLE newHandle = ::MsiCreateRecord(2);
MsiRecordSetString(newHandle, 0, szString);
return (MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER + dwDlgFlags), newHandle));
}
DWORD ExecuteProcess(TCHAR *szProcess, TCHAR *szParams) {
INT iMyCounter = 0, iPos = 0;
DWORD dwReturnVal = 0;
TCHAR *sTempStr = L"";
/* CreateProcessW can modify Parameters thus we allocate needed memory */
wchar_t * pwszParam = new wchar_t[wcslen(szParams) + 1];
if (NULL == pwszParam) {
return 1;
}
wcscpy_s(pwszParam, wcslen(szParams) + 1, szParams);
/* CreateProcess API initialization */
STARTUPINFOW siStartupInfo;
PROCESS_INFORMATION piProcessInfo;
memset(&siStartupInfo, 0, sizeof(siStartupInfo));
memset(&piProcessInfo, 0, sizeof(piProcessInfo));
siStartupInfo.cb = sizeof(siStartupInfo);
if (CreateProcessW(const_cast<LPCWSTR>(szProcess),
pwszParam, 0, 0, false,
CREATE_DEFAULT_ERROR_MODE, 0, 0,
&siStartupInfo, &piProcessInfo) != false) {
/* Watch the process. */
WaitForSingleObject(piProcessInfo.hProcess, INFINITE);
if (!GetExitCodeProcess(piProcessInfo.hProcess, &dwReturnVal)) {
dwReturnVal = GetLastError();
}
} else {
/* CreateProcess failed */
dwReturnVal = GetLastError();
}
/* Free memory */
free(pwszParam);
pwszParam = NULL;
/* Release handles */
CloseHandle(piProcessInfo.hProcess);
CloseHandle(piProcessInfo.hThread);
return dwReturnVal;
}
When run on my Windows Server 2003 R2 Visual Studio 2008 box, I get the error code as expected:
--------- Return value from command: 5023
When run on my Windows XP test box, I get a 0, even though it should be an error:
--------- Return value from command: 0
Both machines have Windows Installer 3.1. XP is 3.01.4001.5512, 2003 R2 is 3.01.4000.3959.
So it's something acting different between the boxes although I have no idea what.
EDIT 2
The exact table row for the action, as generated by the Wise for Windows Installer tool, is:
"RunMigrationActionCA","1537","Calllaunchchildprocess","RunMigrationAction","0"
To test the immediate flag I added 0x800 to the type column and no change was seen in the end behavior.
To be clear - this works fine on the 2003 R2 machine. That machine is not joined to a domain, but the XP machine is. Is there anything in group policy that could cause this behavior? (Grasping at straws at this point.)
It seems to be a bug in the cmd.exe of WinXP.
The solution is to use exit 123 instead of exit /b 123 in the batch file.
If you don't wish to change existing batch files, just add a wrapper.bat:
#echo off
call %*
exit %errorlevel%
And invoke it:
system("wrapper.bat your.bat all your args")
WScript objects don't work inside custom actions Please reader more here. You could use a DLL custom action. Here is a step by step tutorial.
I have a program coded by someone else. It has a function RegGetValue used as:
uFuncReturnValue = RegOpenKeyExA( HKEY_LOCAL_MACHINE,
acSubKey,
NULL,
KEY_READ | KEY_WRITE,
&hRegistry
);
//Store signature of the disk in the first 32 bytes of structVal
if( uFuncReturnValue != ERROR_SUCCESS )
{
printf("Unable to open registry with error %u\n", uFuncReturnValue);
exit(EXIT_FAILURE);;
}
uFuncReturnValue = RegGetValueA( hRegistry,
NULL,
"\\DosDevices\\C:",
RRF_RT_REG_BINARY,
NULL,
(LPVOID)&structVal,
&dwSize
);
This block of code works perfectly on Windows 7 but returns error when run on Windows XP (32 bit). As 32-bit xp don't have RegGetValue function so I am trying to make use of RegQueryValueEX but I am having problem in passing arguments to this function. I think it should be used some thing like:
uFuncReturnValue = RegQueryValueExA ( hRegistry,
"\\DosDevices\\J:",
NULL,
NULL,
(LPBYTE) &structVal,
&dwSize
);
But something is wrong here because the code compiles successfully but when I execute it I get a message:
The program '(128) myProgram.exe:
Native' has exited with code 1 (0x1).
Can someone help me here please?
I have an application that aids people with disabilities. In order to work, it tracks the what window is currently in the foreground. Normally, I use this function to get process executable.
bool GetWindowProcessExe2(HWND hwnd, wxString& process_exe)
//LPTSTR buf, DWORD size)
{
DWORD result = 0;
DWORD pid = 0;
GetWindowThreadProcessId(hwnd, &pid);
if (HANDLE process =
OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid))
{
char buff[512];
LPTSTR pbuff = buff;
result = GetModuleFileNameEx(process, 0, pbuff, 512);
if(result == 0)
{
//failed.
wxLogError("GetModuleFileNameEx failed with error code %d", GetLastError());
}
CloseHandle(process);
process_exe = fromCString(pbuff);
}
return result > 0 ? true : false;
}
Unfortunately, if the foreground window is the Vista file manager window (the window that opens when you click Start->Computer), GetModuleFileNameEx() fails with error code 299 which says I don't have privileges for this action. My code works for any regular application but not for the windows built in window (the file explorer). I need to know when this window is forefront. Is there another way to do it? I tried reading the window title but that just returns the current directory being shown. Any ideas?
I'm not sure why this isn't working for explorer, but error 299 is ERROR_PARTIAL_COPY, meaning that attempting to read the module name out of explorer is failing.
On Vista, prefer QueryProcessImageFileName and only open the process with PROCESS_QUERY_LIMITED_INFORMATION - your code will work in more cases.
WCHAR exeName[512];
DWORD cchExeName = 512;
HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid);
QueryFullProcessImageName(process, 0, exeName, &cchExeName);
EDIT: I also got ERROR_PARTIAL_COPY with your code running on 64-bit, but only when the querying process was 32-bit. 64-bit/64-bit worked fine.
Looks like a 32 bit process can call GetModuleFileNameEx only on othere 32 bit processes. If you try to call it on 64 bit processes it fails with ERROR_PARTIAL_COPY.On a 64 bit platform make the calling process 64 bit and you should be able to call GetModuleFileNameEx on both 64 and 32 bit processes.