结构体分类

结构体的一个显著特点在于,结构体中的数据字段是通过名称访问,而不是像数组那样通过索引访问。不好的是,字段名称被编译器转换成了数字偏移量。结果,在反汇编代码清单中,访问结构体字段的方式看起来与使用常量索引访问数组元素的方式极其相似。

注意的是,结构体中有个内存对齐规则,所以不要认为编译器会利用所需的最小空间来分配结构体。默认情况下,编译器会设法将结构体字段与内存地址对齐,以最有效地读取和写入这些字段

1. 全局分配的结构体

和全局分配的数组一样,编译器在编译时可获知全局分配的结构体的地址。这使得编译器能够在编译时计算出结构体中每个成员的地址,而不必在运行时进行任何计算。

如下代码:

[cpp] view plaincopy
  1. struct stTest
  2. {
  3. int    f1;
  4. short  f2;
  5. char   f3;
  6. int    f4;
  7. double f5;
  8. };
  9. stTest g_st;
  10. void fun()
  11. {
  12. g_st.f1 = 10;
  13. g_st.f2 = 20;
  14. g_st.f3 = 30;
  15. g_st.f4 = 40;
  16. g_st.f5 = 50.0;
  17. }

对应汇编为:可以看到,在这个反汇编代码清单中,访问结构体成员不需要任何算术计算,如果没有源代码,你根本无法断定这个程序使用了结构体。

[cpp] view plaincopy
  1. push    ebp
  2. mov     ebp, esp
  3. mov     dword_403018, 10 ; int f1 = 10
  4. mov     eax, 20
  5. mov     word_40301C, ax ; word f2 = 20
  6. mov     byte_40301E, 30 ; byte f3 = 30
  7. mov     dword_403020, 40 ; int f4 = 40
  8. fld     ds:dbl_4020E0   ; f5 = dbl_403028 = 50.0
  9. fstp    dbl_403028
  10. pop     ebp
  11. retn

2. 栈分配的结构体

同样,仅仅根据栈布局,同样很难识别出栈分配的结构体。

[cpp] view plaincopy
  1. void fun()
  2. {
  3. stTest l_st;
  4. l_st.f1 = 10;
  5. l_st.f2 = 20;
  6. l_st.f3 = 30;
  7. l_st.f4 = 40;
  8. l_st.f5 = 50.0;
  9. }

对应汇编为:同样,访问结构体中的字段不需要进行任何算术计算,因为在编译时,编译器能够确定栈帧内每个字段的相对偏移量

[cpp] view plaincopy
  1. 000 push    ebp
  2. 004 mov     ebp, esp
  3. 004 sub     esp, 18h
  4. 01C mov     [ebp+var_18], 10
  5. 01C mov     eax, 14h
  6. 01C mov     [ebp+var_14], ax
  7. 01C mov     [ebp+var_12], 30
  8. 01C mov     [ebp+var_10], 40
  9. 01C fld     ds:dbl_4020E0
  10. 01C fstp    [ebp+var_8]
  11. 01C mov     esp, ebp
  12. 004 pop     ebp
  13. 000 retn

3. 堆分配的结构体

由于结构体的地址在编译时未知,编译器别无选择,只有生成代码来计算每个字段在结构体中的正确偏移量。

如果一个结构体在堆中分配,那么对编译器来说,引用该结构体的唯一线索就是指向该结构体起始地址的指针。

[cpp] view plaincopy
  1. void fun()
  2. {
  3. stTest* pst = new stTest;
  4. pst->f1 = 10;
  5. pst->f2 = 20;
  6. pst->f3 = 30;
  7. pst->f4 = 40;
  8. pst->f5 = 50.0;
  9. }

对应汇编为:

[cpp] view plaincopy
  1. push    ebp
  2. mov     ebp, esp
  3. sub     esp, 8
  4. push    24              ; unsigned int
  5. call    ??2@YAPAXI@Z    ; operator new(uint)
  6. add     esp, 4
  7. mov     [ebp+var_8], eax ; eax为pst指针,var_8也是
  8. mov     eax, [ebp+var_8]
  9. mov     [ebp+var_4], eax
  10. mov     ecx, [ebp+var_4] ; ecx = pst
  11. mov     dword ptr [ecx], 10 ; (int*)pst = 10
  12. mov     edx, 20
  13. mov     eax, [ebp+var_4] ; eax = pst
  14. mov     [eax+4], dx     ; (word*)(pst+4) = 20
  15. mov     ecx, [ebp+var_4] ; ecx = pst
  16. mov     byte ptr [ecx+6], 30 ; (byte*)(pst+6)=30,验证了第二个是word字节
  17. mov     edx, [ebp+var_4] ; edx = pst
  18. mov     dword ptr [edx+8], 40 ; (int*)(pst+8) = 40
  19. mov     eax, [ebp+var_4] ; eax = pst
  20. fld     ds:dbl_4020F0
  21. fstp    qword ptr [eax+10h] ; (dq)(pst+16)=50.0
  22. mov     esp, ebp
  23. pop     ebp
  24. retn

4. 结构体数组

[cpp] view plaincopy
  1. void fun()
  2. {
  3. stTest* pst = new stTest[2];
  4. pst[1].f1 = 10;
  5. pst[1].f2 = 20;
  6. pst[1].f3 = 30;
  7. pst[1].f4 = 40;
  8. pst[1].f5 = 50.0;
  9. }

反汇编没什么区别:与上面相比,每项加了24

[cpp] view plaincopy
  1. push    ebp
  2. mov     ebp, esp
  3. sub     esp, 8
  4. push    48              ; 48/2 = 24
  5. call    ??2@YAPAXI@Z    ; operator new(uint)
  6. add     esp, 4
  7. mov     [ebp+var_8], eax
  8. mov     eax, [ebp+var_8]
  9. mov     [ebp+var_4], eax
  10. mov     ecx, [ebp+var_4]
  11. mov     dword ptr [ecx+24], 10 ; pst+24,注意+24
  12. mov     edx, 20
  13. mov     eax, [ebp+var_4]
  14. mov     [eax+1Ch], dx
  15. mov     ecx, [ebp+var_4]
  16. mov     byte ptr [ecx+30], 30 ; pst+24+6
  17. mov     edx, [ebp+var_4]
  18. mov     dword ptr [edx+32], 40 ; pst+24+8
  19. mov     eax, [ebp+var_4]
  20. fld     ds:dbl_4020F0
  21. fstp    qword ptr [eax+40] ; pst+14+16
  22. mov     esp, ebp
  23. pop     ebp
  24. retn

创建结构体

IDA之所以在分析阶段无法识别结构体,可能源于两个原因。首先,虽然IDA了解某个结构体的布局,但它并没有足够的信息,能够判断程序确实使用了结构体。其次,程序中的结构体可能是一种IDA对其一无所知的非标准结构体。在这两种情况下,问题都可以得到解决,且首先从Structures窗口下手

1.添加结构体

Structures窗口的前4行文本用于提醒用户该窗口中可能进行的操作。

使用热键INSERT启动

指定结构体的名称并单击OK按钮后,IDA将在Structures窗口中创建一个空结构体定义

2.编辑结构体成员

(1)要给结构体添加新字段,将光标放在结构体定义的最后一行(包含ends的那一行)并按下D键。新字段的大小取决于你在数据转盘上选择的第一个大小

(2)如果需要修改字段的大小,首先将光标放在新字段的名称上,然后重复按下D键,使数据转盘上的数据类型开始循环,从而为新字段选择正确的数据大小。另外,你还可以使用Options▶Setup Data Types来指定一个在数据转盘上不存在的大小。如果新字段是一个数组,右击其名称并在上下文菜单中选择Array

(3)要更改一个结构体字段的名称,单击字段名称并按下N键,或者右击该名称并在上下文菜单中选择ReName,然后在输入框中输入一个名称即可。

以下是帮助说明:

•一个字段的字节偏移量在Structures窗口的左侧显示。

•结构体的新大小会在结构体定义的第一行及时更新出来。

•可以给一个结构体字段添加注释,就像给任何反汇编行添加注释一样。

•只有当一个字段是结构体中的最后一个字段时,使用U键才能删除该字段。对于其他字段,按下U键只是变成undefined,可以通过D键恢复

•IDA并不区分已压缩和未压缩的结构体。为将字段适当对齐,如果你需要填补字节,那么你必须负责添加这些字节。填补字节最好作为适当大小的哑字段添加。在添加额外的字段后,你可以选择取消或保留这些字段的定义。

•分配到结构体中间的字节只有在取消关联字段的定义后(undefined状态),Edit▶Shrink Struct Type(缩小结构体类型)即可删除被取消定义的字节。

•也可以在结构体的中间添加新的字节:选择新字节后面的一个字段,然后使用Edit▶Expand Struct Type(扩大结构体类型)在选中的字段前插入一定数量的字节。

•如果知道结构体的大小,而不了解它的布局,你需要创建两个字段。第一个字段为一个数组,它的大小为结构体的大小减去1个字节(size-1);第二个字段应为1个字节。创建第二个字段后,取消第一个(数组)字段的定义。这样,结构体的大小被保留下来,随后,当你进一步了解该结构体的布局后,你可以回过头来定义它的字段及其大小。

创建完成后如下:

3.折叠打开结构体

可以选择结构体中的任何字段并按下数字键盘中的减号键,将结构体的定义折叠成一行摘要,双击结构体名称也可以打开该定义。(或ctrl+/ctrl-)

使用结构体模板

1.右击中,可在上下文菜单上看到Structure offset选项:很明确的说明了它可以是stTest.f5+24(pst[1].f5 = 50.0;)

2.另一种方法,可以将栈和全局变量格式化成整个结构体,双击该变量,打开详细栈帧视图,然后使用Edit▶Struct Var(ALT+Q)命令显示一组已知的结构体

之后上面结构体数组汇编变为:重新格式化之后,IDA认识到,任何对分配给var_8的24个字节块的内存引用,都必须引用该结构体中的一个字段。如果IDA发现这样一个引用,它会尽一切努力,将这个内存引用与结构体变量中的一个已定义的字段关联起来

[cpp] view plaincopy
  1. var_8= stTest ptr -8
  2. push    ebp
  3. mov     ebp, esp
  4. sub     esp, 8
  5. push    48              ; unsigned int
  6. call    ??2@YAPAXI@Z    ; operator new(uint)
  7. add     esp, 4
  8. mov     [ebp+var_8.f1], eax
  9. mov     eax, [ebp+var_8.f1]
  10. mov     dword ptr [ebp+var_8.f2], eax
  11. mov     ecx, dword ptr [ebp+var_8.f2]
  12. mov     dword ptr [ecx+(size stTest)], 10
  13. mov     edx, 20
  14. mov     eax, dword ptr [ebp+var_8.f2]
  15. mov     [eax+1Ch], dx
  16. mov     ecx, dword ptr [ebp+var_8.f2]
  17. mov     byte ptr [ecx+30], 30
  18. mov     edx, dword ptr [ebp+var_8.f2]
  19. mov     [edx+(stTest.f4+18h)], 40
  20. mov     eax, dword ptr [ebp+var_8.f2]
  21. fld     ds:dbl_4020F0
  22. fstp    [eax+(stTest.f5+18h)]
  23. mov     esp, ebp
  24. pop     ebp
  25. retn

将全局变量格式化成结构体的过程与格式化栈变量所使用的过程几乎完全相同

导入新的结构体

在创建新结构体方面,IDA确实提供了一些捷径。IDA能够解析C(而非C++)数据声明,以及整个C头文件,并自动为在这些声明或头文件中定义的结构体创建对应的IDA结构体。如果你碰巧拥有你正进行逆向工程的二进制文件的源代码,或者至少是头文件,那么,你就可以让IDA直接从源代码中提取出相关结构体,从而节省大量时间。

使用View▶Open Subviews▶Local Types(查看▶打开子窗口▶本地类型)命令,可以打开Local Types子窗口,其中列出了所有解析到当前数据库中的类型。

通过INSERT键或上下文菜单中的Insert选项解析:

------------->

解析C头文件

要解析头文件,可以使用File▶Load File▶Parse C HeaderFile(文件▶加载文件▶解析C头文件)选择你想要解析的头文件。如果一切正常,IDA会通知你Compilation successful(编译完成)。如果解析器遇到任何问题,IDA将会在输出窗口中显示错误消息

IDA会将所有被成功解析的结构体添加到当前数据库的标准结构体列表中(具体地说,是列表的末尾)。如果新结构体的名称与现有结构体的名称相同,IDA会用新结构体布局覆盖原有结构体定义。除非你明确选择添加新的结构体,否则,新结构体不会出现在Structures窗口中

•默认情况下,解析器会建立4字节对齐的结构体,当然,你可以使用pack改变它

•解析器只能理解C标准数据类型。但是,解析器还能够理解预处理器define指令和C typedef语句。因此,如果解析器之前遇到过适当的typedef,它将能够正确解析unit32_t之类的类型。

•如果你没有源代码,那么你会发现,使用文本编辑器以C表示法迅速定义一个结构体布局,并解析得到的头文件或把声明粘贴为一个新的本地类型,会比使用IDA烦琐的手动结构体定义工具更加方便。

添加到Local Types(本地类型)窗口中的数据类型不会立即在Structures(结构体)最简单的方法是在类型上单击鼠标右键,并选择Synchronize to idb。

使用标准结构体

IDA能够识别大量与各种库和API函数有关的数据结构。当IDA在反汇编代码清单中操纵结构体时,它会在Structures窗口中添加相应的结构体定义。因此,Structures窗口中显示的是应用于当前二进制文件的已知结构体的子集。除了创建自定义结构体外,你还可以从IDA的已知结构体列表中提取出其他标准结构体,并将其添加到Structures窗口中。

首先,在Structures窗口中按下IN-SERT键。在Create structure/ union对话框中,包含一个Add standard structure(添加标准结构体)按钮。单击这个按钮,IDA将显示与当前编译器(在分析阶段检测出来)和文件格式有关的结构体主列表。这个结构体主列表中还包含通过解析C头文件添加到数据库中的结构体。

示例:(分析文件头)

默认情况下,在创建后,文件头不会立即加载到数据库中。但是,如果你在最初创建数据库时选择Manual load(手动加载)选项,就可以将文件头加载到数据库中。加载文件头可确保只有与这些头部有关的数据类型才出现在数据库中。多数情况下,文件头不会以任何形式被格式化,因为通常程序并不会直接引用它们自己的文件头。因此,分析器也没有必要对文件头应用结构体模板。

对一个PE二进制文件进行一番研究后,你会发现,PE文件的开头部分是一个名为IMAGE_DOS_HEADER的MS-DOS头部结构体。另外,IMAGE_DOS_HEADER中的数据指向一个IM-AGE_NE_HEADER结构体的位置。

第一步是添加标准的IMAGE_DOS_HEADER结构体(你可以在打开IMAGE_NT_HEADER结构体的同时添加该结构体)。

第二步是使用Edit▶Struct Var(ALT+Q),将从_Image-Base开始的字节转换成一个IMAGE_DOS_HEADER结构体

7.IDA-创建结构体相关推荐

  1. IDA创建结构体方法

    1. ida是最好用的逆向分析工具 2.问题 如果你在使用IDA分析时遇到大量的结构体指针偏移形式的变量,肯定想根据自己的分析重命名结构体各项. 3.定义结构体 这是需要先找到结构体初始化的地方 这里 ...

  2. 基于LLVM编译器的IDA自动结构体分析插件

    引用 这篇文章旨在介绍一款对基于LLVM的retdec开源反编译器工具进行二次开发的IDA自动结构体识别插件实现原理分析 文章目录 引用 简介 源码分析 LLVM编译器简介 Retdec源码分析 Kl ...

  3. [matlab]使用struct创建结构体

    [matlab]使用struct创建结构体 语法: struct(域名1,属性值1,域名2,属性值2,.....) 我们具体看看结构体怎么使用: >> person(1)=struct(' ...

  4. Python创建结构体和类数组

    Pygame的精灵类看的头疼,就干脆把类当C的结构体用 # 使用类创建结构体 class Bullet(object):class Struct(object):def __init__(self, ...

  5. 创建结构体数组保存5名学生的成绩单(每人包含3门课程成绩),计算每人成绩的总分,并打印出总分的最高分...

    在 C 语言中,可以使用如下的代码来创建结构体数组并保存学生的成绩单: #define N 5 #define M 3struct student {int id;char name[20];int ...

  6. new 一个结构体数组_每天一个IDA小技巧(四):结构体识别

    之前提到IDA可以将一长串的数组数据声明变成一行数组声明,简化反汇编代码,对于结构体,IDA也同样支持通过各种设置工具来改善结构体代码的可读性. 这篇文章的目标是将[edx+10h]之类的结构体元素访 ...

  7. 【C 语言】结构体 ( 结构体 数组 作为函数参数 | 数组 在 堆内存创建 )

    文章目录 一.结构体 数组 作为函数参数 ( 数组 在 堆内存创建 ) 二.完整代码示例 一.结构体 数组 作为函数参数 ( 数组 在 堆内存创建 ) 在上一篇博客 [C 语言]结构体 ( 结构体 数 ...

  8. matlab之结构体的创建与存储(CSV数据)

    1.使用的版本是2019b 2.直接双击csv文件(一定要只保留数字,其他的一概删掉),存储为数值矩阵 3.创建结构体struct,方法很简单 test_data.zero_dB=testaccura ...

  9. Matlab中结构体struct创建和使用

    在项目上遇见了调用api接口,接口返回的信息为struct,故探讨一下matlab的struct结构体 1.struct结构体创建 创建结构体数组有两种方式,分别为直接创建和使用struct函数 1. ...

  10. C++结构体(结构体创建,结构体数组,结构体指针,结构体嵌套结构体,结构体做函数参数,const变量使用)

    C++结构体(结构体创建,结构体数组,结构体指针,结构体嵌套结构体,结构体做函数参数,const变量使用) 目录 C++结构体(结构体创建,结构体数组,结构体指针,结构体嵌套结构体,结构体做函数参数, ...

最新文章

  1. Android StateListDrawable的坑
  2. ubuntu1604编译android5.1(android L)失败error: unsupportedreloc 43等问题
  3. 北风设计模式课程---2、工厂方法模式
  4. 第十天-linux命令及基础手把手实战
  5. 嵌套套娃,MySQL子查询,单行与多行子查询,相关和不相关(关联)子查询,完整详细可收藏
  6. java学习(12):i++和++i的区别
  7. 二叉排序树和平衡二叉排序树
  8. android 贝塞尔曲线_OpenGL 实践之贝塞尔曲线绘制
  9. VC++开发演算稿式计算语言-可编程绘图的计算器
  10. 三国演义人物出场统计代码含义_Python分析《三国演义》人物出场次数,孔明第二,赵云第五...
  11. Linux gcc编译简介、常用命令
  12. BootStrap Table和Mybatis Plus实现服务端分页
  13. CentOS 6.9修改网卡名eth1为eth0
  14. 2019 CCF 推荐 中文期刊
  15. 极域电子教室卸载、忘记密码解决方案
  16. 计算机硬盘分区信息,硬盘分区整数G计算公式及计算器
  17. yaml css教程,YAML 语法
  18. MicroPython之ADS1115+平滑滤波NTC温度采集
  19. c语言利用二维数组制作地图,用程序实现RPG背景地图二维数组构建
  20. wstmart不错。可以用用

热门文章

  1. HDFS的API操作-创建文件和文件夹
  2. Servlet_urlpartten配置
  3. 递归-递归的特点及基本代码演练
  4. CPU资源和可用内存大小
  5. plsql(轻量版)_记录类型1
  6. 河南职称计算机啥时间考试,河南省2020年高级职称考试时间与具体安排
  7. oracle解析select,oracle_select语句例子解析
  8. 210127阶段三进程间通信-消息队列
  9. 200815-C指针高级和链表
  10. vue 开发一个按钮组件