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

简介: 在反汇编常常要在无法使用调试信息和源代码的情况下查看数据内容,数据结构比较好处理,如果是C++的类,就需要做些总结了。 基础 - POD? C++的成员变量的排列顺序关键在于区别是不是POD(Plain Old Data)类型,从而确定是否需要有VPTR。

在反汇编常常要在无法使用调试信息和源代码的情况下查看数据内容,数据结构比较好处理,如果是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

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


目录
相关文章
|
6月前
|
消息中间件 缓存 NoSQL
Redis各类数据结构详细介绍及其在Go语言Gin框架下实践应用
这只是利用Go语言和Gin框架与Redis交互最基础部分展示;根据具体业务需求可能需要更复杂查询、事务处理或订阅发布功能实现更多高级特性应用场景。
395 86
|
8月前
|
存储 监控 安全
企业上网监控系统中红黑树数据结构的 Python 算法实现与应用研究
企业上网监控系统需高效处理海量数据,传统数据结构存在性能瓶颈。红黑树通过自平衡机制,确保查找、插入、删除操作的时间复杂度稳定在 O(log n),适用于网络记录存储、设备信息维护及安全事件排序等场景。本文分析红黑树的理论基础、应用场景及 Python 实现,并探讨其在企业监控系统中的实践价值,提升系统性能与稳定性。
453 1
|
8月前
|
存储 监控 算法
公司员工泄密防护体系中跳表数据结构及其 Go 语言算法的应用研究
在数字化办公中,企业面临员工泄密风险。本文探讨使用跳表(Skip List)数据结构优化泄密防护系统,提升敏感数据监测效率。跳表以其高效的动态数据处理能力,为企业信息安全管理提供了可靠技术支持。
171 0
|
8月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
219 0
|
11月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
439 12
|
9月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
233 0
|
9月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
372 0
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
220 16
|
12月前
|
编译器 C++
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。
|
12月前
|
存储 编译器 C++
类和对象(上)(C++)
本篇内容主要讲解了C++中类的相关知识,包括类的定义、实例化及this指针的作用。详细说明了类的定义格式、成员函数默认为inline、访问限定符(public、protected、private)的使用规则,以及class与struct的区别。同时分析了类实例化的概念,对象大小的计算规则和内存对齐原则。最后介绍了this指针的工作机制,解释了成员函数如何通过隐含的this指针区分不同对象的数据。这些知识点帮助我们更好地理解C++中类的封装性和对象的实现原理。