零散

  • 如何找main
  • 导航栏
    • 灰条:数据
    • 蓝色:代码
  • 反汇编
    • arg:参数
    • var:局部变量
  • general->IDA options
    • line prefixes:显示十六进制地址

image.png

  • Auto comments:显示汇编指令含义
    • 交叉引用
  • 双击上箭头可以跳到交叉引用的位置(x)

image.png

快捷键

快捷键 功能
C 转换为代码 一般在IDA无法识别代码时使用这两个功能整理代码(代码混淆)
D 转换为数据
A 转换为字符
N 为标签重命名 方便记忆,避免重复分析。
添加注释
R 把立即值转换为字符 便于分析立即值
H 把立即值转换为10进制
Q 把立即值转换为16进制
B 把立即值转换为2进制
G 跳转到指定地址
X 交叉参考 便于查找API或变量的引用
SHIFT+/ 计算器
ALT+ENTER 新建窗口并跳转到选中地址 这四个功能都是方便在不同函数之间分析(尤其是多层次的调用)。具体使用看个人喜好
ALT+F3 关闭当前分析窗口
ESC 返回前一个保存位置
CTRL+ENTER 返回后一个保存位置
Alt+b 查找
F5 汇编->源码
shift + F12 打开string窗口
Alt+6(option+6) 导入表
  • mov eax,0ccccccch以上的汇编可以忽略分析(debug版本中编译器加上的)
  • rep stosd:区域填充,给edi的地址填充eax的值,填充次数由ecx决定(数组初始化

    • mov [edi],eax;edi=edi+4;
    • 填充方向由方向标志 DF 来决定,可以由指令 cldstd 指令设置

      cld: 从低地址往高地址传送 std: 从高地址往低地址传送

  • mov [ebp+edx*4],ecx

    • edx是数组下标,一个项占4个字节(32位)
  • 初始化时未使用的变量先不管

  • 流程图中折叠分支
    • 右键 -> group nodes

IDC脚本(去混淆、解密)

  • file->script command

image.png

  • file->script file

    • .py
    • .idc

      静态脱壳(MSLRHv0.31)

  • IDA打开程序,发现几句一个跳转,很像是被混淆过。

image.png

image.png

  • 动态或静态方法找到与开头pusha对应的popa,发现有花指令,干扰定位,因此无法直接根据popa找OEP

image.png

  • 修改代码,使IDA在loc_4560FA能继续往下分析
    • 红颜色箭头:代码分析有冲突

image.png

  • 按d,将分析不正确的地方先转为数据

image.png
image.png

  • 再将合理的机器码按c转成代码。
    • pushad popad
    • jz jnz(后面的jmp一般是花指令)

image.png

  • call jmp(E8 EB)
  • jmp跳转到一个未分析的地址or一个错误的地址(转成d后,在跳转的位置c)

image.png

  • 有红色标记
  • sun eax,[esp-8+arg_4] == sub eax,[esp]

image.png

  • 继续向下修改混淆(花指令)的地方,发现有重复的模式

image.png
image.png
image.png
image.png
image.png image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png

  • 整理修改后的代码逻辑,把花指令直接nop掉(打补丁)

    • FindBinary:从给定的地址(arg1,一般是0x3)按指定的方式(arg2)搜索一个数据项(arg3)
      • arg2:
        • SEARCH_UP,SEARCH_DOWN 用来指明搜索方向
        • SEARCH_NEXT 用来获取下一个已经找到的对象
        • SEARCH_CASE用来指明是否区分大小写
        • SEARCH_NOSHOW 用来指明是否显示搜索的进度
        • SEARCH_UNICODE用于将所有搜索字符串视为Unicode
    • MinEA()获取可执行文件的最开始的地址
    • PatchByte:打一个字节的补丁
    • PatchWord:打一个字的补丁
    • PatchDword:打一个双字的补丁
    • BADADDR

      1. static PatchJunkCode(){
      2. auto x,FBin,ProcRange;
      3. FBin = "E8 0A 00 00 00 E8 EB 0C 00 00 E8 F6 FF FF FF";//无用代码的机器码(无用的call)
      4. for(x=FindBinary(MinEA(),0x03,FBin);x!=BADADDR;x=FindBinary(x,0x03,FBin))
      5. {
      6. x=x+5;//nop E8
      7. PatchByte(x,0x90);
      8. x+=3;
      9. PatchByte(x,0x90);
      10. x++;
      11. PatchWord(x,0x9090);
      12. x+=2;
      13. PatchDword(x,0x90909090);
      14. }
      15. FBin2 = "74 04 75 02 EB 02 EB 01 81";//jnz jz
      16. for(x=FindBinary(MinEA(),0x03,FBin);x!=BADADDR;x=FindBinary(x,0x03,FBin))
      17. {
      18. }
      19. FBin3 = "50 E8 02 00 00 00 29 5A 58 6B C0 03 E8 02 00 00 00 29 5A 83 C4 04"//jmp->call->jmp->call...
      20. for(x=FindBinary(MinEA(),0x03,FBin);x!=BADADDR;x=FindBinary(x,0x03,FBin))
      21. {
      22. }
      23. FBin4 = "EB 01 68 EB 02 CD 20 EB 01 E8";//连环jmp
      24. for(x=FindBinary(MinEA(),0x03,FBin);x!=BADADDR;x=FindBinary(x,0x03,FBin))
      25. {
      26. }
      27. }
  • 把nop隐藏掉(方便查看其他代码)

    • HideArea:隐藏一个区域
    • MakeString:转换成字符串
    • MakeCode:转换成代码
    • MakeUnknow:转换成未知类型(IDA7.0:ida_bytes.del_items)
      • arg1:起始地址
      • arg2:长度
    • HideArea
      • arg1:起始
      • arg2:结束
      • arg3:折叠起来唯一出现的
      • arg4:header(展开)
      • arg5:footer(展开)
      • arg6:color(-1: 默认)
    • atoa:Convert address value to a string
      1. static HideJunkCode{
      2. auto x,y,FBin;
      3. FBin = "E8 0A 00 00 00 E8 EB 0C 00 00 E8 F6 FF FF FF";
      4. for(x=FindBinary(MinEA(),0x03,FBin);x!=BADADDR;x=FindBinary(x,0x03,FBin))
      5. {
      6. //x:起始地址
      7. MakeUnknow(x,0x0F,1);
      8. //y:结束地址
      9. y=x+0x0F;
      10. HideArea(x,y,atoa(x),atoa(x),atoa(y),-1);
      11. }
      12. 。。。
      13. }
  • 由于花指令的关系,会使IDA错误识别指令,可能隐藏区域的边界刚好在一条指令的机械码中间,这样隐藏的操作便会失败。因此在隐藏指令执行之前,先使用MakeUnknown将目标代码设置为未识别的状态。在完成隐藏和替换之后,再使用分析引擎分析代码。

    • AnalyzeArea (IDA7:idc.plan_and_wait)
      • Perform full analysis of the range
        1. #include<idc.idc>
        2. static main{
        3. HideJunkCode();
        4. PatchJunkCode();
        5. AnalyzeArea(MinEA(),MaxEA());
        6. }

SEH

  • SEH是window操作系统默认的异常处理机制,逆向分析中,SEH除了基本的异常处理功能外,还大量用于反调试程序
  • SEH的反调试原理
    • 利用异常处理例程来进行反调试
      • 首先安装好一个异常处理例程,然后故意制造一个异常
    • 未处理异常用于反跟踪
      • UnhandledExceptionFilter在没有debugger attach的时候才会被调用。所以,SetUnhandledExceptionFilter函数还有一个妙用,就是让某些敏感代码避开debugger的追踪。比如你想把一些代码保护起来,避免调试器的追踪,可以采用的方法:
        • 常规反调试
        • 把代码放到SetUnhandledExceptionFilter设定的函数里面。通过人为触发一个unhandled exception来执行。由于设定的UnhandledExceptionFilter函数只有在调试器没有加载的时
          候才会被系统调用,这里巧妙地使用了系统的这个功能来保护代码(UnhandledExceptionFilter是否调用取决于系统内核的判断)
  • cli:关中断
    • 关中断后可以任意处理程序4GB空间
  • 可以导致调试器异常
    • trap to debugger

硬件断点的检查

  • 假如没有设置硬件断点,那么ECX的结果应该是0。
    1. seg005:004587B6 mov eax, [esp+0Ch]
    2. seg005:004587BA xor ecx, ecx
    3. seg005:004587BC xor ecx, [eax+4]
    4. seg005:004587BF xor ecx, [eax+8]
    5. seg005:004587C2 xor ecx, [eax+0Ch]
    6. seg005:004587C5 xor ecx, [eax+10h]

循环

  • while:蓝颜色
  • do…while:绿颜色
  • 执行路径:红色

反汇编片段

1)编译器添加

image.png

2)两个数组赋值(找准基地值和下标)

image.png
image.png

3)0FFFFFFFFh = -1

4)数组初始化(顺序!!)

  • char:一个字节(也可以定义为BYTE)

image.png
char s[]={0x2,0x3,0x4,0x5,0x4,0x2,0x1,0x2,0x3,0x2C,0x1B};

5)数组循环比较

  • movsx(数组内每个值赋值)

image.png

6))函数调用与清理

image.png

7)使用BYTE变量

  • include

  • include

image.png

8)数组取值

image.png
image.png

9)交换

image.png

(ebp_var_4)=(ebp_arg_0)
(ebp_arg_0)=(ebp_arg_4)
(ebp_arg_4)=
(ebp_var_4)

10)双层循环

image.png

11)创建结构体

  • dd:双字 dw:字
  • 特点:连续赋值中几字节的都有,注意观察后续字符串等,可能会提示结构体里有什么
    • 在structure页插入结构体后(i),光标在ends上,点击d插入值,然后在外部这个值上按ALT+Q来修改结构

image.png
image.png
image.png
image.png
image.png

  • r[]内元素个数:sizeof(r)/sizeof(role)
  • 结构体的遍历(每个结构体元素20个字节 :020 120…):

image.png

  • 导入C声明的结构体

    • View—>Open Subviews—>Local Types中可以看到本地已有的结构体,在该窗口中右击insert
    • 同时我们发现structure视图里面,并没有这个结构体,我们需要对着my_structure右击,选择 synchronize to idb

      12)创建数组

  • 点击选中你想要转换成数组的一块区域

image.png

  • 在菜单栏中选择:edit—>array

image.png

IDAPython

  • 三个python模块
    • idaapi:访问核心IDA API
    • idc:提供IDA中所有函数功能
    • idautils:提供实用函数
  • 实例 ```cpp

    导入包

    import idaapi import idc import idautils

获取函数的引用

for addr in XrefsTo(0x00405BF0, flags=0): print hex(addr.frm)

前一句&后一句指令的地址

idc.PrevHead(addr) idc.NextHead(addr)

获得操作码

GetMnem(addr)

获得操作数

GetOpnd(addr, 0)
GetOperandValue(addr, 1)

添加注释

MakeComm(x.frm, dec)

获取pe导出表函数名

pe = pefile.PE(dll_path)
for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols: if exp.name: expname.append(exp.name)

找到程序 _start (针对 ELF 而言) 

ea = BeginEA()

在.text段中 SegStart() 找到程序地址最小的汇编指令的地址,SegEnd() 找到程序地址最大的汇编指令的地址

for funcea in Functions(SegStart(ea), SegEnd(ea)):

获得函数名

functionName = GetFunctionName(funcea)
```

keypatch

  • 打补丁

IDA常见命名意义

  • sub 指令和子函数起点
  • locret 返回指令
  • loc 指令
  • off 数据,包含偏移量
  • seg 数据,包含段地址值
  • asc 数据,ASCII字符串
  • byte 数据,字节(或字节数组)
  • word 数据,16位数据(或字数组)
  • dword 数据,32位数据(或双字数组)
  • qword 数据,64位数据(或4字数组)
  • flt 浮点数据,32位(或浮点数组)
  • dbl 浮点数,64位(或双精度数组)
  • tbyte 浮点数,80位(或扩展精度浮点数)
  • stru 结构体(或结构体数组)
  • algn 对齐指示
  • unk 未处理字节
  • IDA中有常见的说明符号,如db、dw、dd分别代表了1个字节、2个字节、4个字节


IDA反编译报错(非花的情况)

  • image.png

    1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1756019/1615185001553-93f5664f-1a32-478e-b44b-0068fbc8dc5c.png#height=362&id=ayjw8&margin=%5Bobject%20Object%5D&name=image.png&originHeight=724&originWidth=627&originalType=binary&size=122456&status=done&style=none&width=313.5)
  • 修改报错的地方的汇编语句(改成nop)

image.png