我尝试使用Powershell从MSI文件中提取二进制数据。 我可以获得任何其他数据,但我似乎无法提取二进制信息。
$Query = "SELECT Data FROM Binary WHERE Name = 'bannrbmp'"
$View = $Database.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $Database, ($Query))
$View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null)
$Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $View, $null)
$BinaryData = $Record.GetType().InvokeMember("StringData", "GetProperty", $null, $Record, 1)
它打破了最后一行,这让我相信" StringData"存在一个问题,但我可能会偏离目标。这是在Orca打开时表格的样子。
在提取文本数据时,此代码将成功完成,如下所示。
$Query = "SELECT Component FROM FeatureComponents WHERE Feature = 'OrcaHelp'"
$View = $Database.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $Database, ($Query))
$View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null)
$Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $View, $null)
$Data = $Record.GetType().InvokeMember("StringData", "GetProperty", $null, $Record, 1)
我似乎无法在网上找到任何内容,如果有人能够提供帮助,我们将不胜感激。
答案 0 :(得分:1)
这是一个C ++代码片段,它提取二进制文件并将其写入磁盘。您可以使用它,或者至少看到流和流读取。这些是所有脚本语言最终调用的基本Win32 API调用,不需要互操作。它需要一些包括stdio.h,windows.h,msiquery.h和程序包装或Dll来调用 - 我不知道你的C ++舒适度。这应该可行,即使我最近没有测试过。
PMSIHANDLE hDatabase;
PMSIHANDLE hBinaryView;
PMSIHANDLE hBinaryRecord;
//Get the handle to the active database. we need this to do view manipulation
UINT nr = MsiOpenDatabase ("some.msi", MSIDBOPEN_READONLY, &hDatabase);
//Get a view of the binary table based on the SQL Query
char sQuery [] = {"SELECT * FROM Binary WHERE Name='somebinary'"}; // Binary
nr = MsiDatabaseOpenView(hDatabase, sQuery, &hBinaryView);
if (nr!= ERROR_SUCCESS)
return 1;
//MsiViewExecute Needs to to be called for MsiFetchView.
//We pass it null because the query above is as granular as we can get
//so we do not need to take it further by specifying an additional value.
nr = MsiViewExecute(hBinaryView, NULL);
if (nr == ERROR_SUCCESS)
//Fetch the view into a record. We do this because we can only do
//streams out of a record and not out of the view.
nr = MsiViewFetch(hBinaryView, &hBinaryRecord);
//Make sure that the entry was found in the table
if (nr == ERROR_SUCCESS)
{
//Build the path to write the file to
TCHAR FileName [MAX_PATH] = {"somefile.ext"};
char bStream [4096] = {0};
BOOL bOkay=TRUE;
HANDLE hFile = CreateFile(FileName, GENERIC_WRITE, 0, 0,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile == INVALID_HANDLE_VALUE)
nr = -1;
else
{
long nTotal = 0;
long nattr = 0;
DWORD nWritten, nBuffer;
do
{ // Read the stream into a buffer, 1023 bytes at a time
nBuffer=1023;
nr = MsiRecordReadStream(hBinaryRecord, 2, bStream, &nBuffer); // Binary & cab are 2
if ((ERROR_SUCCESS == nr) && (nBuffer > 0))
{
//Write the buffer to a file.
nr = WriteFile(hFile, bStream, nBuffer, &nWritten, NULL);
if (nr != 0)// 0 is bad
nTotal = nTotal + nBuffer; // debug only
else
bOkay = FALSE;
}
else
if (nr != ERROR_SUCCESS)
bOkay = FALSE;
} // record record stream
while (bOkay == TRUE && (nBuffer > 0));
// done copying file
CloseHandle(hFile);
}// create file
// we only needed one row, so close the view
MsiViewClose(hBinaryView);
// done with query
MsiCloseHandle(hBinaryRecord);
}
// done with binary table
MsiCloseHandle(hBinaryView);
// done with MSI database
MsiCloseHandle(hDatabase);
答案 1 :(得分:1)
以下是如何提取嵌入在msi二进制表中的每个二进制数据条目的示例。 原始文件名丢失,但是嵌入数据的名称列用作输出文件的名称。
在我的情况下,选择标志'msiReadStreamAnsi'提取与嵌入的文件完全相同的文件,但还有其他标志: https://docs.microsoft.com/en-us/windows/win32/msi/record-readstream
function Get-Property ($Object, $PropertyName, [object[]]$ArgumentList) {
return $Object.GetType().InvokeMember($PropertyName, 'Public, Instance, GetProperty', $null, $Object, $ArgumentList)
}
function Invoke-Method ($Object, $MethodName, $ArgumentList) {
return $Object.GetType().InvokeMember($MethodName, 'Public, Instance, InvokeMethod', $null, $Object, $ArgumentList)
}
$msiPath = 'X:\install\setup.msi' # FULL PATH TO MSI*
$msiOpenDatabaseModeReadOnly = 0
$Installer = New-Object -ComObject WindowsInstaller.Installer
$Database = Invoke-Method $Installer OpenDatabase @($msiPath, $msiOpenDatabaseModeReadOnly)
$ViewTableBinary = Invoke-Method $Database OpenView @("SELECT Name FROM _Tables WHERE Name='Binary'")
Invoke-Method $ViewTableBinary Execute
$TableBinary = Invoke-Method $ViewTableBinary Fetch
Invoke-Method $ViewTableBinary Close @()
if ($TableBinary) {
$msiReadStreamAnsi = 2
$ViewBinary = Invoke-Method $Database OpenView @("SELECT Name, Data FROM Binary")
Invoke-Method $ViewBinary Execute
Do {
$Binary = Invoke-Method $ViewBinary Fetch
if ($Binary) {
$Name = Get-Property $Binary StringData 1
$DataSize = Get-Property $Binary DataSize 2
Write-Output "Found Binary: $msiPath/$Name ($DataSize bytes)"
$BinaryData = Invoke-Method $Binary ReadStream @(2, $DataSize, $msiReadStreamAnsi)
$BinaryData | Set-Content -NoNewLine $Name
}
}
While ($Binary)
Invoke-Method $ViewBinary Close @()
Remove-Variable -Name Binary, ViewBinary
}
Remove-Variable -Name TableBinary, ViewTableBinary, Database, Installer
*必须提供msi文件的完整路径,否则可能会出现异常:
Exception calling "InvokeMember" with "5" argument(s): "OpenDatabase,DatabasePath,OpenMode"