|idZddlmZdZddlZddlZddlZddlZddlZddl Z ddl Z ddl Zddl m Z ej ej ej eZej ej edZeej vrej deddlmZddlmZdd lmZmZddlZdd lmZGd d ej Z!ej"d zfdZ#e#dZ$dZ%dZ&de&zdzZ'dZ(dZ)de(zdze)zdzZ*ej+e*Z,dZ-ej+e-Z.dZ/ej+e/Z0de'zdze-zdze*zdzZ1de%ze1zdze%zd zZ2ej+e2Z3ej+e1Z4ej+d!Z5ej+d"Z6d"Z7d#Z8d$Z9d%Z:ej+e8e9ze:ze7zZ;ej+e7e2zZ<ej+d&Z=e>d'Z?ej@dd(kr d)ZAd*ZBd+ZCeDZEn#eFd)ZAeFd*ZBeFd+ZCeGZEd,ZHd-ZIGd.d/eJZKGd0d1eJZLGd2d3eJZMGd4d5eLZNd@d7ZOdAd9ZPdBd<ZQdCd=ZRd>ZSeTd?kr eSdSdS)DaW rtfobj.py rtfobj is a Python module to extract embedded objects from RTF files, such as OLE ojects. It can be used as a Python library or a command-line tool. Usage: rtfobj.py rtfobj project website: http://www.decalage.info/python/rtfobj rtfobj is part of the python-oletools package: http://www.decalage.info/python/oletools )print_functionz0.60.1N)time..)xglob) tablestream)oleobjftguess)clsidceZdZdZdZdS) NullHandlera Log Handler without output, to avoid printing messages if logging is not configured by the main application. Python 2.7 has logging.NullHandler, but this is necessary for 2.6: see https://docs.python.org/2.6/library/logging.html#configuring-logging-for-a-library cdSN)selfrecords =C:\PYTHON\_runtimes\venv\Lib\site-packages\oletools/rtfobj.pyemitzNullHandler.emit N)__name__ __module__ __qualname____doc__rrrrr r s-      rr c |tjjjvr+tj|}|||Stj|}|t|||S)an Create a suitable logger object for this module. The goal is not to change settings of the root logger, to avoid getting other modules' logs on the screen. If a logger exists with same name, reuse it. (Else it would have duplicate handlers and messages would be doubled.) The level is set to CRITICAL+1 by default, to avoid any logging. )loggingLoggermanager loggerDict getLoggersetLevel addHandlerr )namelevelloggers r get_loggerr&s w~%000"4((  t $ $F kmm$$$ OOE Mrrtfobjs [0-9A-Fa-f]s[{](?:\\.|[^{}\\])*[}]s[{](?:\\.|[^{}\\]|s)*[}]s([a-zA-Z]{1,250})s(-?\d+)s(?:\\s(?:s(?=[^0-9])|(?=[^a-zA-Z0-9])|$))s(?:\\[^a-zA-Z])s[^{}\\]+s(?:\s||s)*s\b(?:s){7,}s\bs\d+s [ \t\r\n\f\v]s[ \t\r\n\f\v]*s\\bins (\d{1,250})z(?i)\.(BAT|CLASS|CMD|CPL|DLL|EXE|COM|GADGET|HTA|INF|JAR|JS|JSE|LNK|MSC|MSI|MSP|PIF|PS1|PS1XML|PS2|PS2XML|PSC1|PSC2|REG|SCF|SCR|SCT|VB|VBE|VBS|WS|WSC|WSF|WSH)\b(saftncnsaftnsepsaftnsepcs annotations atnauthorsatndatesatnids atnparentsatnrefsatrfends atrfstartsauthors backgroundsbkmkends bkmkstartsblipuidsbuptimscategoryscolorschememappingscolortblscommentscompanyscreatims datafields datastoresdefchpsdefpapsdosdoccommsdocvars dptxbxtextsebcendsebcstarts factoidnamesfaltsfcharss ffdeftexts ffentrymcrs ffexitmcrsffformats ffhelptextsfflsffnames ffstattextsfieldsfilesfiletblsfldinstsfldrsltsfldtypesfontembsfonttblsfootersfooterfsfooterlsfooterrsfootnotes formfieldsftncnsftnsepsftnsepcgs generatorsgridtblsheadersheaderfsheaderlsheaderrshlshlfrs hlinkbaseshllocshlsrcshsvsinfoskeywordss latentstylesslcharss levelnumberss leveltextslfolevelslinkvalslists listlevelslistnames listoverrideslistoverridetables listpictures liststylenames listtableslisttextslsdlockedexceptsmaccsmaccPrs mailmergesmalnScrsmanagersmargPrsmbarsmbarPrsmbaseJcsmbegChrs mborderBoxs mborderBoxPrsmboxsmboxPrsmchrsmcountsmctrlPrsmdsmdegsmdegHidesmdensmdiffsmdPrsmesmendChrsmeqArrsmeqArrPrsmfsmfNamesmfPrsmfuncsmfuncPrs mgroupChrs mgroupChrPrsmgrowsmhideBots mhideLefts mhideRightsmhideTopsmlimsmlimLocsmlimLows mlimLowPrsmlimUpps mlimUppPrsmmsmmaddfieldnames mmathPictsmmaxDistsmmcsmmcJcs mmconnectstrsmmconnectstrdatasmmcPrsmmcss mmdatasourcesmmheadersources mmmailsubjectsmmodsos mmodsofiltersmmodsofldmpdatasmmodsomappednames mmodsonamesmmodsorecipdatas mmodsosorts mmodsosrcs mmodsotables mmodsoudls mmodsoudldatasmmodsouniquetagsmmPrsmmquerysmmrsmnarysmnaryPrsmnoBreaksmnumsmobjDistsmoMaths moMathParas moMathParaPrsmopEmusmphantsmphantPrsmplcHidesmpossmrsmradsmradPrsmrPrsmsepChrsmshowsmshpsmsPresmsPrePrsmsSubsmsSubPrsmsSubSups msSubSupPrsmsSupsmsSupPrs mstrikeBLTRsmstrikeHs mstrikeTLBRsmstrikeVsmsubsmsubHidesmsupsmsupHidesmtranspsmtypesmvertJcsmvfmfsmvfmlsmvtofsmvtolsmzeroAscs mzeroDescsmzeroWidsnesttablepropss nonesttablessobjaliassobjclassobjdatasobjectsobjnamesobjsects oldcpropss oldppropss oldspropss oldtpropssoleclsidsoperatorspanosespasswords passwordhashspgpspgptblspicpropspictspnspnseclvlspntextspntxtaspntxtbsprintimspropnamesprotends protstarts protusertblsresultsrevtblsrevtimsrxesshpsshpgrpsshpinstsshppictsshprsltsshptxtssnssps staticvals stylesheetssubjectssvssvbstcstemplates themedatastitlestxesudsuprs userpropss wgrffmtfilters windowcaptionswritereservationswritereservhashsxesxforms xmlattrnames xmlattrvaluesxmlclosesxmlnamesxmlnstblsxmlopensmargSzspnaiuspnaiud\{}s{\rtc|}d}|dkr!|dz}d}|dkr|dz}d}|dkr|dz}d}d ||S) z? create a human-readable string representation of duration [s] sZgN@minhHg8@daysz {0:.1f}{1})format)durationvalueunits r duration_strr:_sg E D rzz   2:: SLEDrzz    ud + ++rceZdZdZddZdS) DestinationzD Stores the data associated with a destination control word NcL||_d|_d|_d|_d|_dS)Nrr)cworddatastartend group_level)rr>s r__init__zDestination.__init__us,   rrrrrrrCrrrr<r<qs2rr<ceZdZdZdZdZdZdZdZdZ dZ d Z d Z d Z d Zd ZdZdZdZdZdZdZdZdZdS) RtfParserzs Very simple but robust generic RTF parser, designed to handle malformed malicious RTF as MS Word does c||_d|_t||_d|_t }|g|_||_dS)zy RtfParser constructor. :param data: bytes object containing the RTF data to be parsed rN)r?indexlensizerBr< destinationscurrent_destination)rr?document_destinations rrCzRtfParser.__init__sJ   II *}}12#7   rc tt}||ks |jdkrdSd|jz|jz }||z t|jz }t|j|jz |z}tdt||z ||jt|dS)z1 report progress on parsing at regular intervals rNgY@zSAfter {0} finished {1:4.1f}% of current file ({2} bytes); will finish in approx {3})floatrrJrHlogdebugr6r:)r start_timenow percent_donetime_per_index finish_estims r_report_progresszRtfParser._report_progresssDFFmm *   Q Fdj(494  *eDJ.?.??TY344~E  .6,s:~66 )\,%?%?AA B B B B Brcd|_t}|}|j|jkrt|z dkr#||t}|j|jt kr%||xjdz c_|j|jtkr%||xjdz c_|j|jtkr_|j|j|jdz}|dz }t |}|r| d}d}t|dkr| d}|||||xjt| z c_|dkr|||t" |j|j}|rF|||xjt| z c_Dt& |j|j}|rF|||xjt| z c_t+d |jz|dS) zF Parse the RTF data :return: nothing rr Nr+sbinz.Should not have reached this point - index=%Xh)rHrrJrWr? BRACE_OPEN _open_group BRACE_CLOSE _close_group BACKSLASHre_control_wordmatchgrouprIgroups _control_word_binre_control_symbolcontrol_symbolre_text_text RuntimeError _end_of_file)rrR last_report data_croppedmr>params rparsezRtfParser.parses VV  j49$$vv #b((%%j111"ff y$ 22  """ a y$ 33!!### a y$ 11 $yDJsN)BC $ #)),77 GGAJJE E188::** ! &&q%777JJ#aggii..0JJ !U+++%++DItzBB''***JJ#aggii..0JJ di44A  1  c!''))nn, ORVR\\]] ] rcN|xjdz c_|dSNr)rB open_grouprs rr]zRtfParser._open_groups. A rcdSrrrus rrtzRtfParser.open_group rc||j|jjkr|n |xjdzc_dSrs) close_grouprBrL_close_destinationrus rr_zRtfParser._close_groups`   t7C C C  # # % % % %  ArcdSrrrus rryzRtfParser.close_grouprwrc`|jj|jkr|t|}|j|_|j|||_|jt|z|_ | |jdSr) rLrBrzr<rKappendrHrIrcr@open_destination)r matchobjectr>new_dests r_open_destinationzRtfParser._open_destination s  # /43C C C  # # % % %u%%#/   ***#+ c+*;*;*=*=&>&>> d677777rcdSrrr destinations rr~zRtfParser.open_destinationrrc|j|j_||jt |jdkr|jt |jdkr|jd|_dSdS)Nr)rHrLrAclose_destinationrIrKpoprus rrzzRtfParser._close_destinations(,z $ t7888 t ! !! # #   ! ! # # # t ! !A % %'+'8'rps rrezRtfParser._control_word.sf - - - IIaejlplvdww x x x  " "; 6 6 6 +ue44444rcdSrrrs rrzRtfParser.control_word8rrcdSrr)rrs rrhzRtfParser.control_symbol;rwrc|}|jxj|z c_|||dSr)rcrLr?textrrrs rrjzRtfParser._text?sD  ""  %%-%% +t$$$$$rcdSrrrs rrzRtfParser.textDrwrcJ|%td|jzd}nt|}|dkr$td|jzd}t |j|j|jdzt dkr*td|xjdz c_td|z|j|j|j|z}|xj|z c_||dS)NzDDetected anti-analysis trick: \bin object without length at index %XrzJDetected anti-analysis trick: \bin object with negative length at index %Xr z%\bin: ignoring whitespace before dataz%\bin: reading %d bytes of binary data)rPinforHintordr?rQbin)rrrpbinlenbindatas rrfzRtfParser._binHs  = HH\_c_ii j j jFFZZF A:: HHbeieoo p p pF tyDJN23 4 4C @ @ II> ? ? ? JJ!OJJ :VCDDD)DJtzF'::; f  rcdSrrrrs rrz RtfParser.bin_rrc|jdkrAtd|jz||jdkA|dS)NrzGroup Level = %d, closing group)rBrPrQr_ end_of_filerus rrlzRtfParser._end_of_filebsi"" II7$:JJ K K K      "" rcdSrrrus rrzRtfParser.end_of_filejrrN)rrrrrCrWrqr]rtr_ryrr~rzrrerrhrjrrfrrlrrrrrFrFsN 8 8 8 B B B???D            8 8 8         555      %%%    .        rrFceZdZdZdZdS) RtfObjectzI An object or a file (OLE Package) embedded into an RTF document cd|_d|_d|_d|_d|_d|_d|_d|_d|_d|_ d|_ d|_ d|_ d|_ d|_d|_d|_dS)z' RtfObject constructor NF)r@rAhexdatarawdatais_oleoledata format_id class_name oledata_size is_package olepkgdatafilenamesrc_path temp_pathftgr clsid_descrus rrCzRtfObject.__init__rs          rNrDrrrrrns-rrc@eZdZdZfdZdZdZdZdZdZ xZ S) RtfObjParserz7 Specialized RTF parser to extract OLE objects cftt||g|_dSr)superrrCobjects)rr? __class__s rrCzRtfObjParser.__init__s, lD!!**4000 rcd|jdkr$td|jzdSdS)Nr*z"*** Start object data at index %Xh)r>rPrQr@rs rr~zRtfObjParser.open_destinations9   * * II:[=NN O O O O O + *rc|jdkrrtd|jzt }|j||j|_|j|_|j dd}tj dd|}t|t|kr3tj dd|}td|zt|dzr$td |dd }||_tj|}||_t%j||_t-j} |||j|_|j|_|j|_|j |_t%j|j |_d |_|j !d d krt-j"|j d }|j#|_#|j$|_$|j%|_%|j |_&t%j|j |_'tQj)|j&|_*d |_+dStYj-|j rhtYj.|j } | j/j0|_0t`j12|j03d|_4dSdS# tdYdSxYwdS)Nr*z"*** Close object data at index %Xhs [^a-fA-F0-9]rs [a-fA-F0-9]z"Found non-hex chars in hexdata: %rrzOdd length, trimmed last byte.rTpackage)rpackage)r?zMunknown CLSID (please report at https://github.com/decalage2/oletools/issues)z*** Not an OLE 1.0 Object)5r>rPrQrHrrr}r@rAr? translateresubrIrbinascii unhexlifyrhashlibmd5 hexdigest rawdata_md5r OleObjectrqrr data_sizerr oledata_md5rlowerrstripOleNativeStreamrrrrolepkgdata_md5r FileTypeGuesserrrolefile isOleFile OleFileIOrootr KNOWN_CLSIDSgetupperr) rrr'hexdata1rnonhex object_dataobjopkgoles rrzRtfObjParser.close_destinations   * * II:TZG H H H[[F L   ' ' '&,FL$FJ"'11$GGHf_c8<> >GHHH7||a ' :;;;!#2#,$FN",W55K(FN!([!9!9!C!C!E!EF "$$C 7 +&&&#&= $'N!&)m#!$%,[%:%:%D%D%F%F" $ >''))0077:EE!1#(:>@@@D&*mFO&*mFO'+~F$(, F%,3K ,B,B,L,L,N,NF)!(!8f>O!P!P!PFJ(,F%%%(22m%/99'*x~ ,1,>,B,B6r?rhexlifyrs rrzRtfObjParser.binsE  # )Z 7 7  $ ) )X-=g-F-F F ) ) ) ) 8 7rcdSrrrs rrzRtfObjParser.control_words rc|dd}|dkr|j|jdz|jdz}|xjdz c_|jjdkrlt jdd|jj|j_t|jjdzdkr$|jjdd|j_dSdSdSdS) Nrr+'r*rrr)rcr?rHrLr>rrrI)rrsymbol hexdigitss rrhzRtfObjParser.control_symbols""$$QqS) T>> $*Q,tz!|";>484L4QRUSURU4VD,111# ><;?>r) rrrrrCr~rrrrh __classcell__)rs@rrrs PPP 575757nGGG    WWWWWWWrr c#Kt|d}t|}||jD]"}|j|jz }|j||jfV#dS)a& [DEPRECATED] Backward-compatible API, for applications using the old rtfobj: Open a RTF file, extract each embedded object encoded in hexadecimal of size > min_size, yield the index of the object in the RTF file, the original length in the RTF file, and the decoded object data in binary format. This is an iterator. :param filename: str, RTF file name/path to open on disk :param min_size: ignored, kept for backward compatibility :returns: iterator, yielding tuples (start index, original length, binary data) rbN)openreadrrqrrAr@r)rmin_sizer?rtfprorig_lens rrtf_iter_objectsrs $   $ $ & &D   DJJLLL|//7SY&i3;.....//rFc|tt}t|trXt |d5}|tttkcdddS#1swxYwYt|t r(t|ts|d|tkSt|tr|d|tkSt|tr|r: |d| ddtkS#t$rYdSwxYwt |d5}|tttkcdddS#1swxYwYt|dr0|tttkSt|ttfrt|}n|}ttD])} t!||vrdS#t"$rYdSwxYwdS) an determine whether given file / stream / array represents an rtf file arg can be either a file name, a byte stream (located at start), a list/tuple or a an iterable that contains bytes. For str it is not clear whether data is a file name or the data read from it (at least for py2-str which is bytes). Argument treat_str_as_data clarifies. rNasciistrict)errorsFrT)rI RTF_MAGIC isinstance UNICODE_TYPErrbytesstr bytearrayencode UnicodeErrorhasattrlisttupleiterzipnext StopIteration)argtreat_str_as_data magic_lenreaderiter_arg magic_bytes ris_rtfrsII#|$$< #t__ <;;s9~~..); < < < < < < < < < < < < < < < <#u,jc&:&:,:I:)++#y!!,:I:)++#s @  @ :I:--gh-GG !!   uu c4 @F{{3y>>22i? @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @sF5xxI''944#e}%%99)nn  H~~Z//uu0   555  4sG0A77A;>A;?'D'' D54D50FF  F H** H98H9_cFtj|}t jd||}d|vr|dd}d|vd|vr|dd}d|vt|sd}|r |d|}|S)zcompute basename of filename. Replaces all non-whitelisted characters. The returned filename is always a basename of the file.z [^\w\.\- ]r.z rNONAMEN)ospathbasenamestriprrreplacerI)r replacement max_lengthbasepath sane_fnames rsanitize_filenamer?sw))//11H {H==J *  ''c22  *   *  ''c22  *   x== -  , rc|r~tj|s1td|ztj|tj|t|}nNtj|}t|}tj||}|"t|d }tdtd|t|fztjddtj}t!|} | | jD] } d} | jrd| jz} | jt*jjkr| d z } n%| jt*jjkr| d z } n| d z } | d | jzz } | j| d z } n | d| jzz } | jr1| dz } | d| jzz } | d| jzz } | d| jzz } | d| jzz } d} tj | j\} }t!d|ztj | j\} }t!d|z||kr| dz } tD#|stD#|rd} | dz } | d$| j%j&j'z } n | d| j(zz } | j)%| d| j)zz } | d| j*zz } d| j*vrd} | jdkrd} | dz } g}tWj,d }d!|-| j.D}|D]}d"|vr|/|tatc|}|r| d#d$|zz } n6| j23d%rd} | d&z } nd'} |4| j5| d(| j6z| fdd| f)|7"|r|d*kr| j}nA tq|}| j|g}n"#t9d+YdSxYw|D]x} |5| }| jrtd,|ztd-| jztd.| jztd/| jz| jr|d0t| j}n d1|| j6fz}td2|ztd3| jzt|d4:| j;| jr | jtd5|ztd6| jztd7| jztd8| jz| j2}|3d9rd:}n|3d;rd<}nd=}d>|| j6|fz}td2|ztd3| j(zt|d4:| j.td?|zd@|| j6fz}tdA|ztd3| j<zt|d4:| j=xdSdS)BNzcreating output directory %srzO===============================================================================zFile: %r - size: %d bytes) ?)idrHz OLE Object) column_width header_rowstylezformat_id: %d z (Embedded) z (Linked) z (Unknown) zclass name: %r zdata size: N/Az data size: %dz OLE Package object:z Filename: %rz Source path: %rz Temp path = %rz MD5 = %ryellowzTemp path extension: %rzFile extension: %rz MODIFIED FILE EXTENSIONredz EXECUTABLE FILEz File Type: {}z CLSID: %sz %sCVEsOLE2LinkzO Possibly an exploit for the OLE2Link vulnerability (VU#921560, CVE-2017-0199) s(?:[\x20-\x7E][\x00]){3,}c8g|]}|dS)zutf-16le)decode).0ws r z process_file..s$SSS!*--SSSrhttpzURL extracted: z, s equation.3zV Possibly an exploit for the Equation Editor vulnerability (VU#421280, CVE-2017-11882)zNot a well-formed OLE objectz%08Xh)colorsallzTThe -s option must be followed by an object index or all, such as "-s 2" or "-s all"z+Saving file from OLE Package in object #%d:z Filename = %rz Source path = %rz Temp path = %rrz%s_object_%08X.nonamez saving to file %sz md5 %swbz'Saving file embedded in OLE object #%d:z format_id = %dz class name = %rz data size = %dsworddocrrrz%s_object_%08X.%szSaving raw data in object #%d:z%s_object_%08X.rawz saving object to file %s)>r risdirrPrmkdirjoinrdirnamerrprintrIr TableStreamTableStyleSlimrrqrrrrr TYPE_EMBEDDED TYPE_LINKEDrrrrrrrsplitextrQre_executable_extensionsrbr6rftyper#rr rrcompilefindallrr}sortedsetr startswith write_rowrHr@ write_seprerrorwriterrr) containerrr? output_dir save_object fname_prefixbase_dirrtstreamrr' ole_color ole_columnrtemp_extfile_exturlspatwordsr%rifnamerexts r process_filerQUs :w}}Z(( ! HH3j@ A A A HZ w||J$5h$?$?AA 7??8,,&x00 w||Hj99  |Hd##((** &MMM %3t99(= =>>>% 0(G   DJJLLL,MM =C 8)F,<??? g..v?? 8 .9:::x''"==J+11(;;6?W?]?]^f?g?g6 %I"55J/66vz7G7LMMM lV-??? |'mfl:: fv'888 F--- %I K//! qq j!@AASSs{{6>7R7RSSS''A{{ Ac$ii((F"3diioo"EEJ"((**55mDD x! ww 7J L  v & & fl "   T9-     08 %  lGG  $$ LO-  pqqq& 8& 8F f%%A $ 8CaGHHH'&/9:::*V_<===(6+;;<<<?S'3||'8'I'I'IKEE4|V\6RRE+e3444j6#88999UD!!''(9:::: 86#6#B?!CDDD)F,<<===)F,==>>>)F,??@@@#.4466 ((11 CC**:66 #CCC+|V\3.OO+e3444j6#55666UD!!''77776:;;;, fl/KK2U:;;;j6#55666UD!!''7777a0808& 8& 8s "SScdtjddz}tdtd|dtdtdtd d }tjtjtjtjtj d }d }tj | }| dddd| dddddd| dddddd| ddd d!|d"#| d$d%d&ddd'| d(dd)d*d+| \}}t|dkr;tt|tjt j||jtjd,-t*tjt1jt5j||j|j|j.D]<\}}} |r|d/rtA||| |j!|j"0=dS)1Nz%d.%d.%drrzrtfobj z on Python z' - http://decalage.info/python/oletoolsz3THIS IS WORK IN PROGRESS - Check updates regularly!zGPlease report any issue at https://github.com/decalage2/oletools/issueswarning)rQrrTr?criticalz1usage: %prog [options] [filename2 ...])usagez-r store_true recursivez)find files recursively in subdirectories.)actiondesthelpz-zz--zip zip_passwordrziif the file is a zip archive, open first file from it, using the provided password (requires Python 2.6+))rZtypedefaultr[z-fz --zipfname zip_fname*zoif the file is a zip archive, file(s) to be opened within the zip. Wildcards * and ? are supported. (default:*)z-lz --loglevelloglevelstorezBlogging level debug/info/warning/error/critical (default=%default))rZrYr^r[z-sz--saverCz}Save the object corresponding to the provided number to a file, for example "-s 2". Use "-s all" to save all objects at once.z-drBz-use specified directory to save output files.)r]rZr[r^z%(levelname)-8s %(message)s)r$streamr6)rXr\r_/)rBrC)#sys version_infor0 __version__rDEBUGINFOWARNINGERRORCRITICALoptparse OptionParser add_option parse_argsrIr print_helpexit basicConfigrastdoutrPr!NOTSETrenable_loggingr iter_filesrXr\r_endswithrQrBrC) python_versionDEFAULT_LOG_LEVEL LOG_LEVELSrVparseroptionsargsrArr?s rmainrs#"21Q3"77N E KK )*** @AAA TUUU 2JJJ!MLOM$   J @E  " / / /F  d>>>LL    %*%5dgFW)W5F&H&H&H66! 8T  **3//  Y$7;M!(!4 6 6 6 6 6 66r__main__)r)F)rr )NF)Ur __future__rrgrr rerrrmros.pathrrnormpathabspathr/__file___thismodule_dirr. _parent_dirinsertoletools.thirdparty.xglobroletools.thirdparty.tablestreamroletoolsrr roletools.commonr Handlerr rlr&rP HEX_DIGITSINGLE_RTF_TAGNESTED_RTF_TAG ASCII_NAMESIGNED_INTEGER CONTROL_WORDr8raCONTROL_SYMBOLrgTEXTriIGNOREDPATTERN re_hexblockre_embedded_tags re_decimal re_delimiter DELIMITERDELIMITERS_ZeroOrMore BACKSLASH_BIN DECIMAL_GROUPre_delims_bin_decimalre_delim_hexblockr6 frozensetrrfr`r\r^unicoderrrrr:objectr<rFrrrrrrQrrrrrrs1  B&%%%%% 9888888888888888888888888888'""27??27??83L3L#M#MNNgrw||OTBBCC chHOOA{###++++++777777$$$$$$$$!!!!!!     '/   #+A-6j  /+>9(B" J&/.@Cee "*\**&BJ~.. "*T   ^ #D ( 7 <\ I% O( i ' )H 4y @6 Ibj!! 2:g&& RZ rz/00 ! .  " #8=#H%2$35>$?@@BJy7233&2:ghh&I/'//hA!IJKLLD ISJ#c((KL   , , ,$     &   .c c c c c c c c LFgWgWgWgWgW9gWgWgWX////(++++\,W8W8W8W8xG6G6G6T zDFFFFFr