现在的位置: 首页 > 综合 > 正文

[WebKit]C++类的数据结构及在反汇编上的应用

2013年02月17日 ⁄ 综合 ⁄ 共 4141字 ⁄ 字号 评论关闭

参考:

1. http://c2.com/cgi/wiki?PlainOldData

2. http://c2.com/cgi/wiki?VeeTablevtable

POD:内置数据类型并且包含trivial constructor的类

VTable:一个执行函数的数组指针,其成员函数的layout与实现方式有关 


trivial constructor:使用了默认构造函数的无虚成员函数,包括父类的类。


在反汇编常常要在无法使用调试信息和源代码的情况下查看数据内容,数据结构比较好处理,如果是C++的类,就需要做些总结了。


基础 - POD?

C++的成员变量的排列顺序关键在于区别是不是POD(Plain Old Data)类型,从而确定是否需要有VPTR。POD类型会保持和struct相同的数据排列顺序,但在类的定义中不能出现虚函数、析构函数及拷贝的赋值函数等,否则编译器会增加一个Virtual Table Pointer。下面两张图分别表示在继承关系下成员变量的排列顺序。




在反汇编过程中,在已知类定义的情况下(基于开源代码的Safari), 就可以准确的输出类的成员内容。

iOS Safari反汇编实例1


这是一个简单的例子,在Safari调用JSC执行脚本时会执行的一个C函数:
JS_EXPORT JSValueRef JSEvaluateScript(

JSContextRef ctx,
JSStringRef script,
JSObjectRef thisObject,
JSStringRef sourceURL,
int startingLineNumber,

JSValueRef *exception);
这个例子的目的是要查看第二个参数script的内容,以观察Safari有没有执行特殊的脚本。
2. 根据Apple开源的JavaScriptCore的代码,可以了解到script是一个OpaqueJSString, 相关定义如下:
typedef struct OpaqueJSString* JSStringRef;
下面列出各个类的成员变量:
class ThreadSafeRefCountedBase {

private:

int m_refCount;
};

struct OpaqueJSString : public ThreadSafeRefCounted<OpaqueJSString>
{
UChar* m_characters;
unsigned m_length;

};


合并起来数据格式为:

3.在函数入口处下断点,然后查看数据:
*关于如何取出各个参数,请参考:[iOS逆向工程] 在汇编语言调试中获取当前实例句柄

(lldb) p/x `*(int*)($ebp+12)` 

(int) $1 = 0x19aa1df0 <-这是script指针的值

(lldb) mem read `$1`
0x19aa1df0: 01 00 00 00 00 10 84 18 94 08 01 00 00 00 00 00 ................

这个就是script指向数据的内容,依次填进去就可以了
*m_refCount= 1
*m_characters = 0x18841000
*m_length = 0x010894

下面显示一下脚本内容:

(lldb) mem read `*(int *)(*(int*)($ebp+12)+4)` -c 64
0x18841000: 2f 00 2a 00 0a 00 20 00 2a 00 20 00 43 00 6f 00 /.*... .*. .C.o.
0x18841010: 70 00 79 00 72 00 69 00 67 00 68 00 74 00 20 00 p.y.r.i.g.h.t. .
0x18841020: a9 00 20 00 32 00 30 00 31 00 30 00 20 00 41 00 .. .2.0.1.0. .A.

0x18841030: 70 00 70 00 6c 00 65 00 20 00 49 00 6e 00 63 00 p.p.l.e. .I.n.c.

如果使用上m_length来指定长度(-c后面的参数,乘以2是因为数据为UTF-16),就可以显示全部内容:
(lldb) mem read `*(int *)(*(int*)($ebp+12)+4)` -c `*(int *)(*(int*)($ebp+12)+8)*2`

在lldb里加个参数-o就可以存储到文件,然后用个脚本就能将数据转换为真实的脚本数据:
(lldb) mem read `*(int *)(*(int*)($ebp+12)+4)` -c `*(int *)(*(int*)($ebp+12)+8)*2` -o /xxxx/captured.txt

iOS Safari反汇编实例2


这个例子稍为复杂,不但类的继承层次多,而且是非POD类型,所以有了VPTR。

针对函数

JavaScriptCore`JSC::evaluate(JSC::ExecState*, JSC::ScopeChainNode*, JSC::SourceCode const&, JSC::JSValue, JSC::JSValue*);

目标是打印JSC::SourceCode参数内容,其实这个函数是由上面的函数调用的。收集类的定义的步骤是相同,这里省去,只确定一个合并的总结构如下:
m_provider
m_ptr

UString m_url; 
TextPosition m_startPosition;
bool m_validated;
SourceProviderCache* m_cache;

bool m_cacheOwned;
String m_source;
m_urlm_source是重点数据。另外并不是所有的SourceCode的m_provider都会带m_source成员,视情况而定(StringSourceProvider),这里不考虑这个问题。

下面是分析数据:
(lldb) p/x `*(int*)($ebp+16)`
(int) $66 = 0xb015bc04
(lldb) mem read 0xb015bc04
0xb015bc04: f8 42 98 18 00 00 00 00 df 9c 02 00 01 00 00 00 .B..............
0xb015bc14: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
m_provider {m_ptr} = 0x189842f8
m_startChar = 0
m_endChar = 0x029cdf
m_firstLine = 0x01

进一步读取m_provider
(lldb) (lldb) mem read 0x189842f8 -c 64
0x189842f8: 68 a3 c9 03 02 00 00 00 c8 f3 5f 19 00 00 00 00 h........._.....
0x18984308: 00 00 00 00 00 2b 03 53 80 7d 5f 19 00 00 00 00 .....+.S.}_.....
0x18984318: 8c a3 c9 03 80 47 6e 18 06 00 00 00 08 00 00 00 .....Gn.........
0x18984328: 34 43 98 18 00 00 00 00 10 16 90 6c 64 00 72 00 4C.........ld.r.


VPTR = 0x03c9a368
==>RefCountedBase
int m_refCount = 0x02
==>SourceProvider
UString m_url = 0x195ff3c8

TextPosition m_startPosition

m_line = 0x0
m_column = 0x0 
bool m_validated = 0x53032b00
SourceProviderCache* m_cache = 0x195f7d80

bool m_cacheOwned = 0; //FALSE
==>StringSourceProvider
UString m_source {m_impl {m_ptr }} = 0x03c9a38c

读取URL,这里是一个StringImpl指针:

(lldb) mem read 0x195ff3c8
0x195ff3c8: 06 00 00 00 3d 00 00 00 dc f3 5f 19 00 00 00 00 ....=....._.....

0x195ff3d8:40 00 00 00 68 74 74 703a 2f 2f 62 31 2e 62 73 @...http://b1.bs
字串内容是起始地址偏移8字节处的地址。
其中:
m_refCount = 0x06; (offset:0byte)
m_length = 0x3d (offset:4bytes)
m_data8 or m_data16 = 0x195ff3dc (offset:8bytes)

用下面的指令可以输出完整的url:

(lldb) mem read `*(int *)(*(int *)(*(int *)(*(int*)($ebp+16))+8)+8)` -c `*(int *)(*(int *)(*(int *)(*(int*)($ebp+16))+8)+4)`
0x195ff3dc: 68 74 74 70 3a 2f 2f 62 31 2e 62 73 74 2e 31 32 http://b1.bst.12
0x195ff3ec: 36 2e 6e 65 74 2f 6e 65 77 70 61 67 65 2f 72 2f 6.net/newpage/r/
0x195ff3fc: 6a 2f 6d 2f 6d 2d 33 2f 70 6d 2e 6a 73 3f 76 3d j/m/m-3/pm.js?v=

0x195ff40c: 31 33 36 39 38 30 39 35 32 36 34 33 31 1369809526431
使用下面的指令就可以读出完整的脚本内容
(lldb) mem read `*(int *)(*(int *)(*(int *)(*(int*)($ebp+16))+32)+8)`
-c `*(int *)(*(int *)(*(int *)(*(int*)($ebp+16))+32)+4)*2`

转载请注明出处:http://blog.csdn.net/horkychen

关于POD可以看这里: POD Types

关于Virtual Table看这里:Virtual Method Table

关于反汇编下参数获取看这里: 在汇编语言调试中获取当前实例句柄

抱歉!评论已关闭.