烂橘子 - go0x.com

ZERO to ONE


  • 首页

  • 分类

  • 归档

  • 标签

  • 关于

  • 搜索

利用rip寄存器实现循环

发表于 2019-12-24 | | 阅读次数

想要循环调用一个函数,其实有很多种方法,除了以下常见实现方式(for ,while),还可以通过修改rip寄存器来实现循环,文章末尾会介绍具体的案例。

常见循环实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 1. for
for (;;) {
loop();
}

// 2. while
while (true) {
loop();
}

// 3. do while
do {
loop();
} while (true);

// 4. goto
label:
loop();
goto label;

rip实现循环

先来看一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// loop.c
#include <stdio.h>

void loop() {
long i = 111;
printf("in loop: %p\n", &i);
// 关键代码
*(&i+2) -= 5;
}

int main(int argc, char* argv[])
{
long i = 110;
loop();
printf("before loop: %p\n", &i);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 执行
$ clang loop.c
$ ./a.out
in loop: 0x7ffee225f848
in loop: 0x7ffee225f848
in loop: 0x7ffee225f848
in loop: 0x7ffee225f848
in loop: 0x7ffee225f848
in loop: 0x7ffee225f848
in loop: 0x7ffee225f848
in loop: 0x7ffee225f848
in loop: 0x7ffee225f848
in loop: 0x7ffee225f848
in loop: 0x7ffee225f848
in loop: 0x7ffee225f848
in loop: 0x7ffee225f848
in loop: 0x7ffee225f848
^C

为什么会出现这种效果?让我们先了解一下程序的执行过程。

运行时栈

main函数中调用loop,调用堆栈如下图所示:
stack _1_.jpg
在main函数的栈帧中可以看到,会将loop函数(例子中没有参数)参数入栈,随后保存PC(即main中printf的地址)。通过call指令实现将PC值push到栈中,并将PC设置为loop函数的地址。loop执行后调用ret指令,从栈中保存的返回地址恢复到PC中,从而main函数继续执行。

PC (program counter), 程序计数器,在x86-64中,用%rip寄存器表示。
The instruction pointer register points to the memory address which the processor will next attempt to execute.

反汇编

现在我们大致了解了程序执行时栈的变化过程,要想实现loop函数循环,只要将saved PC在栈中的值改为loop()这条指令的地址。接下来,让我们看看a.out的关键部分的反汇编代码。

1
2
# objdump反汇编a.out
$ objdump -D a.out > a.obj

反汇编.png

27行汇编 callq调用loop的时候,会将下一条指令(leaq)的地址写入栈中,同时将PC设置为loop的地址,实现调用loop的效果,callq和leaq的地址相差5,在loop函数中,我们可以通过变量i来寻找saved PC的在栈中的地址,从来达到loop返回后,继续执行loop的效果。

1
2
3
# &i+2是saved PC的地址
# 只要将栈中的数据改为callq的地址即可
*(&i+2) -= 5;

案例-go defer实现

在函数ret之前,调用deferreturn,deferreturn会循环调用,检查defer函数是否全部调用完成。

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
runtime.jmpdefer:
104f890: 48 8b 54 24 08 movq 8(%rsp), %rdx
104f895: 48 8b 5c 24 10 movq 16(%rsp), %rbx
104f89a: 48 8d 63 f8 leaq -8(%rbx), %rsp
104f89e: 48 8b 6c 24 f8 movq -8(%rsp), %rbp
104f8a3: 48 83 2c 24 05 subq $5, (%rsp) // 修改saved PC
104f8a8: 48 8b 1a movq (%rdx), %rbx
104f8ab: ff e3 jmpq *%rbx
104f8ad: cc int3
104f8ae: cc int3
104f8af: cc int3

runtime.deferreturn:
10264a0: 48 83 ec 48 subq $72, %rsp
10264a4: 48 89 6c 24 40 movq %rbp, 64(%rsp)
10264a9: 48 8d 6c 24 40 leaq 64(%rsp), %rbp
10264ae: 65 48 8b 04 25 a0 08 00 00 movq %gs:2208, %rax
10264b7: 48 8b 48 28 movq 40(%rax), %rcx
...
1026534: 48 8d 44 24 50 leaq 80(%rsp), %rax
1026539: 48 89 44 24 38 movq %rax, 56(%rsp)
102653e: 48 8b 44 24 20 movq 32(%rsp), %rax
1026543: 48 89 04 24 movq %rax, (%rsp)
1026547: 48 8b 44 24 38 movq 56(%rsp), %rax
102654c: 48 89 44 24 08 movq %rax, 8(%rsp)
1026551: e8 3a 93 02 00 callq 168762 <runtime.jmpdefer>
1026556: 48 8b 6c 24 40 movq 64(%rsp), %rbp
102655b: 48 83 c4 48 addq $72, %rsp
102655f: c3 retq
...

go get代理

发表于 2018-11-27 | | 阅读次数

平时在用go get命令下载依赖时,经常会遇到类似

package google.golang.org/grpc/encoding/gzip: unrecognized import path “google.golang.org/grpc/encoding/gzip” (https fetch: Get https://google.golang.org/grpc/encoding/gzip?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)

的问题,其实是这些资源被墙,所幸的是有很多使用代理的方式解决了这个问题。

以google.golang.org/grpc/encoding/gzip为例:

设置git代理

1
2
$ git config [--global] http.proxy http://proxy.example.com:port
$ go get "google.golang.org/grpc/encoding/gzip"

shell环境变量

1
2
3
4
# socks
http_proxy=socks5://127.0.0.1:1080 go get "google.golang.org/grpc/encoding/zip"
# http
http_proxy=http://127.0.0.1:1080 go get "google.golang.org/grpc/encoding/gzip"

VPN

那就直接go get “google.golang.org/grpc/encoding/gzip”吧

佛祖包邮,永无BUG

发表于 2018-01-10 | 分类于 Linux/Unix | | 阅读次数

在知乎闲逛时看到某回答中有个”佛祖保佑,永无BUG“的注释,便想着何不在登陆linux时可以显示出来,并且加上金光闪闪的颜色。

修改/etc/motd文件,加入如下内容:

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
^[
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O\ = /O
____/`---'\____
.' \\| |// `.
/ \\||| : |||// \
/ _||||| -:- |||||- \
| | \\\ - /// | |
| \_| ''\---/'' | |
\ .-\__ `-` ___/-. /
___`. .' /--.--\ `. . __
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;`\ _ /`;.`/ - ` : | |
\ \ `-. \_ __\ /__ _/ .-` / /
======`-.____`-.___\_____/___.-`____.-'======
`=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
佛祖保佑 永无BUG
佛曰:
写字楼里写字间,写字间里程序员;
程序人员写程序,又拿程序换酒钱。
酒醒只在网上坐,酒醉还来网下眠;
酒醉酒醒日复日,网上网下年复年。
但愿老死电脑间,不愿鞠躬老板前;
奔驰宝马贵者趣,公交自行程序员。
别人笑我忒疯癫,我笑自己命太贱;
不见满街漂亮妹,哪个归得程序员?
^[

效果图:
fozubaoyou

Hello World

发表于 2017-11-15 | 分类于 我的杂谈 | | 阅读次数

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

Rotten Orange

Rotten Orange

专注于技术,享受编程带来的快乐

4 日志
2 分类
5 标签
RSS
GitHub
友情链接
  • coolshell
  • 刘未鹏
  • nicksxs
© 2021 Rotten Orange
由 Hexo 强力驱动
主题 - NexT.Mist