-
-
Notifications
You must be signed in to change notification settings - Fork 188
poc: Try to extract an embedded .msi file #237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,9 @@ | |
| // Scott Willeke (scott@willeke.com) | ||
| // | ||
| using Microsoft.Tools.WindowsInstallerXml.Msi; | ||
| using System; | ||
| using System.IO; | ||
| using System.IO.MemoryMappedFiles; | ||
|
|
||
| namespace LessMsi.Msi | ||
| { | ||
|
|
@@ -53,5 +56,81 @@ public static Database Create(LessIO.Path msiDatabaseFilePath) | |
| return new Database(msiDatabaseFilePath.PathString, OpenDatabase.ReadOnly | (OpenDatabase)MSIDBOPEN_PATCHFILE); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| // 'Magic bytes' that identify the beginning of an 'StgStorage' file, which is the format used by MSI files. | ||
| static readonly Byte[] STG_STORAGE_magic_bytes = new byte[]{ 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }; | ||
|
|
||
| public static bool TryDetectMsiHeader(LessIO.Path filePath, out long offset) | ||
| { | ||
| using (var mapped = MemoryMappedFile.CreateFromFile(filePath.PathString, System.IO.FileMode.Open, null, 0L, MemoryMappedFileAccess.Read)) | ||
| { | ||
| using (var accessor = mapped.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read)) | ||
| { | ||
| byte[] buffer = new byte[4096]; | ||
| long readOffset = 0; | ||
| while (true) | ||
| { | ||
| var totalRemaining = accessor.Capacity - readOffset; | ||
| if (totalRemaining <= 0) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe it's worth to extract
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The second condition is not strictly needed, if you prefer no repetition I'll just remove it. |
||
| break; | ||
| int remaining = accessor.ReadArray(readOffset, buffer, 0, (int)Math.Min(totalRemaining, buffer.Length)); | ||
|
|
||
| bool moveLess = false; | ||
| for (int n = 0; n < remaining; n++) | ||
| { | ||
| if (buffer[n] == STG_STORAGE_magic_bytes[0]) | ||
| { | ||
| if (buffer.Length - n >= STG_STORAGE_magic_bytes.Length) | ||
| { | ||
| bool match = true; | ||
| for (int m = 1; m < STG_STORAGE_magic_bytes.Length; m++) | ||
| { | ||
| if (buffer[n + m] != STG_STORAGE_magic_bytes[m]) | ||
| { | ||
| match = false; | ||
| break; | ||
| } | ||
| } | ||
| if (match) | ||
| { | ||
| offset = readOffset + n; | ||
| return true; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| moveLess = true; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (moveLess && remaining > STG_STORAGE_magic_bytes.Length && readOffset > STG_STORAGE_magic_bytes.Length) | ||
| { | ||
| readOffset -= STG_STORAGE_magic_bytes.Length; | ||
| } | ||
| readOffset += remaining; | ||
| } | ||
|
|
||
| offset = -1; | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public static void ExtractMsiFromExe(LessIO.Path filePath, LessIO.Path outputFile, long offset) | ||
| { | ||
| using (var mapped = MemoryMappedFile.CreateFromFile(filePath.PathString, FileMode.Open, null, 0L, MemoryMappedFileAccess.Read)) | ||
| { | ||
| using (var reader = mapped.CreateViewStream(offset, 0, MemoryMappedFileAccess.Read)) | ||
| { | ||
| using (var output = File.Create(outputFile.PathString)) | ||
| { | ||
| reader.CopyTo(output); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -78,6 +78,11 @@ internal interface IMainFormView | |
| /// </summary> | ||
| void ShowUserError(string formatStr, params object[] args); | ||
| /// <summary> | ||
| /// Displays a message to the user with a Yes/No question and returns the user's response. | ||
| /// </summary> | ||
| /// <param name="message">The message to display to the user.</param> | ||
| bool ShowUserMessageQuestionYesNo(string message); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this method necessary?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because we drop a file on disk for the user (even if it is just in the temp file), |
||
| /// <summary> | ||
| /// Adds a column to the MSI table grid. | ||
| /// </summary> | ||
| void AddTableViewGridColumn(string headerText); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,19 +22,20 @@ | |
| // Authors: | ||
| // Scott Willeke (scott@willeke.com) | ||
| // | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.ComponentModel; | ||
| using System.Diagnostics; | ||
| using System.IO; | ||
| using System.Linq; | ||
| using LessIO; | ||
| using LessMsi.Gui.Model; | ||
| using LessMsi.Gui.Resources.Languages; | ||
| using LessMsi.Gui.Windows.Forms; | ||
| using LessMsi.Msi; | ||
| using LessMsi.OleStorage; | ||
| using Microsoft.Tools.WindowsInstallerXml.Msi; | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.ComponentModel; | ||
| using System.Diagnostics; | ||
| using System.IO; | ||
| using System.IO.MemoryMappedFiles; | ||
| using System.Linq; | ||
|
|
||
| namespace LessMsi.Gui | ||
| { | ||
|
|
@@ -545,6 +546,16 @@ public void LoadCurrentFile() | |
| } | ||
| catch (Exception eCatchAll) | ||
| { | ||
| if (eCatchAll is IOException && this.SelectedMsiFile.Extension.ToLower() == ".exe" && MsiDatabase.TryDetectMsiHeader(new LessIO.Path(this.SelectedMsiFile.FullName), out long offset)) | ||
| { | ||
| if (View.ShowUserMessageQuestionYesNo("It looks like this file contains a .msi file, do you want to extract it?")) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you really think we need to ask? I would assume if the dropped an exe they want us to do it, so why not just do it and assume that's what they want?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It drops a file on the users pc, and this file will be removed again later on. |
||
| { | ||
| var tempFile = System.IO.Path.GetTempFileName(); | ||
| MsiDatabase.ExtractMsiFromExe(new LessIO.Path(this.SelectedMsiFile.FullName), new LessIO.Path(tempFile), offset); | ||
| LoadFile(tempFile); | ||
| return; | ||
| } | ||
| } | ||
| isBadFile = true; | ||
| Error(Strings.OpenFileError, eCatchAll); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need the 4K buffer here. Since you're already in a MemoryMappedFile (good call!!), you can just use the view to scan through for the magic bits.
Minor: I might just return an offset of -1 instead of using an out param, but that's just a preference. up to you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean using the ReadByte function of the MemoryMappedViewAccessor,
or the ReadByte from the MemoryMappedViewStream ?
I am not too familiar with MMF in c#, and I didn't find another way to use it.
(In c you'd directly read from the raw memory pointer)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MMF is already memory (like C) so the extra buffer is probably redundant. It’s a nitpick. We can investigate later.