public void Test_COMPRESS_SPACE() { String FILENAME = "MultipleStorage3.cfs"; // 22Kb FileInfo srcFile = new FileInfo(FILENAME); File.Copy(FILENAME, "MultipleStorage_Deleted_Compress.cfs", true); CompoundFile cf = new CompoundFile("MultipleStorage_Deleted_Compress.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors); CFStorage st = cf.RootStorage.GetStorage("MyStorage"); st = st.GetStorage("AnotherStorage"); Assert.IsNotNull(st); st.Delete("Another2Stream"); cf.Commit(); cf.Close(); CompoundFile.ShrinkCompoundFile("MultipleStorage_Deleted_Compress.cfs"); // -> 7Kb FileInfo dstFile = new FileInfo("MultipleStorage_Deleted_Compress.cfs"); Assert.IsTrue(srcFile.Length > dstFile.Length); }
public static VBAInfo FromCompoundFile(CompoundFile cf, List <string> moduleNames) { VBAInfo info = new VBAInfo(); try { CFStorage projectStorage = cf.RootStorage.GetStorage("_VBA_PROJECT_CUR"); CFStorage vbaStorage = projectStorage.GetStorage("VBA"); info.ProjectWmStream = projectStorage.GetStream("PROJECTwm"); info.ProjectStream = projectStorage.GetStream("PROJECT"); info.ThisWorkbookStream = vbaStorage.GetStream("ThisWorkbook"); info.VbaProjectStream = vbaStorage.GetStream("_VBA_PROJECT"); info.dirStream = vbaStorage.GetStream("dir"); info.ModuleStreams = new List <CFStream>(); foreach (var moduleName in moduleNames) { try { info.ModuleStreams.Add(vbaStorage.GetStream(moduleName)); } catch (CFItemNotFound) { } } } catch (CFItemNotFound) { // If we don't have any VBA directory then just return null return(null); } return(info); }
public void LoadRpmsgDataFromEmbeddedAttachment(CFStorage msgStorage) { LogUtils.Log(""); try { CFStorage firstAttachmentStorage = msgStorage.GetStorage(FIRST_MSG_ATTACHMENT_STORAGE_NAME); string fileName = Encoding.Unicode.GetString(firstAttachmentStorage.GetStream(PidTagAttachLongFilename).GetData()); if (fileName.Equals(RPMSG_ATTACHMENT_NAME)) { Content = firstAttachmentStorage.GetStream(PidTagAttachDataBinary).GetData(); Extension = ".rpmsg"; //embedded messages do not have an extension in the attachment props Name += ".msg"; MimeType = Encoding.Unicode.GetString(firstAttachmentStorage.GetStream(MIME_TYPE_STREAM_NAME).GetData()); return; } throw new CFItemNotFound(""); } catch (CFItemNotFound) { LogUtils.Log("Embedded message is not protected"); Content = new byte[0]; } }
/// <summary> /// Получить ссылку на поток из файла MCDF по его пути /// </summary> /// <param name="file">Файл - хранилище</param> /// <param name="streamPath">Путь к потоку в файле</param> /// <returns></returns> public static CFStream GetFileStream(CompoundFile file, string streamPath) { string[] path = streamPath.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); CFStorage curDir = file.RootStorage; for (int i = 0; i < path.Length - 1; i++) { curDir = curDir.GetStorage(path[i]); } return(curDir.GetStream(path[path.Length - 1])); }
/// <summary> /// Writes the properties to the <see cref="CFStorage" /> /// </summary> /// <param name="storage"></param> /// <param name="messageSize"></param> /// <remarks> /// Unfortunately this is going to have to be used after we already written the top level properties. /// </remarks> internal void WriteProperties(CFStorage storage, long messageSize = 0) { // Grab the nameIdStorage, 3.1 on the SPEC storage = storage.GetStorage(PropertyTags.NameIdStorage); var entryStream = new EntryStream(storage); var stringStream = new StringStream(storage); var guidStream = new GuidStream(storage); var nameIdMappingStream = new EntryStream(storage); ushort propertyIndex = 0; foreach (var guid in Guids.Where(g => g != PropertySets.PS_MAPI && g != PropertySets.PS_PUBLIC_STRINGS)) { guidStream.Add(guid); } foreach (var namedProperty in this) { var guidIndex = GetGuidIndex(namedProperty); var indexAndKind = new IndexAndKindInformation(propertyIndex, guidIndex, namedProperty.Kind); if (namedProperty.Kind == PropertyKind.Name) { var stringStreamItem = new StringStreamItem(namedProperty.Name); stringStream.Add(stringStreamItem); entryStream.Add(new EntryStreamItem(stringStream.GetItemByteOffset(stringStreamItem), indexAndKind)); } else { entryStream.Add(new EntryStreamItem(namedProperty.NameIdentifier, indexAndKind)); } nameIdMappingStream.Add(new EntryStreamItem(GenerateNameIdentifier(namedProperty), indexAndKind)); nameIdMappingStream.Write(storage, GenerateStreamName(namedProperty)); // Dependign on the property type. This is doing name. //entryStream.Add(new EntryStreamItem(namedProperty.NameIdentifier, new IndexAndKindInformation(propertyIndex, guidIndex, PropertyKind.Lid))); //+3 as per spec. //entryStream2.Add(new EntryStreamItem(namedProperty.NameIdentifier, new IndexAndKindInformation(propertyIndex, guidIndex, PropertyKind.Lid))); // 3.2.2 of the SPEC Needs to be written, because the stream changes as per named object. nameIdMappingStream.Clear(); propertyIndex++; } guidStream.Write(storage); entryStream.Write(storage); stringStream.Write(storage); }
private CFStorage GetStorage(CFStorage cfstorage, string storagename) { CFStorage storage = null; try { storage = cfstorage.GetStorage(storagename); } catch (Exception) { } return(storage); }
public static CFStorage GetStorage(List <string> pathList, ref CFStorage storage) { if (pathList == null || pathList[0] == "") { return(storage); } if (pathList.Count <string>() > 1) { int index = pathList.Count <string>() - 1; string storageName = pathList[index]; pathList.RemoveAt(index); return(CFUtil.GetStorage(pathList, ref storage).GetStorage(storageName)); } return(storage.GetStorage(pathList[0])); }
static void CopyCfStreamsExcept(CFStorage src, CFStorage dest, string excludeName) { src.VisitEntries(i => { if (i.Name?.Equals(excludeName, StringComparison.InvariantCultureIgnoreCase) ?? false) { return; } if (i.IsStorage) { dest.AddStorage(i.Name); CopyCfStreamsExcept((CFStorage)i, dest.GetStorage(i.Name), null); } else { dest.AddStream(i.Name); dest.GetStream(i.Name).SetData(((CFStream)i).GetData()); } }, false); }
static string GetPrepend(Module mod, CFStorage vbaProject, Encoding enc) { switch (mod.Type) { case ModuleType.Class: case ModuleType.StaticClass: return("VERSION 1.0 CLASS\r\nBEGIN\r\n MultiUse = -1 'True\r\nEND\r\n"); case ModuleType.Form: var vbFrameLines = enc.GetString(vbaProject.GetStorage(mod.StreamName).GetStream("\x0003VBFrame").GetData()) .Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries).ToList(); vbFrameLines.Insert(2, $" OleObjectBlob = \"{mod.Name}.frx\":0000"); return(string.Join("\r\n", vbFrameLines) + "\r\n"); case ModuleType.Standard: return(""); default: throw new ApplicationException("Unrecognized module type"); } }
/// <summary> /// Writes the properties to the <see cref="CFStorage" /> /// </summary> /// <param name="storage"></param> /// <param name="messageSize"></param> /// <remarks> /// Unfortunately this is going to have to be used after we already written the top level properties. /// </remarks> internal void WriteProperties(CFStorage storage, long messageSize = 0) { // Grab the nameIdStorage, 3.1 on the SPEC storage = storage.GetStorage(PropertyTags.NameIdStorage); var entryStream = new EntryStream(storage); var stringStream = new StringStream(storage); var guidStream = new GuidStream(storage); var entryStream2 = new EntryStream(storage); ushort propertyIndex = 0; var guids = this.Select(x => x.Guid).Distinct().ToList(); foreach (var guid in guids) { guidStream.Add(guid); } foreach (var namedProperty in this) { var guidIndex = (ushort)(guids.IndexOf(namedProperty.Guid) + 3); // Dependign on the property type. This is doing name. entryStream.Add(new EntryStreamItem(namedProperty.NameIdentifier, new IndexAndKindInformation(propertyIndex, guidIndex, PropertyKind.Lid))); //+3 as per spec. entryStream2.Add(new EntryStreamItem(namedProperty.NameIdentifier, new IndexAndKindInformation(propertyIndex, guidIndex, PropertyKind.Lid))); //3.2.2 of the SPEC [MS-OXMSG] entryStream2.Write(storage, GenerateStreamString(namedProperty.NameIdentifier, guidIndex, namedProperty.Kind)); // 3.2.2 of the SPEC Needs to be written, because the stream changes as per named object. entryStream2.Clear(); propertyIndex++; } guidStream.Write(storage); entryStream.Write(storage); stringStream.Write(storage); }
/// <summary> /// Reads model information from the current file, and assigns it to their respective /// component bodies. /// Model information includes the model positioning parameters and its STEP data. /// </summary> /// <param name="library">Storage where to look for the models data.</param> private void ReadLibraryModels(CFStorage library) { BeginContext("Models"); var models = library.GetStorage("Models"); var recordCount = ReadHeader(models); using (var reader = models.GetStream("Data").GetBinaryReader()) { for (var i = 0; i < recordCount; ++i) { var parameters = ReadBlock(reader, size => ReadParameters(reader, size)); var modelId = parameters["ID"].AsString(); var modelCompressedData = models.GetStream($"{i}").GetData(); // models are stored as ASCII STEP files but using zlib compression var stepModel = ParseCompressedZlibData(modelCompressedData, stream => { using (var modelReader = new StreamReader(stream, Encoding.ASCII)) { return(modelReader.ReadToEnd()); } }); // assign STEP data to component bodies var bodies = Data.Items.SelectMany(c => c.GetPrimitivesOfType <PcbComponentBody>(false)) .Where(body => body.ModelId.ToUpperInvariant() == modelId.ToUpperInvariant()); foreach (var body in bodies) { body.StepModel = stepModel; } } } EndContext(); }
public void Test_DELETE_WITHOUT_COMPRESSION() { String FILENAME = "MultipleStorage3.cfs"; FileInfo srcFile = new FileInfo(FILENAME); CompoundFile cf = new CompoundFile(FILENAME); CFStorage st = cf.RootStorage.GetStorage("MyStorage"); st = st.GetStorage("AnotherStorage"); Assert.IsNotNull(st); st.Delete("Another2Stream"); //17Kb //cf.CompressFreeSpace(); cf.Save("MultipleStorage_Deleted_Compress.cfs"); cf.Close(); FileInfo dstFile = new FileInfo("MultipleStorage_Deleted_Compress.cfs"); Assert.IsFalse(srcFile.Length > dstFile.Length); }
public void LoadAttachmentProps(CFStorage attachmentStorage) { byte[] descBytes; try { descBytes = attachmentStorage.GetStream("AttachDesc").GetData(); } catch (CFItemNotFound ex) { throw new NotSupportedException("Attachment description stream not found", ex); } if (descBytes.Length == 0) { throw new ArgumentException("no description bytes to read"); } int nextIndex = 2; //first two bytes are the version StringUtils.ReadStringA(descBytes, ref nextIndex); StringUtils.ReadStringA(descBytes, ref nextIndex); string displayName = StringUtils.ReadStringA(descBytes, ref nextIndex); StringUtils.ReadStringA(descBytes, ref nextIndex); StringUtils.ReadStringA(descBytes, ref nextIndex); string extension = StringUtils.ReadStringA(descBytes, ref nextIndex); nextIndex += 16; AttachmentMethod = (AttachType)BitConverter.ToUInt32(descBytes, nextIndex); nextIndex += 4; ContentID = StringUtils.ReadStringW(descBytes, ref nextIndex); StringUtils.ReadStringW(descBytes, ref nextIndex); StringUtils.ReadStringW(descBytes, ref nextIndex); StringUtils.ReadStringW(descBytes, ref nextIndex); string displayNameW = StringUtils.ReadStringW(descBytes, ref nextIndex); StringUtils.ReadStringW(descBytes, ref nextIndex); StringUtils.ReadStringW(descBytes, ref nextIndex); string extensionW = StringUtils.ReadStringW(descBytes, ref nextIndex); //there's other stuff here, but I'm not going to read it Name = displayName ?? displayNameW; Extension = extension ?? extensionW; #if __ANDROID__ MimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(Extension == null ? null : Extension.Trim('.')); #endif switch (AttachmentMethod) { case AttachType.afByBalue: Content = attachmentStorage.GetStream("AttachContents").GetData(); return; case AttachType.afEmbeddedMessage: LoadRpmsgDataFromEmbeddedAttachment(attachmentStorage.GetStorage("MAPIMessage")); return; default: LogUtils.Error("unsupported attachment method: " + AttachmentMethod); Content = new byte[0]; return; } }
private static bool CfStoragesAreDifferent(CFStorage s1, CFStorage s2, out string explain) { var s1Names = new List <Tuple <string, bool> >(); var s2Names = new List <Tuple <string, bool> >(); s1.VisitEntries(i => s1Names.Add(Tuple.Create(i.Name, i.IsStorage)), false); s2.VisitEntries(i => s2Names.Add(Tuple.Create(i.Name, i.IsStorage)), false); s1Names.Sort(); s2Names.Sort(); if (!s1Names.SequenceEqual(s2Names)) { explain = string.Format(VBASyncResources.ExplainFrxDifferentFileLists, s1.Name, string.Join("', '", s1Names.Select(t => t.Item1)), string.Join("', '", s2Names.Select(t => t.Item1))); return(true); } FormControl fc1 = null; FormControl fc2 = null; foreach (var t in s1Names) { if (t.Item2) { if (CfStoragesAreDifferent(s1.GetStorage(t.Item1), s2.GetStorage(t.Item1), out explain)) { return(true); } } else if (t.Item1 == "f") { fc1 = new FormControl(s1.GetStream("f").GetData()); fc2 = new FormControl(s2.GetStream("f").GetData()); if (!fc1.Equals(fc2)) { explain = string.Format(VBASyncResources.ExplainFrxGeneralStreamDifference, "f", s1.Name); return(true); } } else if (t.Item1 == "o" && fc1 != null && fc2 != null) { var fc2SitesList = fc2.Sites.ToList(); var o1Controls = DecomposeOStream(fc1.Sites, s1.GetStream("o").GetData()); var o2Controls = DecomposeOStream(fc2.Sites, s2.GetStream("o").GetData()); for (var siteIdx1 = 0; siteIdx1 < fc1.Sites.Length; ++siteIdx1) { var siteIdx2 = fc2SitesList.FindIndex(s => s.Id == fc1.Sites[siteIdx1].Id); if (!Equals(o1Controls[siteIdx1], o2Controls[siteIdx2])) { explain = string.Format(VBASyncResources.ExplainFrxOStreamDifference, fc1.Sites[siteIdx1].Name, s1.Name); return(true); } } } else if (!s1.GetStream(t.Item1).GetData().SequenceEqual(s2.GetStream(t.Item1).GetData())) { explain = string.Format(VBASyncResources.ExplainFrxGeneralStreamDifference, t.Item1, s1.Name); return(true); } } explain = "No differences found."; return(false); }
static void Main(string[] args) { try { if (args.Length == 0 || args.Contains("-h")) { PrintHelp(); return; } Dictionary <string, string> argDict = Utils.ParseArgs(args); if (argDict.ContainsKey("f")) { filename = argDict["f"]; } else { Console.WriteLine("\n[!] Missing file (-f)\n"); return; } if (args.Contains("-l")) { list_modules = true; } else { if (argDict.ContainsKey("m")) { module = argDict["m"]; } else { Console.WriteLine("\n[.] Will automatically decide which modules to purge."); } } bool is_OpenXML = false; // Temp path to unzip OpenXML files to String unzipTempPath = ""; string outFilename = Utils.getOutFilename(filename); string oleFilename = outFilename; // VBA Purging try { // Make a copy of document to VBA Purge if user is not listing modules if (!list_modules) { if (File.Exists(outFilename)) { File.Delete(outFilename); } File.Copy(filename, outFilename); filename = outFilename; } try { unzipTempPath = CreateUniqueTempDirectory(); ZipFile.ExtractToDirectory(filename, unzipTempPath); if (File.Exists(Path.Combine(unzipTempPath, "word", "vbaProject.bin"))) { oleFilename = Path.Combine(unzipTempPath, "word", "vbaProject.bin"); } else if (File.Exists(Path.Combine(unzipTempPath, "xl", "vbaProject.bin"))) { oleFilename = Path.Combine(unzipTempPath, "xl", "vbaProject.bin"); } is_OpenXML = true; } catch (Exception) { // Not OpenXML format, Maybe 97-2003 format, Make a copy if (File.Exists(outFilename)) { File.Delete(outFilename); } File.Copy(filename, outFilename); } CompoundFile cf = new CompoundFile(oleFilename, CFSUpdateMode.Update, 0); CFStorage commonStorage = cf.RootStorage; if (cf.RootStorage.TryGetStorage("Macros") != null) { commonStorage = cf.RootStorage.GetStorage("Macros"); } if (cf.RootStorage.TryGetStorage("_VBA_PROJECT_CUR") != null) { commonStorage = cf.RootStorage.GetStorage("_VBA_PROJECT_CUR"); } var vbaStorage = commonStorage.GetStorage("VBA"); if (vbaStorage == null) { throw new CFItemNotFound("Cannot find item"); } // Grab data from "dir" module stream. Used to retrieve list of module streams in document. byte[] dirStream = Utils.Decompress(vbaStorage.GetStream("dir").GetData()); List <Utils.ModuleInformation> vbaModules = Utils.ParseModulesFromDirStream(dirStream); // Only list module streams in document and return if (list_modules) { foreach (var vbaModule in vbaModules) { Console.WriteLine("[*] VBA module name: " + vbaModule.moduleName); } Console.WriteLine("[*] Finished listing modules\n"); return; } string [] dontPurgeTheseModules = { "ThisDocument", "ThisWorkbook", "Sheet", }; byte[] streamBytes; bool module_found = false; foreach (var vbaModule in vbaModules) { //VBA Purging begins bool purge = true; if (module.Length > 0) { purge = vbaModule.moduleName == module; } else { foreach (string mod in dontPurgeTheseModules) { if (vbaModule.moduleName.StartsWith(mod)) { purge = false; } } } if (purge) { Console.WriteLine("\n[*] Purging VBA code in module: " + vbaModule.moduleName); Console.WriteLine("[*] Offset for code: " + vbaModule.textOffset); // Get the CompressedSourceCode from module streamBytes = vbaStorage.GetStream(vbaModule.moduleName).GetData(); string OG_VBACode = Utils.GetVBATextFromModuleStream(streamBytes, vbaModule.textOffset); // Remove P-code from module stream and set the module to only have the CompressedSourceCode streamBytes = Utils.RemovePcodeInModuleStream(streamBytes, vbaModule.textOffset, OG_VBACode); vbaStorage.GetStream(vbaModule.moduleName).SetData(streamBytes); module_found = true; } } if (module_found == false) { Console.WriteLine("\n[!] Could not find module in document (-m). List all module streams with (-l).\n"); if (!is_OpenXML) { cf.Commit(); cf.Close(); CompoundFile.ShrinkCompoundFile(oleFilename); File.Delete(oleFilename); if (File.Exists(outFilename)) { File.Delete(outFilename); } } return; } // Change offset to 0 so that document can find compressed source code. vbaStorage.GetStream("dir").SetData(Utils.Compress(Utils.ChangeOffset(dirStream))); Console.WriteLine("\n[*] Module offset changed to 0."); // Remove performance cache in _VBA_PROJECT stream. Replace the entire stream with _VBA_PROJECT header. string b1 = "00"; string b2 = "00"; Random rnd = new Random(); b1 = String.Format("{0:X2}", rnd.Next(0, 255)); b2 = String.Format("{0:X2}", rnd.Next(0, 255)); byte[] data = Utils.HexToByte(String.Format("CC-61-FF-FF-00-{0}-{1}", b1, b2)); vbaStorage.GetStream("_VBA_PROJECT").SetData(data); Console.WriteLine("[*] PerformanceCache removed from _VBA_PROJECT stream."); // Check if document contains SRPs. Must be removed for VBA Purging to work. try { for (int i = 0; i < 10; i++) { string srp = String.Format("__SRP_{0}", i); var str = vbaStorage.TryGetStream(srp); if (str != null) { vbaStorage.Delete(srp); } } Console.WriteLine("[*] SRP streams deleted!"); } catch (Exception e) { Console.WriteLine("[*] No SRP streams found."); } // Commit changes and close cf.Commit(); cf.Close(); CompoundFile.ShrinkCompoundFile(oleFilename); // Zip the file back up as a docm or xlsm if (is_OpenXML) { if (File.Exists(outFilename)) { File.Delete(outFilename); } ZipFile.CreateFromDirectory(unzipTempPath, outFilename); } Console.WriteLine("[+] VBA Purging completed successfully!\n"); } // Error handle for file not found catch (FileNotFoundException ex) when(ex.Message.Contains("Could not find file")) { Console.WriteLine("[!] Could not find path or file (-f). \n"); } // Error handle when document specified and file chosen don't match catch (CFItemNotFound ex) when(ex.Message.Contains("Cannot find item")) { Console.WriteLine("[!] File (-f) does not contain macros.\n"); } // Error handle when document is not OLE/CFBF format catch (CFFileFormatException) { Console.WriteLine("[!] Incorrect filetype (-f). OfficePurge supports documents in .docm or .xlsm format as well as .doc/.xls/.pub in the Office 97-2003 format.\n"); } finally { if (is_OpenXML) { Directory.Delete(unzipTempPath, true); } } } // Error handle for incorrect use of flags catch (IndexOutOfRangeException) { Console.WriteLine("\n[!] Flags (-d), (-f), (-m) need an argument. Make sure you have provided these flags an argument.\n"); } }
static bool CfStoragesAreDifferent(CFStorage s1, CFStorage s2, out string explain) { var s1Names = new List <Tuple <string, bool> >(); var s2Names = new List <Tuple <string, bool> >(); s1.VisitEntries(i => s1Names.Add(Tuple.Create(i.Name, i.IsStorage)), false); s2.VisitEntries(i => s2Names.Add(Tuple.Create(i.Name, i.IsStorage)), false); s1Names.Sort(); s2Names.Sort(); if (!s1Names.SequenceEqual(s2Names)) { explain = $"Different file lists in storage '{s1.Name}'.\r\nFile 1: {{'{string.Join("', '", s1Names)}'}}\r\nFile 2: {{'{string.Join("'', '", s2Names)}'}}."; return(true); } FormControl fc1 = null; foreach (var t in s1Names) { if (t.Item2) { if (CfStoragesAreDifferent(s1.GetStorage(t.Item1), s2.GetStorage(t.Item1), out explain)) { return(true); } } else if (t.Item1 == "f") { fc1 = new FormControl(s1.GetStream("f").GetData()); var fc2 = new FormControl(s2.GetStream("f").GetData()); if (!fc1.Equals(fc2)) { explain = $"Different contents of stream 'f' in storage '{s1.Name}'."; return(true); } } else if (t.Item1 == "o" && fc1 != null) { var o1 = s1.GetStream("o").GetData(); var o2 = s2.GetStream("o").GetData(); uint idx = 0; foreach (var site in fc1.Sites) { explain = $"Different contents of stream 'o', site '{site.Name}' in storage '{s1.Name}'."; var o1Range = o1.Range(idx, site.ObjectStreamSize); var o2Range = o2.Range(idx, site.ObjectStreamSize); switch (site.ClsidCacheIndex) { case 15: // MorphData case 26: // CheckBox case 25: // ComboBox case 24: // ListBox case 27: // OptionButton case 23: // TextBox case 28: // ToggleButton if (!new MorphDataControl(o1Range).Equals(new MorphDataControl(o2Range))) { return(true); } break; case 17: // CommandButton if (!new CommandButtonControl(o1Range).Equals(new CommandButtonControl(o2Range))) { return(true); } break; case 21: // Label if (!new LabelControl(o1Range).Equals(new LabelControl(o2Range))) { return(true); } break; default: if (!o1Range.SequenceEqual(o2Range)) { return(true); } break; } idx += site.ObjectStreamSize; } } else if (!s1.GetStream(t.Item1).GetData().SequenceEqual(s2.GetStream(t.Item1).GetData())) { explain = $"Different contents of stream '{t.Item1}' in storage '{s1.Name}'."; return(true); } } explain = "No differences found."; return(false); }
public MSOfficeManipulator(string filename, string[] names) { targetModules = new List <string>(); foreach (string s in names) { targetModules.Add(s); } // OLE Filename (make a copy so we don't overwrite the original) outFilename = getOutFilename(filename); oleFilename = outFilename; // Attempt to unzip as docm or xlsm OpenXML format try { unzipTempPath = CreateUniqueTempDirectory(); ZipFile.ExtractToDirectory(filename, unzipTempPath); if (File.Exists(Path.Combine(unzipTempPath, "word", "vbaProject.bin"))) { oleFilename = Path.Combine(unzipTempPath, "word", "vbaProject.bin"); } else if (File.Exists(Path.Combine(unzipTempPath, "xl", "vbaProject.bin"))) { oleFilename = Path.Combine(unzipTempPath, "xl", "vbaProject.bin"); } is_OpenXML = true; } catch (Exception) { // Not OpenXML format, Maybe 97-2003 format, Make a copy if (File.Exists(outFilename)) { File.Delete(outFilename); } File.Copy(filename, outFilename); } // Open OLE compound file for editing try { cf = new CompoundFile(oleFilename, CFSUpdateMode.Update, 0); } catch (Exception e) { Console.WriteLine("ERROR: Could not open file " + filename); Console.WriteLine("Please make sure this file exists and is .docm or .xlsm file or a .doc in the Office 97-2003 format."); Console.WriteLine(); Console.WriteLine(e.Message); return; } // Read relevant streams commonStorage = cf.RootStorage; // docm or xlsm if (cf.RootStorage.TryGetStorage("Macros") != null) { commonStorage = cf.RootStorage.GetStorage("Macros"); // .doc } if (cf.RootStorage.TryGetStorage("_VBA_PROJECT_CUR") != null) { commonStorage = cf.RootStorage.GetStorage("_VBA_PROJECT_CUR"); // xls } vbaProjectStream = commonStorage.GetStorage("VBA").GetStream("_VBA_PROJECT").GetData(); projectStream = commonStorage.GetStream("project").GetData(); projectwmStream = commonStorage.GetStream("projectwm").GetData(); dirStream = Decompress(commonStorage.GetStorage("VBA").GetStream("dir").GetData()); // Read project streams as string projectStreamString = Encoding.UTF8.GetString(projectStream); projectwmStreamString = Encoding.UTF8.GetString(projectwmStream); // Find all VBA modules in current file vbaModules = ParseModulesFromDirStream(dirStream); // Write streams to debug log (if verbosity enabled) //Console.WriteLine("Hex dump of original _VBA_PROJECT stream:\n" + Utils.HexDump(vbaProjectStream)); //Console.WriteLine("Hex dump of original dir stream:\n" + Utils.HexDump(dirStream)); //Console.WriteLine("Hex dump of original project stream:\n" + Utils.HexDump(projectStream)); }
static public void Main(string[] args) { // List of target VBA modules to stomp, if empty => all modules will be stomped List <string> targetModules = new List <string>(); // Filename that contains the VBA code used for substitution string VBASourceFileName = ""; // Target MS Office version for pcode string targetOfficeVersion = ""; // Option to hide modules from VBA editor GUI bool optionHideInGUI = false; // Option to start web server to serve malicious template int optionWebserverPort = 0; // Option to display help bool optionShowHelp = false; // File format is OpenXML (docm or xlsm) bool is_OpenXML = false; // Option to delete metadata from file bool optionDeleteMetadata = false; // Option to set random module names in dir stream bool optionSetRandomNames = false; // Temp path to unzip OpenXML files to String unzipTempPath = ""; // Start parsing command line arguments var p = new OptionSet() { { "n|name=", "The target module name to stomp.\n" + "This argument can be repeated.", v => targetModules.Add(v) }, { "s|sourcefile=", "File containing substitution VBA code (fake code).", v => VBASourceFileName = v }, { "g|guihide", "Hide code from VBA editor GUI.", v => optionHideInGUI = v != null }, { "t|targetversion=", "Target MS Office version the pcode will run on.", v => targetOfficeVersion = v }, { "w|webserver=", "Start web server on specified port to serve malicious template.", (int v) => optionWebserverPort = v }, { "d|delmetadata", "Remove metadata stream (may include your name etc.).", v => optionDeleteMetadata = v != null }, { "r|randomnames", "Set random module names, confuses some analyst tools.", v => optionSetRandomNames = v != null }, { "v", "Increase debug message verbosity.", v => { if (v != null) { ++verbosity; } } }, { "h|help", "Show this message and exit.", v => optionShowHelp = v != null }, }; List <string> extra; try { extra = p.Parse(args); } catch (OptionException e) { Console.WriteLine(e.Message); Console.WriteLine("Try '--help' for more information."); return; } if (extra.Count > 0) { filename = string.Join(" ", extra.ToArray()); } else { optionShowHelp = true; } if (optionShowHelp) { ShowHelp(p); return; } // End parsing command line arguments // OLE Filename (make a copy so we don't overwrite the original) string outFilename = getOutFilename(filename); string oleFilename = outFilename; // Attempt to unzip as docm or xlsm OpenXML format try { unzipTempPath = CreateUniqueTempDirectory(); ZipFile.ExtractToDirectory(filename, unzipTempPath); if (File.Exists(Path.Combine(unzipTempPath, "word", "vbaProject.bin"))) { oleFilename = Path.Combine(unzipTempPath, "word", "vbaProject.bin"); } else if (File.Exists(Path.Combine(unzipTempPath, "xl", "vbaProject.bin"))) { oleFilename = Path.Combine(unzipTempPath, "xl", "vbaProject.bin"); } is_OpenXML = true; } catch (Exception) { // Not OpenXML format, Maybe 97-2003 format, Make a copy if (File.Exists(outFilename)) { File.Delete(outFilename); } File.Copy(filename, outFilename); } // Open OLE compound file for editing try { cf = new CompoundFile(oleFilename, CFSUpdateMode.Update, 0); } catch (Exception e) { Console.WriteLine("ERROR: Could not open file " + filename); Console.WriteLine("Please make sure this file exists and is .docm or .xlsm file or a .doc in the Office 97-2003 format."); Console.WriteLine(); Console.WriteLine(e.Message); return; } // Read relevant streams CFStorage commonStorage = cf.RootStorage; // docm or xlsm if (cf.RootStorage.TryGetStorage("Macros") != null) { commonStorage = cf.RootStorage.GetStorage("Macros"); // .doc } if (cf.RootStorage.TryGetStorage("_VBA_PROJECT_CUR") != null) { commonStorage = cf.RootStorage.GetStorage("_VBA_PROJECT_CUR"); // xls } vbaProjectStream = commonStorage.GetStorage("VBA").GetStream("_VBA_PROJECT").GetData(); projectStream = commonStorage.GetStream("project").GetData(); dirStream = Decompress(commonStorage.GetStorage("VBA").GetStream("dir").GetData()); // Read project stream as string string projectStreamString = System.Text.Encoding.UTF8.GetString(projectStream); // Find all VBA modules in current file List <ModuleInformation> vbaModules = ParseModulesFromDirStream(dirStream); // Write streams to debug log (if verbosity enabled) DebugLog("Hex dump of original _VBA_PROJECT stream:\n" + Utils.HexDump(vbaProjectStream)); DebugLog("Hex dump of original dir stream:\n" + Utils.HexDump(dirStream)); DebugLog("Hex dump of original project stream:\n" + Utils.HexDump(projectStream)); // Replace Office version in _VBA_PROJECT stream if (targetOfficeVersion != "") { ReplaceOfficeVersionInVBAProject(vbaProjectStream, targetOfficeVersion); commonStorage.GetStorage("VBA").GetStream("_VBA_PROJECT").SetData(vbaProjectStream); } // Hide modules from GUI if (optionHideInGUI) { foreach (var vbaModule in vbaModules) { if ((vbaModule.moduleName != "ThisDocument") && (vbaModule.moduleName != "ThisWorkbook")) { Console.WriteLine("Hiding module: " + vbaModule.moduleName); projectStreamString = projectStreamString.Replace("Module=" + vbaModule.moduleName, ""); } } // Write changes to project stream commonStorage.GetStream("project").SetData(Encoding.UTF8.GetBytes(projectStreamString)); } // Stomp VBA modules if (VBASourceFileName != "") { byte[] streamBytes; foreach (var vbaModule in vbaModules) { DebugLog("VBA module name: " + vbaModule.moduleName + "\nOffset for code: " + vbaModule.textOffset); // If this module is a target module, or if no targets are specified, then stomp if (targetModules.Contains(vbaModule.moduleName) || !targetModules.Any()) { Console.WriteLine("Now stomping VBA code in module: " + vbaModule.moduleName); streamBytes = commonStorage.GetStorage("VBA").GetStream(vbaModule.moduleName).GetData(); DebugLog("Existing VBA source:\n" + GetVBATextFromModuleStream(streamBytes, vbaModule.textOffset)); // Get new VBA source code from specified text file. If not specified, VBA code is removed completely. string newVBACode = ""; if (VBASourceFileName != "") { try { newVBACode = System.IO.File.ReadAllText(VBASourceFileName); } catch (Exception e) { Console.WriteLine("ERROR: Could not open VBA source file " + VBASourceFileName); Console.WriteLine("Please make sure this file exists and contains ASCII only characters."); Console.WriteLine(); Console.WriteLine(e.Message); return; } } DebugLog("Replacing with VBA code:\n" + newVBACode); streamBytes = ReplaceVBATextInModuleStream(streamBytes, vbaModule.textOffset, newVBACode); DebugLog("Hex dump of VBA module stream " + vbaModule.moduleName + ":\n" + Utils.HexDump(streamBytes)); commonStorage.GetStorage("VBA").GetStream(vbaModule.moduleName).SetData(streamBytes); } } } // Set random ASCII names for VBA modules in dir stream if (optionSetRandomNames) { Console.WriteLine("Setting random ASCII names for VBA modules in dir stream (while leaving unicode names intact)."); // Recompress and write to dir stream commonStorage.GetStorage("VBA").GetStream("dir").SetData(Compress(SetRandomNamesInDirStream(dirStream))); } // Delete metadata from document if (optionDeleteMetadata) { try { cf.RootStorage.Delete("\u0005SummaryInformation"); } catch (Exception e) { Console.WriteLine("ERROR: metadata stream does not exist (option ignored)"); DebugLog(e.Message); } } // Commit changes and close file cf.Commit(); cf.Close(); // Purge unused space in file CompoundFile.ShrinkCompoundFile(oleFilename); // Zip the file back up as a docm or xlsm if (is_OpenXML) { if (File.Exists(outFilename)) { File.Delete(outFilename); } ZipFile.CreateFromDirectory(unzipTempPath, outFilename); // Delete Temporary Files Directory.Delete(unzipTempPath, true); } // Start web server, if option is specified if (optionWebserverPort != 0) { try { WebServer ws = new WebServer(SendFile, "http://*:" + optionWebserverPort.ToString() + "/"); ws.Run(); Console.WriteLine("Webserver starting on port " + optionWebserverPort.ToString() + ". Press a key to quit."); Console.ReadKey(); ws.Stop(); Console.WriteLine("Webserver closed. Goodbye!"); } catch (Exception e) { Console.WriteLine("ERROR: could not start webserver on specified port"); DebugLog(e.Message); } } }
static public void Main(string[] args) { // List of target VBA modules to stomp, if empty => all modules will be stomped List <string> targetModules = new List <string>(); // Filename that contains the VBA code used for substitution string VBASourceFileName = ""; // Target MS Office version for pcode string targetOfficeVersion = ""; // Option to hide modules from VBA editor GUI bool optionHideInGUI = false; // Option to unhide modules from VBA editor GUI bool optionUnhideInGUI = false; // Option to start web server to serve malicious template int optionWebserverPort = 0; // Option to display help bool optionShowHelp = false; // File format is OpenXML (docm or xlsm) bool is_OpenXML = false; // Option to delete metadata from file bool optionDeleteMetadata = false; // Option to set random module names in dir stream bool optionSetRandomNames = false; // Option to reset module names in dir stream (undo SetRandomNames option) bool optionResetModuleNames = false; // Option to set locked/unviewable options in Project Stream bool optionUnviewableVBA = false; // Option to set unlocked/viewable options in Project Stream bool optionViewableVBA = false; // Temp path to unzip OpenXML files to String unzipTempPath = ""; // Start parsing command line arguments var p = new OptionSet() { { "n|name=", "The target module name to stomp.\n" + "This argument can be repeated.", v => targetModules.Add(v) }, { "s|sourcefile=", "File containing substitution VBA code (fake code).", v => VBASourceFileName = v }, { "g|guihide", "Hide code from VBA editor GUI.", v => optionHideInGUI = v != null }, { "gg|guiunhide", "Unhide code from VBA editor GUI.", v => optionUnhideInGUI = v != null }, { "t|targetversion=", "Target MS Office version the pcode will run on.", v => targetOfficeVersion = v }, { "w|webserver=", "Start web server on specified port to serve malicious template.", (int v) => optionWebserverPort = v }, { "d|delmetadata", "Remove metadata stream (may include your name etc.).", v => optionDeleteMetadata = v != null }, { "r|randomnames", "Set random module names, confuses some analyst tools.", v => optionSetRandomNames = v != null }, { "rr|resetmodulenames", "Undo the set random module names by making the ASCII module names in the DIR stream match their Unicode counter parts", v => optionResetModuleNames = v != null }, { "u|unviewableVBA", "Make VBA Project unviewable/locked.", v => optionUnviewableVBA = v != null }, { "uu|viewableVBA", "Make VBA Project viewable/unlocked.", v => optionViewableVBA = v != null }, { "v", "Increase debug message verbosity.", v => { if (v != null) { ++verbosity; } } }, { "h|help", "Show this message and exit.", v => optionShowHelp = v != null }, }; List <string> extra; try { extra = p.Parse(args); } catch (OptionException e) { Console.WriteLine(e.Message); Console.WriteLine("Try '--help' for more information."); return; } if (extra.Count > 0) { filename = string.Join(" ", extra.ToArray()); } else { optionShowHelp = true; } if (optionShowHelp) { ShowHelp(p); return; } // End parsing command line arguments // OLE Filename (make a copy so we don't overwrite the original) outFilename = getOutFilename(filename); string oleFilename = outFilename; // Attempt to unzip as docm or xlsm OpenXML format try { unzipTempPath = CreateUniqueTempDirectory(); ZipFile.ExtractToDirectory(filename, unzipTempPath); if (File.Exists(Path.Combine(unzipTempPath, "word", "vbaProject.bin"))) { oleFilename = Path.Combine(unzipTempPath, "word", "vbaProject.bin"); } else if (File.Exists(Path.Combine(unzipTempPath, "xl", "vbaProject.bin"))) { oleFilename = Path.Combine(unzipTempPath, "xl", "vbaProject.bin"); } is_OpenXML = true; } catch (Exception) { // Not OpenXML format, Maybe 97-2003 format, Make a copy if (File.Exists(outFilename)) { File.Delete(outFilename); } File.Copy(filename, outFilename); } // Open OLE compound file for editing try { cf = new CompoundFile(oleFilename, CFSUpdateMode.Update, 0); } catch (Exception e) { Console.WriteLine("ERROR: Could not open file " + filename); Console.WriteLine("Please make sure this file exists and is .docm or .xlsm file or a .doc in the Office 97-2003 format."); Console.WriteLine(); Console.WriteLine(e.Message); return; } // Read relevant streams CFStorage commonStorage = cf.RootStorage; // docm or xlsm if (cf.RootStorage.TryGetStorage("Macros") != null) { commonStorage = cf.RootStorage.GetStorage("Macros"); // .doc } if (cf.RootStorage.TryGetStorage("_VBA_PROJECT_CUR") != null) { commonStorage = cf.RootStorage.GetStorage("_VBA_PROJECT_CUR"); // xls } vbaProjectStream = commonStorage.GetStorage("VBA").GetStream("_VBA_PROJECT").GetData(); projectStream = commonStorage.GetStream("project").GetData(); projectwmStream = commonStorage.GetStream("projectwm").GetData(); dirStream = Decompress(commonStorage.GetStorage("VBA").GetStream("dir").GetData()); // Read project streams as string string projectStreamString = System.Text.Encoding.UTF8.GetString(projectStream); string projectwmStreamString = System.Text.Encoding.UTF8.GetString(projectwmStream); // Find all VBA modules in current file List <ModuleInformation> vbaModules = ParseModulesFromDirStream(dirStream); // Write streams to debug log (if verbosity enabled) DebugLog("Hex dump of original _VBA_PROJECT stream:\n" + Utils.HexDump(vbaProjectStream)); DebugLog("Hex dump of original dir stream:\n" + Utils.HexDump(dirStream)); DebugLog("Hex dump of original project stream:\n" + Utils.HexDump(projectStream)); // Replace Office version in _VBA_PROJECT stream if (targetOfficeVersion != "") { ReplaceOfficeVersionInVBAProject(vbaProjectStream, targetOfficeVersion); commonStorage.GetStorage("VBA").GetStream("_VBA_PROJECT").SetData(vbaProjectStream); } //Set ProjectProtectionState and ProjectVisibilityState to locked/unviewable see https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-ovba/dfd72140-85a6-4f25-8a17-70a89c00db8c if (optionUnviewableVBA) { string tmpStr = Regex.Replace(projectStreamString, "CMG=\".*\"", "CMG=\"\""); string newProjectStreamString = Regex.Replace(tmpStr, "GC=\".*\"", "GC=\"\""); // Write changes to project stream commonStorage.GetStream("project").SetData(Encoding.UTF8.GetBytes(newProjectStreamString)); } //Set ProjectProtectionState and ProjectVisibilityState to be viewable see https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-ovba/dfd72140-85a6-4f25-8a17-70a89c00db8c if (optionViewableVBA) { Console.WriteLine("Making the project visible..."); // Console.WriteLine("Stream before: " + projectStreamString); string tmpStr = projectStreamString; tmpStr = Regex.Replace(tmpStr, "CMG=\"?.*\"?", "CMG=\"CAC866BE34C234C230C630C6\""); tmpStr = Regex.Replace(tmpStr, "ID=\"?.*\"?", "ID=\"{00000000-0000-0000-0000-000000000000}\""); tmpStr = Regex.Replace(tmpStr, "DPB=\"?.*\"?", "DPB=\"94963888C84FE54FE5B01B50E59251526FE67A1CC76C84ED0DAD653FD058F324BFD9D38DED37\""); tmpStr = Regex.Replace(tmpStr, "GC=\"?.*\"?", "GC=\"5E5CF2C27646414741474\""); string newProjectStreamString = tmpStr; // Console.WriteLine("Stream afterw: " + newProjectStreamString); // Write changes to project stream commonStorage.GetStream("project").SetData(Encoding.UTF8.GetBytes(newProjectStreamString)); } // Hide modules from GUI if (optionHideInGUI) { foreach (var vbaModule in vbaModules) { if ((vbaModule.moduleName != "ThisDocument") && (vbaModule.moduleName != "ThisWorkbook")) { Console.WriteLine("Hiding module: " + vbaModule.moduleName); projectStreamString = projectStreamString.Replace("Module=" + vbaModule.moduleName, ""); } } // Write changes to project stream commonStorage.GetStream("project").SetData(Encoding.UTF8.GetBytes(projectStreamString)); } // Undo the Hide modules from GUI effects if (optionUnhideInGUI) { ArrayList vbaModulesNamesFromProjectwm = getModulesNamesFromProjectwmStream(projectwmStreamString); Regex theregex = new Regex(@"(Document\=.*\/.{10})([\S\s]*?)(ExeName32\=|Name\=|ID\=|Class\=|BaseClass\=|Package\=|HelpFile\=|HelpContextID\=|Description\=|VersionCompatible32\=|CMG\=|DPB\=|GC\=)"); Match m = theregex.Match(projectStreamString); if (m.Groups.Count != 4) { Console.WriteLine("Error, could not find the location to insert module names. Not able to unhide modules"); } else { string moduleString = "\r\n"; foreach (var vbaModuleName in vbaModulesNamesFromProjectwm) { Console.WriteLine("Unhiding module: " + vbaModuleName); moduleString = moduleString.Insert(moduleString.Length, "Module=" + vbaModuleName + "\r\n"); } projectStreamString = projectStreamString.Replace(m.Groups[0].Value, m.Groups[1].Value + moduleString + m.Groups[3].Value); // write changes to project stream commonStorage.GetStream("project").SetData(Encoding.UTF8.GetBytes(projectStreamString)); } } // Stomp VBA modules if (VBASourceFileName != "") { byte[] streamBytes; foreach (var vbaModule in vbaModules) { DebugLog("VBA module name: " + vbaModule.moduleName + "\nOffset for code: " + vbaModule.textOffset); // If this module is a target module, or if no targets are specified, then stomp if (targetModules.Contains(vbaModule.moduleName) || !targetModules.Any()) { Console.WriteLine("Now stomping VBA code in module: " + vbaModule.moduleName); streamBytes = commonStorage.GetStorage("VBA").GetStream(vbaModule.moduleName).GetData(); DebugLog("Existing VBA source:\n" + GetVBATextFromModuleStream(streamBytes, vbaModule.textOffset)); // Get new VBA source code from specified text file. If not specified, VBA code is removed completely. string newVBACode = ""; if (VBASourceFileName != "") { try { newVBACode = System.IO.File.ReadAllText(VBASourceFileName); } catch (Exception e) { Console.WriteLine("ERROR: Could not open VBA source file " + VBASourceFileName); Console.WriteLine("Please make sure this file exists and contains ASCII only characters."); Console.WriteLine(); Console.WriteLine(e.Message); return; } } DebugLog("Replacing with VBA code:\n" + newVBACode); streamBytes = ReplaceVBATextInModuleStream(streamBytes, vbaModule.textOffset, newVBACode); DebugLog("Hex dump of VBA module stream " + vbaModule.moduleName + ":\n" + Utils.HexDump(streamBytes)); commonStorage.GetStorage("VBA").GetStream(vbaModule.moduleName).SetData(streamBytes); } } } // Set random ASCII names for VBA modules in dir stream if (optionSetRandomNames) { Console.WriteLine("Setting random ASCII names for VBA modules in dir stream (while leaving unicode names intact)."); // Recompress and write to dir stream commonStorage.GetStorage("VBA").GetStream("dir").SetData(Compress(SetRandomNamesInDirStream(dirStream))); } // Reset module names in dir stream so that the ASCII names match the Unicode names (undo SetRandomNames option) if (optionResetModuleNames) { Console.WriteLine("Resetting module names in dir stream to match names is _VBA_PROJECT stream (undo SetRandomNames option)"); // Recompress and write to dir stream commonStorage.GetStorage("VBA").GetStream("dir").SetData(Compress(ResetModuleNamesInDirStream(dirStream))); } // Delete metadata from document if (optionDeleteMetadata) { try { cf.RootStorage.Delete("\u0005SummaryInformation"); } catch (Exception e) { Console.WriteLine("ERROR: metadata stream does not exist (option ignored)"); DebugLog(e.Message); } } // Commit changes and close file cf.Commit(); cf.Close(); // Purge unused space in file CompoundFile.ShrinkCompoundFile(oleFilename); // Zip the file back up as a docm or xlsm if (is_OpenXML) { if (File.Exists(outFilename)) { File.Delete(outFilename); } ZipFile.CreateFromDirectory(unzipTempPath, outFilename); // Delete Temporary Files Directory.Delete(unzipTempPath, true); } // Start web server, if option is specified if (optionWebserverPort != 0) { try { WebServer ws = new WebServer(SendFile, "http://*:" + optionWebserverPort.ToString() + "/"); ws.Run(); Console.WriteLine("Webserver starting on port " + optionWebserverPort.ToString() + ". Press a key to quit."); Console.ReadKey(); ws.Stop(); Console.WriteLine("Webserver closed. Goodbye!"); } catch (Exception e) { Console.WriteLine("ERROR: could not start webserver on specified port"); DebugLog(e.Message); } } }
/// <summary> /// Gets the MAPI property value from a stream or storage in this storage. /// </summary> /// <param name="propIdentifier">The 4 char hexadecimal prop identifier.</param> /// <returns>The value of the MAPI property or null if not found.</returns> private object GetMapiPropertyFromStreamOrStorage(string propIdentifier) { //determine if the property identifier is in a stream or sub storage string propTag = null; var propType = PropertyType.PT_UNSPECIFIED; foreach (string propKey in _keys) { if (propKey.StartsWith(MapiTags.SubStgVersion1 + propIdentifier)) { propTag = propKey.Substring(12, 8); propType = (PropertyType)ushort.Parse(propKey.Substring(16, 4), System.Globalization.NumberStyles.HexNumber); break; } } //depending on prop type use method to get property value string containerName = MapiTags.SubStgVersion1 + propTag; switch (propType) { case PropertyType.PT_UNSPECIFIED: return(null); case PropertyType.PT_STRING8: return(this.GetStreamAsString(containerName, Encoding.Default)); case PropertyType.PT_UNICODE: return(this.GetStreamAsString(containerName, Encoding.Unicode)); case PropertyType.PT_BINARY: return(_storage.GetStream(containerName).GetData()); case PropertyType.PT_OBJECT: return(_storage.GetStorage(containerName)); case PropertyType.PT_MV_STRING8: case PropertyType.PT_MV_UNICODE: // If the property is a unicode multiview item we need to read all the properties // again and filter out all the multivalue names, they end with -00000000, -00000001, etc.. var multiValueContainerNames = _keys.Where(propKey => propKey.StartsWith(containerName + "-")).ToList(); var values = new List <string>(); foreach (var multiValueContainerName in multiValueContainerNames) { var value = GetStreamAsString(multiValueContainerName, propType == PropertyType.PT_MV_STRING8 ? Encoding.Default : Encoding.Unicode); // Multi values always end with a null char so we need to strip that one off if (value.EndsWith("/0")) { value = value.Substring(0, value.Length - 1); } values.Add(value); } return(values); default: throw new ApplicationException("MAPI property has an unsupported type and can not be retrieved."); } }
/** * Create VBA Project from file * * @param sPath path of macro enabled Excel, Powerpoint or Word file. */ public VBAProject(string sPath) { //Initialize pPath pPath = sPath; //Get unzip location unzipTempPath = CreateUniqueTempDirectory(); /* EvilClippy brute forces the file open, It does this in a fairly unclean manner. We will assume the file, * is named correctly (extension wise). This, although requiring more boiler plate will look a lot cleaner. */ // Get extension of file: string ext = Path.GetExtension(sPath); //Parse extension and create sOLEFileName switch (ext) { case ".xlsm": case ".xlam": isOpenXML = true; sOLEFileName = Path.Combine(unzipTempPath, "xl", "vbaProject.bin"); break; case ".docm": isOpenXML = true; sOLEFileName = Path.Combine(unzipTempPath, "word", "vbaProject.bin"); break; case ".pptm": isOpenXML = true; sOLEFileName = Path.Combine(unzipTempPath, "ppt", "vbaProject.bin"); //untested break; case ".xls": case ".doc": case ".ppt": //Copy path to sOLEFileName to prevent overwriting (we'll overrite this file directly) sOLEFileName = Path.Combine(unzipTempPath, Path.GetFileName(sPath)); File.Copy(sPath, sOLEFileName); break; default: Console.WriteLine("ERROR: Could not open file " + sPath); Console.WriteLine("Please make sure this file exists, has a valid extension and is of a valid type."); Console.WriteLine(); break; } //Unzip to unzipTemoPath if isOpenXML. Otherwise create compound file. if (isOpenXML) { ZipFile.ExtractToDirectory(sPath, unzipTempPath); } //Create Compound file from VBProject.bin or xls,doc,ppt file: cf = new CompoundFile(sOLEFileName, CFSUpdateMode.Update, 0); // Read relevant streams switch (ext) { case ".xlsm": case ".xlam": case ".docm": case ".pptm": commonStorage = cf.RootStorage; break; case ".doc": commonStorage = cf.RootStorage.GetStorage("Macros"); //Note you can also use `cf.RootStorage.TryGetStorage("Macros")` which returns null if not found. break; case ".ppt": //untested - don't know if this is the case for ppts case ".xls": commonStorage = cf.RootStorage.GetStorage("_VBA_PROJECT_CUR"); break; } vbaProjectStream = commonStorage.GetStorage("VBA").GetStream("_VBA_PROJECT").GetData(); //Contains VBA Project properties, including module names. Remove module names from this stream to hide them. //PROJECT stream: https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-ovba/cc848a02-6f87-49a4-ad93-6edb3103f593 //More information: https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-ovba/d88cb9d8-a475-423d-b370-cc0caaf78628 projectStream = commonStorage.GetStream("project").GetData(); //PROJECTwm stream contains all names of all Modules. //PROJECTwm stream: https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-ovba/514acc65-ea7b-4813-aaf7-fabb1bca0ba2 //More Information: https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-ovba/c458f2e6-f2cc-4c2d-96c7-91a3e63f2fe1 projectwmStream = commonStorage.GetStream("PROJECTwm").GetData(); //Contains names of visible modules and module source code text (compressed) //VBA Storage: https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-ovba/b693e0ba-489f-4ac8-ac9d-6387fb5779bb //VBA StorageInfo: https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-ovba/170f52a0-4cd6-4729-b51a-d08155cbd213 //Dir Stream: https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-ovba/672d276c-d83c-4452-993b-ca9eca3d8917 dirStream = VbaCompression.Decompress(commonStorage.GetStorage("VBA").GetStream("dir").GetData()); // Read project streams as string string projectStreamString = System.Text.Encoding.UTF8.GetString(projectStream); string projectwmStreamString = System.Text.Encoding.UTF8.GetString(projectwmStream); Console.WriteLine(System.Text.Encoding.UTF8.GetString(projectStream)); Console.WriteLine(System.Text.Encoding.UTF8.GetString(projectwmStream)); // Find all VBA modules in current file vbaModulesEx = ParseModulesFromDirStream(dirStream); // Write streams to debug log (if verbosity enabled) Console.WriteLine("Hex dump of original _VBA_PROJECT stream:\n" + Utils.HexDump(vbaProjectStream)); Console.WriteLine("Hex dump of original dir stream:\n" + Utils.HexDump(dirStream)); }
private static bool CfStoragesAreDifferent(CFStorage s1, CFStorage s2, out string explain) { var s1Names = new List <Tuple <string, bool> >(); var s2Names = new List <Tuple <string, bool> >(); s1.VisitEntries(i => s1Names.Add(Tuple.Create(i.Name, i.IsStorage)), false); s2.VisitEntries(i => s2Names.Add(Tuple.Create(i.Name, i.IsStorage)), false); s1Names.Sort(); s2Names.Sort(); if (!s1Names.SequenceEqual(s2Names)) { explain = string.Format(VBASyncResources.ExplainFrxDifferentFileLists, s1.Name, string.Join("', '", s1Names.Select(t => t.Item1)), string.Join("', '", s2Names.Select(t => t.Item1))); return(true); } FormControl fc1 = null; foreach (var t in s1Names) { if (t.Item2) { if (CfStoragesAreDifferent(s1.GetStorage(t.Item1), s2.GetStorage(t.Item1), out explain)) { return(true); } } else if (t.Item1 == "f") { fc1 = new FormControl(s1.GetStream("f").GetData()); var fc2 = new FormControl(s2.GetStream("f").GetData()); if (!fc1.Equals(fc2)) { explain = string.Format(VBASyncResources.ExplainFrxGeneralStreamDifference, "f", s1.Name); return(true); } } else if (t.Item1 == "o" && fc1 != null) { var o1 = s1.GetStream("o").GetData(); var o2 = s2.GetStream("o").GetData(); uint idx = 0; foreach (var site in fc1.Sites) { explain = string.Format(VBASyncResources.ExplainFrxOStreamDifference, site.Name, s1.Name); var o1Range = o1.Range(idx, site.ObjectStreamSize); var o2Range = o2.Range(idx, site.ObjectStreamSize); switch (site.ClsidCacheIndex) { case 15: // MorphData case 26: // CheckBox case 25: // ComboBox case 24: // ListBox case 27: // OptionButton case 23: // TextBox case 28: // ToggleButton if (!new MorphDataControl(o1Range).Equals(new MorphDataControl(o2Range))) { return(true); } break; case 17: // CommandButton if (!new CommandButtonControl(o1Range).Equals(new CommandButtonControl(o2Range))) { return(true); } break; case 18: // TabStrip if (!new TabStripControl(o1Range).Equals(new TabStripControl(o2Range))) { return(true); } break; case 21: // Label if (!new LabelControl(o1Range).Equals(new LabelControl(o2Range))) { return(true); } break; default: if (!o1Range.SequenceEqual(o2Range)) { return(true); } break; } idx += site.ObjectStreamSize; } } else if (!s1.GetStream(t.Item1).GetData().SequenceEqual(s2.GetStream(t.Item1).GetData())) { explain = string.Format(VBASyncResources.ExplainFrxGeneralStreamDifference, t.Item1, s1.Name); return(true); } } explain = "No differences found."; return(false); }
static void Main(string[] args) { try { if (args.Length == 0 || args.Contains("-h")) { PrintHelp(); return; } Dictionary <string, string> argDict = Utils.ParseArgs(args); if (argDict.ContainsKey("d")) { document = argDict["d"]; if (document != "word" && document != "excel" && document != "publisher") { Console.WriteLine("\n[!] Unknown document type. Options are 'word', 'excel', or 'publisher'.\n"); return; } } else { Console.WriteLine("\n[!] Missing document type (-d)\n"); return; } if (argDict.ContainsKey("f")) { filename = argDict["f"]; } else { Console.WriteLine("\n[!] Missing file (-f)\n"); return; } if (args.Contains("-l")) { list_modules = true; } else { if (argDict.ContainsKey("m")) { module = argDict["m"]; } else { Console.WriteLine("\n[!] Missing module (-m)\n"); return; } } // VBA Purging try { // Make a copy of document to VBA Purge if user is not listing modules if (!list_modules) { string outFilename = Utils.getOutFilename(filename); string oleFilename = outFilename; if (File.Exists(outFilename)) { File.Delete(outFilename); } File.Copy(filename, outFilename); filename = outFilename; } CompoundFile cf = new CompoundFile(filename, CFSUpdateMode.Update, 0); CFStorage commonStorage = cf.RootStorage; if (document == "word") { commonStorage = cf.RootStorage.GetStorage("Macros"); } else if (document == "excel") { commonStorage = cf.RootStorage.GetStorage("_VBA_PROJECT_CUR"); } else if (document == "publisher") { commonStorage = cf.RootStorage.GetStorage("VBA"); } // Grab data from "dir" module stream. Used to retrieve list of module streams in document. byte[] dirStream = Utils.Decompress(commonStorage.GetStorage("VBA").GetStream("dir").GetData()); List <Utils.ModuleInformation> vbaModules = Utils.ParseModulesFromDirStream(dirStream); // Only list module streams in document and return if (list_modules) { foreach (var vbaModule in vbaModules) { Console.WriteLine("\n[*] VBA module name: " + vbaModule.moduleName); } Console.WriteLine("\n[*] Finished listing modules\n"); return; } byte[] streamBytes; bool module_found = false; foreach (var vbaModule in vbaModules) { //VBA Purging begins if (vbaModule.moduleName == module) { Console.WriteLine("\n[*] VBA module name: " + vbaModule.moduleName); Console.WriteLine("\n[*] Offset for code: " + vbaModule.textOffset); Console.WriteLine("\n[*] Now purging VBA code in module: " + vbaModule.moduleName); // Get the CompressedSourceCode from module streamBytes = commonStorage.GetStorage("VBA").GetStream(vbaModule.moduleName).GetData(); string OG_VBACode = Utils.GetVBATextFromModuleStream(streamBytes, vbaModule.textOffset); // Remove P-code from module stream and set the module to only have the CompressedSourceCode streamBytes = Utils.RemovePcodeInModuleStream(streamBytes, vbaModule.textOffset, OG_VBACode); commonStorage.GetStorage("VBA").GetStream(vbaModule.moduleName).SetData(streamBytes); module_found = true; } } if (module_found == false) { Console.WriteLine("\n[!] Could not find module in document (-m). List all module streams with (-l).\n"); cf.Commit(); cf.Close(); CompoundFile.ShrinkCompoundFile(filename); File.Delete(filename); return; } // Change offset to 0 so that document can find compressed source code. commonStorage.GetStorage("VBA").GetStream("dir").SetData(Utils.Compress(Utils.ChangeOffset(dirStream))); Console.WriteLine("\n[*] Module offset changed to 0."); // Remove performance cache in _VBA_PROJECT stream. Replace the entire stream with _VBA_PROJECT header. byte[] data = Utils.HexToByte("CC-61-FF-FF-00-00-00"); commonStorage.GetStorage("VBA").GetStream("_VBA_PROJECT").SetData(data); Console.WriteLine("\n[*] PerformanceCache removed from _VBA_PROJECT stream."); // Check if document contains SRPs. Must be removed for VBA Purging to work. try { commonStorage.GetStorage("VBA").Delete("__SRP_0"); commonStorage.GetStorage("VBA").Delete("__SRP_1"); commonStorage.GetStorage("VBA").Delete("__SRP_2"); commonStorage.GetStorage("VBA").Delete("__SRP_3"); Console.WriteLine("\n[*] SRP streams deleted!"); } catch (Exception) { Console.WriteLine("\n[*] No SRP streams found."); } // Commit changes and close cf.Commit(); cf.Close(); CompoundFile.ShrinkCompoundFile(filename); Console.WriteLine("\n[*] VBA Purging completed successfully!\n"); } // Error handle for file not found catch (FileNotFoundException ex) when(ex.Message.Contains("Could not find file")) { Console.WriteLine("\n[!] Could not find path or file (-f). \n"); } // Error handle when document specified and file chosen don't match catch (CFItemNotFound ex) when(ex.Message.Contains("Cannot find item")) { Console.WriteLine("\n[!] File (-f) does not match document type selected (-d).\n"); } // Error handle when document is not OLE/CFBF format catch (CFFileFormatException) { Console.WriteLine("\n[!] Incorrect filetype (-f). Must be an OLE strucutred file. OfficePurge supports .doc, .xls, or .pub documents.\n"); } } // Error handle for incorrect use of flags catch (IndexOutOfRangeException) { Console.WriteLine("\n[!] Flags (-d), (-f), (-m) need an argument. Make sure you have provided these flags an argument.\n"); } }