MpjdZddlmZdZddlZddlZddlZddlZddlZddl Z ddl Z ddl Zddl m Z ejjejjejj!eZejjejj'edZeejvrejj+deddlmZddlmZdd lmZmZddlZdd lmZGd d ej@Z!ejDd zfdZ#e#dZ$dZ%dZ&de&zdzZ'dZ(dZ)de(zdze)zdzZ*ejVe*Z,dZ-ejVe-Z.dZ/ejVe/Z0de'zdze-zdze*zdzZ1de%ze1zdze%zd zZ2ejVe2Z3ejVe1Z4ejVd!Z5ejVd"Z6d"Z7d#Z8d$Z9d%Z:ejVe8e9ze:ze7zZ;ejVe7e2zZ<ejVd&Z=e>d'Z?ejdd(kr d)ZAd*ZBd+ZCeDZEneFd)ZAeFd*ZBeFd+ZCeGZEd,ZHd-ZIGd.d/eJZKGd0d1eJZLGd2d3eJZMGd4d5eLZNdd8ZQd?d9ZRd:ZSeTd;k(reSyy)@aW 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)clsidceZdZdZdZy) 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 cyN)selfrecords ]/mnt/ssd/data/Dropbox/adrian/scripts/msg_venv/lib/python3.12/site-packages/oletools/rtfobj.pyemitzNullHandler.emit N)__name__ __module__ __qualname____doc__rrrrr r s   rr c |tjjjvr(tj|}|j ||Stj|}|j t|j ||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&sr w~~%%000""4(   t $F km$ 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{\rtcv|}d}|dkDr|dz}d}|dkDr|dz}d}|dkDr|dz}d}d j||S) z? create a human-readable string representation of duration [s] sZgN@minhHg8@daysz {0:.1f}{1})format)durationvalueunits r duration_strr:_s^ E D rz   2: SLEDrz    ud ++rceZdZdZddZy) DestinationzD Stores the data associated with a destination control word NcJ||_d|_d|_d|_d|_y)Nrr)cworddatastartend group_level)rr>s r__init__zDestination.__init__us&   rrrrrrrCrrrr<r<qs rr<ceZdZdZdZdZdZdZdZdZ dZ d Z d Z d Z d Zd ZdZdZdZdZdZdZdZdZy) RtfParserzs Very simple but robust generic RTF parser, designed to handle malformed malicious RTF as MS Word does c||_d|_t||_d|_t }|g|_||_y)zy RtfParser constructor. :param data: bytes object containing the RTF data to be parsed rN)r?indexlensizerBr< destinationscurrent_destination)rr?document_destinations rrCzRtfParser.__init__s@   I *}12#7 rc tt}||k(s|jdk(ryd|jz|jz }||z t|jz }t|j|jz |z}tj dj t||z ||jt|y)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_progresssDFm *  Q djj(4994  *eDJJ.??TYY34~E  .6,s:~6 ))\,%?A Brcd|_t}|}|j|jkrt|z dkDr|j|t}|j|jt k(r&|j |xjdz c_|j|jtk(r&|j|xjdz c_|j|jtk(rE|j|j|jdz}|dz }tj|}|r|jd}d}t|jdkDr|jd}|j||||xjt|jz c_|dk(r|j!||t"j|j|j}|r?|j%||xjt|jz c_7t&j|j|j}|r?|j)||xjt|jz c_t+d |jz|j-y) 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 V  jj499$v #b(%%j1"f yy$ 2  " a yy$ 3!!# a yy$ 1 $yyDJJsNC $ #)),7GGAJE E188:* ! &&q%7JJ#aggi.0J !U+%++DIItzzB''*JJ#aggi.0J dii4A 1  c!'')n, ORVR\R\\] ] rcN|xjdz c_|jyNr)rB open_grouprs rr]zRtfParser._open_groups A rcyrrrus rrtzRtfParser.open_group rc|j|j|jjk(r|jn |xjdzc_yrs) close_grouprBrL_close_destinationrus rr_zRtfParser._close_groupsN    t77CC C  # # %  Arcyrrrus rryzRtfParser.close_grouprwrcr|jj|jk(r|jt|}|j|_|jj |||_|j t|jz|_ |j|jyr) rLrBrzr<rKappendrHrIrcr@open_destination)r matchobjectr>new_dests r_open_destinationzRtfParser._open_destination s  # # / /43C3C C  # # %u%#//   *#+ c+*;*;*=&>> d667rcyrrr destinations rr~zRtfParser.open_destinationrrc.|j|j_|j|jt |j dkDr|j j t |j dkDr|j d|_yy)Nr)rHrLrAclose_destinationrIrKpoprus rrzzRtfParser._close_destinationsz(,zz  $ t778 t  !! #    ! ! # t  !A %'+'8'8'rps rrezRtfParser._control_word.sN - - IIaejlplvlvdww x  " "; 6 +ue4rcyrrrs rrzRtfParser.control_word8rrcyrr)rrs rrhzRtfParser.control_symbol;rwrc|j}|jxj|z c_|j||yr)rcrLr?textrrrs rrjzRtfParser._text?s5  "   %%-% +t$rcyrrrs rrzRtfParser.textDrwrcL|%tjd|jzd}n t|}|dkr$tjd|jzd}t |j |j|jdzt dk(r*tj d|xjdz c_tj d|z|j |j|j|z}|xj|z c_|j|y)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_i_ii jFZF A: HHbeieoeoo pF tyyDJJN3 4C @ II> ? JJ!OJ :VCD))DJJtzzF':; f  rcyrrrrs rrz RtfParser.bin_rrc|jdkDrBtjd|jz|j|jdkDrB|j y)NrzGroup Level = %d, closing group)rBrPrQr_ end_of_filerus rrlzRtfParser._end_of_filebsT" II7$:J:JJ K    " rcyrrrus rrzRtfParser.end_of_filejrrN)rrrrrCrWrqr]rtr_ryrr~rzrrerrhrjrrfrrlrrrrrFrFsm 8 B?D      8   5  %  .  rrFceZdZdZdZy) 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|_y)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 c:tt| |g|_yr)superrrCobjects)rr? __class__s rrCzRtfObjParser.__init__s lD*40 rch|jdk(r#tjd|jzyy)Nr*z"*** Start object data at index %Xh)r>rPrQr@rs rr~zRtfObjParser.open_destinations-    * II:[=N=NN O +rc^|jdk(rtjd|jzt }|j j ||j|_|j|_|jjdd}tjdd|}t|t|kr/tjdd|}tjd|zt|dzrtjd |dd }||_tj |}||_t%j&|j)|_t-j.} |j1||j2|_|j4|_|j6|_|j|_t%j&|jj)|_d |_|j4jAjCd d k(rt-jD|jd }|jF|_#|jH|_$|jJ|_%|j|_&t%j&|jj)|_'tQjR|jL|_*d |_+ytYjZ|jrxtYj\|j} | j^j`|_0t`jbje|j`jgd|_4yyy# tjdYyxYw)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:TZZG H[F LL   '&,,FL$FJ"''11$GHff_c8 >GH7|a :;!#2,$FN",,W5K(FN!([!9!C!C!EF ""$C 7 +&#&== $'NN!&)mm#!$%,[[%:%D%D%F" $ >>'')007:E!11#((:>@D&*mmFO&*mmFO'+~~F$(, F%,3KK ,B,L,L,NF)!(!8!8f>O>O!PFJ(,F%((2%//9'*xx~~ ,1,>,>,B,B6<r?rhexlifyrs rrzRtfObjParser.bins?  # # ) )Z 7  $ $ ) )X-=-=g-F F ) 8rcyrrrs rrzRtfObjParser.control_words rc|jdd}|dk(r|j|jdz|jdz}|xjdz c_|jjdk(rt j dd|jj|j_t|jjdzdk(r)|jjdd|j_yyyy) Nrr+'r*rrr)rcr?rHrLr>rrrI)rrsymbol hexdigitss rrhzRtfObjParser.control_symbols""$Qq) T> $**Q,tzz!|484L4L4Q4QRUSU4VD,,1?< r) rrrrrCr~rrrrh __classcell__)rs@rrrs* P 57nG  Wrrc#Kt|dj}t|}|j|jD]6}|j |j z }|j ||jf8yw)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_objectsrsj $  $ $ &D  DJJL||/77SYY&ii3;;../sActt}t|tr;t |d5}|j tttk(cdddSt|t rt|ts |d|tk(St|tr |d|tk(St|tr[|r |d|jddtk(St |d5}|j tttk(cdddSt|dr%|j tttk(St|ttfr t|}n|}ttD]} t!||vryy#1swY7xYw#t$rYywxYw#1swYxYw#t"$rYywxYw) 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~.); < <#ujc&::I)++#y!:I)++#s  :I--gh-G !! c4 @F{{3y>2i? @ @sFxxI'944#e}%9)n  H~Z/0 ? < <    @ @  s;%F6F%F$6 F0F F! F!$F-0 F=<F=c"tjj|j}t j d||}d|vr|j dd}d|vrd|vr|j dd}d|vrt|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?sww)//1H {H=J * ''c2  *  * ''c2  *  x=  , rc^|rvtjj|s-tj d|ztj |tjj |t|}nJtjj|}t|}tjj ||}|t|dj}tdtd|t|fztjddtj}t!|} | j#| j$D]} d} | j&r(d| j(z} | j(t*j,j.k(r| d z } n2| j(t*j,j0k(r| d z } n| d z } | d | j2zz } | j4| d z } n| d| j4zz } | j6rE| dz } | d| j8zz } | d| j:zz } | d| j<zz } | d| j>zz } d} tjjA| j<\} }tjCd|ztjjA| j8\} }tjCd|z||k7r| dz } tDjG|stDjG|rd} | dz } | djI| jJjLjNz } n| d| jPzz } | jR4| d| jRzz } | d| jTzz } d| jTvrd} | j2dk(rd} | dz } g}tWjXd }|j[| j\Dcgc]}|j_d!}}|D]}d"|vs|ja|tcte|}|rK| d#d$j |zz } n3| j2jgjid%r d} | d&z } nd'} |jk| j$jm| d(| jnz| fdd| f)|jq|r|d*k(r | j$}n ts|}| j$|g}|D]v} |jm| }| j6rtd,|ztd-| j8ztd.| j:ztd/| j<z| j8r|d0t| j8}nd1|| jnfz}td2|ztd3| j>zt|d4jw| jx| j&r| j4td5|ztd6| j(ztd7| j2ztd8| j4z| j2jg}|jid9rd:}n|jid;rd<}nd=}d>|| jn|fz}td2|ztd3| jPzt|d4jw| j\ td?|zd@|| jnfz}tdA|ztd3| jzzt|d4jw| j|yyycc}w#tjud+YyxYw)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,}zutf-16lehttpzURL 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 = %r_z%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 r isdirrPrmkdirjoinrdirnamerrprintrIr TableStreamTableStyleSlimrrqrrrrr TYPE_EMBEDDED TYPE_LINKEDrrrrrrrsplitextrQre_executable_extensionsrbr6rftyper#rr rrcompilefindallrdecoder}sortedsetr startswith write_rowrHr@ write_seprerrorwriterrr) containerrr? output_dir save_object fname_prefixbase_dirrtstreamrr' ole_color ole_columnr"temp_extfile_exturlspatwwordsrifnamerexts r process_filerLUsww}}Z( HH3j@ A HHZ ww||J$5h$?A 77??8,&x0 ww||Hj9  |Hd#((* &M %3t9(= =>%% 0((G  DJJL,,M ==)F,<,<? gg..v? 8 .9:x'"==J+11(;?W?]?]^f?g %I"55J/66vzz7G7G7L7LMM lV-?-??? ||'mfll:: fv'8'888 F--- %I  K/! qq jj!@A7:{{6>>7RS!*-SS'A{ A'c$i("3diio"EEJ""((*55mD! ww 7J LL  v & fll "   T9-   [M\ % llG  $ LLO-& 8F f%A  CaGH'&//9:*V__<=(6+;+;;<??'3'8'IKE4|V\\6RRE+e34j6#8#889UD!''(9(9:6#6#6#B?!CD)F,<,<<=)F,=,==>)F,?,??@#..446 ((1C**:6#CC+|V\\3.OO+e34j6#5#556UD!''76:;, fll/KK2U:;j6#5#556UD!''7M& 8/T<  pqs5^5^^,cdtjddz}tdtd|dtdtdtd d }tj tj tjtjtjd }d }tj| }|jdddd|jdddddd|jdddddd|jddd d!|d"#|jd$d%d&ddd'|jd(dd)d*d+|j\}}t|dk(r3tt|j!tj"t j$||j&tj(d,-t*j-tj.t1j2t5j6||j8|j:|j<.D]>\}}} |r|j?d/rtA||| |jB|jD0@y)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)rQrrOr9criticalz1usage: %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+))rUtypedefaultrVz-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))rUrTrYrVz-sz--saver=z}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-dr<z-use specified directory to save output files.)rXrUrVrYz%(levelname)-8s %(message)s)r$streamr6)rSrWrZ/)r<r=)#sys version_infor) __version__rDEBUGINFOWARNINGERRORCRITICALoptparse OptionParser add_option parse_argsrIr print_helpexit basicConfigr\stdoutrPr!NOTSETrenable_loggingr iter_filesrSrWrZendswithrLr<r=) python_versionDEFAULT_LOG_LEVEL LOG_LEVELSrQparseroptionsargsr;rr?s rmainrzsH#"2"21Q"77N  )* @A TU 2J!MMLLOOMM$$  J @E  " " /F  dLL  %*%5%5dgFWFW))W5F5F&H6! 8T **3/ Y$7;M;M!(!4!4 6 6r__main__) )F)r")NF)Ur __future__rrbrr r`rrrhros.pathrr normpathabspathr(__file___thismodule_dirr' _parent_dirinsertoletools.thirdparty.xglobroletools.thirdparty.tablestreamroletoolsrr roletools.commonr Handlerr rgr&rP HEX_DIGITSINGLE_RTF_TAGNESTED_RTF_TAG ASCII_NAMESIGNED_INTEGER CONTROL_WORDr1raCONTROL_SYMBOLrgTEXTriIGNOREDPATTERN re_hexblockre_embedded_tags re_decimal re_delimiter DELIMITERDELIMITERS_ZeroOrMore BACKSLASH_BIN DECIMAL_GROUPre_delims_bin_decimalre_delim_hexblockr/ frozensetrrar`r\r^unicoderrrrr:objectr<rFrrrrrrLrzrrrrrs- B& 9888''""277??277??83L#MNggrww||OTBC chhHHOOA{#+7$! '// #++A-6  /+>9(B" J&/.@Cee "**\*&BJJ~. "**T  ^ #D ( 7 <\ I% O( i ' )H 4y @6 Ibjj! 2::g& RZZ rzz/0 ! .  " #8=#H%2$35>$?@BJJy723&2::gh&/'/hA!IJKLD ISJc(KL   ,$ & .c c LFgW9gWX/(+\,W8xG6T zFr