随着市面上获取指纹的方式越来越多,获取的方式也千奇百怪。
比如最开始的system_property_get , system_property_find , system_property_read 直接调用native底层获取。
到后来的进阶包括svc读取boot_id 文件,内存反射mValues 的 map 变量获取android id
(需要过掉反射限制) , 都是很不错的方法指纹获取方法。
今天主要介绍的是通过内核通讯的方式获取设备网卡mac指纹,主要通过netlink的方式和内核通讯去获取mac网卡地址 。
这种方式可以直接绕过android的权限。
在不给app授权的时候也可以直接获取到网卡信息。因为很难进行mock,所以很多大厂app也都是采用这种办法去获取。
我在原有的基础上继续完善了一下逻辑,在接收消息的时候通过内联svc的方式处理接收收到的数据包,大大增加了数据的安全性。
也防止有人通过inlinehook 直接hook recv ,recvform,recvmsg 直接在收到数据包的时候被拦截和替换掉。
理论上这种方式可以过掉99%以上的改机软件。
一般来说用户空间和内核空间的通信方式有三种:
proc
ioctl
Netlink
而前两种都是单向的,但是Netlink可以实现双工通信。
Netlink协议基于BSD socket和AF_NETLINK地址簇(address family)。
使用32位的端口号寻址(以前称为PID),每个Netlink协议(或称作总线,man手册中则称之为netlink family),通常与一个或者一组内核服务/组件相关联,如NETLINK_ROUTE用于获取和设置路由与链路信息、NETLINK_KOBJECT_UEVENT用于内核向用户空间的udev进程发送通知等。
1,支持全双工、异步通信
2,用户空间可以使用标准的BSD socket接口(但netlink并没有屏蔽掉协议包的构造与解析过程,推荐使用libnl等第三方库)
3,在内核空间使用专用的内核API接口
4,支持多播(因此支持“总线”式通信,可实现消息订阅)
5,在内核端可用于进程上下文与中断上下文
不管是ip命令行还是Java的network接口,最终都是调用到ifaddrs.cpp -> getifaddrs
源码摘抄自:
http://aospxref.com/android-10.0.0_r47/xref/bionic/libc/bionic/ifaddrs.cpp#236
NetlinkConnection这个结构体是一个netlink的封装类
重点看一下ReadResponses的实现过程
代码摘抄自:
http://aospxref.com/android-10.0.0_r47/xref/bionic/libc/bionic/bionic_netlink.cpp
通过遍历拿到我们需要的内容,输出即可。
在接受消息的时候android源码是采用recv去接受的消息
通过循环的方式去判断结束位置。
但是recv这种函数很容易被hook,inlinehook recv ,recvfrom ,recvmsg
在方法执行完毕以后直接就可以处理参数二的返回值。
在不直接使用系统提供的recv 以后,有两种方式可以选择。
直接调用syscall函数,通过syscall函数进行切入到recv 。
这种方式可以更好的兼容32和64位,但是可能被直接hook syscall这个函数入口 。
因为和设备指纹相关的函数,是重点函数,侧重安全。所以重点采用方法2
将syscall 汇编代码嵌入到指定方法内部。
方法2:我们直接把recv换成svc内联汇编代码如下
相当于自己实现syscall (代码摘抄自libc syscall)
使用的话也很简单,导入函数头就好。
将代码替换成如下:
很不幸,报错了,安卓8内核上使用了seccomop 过滤掉了svc 直接调用 recv
报错的原因一句话
seccomp prevented call to disallowed arm system call 291
seccomp是Linux的一种安全机制,android 8.1以上使用了seccomp
主要功能是限制直接通过syscall去调用某些系统函数
seccomp的过滤模式有两种(strict&filter)
第一种strict只支持如下四种,如果一旦使用了其他的syscall 则会收到SIGKILL信号
read()
write()
exit()
rt_sigreturn
通过下面方式进行设置。
Seccomp-bpf
bpf是一种过滤模式,只有在linux高版本会存在该功能
当某进程调用了svc 首先会进入我们自己写的bpf规则
通过我们自己的写的规则,进行判断该函数是否被运行调用。
常用的就是ptrace+seccomp去修改svc的参数内容&返回值结果。
回到正文,不过还好,
在android底层 recv的实现是recvfom代码如下
我们将svc调用号切换到recvform
程序完美运行起来,网卡获取成功。
https://github.com/w296488320/getMacForNetlink
参考文章:
https://blog.csdn.net/zhizhengguan/article/details/120448337
文章来源:
课程78-79课件。v296488320
/
/
传入对应的结构体指针
int
getifaddrs(ifaddrs
*
*
out) {
/
/
We construct the result directly into `out`, so terminate the
list
.
*
out
=
nullptr;
/
/
Open
the netlink socket
and
ask
for
all
the links
and
addresses.
NetlinkConnection nc;
/
/
判断get addresses 和 get link是否打开成功,返回成功则返回
0
bool
okay
=
nc.SendRequest(RTM_GETLINK) && nc.ReadResponses(__getifaddrs_callback, out) &&
nc.SendRequest(RTM_GETADDR) && nc.ReadResponses(__getifaddrs_callback, out);
if
(!okay) {
out
=
nullptr;
freeifaddrs(
*
out);
/
/
Ensure that callers crash
if
they forget to check
for
success.
*
out
=
nullptr;
return
-
1
;
}
return
0
;
}
/
/
传入对应的结构体指针
int
getifaddrs(ifaddrs
*
*
out) {
/
/
We construct the result directly into `out`, so terminate the
list
.
*
out
=
nullptr;
/
/
Open
the netlink socket
and
ask
for
all
the links
and
addresses.
NetlinkConnection nc;
/
/
判断get addresses 和 get link是否打开成功,返回成功则返回
0
bool
okay
=
nc.SendRequest(RTM_GETLINK) && nc.ReadResponses(__getifaddrs_callback, out) &&
nc.SendRequest(RTM_GETADDR) && nc.ReadResponses(__getifaddrs_callback, out);
if
(!okay) {
out
=
nullptr;
freeifaddrs(
*
out);
/
/
Ensure that callers crash
if
they forget to check
for
success.
*
out
=
nullptr;
return
-
1
;
}
return
0
;
}
/
*
*
*
@param
type
发送参数的类型,具体获取的内容参考
*
@see rtnetlink.h
*
@
return
*
/
bool
NetlinkConnection::SendRequest(
int
type
) {
/
/
Rather than force
all
callers to check
for
the unlikely event of being
/
/
unable to allocate
8KiB
, check here.
/
/
NetlinkConnection构造方法 的时候生成的
8kb
的data内存
if
(data_
=
=
nullptr)
return
false;
/
/
Did we
open
a netlink socket yet?
if
(fd_
=
=
-
1
) {
/
/
尝试建立socket netlink 链接
fd_
=
socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
if
(fd_
=
=
-
1
)
return
false;
}
/
/
Construct
and
send the message.
/
/
构造要发送的消息
struct NetlinkMessage {
nlmsghdr hdr;
rtgenmsg msg;
} request;
memset(&request,
0
, sizeof(request));
request.hdr.nlmsg_flags
=
NLM_F_DUMP | NLM_F_REQUEST;
request.hdr.nlmsg_type
=
type
;
request.hdr.nlmsg_len
=
sizeof(request);
/
/
All
families
request.msg.rtgen_family
=
AF_UNSPEC;
/
/
使用socket数据发送
return
(TEMP_FAILURE_RETRY(send(fd_, &request, sizeof(request),
0
))
=
=
sizeof(request));
}
/
*
*
*
@param
type
发送参数的类型,具体获取的内容参考
*
@see rtnetlink.h
*
@
return
*
/
bool
NetlinkConnection::SendRequest(
int
type
) {
/
/
Rather than force
all
callers to check
for
the unlikely event of being
/
/
unable to allocate
8KiB
, check here.
/
/
NetlinkConnection构造方法 的时候生成的
8kb
的data内存
if
(data_
=
=
nullptr)
return
false;
/
/
Did we
open
a netlink socket yet?
if
(fd_
=
=
-
1
) {
/
/
尝试建立socket netlink 链接
fd_
=
socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
if
(fd_
=
=
-
1
)
return
false;
}
/
/
Construct
and
send the message.
/
/
构造要发送的消息
struct NetlinkMessage {
nlmsghdr hdr;
rtgenmsg msg;
} request;
memset(&request,
0
, sizeof(request));
request.hdr.nlmsg_flags
=
NLM_F_DUMP | NLM_F_REQUEST;
request.hdr.nlmsg_type
=
type
;
request.hdr.nlmsg_len
=
sizeof(request);
/
/
All
families
request.msg.rtgen_family
=
AF_UNSPEC;
/
/
使用socket数据发送
return
(TEMP_FAILURE_RETRY(send(fd_, &request, sizeof(request),
0
))
=
=
sizeof(request));
}
/
*
*
获取socket的返回结果
*
/
bool
NetlinkConnection::ReadResponses(void callback(void
*
, nlmsghdr
*
), void
*
context) {
/
/
Read through
all
the responses, handing interesting ones to the callback.
ssize_t bytes_read;
while
((bytes_read
=
TEMP_FAILURE_RETRY(recv(fd_, data_, size_,
0
))) >
0
) {
/
/
将拿到的data数据进行赋值
auto
*
hdr
=
reinterpret_cast<nlmsghdr
*
>(data_);
for
(; NLMSG_OK(hdr, static_cast<size_t>(bytes_read)); hdr
=
NLMSG_NEXT(hdr, bytes_read)) {
/
/
判断是否读取结束,否则读取callback
if
(hdr
-
>nlmsg_type
=
=
NLMSG_DONE)
return
true;
if
(hdr
-
>nlmsg_type
=
=
NLMSG_ERROR) {
auto
*
err
=
reinterpret_cast<nlmsgerr
*
>(NLMSG_DATA(hdr));
errno
=
(hdr
-
>nlmsg_len >
=
NLMSG_LENGTH(sizeof(nlmsgerr))) ?
-
err
-
>error : EIO;
return
false;
}
/
/
处理具体逻辑
callback(context, hdr);
}
}
/
/
We only get here
if
recv fails before we see a NLMSG_DONE.
return
false;
}
/
*
*
获取socket的返回结果
*
/
bool
NetlinkConnection::ReadResponses(void callback(void
*
, nlmsghdr
*
), void
*
context) {
/
/
Read through
all
the responses, handing interesting ones to the callback.
ssize_t bytes_read;
while
((bytes_read
=
TEMP_FAILURE_RETRY(recv(fd_, data_, size_,
0
))) >
0
) {
/
/
将拿到的data数据进行赋值
auto
*
hdr
=
reinterpret_cast<nlmsghdr
*
>(data_);
for
(; NLMSG_OK(hdr, static_cast<size_t>(bytes_read)); hdr
=
NLMSG_NEXT(hdr, bytes_read)) {
/
/
判断是否读取结束,否则读取callback
if
(hdr
-
>nlmsg_type
=
=
NLMSG_DONE)
return
true;
if
(hdr
-
>nlmsg_type
=
=
NLMSG_ERROR) {
auto
*
err
=
reinterpret_cast<nlmsgerr
*
>(NLMSG_DATA(hdr));
errno
=
(hdr
-
>nlmsg_len >
=
NLMSG_LENGTH(sizeof(nlmsgerr))) ?
-
err
-
>error : EIO;
return
false;
}
/
/
处理具体逻辑
callback(context, hdr);
}
}
/
/
We only get here
if
recv fails before we see a NLMSG_DONE.
return
false;
}
int
listmacaddrs(void) {
struct ifaddrs
*
ifap,
*
ifaptr;
if
(myGetifaddrs(&ifap)
=
=
0
) {
for
(ifaptr
=
ifap; ifaptr !
=
NULL; ifaptr
=
(ifaptr)
-
>ifa_next) {
char macp[INET6_ADDRSTRLEN];
if
(ifaptr
-
>ifa_addr!
=
nullptr) {
if
(((ifaptr)
-
>ifa_addr)
-
>sa_family
=
=
AF_PACKET) {
auto
*
sockadd
=
(struct sockaddr_ll
*
) (ifaptr
-
>ifa_addr);
int
i;
int
len
=
0
;
for
(i
=
0
; i <
6
; i
+
+
) {
len
+
=
sprintf(macp
+
len
,
"%02X%s"
, sockadd
-
>sll_addr[i],( i <
5
?
":"
: ""));
}
/
/
LOGE(
"%s %s "
,(ifaptr)
-
>ifa_name,macp)
if
(strcmp(ifaptr
-
>ifa_name,
"wlan0"
)
=
=
0
){
LOGE(
"%s %s "
,(ifaptr)
-
>ifa_name,macp)
freeifaddrs(ifap);
return
1
;
}
}
}
}
freeifaddrs(ifap);
return
0
;
}
else
{
return
0
;
}
}
int
listmacaddrs(void) {
struct ifaddrs
*
ifap,
*
ifaptr;
if
(myGetifaddrs(&ifap)
=
=
0
) {
for
(ifaptr
=
ifap; ifaptr !
=
NULL; ifaptr
=
(ifaptr)
-
>ifa_next) {
char macp[INET6_ADDRSTRLEN];
if
(ifaptr
-
>ifa_addr!
=
nullptr) {
if
(((ifaptr)
-
>ifa_addr)
-
>sa_family
=
=
AF_PACKET) {
auto
*
sockadd
=
(struct sockaddr_ll
*
) (ifaptr
-
>ifa_addr);
int
i;
int
len
=
0
;
for
(i
=
0
; i <
6
; i
+
+
) {
len
+
=
sprintf(macp
+
len
,
"%02X%s"
, sockadd
-
>sll_addr[i],( i <
5
?
":"
: ""));
}
/
/
LOGE(
"%s %s "
,(ifaptr)
-
>ifa_name,macp)
if
(strcmp(ifaptr
-
>ifa_name,
"wlan0"
)
=
=
0
){
LOGE(
"%s %s "
,(ifaptr)
-
>ifa_name,macp)
freeifaddrs(ifap);
return
1
;
}
}
}
}
freeifaddrs(ifap);
return
0
;
}
else
{
return
0
;
}
}
/
*
*
获取socket的返回结果
*
/
bool
NetlinkConnection::ReadResponses(void callback(void
*
, nlmsghdr
*
), void
*
out) {
/
/
Read through
all
the responses, handing interesting ones to the callback.
ssize_t bytes_read;
/
/
while
((bytes_read
=
TEMP_FAILURE_RETRY(recv(fd_, data_, size_,
0
))) >
0
) {
/
/
while
((bytes_read
=
TEMP_FAILURE_RETRY(recvfrom(fd_, data_, size_,
0
,NULL,
0
))) >
0
) {
while
((bytes_read
=
TEMP_FAILURE_RETRY(raw_syscall(__NR_recvfrom,fd_, data_, size_,
0
, NULL,
0
))) >
0
) {
auto
*
hdr
=
reinterpret_cast<nlmsghdr
*
>(data_);
for
(; NLMSG_OK(hdr, static_cast<size_t>(bytes_read)); hdr
=
NLMSG_NEXT(hdr, bytes_read)) {
if
(hdr
-
>nlmsg_type
=
=
NLMSG_DONE)
return
true;
if
(hdr
-
>nlmsg_type
=
=
NLMSG_ERROR) {
auto
*
err
=
reinterpret_cast<nlmsgerr
*
>(NLMSG_DATA(hdr));
errno
=
(hdr
-
>nlmsg_len >
=
NLMSG_LENGTH(sizeof(nlmsgerr))) ?
-
err
-
>error : EIO;
return
false;
}
callback(out, hdr);
}
}
/
/
We only get here
if
recv fails before we see a NLMSG_DONE.
return
false;
}
/
*
*
获取socket的返回结果
*
/
bool
NetlinkConnection::ReadResponses(void callback(void
*
, nlmsghdr
*
), void
*
out) {
/
/
Read through
all
the responses, handing interesting ones to the callback.
ssize_t bytes_read;
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2022-8-3 16:59
被珍惜Any编辑
,原因: