origami 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. data/COPYING.LESSER +165 -0
  2. data/README +77 -0
  3. data/VERSION +1 -0
  4. data/bin/config/pdfcop.conf.yml +237 -0
  5. data/bin/gui/about.rb +46 -0
  6. data/bin/gui/config.rb +132 -0
  7. data/bin/gui/file.rb +385 -0
  8. data/bin/gui/hexdump.rb +74 -0
  9. data/bin/gui/hexview.rb +91 -0
  10. data/bin/gui/imgview.rb +72 -0
  11. data/bin/gui/menu.rb +392 -0
  12. data/bin/gui/properties.rb +132 -0
  13. data/bin/gui/signing.rb +635 -0
  14. data/bin/gui/textview.rb +107 -0
  15. data/bin/gui/treeview.rb +409 -0
  16. data/bin/gui/walker.rb +282 -0
  17. data/bin/gui/xrefs.rb +79 -0
  18. data/bin/pdf2graph +121 -0
  19. data/bin/pdf2ruby +353 -0
  20. data/bin/pdfcocoon +104 -0
  21. data/bin/pdfcop +455 -0
  22. data/bin/pdfdecompress +104 -0
  23. data/bin/pdfdecrypt +95 -0
  24. data/bin/pdfencrypt +112 -0
  25. data/bin/pdfextract +221 -0
  26. data/bin/pdfmetadata +123 -0
  27. data/bin/pdfsh +13 -0
  28. data/bin/pdfwalker +7 -0
  29. data/bin/shell/.irbrc +104 -0
  30. data/bin/shell/console.rb +136 -0
  31. data/bin/shell/hexdump.rb +83 -0
  32. data/origami.rb +36 -0
  33. data/origami/3d.rb +239 -0
  34. data/origami/acroform.rb +321 -0
  35. data/origami/actions.rb +299 -0
  36. data/origami/adobe/fdf.rb +259 -0
  37. data/origami/adobe/ppklite.rb +489 -0
  38. data/origami/annotations.rb +775 -0
  39. data/origami/array.rb +187 -0
  40. data/origami/boolean.rb +101 -0
  41. data/origami/catalog.rb +486 -0
  42. data/origami/destinations.rb +213 -0
  43. data/origami/dictionary.rb +188 -0
  44. data/origami/docmdp.rb +96 -0
  45. data/origami/encryption.rb +1293 -0
  46. data/origami/export.rb +283 -0
  47. data/origami/file.rb +222 -0
  48. data/origami/filters.rb +250 -0
  49. data/origami/filters/ascii.rb +189 -0
  50. data/origami/filters/ccitt.rb +515 -0
  51. data/origami/filters/crypt.rb +47 -0
  52. data/origami/filters/dct.rb +61 -0
  53. data/origami/filters/flate.rb +112 -0
  54. data/origami/filters/jbig2.rb +63 -0
  55. data/origami/filters/jpx.rb +53 -0
  56. data/origami/filters/lzw.rb +195 -0
  57. data/origami/filters/predictors.rb +276 -0
  58. data/origami/filters/runlength.rb +117 -0
  59. data/origami/font.rb +209 -0
  60. data/origami/functions.rb +93 -0
  61. data/origami/graphics.rb +33 -0
  62. data/origami/graphics/colors.rb +191 -0
  63. data/origami/graphics/instruction.rb +126 -0
  64. data/origami/graphics/path.rb +154 -0
  65. data/origami/graphics/patterns.rb +180 -0
  66. data/origami/graphics/state.rb +164 -0
  67. data/origami/graphics/text.rb +224 -0
  68. data/origami/graphics/xobject.rb +493 -0
  69. data/origami/header.rb +90 -0
  70. data/origami/linearization.rb +318 -0
  71. data/origami/metadata.rb +114 -0
  72. data/origami/name.rb +170 -0
  73. data/origami/null.rb +75 -0
  74. data/origami/numeric.rb +188 -0
  75. data/origami/obfuscation.rb +233 -0
  76. data/origami/object.rb +527 -0
  77. data/origami/outline.rb +59 -0
  78. data/origami/page.rb +559 -0
  79. data/origami/parser.rb +268 -0
  80. data/origami/parsers/fdf.rb +45 -0
  81. data/origami/parsers/pdf.rb +27 -0
  82. data/origami/parsers/pdf/linear.rb +113 -0
  83. data/origami/parsers/ppklite.rb +86 -0
  84. data/origami/pdf.rb +1144 -0
  85. data/origami/reference.rb +113 -0
  86. data/origami/signature.rb +474 -0
  87. data/origami/stream.rb +575 -0
  88. data/origami/string.rb +416 -0
  89. data/origami/trailer.rb +173 -0
  90. data/origami/webcapture.rb +87 -0
  91. data/origami/xfa.rb +3027 -0
  92. data/origami/xreftable.rb +447 -0
  93. data/templates/patterns.rb +66 -0
  94. data/templates/widgets.rb +173 -0
  95. data/templates/xdp.rb +92 -0
  96. data/tests/dataset/test.dummycrt +28 -0
  97. data/tests/dataset/test.dummykey +27 -0
  98. data/tests/tc_actions.rb +32 -0
  99. data/tests/tc_annotations.rb +85 -0
  100. data/tests/tc_pages.rb +37 -0
  101. data/tests/tc_pdfattach.rb +24 -0
  102. data/tests/tc_pdfencrypt.rb +110 -0
  103. data/tests/tc_pdfnew.rb +32 -0
  104. data/tests/tc_pdfparse.rb +98 -0
  105. data/tests/tc_pdfsig.rb +37 -0
  106. data/tests/tc_streams.rb +129 -0
  107. data/tests/ts_pdf.rb +45 -0
  108. metadata +193 -0
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ =begin
4
+
5
+ = Author:
6
+ Guillaume Delugré <guillaume/at/security-labs.org>
7
+
8
+ = Info:
9
+ Uncompresses all binary streams of a PDF document.
10
+
11
+ = License:
12
+ Origami is free software: you can redistribute it and/or modify
13
+ it under the terms of the GNU Lesser General Public License as published by
14
+ the Free Software Foundation, either version 3 of the License, or
15
+ (at your option) any later version.
16
+
17
+ Origami is distributed in the hope that it will be useful,
18
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ GNU Lesser General Public License for more details.
21
+
22
+ You should have received a copy of the GNU Lesser General Public License
23
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
24
+
25
+ =end
26
+
27
+ begin
28
+ require 'origami'
29
+ rescue LoadError
30
+ ORIGAMIDIR = "#{File.dirname(__FILE__)}/.."
31
+ $: << ORIGAMIDIR
32
+ require 'origami'
33
+ end
34
+ include Origami
35
+
36
+ require 'optparse'
37
+
38
+ class OptParser
39
+ BANNER = <<USAGE
40
+ Usage: #{$0} [<PDF-file>] [-p <password>] [-o <output-file>]
41
+ Uncompresses all binary streams of a PDF document.
42
+ Bug reports or feature requests at: http://origami-pdf.googlecode.com/
43
+
44
+ Options:
45
+ USAGE
46
+
47
+ def self.parser(options)
48
+ OptionParser.new do |opts|
49
+ opts.banner = BANNER
50
+
51
+ opts.on("-o", "--output FILE", "Output PDF file (stdout by default)") do |o|
52
+ options[:output] = o
53
+ end
54
+
55
+ opts.on_tail("-h", "--help", "Show this message") do
56
+ puts opts
57
+ exit
58
+ end
59
+ end
60
+ end
61
+
62
+ def self.parse(args)
63
+ options =
64
+ {
65
+ :output => STDOUT,
66
+ }
67
+
68
+ self.parser(options).parse!(args)
69
+
70
+ options
71
+ end
72
+ end
73
+
74
+ begin
75
+ @options = OptParser.parse(ARGV)
76
+
77
+ target = (ARGV.empty?) ? STDIN : ARGV.shift
78
+ params =
79
+ {
80
+ :verbosity => Parser::VERBOSE_QUIET,
81
+ }
82
+
83
+ pdf = PDF.read(target, params)
84
+
85
+ pdf.root_objects.find_all { |obj|
86
+ obj.is_a?(Stream)
87
+ }.each { |stream|
88
+ filters = stream.Filter
89
+ filters = [ filters ] unless filters.is_a?(::Array)
90
+
91
+ unless filters.any?{|filter| [ :JPXDecode, :DCTDecode, :JBIG2Decode ].include? filter}
92
+ stream.rawdata = stream.data
93
+ stream.dictionary.delete(:Filter)
94
+ end
95
+ }
96
+
97
+ pdf.save(@options[:output], :noindent => true)
98
+
99
+ rescue SystemExit
100
+ rescue Exception => e
101
+ STDERR.puts "#{e.class}: #{e.message}"
102
+ exit 1
103
+ end
104
+
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ =begin
4
+
5
+ = Author:
6
+ Guillaume Delugré <guillaume/at/security-labs.org>
7
+
8
+ = Info:
9
+ Decrypts a PDF document.
10
+
11
+ = License:
12
+ Origami is free software: you can redistribute it and/or modify
13
+ it under the terms of the GNU Lesser General Public License as published by
14
+ the Free Software Foundation, either version 3 of the License, or
15
+ (at your option) any later version.
16
+
17
+ Origami is distributed in the hope that it will be useful,
18
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ GNU Lesser General Public License for more details.
21
+
22
+ You should have received a copy of the GNU Lesser General Public License
23
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
24
+
25
+ =end
26
+
27
+ begin
28
+ require 'origami'
29
+ rescue LoadError
30
+ ORIGAMIDIR = "#{File.dirname(__FILE__)}/.."
31
+ $: << ORIGAMIDIR
32
+ require 'origami'
33
+ end
34
+ include Origami
35
+
36
+ require 'optparse'
37
+
38
+ class OptParser
39
+ BANNER = <<USAGE
40
+ Usage: #{$0} [<PDF-file>] [-p <password>] [-o <output-file>]
41
+ Decrypts a PDF document. Supports RC4 40 to 128 bits, AES128, AES256.
42
+ Bug reports or feature requests at: http://origami-pdf.googlecode.com/
43
+
44
+ Options:
45
+ USAGE
46
+
47
+ def self.parser(options)
48
+ OptionParser.new do |opts|
49
+ opts.banner = BANNER
50
+
51
+ opts.on("-o", "--output FILE", "Output PDF file (stdout by default)") do |o|
52
+ options[:output] = o
53
+ end
54
+
55
+ opts.on("-p", "--password PASSWORD", "Password of the document") do |p|
56
+ options[:password] = p
57
+ end
58
+
59
+ opts.on_tail("-h", "--help", "Show this message") do
60
+ puts opts
61
+ exit
62
+ end
63
+ end
64
+ end
65
+
66
+ def self.parse(args)
67
+ options =
68
+ {
69
+ :output => STDOUT,
70
+ :password => ''
71
+ }
72
+
73
+ self.parser(options).parse!(args)
74
+
75
+ options
76
+ end
77
+ end
78
+
79
+ begin
80
+ @options = OptParser.parse(ARGV)
81
+
82
+ target = (ARGV.empty?) ? STDIN : ARGV.shift
83
+ params =
84
+ {
85
+ :verbosity => Parser::VERBOSE_QUIET,
86
+ :password => @options[:password]
87
+ }
88
+
89
+ PDF.read(target, params).save(@options[:output], :decrypt => true, :noindent => true)
90
+ rescue SystemExit
91
+ rescue Exception => e
92
+ STDERR.puts "#{e.class}: #{e.message}"
93
+ exit 1
94
+ end
95
+
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ =begin
4
+
5
+ = Author:
6
+ Guillaume Delugré <guillaume/at/security-labs.org>
7
+
8
+ = Info:
9
+ Encrypts a PDF document.
10
+
11
+ = License:
12
+ Origami is free software: you can redistribute it and/or modify
13
+ it under the terms of the GNU Lesser General Public License as published by
14
+ the Free Software Foundation, either version 3 of the License, or
15
+ (at your option) any later version.
16
+
17
+ Origami is distributed in the hope that it will be useful,
18
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ GNU Lesser General Public License for more details.
21
+
22
+ You should have received a copy of the GNU Lesser General Public License
23
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
24
+
25
+ =end
26
+
27
+ begin
28
+ require 'origami'
29
+ rescue LoadError
30
+ ORIGAMIDIR = "#{File.dirname(__FILE__)}/.."
31
+ $: << ORIGAMIDIR
32
+ require 'origami'
33
+ end
34
+ include Origami
35
+
36
+ require 'optparse'
37
+
38
+ class OptParser
39
+ BANNER = <<USAGE
40
+ Usage: #{$0} [<PDF-file>] [-p <password>] [-c <cipher>] [-s <key-size>] [-o <output-file>]
41
+ Encrypts a PDF document. Supports RC4 40 to 128 bits, AES128, AES256.
42
+ Bug reports or feature requests at: http://origami-pdf.googlecode.com/
43
+
44
+ Options:
45
+ USAGE
46
+
47
+ def self.parser(options)
48
+ OptionParser.new do |opts|
49
+ opts.banner = BANNER
50
+
51
+ opts.on("-o", "--output FILE", "Output PDF file (stdout by default)") do |o|
52
+ options[:output] = o
53
+ end
54
+
55
+ opts.on("-p", "--password PASSWORD", "Password of the document") do |p|
56
+ options[:password] = p
57
+ end
58
+
59
+ opts.on("-c", "--cipher CIPHER", "Cipher used to encrypt the document (Default: AES)") do |c|
60
+ options[:cipher] = c
61
+ end
62
+
63
+ opts.on("-s", "--key-size KEYSIZE", "Key size in bits (Default: 128)") do |s|
64
+ options[:key_size] = s.to_i
65
+ end
66
+
67
+ opts.on_tail("-h", "--help", "Show this message") do
68
+ puts opts
69
+ exit
70
+ end
71
+ end
72
+ end
73
+
74
+ def self.parse(args)
75
+ options =
76
+ {
77
+ :output => STDOUT,
78
+ :password => '',
79
+ :cipher => 'aes',
80
+ :key_size => 128
81
+ }
82
+
83
+ self.parser(options).parse!(args)
84
+
85
+ options
86
+ end
87
+ end
88
+
89
+ begin
90
+ @options = OptParser.parse(ARGV)
91
+
92
+ target = (ARGV.empty?) ? STDIN : ARGV.shift
93
+ params =
94
+ {
95
+ :verbosity => Parser::VERBOSE_QUIET,
96
+ }
97
+
98
+ pdf = PDF.read(target, params)
99
+ pdf.encrypt(
100
+ :user_password => @options[:password],
101
+ :owner_password => @options[:password],
102
+ :cipher => @options[:cipher],
103
+ :key_size => @options[:key_size]
104
+ )
105
+ pdf.save(@options[:output], :noindent => true)
106
+
107
+ rescue SystemExit
108
+ rescue Exception => e
109
+ STDERR.puts "#{e.class}: #{e.message}"
110
+ exit 1
111
+ end
112
+
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ =begin
4
+
5
+ = Author:
6
+ Guillaume Delugré <guillaume/at/security-labs.org>
7
+
8
+ = Info:
9
+ Extracts valuable data from a PDF document. Can extract:
10
+ - decoded streams
11
+ - JavaScript
12
+ - file attachments
13
+
14
+ = License:
15
+ Origami is free software: you can redistribute it and/or modify
16
+ it under the terms of the GNU Lesser General Public License as published by
17
+ the Free Software Foundation, either version 3 of the License, or
18
+ (at your option) any later version.
19
+
20
+ Origami is distributed in the hope that it will be useful,
21
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
22
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
+ GNU Lesser General Public License for more details.
24
+
25
+ You should have received a copy of the GNU Lesser General Public License
26
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
27
+
28
+ =end
29
+
30
+ begin
31
+ require 'origami'
32
+ rescue LoadError
33
+ ORIGAMIDIR = "#{File.dirname(__FILE__)}/.."
34
+ $: << ORIGAMIDIR
35
+ require 'origami'
36
+ end
37
+ include Origami
38
+
39
+ require 'optparse'
40
+ require 'rexml/document'
41
+
42
+ class OptParser
43
+ BANNER = <<USAGE
44
+ Usage: #{$0} <PDF-file> [-afjms] [-d <output-directory>]
45
+ Extracts various data out of a document (streams, scripts, fonts, metadata, attachments).
46
+ Bug reports or feature requests at: http://origami-pdf.googlecode.com/
47
+
48
+ Options:
49
+ USAGE
50
+
51
+ def self.parser(options)
52
+ OptionParser.new do |opts|
53
+ opts.banner = BANNER
54
+
55
+ opts.on("-d", "--output-dir DIR", "Output directory") do |d|
56
+ options[:output_dir] = d
57
+ end
58
+
59
+ opts.on("-s", "--streams", "Extracts all decoded streams") do
60
+ options[:streams] = true
61
+ end
62
+
63
+ opts.on("-a", "--attachments", "Extracts file attachments") do
64
+ options[:attachments] = true
65
+ end
66
+
67
+ opts.on("-f", "--fonts", "Extracts embedded font files") do
68
+ options[:fonts] = true
69
+ end
70
+
71
+ opts.on("-j", "--js", "Extracts JavaScript scripts") do
72
+ options[:javascript] = true
73
+ end
74
+
75
+ opts.on("-m", "--metadata", "Extracts metadata streams") do
76
+ options[:metadata] = true
77
+ end
78
+
79
+ opts.on_tail("-h", "--help", "Show this message") do
80
+ puts opts
81
+ exit
82
+ end
83
+ end
84
+ end
85
+
86
+ def self.parse(args)
87
+ options =
88
+ {
89
+ }
90
+
91
+ self.parser(options).parse!(args)
92
+
93
+ options
94
+ end
95
+ end
96
+
97
+ begin
98
+ @options = OptParser.parse(ARGV)
99
+
100
+ if ARGV.empty?
101
+ STDERR.puts "Error: No filename was specified. #{$0} --help for details."
102
+ exit 1
103
+ else
104
+ target = ARGV.shift
105
+ end
106
+
107
+ unless [:streams,:javascript,:attachments,:fonts,:metadata].any? {|opt| @options[opt]}
108
+ @options[:streams] =
109
+ @options[:javascript] =
110
+ @options[:fonts] =
111
+ @options[:attachments] = true
112
+ end
113
+
114
+ if @options[:output_dir].nil?
115
+ @options[:output_dir] = "#{File.basename(target, '.pdf')}.dump"
116
+ end
117
+
118
+ OUTPUT_DIR = @options[:output_dir]
119
+ Dir::mkdir(OUTPUT_DIR) unless File.directory?(OUTPUT_DIR)
120
+
121
+ params =
122
+ {
123
+ :verbosity => Parser::VERBOSE_QUIET,
124
+ }
125
+ pdf = PDF.read(target, params)
126
+
127
+ if @options[:streams]
128
+ pdf.root_objects.find_all{|obj| obj.is_a?(Stream)}.each do |stream|
129
+ stream_file = "#{OUTPUT_DIR}/stream_#{stream.reference.refno}.dmp"
130
+ File.open(stream_file, "w") do |fd|
131
+ fd.write(stream.data)
132
+ end
133
+ end
134
+ end
135
+
136
+ if @options[:javascript]
137
+ pdf.ls(/^JS$/).each do |script|
138
+ script_file = "#{OUTPUT_DIR}/script_#{script.hash}.js"
139
+ File.open(script_file, "w") do |fd|
140
+ fd.write(
141
+ case script
142
+ when Stream then
143
+ script.data
144
+ else
145
+ script.value
146
+ end
147
+ )
148
+ end
149
+ end
150
+
151
+ # Also checking for presence of JavaScript in XML forms.
152
+ if pdf.has_form? and pdf.Catalog.AcroForm.has_key?(:XFA)
153
+ xfa = pdf.Catalog.AcroForm[:XFA].solve
154
+
155
+ case xfa
156
+ when Array then
157
+ xml = ""
158
+ i = 0
159
+ xfa.each do |packet|
160
+ if i % 2 == 1
161
+ xml << packet.solve.data
162
+ end
163
+
164
+ i = i + 1
165
+ end
166
+ when Stream then
167
+ xml = xfa.data
168
+ else
169
+ reject("Malformed XFA dictionary")
170
+ end
171
+
172
+ xfadoc = REXML::Document.new(xml)
173
+ REXML::XPath.match(xfadoc, "//script").each do |script|
174
+ script_file = "#{OUTPUT_DIR}/script_#{script.hash}.js"
175
+ File.open(script_file, 'w') do |fd|
176
+ fd.write(script.text)
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ if @options[:attachments]
183
+ pdf.ls_names(Names::Root::EMBEDDEDFILES).each do |name, attachment|
184
+ attached_file = "#{OUTPUT_DIR}/attached_#{File.basename(name)}"
185
+ spec = attachment.solve
186
+ ef = spec[:EF].solve
187
+ f = ef[:F].solve
188
+
189
+ File.open(attached_file, "w") do |fd|
190
+ fd.write(f.data)
191
+ end
192
+ end
193
+ end
194
+
195
+ if @options[:fonts]
196
+ pdf.root_objects.find_all{|obj| obj.is_a?(Stream)}.each do |stream|
197
+ font = stream.xrefs.find{|obj| obj.is_a?(FontDescriptor)}
198
+ if font
199
+ font_file = "#{OUTPUT_DIR}/font_#{File.basename(font.FontName.value.to_s)}"
200
+ File.open(font_file, "w") do |fd|
201
+ fd.write(stream.data)
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ if @options[:metadata]
208
+ pdf.root_objects.find_all{|obj| obj.is_a?(MetadataStream)}.each do |stream|
209
+ metadata_file = "#{OUTPUT_DIR}/metadata_#{stream.reference.refno}.xml"
210
+ File.open(metadata_file, "w") do |fd|
211
+ fd.write(stream.data)
212
+ end
213
+ end
214
+ end
215
+
216
+ rescue SystemExit
217
+ rescue Exception => e
218
+ STDERR.puts "#{e.class}: #{e.message}"
219
+ exit 1
220
+ end
221
+