• 以组为单位分析指令
  • 一个代码结构是一段代码的抽象层,定义了一个功能属性而不是实现细节
  • 编译器版本和设置能影响一个特定结构在反汇编代码中的表现

1、全局与局部变量

  • 全局变量:被程序中任意函数访问和使用,通过内存地址引用
  • 局部变量:在它被定义的函数中访问,通过栈地址引用
  • 例:全局变量x通过dword_40CF60标记,指0x400CF60处的内存位置,当mov dword_40CF60,eax, 该内存地址的值被修改,后续变量函数都会受影响
  • 例:mov dword ptr[ebp-4],1;局部变量x位于栈上一个相对ebp的常量偏移处,可以用var_标记

2、反汇编算数操作

  • mov、add、sub、cdq、idiv(取模)
  • idiv ecx:用edx:eax除操作数(ecx)并将结果保存在eax中,余数保存在edx中
  • cdq: 这个指令把 EAX 的第 31 bit 复制到 EDX 的每一个 bit 上。 它大多出现在除法运算之前。它实际的作用只是把EDX的所有位都设成EAX最高位的值(把edx扩展为eax的高位,变成64位寄存器)

3、识别if语句

  • 基于特定条件改变程序的执行
  • cmp jnz:一般比较不成功跳转,比较成功继续执行
  • 用IDA pro图形化分析函数
  • 识别结构
  • 识别嵌套的if语句

4、识别循环

  • 找到for循环:四个组件-初始化、比较、执行指令、递增或递减
    7.jpg
    8.jpg
  • 找到while循环:接收数据包或命令
    9.jpg

10.jpg

5、理解函数调用约定

  • 调用约定决定函数调用发生的方式:参数被放到栈上或寄存器中的次序、是由调用者还是被调用者负责在函数执行完成时清理栈。
  • 不同编译器的函数调用约定有细微差别

  • cdecl

    • 参数从右到左按序被压入栈
    • 函数完成时由调用者清理栈
    • 将返回值保存在EAX中
    • 例:test(a,b,c)
      push c;push b;push a;call test;add esp 12;mov ret,eax
  • stdcall

    • 被调用者在函数完成时清理栈
    • 其余同cdecl
    • 例:test(a,b,c)
      push c;push b;push a;call test;mov ret,eax
    • windows API的标准调用约定,清理栈的责任由实现API函数代码的DLL程序承担
  • fastcall

    • 前一些参数(典型的是前两个)被传到寄存器中
    • 备用的寄存器是EDX和ECX
    • 需要的话剩下的参数再以从右到左的顺序加载到栈上
    • 通常使用fastcall比其他约定更高效,因为代码不需要涉及过多栈操作
  • 压栈与移动

    • 编译器可能会选择使用不同的指令来执行同样的操作
    • 例:VS-参数压入栈中 GCC-参数移动到栈上
      11.jpg12.jpg

6、分析switch语句

  • 用来做一个基于字符或整数的决策
  • 通常以两种方式被编译:使用if样式或使用跳转表
    • if样式:
      13.jpg
    • 跳转表:
      • 经常在庞大连续的switch语句中出现,更高效
      • 定义了一些附加内存地址的偏移,switch变量被用来作为这个跳转表的一个索引
        14.jpg
        • ja:大于则跳转,用于比较两个无符号数
        • sub ecx,1:使跳转表可以被合理的添加索引,1处指令是基于跳转表目标的位置
        • edx*4:跳转表中每一项是一个4字节大小的地址

7、反汇编数组

  • 用来定义一个相似数据项的有序集合(应用:恶意代码使用一个指向字符串的指针数组,其中包含多个主机名作为连续选项)
  • 汇编代码中数组是通过使用一个基地址作为起始点来进行访问的
    15.jpg

16.jpg

  • 数组b的基地址-dword_40A000,数组a的基地址var_14,整数数组每一个元素大小为4

8、识别结构体

  • 结构体包括不同类型的元素
  • 通过一个作为起始指针的基地址来访问,要判断附近的数据字节类型是同一个结构的组成部分还是凑巧相互挨着比较困难,这依赖于结构体的上下文
    17.jpg

19.jpg

  • gms全局变量,基地址在内存dword_40EA30处
    18.jpg

20.jpg

  • arg_0是结构体基地址,偏移0x14保存了结构中的字符
  • 偏移0x18是double变量,fld-将浮点数据压入协处理器的堆栈中,fstp-将协处理器堆栈栈顶的数据传送到目标操作数中
  • IDA pro中使用T键将创建的结构体赋给内存引用

9、分析链表遍历

  • 一个链表是包含一个数据记录序列的数据结构,每一个记录都包括一个对序列中下一记录的引用(链接)域
  • 使用链表的好处:被链接项的访问次序与数据域被保存在内存或磁盘上的次序可以不一样
  • 识别链表首先识别出一些包含指针的对象,这些指针是指向同一类型的对象。

10、实验

  • 目标:通过分析代码结构来理解一个程序的总体功能

Lab6-1

  • if结构
  • printf(前面有格式化字符串被压栈)
  • 检查是否有可用internet连接,存在打印对应字符串返回1,否则返回0(静态分析导入表API名称和string可以得出)

Lab6-2

  • if
  • printf
  • 00401040处:InternetOpeA、InternetOpenUrlA、InternetReadFile-下载网页并分析

  • InternetOpeA:初始化对WInINet库的使用,设置用于HTTP通信的User-Agent字段

  • InternetOpenUrlA:使用一个完整的FTP或HTTP的URL,来打开一个句柄
  • InternetReadFile:从上述句柄处读取数据
  • InternetCloseHandle:关闭打开文件的句柄

  • 调用该InternetReadFile,将返回的数据填充到一个字符数组中,每次一个字节的对这个数组进行比较,解析一个html注释

  • 使用Internet Explorer 7.5/pma作为User-Agent字段,从http://下载了网页

Lab6-3

  • 0x401130
  • argv[0],var_8
  • switch,跳转表
  • 打印出错信息、sleep、删除文件、创建文件夹、复制文件、修改注册表项
  • 本地特征:注册表项、本地文件名
  • 检测网络连接——>远程下载文件——>从html注释中解析出下一个字符——>用于switch决定执行的行为——>

Lab6-4

  • 多了一个循环
  • 每次计数器递增,User-Agent也会随之改变。(可以被管理和监控web服务器的攻击者跟踪恶意代码运行了多长时间)