关于技术的一些奇奇怪怪的知识点
include
include本质就是把include的文件内容拷贝到当前插入include的位置.
所以就可以有一些奇怪的用法:
a.h
1 2 3 4 5 6
| #ifndef _AAA #define _AAA else: printf("1\n");
#endif
|
a.c
1 2 3 4 5 6 7 8 9 10
| #include <stdio.h>
void main(void){ int a = 0; if(a==1){ ... } #include "a.h" return; }
|
c的结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| struct new1 { int a1; };
struct a2 { struct new1;// 这个位置我们没有给结构体变量名 int a22; };
int main() { struct a2 b1; b1.a1;// 但是, 我们可以直接引用结构体的成员! return 0; }
|
int 到 size_t
对于x64, 无论是gcc还是msvc, 编译int a=-1;size_t new = (size_t)a;
的结果, new都是0xffffffffffffffff.
关于clone的编写bug
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include<linux/sched.h> int this_pthread_create(int *tid, void(*thread_func)(void *), void *arg, void *stack_end){ int pid; *(long*)(stack_end-0x100) = (long)thread_func; *(long*)(stack_end-0x108) = (long)arg;
pid = (int)this_syscall((CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD), (long)stack_end, 0, 0, 0, 0, 56 ); if (pid == -1) { perror("clone"); return 0; } if(pid) return 1; else{ printf("child thread\n"); __asm__ __volatile__( "movq -0x108(%%rsp), %0\n\t" "movq -0x100(%%rsp), %1\n\t" :"=r"(arg),"=r"(thread_func) ); thread_func(arg); } }
int x = 0; void fnc(void *new){ x = 1; return; } void main(){ int pid; void *a = calloc(0x1000, 2); this_pthread_create(&pid, fnc, 0, a+0x1000); while(1); }
|
1 2 3 4 5 6 7 8 9
| this_syscall: push rbp mov rbp, rsp mov r10, rcx mov rax, qword ptr[rbp+0x10] syscall pop rbp ret
|
这段代码一运行, 就会出现崩溃, 且rip
等于0, 用上gdb调试, 也摸不着头脑, 最后终于知道问题在哪, 问题就在this_syscall
的实现上.
clone这个syscall调用, 成功时, 会返回两种值, 一种是线程pid, 此时就运行在调用方的线程里. 一种是0, 此时在新建的线程里. 当从新线程返回时, rsp
会被更新为新线程的rsp
(即函数里的stack_end参数).
在this_syscall
的第七行调用了pop rbp
和ret
, 如果当前线程是新线程, rsp已经被更新了, pop和ret操作都是在新的栈里操作的, 导致ret获取了0作为rip, 从而造成了崩溃.