技术宅的结界

 找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 6602|回复: 0
收起左侧

跨平台注入工具frida使用

[复制链接]

268

主题

441

帖子

4646

积分

用户组: 真·技术宅

UID
2
精华
61
威望
148 点
宅币
3478 个
贡献
126 次
宅之契约
0 份
在线时间
599 小时
注册时间
2014-1-25
发表于 2016-6-4 16:35:46 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有帐号?立即注册→加入我们

x
本帖最后由 元始天尊 于 2016-6-15 12:55 编辑

frida是跨平台注入框架,支持linux mac win android
网上基本是针对linux下安装frida的说明,这里说下windows下安装frida
frida分服务器和客户端2部分,服务器为frida-server,根据不同平台选择不同的frida-server    http://build.frida.re/frida/android
这里服务器选择arm-android平台,而客户端为frida/frida-ps/等脚本,网上可见的都是安装linux客户端

windows上运行frida客户端
1.        下载https://bootstrap.pypa.io/ez_setup.py    安装:python ez_setup.py    添加%PYTHONDIR%\Script到PATH
2.        下载https://pypi.python.org/pypi/frida对应的frida客户端版本   安装:easy_install frida.egg  测试:frida --version
3.        下载http://build.frida.re/frida/android/arm/bin/frida-server
4.        启动android arm 4.4 虚拟机
adb push frida-server /data/local/tmp/
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043
frida-ps –R验证是否成功
5.        附加进程
C:\Users\Administrator>frida -R 1024
     ____
    / _  |   Frida 7.2.0 - A world-class dynamic instrumentation framework
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at http://www.frida.re/docs/home/

[Remote:ID::1024]->

linux上运行frida客户端
pip install frida
npm install frida
pip3 install colorama prompt-toolkit pygments
其他步骤类似于win

编译windows frida客户端
1.        安装git, nodejs  https://nodejs.org/dist/v4.4.5/node-v4.4.5-x64.msi 并添加到环境变量path,如E:\Program Files\Git\bin;E:\nodejs\;
2.        Vs2013以上版本打开frida/frida.sln编译即可得到frida-server.exe

编译android-arm frida客户端
1.        安装必备包
apt-get install build-essential gcc-multilib git lib32stdc++-4.9-dev lib32z1-dev python-dev python3-dev zlib1g-dev
2.        安装npm & nodejs
wget https://nodejs.org/dist/v4.4.5/node-v4.4.5-linux-x64.tar.xz
tar –zxvf node-v4.4.5-linux-x64.tar.xz
cp –r node-v4.4.5-linux-x64/bin/* /usr/local/bin/
cp –r node-v4.4.5-linux-x64/lib/* /usr/local/lib/
cp –r node-v4.4.5-linux-x64/include/* /usr/local/include/
cp –r node-v4.4.5-linux-x64/share/* /usr/local/share/
cp /usr/local/bin/node /usr/bin/nodejs
chmod 777 /usr/bin/nodejs
注意/usr/local/bin/npm是软链接,指向的位置要正确
测试:node –v   nodejs –v    npm -v
3.        git clone git://github.com/frida/frida.git
cd frida
make
make server-android

编译android-x86 frida客户端
由于frida不直接提供android-x86平台frida-server编译,因此需要修改makefile
Makefile.linux.mk
server-android: build/frida_stripped-android-arm/bin/frida-server build/frida_stripped-android-arm64/bin/frida-server ##@server Build for Android
        mkdir -p $(BINDIST)/bin
        cp -f build/frida_stripped-android-arm/bin/frida-server $(BINDIST)/bin/frida-server-android
        cp -f build/frida_stripped-android-arm64/bin/frida-server $(BINDIST)/bin/frida-server-android64
之后添加
server-android-i386: build/frida_stripped-android-i386/bin/frida-server ##@server Build for Android-i386
        mkdir -p $(BINDIST)/bin
        cp -f build/frida_stripped-android-i386/bin/frida-server $(BINDIST)/bin/frida-server-android-i386
.PHONY: \
        help \
        distclean clean clean-submodules git-submodules git-submodule-stamps \
        capstone-update-submodule-stamp \
        gum-32 gum-64 gum-android check-gum-32 check-gum-64 frida-gum-update-submodule-stamp \
        core-32 core-64 core-android check-core-32 check-core-64 frida-core-update-submodule-stamp \
        server-32 server-64 server-android server-qnx-arm server-qnx-armeabi \
添加server-android-i386
最后make server-android-i386
frida-ps用于枚举进程
Usage: frida-ps-script.py [options]
Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -D ID, --device=ID    connect to device with the given ID
  -U, --usb             connect to USB device
  -R, --remote          connect to remote frida-server
  -H HOST, --host=HOST  connect to remote frida-server on HOST
  -a, --applications    list only applications
  -i, --installed       include all installed applications

# Connect Frida to an iPad over USB and list running processes
$ frida-ps -U
# List running applications
$ frida-ps -Ua
# List installed applications
$ frida-ps –Uai
frida-trace动态跟踪工具,类似于strace
Usage: frida-trace-script.py [options] target
Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -D ID, --device=ID    connect to device with the given ID
  -U, --usb             connect to USB device
  -R, --remote          connect to remote frida-server
  -H HOST, --host=HOST  connect to remote frida-server on HOST
  -f FILE, --file=FILE  spawn FILE
  -n NAME, --attach-name=NAME                   attach to NAME
  -p PID, --attach-pid=PID                         attach to PID
  --debug               enable the Node.js compatible script debugger
  --disable-jit         disable JIT
  -I MODULE, --include-module=MODULE     include MODULE
  -X MODULE, --exclude-module=MODULE   exclude MODULE
  -i FUNCTION, --include=FUNCTION               include FUNCTION
  -x FUNCTION, --exclude=FUNCTION             exclude FUNCTION
  -a MODULE!OFFSET, --add=MODULE!OFFSET     add MODULE!OFFSET
  -T, --include-imports                                          include program's imports
  -t MODULE, --include-module-imports=MODULE       include MODULE imports
  -m OBJC_METHOD, --include-objc-method=OBJC_METHOD     include OBJC_METHOD

实例:跟踪twitter进程的recv read函数
frida-trace -i "recv*" -i "read*" *twitter*
recv: Auto-generated handler: …/recv.js
# (snip)
recvfrom: Auto-generated handler: …/recvfrom.js
Started tracing 21 functions. Press Ctrl+C to stop.
    39 ms       recv()
   112 ms       recvfrom()
   128 ms       recvfrom()
   129 ms       recvfrom()
得到的recv.js
{
onEnter: function onEnter(log, args, state) {
        log("recvfrom()");
},
onLeave: function onLeave(log, retval, state) {
}
}
修改onEnter函数为
log("recvfrom(socket=" + args[0].toInt32()
    + ", buffer=" + args[1]
    + ", length=" + args[2].toInt32()
    + ", flags=" + args[3]
    + ", address=" + args[4]
    + ", address_len=" + Memory.readPointer(args[5]).toInt32()
+ ")");
得到如下日志
  8098 ms       recvfrom(socket=70,
                         buffer=0x32cc018, length=65536,
                         flags=0x0,
                         address=0xb0420bd8, address_len=16)

实例:拦截记事本进程打开文件并输出
C:\Users\Administrator>frida-trace -i "CreateFileW" -f c:\windows\system32\notepad.exe
Instrumenting functions...
CreateFileW: Loaded handler at "C:\Users\Administrator\__handlers__\KERNEL32.DLL\CreateFileW.js"
CreateFileW: Loaded handler at "C:\Users\Administrator\__handlers__\KERNELBASE.dll\CreateFileW.js"
Started tracing 2 functions. Press Ctrl+C to stop.
           /* TID 0x3b4c */
   133 ms  CreateFileW()
   133 ms  CreateFileW()
   134 ms  CreateFileW()
。。。。。。。。。。。。。。。
修改CreateFileW.js,为
    onEnter: function (log, args, state) {
        log(Memory.readUtf16String(args[0]));
},
得到输出
1559303 ms  C:\Users\Administrator\APPLIC~1\desktop.ini
           /* TID 0x3b4c */
esources\Themes\Aero\Shell\NormalColor\ShellStyle.dll
esources\Themes\Aero\Shell\NormalColor\ShellStyle.dll
esources\Themes\Aero\Shell\NormalColor\ShellStyle.dll
esources\Themes\Aero\Shell\NormalColor\ShellStyle.dll
esources\Themes\Aero\Shell\NormalColor\ShellStyle.dll
           /* TID 0x3824 */
1559314 ms  C:\Users\Administrator\Contacts\desktop.ini
1559315 ms  C:\Users\Administrator\Cookies\desktop.ini
           /* TID 0x1a74 */
1559315 ms  C:\Users\Administrator\Desktop\desktop.ini
1559316 ms  C:\Users\Administrator\Desktop
1559316 ms  C:\Users\Public\Desktop\desktop.ini
1559316 ms  C:\Users\Public\Desktop
           /* TID 0x3824 */
frida-discover用于发现内部函数

frida-ls-devices枚举设备
Usage: frida-ls-devices-script.py [options]
Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -D ID, --device=ID    connect to device with the given ID
  -U, --usb             connect to USB device
  -R, --remote          connect to remote frida-server
  -H HOST, --host=HOST  connect to remote frida-server on HOST
C:\Users\Administrator>frida-ls-devices
Id     Type    Name
-----  ------  ------------
local  local   Local System
tcp    remote  Local TCP

Frida API内存分配内存Memory.alloc(size)                   在堆上分配内存
         Memory.allocUtf8String(str)            分配utf字符串
         Memory.allocUtf16String                  分配utf16字符串
         Memory.allocAnsiString                     分配ansi字符串
搜索内存Memory.scanSync(address, size, pattern)
pattern模式             13 37 ?? ff
拷贝内存Memory.copy(dst,src,n)
修改内存权限Memory.protect(address,size,protection)
         protection保护类型,和Process.enumerateRanges()返回结果一致,例如Memory.protect(ptr("0x1234"), 4096, 'rw-');
读写内存
         Memory.readPointer(address) Memory.writePointer(address,ptr)                 读写指针类型数据
         Memory.readByteArray(address,length) Memory.writeByteArray(address,bytes)读写字节数组
         Memory.readCString(address[,size = -1])
         Memory.readUtf8String(address[,size = -1]) Memory.writeUtf8String(address,str)读写utf8字符串
         Memory.readUtf16String(address[,size = -1]) Memory.writeUtf16String(address,str)读写utf16字符串
         Memory.readAnsiString(address[,size = -1]) Memory.writeAnsiString(address,str)读写utf16字符串

         Memory.readS8(address) Memory.writeS8(address)                                          读写int8类型数据
         Memory.readS16(address) Memory.writeS16(address)                                               读写int16类型数据
         Memory.readS32(address) Memory.writeS32(address)                                               读写int32类型数据
Memory.readS64(address) Memory.writeS64(address)                                               读写int64类型数据
         Memory.readShort(address) Memory.writeShort(address)                               读写short类型数据
Memory.readInt(address) Memory.writeInt(address)                                        读写int类型数据
Memory.readLong(address) Memory.writeLong(address)                                 读写long类型数据
Memory.readFloat(address) Memory.writeFloat(address)                                读写float类型数据
Memory.readDouble (address) Memory.writeDouble(address)                        读写double类型数据
         Memory.readU8(address) Memory.writeU8(address)                                        读写uint8类型数据
         Memory.readU16(address) Memory.writeU16(address)                                    读写uint16类型数据
         Memory.readU32(address) Memory.writeU32(address)                                    读写uint32类型数据
         Memory.readU64(address) Memory.writeU64(address)                                    读写uint64类型数据
         Memory.readUShort(address) Memory.writeUShort(address)                         读写ushort类型数据
Memory.readUInt(address) Memory.writeUInt(address)                                  读写uint类型数据
Memory.readULong(address) Memory.writeULong(address)                           读写ulong类型数据

函数查找符号         Module.findExportByName(modname,funcname)
反汇编              Instruction.parse(ptr)

例:反汇编10
[Local:rocName::Thunder.exe]-> var addr=Module.findExportByName("kernel32.dll","CreateFileW")\
... for(var i=0;i<10;i++)\
... {\
... var result=Instruction.parse(addr);\
... addr=ptr(result.next);\
... console.log(result.address+"\t"+result.mnemonic+" "+result.opStr)\
... }
0x754c8930      jmp dword ptr [0x75520348]
0x754c8936      int3
0x754c8937      int3
0x754c8938      int3
0x754c8939      int3
0x754c893a      int3
0x754c893b      int3
0x754c893c      int3
0x754c893d      int3
0x754c893e      int3

进程/线程/模块Process.arch 处理器架构
Process.platform操作系统
Process.pageSize页大小
Process.pointerSize指针大小
Process.isDebuggerAttached()
Process.getCurrentThreadId()
Process.enumateThreadsSync()枚举线程
Process.findModuleByAddress(address)
Process.getModuleByAddress(address)
Process.findRangeByAddress(address)
Process.getRangeByAddress(address)
Process.enumerateModulesSync()
Process.enumerateRangesSync(protection|specifier)枚举内存块
Process.enumerateMallocRangesSync(protection)
Module.enumerateImportsSync(name)枚举模块导入函数
Module.enumerateExportsSYnc(name)枚举模块导出函数
Module.enumerateRangesSync(name,protection)枚举模块内存块
Module.findBaseAddress(name)获取模块基址
Module.findExportByName(module|null,exp)获取导出函数
Thread.backtrace([context,backtracer])打印回溯栈
Thread.sleep(delay)
网络Socket.type(handle) tcp/udp/tcp6/udp6/unix:stream/unix:dgram
Socket.localAddress(handle) Socket.peerAddress(handle)      ip port path
文件new File(filePath,mode)
write(data)//Memory.readByteArray类型一致
flush()
close()

其他查看某函数定义:console.log(A.B)
Dalvik
         androidVersion
                   Dalvik.performNow(function(){console.log(Dalvik.androidVersion)}
         available
         cast 强制类型转换
         choose(classname,{onMatch:function(match){}, onComplete:function(){}}) 搜索java类对象
         enumerateLoadedClassesSync 枚举加载类
         isMainThread  当前是否主线程
         openClassFile            加载指定java类文件(apk dex jar)
         perform(funtion(){}) 初始化loader时执行(只有系统类)
         performNow(funtion(){}) 立即执行(只有系统类)
         scheduleOnMainThread(funtion(){})        主线程执行(可以找到app定义类)
         use   获取java
                   Dalvik.use(“com.example.hellojni.HelloJni”)

例:调用java内部函数
test.java:
package com.example.hellojni;
public class test {
    public String func1(){return "ok";};
    public static String func2(){return "ok";};
}

test.py:
import frida
Dalvik.performNow(function(){
var DexClassLoader=Dalvik.use(“dalvik.system.DexClassLoader”);
var ClassLoader=Dalvik.use(“java.lang.ClassLoader”);
var Class=Dalvik.use(“java.lang.Class”);
var loader= DexClassLoader.$new("/data/app/com.example.hellojni-2.apk", "/data/data/com.example.hellojni/",null, ClassLoader.getSystemClassLoader());
//var test=loader.loadClass(“com.example.hellojni.test”);
var test=Dalvik.use(“com.example.hellojni.test”);
console.log(test.$new().func1());
console.log(test.$new().func2());
}

Dalvik.performNow(function(){
Dalvik.openClassFile(“/data/app/com.example.hellojni-2.apk”).load(“com.example.hellojni.test”);
var test=Dalvik.use(“com.example.hellojni.test”);
console.log(test.$new().func1());
console.log(test.$new().func2());


Dalvik.perform(function(){Dalvik.scheduleOnMainThread(function(){console.log(Dalvik.use("com.example.hellojni.test"
).func2())})})
结果:ok

搜索所有String对象实例
Dalvik.performNow(function(){Dalvik.choose("java.lang.String",{onMatch:function(match){console.log("match")},onComplete:function(){console.log("complete")}})})


Java
         androidVersion
         available
         cast
         choose
         enumerateLoadedClassesSync
         isMainThread
         openClassFile
         perform
         performNow
         scheduleOnMainThread
         use




PYTHON API源码frida/core.py frida/tracer.py
附加进程frida.attach(“cat”)
枚举模块session.enumerate_modules()
枚举内存块session.enumerate_ranges(‘rw-‘)
读写内存read_bytes(address,num)  write_bytes(address,data)
设置钩子Interceptor.attach(ptr,{onEnter:function(args){}}


实验1.测试挂钩
编译运行hello.c
#include <stdio.h>
#include <unistd.h>

void f (int n)
{
  printf ("Number: %d\n", n);
}

int main (int argc,
      char * argv[])
{
  int i = 0;
  printf ("f() is at %p\n", f);
  while (1)
  {
    f (i++);
    sleep (1);
  }
}

f() is at 0x400544
Number: 0
Number: 1
Number: 2

编写hook.py
from __future__ import print_function
import frida
import sys

session = frida.attach("hello")
script = session.create_script("""
Interceptor.attach(ptr("%s"), {
    onEnter: function(args) {
        send(args[0].toInt32());
    }
});
""" % int(sys.argv[1], 16))
def on_message(message, data):
    print(message)
script.on('message', on_message)
script.load()
sys.stdin.read()

执行python hook.py 0x400544
输出
{u'type': u'send', u'payload': 531}
{u'type': u'send', u'payload': 532}


编写modify.py
import frida
import sys

session = frida.attach("hello")
script = session.create_script("""
Interceptor.attach(ptr("%s"), {
    onEnter: function(args) {
        args[0] = ptr("1337");
    }
});
""" % int(sys.argv[1], 16))
script.load()
sys.stdin.read()

执行python modify.py 0x400544
输出
Number: 1281
Number: 1282
Number: 1337
Number: 1337
Number: 1337
Number: 1337
Number: 1296
Number: 1297
Number: 1298

编写call.py
import frida
import sys

session = frida.attach("hello")
script = session.create_script("""
var f = new NativeFunction(ptr("%s"), 'void', ['int']);
f(1911);
f(1911);
f(1911);
""" % int(sys.argv[1], 16))
script.load()

执行python call.py 0x400544
输出
Number: 1879
Number: 1911
Number: 1911
Number: 1911
Number: 1880


2.测试注入
编写运行hi.c
#include <stdio.h>
#include <unistd.h>

int f (const char * s)
{
  printf ("String: %s\n", s);
  return 0;
}

int main (int argc,
      char * argv[])
{
  const char * s = "Testing!";
  printf ("f() is at %p\n", f);
  printf ("s is at %p\n", s);
  while (1)
  {
    f (s);
    sleep (1);
  }
}

编写stringhook.py
from __future__ import print_function
import frida
import sys

session = frida.attach("hi")
script = session.create_script("""
var st = Memory.allocUtf8String("TESTMEPLZ!");
var f = new NativeFunction(ptr("%s"), 'int', ['pointer']);
    // In NativeFunction param 2 is the return value type,
    // and param 3 is an array of input types
f(st);
""" % int(sys.argv[1], 16))
def on_message(message, data):
    print(message)
script.on('message', on_message)
script.load()

结果
...
String: Testing!
String: Testing!
String: TESTMEPLZ!
String: Testing!
String: Testing!
...

3.注入的内存对象
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

int main (int argc,char * argv[])
{
  int sock_fd, i, n;
  struct sockaddr_in serv_addr;
  unsigned char * b;
  const char * message;
  char recv_buf[1024];
  if (argc != 2)
  {
    fprintf (stderr, "Usage: %s <ip of server>\n", argv[0]);
    return 1;
  }
  printf ("connect() is at: %p\n", connect);
  if ((sock_fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
  {
    perror ("Unable to create socket");
    return 1;
  }
  bzero (&serv_addr, sizeof (serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons (5000);
  if (inet_pton (AF_INET, argv[1], &serv_addr.sin_addr) <= 0)
  {
    fprintf (stderr, "Unable to parse IP address\n");
    return 1;
  }
  printf ("\nHere's the serv_addr buffer:\n");
  b = (unsigned char *) &serv_addr;
  for (i = 0; i != sizeof (serv_addr); i++)
    printf ("%s%02x", (i != 0) ? " " : "", b);
  printf ("\n\nPress ENTER key to Continue\n");
  while (getchar () == EOF && ferror (stdin) && errno == EINTR);
  if (connect (sock_fd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0)
  {
    perror ("Unable to connect");
    return 1;
  }
  message = "Hello there!";
  if (send (sock_fd, message, strlen (message), 0) < 0)
  {
    perror ("Unable to send");
    return 1;
  }
  while (1)
  {
    n = recv (sock_fd, recv_buf, sizeof (recv_buf) - 1, 0);
    if (n == -1 && errno == EINTR)
      continue;
    else if (n <= 0)
      break;
    recv_buf[n] = 0;
    fputs (recv_buf, stdout);
  }
  if (n < 0)
  {
    perror ("Unable to read");
  }
  return 0;
}

$ ./client 127.0.0.1
connect() is at: 0x400780
Here's the serv_addr buffer:
02 00 13 88 7f 00 00 01 30 30 30 30 30 30 30 30
Press ENTER key to Continue

struct_mod.py
from __future__ import print_function
import frida
import sys

session = frida.attach("client")
script = session.create_script("""
// First, let's give ourselves a bit of memory to put our struct in:
send("Allocating memory and writing bytes...");
var st = Memory.alloc(16);
// Now we need to fill it - this is a bit blunt, but works...
Memory.writeByteArray(st,[0x02, 0x00, 0x13, 0x89, 0x7F, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30]);
// Module.findExportByName() can find functions without knowing the source
// module, but it's slower, especially over large binaries! YMMV...
Interceptor.attach(Module.findExportByName(null, "connect"), {
    onEnter: function(args) {
        send("Injecting malicious byte array:");
        args[1] = st;
    }
    //, onLeave: function(retval) {
    //   retval.replace(0); // Use this to manipulate the return value
    //}
});
""")

# Here's some message handling..
# [ It's a little bit more meaningful to read as output :-D
#   Errors get [!] and messages get prefixes. ]
def on_message(message, data):
    if message['type'] == 'error':
        print("[!] " + message['stack'])
    elif message['type'] == 'send':
        print(" " + message['payload'])
    else:
        print(message)
script.on('message', on_message)
script.load()
sys.stdin.read()

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|技术宅的结界 ( 滇ICP备16008837号|网站地图

GMT+8, 2018-11-20 14:16 , Processed in 0.087545 second(s), 14 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表