ankikong

  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

frida

发表于 2020-09-20 更新于 2023-02-26

windows安装

1
pip3 install frida frida-tools

不过frida安装时总会卡住不动,可以到这里 选择和你的python版本一致的,注意指令集也要和python的一样.然后直接easy_install xxx.egg

Android安装

到这里下载server,主要要和手机的指令集一致的,解压后

1
2
3
4
5
adb push server 手机路径
adb shell chmod +x server
forward tcp:27042 tcp:27042
forward tcp:27043 tcp:27043
adb shell ./server

应用

python脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import frida
import sys

js = open("code/frida/a.js", encoding="utf8").read()

def on_message(message, data):
print(data)
for k in ["stack", "description"]:
msg = message.get(k)
if msg is not None:
print(msg)

session = frida.get_remote_device().attach('com.picacomic.fregata')
script = session.create_script(js)
script.on('message', on_message)
script.load()

sys.stdin.read()
  1. 获取非native方法的输入值和返回值,这里的方法为 String c([]String)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    if (Java.available) {
    Java.perform(function () {
    var MyApplication = Java.use('com.picacomic.fregata.MyApplication');
    console.log(MyApplication);
    MyApplication.c.implementation = function (v) {
    console.log("param:");
    console.log(v);
    var ret = this.c(v);
    console.log("return value:");
    console.log(ret);
    return ret;
    };
    });
    } else {
    console.log("load java failed");
    }
  2. 获取native方法的输入值和返回值,这里的方法是 String getStringSigFromNative();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (Java.available) {
Java.perform(function () {
Interceptor.attach(Module.findExportByName("libJniTest.so", "Java_com_picacomic_fregata_MyApplication_getStringSigFromNative"), {
// onEnter 是方法开始前调用的,args[0]是env,args[1]是jclass,args[2...]就是方法的输入值了
onEnter: function(args) {
send(args[0]);
send(args[1]);
},
onLeave: function(ret) {
send("ret:" + Java.vm.getEnv().getStringUtfChars(ret, null).readCString());
}
});
});
} else {
console.log("load java failed");
}

这里的Java_com_picacomic_fregata_MyApplication_getStringSigFromNative是查so文件得到的方法名,非java源码里面的名称.可以把 .so抠出来,丢到linux里面,然后 nm -D libJniTest.so,输出大概长这样
native1

其他注意事项

打印String VAL 需要用 Java.vm.getEnv().getStringUtfChars(VAL, null).readCString(),不然打出来是个地址

mysql

发表于 2020-04-07 更新于 2023-02-26

mysql 学习日记

阅读全文 »

redis

发表于 2020-03-20 更新于 2023-02-26

简单动态字符串(SDS)

1
2
3
4
5
6
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; // 字符串长度
uint8_t alloc; // 不包括头部长度和结束符长度
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
}

不包括头的意思是:redis在新建完sds,并不会直接返回这个结构体,而是返回buf的地址,这样就可以直接使用string.h里面的方法进行处理,所以下面:

1
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))

这个就是输入T(是sdshdrT),s就是字符串的首地址,这样处理就可以通过字符串的首地址获得整个结构体

ziplist

结构为:

1
2
3
4
5

+-------+----------------------------
|4 byte |4 byte |2 byte |
+-------+
|zlbytes|zltail |entries|

Rust learning note

发表于 2020-02-25 更新于 2023-02-26

Option<T>

像C#的nullable,就是使没有支持null(None)的数据类型增加null(None)这个取值,要用Some(x)进行解包

变量所有权

一个变量只能拥有一个可写的引用,但是可以拥有多个只读引用,读与写引用互斥.

栈上变量直接赋值,会直接复制,不会转移所有权.但是堆上变量赋值如果没有使用clone(),则会转移所有权,转移后原来变量名失去所有权,使用的话会在编译阶段报错

函数如果没有传引用的话,会把所有权转移到被调用函数中

生命周期

1
2
3
4
5
6
7
8
9
10
// 表示a,b和返回值有相同的生命周期
pub fn t3<'a, T> (a:&'a T, b:&'a T) -> &'a T
where T: cmp::PartialOrd
{
if a > b {
a
} else {
b
}
}

静态生命周期

1
let s: &'static str = "455";

trait

很像接口

1
2
3
4
5
6
7
8
9
10
11
12
13
pub trait Ahh {
fn say(&self) -> String {
String::from("ahhhhh")
}
}
pub struct Bhh {

}
impl Ahh for Bhh {
fn say(&self) -> String {
String::from("ffffff")
}
}

IP是否在中国

发表于 2020-02-14 更新于 2023-02-26

由于某些不可名状的原因,国内dns有点脏

阅读全文 »

TCP

发表于 2020-02-11 更新于 2023-02-26

一种面向连接的、可靠的、基于字节流的传输层通信协议

阅读全文 »

zero copy

发表于 2020-02-10 更新于 2023-02-26

概念

由于Linux文件读写是对缓存进行操作的,虽说可以减少对磁盘的读写,但是数据拷贝时会消耗大量CPU资源.零拷贝zero-copy这种技术可以有效地改善数据传输的性能,在内核驱动程序(比如网络堆栈或者磁盘存储驱动程序)处理 I/O 数据的时候,零拷贝技术可以在某种程度上减少甚至完全避免不必要 CPU 数据拷贝操作。
但是只能用于单纯的文件传输,无法实现文件修改后再传输
正常文件拷贝传输,是由内核拷贝到缓冲区,然后内核再把数据写到用户所给内存,再写到socket缓冲区,最后再交付

zero1

分类

  • 直接 I/O:对于这种数据传输方式来说,应用程序可以直接访问硬件存储,操作系统内核只是辅助数据传输:这类零拷贝技术针对的是操作系统内核并不需要对数据进行直接处理的情况,数据可以在应用程序地址空间的缓冲区和磁盘之间直接进行传输,完全不需要 Linux 操作系统内核提供的页缓存的支持。
  • 在数据传输的过程中,避免数据在操作系统内核地址空间的缓冲区和用户应用程序地址空间的缓冲区之间进行拷贝。有的时候,应用程序在数据进行传输的过程中不需要对数据进行访问,那么,将数据从 Linux 的页缓存拷贝到用户进程的缓冲区中就可以完全避免,传输的数据在页缓存中就可以得到处理。在某些特殊的情况下,这种零拷贝技术可以获得较好的性能。Linux 中提供类似的系统调用主要有 mmap(),sendfile() 以及 splice()。
  • 对数据在 Linux 的页缓存和用户进程的缓冲区之间的传输过程进行优化。该零拷贝技术侧重于灵活地处理数据在用户进程的缓冲区和操作系统的页缓存之间的拷贝操作。这种方法延续了传统的通信方式,但是更加灵活。在 Linux 中,该方法主要利用了写时复制技术

直接I/O

1
2
// flag使用O_DIRECT
int open(const char *pathname, int oflag, … /*, mode_t mode */ )

针对数据传输不需要经过应用程序地址空间的零拷贝技术

mmap

1
2
tmp_buf = mmap(file, len);
write(socket, tmp_buf, len);

应用程序调用了 mmap() 之后,数据会先通过 DMA 拷贝到操作系统内核的缓冲区中去。接着,应用程序跟操作系统共享这个缓冲区,这样,操作系统内核和应用程序存储空间就不需要再进行任何的数据拷贝操作。应用程序调用了 write() 之后,操作系统内核将数据从原来的内核缓冲区中拷贝到与 socket 相关的内核缓冲区中。接下来,数据从内核 socket 缓冲区拷贝到协议引擎中去,这是第三次数据拷贝操作

zero2

当对文件进行了内存映射,然后调用 write() 系统调用,如果此时其他的进程截断了这个文件,那么 write() 系统调用将会被总线错误信号 SIGBUS 中断,因为此时正在执行的是一个错误的存储访问。这个信号将会导致进程被杀死,解决这个问题可以通过以下这两种方法:

  1. 为 SIGBUS 安装一个新的信号处理器
  2. 第二种方法是通过文件租借锁来解决这个问题的,这种方法相对来说更好一些。我们可以通过内核对文件加读或者写的租借锁,当另外一个进程尝试对用户正在进行传输的文件进行截断的时候,内核会发送给用户一个实时信号:RT_SIGNAL_LEASE 信号,这个信号会告诉用户内核破坏了用户加在那个文件上的写或者读租借锁,那么 write() 系统调用则会被中断,并且进程会被 SIGBUS 信号杀死,返回值则是中断前写的字节数,errno 也会被设置为 success。文件租借锁需要在对文件进行内存映射之前设置。

sendfile

1
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count)

sendfile() 系统调用利用 DMA 引擎将文件中的数据拷贝到操作系统内核缓冲区中,然后数据被拷贝到与 socket 相关的内核缓冲区中去。接下来,DMA 引擎将数据从内核 socket 缓冲区中拷贝到协议引擎中去。如果在用户调用 sendfile () 系统调用进行数据传输的过程中有其他进程截断了该文件,那么 sendfile () 系统调用会简单地返回给用户应用程序中断前所传输的字节数,errno 会被设置为 success。如果在调用 sendfile() 之前操作系统对文件加上了租借锁,那么 sendfile() 的操作和返回状态将会和 mmap()/write () 一样

zero3
sendfile() 系统调用不需要将数据拷贝或者映射到应用程序地址空间中去,所以 sendfile() 只是适用于应用程序地址空间不需要对所访问数据进行处理的情况。相对于 mmap() 方法来说,因为 sendfile 传输的数据没有越过用户应用程序 / 操作系统内核的边界线,所以 sendfile () 也极大地减少了存储管理的开销。

  1. sendfile() 局限于基于文件服务的网络应用程序
  2. 由于网络传输具有异步性,很难在 sendfile () 系统调用的接收端进行配对的实现方式,所以数据传输的接收端一般没有用到这种技术
  3. 基于性能的考虑来说,sendfile () 仍然需要有一次从文件到 socket 缓冲区的 CPU 拷贝操作,这就导致页缓存有可能会被传输的数据所污染

splice

1
2
3
#include <fcntl.h>
ssize_t splice(int fd_in, loff_t *off_in, int fd_out,
loff_t *off_out, size_t len, unsigned int flags)

在 Linux 2.6.23 中,sendfile由splice实现

调用 splice() 系统调用会导致操作系统内核从数据源 fdin 移动最多 len 个字节的数据到 fdout 中去,这个数据的移动过程只是经过操作系统内核空间,需要最少的拷贝次数。使用 splice() 系统调用需要这两个文件描述符中的一个必须是用来表示一个管道设备的。不难看出,这种设计具有局限性。

  • fd_in参数是待输入描述符。如果它是一个管道文件描述符,则off_in必须设置为NULL;如果off_in不是一个管道文件描述符(比如socket),那么off_in表示从输入数据流的何处开始读取数据,此时若为NULL,则从输入数据流的当前偏移位置读入

  • fd_out/off_out参数的含义与fd_in/off_in相同,不过用于输出数据流

  • len参数指定移动数据的长度

  • 参数 flags 用于表示拷贝操作的执行方法,当前的 flags 有如下这些取值:

    1. SPLICE_F_NONBLOCK:splice 操作不会被阻塞。然而,如果文件描述符没有被设置为不可被阻塞方式的 I/O ,那么调用 splice 有可能仍然被阻塞。
    2. SPLICE_F_MORE:告知操作系统内核下一个 splice 系统调用将会有更多的数据传来。
    3. SPLICE_F_MOVE:如果输出是文件,这个值则会使得操作系统内核尝试从输入管道缓冲区直接将数据读入到输出地址空间,这个数据传输过程没有任何数据拷贝操作发生
1
2
3
4
5
6
//创建管道
ret = pipe(pipefd);
//将connfd上流入的客户端数据定向到管道中
ret = splice(connfd, NULL, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
//将管道的输出定向到connfd连接文件符上
ret = splice(pipefd[0], NULL, connfd, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);

写时复制 Copy On Write(COW)

如果有多个应用程序需要同时访问同一块数据,那么可以为这些应用程序分配指向这块数据的指针,在每一个应用程序看来,它们都拥有这块数据的一份数据拷贝,当其中一个应用程序需要对自己的这份数据拷贝进行修改的时候,就需要将数据真正地拷贝到该应用程序的地址空间中去,也就是说,该应用程序拥有了一份真正的私有数据拷贝,这样做是为了避免该应用程序对这块数据做的更改被其他应用程序看到。这个过程对于应用程序来说是透明的,如果应用程序永远不会对所访问的这块数据进行任何更改,那么就永远不需要将数据拷贝到应用程序自己的地址空间中去。这也是写时复制的最主要的优点。

写时复制的实现需要 MMU 的支持,MMU 需要知晓进程地址空间中哪些特殊的页面是只读的,当需要往这些页面中写数据的时候,MMU 就会发出一个异常给操作系统内核,操作系统内核就会分配新的物理存储空间,即将被写入数据的页面需要与新的物理存储位置相对应。

参考文章

  • Linux 中的零拷贝技术,第 1 部分
  • Linux 中的零拷贝技术,第 2 部分
  • Linux Programmer’s Manual

笔记

发表于 2020-02-10 更新于 2023-02-26

Reactor

Reactor 要求主线程或内核负责监听文件描述符是否有事件发生,有的话立刻告知线程.数据读写全部由控制线程负责.同步io

epoll就是这种方式

组件

  1. event demultiplexer事件多路分发器,如select,poll,epoll
  2. handle事件源:用于识别每一个事件,Linux上是文件描述符
  3. Reactor反应器,用于管理事件的调度和注册删除
  4. event handler事件处理器:管理已经注册的事件和已经激活等待调度的事件,并分成了不同类型的事件(如读/写、信号),当事件发生时,会提供对应的处理程序。由Reactor调用。

Proactor

Proactor 所有io操作由主线程或者内核负责,控制线程只负责业务逻辑,异步io

aio

timewait

  • TCP四次挥手中,主动方在发送第二个ACK后的2MSL(max segment lifetime)时间之内,RFC推荐是2MSL=120,Linux内核2MSL=60s

  • 目的:防止ACK包丢失,等待是否有被动方重发的seq

  • 解决方法:

    • 编程时使用SO_REUSEADDR
    • Linux里
    1
    2
    3
    4
    5
    6
    7
    8
    # 启动TCP ts,tcp_tw_reuse和.tcp_tw_recycle依赖此实现
    net.ipv4.tcp_timestamps=1
    # 复用TIME_WAIT的socket
    net.ipv4.tcp_tw_reuse=1
    # 销毁TIME_WAIT的socket,由于nat可能会导致无法握手
    net.ipv4.tcp_tw_recycle=1
    # 重设超时
    net.ipv4.tcp_fin_timeout=30

Linux IO模型

  • 阻塞IO
  • 非阻塞IO
  • IO复用
  • 信号驱动IO
  • 异步IO

僵尸进程解决

  1. 两次fork.父进程创建子进程,子进程负责fork孙子进程,孙子进程负责执行逻辑业务,子进程fork完孙子线程后直接exit.孙子进程变成孤儿线程,交由init管理

  2. 子进程在结束时会向父线程发送SIGCHLD.父进程只要在信号处理函数中wait,进行子进程的PCB进行回收

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    static void sig_child(int signo) {
    pid_t pid;
    int stat;
    //处理僵尸进程
    while ((pid = waitpid(-1, &stat, WNOHANG)) >0)
    printf("child %d terminated.\n", pid);
    }
    int main() {
    signal(SIGCHLD, sig_child);
    ......
    }
  3. 忽略请求

    1
    signal(SIGCLD, SIG_IGN);

一致性hash

哈希值映射到圆环,节点分布在环上,顺时针的第一个节点就是数据存取节点

RVO (return value optimization) 和NRVO (named return value optimization)

Traits

被其他object或algorithm使用的,用来携带信息的object

实现:模板的特化与偏特化

cloudflare

发表于 2020-02-06 更新于 2023-02-26

cloudflare 的服务器支持SNI,所以其实服务器委托在cf上的话,他的ip可以指向cf的任意ip地址,也可以正常访问

阅读全文 »

dns

发表于 2020-02-05 更新于 2023-02-26

回家后发现家里的新拉的光纤是移动的,但是移动居然对53端口的udp实行完全拦截,也就是dns拦截。想起之前在宿舍使用的cloudflare的json格式的DoH还有DoT,所以决定搞一搞。

阅读全文 »
1234

ankikong

ankikong's blog
31 日志
25 标签
GitHub E-Mail
© 2023 ankikong
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Gemini v7.3.0