Our InnoSetup uses an external dll to grab and check an xml file for the paths to data files.
This works fine in most cases, Windows XP, 7, 10 (32/64bit). But for some few users this fails and for me it fails in Crossover 19 for macOS 10.15.
So first I added a delayload to the inno script to get past the "cannot import dll" runtime error.
But then I get "could not call proc" runtime error.
The InnoSetup debugger pointed me at procedure GetExultGamePaths of our script.
Code in our dll:
extern "C" {
__declspec(dllexport) void __stdcall GetExultGamePaths(char *ExultDir, char *BGPath, char *SIPath, int MaxPath) {
MessageBoxDebug(nullptr, ExultDir, "ExultDir", MB_OK);
MessageBoxDebug(nullptr, BGPath, "BGPath", MB_OK);
MessageBoxDebug(nullptr, SIPath, "SIPath", MB_OK);
int p_size = strlen(ExultDir) + strlen("/exult.cfg") + MAX_STRLEN;
char *p = new char[p_size];
// Get the complete path for the config file
Path config_path(ExultDir);
config_path.AddString("exult.cfg");
config_path.GetString(p, p_size);
setup_program_paths();
const static char *si_pathdef = CFG_SI_NAME;
const static char *bg_pathdef = CFG_BG_NAME;
MessageBoxDebug(nullptr, ExultDir, p, MB_OK);
try {
// Open config file
Configuration config;
if (get_system_path("<CONFIG>") == ".")
config.read_config_file(p);
else
config.read_config_file("exult.cfg");
std::string dir;
// SI Path
config.value("config/disk/game/serpentisle/path", dir, si_pathdef);
if (dir != si_pathdef) {
Path si(ExultDir);
si.AddString(dir.c_str());
si.GetString(SIPath, MaxPath);
} else {
std::strncpy(SIPath, si_pathdef, MaxPath);
}
// BG Path
config.value("config/disk/game/blackgate/path", dir, bg_pathdef);
if (dir != bg_pathdef) {
Path bg(ExultDir);
bg.AddString(dir.c_str());
bg.GetString(BGPath, MaxPath);
} else {
std::strncpy(BGPath, bg_pathdef, MaxPath);
}
} catch (...) {
std::strncpy(BGPath, bg_pathdef, MaxPath);
std::strncpy(SIPath, si_pathdef, MaxPath);
}
delete [] p;
}
The part of the InnoSetup Script
procedure GetExultGamePaths(sExultDir, sBGPath, sSIPath: String; iMaxPath: Integer);
external 'GetExultGamePaths#files:exconfig.dll stdcall delayload';
procedure CurPageChanged(CurPageID: Integer);
var
sBGPath: String;
sSIPath: String;
begin
if CurPageID = DataDirPage.ID then begin
if bSetPaths = False then begin
setlength(sBGPath, 1024);
setlength(sSIPath, 1024);
GetExultGamePaths(ExpandConstant('{app}'), sBGPath, sSIPath, 1023 );
BGEdit.Text := sBGPath;
SIEdit.Text := sSIPath;
end;
end;
end;
The GetExultGamePaths(ExpandConstant('{app}'), sBGPath, sSIPath, 1023 ); is what is producing the "could not call proc" runtime error.
I have no idea why that fails on only a few systems.
The full code for our dll and the script is at https://github.com/exult/exult/blob/master/win32/
The dll's code is in exconfig.* and the InnoSetup script is in exult_installer.iss
Through the help of Piotr in our Wine bug report https://bugs.winehq.org/show_bug.cgi?id=49033 it was discovered that using the mingw tool "dllwrap" is broken and added invalid relocation data.
NOT using dllwrap was the solution and fixed it for my Wine/Crossover install. Still waiting for the last user to report back who had this problem on a real Windows 10 installation.
Thanks for your help
Related
I'm trying to enumerate 32bit process modules names from 64bit application using the following code:
if (EnumProcessModulesEx(hProcess, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL))
{
for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
{
TCHAR szModName[MAX_PATH] = { 0 };
if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
sizeof(szModName) / sizeof(TCHAR)))
{
printf("module name is: %S", szModName);
}
}
}
The code works as expected in Windows 7, as part of the results are:
...
C:\Windows\**SysWOW64**\ntdll.dll
...
In Windows 10 the above code returns the full path but with System32 instead of SysWOW64. e.g,
...
C:\Windows\**System32**\ntdll.dll
...
Looking deeper for the cause, I notice that GetModuleFileNameEx reads the remote process PEB and LDR_TABLE_ENTRY, and starting from Windows 10 the LDR_TABLE_ENTRY contains the full path with System32 and not SysWOW64 - also for 32bit applications.
I also tried to use GetMappedFileName but it isn't straight forward and efficient to translate the path from dos path (\device\harddiskvolume) to standard (c:\) path.
I wonder if there are any other easy way to extract the full syswow64 path.
for get valid win32 file path from file nt-path - simplest way - add L"\\\\?\\globalroot" (\\?\globalroot) prefix. this is because CreateFileW looked from \??\ directory and globalroot is symbolic link in \??\ which let as to jump to root of nt namespace.
for example - \Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll is nt absolute path. and \\?\globalroot\Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll is valid win32 path for CreateFileW - this api convert well known prefix \\?\ to nt prefix \??\ and pass name \??\globalroot\Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll to kernel. when parsing this name - after process symbolic link globalroot which point to root of namespace - we again got \Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll - correct nt path.
so if we need valid win32 path for use in CreateFileW - simply append this prefix to nt path. however some shell32 api not accept this form path. also it not nice looked in UI. if we want got DOS drive letter form path (this is subset of valid win32 paths) - we can use IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH which convert device name to drive letter. this ioctl take as input MOUNTDEV_NAME (declared in mountmgr.h) and output buffer is MOUNTMGR_VOLUME_PATHS. in MOUNTDEV_NAME buffer must be exactly device name, without file path. so we need break returned nt path to 2 components. for example in \Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll :
\Device\HarddiskVolume9 - device path
\Windows\SysWOW64\ntdll.dll - file system path
correct way here first open file and call GetFileInformationByHandleEx with FileNameInfo - we got file system path in output. with this we can use wcsstr for separate device path. also if we open file handle - we can use it in call GetFinalPathNameByHandleW with VOLUME_NAME_DOS. this api do exactly which we will be do - query file path, separate device path and call IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH. + open/close mount manager.
but usual nt file path begin from \Device\HarddiskVolumeX. this allow first try fast way - avoid open file and query it path.
so first we need open mount manager:
#include <mountmgr.h>
HANDLE hMountManager = CreateFile(MOUNTMGR_DOS_DEVICE_NAME,
0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
then we can run next code:
void dumpModules(HANDLE hMountManager, HANDLE hProcess)
{
ULONG cb = 0, cbNeeded = 16;
volatile static UCHAR guz;
PVOID stack = alloca(guz);
HMODULE *hMods, hmod;
__continue:
// cumulative allocate memory in stack, not need free it
cb = RtlPointerToOffset(hMods = (HMODULE*)alloca(cbNeeded - cb), stack);
if (EnumProcessModulesEx(hProcess, hMods, cb, &cbNeeded, LIST_MODULES_32BIT))
{
if (cb < cbNeeded)
{
goto __continue;
}
if (cbNeeded /= sizeof(HMODULE))
{
//i use hard coded size buffers, for reduce code and show main idea
#define FILE_NAME_INFO_buffer_size FIELD_OFFSET(FILE_NAME_INFO, FileName[MAX_PATH])
#define MOUNTDEV_NAME_buffer_size FIELD_OFFSET(MOUNTDEV_NAME, Name[MAX_PATH])
#define MOUNTMGR_VOLUME_PATHS_buffer_size FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz[64])
// + space for 0 at the end
PFILE_NAME_INFO pfni = (PFILE_NAME_INFO)alloca(FILE_NAME_INFO_buffer_size + sizeof(WCHAR));
PMOUNTMGR_VOLUME_PATHS pmvp = (PMOUNTMGR_VOLUME_PATHS)alloca(MOUNTMGR_VOLUME_PATHS_buffer_size);
PMOUNTDEV_NAME pmdn = (PMOUNTDEV_NAME)alloca(MOUNTDEV_NAME_buffer_size);
static WCHAR globalroot[] = L"\\\\.\\globalroot";
alloca(sizeof(globalroot));
PWSTR win32Path = pmdn->Name - RTL_NUMBER_OF(globalroot) + 1;
memcpy(win32Path, globalroot, sizeof(globalroot));
USHORT NameLength = pmdn->NameLength;
do
{
hmod = *hMods++;
if (GetMappedFileNameW(hProcess, hmod, pmdn->Name, MAX_PATH))
{
DbgPrint("%p %S\n",hmod, pmdn->Name);
PWSTR c = 0;
static const WCHAR HarddiskVolume[] = L"\\Device\\HarddiskVolume";
// fast way
if (!memcmp(pmdn->Name, HarddiskVolume, sizeof(HarddiskVolume) - sizeof(WCHAR)))
{
c = wcschr(pmdn->Name + RTL_NUMBER_OF(HarddiskVolume) - 1, '\\');
}
// else - for demo
{
pmdn->NameLength = NameLength;
HANDLE hFile = CreateFile(win32Path, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
//++ just for demo
WCHAR DosPath[MAX_PATH];
if (GetFinalPathNameByHandleW(hFile, DosPath, RTL_NUMBER_OF(DosPath), VOLUME_NAME_DOS))
{
DbgPrint("%S\n", DosPath);
}
RtlGetLastNtStatus();
//-- just for demo
BOOL fOk = GetFileInformationByHandleEx(hFile, FileNameInfo, pfni, FILE_NAME_INFO_buffer_size);
CloseHandle(hFile);
if (fOk)
{
// FileName not 0 terminated
pfni->FileName[pfni->FileNameLength/sizeof(WCHAR)] = 0;
c = wcsstr(pmdn->Name, pfni->FileName);
}
}
}
if (c)
{
pmdn->NameLength = (USHORT)RtlPointerToOffset(pmdn->Name, c);
if (DeviceIoControl(hMountManager, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
pmdn, MOUNTDEV_NAME_buffer_size,
pmvp, MOUNTMGR_VOLUME_PATHS_buffer_size, &cb, NULL))
{
DbgPrint("%S%S\n", pmvp->MultiSz, c);
}
}
}
} while (--cbNeeded);
}
}
}
and demo output for notepad:
0000000000170000 \Device\HarddiskVolume9\Windows\SysWOW64\notepad.exe
\\?\C:\Windows\SysWOW64\notepad.exe
C:\Windows\SysWOW64\notepad.exe
0000000077A90000 \Device\HarddiskVolume9\Windows\SysWOW64\ntdll.dll
\\?\C:\Windows\SysWOW64\ntdll.dll
0000000075460000 \Device\HarddiskVolume9\Windows\SysWOW64\kernel32.dll
\\?\C:\Windows\SysWOW64\kernel32.dll
C:\Windows\SysWOW64\kernel32.dll
0000000074A30000 \Device\HarddiskVolume9\Windows\SysWOW64\KernelBase.dll
\\?\C:\Windows\SysWOW64\KernelBase.dll
C:\Windows\SysWOW64\KernelBase.dll
00000000749B0000 \Device\HarddiskVolume9\Windows\SysWOW64\advapi32.dll
\\?\C:\Windows\SysWOW64\advapi32.dll
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 am writing C code, using WinAPI, that installs a global hook (WH_GETMESSAGE) which injects a DLL into all compatible processes in the system. The injected DLL monitors a certain messages passed to the message queue and creates a text file. The callback function in the DLL should create a text file using a randomly generated name in a specific directory. The methods are working when tested on main but fail when in the DLL (i.e no text file is created). NB: The following are working fine.
The DLL is successfully injected into all (32bit) processes, checked using
ProcessExplorer.
The Callback function is being called, checked using
MessageBeep(-1)
Is there something I am missing in the code? and are there any ways to debug my DLL code injected in a processes e.g in Firefox.
Here's my code in the DLL:
LRESULT CALLBACK GetMsgProc(int code,WPARAM wParam,LPARAM lParam){
if (code < 0) {
return CallNextHookEx(NULL, code, wParam, lParam);
}
if ((code == HC_ACTION) && (wParam == PM_REMOVE)){
//for any message create and write to text file
writeToLogFile();
}
return CallNextHookEx(NULL, code, wParam, lParam);
}
Here's the code that creates the text file:
BOOL writeToLogFile() {
char fileName[10];
char fName[] = "C:\\Users\\MyDir\\";
char buffer[75]; //place concatenated string here
generateRandomStr(fileName); //methos to generate file name
snprintf(buffer, sizeof(buffer), "%s%s.txt", fName, fileName);
HANDLE fHandle =
CreateFile(buffer,
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
//file handle code check ommited for brevity
DWORD bytesWritten;
WriteFile(fHandle, buffer, strlen(buffer), &bytesWritten, NULL);
CloseHandle(fHandle);
return TRUE;
}
/*Generate file name string (a-z characters)*/
VOID generateRandomStr(char holder[]) {
size_t i = 0;
srand(time(NULL)); //system clock seconds for seed
while (i < 9) {
int x = (97 + rand() % 97);
if (x > 96 && x < 122) {
holder[i] = ((char)x);
//puts((char)x);
i++;
}
}
holder[i++] = '\0'; //terminate the string
Check out the following http://www.codeproject.com/Articles/116253/Hot-Patching-Made-Easy. It talks about DLL injection.
Thanks everyone for the comments and tips. Finally managed to make it work, the issue was with the function CreateFile which was failing (some illegal characters in the file name generated). After using the OutputDebugString, I was able to view the output and debugged the generateRandomStr function responsible for generating the filename and it worked.
I've got a problem with default icons for folder on Windows 7.
I'm used to get icons with next code
DWORD flags = directory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL;
...
HICON largeIcon = NULL;
SHGetFileInfo(filename.c_str(), flags, &shfi, sizeof(SHFILEINFO),
SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX);
IImageList* imageList = NULL;
HRESULT hResult = SHGetImageList(SHIL_JUMBO, IID_IImageList, (void**)&imageList);
if (!imageList || hResult != S_OK)
return NULL;
hResult = imageList->GetIcon(shfi.iIcon, ILD_TRANSPARENT, &largeIcon);
if (hResult != S_OK)
return NULL;
icon = LoadBitmapFromHicon(largeIcon);
DestroyIcon(shfi.hIcon);
DestroyIcon(largeIcon);
But it returns corrupted icon for folder. Another ones is fine.
I've applied grey filter over it. Original one looks the same.
http://i.stack.imgur.com/IM7pm.png
PS. I looks fine on W8.
Have the same problem. But I use workaround:
function ExtractFolderIcon: HICON;
var
Info: TSHStockIconInfo;
Lib: HMODULE;
begin
Info.cbSize := SizeOf(Info);
OleCheck(SHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, Info));
Lib := LoadLibrary(Info.szPath);
if Lib = 0 then RaiseLastOSError;
try
Result := LoadImage(Lib, MAKEINTRESOURCE(-Info.iIcon), IMAGE_ICON, 256, 256, LR_DEFAULTCOLOR);
finally
FreeLibrary(Lib);
end;
end;
Left image was received with your "classic" way, right with workaround:
I've written function (in Visual C++ in MS VS 2013) not unlike as on
Recursively searching for files in the computer
Below is my function's source code:
wstring FolderPathValidator::FindRequiredFolder(const wstring& p_InitialPath, wstring p_RequiredFolderName)
{
wstring foundFolder = L"";
wstring folderPath = p_InitialPath + L"\\*";
WIN32_FIND_DATAW folderInfo;
HANDLE search_handle = FindFirstFileW(folderPath.c_str(), &folderInfo);
if (search_handle != INVALID_HANDLE_VALUE)
{
vector<wstring> folders;
do
{
if (folderInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if ((!lstrcmpW(folderInfo.cFileName, L".")) || (!lstrcmpW(folderInfo.cFileName, L"..")))
continue;
}
folderPath = p_InitialPath + L"\\" + wstring(folderInfo.cFileName);
if (folderInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (folderInfo.cFileName == p_RequiredFolderName)
{
foundFolder = folderInfo.cFileName;
return foundFolder;
}
folders.push_back(folderPath);
}
} while (FindNextFileW(search_handle, &folderInfo));
CloseHandle(search_handle);
for (vector<wstring>::iterator iter = folders.begin(), end = folders.end(); iter != end; ++iter)
FindRequiredFolder(*iter, p_RequiredFolderName);
}
return foundFolder;
}
The funcion begins to work without problems. But when it try to execute the line
CloseHandle(search_handle);
then the following exception has place:
First step of exception handling on address 0x76D712C7 в WordsCounter.exe: 0xC0000008: An invalid handle was specified.
Where 'WordsCounter' is the name of application executable file. The FindRequiredFolder function is the member of The FolderPathValidator class. FolderPathValidator class is located in static class library project. Both projects: the class library and C++ console application wich consumes the library are in the same solution. Among files and folders names sometimes occur Russian names in Cyrillic alphabet. But I don't think that Cyrillic folder or file names is the reason of this error. What the reason of this error? How can I correct it? Please help.
Use FindClose instead of CloseHandle. Where you read that you have to use CloseHandle?
Use T() or TEXT() macro, insteaf of L" prefix for unicode strings (TEXT( "" )).
Use lstrcmp without W. It is macro and calls lstrcmpW if your project is Unicode.