Frida 入门
Frida文档:Welcome | Frida • A world-class dynamic instrumentation framework
0x01快速开始
## 用pip安装
pip3 install frida-tools
## 快速体验,hook一下微信,可以在微信首发消息看一下输出
frida-trace -i "recv*" -i "read*" 微信
## 下面是输出
...
...
Started tracing 36 functions. Press Ctrl+C to stop.
/* TID 0x103 */
31910 ms read(d=0xb6, buf=0x6000017cf2c0, nbyte=0xa, offset=0xffffffffffffffff)
/* TID 0x2c02b */
31917 ms read(d=0xee, buf=0x700002e546d4, nbyte=0x4, offset=0x0)
/* TID 0x38593 */
31921 ms read(d=0x9, buf=0x700002ccb170, nbyte=0x400, offset=0x10000000100)
31921 ms read(d=0x9, buf=0x700002ccb170, nbyte=0x400, offset=0x7ff81ed153ba)
/* TID 0xdd67 */
31953 ms recv(socket=0xee, buffer=0x7fd604856000, length=0x2000, flags=0x0)
31953 ms | recvfrom(socket=0xee, buffer=0x7fd604856000, length=0x2000, flags=0x0, address=0x0, address_len=0x0)
...
...
0x02官方示例教程
Functions
Functions | Frida • A world-class dynamic instrumentation framework
简单示例
- 编写和编译测试文件
#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);
}
}
gcc -Wall hello.c -o hello
- hook方法内容
import frida
import sys
session = frida.attach("hello")
script = session.create_script("""
Interceptor.attach(ptr("%s"), {
onEnter(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()
- 修改参数值
import frida
import sys
session = frida.attach("hello")
script = session.create_script("""
Interceptor.attach(ptr("%s"), {
onEnter(args) {
// send(args[0].toInt32());
args[0] = ptr(10086)
}
});
""" % int(sys.argv[1], 16))
# def on_message(message,data):
# print(message)
# script.on("message",on_message)
script.load()
sys.stdin.read()
- 通过js调用程序内的函数
import frida
import sys
session = frida.attach("hello")
script = session.create_script("""
const f = new NativeFunction(ptr(%s),"void",["int"]);
f(10086);
f(10087);
f(10088);
""" % int(sys.argv[1],16))
script.load()
sys.stdin.read()
注入字符串并调用方法
- 创建测试文件
#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);
}
}
gcc -Wall hi.c -o hi
- 编写字符串hook和注入代码
import frida
import sys
session = frida.attach('hi')
script = session.create_script("""
const st = Memory.allocUtf8String("TESTMEPLZ!");
const f = new NativeFunction(ptr("%s"),'int',['pointer']);
f(st);
""" % int(sys.argv[1],16))
def on_message(message,data):
print(message)
script.on("message",on_message)
script.load()
sys.stdin.read()
- 创建编译socket链接示例
#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[i]);
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;
}
gcc -Wall client.c -o client
- 编写hook脚本,修改连接地址
import frida
import sys
session = frida.attach("client")
script = session.create_script("""
send('Allocating memory and writing bytes...')
const st = Memory.alloc(16);
// 具体的字节需要自己运行一边client之后,再进行修改,不要和官网一致
st.writeByteArray([0x00,0x02,0x13,0x89,0x7f,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
Interceptor.attach(Module.getExportByName(null,'connect'),{
onEnter(args){
send('Injecting malicious byte array:');
args[1] = st;
}
})
""")
def on_message(message,data):
if message['type'] == 'error':
print("[!] " + message['stack'])
elif message['type'] == 'send':
print("[i] " + message['payload'])
else:
print(message)
script.on('message',on_message)
script.load()
sys.stdin.read()
Messages
Messages | Frida • A world-class dynamic instrumentation framework
- 简单示例
import frida
import sys
session = frida.attach("hello")
script = session.create_script("send('123');send(a)")
def on_message(message,data):
print(message)
script.on("message",on_message)
script.load()
sys.stdin.read()
- recv 异步和等待调用
import frida
import sys
session = frida.attach('hello')
script = session.create_script("""
recv('poke',function onMessage(pokeMessage){
send('pokeBack');
});
""")
def on_message(message,data):
print(message)
script.on("message",on_message)
script.load()
script.post({"type":"poke"})
sys.stdin.read()
import frida
import sys
session = frida.attach('hello')
script = session.create_script("""
Interceptor.attach(ptr(%s),{
onEnter(args){
send(args[0].toString());
const op = recv('input',value => {
args[0] = ptr(value.payload);
})
op.wait();
}
})
""" % int(sys.argv[1],16))
def on_message(message,data):
print(message)
val = int(message.get("payload"),16)
script.post({"type":"input","payload":str(val * 2)})
script.on("message",on_message)
script.load()
sys.stdin.read()
0x03 知识点
- MacOS下的frida脚本示例
import frida
import sys
def on_message(message, data):
print("[{}] => {}".format(message, data))
def main(target_process):
session = frida.attach(target_process)
script = session.create_script("""
// macos下是针对Object-C进行处理,可以通过ObjC.classes.类['- 方法']进行获取
// 如果不知道具体的方法名称可以通过ObjC.classes.类.$methods列出当前类下所有的方法名
const appWillFinishLaunching = ObjC.classes.NSApplicationDelegate['- applicationWillFinishLaunching:'];
Interceptor.attach(appWillFinishLaunching.implementation, {
onEnter(args) {
// As this is an Objective-C method, the arguments are as follows:
// 0. 'self'
// 1. The selector (applicationWillFinishLaunching:)
// 2. The first argument to this method
const notification = new ObjC.Object(args[2]);
// Convert it to a JS string and log it
const notificationStr = notification.absoluteString().toString();
console.log('Will finish launching with notification: ' + notificationStr);
},
onLeave(retval) {
// 替换返回值
//retval.replace(0x01);
}
});
""")
script.on("message", on_message)
script.load()
print("[!] Ctrl+D or Ctrl+Z to detach from instrumented program.\n\n")
sys.stdin.read()
session.detach()
if __name__ == "__main__":
main("Safari")
- Hopper
- Hopper Disassembler是一款强大的反汇编程序,字符串处理效果比较好,免费版不能进行修改,导出文件之类的,每次打开最多维持30分钟,不过够用了
- class-dump
- class-dump 可以导出mac-app(Object-C)的头文件,可以查看类和方法的定义,辅助完成hook,使用需要将解压目录添加到Path中
0x04 Typora Crack
【Mac】记一次对付费Markdown软件Typora 1.0的逆向 - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
确定提示字符串:未激活
应用程序显示包内容,查找汉化字符串对应的内容:UNREGISTERED
用Hopper打开应用程序包→MACOS中的可执行文件
然后在左侧边栏的str中搜索
hook LicenseManager 的 hasLicense方法即可。
import frida
import time
session = None
def hook(target):
while True:
try:
session = frida.attach(target)
break
except Exception as ex:
time.sleep(0.5)
print("目标进程Hook成功!")
script = session.create_script("""
const hook_hasLicense = eval('ObjC.classes.LicenseManager["- hasLicense"]');
Interceptor.attach(hook_hasLicense.implementation,{
onEnter(args) {
},
onLeave(retval) {
retval.replace(0x01);
send("hasLicense replace.")
}
});""")
def on_message(message,data):
if message["type"] == "send":
print(message["payload"])
print(message)
script.on("message",on_message)
script.load()
while True:
time.sleep(1)
if session.is_detached:
break
while True:
hook('Typora')
0x05 参考链接
- MacOS逆向 - 『脱壳破解区』 (52pojie.cn)
- macOS | Frida • A world-class dynamic instrumentation framework
- Object-C 中的Selector 概念_kuangben2000的博客-CSDN博客_object selector
- ios逆向工具Hopper Disassembler的基本使用功能整理(持续更新)_小手琴师的博客-CSDN博客_hopper disassembler
- JavaScript API | Frida • A world-class dynamic instrumentation framework