I have tested RegDeleteTree() in 32bit program. It only delete the KEY_WOW64_32KEY key. Is there a function like RegOpenKeyEx() to specify the KEY_WOW64_32KEY or KEY_WOW64_64KEY ?
I have tired this code:
HKEY key = NULL;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\asd"), 0, KEY_READ | KEY_WOW64_64KEY, &key) == ERROR_SUCCESS)
{
long d = RegDeleteTree(key, NULL);
if (d != ERROR_SUCCESS)
{
cout << "Error" << d;
}
else
{
cout << "Success";
}
}
The output is Success but the key still exits in registry. I'm running it on Windows 7 with VS 2013 and compiled as win32 program not x64.
Use RegOpenKeyEx with KEY_WOW64_64KEY to open the 64 bit view of the key. Then call RegDeleteTree passing the returned key.
Regarding your update. You only ask for read access when opening the key. The documentation for RegDeleteTree states:
The key must have been opened with the following access rights: DELETE, KEY_ENUMERATE_SUB_KEYS, and KEY_QUERY_VALUE.
The fact that the call succeeds anyway makes me think that your process is subject to virtualization. You need to have a manifest for the process that forces UAC elevation. Use the requireAdministrator option in the manifest.
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.
I'm facing a rather interesting issue in regards to Authenticode signing an UWP appxbundle file.
Some background:
The client provided us with a SafeNet USB token containing the signing certificate. The private key is not exportable, of course. I want to be able to use this certificate for our automated release builds to sign the package. Unfortunately, the token requires a PIN to be entered once per session, so for example if the build agent reboots, the build will fail. We enabled single login on the token so it's enough to unlock it once a session.
Current state:
We can use signtool on the appxbundle without any problems, given the token has been unlocked. This works well enough but breaks as soon as the machine is rebooted or the workstation is locked.
After some searching I managed to find this piece of code. This takes the signing parameters (including the token PIN) and invokes Windows API to sign the target file. I managed to compile this and it worked flawlessly for signing the installation wrapper (EXE file) - the token did not ask for PIN and was unlocked automatically by the API call.
However, when I invoked the same code on the appxbundle file, the call to CryptUIWizDigitalSign failed with error code 0x80080209 APPX_E_INVALID_SIP_CLIENT_DATA. This is a mystery to me because invoking signtool on the same bundle, with the same parameters/certificate works without problem so the certificate should be fully compatible with the package.
Does anyone have experience with something like this? Is there a way to figure out what is the root cause of the error (what is incompatible between my cert and the bundle)?
EDIT 1
In response to a comment:
The code I'm using to call the APIs (taken directly from the aforementioned SO question)
#include <windows.h>
#include <cryptuiapi.h>
#include <iostream>
#include <string>
#pragma comment (lib, "cryptui.lib")
const std::wstring ETOKEN_BASE_CRYPT_PROV_NAME = L"eToken Base Cryptographic Provider";
std::string utf16_to_utf8(const std::wstring& str)
{
if (str.empty())
{
return "";
}
auto utf8len = ::WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), NULL, 0, NULL, NULL);
if (utf8len == 0)
{
return "";
}
std::string utf8Str;
utf8Str.resize(utf8len);
::WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), &utf8Str[0], utf8Str.size(), NULL, NULL);
return utf8Str;
}
struct CryptProvHandle
{
HCRYPTPROV Handle = NULL;
CryptProvHandle(HCRYPTPROV handle = NULL) : Handle(handle) {}
~CryptProvHandle() { if (Handle) ::CryptReleaseContext(Handle, 0); }
};
HCRYPTPROV token_logon(const std::wstring& containerName, const std::string& tokenPin)
{
CryptProvHandle cryptProv;
if (!::CryptAcquireContext(&cryptProv.Handle, containerName.c_str(), ETOKEN_BASE_CRYPT_PROV_NAME.c_str(), PROV_RSA_FULL, CRYPT_SILENT))
{
std::wcerr << L"CryptAcquireContext failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
return NULL;
}
if (!::CryptSetProvParam(cryptProv.Handle, PP_SIGNATURE_PIN, reinterpret_cast<const BYTE*>(tokenPin.c_str()), 0))
{
std::wcerr << L"CryptSetProvParam failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
return NULL;
}
auto result = cryptProv.Handle;
cryptProv.Handle = NULL;
return result;
}
int wmain(int argc, wchar_t** argv)
{
if (argc < 6)
{
std::wcerr << L"usage: etokensign.exe <certificate file path> <private key container name> <token PIN> <timestamp URL> <path to file to sign>\n";
return 1;
}
const std::wstring certFile = argv[1];
const std::wstring containerName = argv[2];
const std::wstring tokenPin = argv[3];
const std::wstring timestampUrl = argv[4];
const std::wstring fileToSign = argv[5];
CryptProvHandle cryptProv = token_logon(containerName, utf16_to_utf8(tokenPin));
if (!cryptProv.Handle)
{
return 1;
}
CRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO extInfo = {};
extInfo.dwSize = sizeof(extInfo);
extInfo.pszHashAlg = szOID_NIST_sha256; // Use SHA256 instead of default SHA1
CRYPT_KEY_PROV_INFO keyProvInfo = {};
keyProvInfo.pwszContainerName = const_cast<wchar_t*>(containerName.c_str());
keyProvInfo.pwszProvName = const_cast<wchar_t*>(ETOKEN_BASE_CRYPT_PROV_NAME.c_str());
keyProvInfo.dwProvType = PROV_RSA_FULL;
CRYPTUI_WIZ_DIGITAL_SIGN_CERT_PVK_INFO pvkInfo = {};
pvkInfo.dwSize = sizeof(pvkInfo);
pvkInfo.pwszSigningCertFileName = const_cast<wchar_t*>(certFile.c_str());
pvkInfo.dwPvkChoice = CRYPTUI_WIZ_DIGITAL_SIGN_PVK_PROV;
pvkInfo.pPvkProvInfo = &keyProvInfo;
CRYPTUI_WIZ_DIGITAL_SIGN_INFO signInfo = {};
signInfo.dwSize = sizeof(signInfo);
signInfo.dwSubjectChoice = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE;
signInfo.pwszFileName = fileToSign.c_str();
signInfo.dwSigningCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_PVK;
signInfo.pSigningCertPvkInfo = &pvkInfo;
signInfo.pwszTimestampURL = timestampUrl.c_str();
signInfo.pSignExtInfo = &extInfo;
if (!::CryptUIWizDigitalSign(CRYPTUI_WIZ_NO_UI, NULL, NULL, &signInfo, NULL))
{
std::wcerr << L"CryptUIWizDigitalSign failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
return 1;
}
std::wcout << L"Successfully signed " << fileToSign << L"\n";
return 0;
}
The certificate is a CER file (public portion only) exported from the token and the container name is taken from the token's info. As I mentioned, this works correctly for EXE files.
The signtool command
signtool sign /sha1 "cert thumbprint" /fd SHA256 /n "subject name" /t "http://timestamp.verisign.com/scripts/timestamp.dll" /debug "$path"
This also works, when I call it either manually or from the CI build when the token is unlocked. But the code above fails with the mentioned error.
EDIT 2
Thanks to all of you, I now have a working implementation! I ended up using the SignerSignEx2 API, as suggested by RbMm. This seems to work fine for both appx bundles and PE files (different parameters for each). Verified on Windows 10 with a TFS 2017 build agent - unlocks the token, finds a specified certificate in the cert store, and signs+timestamps the specified file.
I published the result on GitHub, if anyone is interested: https://github.com/mareklinka/SafeNetTokenSigner
first of all i look where CryptUIWizDigitalSign failed:
the CryptUIWizDigitalSign called SignerSignEx function, with pSipData == 0. for sign PE file (exe, dll, sys) - this is ok and will be work. but for appxbundle (zip archive file type) this parameter mandatory and must point to APPX_SIP_CLIENT_DATA: for appxbundle call stack is
CryptUIWizDigitalSign
SignerSignEx
HRESULT Appx::Packaging::AppxSipClientData::Initialize(SIP_SUBJECTINFO* subjectInfo)
at very begin of Appx::Packaging::AppxSipClientData::Initialize we can view next code:
if (!subjectInfo->pClientData) return APPX_E_INVALID_SIP_CLIENT_DATA;
this is exactly where your code fail.
instead of CryptUIWizDigitalSign need direct call SignerSignEx2 and pSipData is mandatory parameter in this case.
in msdn exist full worked example - How to programmatically sign an app package (C++)
the key point here:
APPX_SIP_CLIENT_DATA sipClientData = {};
sipClientData.pSignerParams = &signerParams;
signerParams.pSipData = &sipClientData;
the modern SignTool call SignerSignEx2 direct:
here again clear visible:
if (!subjectInfo->pClientData) return APPX_E_INVALID_SIP_CLIENT_DATA;
after this called
HRESULT Appx::Packaging::Packaging::SignFile(
PCWSTR FileName, APPX_SIP_CLIENT_DATA* sipClientData)
here at begin next code:
if (!sipClientData->pSignerParams) return APPX_E_INVALID_SIP_CLIENT_DATA;
this clear stated in msdn:
You must provide a pointer to an APPX_SIP_CLIENT_DATA structure as
the pSipData parameter when you sign an app package. You must
populate the pSignerParams member of APPX_SIP_CLIENT_DATA with
the same parameters that you use to sign the app package. To do this,
define your desired parameters on the SIGNER_SIGN_EX2_PARAMS
structure, assign the address of this structure to pSignerParams,
and then directly reference the structure's members as well when you
call SignerSignEx2.
question - why need again provide the same parameters, which used in call SignerSignEx2 ? because appxbundle is really archive, which containing multiple files. and every file need be sign. for this Appx::Packaging::Packaging::SignFile recursive call SignerSignEx2 again :
for this recursive calls pSignerParams and used - for call SignerSignEx2 with exactly the same parameters as top call
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:
I'm using GetRawInputDeviceInfo to get the device name of a USB HID device name.
For some reason, when I run my code under Windows XP I get a device name which starts with \??\ and not \\?\.
This of course means, that when I try to use this device name (in CreateFile for example" it does not work. If I edit the device name and manually fix it to be \\?\ everything works great.
This does not happens in Windows 7. In Win7 everything works great.
I also test for GetLastError after every API call and no errors occur.
All my OS's are 32 bit and my project is compiling with unicode.
Any suggestions what am I doing wrong?? Here's a code snippets from my console application which gets the device name.
nResult = GetRawInputDeviceInfo( pDeviceList[i].hDevice, RIDI_DEVICENAME, NULL, &nBufferSize );
if( nResult < 0 )
{
cout << "ERR: Unable to get Device Name character count.." << endl;
return false;
}
WCHAR* wcDeviceName = new WCHAR[ nBufferSize + 1 ];
if( wcDeviceName == NULL )
{
cout << "ERR: Unable to allocate memory for Device Name.." << endl;
return false;
}
nResult = GetRawInputDeviceInfo( pDeviceList[i].hDevice, RIDI_DEVICENAME, wcDeviceName, &nBufferSize );
if( nResult < 0 )
{
cout << "ERR: Unable to get Device Name.." << endl;
delete [] wcDeviceName;
return false;
}
wcDeviceName[1]='\\';
//This is the manual fix for the device name in WinXP. How do I get rid of it????
pDesc->hHandle = CreateFile(wcDeviceName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
...
...
You are not doing anything wrong.
Just change the second character to \ and you are set. What you see is the raw device path in its native form (\??\...). When you have the form \\?\ that is a crutch MS invented to make long path names available on Win32 when NT arrived despite the limitation of the Win32 subsystem to the \?? object directory.
Please read a few of the chapters of "Windows Internals" by Russinovich (any old edition will do) and use winobj.exe from Sysinternals to explore the object namespace of Windows to see what I'm talking about.
Side-note: when you call CreateFile the code in kernel32.dll will literally undo the suggested change and convert it back to its native form before the native functions get to see the path. So all you are doing with this is to make the Win32 layer understand the path.
well with some help from this site and dream in code i fixed my error, but am now the code just plain doesn't seem to be working. Here is the code:
using (RegistryKey Key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\"))
if (Key != null)
{
string val = (string)Key.GetValue("COMODO Internet Security");
if (val == null)
{
MessageBox.Show("value not found");
}
else
{
// use the value
}
}
else
{
MessageBox.Show("key not found");
}
and when i use it, it give the "value not found" message box like its supposed to if it doesn't exist, only problem is the value DOES exist... i checked manually through regedit. so whats wrong?
Probably you have the problem to query registry values of HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run key on 64-bit operation system from a 32-bit application. In the case you will see values existion only under HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run (see http://msdn.microsoft.com/en-us/library/aa384232.aspx). If you use unmanaged API (or invoce) you can open Run key with RegOpenKeyEx and KEY_QUERY_VALUE | KEY_WOW64_64KEY flags (see http://msdn.microsoft.com/en-us/library/aa384129.aspx). You should test whether the application run under 64-bit operation system before using KEY_QUERY_VALUE | KEY_WOW64_64KEY flag. In case of 32-bit operation system you should use KEY_QUERY_VALUE flag only.