bin常用工具

1
2
3
4
5
upx -d file #脱壳
./checksec --file file #检查程序打开的保护
disasm 4839dd -c amd64 #将16进制字串反汇编
strace/ltrace #查看系统调用
python -c "print 'A' * 96 + '\x04\xa0\x04\x08' + str(0x80485e3)" | ./passcode #python -c cmd,直接输出重定向到程序

生成固定长度攻击字串及计算偏移

使用pwntools里面的cyclic工具(仅限32bit)

1
2
3
4
# cyclic 150 #先生成测试输入
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma
# cyclic -l 0x62616163 #gdb Invalid address 0x62616163 根据报错地址确认偏移
108

使用pattern.py(32bit/64bit通用)

1
2
3
4
# python pattern.py create 150 > input
# python pattern.py offset 0x3765413665413565
hex pattern decoded as: e5Ae6Ae7
136

打开系统coredump

1
2
ulimit -c unlimited #把core文件的大小设置为无限大
sh -c 'echo "/tmp/core.%t" > /proc/sys/kernel/core_pattern' #设置输出core文件名格式

关闭ASLR

1
2
sudo -s 
echo 0 > /proc/sys/kernel/randomize_va_space #0关闭,2打开

readelf

1
2
3
readelf -S level2 #获取各段地址,如.bss
readelf -s level2 #从符号表中查symbols
readelf -r level2 #读GOT表

objdump

1
2
3
objdump -d -j .plt level2 #查看plt表
objdump -R level2
objdump -d level2 | grep pop -C5 #查pop相关gadget

ROPgadget

1
2
3
ROPgadget --binary r2 --ropchain #ROP链
ROPgadget --binary level2 --only "pop|ret"
ROPgadget --binary ./typo --string /bin/sh

IDA

byte ptr [eax] #1byte
word ptr [eax] #2byte
dword ptr [eax] #4byte

反调试

常用反调试函数MD5值:

偏移 函数名 MD5值
ntdll.dll 771fe56e ZwQuerySystemInformation a7d7bcc95a86a6df3b0a1ccb3c69d440
ntdll.dll 771feb5b ZwSetInformationThread 8eb35a28209979fe6a9983cff0d23c5a
kernel32.dll   CheckRemoteDebuggerPresent 05577e1568efa10b8166728bfb414c59

运行时调整/打印数据

设置断点->Debugger->Breakpoints->Breakpoint,选中其中一个断点,

右键->Edit->Condition处输入IDC脚本:Message(“ECX: %x\n, ECX);

如果仅仅需要输出,不需要拦截,Actions处把Break前的勾去掉

修改binary

选中需要修改的字节,Edit->Patch program->Change byte(或者右键直接Fill with NOPs),然后Edit->Patch program->Apply patches to input file。

反汇编界面

按D可以定义数据,按P可以变成函数,按U可以转成未定义,按C可以变成代码,按A变成字符串。

call analysic failed

无法识别调用参数,按y设置参数类型和个数int sub_401390(int, int)

sp-analysis failed,调用栈不平衡

Option->General->Disassembly, 将选项Stack pointer打勾 观察每条call sub_xxxxxx前后的堆栈指针是否平衡

.text:0000000000000F5A 020                 xor     eax, eax
.text:0000000000000F5C 020                 jz      short loc_F62
.text:0000000000000F5E 020                 add     rsp, 4
.text:0000000000000F62
.text:0000000000000F62     loc_F62:  
.text:0000000000000F62 01C                 pop     rax

由于xor eax,eax一定会引起跳转,因此F5E处的add rsp,4可认为是花指令,选中F5E 的add rsp, 4,右键

  • Fill with NOPs(推荐)

  • change stack pointer(alt+K),将0x4修改为0x0

按D再按C键刷一下函数定义

花指令

  • jz/jnz
.text:0025157C 74 03           jz  short near ptr loc_251580+1
.text:0025157C 75 01           jnz short near ptr loc_251580+1
.text:00251580          loc_251580:
.text:00251580 E3 E8   #jz和jnz都跳转251580+1,因此251580字节为花指令,Fill with NOPs
  • call $+5 pop rax/rbx(sample:flodbg-dfjkctf-2019)
.text:4009A9 E8 00 00 00 00    call    $+5 #call偏移量为0(即下一条指令),并将其作为返回地址压栈
.text:4009AE 58                pop     rax #出栈
.text:4009AF 48 83 C0 0A       add     rax, 0Ah #加0x0A
.text:4009B3 FF E0             jmp     rax #跳到0x4009AE+0x0A=4009B8处
.text:4009B5 EB EB EB          db 3 dup(0EBh) #可以为任意值
.text:4009B8
;类似,可以借助rbx等其他寄存器
.text:4009B8 E8 00 00 00 00    call    $+5
.text:4009BD 5B                pop     rbx
.text:4009BE 48 83 C3 0A       add     rbx, 0Ah
.text:4009C2 FF E3             jmp     rbx #跳到0x4009BD+0x0A=0x4009C7处
.text:4009C4 EB EB EB
  • EB FF/05(sample:flodbg-dfjkctf-2019)
.text:4009E0 EB FF             jmp     loc_4009E0+1 #jmp 0x4009E2+0xFF=0x4009E1
.text:4009E2 C0                db 0C0h
.text:4009E3 FF C8             dec     eax
;patch 0x4009E0为90,得到0x4009E1
.text:4009E1 FF C0             inc     eax
.text:4009E3 FF C8             dec     eax
.text:4009E5 66 B8 EB 05       mov     ax, 5EBh
.text:4009E9 31 C0             xor     eax, eax
.text:4009EB 74 FA             jz      loc_4009E5+2 #必然跳转4009E5+2=4009E7
.text:4009ED EB 90             jmp     short loc_40097F
;patch 0x4009E5为90,得到0x4009E7
.text:4009E7 EB 05             jmp     loc_4009ED+1 #跳转到4009E9+5=4009EE
.text:4009E9 31 C0             xor     eax, eax
.text:4009EB 74 FA             jz      short loc_4009E7
.text:4009ED EB 90             jmp     short loc_40097F
  • jmp rax
1
2
3
mov rax, [rsp+0]
add byte ptr [rsp+0], 8
jmp rax

查找系统调用

(G->输入GetWindowTextA)/(Ctrl+L,找到GetWindowTextA) -> X(Jump to cross ref)

Alt+T->输入GetWindowTextA,Find all occurences

查找数据

Jump->Jump to segment(Ctrl+S),找.rdata(readONLY)和.data(writable)

View->Open subviews->String(Shift+F12)

查找函数调用关系

View -> Open subview -> Function calls 上半部分列出所有调用当前函数的位置,下半部分列出当前函数调用的别的函数

提取数据

  • View->Open Subviews->Strings,选中某个字符串
  • 右键->Array,填数组长度,转换为Array

Shift+E提取数据

反汇编界面显示字节码

Options->General->Number of opcode bytes (non-graph) : 8

运行脚本

File->Script Command

1
2
//idc
auto i;for(i=0;i<0x61;i++){PatchByte(0xF21013+i, Byte(0xf21013+i)^90);}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#python
#patch
key = idc.Byte(0x417000 + array[i])
idc.PatchByte(0x4015E0 + i, src ^ key)

#dump data,sample: father,huwang-2018
addr = 0xDEAD000
end = 0xDEB0000
with open('father64','wb') as f:
  f.write(get_bytes(addr, end-addr))

#extract op value, https://blog.csdn.net/LoopherBear/article/details/87198647
mnemonic=idc.GetMnem(addr) #imul
op1=idc.GetOpnd(addr,0) #eax
type=idc.GetOpType(addr,0) == ida_ua.o_imm #0:o_void;1:o_reg;2:o_mem;5:o_imm
value=idc.GetOperandValue(addr,0) #0

#get register value
reg = "al"
data.append(idc.get_reg_value(reg))

The function has undefined instruction/data at the specified address.

在下方Output Windows找到对应位置,将指令更改为nop。(sample:find_key-安恒周周练-2018-03)

please position the cursor within a function:

右键 creat func

ida动态调试

  • dbgsrv目录下的linux_server或者linux_serverx64放到Linux虚拟机中,运行,会监听端口,默认23946,IDA Debugger选Remote Linux debugger,设置:

    Application=Inputfile=/mnt/shared/path/crackme

    Directory=/mnt/shared/path

    Hostname填Linux的IP

  • Windows类似,运行win32_remotewin64_remote64,选Remote Windows debugger,HostName填:127.0.0.1

IDA下断点,然后start process(注意attach process会报无权限)

too big function

修改cfg\hexrays.cfgMAX_FUNCSIZE=1024

恢复符号表

rizzo插件,打开可能对应的库文件如:libc.so.6,File->Produce file->Rizzo signature file,然后打开去除了符号表的文件,File->Load file->Rizzo signature file。

Windbg

bu fathom!SetFile #设断点 (view->Disassembly->输入GWoC+0x134c,按F9加断点)

bl #列断点

p/t #步过、步进

db/dd/dQ ebp-30 #1/2/4字节

OllyDbg

F2 下断点,Backspace 删除分析,Shift+F9 忽略异常运行

ESP定律脱壳

OEP(Original Entry Point):程序的入口点 。当PE文件运行开始的时候,也就是进入壳的第一行代码的时候 ,寄存器的值和即将执行OEP的时候完全相同(除了EIP)。

根据经验,多数壳运行到OEP的时候,ESP=0012FFC4,而到达OEP之后,绝大多数的程序第一句都是压栈,因此对0012FFC0下硬件断点(右键->Breakpoint/Hardware,on execution,或者命令行键入:HW 12FFC0),即可以停在OEP的第二句,右击程序当前位置第一行代码,选择OllyDump脱壳调试进程。

内存断点不修改原代码,遇到代码校验(会导致INT3断点失败),硬件断点失灵的情况下,可以用内存断点代替:

  • 内存断点等效与命令bpm,他的中断要用到DR0-DR7的调试寄存器,也就是说OD通过这些DR0-DR7的调试寄存器来判断是否断下
  • 普通断点(F2下断)等效于bpx,他是在所执行的的代码的当前地址的一个字节修改为CC(INT3)。当程序运行到INT3的时候就会产生一个异常,而这个异常将交给OD处理,把这个异常的regEIP-1以后就正好停在了需要的中断的地方(这个根据系统不同会不一样),同时OD在把上面的int3修改回原来的代码。

两次内存断点法,前提:壳的解码按code段,data段,rsrc段的顺序,因此假如data段或者rsrc段内存断点被触发,意味code段解码完成:

  1. 对data段下内存访问(写入)断点 ,断在壳对data段的解压写入数据
  2. 对code段下内存访问(执行)断点,断在执行OEP处

无法使用内存断点法的情况:

  1. 像UPX和ASPACK这样当一个壳如果在JMP到OEP前的一行代码仍在对code段解压,就没必要使用内存断点
  2. 在OEP处有stolen code的代码

ctrl+A 分析代码

右键反汇编窗口任意位置→复制到可执行文件→所有修改→全部复制

gdb

finish执行到当前函数返回

重定向输入

1
> r < input

断点

1
2
3
4
5
6
b *0x4071af
b *$rebase(偏移)
b *main+209
bp ptrace
i b
delete 1

跟踪访问

1
2
3
awatch (char)0x7fff40aaf  硬件内存访问(读写)断点
rwatch                                    读断点
watch                                     写断点

修改内存/寄存器

1
2
3
4
set $cl=0x23
set (char)0x7ffff40aaf=0x23
set $rip = 0x401600 #修改程序流向
set $eax=0 #修改返回结果为需要的值0

查看内存

x/<n/f/u> <addr>

Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),t(binary), f(float), a(address), i(instruction), c(char) and s(string).

Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).

1
2
3
4
5
x/10gx 以8字节按16进制显示10个值
x/10wx 以4字节按16进制显示10个值
x/4hx  以2字节按16进制显示4个值
x/8bs  以1字节按字符串显示8个值
x/10i  显示10个汇编指令,等效于disassemble指令

pwndbg

参考调教gdb

1
2
cd ~/Download/pwndbg
vi pwndbg/commands/heap.py
  • 使得heap命令打印chunk的时候prevsize和size显示为16进制,将def malloc_chunk(addr):倒数第二行print(header, chunk[“value”])修改为:
    1
    2
    3
    4
    5
    6
    7
      ## edit start
      chunk_str='{\n'
      for key in chunk["value"].type.keys():
          chunk_str+='  %s = %s,\n'%(str(key),hex(int(chunk["value"][key])))
      chunk_str+='}'
      print(header, chunk_str)
      ## edit end
    
  • 加入fake_fastbin_all函数:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    @pwndbg.commands.ParsedCommand
    @pwndbg.commands.OnlyWhenRunning
    def fake_fastbin_all(addr):
      """
      Finds candidate fake fast chunks that will overlap with the specified
      address. Used for fastbin dups and house of spirit
      """
      main_heap = pwndbg.heap.current
      max_fast = main_heap.global_max_fast
      max_idx  = main_heap.fastbin_index(max_fast)
      start    = int(addr) - int(max_fast)
      mem      = pwndbg.memory.read(start, max_fast, partial=True)
      fmt = {
          'little': '<',
          'big': '>'
      }[pwndbg.arch.endian] + {
          4: 'I',
          8: 'Q'
      }[pwndbg.arch.ptrsize]
      print(C.banner("FAKE CHUNKS"))
      for idx in range(max_idx +1):
          if pwndbg.arch.ptrsize == 8:
              print(message.hint(hex((idx+2)<<4))+": ")
          else:
              print(message.hint(hex((idx+2)<<3))+": ")
          for offset in range(max_fast - pwndbg.arch.ptrsize):
              candidate = mem[offset:offset + pwndbg.arch.ptrsize]
              if len(candidate) == pwndbg.arch.ptrsize:
                  value = struct.unpack(fmt, candidate)[0]
                  if main_heap.fastbin_index(value&0xffffffff) == idx:
                      print('[+]',hex(start+offset-pwndbg.arch.ptrsize),', padding len:',hex(int(addr)-start-offset-pwndbg.arch.ptrsize))
    

查找fast chunk

1
2
find_fake_fast 0x602000 0x7f
fake_fastbin_all 0x7f690516ab10 (需要按上一步操作先加入fake_fastbin_all函数)

找rop,一般由ROPgadget代替

1
rop --grep "pop edi" -- --nojop -nosys --depth 2

查找数值或字符串

1
2
search -p 0x74d10df00
search -s flag

直接计算数值

1
2
print /x 0x7fd798586b10-0x7fd798586aed
$2 = 0x23

查看各段地址和权限

1
2
vmmap
0x00007ffff7a0d000 0x00007ffff7bcd000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.23.so

查看栈

1
telescope $rsp

查看格式化字符串参数偏移

1
fmtarg 0x00007fffffffdb28 #break在printf

DOSBox

1
2
3
4
5
6
7
8
9
10
cp D:\ctf\tools\re086\debug.exe D:\ctf\matches02330513cqgame\re_OldSymbolicCode
z:
cp D:\ctf\tools\re\8086\debug.exe D:\ctf\matches\2023\230513cqgame\re_OldSymbolicCode
z:\>mount c D:\ctf\matches\2023\230513cqgame\re_OldSymbolicCode
z:\>c:
c:\>debug a.com
常用调试命令:https://blog.csdn.net/m0_68757308/article/details/130773663
g 偏移地址 程序自动执行到输入的偏移地址处的程序
d 显示内存
t 执行CS:IP处的指令
r 寄存器 显示或修改寄存器
u 反汇编
gt;mount c D:\ctf\matches02330513cqgame\re_OldSymbolicCode z:
cp D:\ctf\tools\re\8086\debug.exe D:\ctf\matches\2023\230513cqgame\re_OldSymbolicCode
z:\>mount c D:\ctf\matches\2023\230513cqgame\re_OldSymbolicCode
z:\>c:
c:\>debug a.com
常用调试命令:https://blog.csdn.net/m0_68757308/article/details/130773663
g 偏移地址 程序自动执行到输入的偏移地址处的程序
d 显示内存
t 执行CS:IP处的指令
r 寄存器 显示或修改寄存器
u 反汇编
gt;c: c:
cp D:\ctf\tools\re\8086\debug.exe D:\ctf\matches\2023\230513cqgame\re_OldSymbolicCode
z:\>mount c D:\ctf\matches\2023\230513cqgame\re_OldSymbolicCode
z:\>c:
c:\>debug a.com
常用调试命令:https://blog.csdn.net/m0_68757308/article/details/130773663
g 偏移地址 程序自动执行到输入的偏移地址处的程序
d 显示内存
t 执行CS:IP处的指令
r 寄存器 显示或修改寄存器
u 反汇编
gt;debug a.com 常用调试命令:https://blog.csdn.net/m0_68757308/article/details/130773663 g 偏移地址 程序自动执行到输入的偏移地址处的程序 d 显示内存 t 执行CS:IP处的指令 r 寄存器 显示或修改寄存器 u 反汇编

qemu调试arm和mips

环境搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ sudo apt-get install gdb-multiarch
$ sudo apt-get install qemu-user #安装qemu
$ sudo apt-get install "binfmt*" #binfmt用于识别文件类型

$ apt-cache search "libc6" | grep mips #根据file elf的输出选择适当的cross库
$ sudo apt-get install libc6-mipsel-cross # ELF 32-bit LSB executable, MIPS
$ sudo apt-get install libc6-mips-cross # ELF 32-bit MSB executable, MIPS
$ sudo apt-get install libc6-armhf-armel-cross # For ARM

$ sudo mkdir /etc/qemu-binfmt #binfmt用于搜索lib默认目录
$ sudo ln -s /usr/mips-linux-gnu /etc/qemu-binfmt/mips
$ sudo ln -s /usr/mipsel-linux-gnu /etc/qemu-binfmt/mipsel
$ sudo ln -s /usr/arm-linux-gnueabihf /etc/qemu-binfmt/arm # ARM

#如果出现qemu: uncaught target signal 11 (Segmentation fault) - core dumped,通过strace ./ipowtn查看发现:openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3,参考:https://bugs.launchpad.net/qemu/+bug/1701798
$ sudo mkdir /etc/qemu-binfmt/mips/etc
$ sudo ln -s /nonexistent /etc/qemu-binfmt/mips/etc/ld.so.cache #在对应架构下新建一个不存在的ld.so.cache文件

You can override the default sysroot by setting the QEMU_LD_PREFIX environment variable. This affects where qemu will look for files when open() is called, e.g. when the linker is attempting to resolve libc.so

调试

1
2
3
4
$ qemu-mipsel -g 1234 -L /usr/mipsel-linux-gnu/ ./add
$ gdb-multiarch ./add
pwndbg> set architecture mips #如果pwndbg能自动识别,可省略
pwndbg> target remote localhost:1234

反编译mips代码

retdecretdec-idaplugin

  1. 安装配置python3到PATH
  2. 下载Windows版本retdec
  3. 下载最新版本retdec-idaplugin,解压retdec.dll到ida的plugins目录
  4. 打开ida->Options,配置retdec-decompiler.py脚本的路径
  5. 选中代码,Ctrl+D即可反编译

ghidra