()
pdf-parser, use it to parse a PDF document
pdf-parser, use it to parse a PDF document
pdf - parser use it to parse a PDF document
def Main():
    """pdf-parser, use it to parse a PDF document
    """

    oParser = optparse.OptionParser(usage='usage: %prog [options] pdf-file|zip-file|url\n' + __description__, version='%prog ' + __version__)
    oParser.add_option('-s', '--search', help='string to search in indirect objects (except streams)')
    oParser.add_option('-f', '--filter', action='store_true', default=False, help='pass stream object through filters (FlateDecode, ASCIIHexDecode, ASCII85Decode, LZWDecode and RunLengthDecode only)')
    oParser.add_option('-o', '--object', help='id of indirect object to select (version independent)')
    oParser.add_option('-r', '--reference', help='id of indirect object being referenced (version independent)')
    oParser.add_option('-e', '--elements', help='type of elements to select (cxtsi)')
    oParser.add_option('-w', '--raw', action='store_true', default=False, help='raw output for data and filters')
    oParser.add_option('-a', '--stats', action='store_true', default=False, help='display stats for pdf document')
    oParser.add_option('-t', '--type', help='type of indirect object to select')
    oParser.add_option('-v', '--verbose', action='store_true', default=False, help='display malformed PDF elements')
    oParser.add_option('-x', '--extract', help='filename to extract malformed content to')
    oParser.add_option('-H', '--hash', action='store_true', default=False, help='display hash of objects')
    oParser.add_option('-n', '--nocanonicalizedoutput', action='store_true', default=False, help='do not canonicalize the output')
    oParser.add_option('-d', '--dump', help='filename to dump stream content to')
    oParser.add_option('-D', '--debug', action='store_true', default=False, help='display debug info')
    oParser.add_option('-c', '--content', action='store_true', default=False, help='display the content for objects without streams or with streams without filters')
    oParser.add_option('--searchstream', help='string to search in streams')
    oParser.add_option('--unfiltered', action='store_true', default=False, help='search in unfiltered streams')
    oParser.add_option('--casesensitive', action='store_true', default=False, help='case sensitive search in streams')
    oParser.add_option('--regex', action='store_true', default=False, help='use regex to search in streams')
    oParser.add_option('-g', '--generate', action='store_true', default=False, help='generate a Python program that creates the parsed PDF file')
    oParser.add_option('-y', '--yara', help='YARA rule (or directory or @file) to check streams (can be used with option --unfiltered)')
    oParser.add_option('-j', '--json', action='store_true', default=False, help='jsonify certain outputs')
    (options, args) = oParser.parse_args()

    if len(args) != 1:
        oParser.print_help()
        print('')
        print('  %s' % __description__)
        print('  Source code put in the public domain by Didier Stevens, no Copyright')
        print('  Use at your own risk')
        print('  https://DidierStevens.com')

    else:
        oPDFParser = cPDFParser(args[0], options.verbose, options.extract)
        cntComment = 0
        cntXref = 0
        cntTrailer = 0
        cntStartXref = 0
        cntIndirectObject = 0
        dicObjectTypes = {}

        selectComment = False
        selectXref = False
        selectTrailer = False
        selectStartXref = False
        selectIndirectObject = False
        if options.elements:
            for c in options.elements:
                if c == 'c':
                    selectComment = True
                elif c == 'x':
                    selectXref = True
                elif c == 't':
                    selectTrailer = True
                elif c == 's':
                    selectStartXref = True
                elif c == 'i':
                    selectIndirectObject = True
                else:
                    print('Error: unknown --elements value %s' % c)
                    return
        else:
            selectIndirectObject = True
            if not options.search and not options.object and not options.reference and not options.type and not options.searchstream:
                selectComment = True
                selectXref = True
                selectTrailer = True
                selectStartXref = True
            if options.search:
                selectTrailer = True

        if options.type == '-':
            optionsType = ''
        else:
            optionsType = options.type

        if options.generate:
            savedRoot = ['1', '0', 'R']
            print('#!/usr/bin/python')
            print('')
            print('"""')
            print('')
            print('Program generated by pdf-parser.py by Didier Stevens')
            print('https://DidierStevens.com')
            print('Use at your own risk')
            print('')
            print('Input PDF file: %s' % args[0])
            print('This Python program was created on: %s' % Timestamp())
            print('')
            print('"""')
            print('')
            print('import mPDF')
            print('import sys')
            print('')
            print('def Main():')
            print('    if len(sys.argv) != 2:')
            print("        print('Usage: %s pdf-file' % sys.argv[0])")
            print('        return')
            print('    oPDF = mPDF.cPDF(sys.argv[1])')

        if options.yara != None:
            if not 'yara' in sys.modules:
                print('Error: option yara requires the YARA Python module.')
                return
            rules = YARACompile(options.yara)

        while True:
            object = oPDFParser.GetObject()
            if object != None:
                if options.stats:
                    if object.type == PDF_ELEMENT_COMMENT:
                        cntComment += 1
                    elif object.type == PDF_ELEMENT_XREF:
                        cntXref += 1
                    elif object.type == PDF_ELEMENT_TRAILER:
                        cntTrailer += 1
                    elif object.type == PDF_ELEMENT_STARTXREF:
                        cntStartXref += 1
                    elif object.type == PDF_ELEMENT_INDIRECT_OBJECT:
                        cntIndirectObject += 1
                        type1 = object.GetType()
                        if not type1 in dicObjectTypes:
                            dicObjectTypes[type1] = [object.id]
                        else:
                            dicObjectTypes[type1].append(object.id)
                else:
                    if object.type == PDF_ELEMENT_COMMENT and selectComment:
                        if options.generate:
                            comment = object.comment[1:].rstrip()
                            if re.match('PDF-\d\.\d', comment):
                                print("    oPDF.header('%s')" % comment[4:])
                            elif comment != '%EOF':
                                print('    oPDF.comment(%s)' % repr(comment))
                        elif options.yara == None:
                            print('PDF Comment %s' % FormatOutput(object.comment, options.raw))
                            print('')
                    elif object.type == PDF_ELEMENT_XREF and selectXref:
                        if not options.generate and options.yara == None:
                            if options.debug:
                                print('xref %s' % FormatOutput(object.content, options.raw))
                            else:
                                print('xref')
                            print('')
                    elif object.type == PDF_ELEMENT_TRAILER and selectTrailer:
                        oPDFParseDictionary = cPDFParseDictionary(object.content[1:], options.nocanonicalizedoutput)
                        if options.generate:
                            result = oPDFParseDictionary.Get('/Root')
                            if result != None:
                                savedRoot = result
                        elif options.yara == None:
                            if not options.search or options.search and object.Contains(options.search):
                                if oPDFParseDictionary == None:
                                    print('trailer %s' % FormatOutput(object.content, options.raw))
                                else:
                                    print('trailer')
                                    oPDFParseDictionary.PrettyPrint('  ')
                                print('')
                    elif object.type == PDF_ELEMENT_STARTXREF and selectStartXref:
                        if not options.generate and options.yara == None:
                            print('startxref %d' % object.index)
                            print('')
                    elif object.type == PDF_ELEMENT_INDIRECT_OBJECT and selectIndirectObject:
                        if options.search:
                            if object.Contains(options.search):
                                PrintObject(object, options)
                        elif options.object:
                            if object.id == eval(options.object):
                                PrintObject(object, options)
                        elif options.reference:
                            if object.References(options.reference):
                                PrintObject(object, options)
                        elif options.type:
                            if EqualCanonical(object.GetType(), optionsType):
                                PrintObject(object, options)
                        elif options.hash:
                            print('obj %d %d' % (object.id, object.version))
                            rawContent = FormatOutput(object.content, True)
                            print(' len: %d md5: %s' % (len(rawContent), hashlib.md5(rawContent).hexdigest()))
                            print('')
                        elif options.searchstream:
                            if object.StreamContains(options.searchstream, not options.unfiltered, options.casesensitive, options.regex):
                                PrintObject(object, options)
                        elif options.yara != None:
                            results = object.StreamYARAMatch(rules, not options.unfiltered)
                            if results != None and results != []:
                                for result in results:
                                    print('YARA rule: %s (%s)' % (result.rule, result.namespace))
                                PrintObject(object, options)
                        else:
                            PrintObject(object, options)
                    elif object.type == PDF_ELEMENT_MALFORMED:
                        try:
                            fExtract = open(options.extract, 'wb')
                            try:
                                fExtract.write(C2BIP3(object.content))
                            except:
                                print('Error writing file %s' % options.extract)
                            fExtract.close()
                        except:
                            print('Error writing file %s' % options.extract)
            else:
                break

        if options.stats:
            print('Comment: %s' % cntComment)
            print('XREF: %s' % cntXref)
            print('Trailer: %s' % cntTrailer)
            print('StartXref: %s' % cntStartXref)
            print('Indirect object: %s' % cntIndirectObject)
            names = dicObjectTypes.keys()
            names.sort()
            for key in names:
                print(' %s %d: %s' % (key, len(dicObjectTypes[key]), ', '.join(map(lambda x: '%d' % x, dicObjectTypes[key]))))

        if options.generate:
            print("    oPDF.xrefAndTrailer('%s')" % ' '.join(savedRoot))
            print('')
            print("if __name__ == '__main__':")
            print('    Main()')
def Main ( ) : oParser = optparse . OptionParser ( usage = 'usage: %prog [options] pdf-file|zip-file|url\n' + __description__ , version = '%prog ' + __version__ ) oParser . add_option ( '-s' , '--search' , help = 'string to search in indirect objects (except streams)' ) oParser . add_option ( '-f' , '--filter' , action = 'store_true' , default = False , help = 'pass stream object through filters (FlateDecode, ASCIIHexDecode, ASCII85Decode, LZWDecode and RunLengthDecode only)' ) oParser . add_option ( '-o' , '--object' , help = 'id of indirect object to select (version independent)' ) oParser . add_option ( '-r' , '--reference' , help = 'id of indirect object being referenced (version independent)' ) oParser . add_option ( '-e' , '--elements' , help = 'type of elements to select (cxtsi)' ) oParser . add_option ( '-w' , '--raw' , action = 'store_true' , default = False , help = 'raw output for data and filters' ) oParser . add_option ( '-a' , '--stats' , action = 'store_true' , default = False , help = 'display stats for pdf document' ) oParser . add_option ( '-t' , '--type' , help = 'type of indirect object to select' ) oParser . add_option ( '-v' , '--verbose' , action = 'store_true' , default = False , help = 'display malformed PDF elements' ) oParser . add_option ( '-x' , '--extract' , help = 'filename to extract malformed content to' ) oParser . add_option ( '-H' , '--hash' , action = 'store_true' , default = False , help = 'display hash of objects' ) oParser . add_option ( '-n' , '--nocanonicalizedoutput' , action = 'store_true' , default = False , help = 'do not canonicalize the output' ) oParser . add_option ( '-d' , '--dump' , help = 'filename to dump stream content to' ) oParser . add_option ( '-D' , '--debug' , action = 'store_true' , default = False , help = 'display debug info' ) oParser . add_option ( '-c' , '--content' , action = 'store_true' , default = False , help = 'display the content for objects without streams or with streams without filters' ) oParser . add_option ( '--searchstream' , help = 'string to search in streams' ) oParser . add_option ( '--unfiltered' , action = 'store_true' , default = False , help = 'search in unfiltered streams' ) oParser . add_option ( '--casesensitive' , action = 'store_true' , default = False , help = 'case sensitive search in streams' ) oParser . add_option ( '--regex' , action = 'store_true' , default = False , help = 'use regex to search in streams' ) oParser . add_option ( '-g' , '--generate' , action = 'store_true' , default = False , help = 'generate a Python program that creates the parsed PDF file' ) oParser . add_option ( '-y' , '--yara' , help = 'YARA rule (or directory or @file) to check streams (can be used with option --unfiltered)' ) oParser . add_option ( '-j' , '--json' , action = 'store_true' , default = False , help = 'jsonify certain outputs' ) ( options , args ) = oParser . parse_args ( ) if len ( args ) != 1 : oParser . print_help ( ) print ( '' ) print ( '  %s' % __description__ ) print ( '  Source code put in the public domain by Didier Stevens, no Copyright' ) print ( '  Use at your own risk' ) print ( '  https://DidierStevens.com' ) else : oPDFParser = cPDFParser ( args [ 0 ] , options . verbose , options . extract ) cntComment = 0 cntXref = 0 cntTrailer = 0 cntStartXref = 0 cntIndirectObject = 0 dicObjectTypes = { } selectComment = False selectXref = False selectTrailer = False selectStartXref = False selectIndirectObject = False if options . elements : for c in options . elements : if c == 'c' : selectComment = True elif c == 'x' : selectXref = True elif c == 't' : selectTrailer = True elif c == 's' : selectStartXref = True elif c == 'i' : selectIndirectObject = True else : print ( 'Error: unknown --elements value %s' % c ) return else : selectIndirectObject = True if not options . search and not options . object and not options . reference and not options . type and not options . searchstream : selectComment = True selectXref = True selectTrailer = True selectStartXref = True if options . search : selectTrailer = True if options . type == '-' : optionsType = '' else : optionsType = options . type if options . generate : savedRoot = [ '1' , '0' , 'R' ] print ( '#!/usr/bin/python' ) print ( '' ) print ( '"""' ) print ( '' ) print ( 'Program generated by pdf-parser.py by Didier Stevens' ) print ( 'https://DidierStevens.com' ) print ( 'Use at your own risk' ) print ( '' ) print ( 'Input PDF file: %s' % args [ 0 ] ) print ( 'This Python program was created on: %s' % Timestamp ( ) ) print ( '' ) print ( '"""' ) print ( '' ) print ( 'import mPDF' ) print ( 'import sys' ) print ( '' ) print ( 'def Main():' ) print ( '    if len(sys.argv) != 2:' ) print ( "        print('Usage: %s pdf-file' % sys.argv[0])" ) print ( '        return' ) print ( '    oPDF = mPDF.cPDF(sys.argv[1])' ) if options . yara != None : if not 'yara' in sys . modules : print ( 'Error: option yara requires the YARA Python module.' ) return rules = YARACompile ( options . yara ) while True : object = oPDFParser . GetObject ( ) if object != None : if options . stats : if object . type == PDF_ELEMENT_COMMENT : cntComment += 1 elif object . type == PDF_ELEMENT_XREF : cntXref += 1 elif object . type == PDF_ELEMENT_TRAILER : cntTrailer += 1 elif object . type == PDF_ELEMENT_STARTXREF : cntStartXref += 1 elif object . type == PDF_ELEMENT_INDIRECT_OBJECT : cntIndirectObject += 1 type1 = object . GetType ( ) if not type1 in dicObjectTypes : dicObjectTypes [ type1 ] = [ object . id ] else : dicObjectTypes [ type1 ] . append ( object . id ) else : if object . type == PDF_ELEMENT_COMMENT and selectComment : if options . generate : comment = object . comment [ 1 : ] . rstrip ( ) if re . match ( 'PDF-\d\.\d' , comment ) : print ( "    oPDF.header('%s')" % comment [ 4 : ] ) elif comment != '%EOF' : print ( '    oPDF.comment(%s)' % repr ( comment ) ) elif options . yara == None : print ( 'PDF Comment %s' % FormatOutput ( object . comment , options . raw ) ) print ( '' ) elif object . type == PDF_ELEMENT_XREF and selectXref : if not options . generate and options . yara == None : if options . debug : print ( 'xref %s' % FormatOutput ( object . content , options . raw ) ) else : print ( 'xref' ) print ( '' ) elif object . type == PDF_ELEMENT_TRAILER and selectTrailer : oPDFParseDictionary = cPDFParseDictionary ( object . content [ 1 : ] , options . nocanonicalizedoutput ) if options . generate : result = oPDFParseDictionary . Get ( '/Root' ) if result != None : savedRoot = result elif options . yara == None : if not options . search or options . search and object . Contains ( options . search ) : if oPDFParseDictionary == None : print ( 'trailer %s' % FormatOutput ( object . content , options . raw ) ) else : print ( 'trailer' ) oPDFParseDictionary . PrettyPrint ( '  ' ) print ( '' ) elif object . type == PDF_ELEMENT_STARTXREF and selectStartXref : if not options . generate and options . yara == None : print ( 'startxref %d' % object . index ) print ( '' ) elif object . type == PDF_ELEMENT_INDIRECT_OBJECT and selectIndirectObject : if options . search : if object . Contains ( options . search ) : PrintObject ( object , options ) elif options . object : if object . id == eval ( options . object ) : PrintObject ( object , options ) elif options . reference : if object . References ( options . reference ) : PrintObject ( object , options ) elif options . type : if EqualCanonical ( object . GetType ( ) , optionsType ) : PrintObject ( object , options ) elif options . hash : print ( 'obj %d %d' % ( object . id , object . version ) ) rawContent = FormatOutput ( object . content , True ) print ( ' len: %d md5: %s' % ( len ( rawContent ) , hashlib . md5 ( rawContent ) . hexdigest ( ) ) ) print ( '' ) elif options . searchstream : if object . StreamContains ( options . searchstream , not options . unfiltered , options . casesensitive , options . regex ) : PrintObject ( object , options ) elif options . yara != None : results = object . StreamYARAMatch ( rules , not options . unfiltered ) if results != None and results != [ ] : for result in results : print ( 'YARA rule: %s (%s)' % ( result . rule , result . namespace ) ) PrintObject ( object , options ) else : PrintObject ( object , options ) elif object . type == PDF_ELEMENT_MALFORMED : try : fExtract = open ( options . extract , 'wb' ) try : fExtract . write ( C2BIP3 ( object . content ) ) except : print ( 'Error writing file %s' % options . extract ) fExtract . close ( ) except : print ( 'Error writing file %s' % options . extract ) else : break if options . stats : print ( 'Comment: %s' % cntComment ) print ( 'XREF: %s' % cntXref ) print ( 'Trailer: %s' % cntTrailer ) print ( 'StartXref: %s' % cntStartXref ) print ( 'Indirect object: %s' % cntIndirectObject ) names = dicObjectTypes . keys ( ) names . sort ( ) for key in names : print ( ' %s %d: %s' % ( key , len ( dicObjectTypes [ key ] ) , ', ' . join ( map ( lambda x : '%d' % x , dicObjectTypes [ key ] ) ) ) ) if options . generate : print ( "    oPDF.xrefAndTrailer('%s')" % ' ' . join ( savedRoot ) ) print ( '' ) print ( "if __name__ == '__main__':" ) print ( '    Main()' )