mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-03 21:46:32 +00:00
Assisted-by: Claude Code:claude-opus-5-5 AI-Scope: own msi.dll and msiexec.exe replacing the adapted installer, with tests and packaging
109 lines
4.2 KiB
C
109 lines
4.2 KiB
C
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#include <msi.h>
|
|
#include <msiquery.h>
|
|
|
|
/* A Windows Installer package read as a database: its tables, its summary and a property, as setup programs read them. */
|
|
|
|
static void beside(WCHAR *target, const WCHAR *name)
|
|
{
|
|
unsigned cut = GetModuleFileNameW(NULL, target, MAX_PATH);
|
|
while (cut && target[cut - 1] != L'\\')
|
|
--cut;
|
|
for (unsigned i = 0; name[i]; ++i)
|
|
target[cut++] = name[i];
|
|
target[cut] = 0;
|
|
}
|
|
|
|
static BOOL same(const WCHAR *a, const WCHAR *b)
|
|
{
|
|
while (*a && *a == *b)
|
|
++a, ++b;
|
|
return *a == *b;
|
|
}
|
|
|
|
int raven_entry(void)
|
|
{
|
|
WCHAR package[MAX_PATH], text[64];
|
|
beside(package, L"basic.msi");
|
|
MSIHANDLE database = 0, view = 0, record = 0;
|
|
if (MsiOpenDatabaseW(package, (LPCWSTR)MSIDBOPEN_READONLY, &database) != ERROR_SUCCESS)
|
|
return 1;
|
|
if (MsiDatabaseOpenViewW(database, L"SELECT `Value` FROM `Property` WHERE `Property` = 'ProductName'", &view) != ERROR_SUCCESS)
|
|
return 2;
|
|
if (MsiViewExecute(view, 0) != ERROR_SUCCESS || MsiViewFetch(view, &record) != ERROR_SUCCESS)
|
|
return 3;
|
|
DWORD size = 64;
|
|
if (MsiRecordGetStringW(record, 1, text, &size) != ERROR_SUCCESS || !same(text, L"Raven Sample"))
|
|
return 4;
|
|
MsiCloseHandle(record);
|
|
MsiViewClose(view);
|
|
MsiCloseHandle(view);
|
|
/* The file the package installs, from the File table. */
|
|
if (MsiDatabaseOpenViewW(database, L"SELECT `FileName` FROM `File`", &view) != ERROR_SUCCESS || MsiViewExecute(view, 0) != ERROR_SUCCESS ||
|
|
MsiViewFetch(view, &record) != ERROR_SUCCESS)
|
|
return 5;
|
|
size = 64;
|
|
if (MsiRecordGetStringW(record, 1, text, &size) != ERROR_SUCCESS || !same(text, L"hello.txt"))
|
|
return 6;
|
|
MsiCloseHandle(record);
|
|
MsiCloseHandle(view);
|
|
/* The summary information stream names the package's platform. */
|
|
MSIHANDLE summary = 0;
|
|
UINT type = 0;
|
|
INT number = 0;
|
|
FILETIME time;
|
|
size = 64;
|
|
if (MsiGetSummaryInformationW(database, NULL, 0, &summary) != ERROR_SUCCESS ||
|
|
MsiSummaryInfoGetPropertyW(summary, 7 /* PID_TEMPLATE */, &type, &number, &time, text, &size) != ERROR_SUCCESS || type != 30 /* VT_LPSTR */)
|
|
return 7;
|
|
MsiCloseHandle(summary);
|
|
MsiCloseHandle(database);
|
|
/* The file hash is MD5: this is the known digest of "abc". */
|
|
{
|
|
WCHAR hash_path[MAX_PATH], hash_directory[MAX_PATH];
|
|
DWORD written;
|
|
MSIFILEHASHINFO info;
|
|
GetTempPathW(MAX_PATH, hash_directory);
|
|
GetTempFileNameW(hash_directory, L"rvn", 0, hash_path);
|
|
HANDLE hash_file = CreateFileW(hash_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
|
|
WriteFile(hash_file, "abc", 3, &written, NULL);
|
|
CloseHandle(hash_file);
|
|
info.dwFileHashInfoSize = sizeof info;
|
|
UINT hashed = MsiGetFileHashW(hash_path, 0, &info);
|
|
DeleteFileW(hash_path);
|
|
if (hashed != ERROR_SUCCESS || info.dwData[0] != 0x98500190 || info.dwData[3] != 0x727fe128)
|
|
return 90;
|
|
}
|
|
if (MsiVerifyDiskSpace(0) != ERROR_INVALID_HANDLE)
|
|
return 94;
|
|
if (MsiCollectUserInfoW(L"{11111111-2222-3333-4444-555555555555}") != ERROR_UNKNOWN_PRODUCT || MsiCollectUserInfoW(NULL) != ERROR_INVALID_PARAMETER)
|
|
return 95;
|
|
/* A Darwin shortcut laid out as MS-SHLLINK says: a header, then the extra data block with the descriptor in its Unicode field. */
|
|
{
|
|
static const WCHAR descriptor[] = L"zn=BV3!!!!4!!!!MKKSkWord>m[8Q(!7fN4[M5LC!'6LC";
|
|
static BYTE link[0x4c + 0x314 + 4];
|
|
WCHAR link_path[MAX_PATH], link_directory[MAX_PATH], product[39], feature[64], component[39];
|
|
DWORD written;
|
|
*(DWORD *)link = 0x4c;
|
|
*(DWORD *)(link + 0x14) = 0x1000 | 0x80;
|
|
*(DWORD *)(link + 0x4c) = 0x314;
|
|
*(DWORD *)(link + 0x50) = 0xa0000006;
|
|
lstrcpyW((WCHAR *)(link + 0x4c + 0x10c), descriptor);
|
|
GetTempPathW(MAX_PATH, link_directory);
|
|
GetTempFileNameW(link_directory, L"rvn", 0, link_path);
|
|
HANDLE link_file = CreateFileW(link_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
|
|
WriteFile(link_file, link, sizeof link, &written, NULL);
|
|
CloseHandle(link_file);
|
|
UINT target = MsiGetShortcutTargetW(link_path, product, feature, component);
|
|
DeleteFileW(link_path);
|
|
if (target != ERROR_SUCCESS)
|
|
return 91;
|
|
if (lstrcmpW(product, L"{90160000-000F-0000-1000-0000000FF1CE}") || lstrcmpW(feature, L"Word") || lstrcmpW(component, L"{11111111-2222-3333-4444-555555555555}"))
|
|
return 92;
|
|
if (MsiGetShortcutTargetW(L"C:\\raven-no-such.lnk", product, feature, component) == ERROR_SUCCESS)
|
|
return 93;
|
|
}
|
|
return 0;
|
|
}
|