MpjǙdZddlmZdZddlZddlZddlZddlZddlm Z m Z ddl Z ddl Z ddl Z ddlZddlZddlZddlZddlZddlZddlZddlZ ddlmZddlZej<dk(rej>jAd dd l!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-m.Z.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4 dd l5m6Z7dZ8ejrjuejrjwejrjye=Z>ejrjuejrje>d Z@e@ejrvrejrjde@ddlBZBddlCmDZDddlEmFZFmGZGddlHmIZIddlJmKZKddlJmLZLddlJmMZMddlJmNZNddlOmPZPddlQmRZRddlJmSZSddlTmUZUejddkrdZWeXZYdZZnNd ZWdZYe[Z\e]Z^e_Z`ddlambZbdZZejdkr,ddlcZcecjdZedZfecjdefd Zhd9d!ZieUjd"Zkd#ZlGd$d%emZnGd&d'enZoGd(d)enZpGd*d+eqenZrGd,d-eoZsGd.d/enZtdZud0ZvdZwd1Zxd2Zyd3Zzd4Z{d5Z|d6Z}d7Z~d8d9d:d;dd?d@dAdBd:dC ZdDZdEezZdFZdGZdHZdIZdJZdKZdLZdMZedNedOedPedQedRedSedTedUiZdVZdWZdXZdYZdZZed[zZed\zZd]Zed^zZed_zZed\zZed`zZdaZedbzZdcdddedfdgdhdidjdkdl ZdmdndoZidpdqdrdsdtdudvdwdxdydzd{d|d}d~dddddddddddddddddddiddddddddddddddddddddddddddddddddddddddddddddddd ZddddddddȜZddiZdZdZdezdzZdZdezdzezZdezdzezdzZdZeezZdZedzezezZe jPeZddgZde jPefde jPefde jPdezdzfde jPdޫffZe jPd߫ZdZe jPdezdzZegdZe jPdZe jPdZe jPdZe4jhe-dzZGdde_Ze$e'de,eze+e0ze*e'e+dd0zZejqde$e*e&de'e#dze+e2dze*e'e+dd0zZejqde$e*e#de+e2dze*e'e+dd0zZejqdeezezZe(ddZejqde%Ze%Ze*e$e,ee#dze'e#de#dzze'dzdzeze*dΫzZdZejqee*e"ddzeze*dΫzZejqde*e"ddzeze*dΫzZejqde*e"ddzeze*dΫzZeÐjqde*e"ddzeze*dΫzZeĐjqde+e.e-dzZe*de$e+e/ddzze*dzZeƐjqde*eūe*dzeƐdze*dΫzZeǐjqd e*de)eze*dzZeȐjqd e*eūe*dzeȐd ze*dΫzZeɐjqd d ZeezezezezezZee3eːdde1jefdde1jefgzZdZ͐dZΐdZϐdZeezezZee3eѐdde1jefdde1jefdde1jefdde1jefgzZdZe jPdZӐdZeejZ֐dZאdZؐdZGddeګZGddeګZܐd:dZݐd Zސd!Zߐd;d"Zd;d#Zd;d$Zd%Zd&Zd'Zd(Zd)Zd*Zd+Zd<d,Zd=d-ZGd.d/eګZd>d0ZGd1d2eګZGd3d4eZd;d5Zd?d6Zd;d7Zed8k(reyy#e$r3 ddlmcmZn$#e$r ddlmZn#e$r edwxYwYnwxYwY mwxYw#e$rd Z8Y wxYw(@ao olevba.py olevba is a script to parse OLE and OpenXML files such as MS Office documents (e.g. Word, Excel), to extract VBA Macro code in clear text, deobfuscate and analyze malicious macros. XLM/Excel 4 Macros are also supported in Excel and SLK files. Supported formats: - Word 97-2003 (.doc, .dot), Word 2007+ (.docm, .dotm) - Excel 97-2003 (.xls), Excel 2007+ (.xlsm, .xlsb) - PowerPoint 97-2003 (.ppt), PowerPoint 2007+ (.pptm, .ppsm) - Word/PowerPoint 2007+ XML (aka Flat OPC) - Word 2003 XML (.xml) - Word/Excel Single File Web Page / MHTML (.mht) - Publisher (.pub) - SYLK/SLK files (.slk) - Text file containing VBA or VBScript source code - Password-protected Zip archive containing any of the above - raises an error if run with files encrypted using MS Crypto API RC4 Author: Philippe Lagadec - http://www.decalage.info License: BSD, see source code or documentation olevba is part of the python-oletools package: http://www.decalage.info/python/oletools olevba is based on source code from officeparser by John William Davison https://github.com/unixfreak0037/officeparser )print_functionz0.60.2N)BytesIOStringIOzplxml or ElementTree are not installed, see http://codespeak.net/lxml or http://effbot.org/zone/element-index.htmntT) auto_colors)CaselessKeywordCaselessLiteralCombineForwardLiteralOptional QuotedStringRegexSuppressWord WordStart alphanumsalphashexnumsnumsopAssocsrange infixNotation ParserElement) deobfuscatorFz..) tablestream)xglobPathNotFoundException)cBIFF) ppt_parser)oleform)rtfobj)crypto)ensure_stdout_handles_unicode) codepages)ftguess) log_helperutf8c|SN)xs ]/mnt/ssd/data/Dropbox/adrian/scripts/msg_venv/lib/python3.12/site-packages/oletools/olevba.pybyte_ordr/nsreduce)backslashreplacect|trGdjd|j|j|j D}||j fSt |S)Nc3>K|]}dj|yw)z \x{0:02x}Nformat).0cs r. z*backslashreplace_errors..sZqL//2Z) isinstanceUnicodeDecodeErrorjoinobjectstartend_backslashreplace_errors)excus r.backslashreplace_errorsrHsP#12GGZCJJsyyQTQXQX>r0c8tr|S|j|dS)a convert a bytes string to a native str: - on Python 2, it returns the same string (bytes=str) - on Python 3, the string is decoded using the provided encoding (UTF-8 by default) to a unicode str :param bytes_string: bytes string to be converted :param encoding: codec to be used for decoding :return: the string converted to str :rtype: str rJrK)rMdecode) bytes_stringencodings r. bytes2strrUs#""8I">>r0olevbactjtjt j t j y)a Enable logging for this module (disabled by default). For use by third-party libraries that import `olevba` as module. This will set the module-specific logger level to `NOTSET`, which means the main application controls the actual logging level. This also enables logging for the modules used by us, but not the global common logging mechanism (:py:mod:`oletools.common.log_helper.log_helper`). Use :py:func:`oletools.common.log_helper.log_helper.enable_logging` for that. N)logsetLevelr'NOTSETr enable_loggingr#r,r0r.r[r[s.LL""# r0c$eZdZdZdfd ZxZS)OlevbaBaseExceptionzD Base class for exceptions produced here for simpler except clauses c |r'tt| |dj|zfi|ntt| |fi|||_||_||_y)Nz ({0}))superr]__init__r:msgfilenameorig_exc)selfrarbrckwargs __class__s r.r`zOlevbaBaseException.__init__s`  %t 5c6>ooh6O7P @8> @ %t 5c DV D    r0)NN__name__ __module__ __qualname____doc__r` __classcell__rfs@r.r]r]sN ! !r0r]c$eZdZdZdfd ZxZS) FileOpenErrorzy raised by VBA_Parser constructor if all open_... attempts failed probably means the file type is not supported c6tt| d|z||y)NzFailed to open file %s)r_ror`rdrbrcrfs r.r`zFileOpenError.__init__s mT+ $x /8 Er0r+rgrms@r.roros EEr0roc"eZdZdZfdZxZS)ProcessingErrorz. raised by VBA_Parser.process_file* functions c6tt| d|z||y)NzError processing file %s)r_rsr`rqs r.r`zProcessingError.__init__s ot- & 18X Gr0rgrms@r.rsrss8GGr0rsceZdZdZdZy)MsoExtractionErrorzB raised by mso_file_extract if parsing MSO/ActiveMIME data failed c\tj||tj||yr+)rvr`r])rdras r.r`zMsoExtractionError.__init__s"##D#.$$T3/r0Nrhrirjrkr`r,r0r.rvrvs L0r0rvc$eZdZdZdfd ZxZS)SubstreamOpenErrorzE special kind of FileOpenError: file is a substream of original file cztt| t|dzt|z|||_||_yN/)r_rzr`strrb subfilename)rdrbrrcrfs r.r`zSubstreamOpenError.__init__s:  $0 MC #k"2 2H >  &r0r+rgrms@r.rzrzsO''r0rzc"eZdZdZfdZxZS)UnexpectedDataErrorzE raised when parsing is strict (=not relaxed) and data is unexpected ct|trdj|}n]t|tr*dj d|D}dj|}n#t djt |tt|'dj||||||_ ||_ ||_ ||_ y)N{0:04X},c3>K|]}dj|yw)rNr9)r;es r.r=z/UnexpectedDataError.__init__..s@!)**1-@r>z({0})zUnknown type encountered: {0}zIUnexpected value in {0} for variable {1}: expected {2} but found {3:04X}!)r?intr:tuplerA ValueErrortyper_rr` stream_pathvariableexpectedvalue)rdrrrresrfs r.r`zUnexpectedDataError.__init__s h $!!(+B % (@x@@B..$B<CCDNST T !41 . VK2u 5 7'     r0rgrms@r.rrsOr0rr3r4 z mac-romanshiftjisasciigb2321big5hebrewz mac-arabicz mac-greekz mac-turkishthaimaccentraleurope) i'i'i'i'i'i'i'i'ia'i%'i-'i'z,https://github.com/decalage2/oletools/issueszPlease report this issue on %sOLEOpenXML FlatOPC_XML Word2003_XMLMHTMLTextPPTSLKzOLE:zOpX:zFlX:zXML:zMHT:zTXT:zPPT:zSLK:s ActiveMimebasclsfrmz6{http://schemas.microsoft.com/office/word/2003/wordml}binDatanamez5{http://schemas.microsoft.com/office/2006/xmlPackage}packagepart contentTypez$application/vnd.ms-office.vbaProject binaryData)AutoExecAutoOpen DocumentOpen)AutoExit AutoCloseDocument_CloseDocumentBeforeClose)DocumentChange)AutoNew Document_New NewDocument) Document_Open)Document_BeforeClose) Auto_Open Workbook_OpenWorkbook_ActivateAuto_Ope) Auto_CloseWorkbook_CloseWorkbook_BeforeClose)Worksheet_Calculate) z%Runs when the Word document is openedz%Runs when the Word document is closedz'Runs when the Word document is modifiedz(Runs when a new Word document is createdz2Runs when the Word or Publisher document is openedz*Runs when the Publisher document is closedz&Runs when the Excel Workbook is openedz&Runs when the Excel Workbook is closedz)May run when an Excel WorkSheet is opened)z \w+_Paintedz \w+_Painting)z \w+_GotFocusz \w+_LostFocusz\w+_MouseHoverz \w+_Clickz \w+_Changez \w+_Resizez\w+_BeforeNavigate2z\w+_BeforeScriptExecutez\w+_DocumentCompletez\w+_DownloadBeginz\w+_DownloadCompletez\w+_FileDownloadz\w+_NavigateComplete2z\w+_NavigateErrorz\w+_ProgressChangez\w+_PropertyChangez\w+_SetSecureLockIconz\w+_StatusTextChangez\w+_TitleChangez \w+_MouseMovez\w+_MouseEnterz\w+_MouseLeavez \w+_Layoutz\w+_OnConnectingz\w+_FollowHyperlinkz\w+_ContentControlOnEnter)z>Runs when the file is opened (using InkPicture ActiveX object)z?Runs when the file is opened and ActiveX objects trigger eventsz%May read system environment variables)EnvironWin32_Environment EnvironmentExpandEnvironmentStringszHKCU\EnvironmentzHKEY_CURRENT_USER\EnvironmentzMay open a file)Openz+May write to a file (if combined with Open))WritePutOutputzPrint #z7May read or write a binary file (if combined with Open))BinaryzMay copy a file)FileCopyCopyFileCopyHere CopyFolderzMay move a file)MoveHereMoveFile MoveFolderzMay delete a file)KillzMay create a text file)CreateTextFilez ADODB.Stream WriteText SaveToFilez.May run an executable file or a system command)ShellvbNormal vbNormalFocusvbHidevbMinimizedFocusvbMaximizedFocusvbNormalNoFocusvbMinimizedNoFocusz WScript.ShellRun ShellExecute ShellExecuteAshell32 InvokeVerb InvokeVerbExDoItz May run a dll)ControlPanelItemz0May execute file or a system command through WMI)Createz7May run an executable file or a system command on a Mac) MacScript AppleScriptzMay run PowerShell commands) PowerShellnoexitExecutionPolicy noprofilecommandEncodedCommandzinvoke-command scriptblockzInvoke-ExpressionAuthorizationManagerz?May run an executable file or a system command using PowerShell)z Start-Process-May call a DLL using Excel 4 Macros (XLM/XLF))CALLzMay hide the application)zApplication.Visible ShowWindowSW_HIDEzMay create a directory)MkDirzMay save the current workbook)zActiveWorkbook.SaveAszrLsQqTr0&oz[0-7]c"t|ddS)NrrbaserIrJs r.rLrLss1Q4a'8r0z&hz [0-9a-fA-F]c"t|ddS)NrrPrIrJs r.rLrLsS1B%7r0"")escQuotect|dSrHr~rJs r.rLrLss1Q4yr0rBW$(c |d}|dk\r|dkrtt|Stt|jddS#t$r)t j dztd|zcYSwxYw)Nrzutf-8r5z.ERROR: incorrect parameter value for chr(): %rzChr(%r))rDchrunichrrNrrX exception)rKis r. vba_chr_tostrrbs} 2 aD a4AsF's1v. . 'vay'7'7AS'TU U 2 FJK"9q=112s"A #A /A;:A;Ascct|dSrH)ordrJs r.rLrL+sQqTr0Valc:t|djSrH)rstriprJs r.rLrL3sQqTZZ\!2r0rc<tt|ddddS)Nr)rDr~rJs r.rLrL:s$7AaD $B$$Hr0rc$td|dzS)Nz%%%s%%r)rDrJs r.rLrLAs!4X!_!Er0) initChars bodyChars)r(Nct|dSrHrWrJs r.rLrLTs3qt9r0 hex_stringcRttj|jSr+)rDbinasciia2b_hexrorJs r.rLrLXs+>x?O?OPQP\P\?]+^r0ct|dSrHrWrJs r.rLrLbsc!A$ir0 base64_stringcRttj|jSr+)rDrq a2b_base64rtrJs r.rLrLfs.A(BUBUVWVeVeBf.gr0cL|dddd}tdj|S)z[ parse action to concatenate strings in a VBA expression with operators '+' or '&' rNr(r7)rDrA)tokensstringss r.concat_strings_listrzks) Qi!nG rwww/ 00r0+c.|dddd}t|S)zL parse action to sum integers in a VBA expression with operator '+' rNr()sumrxintegerss r. sum_ints_listrs ay1~H x=r0c2|dddd}td|S)zQ parse action to subtract integers in a VBA expression with operator '-' rNr(c ||z Sr+r,r-ys r.rLz$subtract_ints_list.. QqSr0r1r~s r.subtract_ints_listrs" ay1~H .( ++r0c2|dddd}td|S)zQ parse action to multiply integers in a VBA expression with operator '*' rNr(c ||zSr+r,rs r.rLz$multiply_ints_list..rr0r1r~s r.multiply_ints_listr" ay1~H .( ++r0c2|dddd}td|S)zO parse action to divide integers in a VBA expression with operator '/' rNr(c ||z Sr+r,rs r.rLz"divide_ints_list..rr0r1r~s r.divide_ints_listrrr0*r}c,|jtS)a Check if the provided data is the content of a MSO/ActiveMime file, such as the ones created by Outlook in some cases, or Word/Excel when saving a file with the MHTML format or the Word 2003 XML format. This function only checks the ActiveMime magic at the beginning of data. :param data: bytes string, MSO/ActiveMime file content :return: bool, True if the file is MSO, False otherwise ) startswithMSO_ACTIVEMIME_HEADERdatas r. is_mso_filers ??0 11r0r-c t|sJddg} tjd|dddz}tj d|z|j d||D]6} tj d|ztj||d}|cStj dtj|D]F}|j} tj d|ztj||d}|cStd#tj $r?}tjd |ztj d d td d}~wwxYw#tj $r>}tjd|d|dtj d d Yd}~Xd}~wwxYw#tj $r:}tjd|ztj d d Yd}~Ed}~wwxYw)a Extract the data stored into a MSO/ActiveMime file, such as the ones created by Outlook in some cases, or Word/Excel when saving a file with the MHTML format or the Word 2003 XML format. :param data: bytes string, MSO/ActiveMime file content :return: bytes string, extracted data (uncompressed) raise a MsoExtractionError if the data cannot be extracted 2i*t|jtS)z returns True if string s only contains printable ASCII characters (i.e. contained in string.printable) This is similar to Python 3's str.isprintable, for Python 2.x. :param s: str :return: bool )setissubset_PRINTABLE_SET)ss r. is_printablers q6??> **r0c||z }ttjtj|d}t |dg}d|z }|}d|z dz}||||fS)aZ compute bit masks to decode a CopyToken according to MS-OVBA 2.4.1.3.19.1 CopyToken Help decompressed_current: number of decompressed bytes so far, i.e. len(decompressed_container) decompressed_chunk_start: offset of the current chunk in the decompressed container return length_mask, offset_mask, bit_count, maximum_length r(rir3)rmathceilrXmax)decompressed_currentdecompressed_chunk_start difference bit_count length_mask offset_maskmaximum_lengths r.copytoken_helprsj&(@@JDIIdhhz1567IYN#II%K,K )Q.N  Y >>r0c8t|ts t|}tjdj t |t}d}||}|dk7rt dj ||dz }|t |kr|}tjd|||dzd}|dzdz}|d z d z}|dk7r t d |d z dz}tjd j ||||dk(r|dkDrt d|z|dk(r|dk7rt d|z||zt |kDrtjdtt |||zg} |dz}|dk(r|j|||dz|dz }nt |} || kr||} |dz }tddD]} || k\rn| | z dz} | dk(r|j||g|dz }2tjd|||dzd}tt || \}}}}||zdz}||z}d|z }||z dz}t ||z }t|||zD]}|j||g|dz }|| kr|t |krt|S)a Decompress a stream according to MS-OVBA section 2.4.1 :param compressed_container bytearray: bytearray or bytes compressed according to the MS-OVBA 2.4.1.3.6 Compression algorithm :return: the decompressed container as a bytes string :rtype: bytes z-decompress_stream: compressed size = {} bytesrrzinvalid signature byte {0:02X}rr(ir3 rz9Invalid CompressedChunkSignature in VBA compressed streamz2chunk size = {}, offset = {}, compressed flag = {}iz:CompressedChunkSize=%d > 4098 but CompressedChunkFlag == 1z;CompressedChunkSize=%d != 4098 but CompressedChunkFlag == 0z3Chunk size is larger than remaining compressed datairrS)r? bytearrayrXrr:lenrrunpackwarningminextendxrangerbytes)compressed_containerdecompressed_containercompressed_currentsig_bytecompressed_chunk_startcompressed_chunk_header chunk_sizechunk_signature chunk_flagcompressed_endr flag_byte bit_indexflag_bit copy_tokenrrrrBlengthtemp1temp2r copy_sourceindexs r.decompress_streamr%s^: *I 6()=>II=DDSI]E^_`&[#$67H49@@JKK! s#78 8!3 MM$ 45KLbefLf g hij k .6!; 2b8D@ e #XY Y-3t;  FMMjZpr|}~ ?zD0Y\ffg g ?zT1Z]ggh h "J .5I1J J KKM Nc"679OR\9\]^3a7 ? # ) )*>?QRdgkRk*l m $ & (++A'B $$~5 11CD "a'"!'10I)^;!*Y 6!;H1}.557KL^7_6`a*a/*#MM$0DEWXjmnXn0opqrs#BP 679QBS> [)Q",{":a!? *[ 8 "Y"'5.A!5&)*@&AF&J %+Kv9M%N[E299;QRW;X:YZ[*a/*70%~5Y s#78 8` ' ((r0ceZdZdZdZy) VBA_Modulez| Class to parse a VBA module from an OLE file, and to store all the corresponding metadata and VBA source code. c ||_d|_d|_d|_d|_d|_d|_d|_d|_d|_ d|_ d|_ d|_ d|_ d|_d|_d|_d|_d|_ t'j(d|j+dd}|j-dd|t'j(d|j+d d}|j+|}|j/||_t1|j|_t'j(d|j+dd}|d k(rvt'j(d|j+d d}|j+|j3d d |_t'j(d|j+dd}|d k(rt'j(d|j+d d}|j+|}|j/||_t1|j|_t'j(d|j+dd} |j-dd| t'j(d|j+d d}|j+|j3d d |_t'j(d|j+dd}|dk(rt'j(d|j+d d}|j+|} |j/| |_t'j(d|j+dd} |j-dd| t'j(d|j+d d}|j+||_t'j(d|j+dd}|dk(rt'j(d|j+d d}|j-dd |t'j(d|j+d d|_ t'j(d|j+dd}|dk(rt'j(d|j+d d} |j-dd | t'j(d|j+d d} t'j(d|j+dd}|dk(rt'j(d|j+d d}|j-dd|t'j(d|j+dd} t'j(d|j+dd}|dk(s|dk(rW||_ t'j(d|j+d d} t'j(d|j+dd}|dk(rjd|_ t'j(d|j+d d} |j-dd| t'j(d|j+dd}|dk(rjd|_ t'j(d|j+d d} |j-dd| t'j(d|j+dd}|d k(r=t'j(d|j+d d} |j-d!d| d}|dk7r$t4j7d"j9|t4j;d#j9|jt4j;d$j9|j t4j;d%j9|jd}|j|j |j|jf}|D]s}| |j<d&z|z|_t4j;d'|j$z|j>jA|j$j+}n|at4jEd+||jFd,jId-|Dfz|jJrytMd.d&|jzt4j;d/j9tO|t4j;d0j9|j||jd}tO|dkDrtQtS|}||_ |j/||_t1|j|_|jjTjW|jjYd1}d2j9|j||_t1|j |_t4j;d3j9|j"yt4j7d4j9|j y#tB$r'}t4j;d(|d)|d*Yd}~d}~wwxYw#tZtLf$rt\$r7}t4jEd5j9||jFd6d}~wwxYw)7a Parse a VBA Module record from the dir stream of a VBA project. Reference: MS-OVBA 2.3.4.2.3.2 MODULE Record :param VBA_Project project: VBA_Project, corresponding VBA project :param olefile.OleStream dir_stream: olefile.OleStream, file object containing the module record :param int module_index: int, index of the module in the VBA project list NFrr(r MODULENAME_Id.Rs$%C)4&);%6%<%Csz[BASE]zlength of code_data = {0}zoffset of code_data = {0}vbaz{0}.{1}zextracted file {0}z(module stream {0} has code data length 0z Error parsing module {0} of {1}:r)/projectrname_str _name_unicode streamnamestreamname_str_streamname_unicode docstring_docstring_unicode textoffsetrreadonlyprivatecode_rawcodecode_strrb filename_str code_pathrrread check_value decode_bytesrPrRrXrr:rvba_rootole openstreamIOErrorr modules_countrArelaxedrzrrr module_extgetlowerr Exception)rdr dir_stream module_index_idsizemodulename_bytes section_idstreamname_bytesreserveddocstring_bytesmodulehelpcontext_size helpcontextcookie code_data try_namesrioefilextrFs r.r`zVBA_Module.__init__s    !"#' "&        b --jooa&89!DI' 2DM tZ__Q-?@CJV#}}T:??1+=>qA%/__T%:%A%A*i%X"#]]41CDQG V#}}T:??1+=>qA#-??4#8 ")"6"67G"H&1$//&B#!==zq/AB1E##$?R}}T:??1+=>qA+5??4+@+G+G T]+^(#]]41CDQG V#}}T:??1+=>qA",//$"7!(!5!5o!F!==zq/AB1E##$>Q}}T:??1+=>qA*4//$*?'#]]41CDQG V#}}T:??1+=>qA##$7F"(--jooa6H"I!"L#]]41CDQG V#*0tZ__Q=O)PQR)S&##$qA##$7FtZ__Q-?@C#]]41CDQG V#zV';' !==zq/AB1E#]]41CDQG V#!% !==zq/AB1E##$=vxP#]]41CDQG V# $ !==zq/AB1E##$"OP$+KK$:$:4>>$J$O$O$Q  8 O('*?*?HH%C8A%CCDDE??,Xv 7IJJ II188YH I II188I J!$//"23I9~!-i .BC ) #00; +DII 6 0044TYY__5FN * 1 1$))V D $/ $>! .55d6G6GHI FMMdNaNabc?#8 %0##7888@$%78    HH7f\7+@+@A"  $   sR]Fj$.j$1 j!:jj$j!!j$$k3<2k..k3Nrxr,r0r.rrs  Ir0rc2eZdZdZddZdZdZdZd dZy) VBA_Projectzy Class to parse a VBA project from an OLE file, and to store all the corresponding metadata and VBA modules. c'||_||_||_||_||_g|_i|_tjd|z|j|j}ttt|}||_tj d|jdd}|j#dd|tj d|jdd} |j#d d| tj d|jdd|_d d d d d} | j'|j$d|_tjd|j$|j(fz|j$| vr.tj+dj-|j$tj d|jdd} | dk(r| } |j#dd| tj d|jdd} |j#dd| tj d|jdd}tjdj-|tj d|jdd} | }|j#dd|tj d|jdd}|j#dd|tj d|jdd|_|j#dd|j.tj d|jdd}|j#dd|tj d|jdd}|j#dd|tj d|jdd|_|j#dd|j0tj d|jdd}|j#dd |tj d|jdd}|j#d!d|tj d|jdd|_t5j6|j2|_tjd"|j2d#|j8t5j:|j2|_tjd$|j2|j<fztj d|jdd}|j#d%d|tj d|jdd}tjd&|z|dks|d'kDr$tj+d(j-||j|}|j?||_ tj d|jdd}|j#d)d*|tj d|jdd}|d+kDr$tj+d,j-||j|}|j?||_!tj d|jdd}|j#d-d.|tj d|jdd}|dzdk7rtj+d/|j|}|jEd0d12|_#tj d|jdd}|j#d3d4|tj d|jdd}|d5kDr$tj+d6j-||j|} tj d|jdd}!|j#d7d8|!tj d|jdd}"|"|k7rtj+d9|j|"}#|#| k7rtj+d:tj d|jdd}$|j#d;d<|$tj d|jdd}%|j#d=d|%tj d|jdd}&|&}'tj d|jdd}(|j#d>d?|(tj d|jdd})|j#d@d|)tj d|jdd}*|j#dAd|*tj d|jdd}+|j#dBdC|+tj d|jdd},|j#dDd|,tj d|jdd}-tj d|jdd}.|-}'|.}'tj d|jdd}/|j#dEdF|/tj d|jdd}0|0dGkDr$tj+dHj-|0|j|0}1tj d|jdd}2|j#dIdJ|2tj d|jdd}3|3dzdk7rtj+dK|j|3}4|1}'|4}'dL}5 tj d|jdd}5tjdMj-|5|5dNk(ryL|5dOk(r|5}6tj d|jdd}7|j|7}8tjdPtI|j?|8ztj d|jdd}9|9dQk(rAtj d|jdd}:|j|:};|6}'|8}'|;}'1|9}5tjdMj-|5|5dRk(rq|5}tjdStI|j?|>z|<}'|>}'|5dTk(rM|5}?tj d|jdd}@tj d|jdd}A|j|A}BtjdUtI|j?|Bztj d|jdd}C|j#dVd|Ctj d|jdd}D|j#dWd|D|?}'|@}'|B}'tj d|jdd}E|EdOk(r|5}Ftj d|jdd}G|j|G}HtjdXtI|j?|Hztj d|jdd}I|IdQk(rhtj d|jdd}J|j|J}Ktj d|jdd}LF}'H}'|K}'nI}LnE}L|j#dYdZLtj d|jdd}Mtj d|jdd}N|j|N}Otj d|jdd}Ptj d|jdd}Q|jd[}Rtj d|jdd}S|M}'|O}'|P}'|Q}'|R}'|S}' |5d\k(r|5}Ttj d|jdd}Utj d|jdd}V|j|V}Wtjd]tI|j?|Wztj d|jdd}X|j#d^d|Xtj d|jdd}Y|j#d_d|Y|T}'|U}'|W}'7|5d`k(rZ|5}Ztj d|jdd}[tj d|jdd}\|j|\}]tjdatI|j?|]ztj d|jdd}^|j|^}_tjdbtI|j?|_ztj d|jdd}`tj d|jdd}a|Z}'|[}'|]}'|_}'|`}'|a}'tj+dcj-|5tK|ddde|5)fa Extract VBA macros from an OleFileIO object. :param vba_root: path to the VBA root storage, containing the VBA storage and the PROJECT stream :param project_path: path to the PROJECT stream :param relaxed: If True, only create info/debug log entry if data is not as expected (e.g. opening substream fails); if False, raise an error in this case zParsing the dir stream from %rrr(rPROJECTSYSKIND_IdrrrPROJECTSYSKIND_Sizez16-bit Windowsz32-bit Windows Macintoshz64-bit Windows)rrr(r3UnknownzPROJECTSYSKIND_SysKind: %d - %sz&invalid PROJECTSYSKIND_SysKind {0:04X}JPROJETCOMPATVERSION_IdPROJECTCOMPATVERSION_Sizez compat version: {compat_version})compat_versionPROJECTLCID_IdPROJECTLCID_SizePROJECTLCID_Lcidi PROJECTLCIDINVOKE_IdPROJECTLCIDINVOKE_SizePROJECTLCIDINVOKE_LcidInvokePROJECTCODEPAGE_Idr3PROJECTCODEPAGE_SizezProject Code Page:  - z.Python codec corresponding to code page %d: %sPROJECTNAME_IdzProject name size: %d bytesz=PROJECTNAME_SizeOfProjectName value not in range [1-128]: {0}PROJECTDOCSTRING_Idr4iz8PROJECTDOCSTRING_SizeOfDocString value not in range: {0}PROJECTDOCSTRING_Reserved@z3PROJECTDOCSTRING_SizeOfDocStringUnicode is not evenutf16rJrKPROJECTHELPFILEPATH_Idriz;PROJECTHELPFILEPATH_SizeOfHelpFile1 value not in range: {0}PROJECTHELPFILEPATH_Reserved=zVPROJECTHELPFILEPATH_SizeOfHelpFile1 does not equal PROJECTHELPFILEPATH_SizeOfHelpFile2zJPROJECTHELPFILEPATH_HelpFile1 does not equal PROJECTHELPFILEPATH_HelpFile2PROJECTHELPCONTEXT_IdrPROJECTHELPCONTEXT_SizePROJECTLIBFLAGS_IdrPROJECTLIBFLAGS_SizePROJECTLIBFLAGS_ProjectLibFlagsPROJECTVERSION_IdrPROJECTVERSION_ReservedPROJECTCONSTANTS_Idriz8PROJECTCONSTANTS_SizeOfConstants value not in range: {0}PROJECTCONSTANTS_Reserved<z3PROJECTCONSTANTS_SizeOfConstantsUnicode is not evenNzreference type = {0:04X}rzREFERENCE name: %s>3zREFERENCE original lib id: %s/z%REFERENCE control twiddled lib id: %sREFERENCECONTROL_Reserved1REFERENCECONTROL_Reserved2z*REFERENCE control name record extended: %sREFERENCECONTROL_Reserved30rS zREFERENCE registered lib id: %sREFERENCEREGISTERED_Reserved1REFERENCEREGISTERED_Reserved2z%REFERENCE project lib id absolute: %sz%REFERENCE project lib id relative: %sz#invalid or unknown check Id {0:04X}zreference type)rrPrRrSrXr[)&rr project_pathdir_pathrmodulesrrXrrr rrrrrrr syskindr syskind_namerr:lcid lcidinvokecodepager%get_codepage_name codepage_namecodepage2codeccodecr  projectnamerrRdocstring_unicoderPr)brdrrr\r]rdir_compressedrprojectsyskind_idprojectsyskind_size SYSKIND_NAME project_idprojectcompatversion_idprojectcompatversion_size"projectcompatversion_compatversionprojectlcid_idprojectlcid_sizeprojectlcidinvoke_idprojectlcidinvoke_sizeprojectcodepage_idprojectcodepage_sizeprojectname_idsizeof_projectnameprojectname_bytesprojectdocstring_id!projectdocstring_sizeof_docstringr projectdocstring_reserved)projectdocstring_sizeof_docstring_unicodedocstring_unicode_bytesprojecthelpfilepath_id$projecthelpfilepath_sizeof_helpfile1projecthelpfilepath_helpfile1projecthelpfilepath_reserved$projecthelpfilepath_sizeof_helpfile2projecthelpfilepath_helpfile2projecthelpcontext_idprojecthelpcontext_sizeprojecthelpcontext_helpcontextunusedprojectlibflags_idprojectlibflags_sizeprojectlibflags_projectlibflagsprojectversion_idprojectversion_reservedprojectversion_versionmajorprojectversion_versionminorprojectconstants_id!projectconstants_sizeof_constantsprojectconstants_constantsprojectconstants_reserved)projectconstants_sizeof_constants_unicode"projectconstants_constants_unicodecheck reference_idreference_sizeof_namereference_namereference_reservedreference_sizeof_name_unicodereference_name_unicodereferenceoriginal_id&referenceoriginal_sizeof_libidoriginalreferenceoriginal_libidoriginalreferencecontrol_idreferencecontrol_sizetwiddled%referencecontrol_sizeof_libidtwiddledreferencecontrol_libidtwiddledreferencecontrol_reserved1referencecontrol_reserved2check2&referencecontrol_namerecordextended_id/referencecontrol_namerecordextended_sizeof_name(referencecontrol_namerecordextended_name,referencecontrol_namerecordextended_reserved7referencecontrol_namerecordextended_sizeof_name_unicode0referencecontrol_namerecordextended_name_unicodereferencecontrol_reserved3referencecontrol_sizeextended%referencecontrol_sizeof_libidextendedreferencecontrol_libidextendedreferencecontrol_reserved4referencecontrol_reserved5 referencecontrol_originaltypelibreferencecontrol_cookiereferenceregistered_idreferenceregistered_size referenceregistered_sizeof_libidreferenceregistered_libidreferenceregistered_reserved1referenceregistered_reserved2referenceproject_idreferenceproject_size%referenceproject_sizeof_libidabsolutereferenceproject_libidabsolute%referenceproject_sizeof_libidrelativereferenceproject_libidrelativereferenceproject_majorversionreferenceproject_minorversionsb r.r`zVBA_Project.__init__~s  )     2X=>1668.y/HIJ $ #MM$ 0BCAF ,f6GH$mmD*//!2DEaH .8KL}}T:??1+=>qA """  ),,T\\9E 3t||TEVEV6WWX <<| + II>EEdllS T]]4);B!==zq/AB1E +V5EFMM$ (:;A>  +UDII> &}}T:??13EFqI /9MN!'tZ__Q5G!H!K 16;QR --jooa.@A!D 7P$]]41CDQG -v7IJ%}}T:??13EFqI /9MN dJOOA,>?B &88G $--ASASTU--dmm<  BdmmUYU_U_E``a  tZ__Q-?@C )6>B#]]41CDQG /2DDE  !%7#%= IIU\\]op q&OO,>?,,->? %mmD*//!2DEaH .8KL,2MM$ PQ@R,STU,V) ,t 3 IIJQQRst v %//*KL**?;$*MM$ 8J$KA$N! 4f>WX4:MM$ XYHZ4[\]4^1 4q 8A = IIK L#-//2["\!8!?!?PY!?!Z"(tZ__Q5G!H!K 16;QR/5}}T:??STCU/VWX/Y, /# 5 IIMTTUyz |(28\(]%'-}}T:??1;M'Nq'Q$ 7A]^/5}}T:??STCU/VWX/Y, /3W W IIn o(28\(]% (,I I IIb c!' dJOOA4F G J 0&:OP"(--jooa6H"I!"L 2FP*QRS*T' :FDcd#MM$ 0BCAF ,f6GH"(--jooa6H"I!"L 2FWX4:MM$ XYHZ4[\]4^1 4q 8A = IIK L-7__=f-g*+3MM$ (:;A>E II077> ? % (. dJOOA dJOO\]L^8_`a8b51;Af1g. AKPTPaPacAQBECCD-3]]4QRAS-TUV-W*  !=vGab-3]]4QRAS-TUV-W*  !=vGab,67tZ__Q-?@CV#=B:FLmmTXZdZiZijkZlFmnoFpC?IG@I<IIJ[))*RSNUUVCI==QUWaWfWfghWiCjklCm@CvMRXR_R_`dfpfufuvwfxRyz{R|OKU??SLUH5;]]4YZI[5\]^5_2!G!I!Q5a217.  !=vGab06 dJOOTUDV0WXY0Z-8> dJOO\]L^8_`a8b51;Af1g.-3]]4QRAS-TUV-W*-3]]4QRAS-TUV-W*3=??23F0*0--jooa>P*QRS*T'673390*/&+1==zq?Q+RST+U(39==zWXGY3Z[\3]0,6OO<\,]) ;k$J[J[\uJv>wwx06 dJOOTUDV0WXY0Z-  !@&Jgh06 dJOOTUDV0WXY0Z-  !@&Jgh/12',#(. dJOOA dJOO\]L^8_`a8b51;Af1g. AKPTPaPacAQBECCD8> dJOO\]L^8_`a8b51;Af1g. AKPTPaPacAQBECCD06 dJOOTUDV0WXY0Z-06 dJOOTUDV0WXY0Z-,.7766 II;BB5I J%h0@Bfhmn nr0c||k7rK|jr'tjdj|||yt |j |||y)Nz2invalid value for {0} expected {1:04X} got {2:04X})rrXrr:rr])rdrrrs r.r zVBA_Project.check_valuesL u || P!6$%8:*$--xOO r0c"|jj|j}i|_|D]}|j |}t j d|z|j}d|vsA|jdd\}}|j}|dk(r)|jddd}t|j|<|dk(rt|j|<|dk(rt|j|<|d k(st|j|<y ) zP Parse the PROJECT stream from the VBA project :return: z PROJECT: %r=rDocumentr}rModuleClass BaseClassN) rrr\rr rXrrhsplitrCLASS_EXTENSIONMODULE_EXTENSIONFORM_EXTENSION)rdproject_streamlinerrs r.parse_project_streamz VBA_Project.parse_project_streams,,T->->?," r0r)c#Ktjd|zt|||||}|j|j D] \}}}|||fyw)a  Extract VBA macros from an OleFileIO object. Internal function, do not call directly. vba_root: path to the VBA root storage, containing the VBA storage and the PROJECT stream vba_project: path to the PROJECT stream :param relaxed: If True, only create info/debug log entry if data is not as expected (e.g. opening substream fails); if False, raise an error in this case This is a generator, yielding (stream path, VBA filename, VBA source code) for each VBA code stream z relaxed is %sN)rXrr)rr) rrr\r]rrr rbr$s r. _extract_vbar5scIIo'(#xxIG   "*1*?*?*A/& 8Y(I../sAAc |jdd}|jdd}|jdd}|S#tjdt|zxYw)a Parse a VBA module code to detect continuation line characters (underscore) and collapse split lines. Continuation line characters are replaced by spaces. :param vba_code: str, VBA module code :return: str, VBA module code with long lines collapsed z _  z _ z _ ztype(vba_code)=%s)rJrXr`r)vba_codes r.vba_collapse_long_linesrIsd##Hc2##FC0##FC0 O )DN:; s 6:$Ac|j}d}|D]}|jdr d|vr|dz }ndj||d}|S)a Filter VBA source code to remove the first lines starting with "Attribute VB_", which are automatically added by MS Office and not displayed in the VBA Editor. This should only be used when displaying source code for human analysis. Note: lines are not filtered if they contain a colon, because it could be used to hide malicious instructions. :param vba_code: str, VBA source code :return: str, filtered VBA source code rz Attribute VB_:r N) splitlinesrrA)r vba_linesrCrrs r. filter_vbar\s`##%I E ??? +C4K QJE   ))Ief% &C Jr0cg}d}|rd|z}tjD]d\}}|D]Z}tjdtj|zdz|}|s5|j }|j |||zf\ftjD]Q\}}|D]G}tjd|zdz|}|s"|j }|j |||zfIS|S)am Detect if the VBA code contains keywords corresponding to macros running automatically when triggered by specific actions (e.g. when a document is opened or closed). :param vba_code: str, VBA source code :param obfuscation: None or str, name of obfuscation to be added to description :return: list of str tuples (keyword, description) r7 (obfuscation: %s)(?i)\b\b)AUTOEXEC_KEYWORDSitemsresearchescapegrouprAUTOEXEC_KEYWORDS_REGEX r obfuscationresultsobf_text descriptionkeywordskeywordr found_keywords r.detect_autoexecrts GH'+5!2!8!8!:H X HGIIi"))G*<!>!@H X HGIIi'1E98DE %  {X/EFG  HH Nr0cg}d}|rd|z}tjD]d\}}|D]Z}tjdtj|zdz|}|s5|j }|j |||zf\ftjD]Q\}}|D]G}tjd|zdz|}|s"|j }|j |||zfIStjD]=\}}|D]3}|j|vs|dk(r||j |||zf5?|S)a% Detect if the VBA code contains suspicious keywords corresponding to potential malware behaviour. :param vba_code: str, VBA source code :param obfuscation: None or str, name of obfuscation to be added to description :return: list of str tuples (keyword, description) r7rrrr3) SUSPICIOUS_KEYWORDSrrrrrrSUSPICIOUS_KEYWORDS_REGEXSUSPICIOUS_KEYWORDS_NOREGEXrrs r.detect_suspiciousrsoGH'+5!4!:!:!<H X HGIIi"))G*<?? @s7AB==C. C))C.cVd}|D]}|js||z }t|S)Nr7isdigitrinputresultr<s r. StripCharsr! s4 F  99; aKF v;r0c`d}|D]}|jr||z }|dz }t|S)Nr70rrs r.StripCharsWithZeror$ s= F  99; aKF cMF  v;r0ct|dd}t|t|dz dz t|dz }t|t|dz t|dz dz}||z }|dt|dz dz |t|dz dzdz}t|t|dz |dz z t|dz |dz z}|dt|dz |dz z |t|dz |dz zdz}tdt||Dcgc] }||||z }}d}|D]} |t t| |z z }|Scc}w)Nrr(rr7)r$rr!ranger^) inputTextwork strKeyEnc strKeySize nCharSize strKeyEnc2ra work_splitrrs r.DridexUrlDecoder/ so Qr?D"4TQ!(;c$i!m#MNI#D#d)a-3t9q=A:M$NOJY&I $#d)a-1$ %c$i!mq-@-A(B BDD#d)a-IaK!@3t9q=U^_`U`BabcJ 0#d)a-IaK0 1D#d)a-IVWK9X9Y4Z ZD/4QD 9/MN!$q9%NJNG53z%(3445 N Os?D5c|g}t}tj|D]i}|jdd}tj |s,||vs1 t t|}|j||f|j|k|S#t$r"}tjd|zYd}~d}~wwxYw)z Detect if the VBA code contains strings obfuscated with a specific algorithm found in Dridex samples. :param vba_code: str, VBA source code :return: list of str tuples (encoded string, decoded string) rrjzFailed to Dridex-decode (%s)N) rre_dridex_stringrrrrrUr/rrrrXrrs r.detect_dridex_stringsr22 sG EE!**84 @ a#%%e,    @#OE$:;w/0 %  @ N @ 83>?? @s8B B;B66B;c8g}t}|j}|jD]j}tj |D]P\}}}|||}|d}t |t s"||vs'||k7s-|j||f|j|Rl|S)z Detect if the VBA code contains strings obfuscated with VBA expressions using keywords such as Chr, Asc, Val, StrReverse, etc. :param vba_code: str, VBA source code :return: list of str tuples (encoded string, decoded string) r) r expandtabsr vba_expr_str scanStringr?rDrr) rrrvba_linerxrCrDencodedrs r.detect_vba_stringsr9K sG EE""$H'')'"."9"9("C ' FE3uS)GQiG'#67%'Gw,>NNGW#56IIg& ''$ Nr0c| |St|tttfr |St|trt r|j ||j||}||k7r\tjdj|t|tjdj|t||S|St|tr>t r8|j||}tjdj||St|tr>t s8|j ||}tjdj||St|tr|D]}t||||<|St|t t"fr|D] }t|}|Stjdjt%||S)z ensure there is no unicode in json and all strings are safe to decode works recursively, decodes and re-encodes every string to/from unicode to ensure there will be no trouble in loading the dumped json output z#json2ascii: replaced: {0} (len {1})z#json2ascii: with: {0} (len {1})zjson2ascii: encode unicode: {0}z1unexpected type in json2ascii: {0} -- leave as is)r?boolrfloatr~rMrRrNrXrr:runicoderdict json2asciilistrr)json_objrTrLdencodedjson_obj_bytes json_obj_strkeyitems r.r?r?p s P OO HtS%0 1 L OK Hc " x8??&QH8# ? &3x=9; ? &3x=9;OO Hg &7!6: 3::>JK He $Wx8  3::<HI Hd # 6C&x}5HSM 6 O HtEl + $Dd#D $ O E6$x.) + Or0c |r |r td|3t|ts#tdjt ||r|}t j t|dddj}|sy|rtd|dzntd |dz|d dD]}td|jz y) a line-wise print of json.dumps(json2ascii(..)) with options and indent+1 can use in two ways: (1) print_json(some_dict) (2) print_json(key1=value1, key2=value2, ...) This is compatible with :py:mod:`oletools.common.log_helper`: log messages can be mixed if arg `use_json` was `True` in :py:func:`log_helper.enable_logging` provided this function is called before the first "regular" logging with `_json_is_first=True` (and non-empty input) but after log_helper.enable_logging. zOInvalid json argument: want either single dict or key=value parts but got both)Nz^Invalid json argument: want either single dict or key=value parts but got {0} instead of dict)Fr)check_circularindent ensure_asciiz rz, r) rr?r>r:rjsondumpsr?rprintrstrip) json_dict_json_is_first json_partslinesrs r. print_jsonrS sZ9: :  *Y*EH &i13 3 JJz),Ue 55?Z\  hq!" hq!"ab ( h&'(r0c$eZdZdZdZddZdZy) VBA_Scannerz Class to scan the source code of a VBA module to find obfuscated strings, suspicious keywords, IOCs, auto-executable macros, etc. ct||_d|_d|_d|_d|_d|_d|_d|_d|_ d|_ d|_ d|_ d|_ d|_d|_d|_y)zg VBA_Scanner constructor :param vba_code: str, VBA source code to be analyzed r7N)rrcode_hex code_hex_rev code_rev_hex code_base64 code_dridexcode_vba strReverserautoexec_keywordssuspicious_keywordsiocs hex_stringsbase64_stringsdridex_strings vba_strings)rdrs r.r`zVBA_Scanner.__init__ s,H5    !%#'  ""r0c  t|j|_d|_d|jj vrd|_|jD]\}}|xj d|zz c_|js+|xj d|dddzz c_|xjdttj|dddzz c_t|j|_ |jD]\}}|xjd|zz c_ t|j|_|jD]\}}|xj d|zz c_|rt#|j|_ng|_|j$D]\}}|xj&d|zz c_g}g|_g|_g|_|jdf|j df|j df|jd f|jd f|j d f|j&d ffD]b\}}|xj(t/||z c_|xj*t1||z c_|xj,t3||z c_d|jr|j*j5d |jr|j*j5d|jr|j*j5d|j$r|j*j5dt7}|j(D]/\} } | |vs |j5d| | f|j9| 1t7}|j*D]/\} } | |vs |j5d| | f|j9| 1t7}|j,D]/\} } | |vs |j5d| | f|j9| 1|jD]'\}}|s t;|s|j5d||f)|jD]'\}}|s t;|s|j5d||f)|jD]'\}}|s t;|s|j5d||f)|j$D]'\}}|s t;|s|j5d||f)||_|S)a' Analyze the provided VBA code to detect suspicious keywords, auto-executable macros, IOC patterns, obfuscation patterns such as hex-encoded strings. :param include_decoded_strings: bool, if True, all encoded strings will be included with their decoded content. :param deobfuscate: bool, if True attempt to deobfuscate VBA expressions (slow) :return: list of tuples (type, keyword, description) (type = 'AutoExec', 'Suspicious', 'IOC', 'Hex String', 'Base64 String' or 'Dridex String') F strreverseTrNrjHexzHex+StrReversezStrReverse+HexBase64DridexzVBA expression)z Hex Stringsz`Hex-encoded strings were detected, may be used to obfuscate strings (option --decode to see all))zBase64 StringszcBase64-encoded strings were detected, may be used to obfuscate strings (option --decode to see all))zDridex StringszcDridex-encoded strings were detected, may be used to obfuscate strings (option --decode to see all))zVBA obfuscated StringszcVBA string expressions were detected, may be used to obfuscate strings (option --decode to see all)r SuspiciousIOCz Hex Stringz Base64 Stringz Dridex string VBA string)rrrar]rrWrXrYrUrqrrrbrZr2rcr[r9rdr\r^r_r`rrr rrrrr) rdinclude_decoded_strings deobfuscater8rrrr keyword_setrrrrs r.scanzVBA_Scanner.scan s.dii8 499??, ,do $ 0 0 Y GW MMTG^ +M!!TGDbDM%99!!!TIh6H6HQUSUQU6W,X%XX! Y4DII> $ 3 3 / GW   w .  /4DII> $ 3 3 / GW   w .  / 1$)).>*?A Ar0NFF)rhrirjrkr`rprsr,r0r.rUrU s  4hTAr0rUc8t|j||S)av Analyze the provided VBA code to detect suspicious keywords, auto-executable macros, IOC patterns, obfuscation patterns such as hex-encoded strings. (shortcut for VBA_Scanner(vba_code).scan()) :param vba_code: str, VBA source code to be analyzed :param include_decoded_strings: bool, if True all encoded strings will be included with their decoded content. :param deobfuscate: bool, if True attempt to deobfuscate VBA expressions (slow) :return: list of tuples (type, keyword, description) with type = 'AutoExec', 'Suspicious', 'IOC', 'Hex String', 'Base64 String' or 'Dridex String' )rUrp)rrmrns r.scan_vbarvc s x % %&={ KKr0ceZdZdZdddedfdZdZdZdZd Z d Z d Z d Z d Z d#dZdZdZdZdZdZdZdZd#dZdZdZdZdZd$dZdZdZdZdZd Z d!Z!d"Z"y)% VBA_ParserzZ Class to parse MS Office files, to detect VBA macros and extract VBA source code NTFc| |}d|_nt|}d|_d|_g|_||_||_||_d|_d|_d|_ d|_ d|_ d|_ d|_ d|_d|_d|_d|_d|_d|_d|_d|_d|_||_g|_d|_||_d|_d|_d|_d|_d|_tAjB|j||_"tFjId|jDjJjLd|jDj tOjP|r!|jS||jU|j&tWjX|r|j[||jB|%t]|d5}|j_}dddd |vr|ja|d |vr|jc||je} |jLd | vrHd | vrDd | vr@tg| jid | jid z dkr|jk|tmjn|dr/d|jz} tFjq| ts| |judr|jw||jd|vr|jy||j/d|jz} tFjq| ts| y#1swYaxYw)a Constructor for VBA_Parser :param str filename: filename or path of file to parse, or file-like object :param bytes data: None or bytes str, if None the file will be read from disk (or from the file-like object). If data is provided as a bytes string, it will be parsed as the content of the file in memory, and not read from disk. Note: files must be read in binary mode, i.e. open(f, 'rb'). :param str container: str, path and filename of container if the file is within a zip archive, None otherwise. :param bool relaxed: if True, treat mal-formed documents and missing streams more like MS office: do nothing; if False (default), raise errors in these cases :param str encoding: encoding for VBA source code and strings. Default: UTF-8 bytes strings on Python 2, unicode strings on Python 3 (None) raises a FileOpenError if all attempts to interpret the data header failed. NTFrrzftguess: file type=z - container=rbs4http://schemas.microsoft.com/office/word/2003/wordmls3http://schemas.microsoft.com/office/2006/xmlPackagesmimesversions multipartr7)treat_str_as_datazL%s is RTF, which cannot contain VBA Macros. Please use rtfobj to analyse it.sIDz;%s is not a supported file type, cannot extract VBA Macros.)= file_on_diskrole_file ole_subfilesrb containerrr vba_projects vba_formscontains_vba_macroscontains_xlm_macrosvba_code_all_modulesr^analysis_results nb_macros nb_autoexec nb_suspiciousnb_iocs nb_hexstringsnb_base64stringsnb_dridexstrings nb_vbastringsrT xlm_macrosno_xlm disable_pcodepcodedmp_outputvba_stomping_detected is_encryptedxlm_macrosheet_foundtemplate_injection_foundr&FileTypeGuesserftgrXrftyperolefile isOleFileopen_oleopen_pptzipfile is_zipfile open_openxmlr=r open_word2003xml open_flatopcrabsropen_mhtr"is_rtfrroropen_slk open_text) rdrbrrrrTr_file file_handledata_lowercaseras r.r`zVBA_Parser.__init__{ s2 <E $D DME %D    "   #' #' $(! $  ! !   *#%)"!$)!(-%**4==tD DHHNN)n,.N((4~7K7KG7TTUXZZ d#}}TT:egkgtgtt #C((u% d#yy WD%8t$ 99 ORVR_R__C HHSM$ $ O..s 'MM!cTtjd|jz tj|d|_t |_y#tttf$rG}tjd|jd|dtjdd Yd}~yd}~wwxYw) z| Open an OLE file :param _file: filename or file contents in a file object :return: nothing zOpening OLE file %sN) path_encodingzFailed OLE parsing for file rr4rTr) rXrrbr OleFileIOr~TYPE_OLErrrrr)rdrrFs r.rzVBA_Parser.open_ole sy &67 /#--e4HDM DIJ/ / HHdmmSQ R IIhI . . /s'A B' =B""B'c Dtjd|jz tj|}|j D]}tj dj||j|5}|jttj}|tjk(rVtj d|z|j|5}|j}ddd |j|ddd|j#t$|_y#1swYCxYw#t$rh}|jr@tj|d|dtj dd Yd}~dddQt!|j||d}~wwxYw#1swYzxYw#t$r^}|jrGtjd j||jtj dd nYd}~yd}~wt(tj*tj,t.f$rG}tjd |jd |dtj dd Yd}~yd}~wwxYw)z Open an OpenXML file :param _file: filename or file contents in a file object :return: nothing zOpening ZIP/OpenXML file %szOpenXML subfile {}zOpening OLE file %s within zipNrbrz is not a valid OLE file (r4rTrz4Error {0} caught in Zip/OpenXML parsing for file {1}z$Failed Zip/OpenXML parsing for file r)rXrrbrZipFilenamelistrr:r=r rrMAGICappend_subfiler]rrzclose TYPE_OpenXMLr RuntimeError BadZipfile LargeZipFiler)rdrzsubfilermagicole_datarFs r.rzVBA_Parser.open_openxml s .>?E /&A::<1 > .55g>?VVG_/> (,,S-?@E -< "BW"LMVVG_: '2'7'7'9H: > //x/PO/>/>1 >d GGI$DI:: 3>#|| #gWZ)[ \ # (T B (Y/>/>\'99<'>!> >Q/>/>f# ||O &dmm46 (T 23g00'2F2FP / HH!]]C1 2 IIhI . .  /sAGA%F9&D97F9E%G9E >F9 F6>F1 F9 GF11F66F99G >G JAH((0J=JJctjd|jz tj|}|j t D]z}|jtd}tj|j}t|r t|}|j||ctjd |z|t(|_y#t$rj}|j r=tjdj#||tj%ddnt'|j||Yd}~d}~wwxYw#t$rU}|j r>tjd |jd |d tj%ddnYd}~yd}~wt,$rG}tjd |jd |d tj%ddYd}~yd}~wwxYw) z| Open a Word 2003 XML file :param data: file contents in a string or bytes :return: nothing zOpening Word 2003 XML file %sz noname.msorError parsing subfile {0}: {1}rTrNz%s is not a valid MSO fileFailed XML parsing for file rr4)rXrrbET fromstringiter TAG_BINDATAr ATTR_NAMErqrvtextrrrr]rr:rrzTYPE_Word2003_XMLrr)rdretbindatafnamemso_datarrFs r.rzVBA_Parser.open_word2003xmlm s 04==@A( /t$B77;/ C I|<#..w||<x( P#3H#=++U+JHH9EAB+ C.*DI/P<<HH%E&,fUC&8:IIhI>"4T]]E3"OO? P# ||$--QTUV (T 23 / HHdmmSQ R IIhI . .  /sPA-E C0&E  E  A EE E  E G9A F&& G92=G44G9c tjd|jz tj|}|j t D]}|jtd}|jtd}|tk(s9|jtD]5} tj|j}|j!||7t,|_y#t"$rj}|j$r=tjdj'||tj)ddnt+|j||Yd}~d}~wwxYw#t"$rU}|j$r>tjd |jd |d tj)ddnYd}~yd}~wt0$rG}tjd |jd |d tj)ddYd}~yd}~wwxYw) z Open a Word or PowerPoint 2007+ XML file, aka "Flat OPC" :param data: file contents in a string or bytes :return: nothing z,Opening Flat OPC Word/PowerPoint XML file %sunknownrrrTrNrrr4)rXrrbrrr TAG_PKGPARTr ATTR_PKG_NAMEATTR_PKG_CONTENTTYPECTYPE_VBAPROJECTiterfindTAG_PKGBINDATArqrvrrr]rr:rrzTYPE_FlatOPC_XMLrr) rdrrpkgpartr content_typerrrFs r.rzVBA_Parser.open_flatopc s ?$--OP" /t$B77;/ T M9=&{{+?K #33#*#3#3N#C T T'/':':7<<'HH //X/N T  T )DI 3T#|| #)I*0&*<!> # (T B&8s&S S!C T# ||$--QTUV (T 23 / HHdmmSQ R IIhI . .  /sVA"EE 2C"E" E+A E EEE H!A F11 H==G??Hctjd|jz |jd}|j d}|j d}d|cxkr|kr nn||d}n |dkDr||d}t j j}tjd}|t j _ trt j|}nt j|}|t j _|jD]}|j} |jd} tj!d| d | |j#d } t%| t&r@t)| r5tj!d  t+| } |j-| | tj!dt5| z tj!d| ddzt8|_y#|t j _wxYw#t.$ra} |j0r3tj| d| dtj!dd nt3|j| | Yd} ~ kd} ~ wwxYw#t6$r }tj!dYd}~d}~wwxYw#t.$rt:$rCtjd|jdt<tj!dd YywxYw)zt Open a MHTML file :param data: file contents in a string or bytes :return: nothing zOpening MHTML file %ss sMIMEsContentrjNz)^(From |[\041-\071\073-\176]{1,}:?|[\t ])zMHTML part: filename=z, content-type=T)rRz4Found ActiveMime header, decompressing MSO containerrz$ does not contain a valid OLE file (r4rrztype(part_data) = %szpart_data[0:20] = %rrr7zpart_data has no __getitem__zFailed MIME parsing for file r<)rXrrblstripfindemail feedparserheaderRErcompilerMmessage_from_stringmessage_from_byteswalkget_content_type get_filenamer get_payloadr?rrrrr]rrzrr TYPE_MHTMLrMSG_OLEVBA_ISSUES)rdr stripped_data mime_offsetcontent_offset oldHeaderRE loosyHeaderREmhtmlrrr part_datarrFerrs r.rzVBA_Parser.open_mht s (4==89G /!KK 3M(,,W5K*// ;NK1>1 -kl ;  "$ -no >  **33KJJ'STM(5E   % 8!55mDE"44]CE,7  ) B#446 ))$/ |\] ,,D,9 i/K 4JIITU P#3I#>++U+JII4tIFGB "89Qr?"JK= BD#DII-8  )*/P<<HH).&56IIhI>#5T]]E3"OO? P%B "@AAB#   / HH!]],=? @ IIhI . /sBJ/:1G<+B&J/H0"J/J. J/<HJ/ JAI;5J/;JJ/ J, J'!J/'J,,J//ALLctjd tj|jd}|j D]}|j d|dtjd|jjd|_t|_ y#tjtf$rL}|jdk(rtjdntjd |zYd}~yYd}~yd}~wwxYw) a try to interpret self.ole_file as PowerPoint 97-2003 using PptParser Although self.ole_file is a valid olefile.OleFileIO, we set self.ole_file = None in here and instead set self.ole_subfiles to the VBA ole streams found within the main ole file. That makes most of the code below treat this like an OpenXML file and only look at the ole_subfiles (except find_vba_* which needs to explicitly check for self.type) zCheck whether OLE file is PPTT) fast_failN PptParser)rz File is PPTzPPT subfile is not a PPT filez&File appears not to be a ppt file (%s))rXrr rr~ iter_vba_datarrTYPE_PPTrPptUnexpectedDatarrr)rdpptvba_datarFs r.rzVBA_Parser.open_ppt s 01 J&&t}}EC--/ K##D(k#J K HH] # MM   ! DM DI,,j9 J~~, 9: BSHII; JsB B##D<=DDc tjd|jzd}g}|jd|j dD]!}|j dr@|j dD]+}|j dsd}tjd-U|j d r_|r]|j dD]H}|j d s|jd k7s)|jd t|d d zJ|j ds|s|j dD]4}|j ds|jdt|d d z6$|rd|_ ||_ t|_ y )z Open a SLK file, which may contain XLM/Excel 4 macros :param data: file contents in a bytes string :return: nothing zOpening SLK file %sFz8Formulas and XLM/Excel 4 macros extracted from SLK file:O;ETzSLK parser: found macro sheetsNNNzNamed cell: %srNCzFormula or Macro: %s)rXrrbrrrrrrhrUrrTYPE_SLKr)rdrxlm_macro_foundrrrs r.rzVBA_Parser.open_slk5 sc &67 TUOOE* UDt$D)CA||D)*. "ABC'OD)OA||D)aggi5.@"))*:Yqu=M*MNO&?D)UA||D)"))*@9QqrUCS*STU U" '+D $(DO r0ctjd|jzt||_d|_t |_y)z Open a text file containing VBA or VBScript source code :param data: file contents in a string or bytes :return: nothing zOpening text file %sTN)rXrrbrUrr TYPE_TEXTr)rdrs r.rzVBA_Parser.open_textW s7 '$--78%.dO!#'  r0c |jjt||||j|j|j y)zR Create sub-parser for given subfile/data and append to subfiles. )rrTrN)rrrxrrTr)rdrbrrs r.rzVBA_Parser.append_subfilef s>   HdI48LL59]]:>:L:L"N Or0ctjd|j|jtk7ry|j |j S|jtk(rbtj dg|_|jD]+}|j j|j-|j Sd}g|_|j}|jddD]}tjd|z|d jd k(s2tjd d j|zd j|dd }|d k7r|d z }tjd|z|||d}|s|||d}|s|||d}|stjd|z|j j|||f|j S)aO Finds all the VBA projects stored in an OLE file. Return None if the file is not OLE but OpenXML. Return a list of tuples (vba_root, project_path, dir_path) for each VBA project. vba_root is the path of the root OLE storage containing the VBA project, including a trailing slash unless it is the root of the OLE file. project_path is the path of the OLE stream named "PROJECT" within the VBA project. dir_path is the path of the OLE stream named "VBA/dir" within the VBA project. If this function returns an empty list for one of the supported formats (i.e. Word, Excel, Powerpoint), then the file does not contain VBA macros. :return: None if OpenXML file, list of tuples (vba_root, project_path, dir_path) for each VBA project found if OLE file zVBA_Parser.find_vba_projectsN,Returned info is not complete for PPT types!c||z}|j|r?|j|tjk(rtj d|d||Stj d|zy)NzFound z stream: z.check_vba_stream s[ ;.Izz)$i)@GDXDX)X ; JK   X[ffgr0FTstreamsstoragesChecking storage %rrjVBAzFound VBA storage: %sr}r7zChecking vba_root="%s"PROJECTzVBA/_VBA_PROJECTzVBA/dirzVBA root storage: "%s")rXrr~rrrrrrfind_vba_projectslistdirupperrAr) rdrrrstoragerr\vba_project_pathr]s r.rzVBA_Parser.find_vba_projectso s" 01 == TYY(%:    ($$ $ 99  KKF G "D ,, F!!(()B)B)DE F$$ $  mm{{54{@ MG II+g5 6r{  "e+ 1SXXg5FGH88GCRL1r>OH 2X=> 0XyI #X#3CCU#V '+C9E 2X=>!!(((L()KL1 M2   r0cj|j}d}|js|j}|xs|S)a/ Detect the potential presence of VBA or Excel4/XLM macros in the file, by calling detect_vba_macros and detect_xlm_macros. (if the no_xlm option is set, XLM macros are not checked) :return: bool, True if at least one VBA project has been found, False otherwise F)detect_vba_macrosrdetect_xlm_macros)rdrxlms r. detect_macroszVBA_Parser.detect_macros s6$$&{{((*C sr0ctjd|j |jS|jg|jD]P}tjdj ||j |_|jsId|_yd|_y|j}t|dk(rd|_nd|_|j}tt|jD]a}tjd|z|j|}|&|j|}tjd|jtjk(sqtjd |j |j"fz |j%|j&|j"j)}tjd t|zt|d kDr!tj|dd d |ddntjt+|d|vrtjdd|_d|jS#t,$rr}|j.r:tj1d|j ztjddn!t3|j4|j |Yd}~d}~wwxYw)a Detect the potential presence of VBA macros in the file, by checking if it contains VBA projects. Both OLE and OpenXML files are supported. Important: for now, results are accurate only for Word, Excel and PowerPoint Note: this method does NOT attempt to check the actual presence or validity of VBA macro source code, so there might be false positives. It may also detect VBA macros in files embedded within the main file, for example an Excel workbook with macros embedded into a Word document without macros may be detected, without distinction. :return: bool, True if at least one VBA project has been found, False otherwise zdetect vba macrosNzole subfile {}TFrChecking DirEntry #%d$This DirEntry is an orphan or unusedz,Reading data from stream %r - size: %d bytesz Read %d bytesdz...[much more data]...is AttributzFound VBA compressed codez Error when reading OLE Stream %rr) exc_trace)rXrrr~rr:rrrrr direntries_load_direntry entry_typerrrr_open isectStartr reprrrrrzrb)rd ole_subfilerrsiddrrFs r.rzVBA_Parser.detect_vba_macros s\ %&  # # /++ + == #00  *11+>?%)[[ "002/3D,  (-D $--/ |  !',D $'+D $mm#cnn-. MC II-3 4s#Ay&&s+ @A||w333 HAFFTUTZTZK[[\M99Q\\166:??ADIIoD 9:4y3 $t*dSVSWj"YZ $t*-&$. "=>370) M6''' M||!Caff!LM (d ;0LL<MsCI)) K$2A'KK$ctjd|j |jS|jtk(r |jSg|_|j jsd|_ytr3|jstjdn |jS|jy|jS#t$rtjdY=wxYw)a Detect the potential presence of Excel 4/XLM macros in the file, by checking if it contains a macro worksheet. Both OLE and OpenXML files are supported. Only Excel files may contain XLM macros, and also SLK and CSV files. If XLMMacroDeobfuscator is available, it will be used. Otherwise plugin_biff is used as fallback (plugin_biff only supports OLE files, not XLSX or XLSB) :return: bool, True if at least one macro worksheet has been found, False otherwise zdetect xlm macrosFzcXLMMacroDeobfuscator only works with files on disk, not in memory. Analysis might be less complete.z'Error when running XLMMacroDeobfuscator)rXrrrrrris_excelXLMDEOBFUSCATORr}r_extract_xlm_xlmdeobfrrr~_extract_xlm_plugin_biffrrs r.rzVBA_Parser.detect_xlm_macros! s %&  # # /++ + 99 ++ +xx  "',D $ $$ BCI5577 == ,,.. !IIIGHIsC C-,C-ctjddt_dg}tj|j ddddd}t |dk(rd|_y||z }|jd|jd tj|j dddd }||z }tj|||_ d|_y) z Run XLMMacroDeobfuscator to detect and extract XLM macros :return: bool, True if at least one macro worksheet has been found, False otherwise z=Calling XLMMacroDeobfuscator to detect and extract XLM macrosTzRAW EXCEL4/XLM MACRO FORMULAS:r)filenoninteractivenoindentreturn_deobfuscatedtimeout extract_onlyrFzL- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - z3EMULATION - DEOBFUSCATED EXCEL4/XLM MACRO FORMULAS:)r.r/r0r1r2) rXrxlmdeobfuscatorSILENT process_filerbrrrr)rdrr s r.r+z VBA_Parser._extract_xlm_xlmdeobfI s QR!%/0 --4==:>48?C358< - v;! ',D $ v  9 HI --4==:>48?C35 - v  ##' r0cftjddD]k}|jj|s tjd|z|jj |j }tjd t |g|d}|j|_ddj|jvrtjd t |g|d }|xj|jz c_t |g|d }|xj|jz c_t |g|d }|xj|jz c_d |_ y nd|_ y#tjdtzYxYw)z Run plugin_biff to detect and extract XLM macros :return: bool, True if at least one macro worksheet has been found, False otherwise r,)WorkbookBookzFound Excel stream %rz Running BIFF plugin from oledumpz -o BOUNDSHEET)rstreamoptionszExcel 4.0 macro sheetrzFound XLM macrosz-o LABEL -r LNz-c -r LNz -o DCONN -sTz;Error when running oledump.plugin_biff, please report to %sF) rXrr~rrr rAnalyzerrArr`URL_OLEVBA_ISSUES)rd excel_streamr biff_plugins r.r,z#VBA_Parser._extract_xlm_plugin_biffp sn ,-0 uL}}##L1 1L@A}}// =BBD <=u#(l^DRa"bK&1&9&9&;DO.$))DOO2LL "45&+,Vf&g ;+>+>+@@&+,V`&a ;+>+>+@@',,Vc&d ;+>+>+@@370#M u4$) uMM"_bs"sts C3FF0cz|jr$tj|j|_|jSr+)r~r#rrrs r.detect_is_encryptedzVBA_Parser.detect_is_encrypted s, == & 3 3DMM BD    r0cd}|jrStj}|r!t|tr|j |tj |j|}|Sr+)rAr#DEFAULT_PASSWORDSr?r@rdecryptrb)rdpasswords_listdecrypted_file passwordss r. decrypt_filezVBA_Parser.decrypt_file sR  # # %00I*^T"B  0#^^DMM9ENr0cX|j|S|j|jdS)a) Encode a unicode string to bytes or str, using the specified encoding for the VBA_parser. By default, it will be bytes/UTF-8 on Python 2, and a normal unicode string on Python 3. :param str unicode_str: string to be encoded :return: encoded string rJrK)rTrN)rd unicode_strs r. encode_stringzVBA_Parser.encode_string s. ==  %%dmmI%F Fr0c #Ktjd|j|jtk(r'|j d|j |j fy|jtk(r3|jrd}|jD] }|d|zdzz }ddd|fy|jD]}|jD]}||jr'd}|jD] }|d|zdzz }ddd|fyyy|jt}|jD]o\}}} t|j||||jD]B\} } }|j!|jj#| |j | | |fDq|j} t)t+| j,D]} tjd | z| |vrtjd 6| j,| }|&| j/| }tjd |j0t2j4k(stjd |j6z| j9|j:|j<j?}tAjBd |t@jDD]z}|jGdz }tjd|z||d} tItK|}tM|d}|j |j6|j6|f||jr&d}|jD] }|d|zdzz }ddd|f|jOr5d}|jPjSD] }|d|zdzz }ddd|fyy#t$$r } tj'dYd} ~ d} ~ wwxYw#t$$rU}tjd|j6d|j d|dtjddYd}~}d}~wwxYww)a Extract and decompress source code for each VBA macro found in the file Iterator: yields (filename, stream_path, vba_filename, vba_code) for each VBA macro found If the file is OLE, filename is the path of the file. If the file is OpenXML, filename is the path of the OLE subfile containing VBA macros within the zip archive, e.g. word/vbaProject.bin. If the file is PPT, result is as for OpenXML but filename is useless zextract_macros:Nr7z' r xlm_macroz xlm_macro.txtzError in _extract_vbarzAlready extractedrzReading data from stream %rs\x00Attribut[^e])flagsr3z%Found VBA compressed code at index %Xcp1252)rTzError processing stream z in file rr4 Traceback:Trz VBA P-codezVBA_P-code.txt)*rXrr~rrrbrrrrextract_macrosrrrrrr_findrr`rrrr r!rrrr"r#rr rr IGNORECASErCrrrUdetect_vba_stompingrr)rdrrr%rvba_stream_idsrr\r]r vba_filenamerrr&r'rrrCcompressed_codevba_code_bytes vba_code_strrFs r.rQzVBA_Parser.extract_macros s #$ == yyI%}}b$--9R9RSSh&??!H $7 D4K$$667& _hOO$(#4#4&K#.#=#=#?&% &&??!H $7 D4K$$667& _hOO ##$  " " $ UN484E4E ;0, ;(,)14<<AS; \8'**4==+>+>{+KL#}}k<RR S ; --Cc#..12! C 1C78.(II12NN3'9**3/AIIDE<<7#7#77II;affDE99Q\\166:??AD!#-A4r}}!]C %  1 "IE"QR*.uv,C->y?Y-ZN,5^h+WL#'==!&&!&&,"OOC! CF OO3Dt d 22H3"K(KK'') 00;;=3Dt d 22H3#\3CXNN *a!;MM"9::;F )CIIVWV\V\^b^k^kmp&qrIIlTIBBCsfD.Q1A(OB6QB/QAO0A=Q O- O("Q(O--Q0 Q9A Q Q QQc|j@g|_|jD]&\}}}}|jj||||f(t|j|_|jS)af Extract and decompress source code for each VBA macro found in the file by calling extract_macros(), store the results as a list of tuples (filename, stream_path, vba_filename, vba_code) in self.modules. See extract_macros for details. :returns: list of tuples (filename, stream_path, vba_filename, vba_code) )r^rQrrr)rdrrrVrs r.extract_all_macroszVBA_Parser.extract_all_macrossm << DLFJFYFYF[ XBk< ##[+|X$VW XT\\*||r0cd}|jD]5\}}}}t|tstj d.||dzz }7|S)aT Extract the VBA macro source code from all modules, and return it as a single string (str) with all modules concatenated. If an exception is triggered when decompressing a VBA module, it will not be included. The error is logged but the exception is not raised further. :return: str r7z7VBA code returned by extract_all_macros is not a stringr)r[r?r~rXr)rdrrBrs r.get_vba_code_all_modulesz#VBA_Parser.get_vba_code_all_modules(sY "#'#:#:#< 8 Q1hh, ST$47$  8 $#r0c|jre|j |jS|jF|j|_|j D]\}}}|xj|dzz c_ t |j}|j |||_|jrTtjdd}d}|jj||f|jjd||f|jrTtjdd}d}|jj||f|jjd||f|jrTtjd d }d }|jj||f|jjd||f|j\}} } } } } }|xj |z c_|xj"| z c_|xj$| z c_|xj&| z c_|xj(| z c_|xj*| z c_|xj,|z c_|jS) a runs extract_macros and analyze the source code of all VBA+XLM macros found in the file. All results are stored in self.analysis_results. If called more than once, simply returns the previous results. :return: list of tuples (type, keyword, description) (type = 'AutoExec', 'Suspicious', 'IOC', 'Hex String', 'Base64 String' or 'Dridex String') rz*adding VBA stomping to suspicious keywordsz VBA StompingzwVBA Stomping was detected: the VBA source code and P-code are different, this may have been used to hide malicious coderjz2adding XLM macrosheet found to suspicious keywordsz XLM macroz.XLM macro found. It may contain malicious codez0adding Template Injection to suspicious keywordszTemplate Injectionz^Template injection found. A malicious template could have been uploaded from a remote location)rrrr]extract_form_stringsrUrprTrXrr_rrrrrsrrrrrrr)rdshow_decoded_stringsrnrB form_stringscannerrrautoexec suspiciousr` hexstrings base64stringsdridex vbastringss r.analyze_macroszVBA_Parser.analyze_macros:s=    $$0,,,((0,0,I,I,K)+/+D+D+FD'Q;--t1CC-D"$";";.wsSI]^_I`Ear0T)rEreverserlr7rTz"%s"r[r4z(%s))risortedrrrrJrendswith)rdanalysis deobf_codekw_typerr8s r.revealzVBA_Parser.revealrs&&E&B((akop-T-F-FG  + )1 B %GWg,&"//#t4 7*%%c*w/?/?/D$w.G'//A  Br0c 4tjd|j|jtk7ry|jtk(r"|j }tj dn |jg}g|_|D] }|jddD]}tjd|z|dgz}|d gz}tjd |d |d |j|sU|j|tjk(sx|j|s|j|tjk(sd j|}tjd|z|jj| |jS)aK Finds all the VBA forms stored in an OLE file. Return None if the file is not OLE but OpenXML. Return a list of tuples (vba_root, project_path, dir_path) for each VBA project. vba_root is the path of the root OLE storage containing the VBA project, including a trailing slash unless it is the root of the OLE file. project_path is the path of the OLE stream named "PROJECT" within the VBA project. dir_path is the path of the OLE stream named "VBA/dir" within the VBA project. If this function returns an empty list for one of the supported formats (i.e. Word, Excel, Powerpoint), then the file does not contain VBA forms. :return: None if OpenXML file, list of tuples (vba_root, project_path, dir_path) for each VBA project found if OLE file zVBA_Parser.find_vba_formsNrFTr r rNfzChecking if streams z and z existr}zFound VBA Form: %r)rXrr~rrrrrrrrrrrAr)rd ole_filesrro_streamf_stream form_paths r.find_vba_formszVBA_Parser.find_vba_formssP" -. == TYY(%:$ 99 ))I KKF G)I 3C;;ut;D 3 /'9:"cU?"cU? 8XVW::h'CLL,BgFZFZ,ZJJx(S\\(-CwG[G[-[ # 1III2Y>?NN))'2 3 3~~r0c#K|j?|jtk(ry|jD]}|j D]}|y|j |j}|j D]}|dgz}tjddj|z|j|j}tj|D]}tjd|jztr|j}n!|jj!dd}|d k7sg|j"dj||fyw) a2 Extract printable strings from each VBA Form found in the file Iterator: yields (filename, stream_path, form_string) for each printable string found in forms If the file is OLE, filename is the path of the file. If the file is OpenXML, filename is the path of the OLE subfile containing VBA macros within the zip archive, e.g. word/vbaProject.bin. If the file is PPT, result is as for OpenXML but filename is useless Note: form_string is a raw bytes string on Python 2, a unicode str on Python 3 NrNzOpening form object stream %rr}z"Printable string found in form: %rr)rJrKTahoma)r~rrrr_rzrrXrrArr re_printable_stringrrrMrRrb) rdr%rr form_storagerw form_datam found_strs r.r_zVBA_Parser.extract_form_stringss> == yyI%$(#4#4&K#.#C#C#E&% &&    !--C $ M '3%/ 9CHHXE&%E&c#rK|j?|jtk(ry|jD]}|j D]}|y|j |j}|j D]=}tj||D]"}|jdj||f$?ywr|) r~rrrextract_form_strings_extendedrzrr!extract_OleFormVariablesrbrA)rdr%rrr~rs r.rz(VBA_Parser.extract_form_strings_extendeds == yyI%$(#4#4&K#.#L#L#N&% &&    !--C $ L ' @ @l SLH==#((<*@(KKL LsB5B7c|jttfvrd|_y|jrd|_y|jt j d ddlm}ttr t}n t}Gdd} t j d tj }|t_|j#|j$|| |t_t j d |j)|_|jS#t$r5}t jdj|d|_Yd}~yd}~wwxYw#t$r}t j'd Yd}~d}~wwxYw) z Extract and disassemble the VBA P-code, using pcodedmp :return: VBA P-code disassembly :rtype: str r7Nz:Calling pcodedmp to extract and disassemble the VBA P-coder)pcodedmpz%Exception when importing pcodedmp: {}ceZdZdZdZy)&VBA_Parser.extract_pcode..argsTFN)rhrirj disasmOnlyverboser,r0r.argsr7s ! r0rzbefore pcodedmp) output_filezafter pcodedmpzError while running pcodedmp)rrrrrrXrrrrr:r[rMrrsysstderr processFilerbr`getvalue)rdrroutputrrs r. extract_pcodezVBA_Parser.extract_pcode sF 999- -#%D    #%D     ' IIR S -    "   > +,# $$T]]Df$M#  *+$*??#4D ###] @GGJK')$  F > <==  >s1D A.E E%+EE F&FFctjd|j |jS|jtt fvrd|_y|j stjdd|_y|jBtjd|jtjdt}|jjD]1}|jdstjd|jz|jdd }|d }d }t|d k(r|d j}|d vrM|jdr|dd}|jdd d }|jds|j!||dk(s|jdd d }t|d k\r1|d dk(r|ddk(sJ|d d}|j#dd}d|zdz}|j!|4tjdt%t'|zd|_|j)}|D]H} | |vstjdj+| tjdd|_n|jstjd|jS)z Detect VBA stomping, by comparing the keywords present in the P-code and in the VBA source code. :return: True if VBA stomping detected, False otherwise :rtype: bool rTNFz see doc there N)r_rr`)rdrrerfs r.r`zVBA_Parser_CLI.__init__s nd,d=f=r0ctjjr+tddtjj |j ||y)ap Analyze the provided VBA code, without printing the results (yet) All results are stored in self.analysis_results. :param show_decoded_strings: bool, if True hex-encoded strings will be displayed with their decoded content. :param deobfuscate: bool, if True attempt to deobfuscate VBA expressions (slow) :return: None Analysis... r7rDN)rstdoutisattyrMflushri)rdr`rns r. run_analysiszVBA_Parser_CLI.run_analysiss? ::    /r * JJ    0+>r0c|j}|rtjdd}dddd}|D]]\}}}t|s t |}t|s t |}|j |d} |j |||f| ddf _|j|jr td yytd y) a print the analysis results in a table :param show_decoded_strings: bool, if True hex-encoded strings will be displayed with their decoded content. :param deobfuscate: bool, if True attempt to deobfuscate VBA expressions (slow) :return: None ) r7-)TypeKeyword Description) column_width header_rowyellowredcyanrrjrkN)colorszVBA Stomping detection is experimental: please report any false positive/negative at https://github.com/decalage2/oletools/issuesz#No suspicious keyword or IOC found.) rr TableStreamrr$r write_rowrrrM) rdr`rnrrK COLOR_TYPErrrr color_types r.print_analysiszVBA_Parser_CLI.print_analysiss'' ''\3UWA%#J 29 ^-+#G,"7mG#K0"&{"3K'^^GT:  Wg{;ZQUW[D\ ] ^ GGI))Z[* 7 8r0c  tjjr+tddtjj |j ||Dcgc]\}}}t |||c}}}Scc}}}w)ax Analyze the provided VBA code, and return the results in json format :param vba_code: str, VBA source code to be analyzed :param show_decoded_strings: bool, if True hex-encoded strings will be displayed with their decoded content. :param deobfuscate: bool, if True attempt to deobfuscate VBA expressions (slow) :return: dict rr7r)rrr)rrrrMrrir>)rdr`rnrrrrs r.print_analysis_jsonz"VBA_Parser_CLI.print_analysis_jsonsv ::    /r * JJ   595H5HI]_j5kmm1GWk'7 Lm mms A>c |j}|rBdddd}|D]7\}}}|j|d}|s|j|d|d|d|d}9|S) z Colorize keywords found during the VBA code analysis :param vba_code: str, VBA code to be colorized :return: str, VBA code including color tags for Colorclass rrrrNz{auto}z{/)rrrJ)rdrrrrrrrrs r.colorize_keywordsz VBA_Parser_CLI.colorize_keywordssq '' $#J 29 p-+'^^GT: '//jZacm9noH pr0c ||_|r|sd}|jr|jd|j} n |j} tdtd| z td|jz|j rl|j |||jD]'\} } } } |r t| }n| }tdtd| ztd | d t| |sOtd |jd k(r td yd|vr]tjdtjjrt!j"d}nd}|j%d|} tjjr$t!j"|j'|}t|*|j-D];\} } }| tdtd| d | td t|= |j/D]M\} } }| tdtd|dd| d | td tt1|dO |r1tdtd|j9}t||s|j;|||r0tdt|j=n tdtd y#t($rtj+dYdwxYw#t2$r9}tj5d|ztj7ddYd}~d}~wwxYw#t>$rt2$rl}tj5d|jd |d!tAjBtj7ddtE|j|d}~wwxYw)"a Process a single file :param filename: str, path and filename of file on disk, or within the container. :param data: bytes, content of the file if it is in a container, None if it is a file on disk. :param show_decoded_strings: bool, if True hex-encoded strings will be displayed with their decoded content. :param display_code: bool, if False VBA source code is not displayed (default True) :param global_analysis: bool, if True all modules are merged for a single analysis (default), otherwise each module is analyzed separately (old behaviour) :param hide_attributes: bool, if True the first lines starting with "Attribute VB" are hidden (default) :param deobfuscate: bool, if True attempt to deobfuscate VBA expressions (slow) :param show_pcode bool: if True, call pcodedmp to disassemble P-code and display it :param no_xlm bool: if True, don't use the BIFF plugin to extract old style XLM macros Tz in zO===============================================================================zFILE: %szType: %sr`rnzO-------------------------------------------------------------------------------z VBA MACRO %s z in file: z - OLE stream: zN- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - r7z (empty macro)r3z]The VBA code contains special characters such as backspace, that may be used for obfuscation.s{autored}\x08{/red}z\x08z;Unicode conversion to be fixed before colorizing the outputNzVBA FORM STRING IN zVBA FORM Variable "rz" IN rzError parsing form: %srPrzP-CODE disassembly:zAMACRO SOURCE CODE WITH DEOBFUSCATED VBA STRINGS (EXPERIMENTAL): zNo VBA or XLM macros found.Error processing file rr4)#rrrbrMrrrr[rr$rhrXrrrr colorclassColorrJr UnicodeErrorrr_rr~rrrrrrsr] traceback print_excrs)rdr` display_codehide_attributes vba_code_onlyshow_deobfuscated_codern show_pcoderdisplay_filenamerrrVrvba_code_filtered backspaceraform_variablesrFpcodes r.r6zVBA_Parser_CLI.process_files}( L >>-1]]DNNK #}}  h j++,[ 6 *dii' (!!#!!7KYd!eJNJaJaJc!5F[+|X&,6x,@),4)(O/L89KkIZ[\#i(,224:!/2 &):: # -L!M#&::#4#4#60:0@0@AX0YI07I4E4M4MfV_4` 1i$'::#4#4#68B8H8HI_I_`qIr8s$5""34C!5D@D?X?X?Z+;[+{".h+Wbcdi(k* + ;FJFhFhFj@Bk>)5!(O!UcdjUkmx{F#GH!),!#nW&=">? @(O/0 ..0E%L%''(