实现安卓与类安卓系统的定位信息伪造
方法一:下载DevEco Studio跑虚拟机,为Harmony OS镜像营造虚假的定位信息
如今的问题:
1.DevEco Studio不支持下载应用
2.如何为Harmony OS提供一个虚拟的定位设备?
方法二:提供一个自己编写的向应用提供的定位模块,应用看似在进行系统调用,实则在调用我提供的返回虚假定位信息的函数。
方法一:下载DevEco Studio跑虚拟机,为Harmony OS镜像营造虚假的定位信息
如今的问题:
1.DevEco Studio不支持下载应用
2.如何为Harmony OS提供一个虚拟的定位设备?
方法二:提供一个自己编写的向应用提供的定位模块,应用看似在进行系统调用,实则在调用我提供的返回虚假定位信息的函数。
功能:向指定进程或进程组发送信号
函数声明:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
参数说明:
• pid
:目标进程/进程组标识符
• >0
:发送给特定进程
• =0
:发送给调用进程所属进程组
• =-1
:广播发送给所有有权进程
• <-1
:发送给进程组ID为|pid|
的组
• sig
:信号编号(如SIGTERM=15)
返回值:
• 成功返回0,失败返回-1并设置errno(EPERM/ESRCH/EINVAL)
功能:向指定进程组发送信号
函数声明:
#include <signal.h>
int killpg(pid_t pgrp, int sig);
参数说明:
• pgrp
:目标进程组ID(0表示当前进程组)
• sig
:同kill系统调用
返回值:
• 同kill系统调用
功能:向当前进程发送信号
函数声明:
#include <signal.h>
int raise(int sig);
参数说明:
• sig
:待发送信号编号
返回值:
• 成功返回0,失败返回非0值
功能:注册标准信号处理函数
函数声明:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
参数说明:
• signum
:待捕获信号编号(如SIGINT)
• handler
:处理函数指针,可取值:
• SIG_IGN
:忽略信号
• SIG_DFL
:默认处理
• 自定义函数指针
返回值:
• 成功返回原处理函数指针,失败返回SIG_ERR
功能:支持实时信号处理的增强型注册接口
函数声明:
#include <signal.h>
int sigaction(int signum,
const struct sigaction *act,
struct sigaction *oldact);
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
参数说明:
• signum
:同signal
• act
:新处理配置
• sa_handler
:标准处理函数
• sa_sigaction
:实时信号处理函数(需设置SA_SIGINFO标志)
• sa_mask
:执行处理函数期间阻塞的信号集
• sa_flags
:控制标志位,常用值:
◦ `SA_NOCLDSTOP`:子进程停止时不产生SIGCHLD
◦ `SA_RESTART`:自动重启被中断的系统调用
◦ `SA_SIGINFO`:使用sa_sigaction处理函数
• oldact
:保存原处理配置
siginfo_t结构体:
typedef struct {
int si_signo; /* 信号编号 */
int si_code; /* 信号来源 */
pid_t si_pid; /* 发送进程PID */
uid_t si_uid; /* 发送进程UID */
void *si_addr; /* 引发故障的内存地址 */
int si_status; /* 子进程退出状态 */
union sigval si_value;/* 伴随数据 */
} siginfo_t;
返回值:
• 成功返回0,失败返回-1
功能:修改进程信号屏蔽字
函数声明:
#include <signal.h>
int sigprocmask(int how,
const sigset_t *set,
sigset_t *oldset);
参数说明:
• how
:操作类型
• SIG_BLOCK
:添加set到屏蔽集
• SIG_UNBLOCK
:从屏蔽集移除set
• SIG_SETMASK
:直接设置屏蔽集为set
• set
:待操作信号集
• oldset
:保存原屏蔽集
返回值:
• 成功返回0,失败返回-1
功能:原子操作解除信号屏蔽并等待信号
函数声明:
#include <signal.h>
int sigsuspend(const sigset_t *mask);
参数说明:
• mask
:临时信号屏蔽字
返回值:
• 总是返回-1,errno=EINTR
功能:同步等待指定信号并获取详细信息
函数声明:
#include <signal.h>
int sigwaitinfo(const sigset_t *set,
siginfo_t *info);
参数说明:
• set
:等待信号集合
• info
:接收信号详细信息
返回值:
• 成功返回信号编号,失败返回-1
功能:带超时的信号等待
函数声明:
#include <signal.h>
int sigtimedwait(const sigset_t *set,
siginfo_t *info,
const struct timespec *timeout);
参数说明:
• timeout
:超时时间结构体
struct timespec {
time_t tv_sec; /* 秒 */
long tv_nsec; /* 纳秒 */
};
返回值:
• 成功返回信号编号,超时返回-1且errno=EAGAIN
等待方式 | 阻塞行为 | 返回值处理 | 适用场景 |
---|---|---|---|
sigsuspend | 临时解除 | 中断系统调用 | 简单信号等待 |
sigwaitinfo | 持续阻塞 | 直接返回信号编号 | 实时信号处理 |
sigtimedwait | 超时阻塞 | 支持超时控制 | 需要超时机制的场景 |
IO多路复用(Multiplexing)是一种允许单个进程/线程监视多个文件描述符的机制,可以同时检测多个文件描述符是否处于可读、可写或异常状态。
水平触发LT(Level-Triggered):状态可I/O则通知
边缘触发ET(Edge-Triggered):不可I/O变成可I/O则通知(导致一次通知后必须读完)
功能:返回哪些文件描述符不会阻塞,注意只是不会阻塞,分为有数据或出错情况。可以设置超时时间。
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
参数说明:
• nfds
:所有被监视的文件描述符中最大的文件描述符加1
• readfds
:指向可读文件描述符集合的指针,传入函数前需要指定为你想要监控读操作的文件描述符集合,返回时这里会放置可以让read不阻塞的文件描述符集合
• writefds
:指向可写文件描述符集合的指针,,传入函数前需要指定为你想要监控写操作的文件描述符集合,返回时这里会放置可以让write不阻塞的文件描述符集合
• exceptfds
:指向异常(并非错误)文件描述符集合的指针,传入函数前需要指定为你想要监控异常的文件描述符集合,当连接到处于信包模式下的伪终端主设备上的从设备状态发生了改变或流式套接字上接收到了带外数据时会被记录在这个集合。
• timeout
:超时时间结构体指针。该参数可指定为NULL,此时select()会一直阻塞。如果结构体timeval的两个域都为0的话,此时 select()不会阻塞,它只是简单地轮询指定的文件描述符集合,看看其中是否有就绪的文件描述符并立刻返回。否则,timeout将为select()指定一个等待时间的上限值
timeval结构体:
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
相关宏定义:
FD_SETSIZE // 通常为1024,定义了fd_set能容纳的最大文件描述符数量
FD_ZERO(fd_set *set) // 清空文件描述符集合
FD_SET(int fd, fd_set *set) // 将fd添加到集合中
FD_CLR(int fd, fd_set *set) // 从集合中移除fd
FD_ISSET(int fd, fd_set *set) // 检查fd是否在集合中
返回值:
• 成功:返回就绪的文件描述符总数,即三个集合内文件描述符的数量相加的结果,有可能重复
• 超时:返回0
• 出错:即任意一个或多个文件描述符被关闭则返回-1,并设置errno
功能:与select类似,但使用不同的文件描述符集合表示方式,没有最大文件描述符数量限制。
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数说明:
• fds
:指向pollfd结构体数组的指针
• nfds
:fds数组中的元素个数
• timeout
:超时时间(毫秒),-1表示阻塞等待,0表示立即返回
pollfd结构体:
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 等待的事件 */
short revents; /* 实际发生的事件 */
};
事件标志(events/revents):
宏定义 | 值(十六进制) | 说明 | 依赖宏定义 |
---|---|---|---|
POLLIN | 0x0001 | 普通或优先级带数据可读(等效于 POLLRDNORM \ POLLRDBAND ) | 标准 POSIX |
POLLPRI | 0x0002 | 高优先级数据可读(如 TCP 带外数据) | 标准 POSIX |
POLLOUT | 0x0004 | 数据可写(等效于 POLLWRNORM ) | 标准 POSIX |
POLLRDNORM | 0x0040 | 普通数据可读(需定义 _XOPEN_SOURCE ) | _XOPEN_SOURCE |
POLLRDBAND | 0x0080 | 优先级带数据可读(需定义 _XOPEN_SOURCE ) | _XOPEN_SOURCE |
POLLWRNORM | 0x0100 | 普通数据可写(需定义 _XOPEN_SOURCE ) | _XOPEN_SOURCE |
POLLWRBAND | 0x0200 | 优先级带数据可写(需定义 _XOPEN_SOURCE ) | _XOPEN_SOURCE |
POLLERR | 0x0008 | 发生错误(输出事件,不可在 events 中设置) | 标准 POSIX |
POLLHUP | 0x0010 | 连接挂起(输出事件,不可在 events 中设置) | 标准 POSIX |
POLLNVAL | 0x0020 | 无效文件描述符(输出事件,不可在 events 中设置) | 标准 POSIX |
POLLRDHUP | 0x2000 | 对端关闭连接或半关闭(需定义 _GNU_SOURCE ,Linux 特有) | _GNU_SOURCE |
POLLMSG | 0x0400 | 系统消息(Linux 未使用,保留) | _XOPEN_SOURCE |
POLLREMOVE | 0x1000 | 从监控集中移除(Linux 特有,已废弃) | _GNU_SOURCE (已弃用) |
POLLONESHOT | 0x4000 | 一次性监控(触发后自动移除,需定义 _GNU_SOURCE ,Linux 特有) | _GNU_SOURCE |
POLLWRITE | 0x10000 | 可写(非标准扩展,某些平台使用) | 非标准 |
总结以上要点,poll()真正关心的标志位就是POLLIN、POLLOUT、POLLPRI、POLLRDHUP、
POLLHUP以及POLLERR。
返回值:
• 成功:返回就绪的文件描述符数量
• 超时:返回0
• 出错:返回-1,并设置errno
#include <signal.h>
struct sigaction sa;
sa.sa_flags = SA_RESTART; // 默认自动重启被中断的系统调用
sa.sa_handler = sigio_handler;
sigemptyset(&sa.sa_mask);
sigaction(SIGIO, &sa, NULL);
关键参数:
• SA_RESTART
:当信号中断低速系统调用时自动重启
• sigio_handler
:自定义信号处理函数原型void handler(int sig)
fcntl(fd, F_SETOWN, getpid()); // 设置进程为属主
// 或设置进程组
fcntl(fd, F_SETOWN, -getpgrp());
F_SETOWN
参数规则:
• 正数:进程ID,
• 负数:进程组ID,注意这里进程组id若过小函数会返回错误,这是glibc实现的不足之处,具体可参考相关文档的解决办法
设置线程为属主可参考相关文档,也可避免进程组id若过小函数会返回错误
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK);
标志位说明:
• O_ASYNC
:启用信号驱动I/O(原O_SYNC为笔误,正确应为O_ASYNC)
• O_NONBLOCK
:保证read()在无数据时立即返回EAGAIN
• 信号队列化:实时信号(SIGRTMIN-SIGRTMAX)支持队列化存储,避免信号丢失
• 事件信息传递:通过siginfo_t
结构携带文件描述符和事件类型
• 精确控制:可区分不同文件描述符的就绪事件
struct sigaction sa;
sa.sa_flags = SA_SIGINFO; // 必须启用此标志
sa.sa_sigaction = rt_signal_handler; // 三参数处理函数
sigemptyset(&sa.sa_mask);
sigaction(SIGRTMIN, &sa, NULL);
处理函数原型:
void handler(int sig, siginfo_t *info, void *ucontext)
fcntl(fd, F_SETSIG, SIGRTMIN); // 指定实时信号
F_SETSIG
特性:
• 参数需≥SIGRTMIN的实时信号编号
• 设置为0时恢复默认SIGIO行为
sigset_t waitset;
sigaddset(&waitset, SIGRTMIN);
sigprocmask(SIG_BLOCK, &waitset, NULL);
siginfo_t info;
int sig = sigwaitinfo(&waitset, &info); // 同步阻塞等待
siginfo_t
关键成员:
siginfo_t {
int si_signo; // 信号编号
int si_fd; // 产生信号的文件描述符
int si_band; // 事件掩码(POLLIN/POLLOUT等)
}
fcntl(fd, F_SETOWN, 0); // 清除文件属主设置
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags & ~O_ASYNC);
signal(SIGIO, SIG_DFL); // 恢复默认处理
fcntl(fd, F_SETSIG, 0); // 还原为SIGIO
sigqueue_t qinfo;
while(sigwaitinfo(&waitset, &qinfo) > 0); // 排空已排队信号
sigprocmask(SIG_UNBLOCK, &waitset, NULL);
// 查看当前限制
cat /proc/sys/kernel/rtsig-max
// 临时修改限制(需root权限)
echo 1000000 > /proc/sys/kernel/rtsig-max
// 永久修改(在/etc/sysctl.conf添加)
kernel.rtsig-max = 1000000
相关内核参数:
• rtsig-max
:最大排队信号数(默认值通常为1024)
• rtsig-nr
:当前排队信号数(通过/proc/sys/kernel/rtsig-nr
查看)
signal(SIGIO, overflow_handler); // 信号队列满时触发
void overflow_handler(int sig) {
struct pollfd pfds[MAX_FDS];
// 填充所有监控的fd
poll(pfds, num_fds, 0); // 非阻塞检查
}
// 在poll返回后遍历所有fd
for(int i=0; i<num_fds; i++) {
if(pfds[i].revents & POLLIN) {
// 处理读就绪事件
}
}
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
命令 | 参数类型 | 作用域 |
---|---|---|
F_SETOWN | int | 设置进程/进程组属主 |
F_SETSIG | int | 绑定实时信号 |
F_GETFL | void | 获取文件状态标志 |
F_SETFL | int | 设置文件状态标志 |
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
sa_flags
关键标志组合:
• SA_RESTART | SA_SIGINFO
:支持自动重启并携带扩展信息
• SA_NODEFER
:不自动阻塞当前信号类型
标志位 | 值(十六进制) | 对应事件 |
---|---|---|
POLLIN | 0x0001 | 普通或高优先级数据可读 |
POLLPRI | 0x0002 | 高优先级数据可读 |
POLLOUT | 0x0004 | 写数据不会导致阻塞 |
POLLERR | 0x0008 | 发生错误 |
POLLHUP | 0x0010 | 连接挂起 |
POLLNVAL | 0x0020 | 无效的文件描述符 |
select与poll是库函数进行循环调用系统调用来返回就绪的fd,而epoll直接是内核根据其维护的打开文件句柄表来创建红黑树实现监控。select与poll干活的人是库,epoll干活的是内核。
功能:创建epoll实例。
#include <sys/epoll.h>
int epoll_create(int size);
int epoll_create1(int flags);
参数说明:
• size
:提示内核期望监控的文件描述符数量(已废弃),随便你怎么指定
• flags
:
• EPOLL_CLOEXEC
:设置close-on-exec标志,目前只实现了这个
返回值:
• 成功:返回epoll文件描述符
• 失败:返回-1,并设置errno
功能:向epoll实例添加、修改或删除文件描述符。当内核维护的打开文件描述的一项文件句柄没有任何进程的fd指向它时,内核在删除该文件句柄的同时删除epoll实例对其的监控(从红黑树中删除)
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数说明:
• epfd
:epoll文件描述符
• op
:操作类型:
• EPOLL_CTL_ADD
:添加文件描述符
• EPOLL_CTL_MOD
:修改文件描述符
• EPOLL_CTL_DEL
:删除文件描述符
• fd
:要操作的目标文件描述符
• event
:指向epoll_event结构体的指针
epoll_event结构体:
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
typedef union epoll_data {
void *ptr;
int fd; //监控文件描述符需要用这个
uint32_t u32;
uint64_t u64;
} epoll_data_t;
使用时既设置参数fd为要监控的fd,又设置struct event结构体的epoll_data_t为要监控的fd
事件标志(events)(仅仅较poll的事件前面加了一个'e'):
EPOLLIN // 可读事件
EPOLLOUT // 可写事件
EPOLLRDHUP // 对端关闭连接或关闭写操作
EPOLLPRI // 紧急数据可读
EPOLLERR // 错误条件发生
EPOLLHUP // 挂起发生
EPOLLET // 设置边缘触发模式(注意要读完数据的同时防止一个文件描述符上源源不断有数据导致其他文件描述符饥饿)
EPOLLONESHOT // 设置一次性监听
返回值:
• 成功:返回0
• 失败:返回-1,并设置errno
功能:等待epoll文件描述符上的IO事件。
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
参数说明:
• epfd
:epoll文件描述符
• events
:用于返回事件的数组
• maxevents
:events数组的大小,起码大于你往epoll实例里面添加的文件描述符个数
• timeout
:超时时间(毫秒),-1表示阻塞,0表示立即返回
返回值:
• 成功:返回就绪的文件描述符数量
• 超时:返回0
• 出错:返回-1,并设置errno
功能:与epoll_wait类似,但允许指定信号掩码。
#include <sys/epoll.h>
int epoll_pwait(int epfd, struct epoll_event *events,
int maxevents, int timeout,
const sigset_t *sigmask);
参数说明:
• sigmask
:指向信号掩码的指针
返回值:同epoll_wait
tcgetattr()
与 tcsetattr()
功能:获取或设置终端的属性(包括输入/输出模式、控制字符定义等)。
#include <termios.h>
int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
参数说明:
• fd
:终端设备的文件描述符(如 /dev/tty
)。
• termios_p
:指向 termios
结构体的指针,用于存储或传递终端属性。
• optional_actions
(仅 tcsetattr
):决定何时应用新设置,可选宏:
• TCSANOW
:立即生效。
• TCSADRAIN
:等待当前输出完成后再生效。
• TCSAFLUSH
:清空输入/输出缓冲区后生效。
termios
结构体成员:
struct termios {
tcflag_t c_iflag; // 输入模式标志(如输入奇偶校验、回车处理)
tcflag_t c_oflag; // 输出模式标志(如输出映射、延迟处理)
tcflag_t c_cflag; // 控制模式标志(如波特率、数据位)
tcflag_t c_lflag; // 本地模式标志(如回显、信号处理)
cc_t c_cc[NCCS]; // 控制字符数组(如中断、停止字符)
};
返回值:
• 成功时返回 0
,失败返回 -1
并设置 errno
。
ioctl()
功能:执行设备相关的控制操作(如获取终端窗口大小、设置串口参数等)。
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ... /* void *arg */);
参数说明:
• fd
:终端设备的文件描述符。
• request
:控制请求码,与终端相关的常见宏:
• TIOCGWINSZ
:获取终端窗口大小(arg
指向 winsize
结构体)。
• TIOCSWINSZ
:设置终端窗口大小。
• TCGETS
:等效于 tcgetattr
。
• TCSETS
:等效于 tcsetattr
。
• arg
:可变参数,通常为指向数据结构的指针。
winsize
结构体成员:
struct winsize {
unsigned short ws_row; // 终端窗口行数
unsigned short ws_col; // 终端窗口列数
unsigned short ws_xpixel; // 水平像素(通常未使用)
unsigned short ws_ypixel; // 垂直像素(通常未使用)
};
返回值:
• 成功时返回 0
,失败返回 -1
并设置 errno
。
cfsetispeed()
与 cfsetospeed()
功能:设置终端的输入/输出波特率。
#include <termios.h>
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);
参数说明:
• termios_p
:指向 termios
结构体的指针。
• speed
:波特率宏,如:
• B0
:挂断线路。
• B9600
:9600 波特。
• B115200
:115200 波特。
• B4000000
:自定义高速波特率(部分系统支持)。
返回值:
• 成功返回 0
,失败返回 -1
。
tcsendbreak()
功能:在串行线上发送 BREAK 信号(用于重置设备)。
#include <termios.h>
int tcsendbreak(int fd, int duration);
参数说明:
• fd
:终端设备的文件描述符。
• duration
:BREAK 持续时间(若为 0
,则持续至少 0.25 秒)。
返回值:
• 成功返回 0
,失败返回 -1
并设置 errno
。
tcdrain()
功能:阻塞进程,直到所有输出数据传递到终端。
#include <termios.h>
int tcdrain(int fd);
参数说明:
• fd
:终端设备的文件描述符。
返回值:
• 成功返回 0
,失败返回 -1
并设置 errno
。
tcflush()
功能:刷新终端的输入/输出队列。
#include <termios.h>
int tcflush(int fd, int queue_selector);
参数说明:
• fd
:终端设备的文件描述符。
• queue_selector
:刷新目标队列的宏:
• TCIFLUSH
:刷新输入队列。
• TCOFLUSH
:刷新输出队列。
• TCIOFLUSH
:刷新输入和输出队列。
返回值:
• 成功返回 0
,失败返回 -1
并设置 errno
。
tcflow()
功能:暂停或恢复终端与计算机之间的数据传输。
#include <termios.h>
int tcflow(int fd, int action);
参数说明:
• fd
:终端设备的文件描述符。
• action
:控制操作的宏:
• TCOOFF
:挂起输出。
• TCOON
:恢复输出。
• TCIOFF
:发送 STOP 字符(暂停输入)。
• TCION
:发送 START 字符(恢复输入)。
返回值:
• 成功返回 0
,失败返回 -1
并设置 errno
。
tcgetpgrp()
与 tcsetpgrp()
功能:获取或设置终端的前台进程组ID。
#include <termios.h>
pid_t tcgetpgrp(int fd);
int tcsetpgrp(int fd, pid_t pgrp);
参数说明:
• fd
:终端设备的文件描述符。
• pgrp
:要设置的进程组ID(仅 tcsetpgrp
需要)。
返回值:
• tcgetpgrp
返回前台进程组ID。
• tcsetpgrp
成功返回 0
,失败返回 -1
。
tcgetsid()
功能:获取终端的会话ID。
#include <termios.h>
pid_t tcgetsid(int fd);
参数说明:
• fd
:终端设备的文件描述符。
返回值:
• 成功返回会话ID,失败返回 (pid_t)-1
并设置 errno
。
上述库函数最终通过系统调用与内核交互。以 ioctl
为例,其底层通过 int 0x80
(x86)或 syscall
(x86-64)触发中断,参数通过寄存器传递:
• eax
:系统调用号(如 SYS_ioctl
)。
• ebx
:文件描述符 fd
。
• ecx
:请求码 request
。
• edx
:可变参数指针 arg
。
参考资料: