Linux 系统调用 Ptrace 详解

 

From:https://blog.csdn.net/u012417380/article/details/60470075

Ptrace 详解:https://www.cnblogs.com/tangr206/articles/3094358.html

ptrace运行原理及使用详解:https://blog.csdn.net/edonlii/article/details/8717029

 

ptrace 函数代码分析

ptrace 函数深入分析:https://www.cnblogs.com/heixiang/p/10988992.html

Linux 源码分析之 Ptrace:https://blog.csdn.net/u012417380/article/details/60468697

 

 

 

一、系统调用

 

操作系统提供一系列 系统调用 函数 来为应用程序提供服务。关于系统调用的详细相关知识,可以查看 《程序员的自我修养》 第十二章。对于 x86 操作系统来说,用中断命令 "int 0x80" 来进行系统调用,系统调用前,需要将系统调用号放入到 %EAX 寄存器中,将系统的参数依次放入到寄存器 %ebx、%ecx、%edx 以及 %esi 和 %edi 中。

write 系统调用 为例:

write(2,"Hello",5);

在 32位系统中会转换成:

movl $1,%eax
movl $2,%ebx
movl $hello,%ecx
movl $5,%edx
int $0x80

其中 1 为 write 的系统调用号,所有的系统调用号定义在 unistd.h 文件中,$hello 表示字符串 "Hello" 的地址;

32位 Linux 系统通过 0x80 中断来进行系统调用。

64位系统用户应用层用整数寄存器 %rdi、%rsi、%rdx、%rcx、%r8 以及 %r9 来传参。

而内核接口用 %rdi 、%rsi、%rdx、%r10、&r8 以及 %r10 来传参,并且用 syscall 指令而不是 80 中断进行系统调用。

x86 和 x64 都用寄存器 rax 来保存调用号和返回值。

 

 

二、ptrace 函数简介

 

【 ptrace系统调用 】

功能描述:

提供父进程观察和控制另一个进程执行的机制,同时提供查询和修改另一进程的核心影像与寄存器的能力。主要用于执行断点调试和系统调用跟踪。父进程可通过调用fork,接着指定所产生的子进程的PTRACE_TRACEME行为,最后使用exec等操作来初始化一个进程跟踪。可替代的做法是,父进程通过PTRACE_ATTACH请求跟踪一个现存进程的执行。

当子进程被跟踪时,每次接收到信号都会停止执行,即使不对信号进行处理(SIGKILL信号除外)。父进程下次执行wait调用时,会接收到核心的通告,并可能检查和修改已停止的子进程。父进程使子进程继续执行,并有可能忽略接收到的信号。

 

ptrace() 系统调用函数提供了一个进程(the “tracer”)监察和控制另一个进程(the “tracee”)的方法。并且可以检查和改变“tracee”进程的内存和寄存器里的数据。它可以用来实现断点调试和系统调用跟踪。

tracee首先需要被附着到tracer。在多线程进程中,每个线程都可以被附着到一个tracer。ptrace命令总是以ptrace(PTARCE_foo,pid,..)的形式发送到tracee进程。pid是tracee线程ID。

当一个进程可以开始跟踪进程通过调用fork函数创建子进程并让子进程执行PTRACE_TRACEME,然后子进程再调用execve()(如果当前进程被ptrace,execve()成功执行后 SIGTRAP信号量会被发送到该进程)。一个进程也可以使用”PTRACE_ATTACH”或者”PTRACE_SEIZE”来跟踪另一个进程。

当进程被跟踪后,每当信号量传来,甚至信号量会被忽略时,tracee会暂停。tracer会在下次调用waitpid(wstatus)(或者其它wait系统调用)处被通知。该调用会返回一个包含tracee暂停原因信息的状态码。当tracee暂停后,tracer可以使用一系列ptrace请求来查看和修改tracee中的信息。tracer接着可以让tracee继续执行。tracee传递给tracer中的信号量通常被忽略。
当PTRACE_O_TRACEEXEC项未起作用时,所有成功执行execve()的tracee进程会被发送一个 SIGTRAP信号量后暂停,在新程序执行之前,父进程将会取得该进程的控制权。

当tracer结束跟踪后,可以通过调用 PTRACE_DETACH 继续让 tracee 执行。

prace更多相关信息可以查看:http://man7.org/linux/man-pages/man2/ptrace.2.html 官方文档。

简单的说:就是注入到另一个进程里面,外挂用这个函数比较多。

 

用法:

#include <sys/ptrace.h>long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);

 

参数:

  • request:请求执行的行为,可能选择有


    PTRACE_TRACEME //指示父进程跟踪某个子进程的执行。任何传给子进程的信号将导致其停止执行,同时父进程调用wait()时会得到通告。之后,子进程调用exec()时,核心会给它传送SIGTRAP信号,在新程序开始执行前,给予父进程控制的机会。pid, addr, 和 data参数被忽略。

    以上是唯一由子进程使用的请求,剩下部分将由父进程使用的请求。

    PTRACE_PEEKTEXT, PTRACE_PEEKDATA //从子进程内存空间addr指向的位置读取一个字,并作为调用的结果返回。Linux内部对文本段和数据段不加区分,所以目前这两个请求相等。data参数被忽略。
    PTRACE_PEEKUSR //从子进程的用户区addr指向的位置读取一个字,并作为调用的结果返回。
    PTRACE_POKETEXT, PTRACE_POKEDATA //将data指向的字拷贝到子进程内存空间由addr指向的位置。
    PTRACE_POKEUSR //将data指向的字拷贝到子进程用户区由addr指向的位置。
    PTRACE_GETREGS, PTRACE_GETFPREGS //将子进程通用和浮点寄存器的值拷贝到父进程内由data指向的位置。addr参数被忽略。
    PTRACE_GETSIGINFO //获取导致子进程停止执行的信号信息,并将其存放在父进程内由data指向的位置。addr参数被忽略。
    PTRACE_SETREGS, PTRACE_SETFPREGS //从父进程内将data指向的数据拷贝到子进程的通用和浮点寄存器。addr参数被忽略。
    PTRACE_SETSIGINFO //将父进程内由data指向的数据作为siginfo_t结构体拷贝到子进程。addr参数被忽略。
    PTRACE_SETOPTIONS //将父进程内由data指向的值设定为ptrace选项,data作为位掩码来解释,由下面的标志指定
    PTRACE_O_TRACESYSGOOD //当转发syscall陷阱(traps)时,在信号编码中设置位7,即第一个字节的最高位。例如:SIGTRAP | 0x80。这有利于追踪者识别一般的陷阱和那些由syscall引起的陷阱。
    PTRACE_O_TRACEFORK //通过 (SIGTRAP | PTRACE_EVENT_FORK << 8) 使子进程下次调用fork()时停止其执行,并自动跟踪开始执行时就已设置SIGSTOP信号的新进程。新进程的PID可以通过PTRACE_GETEVENTMSG获取。
    PTRACE_O_TRACEVFORK //通过 (SIGTRAP | PTRACE_EVENT_VFORK << 8) 使子进程下次调用vfork()时停止其执行,并自动跟踪开始执行时就已设置SIGSTOP信号的新进程。新进程的PID可以通过PTRACE_GETEVENTMSG获取。
    PTRACE_O_TRACECLONE //通过 (SIGTRAP | PTRACE_EVENT_CLONE << 8) 使子进程下次调用clone()时停止其执行,并自动跟踪开始执行时就已设置SIGSTOP信号的新进程。新进程的PID可以通过PTRACE_GETEVENTMSG获取。
    PTRACE_O_TRACEEXEC //通过 (IGTRAP | PTRACE_EVENT_EXEC << 8) 使子进程下次调用exec()时停止其执行。
    PTRACE_O_TRACEVFORKDONE //通过 (SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8) 使子进程下次调用exec()并完成时停止其执行。
    PTRACE_O_TRACEEXIT //通过 (SIGTRAP | PTRACE_EVENT_EXIT << 8) 使子进程退出时停止其执行。子进程的退出状态可通过PTRACE_GETEVENTMSG。
    PTRACE_GETEVENTMSG //获取刚发生的ptrace事件消息,并存放在父进程内由data指向的位置。addr参数被忽略。
    PTRACE_CONT //重启动已停止的进程。如果data指向的数据并非0,同时也不是SIGSTOP信号,将会作为传递给子进程的信号来解释。那样,父进程可以控制是否将一个信号发送给子进程。 addr参数被忽略。
    PTRACE_SYSCALL, PTRACE_SINGLESTEP //如同PTRACE_CONT一样重启子进程的执行,但指定子进程在下个入口或从系统调用退出时,或者执行单个指令后停止执行,这可用于实现单步调试。addr参数被忽略。
    PTRACE_SYSEMU, PTRACE_SYSEMU_SINGLESTEP //用于用户模式的程序仿真子进程的所有系统调用。
    PTRACE_KILL //给子进程发送SIGKILL信号,从而终止其执行。data,addr参数被忽略。
    PTRACE_ATTACH //衔接到pid指定的进程,从而使其成为当前进程的追踪目标。
    PTRACE_DETACH //PTRACE_ATTACH的反向操作。

  • pid:目标进程标识。

  • addr:执行 peek 和 poke 操作的目标地址。

  • data:对于 poke 操作,存放数据的地方。对于 peek 操作,获取数据的地方。

 

返回说明:

成功执行时,PTRACE_PEEK*请求返回所请求的数据,其它返回0。失败返回-1,errno被设为以下的某个值。由于一个成功的PTRACE_PEEK*请求可能返回-1,决定错误是否发生前,调用者应检查errno。
EBUSY:分配和释放调试寄存器时出错
EFAULT:读写不可访问的内存空间
EINVAL:尝试设置无效选项
EIO:请求无效,或者尝试读写父子进程不可访问的空间
EPERM:没有权限追踪指定的进程
ESRCH:指定的子进程不存在,或者当前正由调用者追踪

 

 

三、示例

 

1. ptrace 追踪子进程执行 exec()

#include <stdio.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/reg.h>   /* For constants ORIG_RAX etc */
int main(){pid_t child;long orig_rax;child=fork();if(child==0){ptrace(PTRACE_TRACEME,0,NULL,NULL);execl("/bin/ls","ls",NULL);}else{wait(NULL);orig_rax = ptrace(PTRACE_PEEKUSER,child,8*ORIG_RAX,NULL);printf("The child made a system call %ld\n",orig_rax);ptrace(PTRACE_CONT,child,NULL,NULL);}}

编译后输出:

The child made a system call 59
user1@user-virtual-machine:~/hookTest$ a.out    attach.c~  ex1.c   ex1.o  ex2.c~  ex3.c   ex3.o  ex4.c~    victim.c~
attach.c  attach.o   ex1.c~  ex2.c  ex2.o   ex3.c~  ex4.c  victim.c  victim.o

execl()函数对应的系统调用为__NR_execve,系统调用值为59。父进程通过调用fork()来创建子进程。在子进程中,先运行patrce().请求参数设为PTRACE_TRACE,来告诉内核当前进程被父进程trace,每当有信号量传递到当前进程,该进程会暂停,提醒父进程在wait()调用处继续执行。然后再调用execl()。当execl()函数成功执行后,新程序运行之前,SIGTRAP信号量会被发送到该进程,让子进程停止,这时父进程会在wait相关调用处被通知,获取子进程的控制权,可以查看子进程内存和寄存器相关信息。

当进程进行系统调用时,int会在内核栈中依次压入用户态的寄存器SS、ESP、EFLAGS、CS、EIP.中断处理程序的SAVE_ALL宏会将 依次将EAX、EBP、EDI、ESI、EDX、ECX、EBX寄存器值压入内核栈。调用ptrace(PTRACE_PEEKUSER,child,8*ORIG_RAX,NULL) 获取USER area信息时<sys/reg.h>文件定义了与内核栈寄存器数组顺序相同的下标:


#ifndef _SYS_REG_H
#define _SYS_REG_H  1#ifdef __x86_64__
/* Index into an array of 8 byte longs returned from ptrace forlocation of the users' stored general purpose registers.  */# define R15    0
# define R14    1
# define R13    2
# define R12    3
# define RBP    4
# define RBX    5
# define R11    6
# define R10    7
# define R9 8
# define R8 9
# define RAX    10
# define RCX    11
# define RDX    12
# define RSI    13
# define RDI    14
# define ORIG_RAX 15
# define RIP    16
# define CS 17
# define EFLAGS 18
# define RSP    19
# define SS 20
# define FS_BASE 21
# define GS_BASE 22
# define DS 23
# define ES 24
# define FS 25
# define GS 26
#else/* Index into an array of 4 byte integers returned from ptrace for* location of the users' stored general purpose registers. */# define EBX 0
# define ECX 1
# define EDX 2
# define ESI 3
# define EDI 4
# define EBP 5
# define EAX 6
# define DS 7
# define ES 8
# define FS 9
# define GS 10
# define ORIG_EAX 11
# define EIP 12
# define CS  13
# define EFL 14
# define UESP 15
# define SS   16
#endif

这样8*ORIG_RAX就找到USER area 中 ORIG_RAX 寄存器值的保存地址。ORIG_RAX保存了系统调用号。

当检查完系统调用之后,可以调用ptrace并设置参数PTRACE_CONT让子进程继续进行。

 

 

2.读取子进程系统调用参数

 

//64位下乌班图程序#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <sys/syscall.h>
#include <stdio.h>
int main(){pid_t child;long orig_rax;int status;int iscalling=0;struct user_regs_struct regs;child = fork();if(child==0){ptrace(PTRACE_TRACEME,0,NULL,NULL);execl("/bin/ls","ls","-l","-h",NULL);}else{while(1){wait(&status);if(WIFEXITED(status))break;orig_rax=ptrace(PTRACE_PEEKUSER,child,8*ORIG_RAX,NULL);if(orig_rax == SYS_write){ptrace(PTRACE_GETREGS,child,NULL,&regs);if(!iscalling){iscalling =1;printf("SYS_write call with %lld, %lld, %lld\n",regs.rdi,regs.rsi,regs.rdx);} else{printf("SYS_write call return %lld\n",regs.rax);iscalling = 0;}                                  }ptrace(PTRACE_SYSCALL,child,NULL,NULL);}}return 0;
}

编译后输出:

SYS_write call with 1, 140179049189376, 14
总用量 28K
SYS_write call return 14
SYS_write call with 1, 140179049189376, 51
-rw-rw-r-- 1 user1 user1  534  2月 26 18:02 ex1.c
SYS_write call return 51
SYS_write call with 1, 140179049189376, 52
-rw-rw-r-- 1 user1 user1  534  2月 26 18:02 ex1.c~
SYS_write call return 52
SYS_write call with 1, 140179049189376, 53
-rw-rw-r-- 1 user1 user1 1.1K  3月  2 13:02 hook2.c
SYS_write call return 53
SYS_write call with 1, 140179049189376, 54
-rw-rw-r-- 1 user1 user1 1.1K  3月  2 13:02 hook2.c~
SYS_write call return 54
SYS_write call with 1, 140179049189376, 53
-rwxrwxr-x 1 user1 user1 8.6K  3月  2 13:02 hook2.o
SYS_write call return 53

可以看到ls -l -h 执行了六次SYS_write系统调用。
读取寄存器中的参数时,可以使用PTRACE_PEEKUSER一个字一个字读取,也可以使用PTRACE_GETREGS参数直接将寄存器的值读取到结构体user_regs_struct 中,该结构体定义在sys/user.h中

对于PTRACE_STSCALL参数,该参数会像PTRACE_CONT一样使暂停的子进程继续执行,并在子进程下次进行系统调用前或系统调后,向子进程发送SINTRAP信号量,让子进程暂停。

WIFEXITED函数(宏)函数用来检查子进程是暂停还准备退出。

 

3.修改子进程系统调用参数

 

val = ptrace(PTRACE_PEEKDATA,child,addr,NULL)

PTRACE_PEEKDATA、PTRACE_PEEKTEXT参数是在tracee内存的addr地址处读取一个字(sizeof(long))的数据,反回值是long 型的,可多次读取addr
+i*sizeof(long)然后再合并得到最终字符串的内容。

现在,我们对系统调用write 输出的字符串参数进行反转:

#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <sys/syscall.h>
#include <sys/user.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#define long_size sizeof(long)void reverse(char * str)
{int i,j;char temp;for(i=0,j=strlen(str)-2;i<=j;++i,--j){temp=str[i];str[i]=str[j];str[j]=temp;}
}void getdata(pid_t child,long addr,char * str,int len){char * laddr;int i,j;union u{long val;char chars[long_size];} data;i=0;j=len/long_size;laddr=str;while(i<j){data.val=ptrace(PTRACE_PEEKDATA,child,addr+i*long_size,NULL);if(data.val == -1){if(errno){printf("READ error: %s\n",strerror(errno));}}memcpy(laddr,data.chars,long_size);++i;laddr +=long_size;};j=len % long_size;if(j!=0){data.val=ptrace(PTRACE_PEEKDATA,child,addr+i*long_size,NULL);memcpy(laddr,data.chars,j);}str[len]='\0';
}void putdata(pid_t child,long addr,char * str,int len){char * laddr;int i,j;union u{long val;char chars[long_size];} data;i=0;j=len /long_size;laddr=str;while(i<j){memcpy(data.chars,laddr,long_size);ptrace(PTRACE_POKEDATA,child,addr +i*long_size,data.val);++i;laddr+=long_size;}j=len%long_size;if(j!=0){   //注意:由于写入时也是按字写入的,所以正确的做法是先将该字的高地址数据读出保存在data的高地址上 ,然后将该字再写入memcpy(data.chars,laddr,j);ptrace(PTRACE_POKEDATA,child,addr +i*long_size,data.val);}}int main(){pid_t child;int status;struct user_regs_struct regs;child =fork();if(child ==0){ptrace(PTRACE_TRACEME,0,NULL,NULL);execl("/bin/ls","ls",NULL);}else{long orig_eax;char *str,*laddr;int toggle =0;while(1){wait(&status);if(WIFEXITED(status))break;orig_eax = ptrace(PTRACE_PEEKUSER,child,8*ORIG_RAX,NULL);if(orig_eax == SYS_write){if(toggle == 0){toggle =1;ptrace(PTRACE_GETREGS,child,NULL,&regs);str=(char * )calloc((regs.rdx+1),sizeof(char));getdata(child,regs.rsi,str,regs.rdx);reverse(str);putdata(child,regs.rsi,str,regs.rdx);}else{toggle =0;}}ptrace(PTRACE_SYSCALL,child,NULL,NULL);}}return 0;
}

输出:

user1@user-virtual-machine:~/hookTest$ ./hook3.o
o.3kooh  ~c.3kooh  c.3kooh  o.2kooh  ~c.2kooh c.2kooh  ~c.1xe  c.1xe

 

 

4. 向其它程序注入指令

我们追踪其它独立运行的进程时,需要使用下面的命令:

ptrace(PTRACE_ATTACH, pid, NULL, NULL)

使pid进程成为被追踪的tracee进程。tracee进程会被发送一个SIGTOP信号量,tracee进程不会立即停止,直到完成本次系统调用。如果要结束追踪,则调用PTRACE_DETACH即可。

debug 设置断点的功能可以通过ptrace实现。原理是ATTACH正在运行的进程使其停止。然后读取该进程的指令寄存器IR(32位x86为EIP,64w的是RIP)内容所指向的指令,备份后替换成目标指令,再使其继续执行,此时被追踪进程就会执行我们替换的指令,运行完注入的指令之后,我们再恢复原进程的IR
,从而达到改变原程序运行逻辑的目的。

tracee进程代码:

stdio.h>int main(){int i=0;while(1){printf("Hello,ptrace! [pid:%d]! num is %d\n",getpid(),i++);sleep(2);}return 0;
}

tracer进程代码


#include<sys/ptrace.h>
#include<sys/reg.h>
#include<sys/wait.h>
#include<sys/user.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<stdio.h>#define long_size sizeof(long)void getdata(pid_t child, long addr ,char * str,int len){char * laddr =str;int i,j;union u{long  val;char   chars [long_size] ;} data;i=0;j=len/long_size;while(i<j){data.val=ptrace(PTRACE_PEEKDATA,child,addr + long_size*i,NULL);if(data.val==-1){if(errno){printf("READ error: %s\n",strerror(errno));}}memcpy(laddr,data.chars,long_size);++i; laddr=laddr+long_size;}j= len %long_size;if(j!=0){data.val=ptrace(PTRACE_PEEKDATA,child,addr + long_size*i,NULL);if(data.val==-1){if(errno){printf("READ error: %s\n",strerror(errno));}}memcpy(laddr,data.chars,j);}str[len]='\0';
}void putdata(pid_t child , long addr,char * str,int len){char * laddr =str;int i,j;j=len/long_size;i=0;union u{long val;char chars [long_size]  ;} data;while(i<j){memcpy(data.chars,laddr,long_size);ptrace(PTRACE_POKEDATA,child,addr + long_size*i,data.val);++i;laddr=laddr+long_size;}j=len%long_size;if(j!=0){data.val= ptrace(PTRACE_PEEKDATA,child,addr + long_size*i,NULL);if(data.val==-1){if(errno){printf("READ error: %s\n",strerror(errno));}}memcpy(data.chars,laddr,j);ptrace(PTRACE_POKEDATA,child,addr + long_size*i,data.val);   }     
}int main(int argc,char * argv[]){if(argc!=2){printf("Usage: %s pid\n",argv[0]);}pid_t tracee = atoi(argv[1]);struct user_regs_struct regs;/*int 80(系统调用) int 3(断点)*/unsigned char code[]={0xcd,0x80,0xcc,0x00,0,0,0,0}; //八个字节,等于long 型的长度char backup[8]; //备份读取的指令ptrace(PTRACE_ATTACH,tracee,NULL,NULL);long inst;  //用于保存指令寄存器所指向的下一条将要执行的指令的内存地址 wait(NULL);ptrace(PTRACE_GETREGS,tracee,NULL,&regs);inst  =ptrace(PTRACE_PEEKTEXT,tracee,regs.rip,NULL);printf("tracee:RIP:0x%llx INST: 0x%lx\n",regs.rip,inst);//读取子进程将要执行的 7 bytes指令并备份getdata(tracee,regs.rip,backup,7);//设置断点putdata(tracee,regs.rip,code,7);//让子进程继续执行并执行“int 3”断点指令停止ptrace(PTRACE_CONT,tracee,NULL,NULL);wait(NULL);long rip=ptrace(PTRACE_PEEKUSER,tracee,8*RIP,NULL);//获取子进程停止时,rip的值long inst2=ptrace(PTRACE_PEEKTEXT,tracee,rip,NULL);printf("tracee:RIP:0x%lx INST: 0x%lx\n",rip,inst2);printf("Press Enter to continue  tracee process\n");getchar();putdata(tracee,regs.rip,backup,7); //重新将备份的指令写回寄存器ptrace(PTRACE_SETREGS,tracee,NULL,&regs);//设置会原来的寄存器值ptrace(PTRACE_CONT,tracee,NULL,NULL);ptrace(PTRACE_DETACH,tracee,NULL,NULL);return 0;}

先运行tracee.o 文件

$  ./tracee.o

此时tracee.o输出:

Hello,ptrace! [pid:14384]! num is 0
Hello,ptrace! [pid:14384]! num is 1
Hello,ptrace! [pid:14384]! num is 2
Hello,ptrace! [pid:14384]! num is 3
......

再另打开一个shell运行attach.o文件

$  ./.attach.o  14384 //pid

此时tracee.o执行到int 3断点指令停止,attach1,o输出:

tracee:RIP:0x7f48b0394f20 INST: 0x3173fffff0013d48
tracee:RIP:0x7f48b0394f23 INST: 0x8348c33100000000
Press Enter to continue  tracee process

按任意键tracee.o恢复执行

参考:http://www.cnblogs.com/pannengzhi/p/5203467.html

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/495363.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

10年软件开发教会我最重要的10件事[转]

1.面向对象比你想象的要难得多 也许只有我有这种想法&#xff0c;不过我曾经以为计算机科学课上学过的“面向对象”是很简单的东西。我的意思是&#xff0c;创建一些类来模拟现实世界能有多难啊&#xff1f;其实&#xff0c;那还真是挺难的。 十年之后&#xff0c;我仍然在…

安卓系统内 的 安卓虚拟机

转载&#xff1a;虚拟大师&#xff0c;让你的系统再安装一个系统&#xff0c;实现Xposed自由&#xff0c;支持摄像头调用http://xposed.appkg.com/2971.html 虚拟大师 VMOS 虚拟大师简介 虚拟大师是 Android 系统上的 Vmvare&#xff0c;完整的且自带 Root 系统能够让你实现无限…

未来一年最有可能成为独角兽的29家美国初创公司

来源&#xff1a; 资本实验室摘要&#xff1a;近期&#xff0c;研究机构PitchBook梳理出了在未来一年最有可能成为独角兽的29家美国初创公司。资本实验室今日投资关注聚焦前沿科技创新与传统产业升级“小兄弟&#xff0c;老夫看你骨骼清奇&#xff0c;天赋异禀&#xff0c;定是…

StreamWriter类的一般使用方法

理解StreamWriter可以对照StreamReader类来进行&#xff0c;因为他们只是读写的方式不同&#xff0c;一个是读&#xff0c;一个是写&#xff0c;其他的差别不是特别大。 StreamWriter继承于抽象类TextWriter&#xff0c;是用来进行文本文件字符流写的类。 它是按照一种特定的编…

Firefox、Chrome 盘助手

From&#xff1a;https://www.runningcheese.com/baiduyun IDM 下载地址&#xff1a;http://www.ucbug.com/soft/26392.html 定制 Firefox、Chrome 下载地址&#xff1a;https://www.runningcheese.com/ 由网友 "哩呵" 制作的 网盘助手 脚本&#xff0c;需要通过拓…

复杂性理论:科学方法的第三个梯级

来源&#xff1a;人机与认知实验室摘要&#xff1a;莫兰认为系统论超越了还原论&#xff0c;复杂性理论又超越了系统论&#xff0c;它们代表着科学方法论依次达到的三个梯级。复杂性研究从20世纪末叶兴起&#xff0c;目前在国内外已成为许多学科领域内研究的前沿和热点。它涉及…

hdu 1241 Oil Deposits 解题报告

题目链接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid1241 第一次写搜索代码 花了好大的功夫检查 看了别人的结题报告 还是错误一大堆 哎 题目是这样的 表示油田 *表示非油田 与其八个方向组成一片连续油田 一个*组成的图 文有几片油田 思路&#xff1a;没被标记找…

Notepad++ 简单使用

github 下载地址&#xff1a;https://github.com/notepad-plus-plus 简介 Notepad 是 Windows 下的一款免费开源代码编辑器&#xff0c;它使用较少的CPU功率&#xff0c;降低电脑系统能源消耗&#xff0c;但轻巧且执行效率高&#xff0c;使得 Notepad 可完美地取代微软视窗的记…

MIT:机器学习预测2018世界杯冠军

来源 &#xff1a;199IT互联网数据中心摘要&#xff1a;麻省理工学院报道&#xff0c;他们的研究人员开发了一种新型人工智能&#xff0c;用以预测世界杯走势。麻省理工学院报道&#xff0c;他们的研究人员开发了一种新型人工智能&#xff0c;用以预测世界杯走势。研究人员声称…

安卓逆向_20 --- 模拟器检测、反调试检测、ELF动态调试、__libc_init 下断

From&#xff08; 模拟器检测实战分析 &#xff09;&#xff1a;https://www.bilibili.com/video/BV1UE411A7rW?p65 怎样过 app 的模拟器检测&#xff1a;https://bbs.pediy.com/thread-249759.htm Android 模拟器如何不被检测思路&#xff1a;https://bbs.pediy.com/thread…

2017-2018中国机器人创新Top100榜单深度分析

来源&#xff1a;产业创新创投数据平台Innov100摘要&#xff1a;人类和机器人由相互隔离、保持距离发展到充分的人机交互&#xff0c;融合共生。一、2017-2018年中国机器人创新Top100榜单二、中国机器人创新Top100数据分析&#xff08;一&#xff09;机器人创新Top100产业生态分…

[SCM]源码管理 - perforce快速入门

Perforce是目前比较流行的集中式的源代码管理工具&#xff0c;在很多的欧美跨国公司被广泛的使用。他能够运行于Windows&#xff0c;Linux&#xff0c;Mac几乎所有的平台&#xff0c;支持客户端&#xff0c;命令行&#xff0c;IDE集成&#xff0c;操作系统shell&#xff0c;API…

揭秘:机器究竟是怎么学习的?

来源&#xff1a;雪球摘要&#xff1a;从算法的角度看&#xff0c;机器学习有很多种算法&#xff0c;例如回归算法、基于实例的算法、正则化算法、决策树算法、贝叶斯算法、聚合算法、关联规则学习算法和人工神经网络算法。从算法的角度看&#xff0c;机器学习有很多种算法&…

安卓逆向_21 --- Java层和so层的反调试( IDA 动态调试 JNI_OnLoad、init_array下断)

1. 安卓程序动态调试条件 安卓程序动态调试条件 ( 2个满足1个即可 )&#xff1a; 1. 在 AndroidMainfest.xml ---> application 标签下&#xff0c;设置或者添加属性 android:debuggable"true" 2. 系统默认模式&#xff0c;在 build.prop(boot.img)&#xff0c;…

JavaScript MSN 弹出消息框

** 类名&#xff1a;CLASS_MSN_MESSAGE ** 功能&#xff1a;提供类似MSN消息框 ** 示例&#xff1a; ** 作者&#xff1a;ttyp ** 邮件&#xff1a;ttyp21cn.com ** 日期&#xff1a;2005-3-18 ** **/ JS 代码&#xff1a; // JScri…

一文读懂:深扒人脸识别60年技术发展史

来源&#xff1a;与非网摘要&#xff1a; “他来听我的演唱会&#xff0c;门票换了手铐一对”。最近歌神张学友变阿SIR&#xff0c;演唱会上频频抓到罪犯&#xff0c;将人脸识别技术又一次推到了大众的视线中。“他来听我的演唱会&#xff0c;门票换了手铐一对”。最近歌神张学…

安卓逆向_22( 一 ) --- Xposed【 Android Studio + Xposed 实现简单的 hook 】

From&#xff1a;使用渗透测试框架 Xposed 框架 hook 调试 Android APP&#xff1a;https://www.freebuf.com/articles/terminal/56453.html Xposed框架分析&#xff1a;https://blog.csdn.net/zjx839524906/article/details/81046844 xposted框架 原理 Xposed框架之函数Hoo…

【数据采集】将16进制字符串转化为Double类型输出(依照IEEE754标准)

因为需要读取二进制文件中包含的数据&#xff0c;故需要这样一个转化过程。 鄙人功力太浅&#xff0c;主要参照http://www.cnblogs.com/xinjun/archive/2010/07/28/1787297.html&#xff0c;略有改动&#xff0c;以保证编译运行通过。 1 #include <iostream> 2 #include…

TED演讲 | 2100年,神经学家如何研究人类大脑?

“本文来源于药明康德微信公众号&#xff08;ID&#xff1a;WuXiAppTecChina&#xff09;”除非我们弄清楚如何在健康人体中获得神经元的电活动&#xff0c;否则不会在理解人类大脑或疾病方面取得任何进展。 Were not going to make any progress towards understanding the hu…

安卓逆向_24( 一 ) --- Hook 框架 frida( Hook Java层 和 so层) )

From&#xff1a;Hook 神器家族的 Frida 工具使用详解&#xff1a;https://blog.csdn.net/FlyPigYe/article/details/90258758 详解 Hook 框架 frida ( 信抢红包 )&#xff1a;https://www.freebuf.com/company-information/180480.html APP逆向神器之Frida【Android初级篇】…